Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

## Unreleased

- Correct `docs/agents.md`: `reject` takes a code and an options object,
`this.reject(code, { message })`, not `this.reject(code, message)`.
- `docs/agents.md` now says that a worker process runs only the actor classes
that it knows. A message for an unregistered class stays in SQL with no
attempt and no error, so register each class before `runtime.run(signal)`.
It also tells agents to install the current release, puts the
deny-by-default reminder beside the install step, and lists the API
mistakes found in agent-written code.
- Claim the Context7 library: `context7.json` now carries the library `url` and
the maintainer `public_key`.
- Add the Context7 refresh workflow. A push to `main` that changes the README,
Expand Down
31 changes: 28 additions & 3 deletions docs/agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ The quickstart runs 25 concurrent calls against one identity on a temporary
SQLite database. It proves that no update is lost. Add `pg` or `mysql2` when
you use PostgreSQL or MySQL.

Install the current release. `npm install solid-objects` selects it. Do not
pin a version that you remember from earlier work; the API changed between
releases. The current version is on <https://www.npmjs.com/package/solid-objects>.

Create the runtime and its tables at startup:

```typescript
Expand All @@ -81,7 +85,9 @@ const runtime = configure({
await runtime.install()
```

[Configuration](configuration.md) lists each option and database adapter.
Every authorization callback denies by default. Do step 5 before you call an
actor. [Configuration](configuration.md) lists each option and database
adapter.

## 5. Authorize

Expand Down Expand Up @@ -184,14 +190,24 @@ Obey these rules in actor code:
object argument.
- Use `this.schedule({ at, key })` for delayed work. A new `schedule` with the
same key moves the alarm.
- Use `this.reject(code, message)` for a business rule failure that must not
retry.
- 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" })`.
- Do not write application tables directly from an operation. Use
`this.commitAction()` for a short write in the same database.
- Do not call an external API in an operation. Use `this.emit()` and an effect
handler.
- Write each operation so that it can run again. Delivery is at least once.

Avoid these mistakes:

| Mistake | Correct form |
| ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `this.schedule({ at, key })` with no operation after it | `this.schedule({ at, key }).expire({ buyer })`. `schedule()` stages a reminder only when you call an operation on its result |
| `this.reject("room full")` or `this.reject(code, message)` | `this.reject("room_full", { message: "The room is full" })` |
| `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 |

[State and lifecycle](state-and-lifecycle.md) and the
[public API](api.md) give the full rules.

Expand All @@ -206,13 +222,22 @@ runs in the caller. These features need a process that calls
- Messages to other actors.
- Realtime broadcasts.

The worker process runs only the actor classes that it knows. Register each
class before `run()`. A message for an unregistered class stays in SQL with no
attempt and no error:
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Outdated

```typescript
runtime.register(TicketSale)

const controller = new AbortController()
process.once("SIGTERM", () => controller.abort())
await runtime.run(controller.signal)
await runtime.close()
```

`TicketSale.ref(...)` also registers the class, so a script that calls `ref()`
before `run()`, like the example in step 6, already works.
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Outdated

The packaged `solid-objects start` command does the same for a runtime that
`solid-objects.config.js` exports. When no process runs, committed work waits
in SQL and runs later. [Operations](operations.md) covers roles and shutdown.
Expand Down
Loading