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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## Unreleased

- The agent guide now lists the import path, the call, and the driver of each
SQL database adapter, and it shows how to cancel a reminder with
`unschedule()`. Agents in the Track B evaluation invented the names
`solid-objects/database/postgres` and `this.cancelReminder()`.

## 1.0.0 - 2026-10-13

- Version 1.0 freezes the public API under semantic versioning.
Expand Down
13 changes: 12 additions & 1 deletion docs/agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,14 @@ const runtime = configure({
await runtime.install()
```

Each SQL database has its own adapter. Import the adapter from its own path:

| Database | Import | Call | Driver |
| ---------- | ---------------------------------------------------------------- | ---------------------------------- | ------------------------------- |
| SQLite | `import { sqlite } from "solid-objects/database/sqlite"` | `sqlite({ path: "app.sqlite3" })` | None. Node.js has `node:sqlite` |
| PostgreSQL | `import { postgresql } from "solid-objects/database/postgresql"` | `postgresql({ connectionString })` | `npm install pg` |
| MySQL | `import { mysql } from "solid-objects/database/mysql"` | `mysql({ connectionString })` | `npm install mysql2` |

Every authorization callback denies by default. Do step 5 before you call an
actor. [Configuration](configuration.md) lists each option and database
adapter.
Expand Down Expand Up @@ -199,7 +207,8 @@ Obey these rules in actor code:
- Public methods are ordered operations. Each one takes no argument or one
object argument.
- Use `this.schedule({ at, key })` for delayed work. A new `schedule` with the
same key moves the alarm.
same key moves the alarm. `this.unschedule("expire", { key: buyer })` cancels
it.
- Use `this.reject(code, { message })` for a business rule failure that must
not retry. The second argument is an object, for example
`this.reject("room_full", { message: "The room is full" })`.
Expand All @@ -221,6 +230,8 @@ Avoid these mistakes:
| ---------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `this.schedule({ at, key })` with no operation after it | `this.schedule({ at, key }).expire({ buyer })`. A `schedule()` call with no operation after it fails the turn with `ScheduleWithoutOperation` |
| `this.reject("room full")` or `this.reject(code, message)` | `this.reject("room_full", { message: "The room is full" })` |
| `postgres()` from `solid-objects/database/postgres` | `postgresql({ connectionString })` from `solid-objects/database/postgresql`. Step 4 lists each adapter |
| A guessed cancel method, such as `this.cancelReminder()` | `this.unschedule("expire", { key: buyer })`. `this.unscheduleAll("expire")` cancels every key of one operation |
| `registerEffect(name, (context, argumentsValue) => ...)` | `registerEffect(name, (argumentsValue, context) => ...)`. The staged arguments come first |
| A worker process that never names the actor class | `runtime.register(TicketSale)` before `runtime.run(signal)`. See step 7 |

Expand Down
25 changes: 25 additions & 0 deletions test/agent-guide.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { readFileSync } from "node:fs"
import { describe, expect, it } from "vitest"

const guide = readFileSync(new URL("../docs/agents.md", import.meta.url), "utf8")
const packageDefinition = JSON.parse(
readFileSync(new URL("../package.json", import.meta.url), "utf8"),
) as { exports: Record<string, { types: string; import: string }> }

describe("agent guide", () => {
it("names the import path of each server database adapter", () => {
const adapters = Object.keys(packageDefinition.exports).filter(
(path) => path.startsWith("./database/") && !path.endsWith("-wasm"),
)
expect(adapters).toEqual(
expect.arrayContaining(["./database/sqlite", "./database/postgresql", "./database/mysql"]),
)
for (const adapter of adapters) {
expect(guide, adapter).toContain(`"solid-objects${adapter.slice(1)}"`)
}
})

it("shows how to cancel a reminder", () => {
expect(guide).toContain('this.unschedule("expire", { key: buyer })')
})
})
Loading