Skip to content
Merged
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
4 changes: 2 additions & 2 deletions .druks/review/checklist.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ English (ASD-STE100 register):
- Active voice with a named actor: "Druks checks this at load", not "this is
checked at load".
- Plain words: use, not utilize; missing, not unimplemented.
- Error messages state the fact, then the fix, imperative: "extension 'x'
- Error messages state the fact, then the fix, imperative: "app 'x'
declares subject Ledger without list_summaries(); the board calls it.
Implement list_summaries() on Ledger."

Expand Down Expand Up @@ -98,7 +98,7 @@ the code is wrong: fix the code, delete the explanation.
and why it falls short — a second copy of one the repo already runs is the
finding.

The author-facing surface (extensions/SDK) is the product, not the plumbing.
The author-facing surface (apps/SDK) is the product, not the plumbing.
Design it by writing the example first: the obvious call is the correct one, the
correct one is short, and a newcomer gets it right without reading the source or
the docs. No exposed internals, no required boilerplate, no ceremony, no knowledge
Expand Down
52 changes: 26 additions & 26 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
# AGENTS.md

Druks runs durable agent applications on DBOS and Postgres. It owns
Druks runs durable agent apps on DBOS and Postgres. It owns
workflow execution, persisted state and events, gates, webhooks, sandbox access,
and the shared dashboard. Apps are **extensions**: standalone Python packages
that self-register through the `druks.extensions` entry point. `ship` is the
bundled reference extension for coordinating coding agents through GitHub PRs.
and the shared dashboard. Apps are standalone Python packages
that self-register through the `druks.apps` entry point. `ship` is the
bundled reference app for coordinating coding agents through GitHub PRs.

## Read map

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

- Workflow lifecycle, state, replay, or recovery: `docs/concepts.md`.
- Extension contracts or the public author surface: `docs/writing-an-extension.md`.
- App contracts or the public author surface: `docs/writing-an-app.md`.
- Configuration or environment variables: `docs/configuration.md`.
- Local install and operations: `docs/full-local.md`.
- Remote deployment: `deploy/README.md`.
Expand All @@ -22,34 +22,34 @@ Start with `README.md`, then read only the material relevant to the task:
- Documentation navigation and audience ownership: `docs/index.md`.
- The checklist and craft gate every change is held to: `.druks/review/checklist.md`.

For extension-surface changes, inspect the proof extension at
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 extension ownership explicit. GitHub issue, branch, PR, and
- Keep platform and app ownership explicit. GitHub issue, branch, PR, and
coding-agent policy belongs to `ship`, not to Druks core.
- Describe durability precisely: completed durable checkpoints are reused when
orchestration replays, but an interrupted operation may run again. Do not imply
arbitrary-line resume or exactly-once external side effects.
- `Run.state` is derived from DBOS workflow status. Do not add a second writable
state mirror.
- Extension authors import the public concern namespaces documented in
`docs/writing-an-extension.md`, not Druks internals.
- Backend extension discovery is runtime packaging. Shared-dashboard extension UI
registration is a compile-time frontend concern. Standalone extensions may ship
- App authors import the public concern namespaces documented in
`docs/writing-an-app.md`, not Druks internals.
- Backend app discovery is runtime packaging. Shared-dashboard app UI
registration is a compile-time frontend concern. Standalone apps may ship
their own `dist/`; do not conflate the two delivery paths.
- Druks owns generic agent, harness, workspace, sandbox, event, gate, webhook, and
settings plumbing. Domain-specific policy stays in the extension.
- The author surface grows by parameter, not by namespace. When an extension needs
settings plumbing. Domain-specific policy stays in the app.
- The author surface grows by parameter, not by namespace. When an app needs
something the SDK lacks, widen the primitive that already owns the concern — a keyword
argument, a method on the class holding the data. Do not add a namespace, a facade, a
context object, or a helper module whose only justification is that the call site
would read shorter.
- No author-surface module imports an extension. `druks.workflows`, `druks.agents`,
- No author-surface module imports an app. `druks.workflows`, `druks.agents`,
`druks.events`, `druks.signals`, `druks.db`, `druks.schemas`, `druks.prompts`,
`druks.durable`, `druks.extensions`, and `druks.webhooks` are what an author imports;
a reference to `druks.build` or any other extension inside them inverts the platform.
`druks.durable`, `druks.apps`, and `druks.webhooks` are what an author imports;
a reference to `druks.build` or any other app inside them inverts the platform.
- Liveness — is this subject still being worked — derives from run state; never mirror
it in a column. An outcome somebody else owns, such as whether a pull request was
merged, is stored when its owner announces it — never inferred from run lifecycle,
Expand All @@ -63,20 +63,20 @@ For extension-surface changes, inspect the proof extension at
through its schedule overrides rather than a settings column.
- A contract is one canonical name and shape that fails loudly on anything else. Do not
accept two spellings of the same thing.
- Extension code does not type-switch over a typed stream. When a projection needs
- App code does not type-switch over a typed stream. When a projection needs
ordering or anchoring, grow the SDK primitive instead of an `isinstance` chain.
- A read-side field carries identity and facts — gate name, kind, reason code. UI
wording lives in the extension's own pages, never on the wire.
wording lives in the app's own pages, never on the wire.
- A shared resource gets one global registry delivered everywhere. Add a scoping axis
when a second consumer needs a different answer, not in anticipation.

## Layout

- `backend/druks/` — FastAPI, DBOS, SQLAlchemy 2.0, Pydantic v2, and bundled
extensions.
apps.
- `backend/migrations/` — platform Alembic migrations.
- `backend/tests/` — pytest suite backed by real Postgres.
- `backend/tests/druks-field_notes/` — independently packaged proof extension.
- `backend/tests/druks-field_notes/` — independently packaged proof app.
- `frontend/` — React 19 and Vite shared SPA; production output is repository-root
`dist/` and is copied into the backend image.
- `deploy/` — Compose files, the bind-mounted Caddy configuration, and sandbox
Expand All @@ -89,7 +89,7 @@ For extension-surface changes, inspect the proof extension at

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 application's and the suite never reads it. DBOS
`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:

Expand All @@ -106,11 +106,11 @@ uv pip install -e backend/tests/druks-field_notes
uv run pytest backend/
```

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

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

Run the frontend gates:

Expand All @@ -121,11 +121,11 @@ npm --prefix frontend run build
```

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

## Documentation discipline

- Put product behavior, setup, operations, troubleshooting, and extension author
- 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.
Expand Down
6 changes: 3 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# Contributing to Druks

Druks is alpha software. Discussion before a large change is useful because the
public extension surface and deployment model are still moving.
public app surface and deployment model are still moving.

## Before opening a pull request

1. Search existing issues and open one for behavior changes or substantial work.
2. Read [the development guide](docs/development.md) and the relevant concept,
operator, or extension-author guide.
3. Keep platform behavior separate from application-specific extension policy.
operator, or app-author guide.
3. Keep platform behavior separate from app-specific policy.
4. Add focused tests for behavior changes and update the canonical public guide
when a contract changes.

Expand Down
25 changes: 12 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ described in [the release process](https://github.com/czpython/druks/blob/main/d
Re-running is also the upgrade path. Then follow
[full local setup](https://github.com/czpython/druks/blob/main/docs/full-local.md) to finish in
the dashboard: connect the agent harnesses and the GitHub App the bundled
`ship` extension acts through; a standalone extension may have different
`ship` app acts through; a standalone app may have different
integration requirements.

Or hand the install to a coding agent — paste this into Claude Code, Codex,
Expand All @@ -72,15 +72,15 @@ set them in `druks.toml` and re-run the same command. See the
for prerequisites, access control, verification, and rollback.

```text
trigger ──> extension workflow ──> durable step ──> agent ──> sandbox
trigger ──> app workflow ──> durable step ──> agent ──> sandbox
│ │ │
│ │ └─ Claude or Codex harness
│ └─ result checkpointed in Postgres
├─ event ──> feed / extension reaction
├─ event ──> feed / app reaction
└─ gate ──> wait for human or external system ──> resume
```

**Platform and applications stay separate**
**Platform and apps stay separate**

Druks owns the execution and operating substrate:

Expand All @@ -89,24 +89,23 @@ Druks owns the execution and operating substrate:
- Claude and Codex harness dispatch through isolated Drukbox sandboxes
- append-only events, live feeds, webhooks, notifications, MCP servers, and skills
- validated operator settings, encrypted MCP/OAuth secrets, and the dashboard shell
- extension discovery, API namespaces, and independent migration histories
- app discovery, API namespaces, and independent migration histories

An **extension** owns the application: its workflows, agents, domain models,
routes, events, provider reactions, and optional dashboard pages. It is a normal
Python distribution registered through the `druks.extensions` entry-point
group. Installing the distribution registers it; Druks does not need an
extension-specific plugin list.
An **app** owns its workflows, agents, domain models, routes, events, provider
reactions, and optional dashboard pages. It is a normal Python distribution
registered through the `druks.apps` entry-point group. Installing the
distribution registers it; Druks does not need an app-specific plugin list.

Scaffold one with the published CLI, no checkout required:

```bash
uvx --from druks druks create extension night_watch
uvx --from druks druks create app night_watch
```

The generated project root carries an `AGENTS.md` with the contracts and a link
to the authoring guide.

The bundled `ship` extension is a concrete example. It coordinates coding
The bundled `ship` app is a concrete example. It coordinates coding
agents through tickets and GitHub pull requests, but GitHub PR orchestration is
`ship` behavior—not the definition of Druks.

Expand All @@ -116,7 +115,7 @@ agents through tickets and GitHub pull requests, but GitHub PR orchestration is
- **Installing locally:** [Full local setup](https://github.com/czpython/druks/blob/main/docs/full-local.md)
- **Operating a remote stack:** [Deployment runbook](https://github.com/czpython/druks/blob/main/deploy/README.md)
- **Configuring integrations and secrets:** [Configuration](https://github.com/czpython/druks/blob/main/docs/configuration.md)
- **Building an application:** [Writing an extension](https://github.com/czpython/druks/blob/main/docs/writing-an-extension.md)
- **Building an app:** [Writing an app](https://github.com/czpython/druks/blob/main/docs/writing-an-app.md)
- **Diagnosing a run or service:** [Troubleshooting](https://github.com/czpython/druks/blob/main/docs/troubleshooting.md)
- **Contributing to Druks:** [Contribution guide](https://github.com/czpython/druks/blob/main/CONTRIBUTING.md)
- **Reporting a vulnerability:** [Security policy](https://github.com/czpython/druks/blob/main/SECURITY.md)
Expand Down
8 changes: 4 additions & 4 deletions deploy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ peers). Other remote providers have their own network and credential
requirements; the local Docker shape is covered in
[Full local](../docs/full-local.md).

The Druks application and sandbox images are published for both `linux/amd64`
The Druks service and sandbox images are published for both `linux/amd64`
and `linux/arm64`.

Everything else — `compose.yaml`, the Caddyfile, `druks.toml`, the rendered
Expand Down Expand Up @@ -225,12 +225,12 @@ Caddyfile fetched by the installer) enforces path-level access:

- `POST /_external/*` — public, authenticated by the matching webhook class in
Druks. Per-provider paths land under
`/_external/<provider>/<category>/`; extension role-module discovery
`/_external/<provider>/<category>/`; app role-module discovery
registers them at import time.
- `/mcp` — public, authenticated per request by personal access token inside
the app; proxied unbuffered so its SSE frames stream.
Druks; proxied unbuffered so its SSE frames stream.
- Everything else — a nonempty trusted identity header (exe.dev login
provides one) required, then proxied to `web` (`127.0.0.1:8001`), which
serves the API, the SPA, and extension frontends alike; the app maps that
serves the API, the SPA, and app frontends alike; Druks maps that
asserted email to your account per request
([access control](../docs/configuration.md#public-urls-and-access-control)).
Loading