Ctrl K
ComunidadGuías…

AGENTS.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# AGENTS.md

Druks runs durable agent apps on DBOS and Postgres. It owns workflow execution,
persisted state, events, gates, webhooks, sandbox access, and the shared
dashboard. Apps are standalone Python packages. They register through the
`druks.apps` entry point. `software_factory` is the bundled reference app for
coding work through GitHub pull requests.

## Read map

Start with `README.md`, then read only the material relevant to the task:

- Workflow lifecycle, state, replay, or recovery: `docs/concepts.md`
- App contracts or the public author surface: `docs/writing-an-app.md`
- App pages, blocks, values, fields, actions, and liveness: `docs/druks-ui.md`
- Configuration or environment variables: `docs/configuration.md`
- Local install and operations: `docs/full-local.md`
- Remote deployment: `docs/deployment.md`
- Failure diagnosis: `docs/troubleshooting.md`
- Backend contribution, migrations, or verification: `docs/development.md`
- Migration head: `alembic heads`, not a scan of `backend/migrations/versions/`
- Shared SPA work: `frontend/README.md`
- Documentation navigation and audience ownership: `docs/index.md`
- The checklist and craft gate every change is held to: `.druks/software_factory/review.md`.

For app-surface changes, inspect the proof app at
`backend/tests/druks-field_notes/` and its tests as well as the author guide.

## Architectural boundaries

- Keep platform and app ownership explicit. Keep GitHub issue, branch, PR, and
  coding-agent policy in `software_factory`, not in Druks core.
- Describe durability precisely. Druks reuses completed durable checkpoints
  during replay. An interrupted operation can run again. Do not imply
  arbitrary-line resume or exactly-once external side effects.
- Derive `Run.state` from DBOS workflow status. Do not add a second writable state mirror.
- Import the public concern namespaces documented in `docs/writing-an-app.md`.
  Do not import Druks internals.
- Keep backend app discovery in runtime packaging. Keep shared-dashboard app UI
  registration in the frontend build. Keep standalone `dist/` delivery separate.
- Keep generic agent, harness, workspace, sandbox, event, gate, webhook, and
  settings plumbing in Druks. Keep domain-specific policy in the app.
- One Druks installation serves one organization. Execution defaults and agent
  overrides are shared. Accounts own credentials, personal preferences, and run
  attribution. Do not add personal execution defaults.
- Grow the author surface by parameter, not by namespace. If the SDK lacks a
  capability, widen the primitive that owns it. Add a keyword argument or a
  method to the class that holds the data. Do not add a namespace, facade,
  context object, or one-use helper module.
- Do not import an app from an author-surface module. Import only
  `druks.accounts`, `druks.workflows`, `druks.workspaces`, `druks.agents`, `druks.events`,
  `druks.signals`, `druks.db`, `druks.schemas`, `druks.prompts`, `druks.durable`,
  `druks.apps`, `druks.ui`, and `druks.webhooks`. A reference to `druks.build` or another app in these modules
  inverts the platform. The builtin apps `core` and `chat` are platform code, so
  these modules can import them.
- Internal code takes `session: AsyncSession` as a required parameter. Only
  author code and the seams that bind the registry read `druks.db.db_session()`;
  ruff's banned-api names the seams in `pyproject.toml`.
- Derive liveness from run state. Do not mirror it in a column. Store an external
  outcome after its owner announces it. Do not infer that outcome from run
  lifecycle. A merge can occur after Druks stops the related run.
- Use `@step` to mark a replay checkpoint, not an expensive call. On replay the body
  re-executes from the top and completed steps return cached results, so code outside a
  step runs again. Moving the boundary changes correctness, not performance.
- Store transient live state and mutual exclusion in Redis, keyed by run id.
  Do not give either one a durable column, row lock, or in-process lock.
- Do not add a store before something reads it.
- Route a workflow cadence or pause through its schedule overrides. Do not use a
  settings column for this purpose.
- Use one canonical name and shape for each contract. Fail loudly on every other shape.
- A managed service differs from a self-hosted one only in configuration: a URL,
  a key, or a verifier. Any manager that serves the same routes and publishes a
  JWKS works. The docs describe Druks as an operator runs it: the
  `[services.<slug>]` table and the Services page. They name no manager.
  A service declares how it authenticates in a nested `Authentication`, found
  by name like `Settings`. It names its ways as instances from
  `druks.services.auth` under fixed names: `api_key`, `jwt`, `oauth`,
  `sign_in`. A kind owns its credential; `Settings` holds the rest. An install
  stores the service's own grant, a vault row with no account; a service
  acts one way at a time. A service never asks `is_managed()`; the manager seams do: the
  OAuth client, the configuration sync, resources, and webhooks.
- Do not type-switch over a typed stream in app code. When a projection needs
  ordering or anchoring, grow the SDK primitive instead of an `isinstance` chain.
- Put only identity and facts in a read-side field. Keep UI wording in the app pages.
- Give a shared resource one global registry. When a second
  consumer needs a different answer, add a scoping axis. Do not add one in anticipation.
- Druks UI shows data and acts on an app's own records. Do not rebuild the
  screens or the settings of a connected service in app pages or app settings.
  The app's Bot changes the service through its API. See "What pages are for"
  in `docs/writing-an-app.md`.

## Layout

- **Backend:** `backend/druks/` contains FastAPI, DBOS, SQLAlchemy 2.0,
  Pydantic v2, and bundled apps.
- **Migrations:** `backend/druks/migrations/` contains platform Alembic migrations.
- **Tests:** `backend/tests/` contains the pytest suite backed by real Postgres.
- **Proof app:** `backend/tests/druks-field_notes/` contains the independently
  packaged proof app.
- **Frontend:** `frontend/` contains the React 19 and Vite shared SPA. The
  backend image includes its repository-root `dist/` output.
- **Calls server:** `druks-calls/` contains `calls`, the Pipecat program that
  carries phone calls, with its own `pyproject.toml` and lock. Its image builds
  from `deploy/calls/` and has its own release workflow.
- **Deployment:** `deploy/` contains Compose files, Caddy configuration, and
  sandbox image inputs. The public runbook is `docs/deployment.md`.
- **Documentation:** `docs/` contains the public and contributor guides.
- **CI:** `.github/workflows/` contains PR checks and the release-image build.

## Verification

Backend tests need Postgres on `localhost:5432` with user and password `druks`, and
the `druks_test` database. `DRUKS_TEST_DATABASE_URL` overrides it —
`DRUKS_DATABASE_URL` is the runtime's and the suite never reads it. DBOS
integration tests also read `DRUKS_TEST_PG`. Start the development database
with:

```bash
docker compose -f deploy/compose.dev.yaml up -d
```

Run the backend gates:

```bash
uv run ruff check backend druks-calls
uv run ruff format --check backend druks-calls
uv pip install -e backend/tests/druks-field_notes
uv run pytest backend/
```

The suite collects the proof app, so `pytest backend/` fails at collection
until that editable install has run.

Before you push, run `druks-house-rules check --base origin/main`. Druks runs
the same check on every pull request and posts it as the `druks-house-rules`
check. Install the command with
`uv tool install git+https://github.com/getdruks/druks-house-rules@v0.0.5`.

If the public app surface changed, also install and exercise the proof
app as described in `docs/development.md`.

Run the frontend gates:

```bash
npm --prefix frontend run lint
npm --prefix frontend test
npm --prefix frontend run build
```

Run the calls server's tests from `druks-calls/`:

```bash
uv run --locked pytest
```

The PR workflows in `.github/workflows/on-pull-request-*.yml` are the source of
truth for CI, including the proof-app install phase.

## Commits

Use `<linear-ticket> -- <commit-msg>` for each commit message, for example
`DRU-547 -- Deliver GitHub MCP to review workspaces`. If no Linear ticket
exists, use only `<commit-msg>`. Do not add an issue or pull request number.
GitHub adds the pull request number.

## Documentation discipline

- Put product behavior, setup, operations, troubleshooting, and app author
  contracts in the appropriate public guide. Keep this file limited to task
  routing, architectural boundaries, and contributor rules.
- Link to one canonical explanation instead of copying it into multiple pages.
- Make sure that behavioral claims match current source and focused tests. Distinguish
  framework capabilities from `software_factory` behavior and guarantees from policy.
- If contributor routing, repository structure, commands, or an architectural
  invariant changes, update this file.

## Style

- Make the minimum change that solves the problem. No speculative abstractions,
  configurability, or error handling for impossible cases. Each changed line
  must trace to the request. Do not improve adjacent code.
- Comments explain a non-obvious *why*—a constraint, invariant, or workaround—not
  what the next line does. Do not add section-divider banner comments.
- If the signature and body do not make a contract obvious, add a class, module,
  or function docstring.
- Exception classes live in the package's `exceptions.py` and subclass
  `DruksError`, not in contracts or models.
- Keep forward-looking notes in the issue tracker, not in source comments.