@abloatai/ablo 0.58.0 → 0.59.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +4 -3
- package/CHANGELOG.md +70 -0
- package/README.md +4 -4
- package/docs/agents.md +20 -1
- package/docs/api.md +55 -5
- package/docs/basic-usage.md +84 -0
- package/docs/client-behavior.md +7 -15
- package/docs/comparison.md +63 -0
- package/docs/concurrency-convention.md +27 -0
- package/docs/context.md +20 -0
- package/docs/coordinate-existing-work.md +104 -0
- package/docs/coordination.md +68 -92
- package/docs/deployment.md +19 -1
- package/docs/examples/{existing-document-pipeline.md → evidence-backed-document-pipeline.md} +2 -2
- package/docs/faq.md +75 -0
- package/docs/guarantees.md +3 -2
- package/docs/idempotency.md +3 -0
- package/docs/implement.md +61 -0
- package/docs/implementation-index.md +20 -0
- package/docs/index.md +59 -178
- package/docs/installation.md +77 -0
- package/docs/instrumentation.md +52 -0
- package/docs/integrations/sandbox-runtime.md +10 -1
- package/docs/migration.md +12 -7
- package/docs/options.md +172 -0
- package/docs/quickstart.md +6 -1
- package/docs/security.md +64 -0
- package/examples/README.md +6 -0
- package/examples/stale-context-agent-turn.ts +106 -0
- package/llms.txt +1 -1
- package/package.json +4 -3
- package/docs/agent-integration-decision-guide.md +0 -123
package/docs/coordination.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
> Choose plain writes, functional updates, stale guards, or claims without losing concurrent work.
|
|
4
4
|
|
|
5
|
+
This page owns row coordination: acquire ownership, proceed from the granted
|
|
6
|
+
state, and release on every exit path.
|
|
7
|
+
|
|
8
|
+
## Choose the owner first
|
|
9
|
+
|
|
10
|
+
| Where the result lands | Start here |
|
|
11
|
+
|---|---|
|
|
12
|
+
| Existing application path: API, GraphQL operation, Postgres transaction, filesystem write, or Git branch merge | [Coordinate existing work](./coordinate-existing-work.md) |
|
|
13
|
+
| Ablo model row, written through the Ablo client | Continue on this page |
|
|
14
|
+
|
|
15
|
+
If Ablo only decides who may start, use the first row and stop here. The claim
|
|
16
|
+
examples below own Ablo-row writes; they are not the starter for wrapping an
|
|
17
|
+
existing operation.
|
|
18
|
+
|
|
5
19
|
Ablo gives you several concurrency tools because not every write has the same
|
|
6
20
|
meaning. Choose the narrowest one that matches the operation.
|
|
7
21
|
|
|
@@ -125,19 +139,66 @@ const forecast = await generateForecast(claim.data.location);
|
|
|
125
139
|
await ablo.reports.update({
|
|
126
140
|
id: claim.data.id,
|
|
127
141
|
data: { forecast, status: 'ready' },
|
|
142
|
+
claim,
|
|
128
143
|
});
|
|
129
144
|
```
|
|
130
145
|
|
|
131
146
|
If another participant already holds the target, `claim` waits its turn and
|
|
132
|
-
then resolves with a fresh row in `claim.data`. Ordinary reads remain open.
|
|
133
|
-
|
|
134
|
-
|
|
147
|
+
then resolves with a fresh row in `claim.data`. Ordinary reads remain open. Pass
|
|
148
|
+
the handle as `claim` on the write so Ablo can verify that you still own the row
|
|
149
|
+
and that it has not changed since the claim was granted.
|
|
135
150
|
|
|
136
151
|
Bind claims with `await using` whenever possible. The claim then releases when
|
|
137
152
|
the scope exits, including when the external call or write throws. For runtimes
|
|
138
153
|
without explicit resource management, use `try/finally` and
|
|
139
154
|
`await claim.release()`.
|
|
140
155
|
|
|
156
|
+
### Handle an expired claim
|
|
157
|
+
|
|
158
|
+
When heartbeat is unset, the lease ends at its TTL. A delayed write that passes
|
|
159
|
+
the expired handle rejects with `AbloClaimedError` and code `claim_lost`. Do not
|
|
160
|
+
apply the prepared result elsewhere; clean up best-effort, then restart from a
|
|
161
|
+
new claim and its fresh `claim.data`.
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
import { AbloClaimedError } from '@abloatai/ablo';
|
|
165
|
+
|
|
166
|
+
const claim = await ablo.tasks.claim({ id: taskId, ttl: '2s' });
|
|
167
|
+
try {
|
|
168
|
+
await new Promise((resolve) => setTimeout(resolve, 2300));
|
|
169
|
+
await ablo.tasks.update({
|
|
170
|
+
id: claim.data.id,
|
|
171
|
+
data: { status: 'done' },
|
|
172
|
+
claim,
|
|
173
|
+
});
|
|
174
|
+
} catch (error) {
|
|
175
|
+
if (error instanceof AbloClaimedError && error.code === 'claim_lost') {
|
|
176
|
+
console.log(error.code);
|
|
177
|
+
} else {
|
|
178
|
+
throw error;
|
|
179
|
+
}
|
|
180
|
+
} finally {
|
|
181
|
+
try { await claim.release(); } catch { /* already expired */ }
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Keep a claim alive
|
|
186
|
+
|
|
187
|
+
Set `ttl` to how quickly another worker should recover if this one stops. If the
|
|
188
|
+
work can take longer, set `heartbeat: true` so Ablo renews the claim:
|
|
189
|
+
|
|
190
|
+
```ts
|
|
191
|
+
const claim = await ablo.records.claim({
|
|
192
|
+
id: recordId,
|
|
193
|
+
ttl: '30s',
|
|
194
|
+
heartbeat: true,
|
|
195
|
+
});
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Leave `heartbeat` out when the claim should expire after the TTL; do not pass
|
|
199
|
+
`false`. If a write returns `claim_lost`, discard that result, claim the row
|
|
200
|
+
again, and restart from the new `claim.data`.
|
|
201
|
+
|
|
141
202
|
### One identity per participant
|
|
142
203
|
|
|
143
204
|
Explicit claims coordinate authenticated participants. Two clients using the
|
|
@@ -241,100 +302,15 @@ The main methods are:
|
|
|
241
302
|
| Method | Purpose |
|
|
242
303
|
|---|---|
|
|
243
304
|
| `claim({ id, ...options })` | Read and claim an existing model row; the handle includes fresh row data. |
|
|
244
|
-
| `claim(id, options)` | Claim an identifier in a registered model namespace without reading a row. |
|
|
245
305
|
| `claim.state({ id })` | Read the current holder without blocking. |
|
|
246
306
|
| `claim.queue({ id })` | Read the current wait order. |
|
|
247
307
|
| `claim.release({ id })` | Release early when you do not hold a handle. |
|
|
248
308
|
| `join({ scope })` | Observe presence for a broader scope. |
|
|
249
309
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
| `model.claim({ id })` | The row exists and the caller may read it. | Coordinate work on a synchronized row while using `handle.data`. |
|
|
255
|
-
| `model.claim(id, options)` | The model namespace is registered; no row is read. | Select one participant before calling an existing authoritative service. |
|
|
256
|
-
|
|
257
|
-
The identifier-only form is row-free, not schema-free. It does not authorize a
|
|
258
|
-
worker or test fixture to push an unrelated model into an inherited production
|
|
259
|
-
schema. Register the namespace through the application's normal schema process,
|
|
260
|
-
or select an already registered namespace whose ownership matches the operation.
|
|
261
|
-
|
|
262
|
-
## Coordinate an existing database operation
|
|
263
|
-
|
|
264
|
-
Use this pattern when an application already has a service that protects a
|
|
265
|
-
transition with a Postgres row lock or advisory lock, but slow preparation such
|
|
266
|
-
as OCR, a model call, or another tool currently happens while that database
|
|
267
|
-
lock is held.
|
|
268
|
-
|
|
269
|
-
Keep the ownership boundary explicit:
|
|
270
|
-
|
|
271
|
-
| Owner | Responsibility |
|
|
272
|
-
|---|---|
|
|
273
|
-
| Ablo claim | Select one participating worker before expensive work begins. |
|
|
274
|
-
| Application service | Authoritative re-read, transition policy, database lock, idempotency, and commit. |
|
|
275
|
-
| Postgres | Canonical row, constraints, and final integrity boundary. |
|
|
276
|
-
|
|
277
|
-
The operation runs in this order:
|
|
278
|
-
|
|
279
|
-
```text
|
|
280
|
-
claim identifier
|
|
281
|
-
-> prepare expensive result once
|
|
282
|
-
-> application service re-reads and commits under its database lock
|
|
283
|
-
-> release claim in finally
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
Model the service seam as two operations rather than moving database policy
|
|
287
|
-
into a resolver, worker, or agent tool:
|
|
288
|
-
|
|
289
|
-
```ts
|
|
290
|
-
interface ExistingOperationService<Input, Result, Row> {
|
|
291
|
-
run(
|
|
292
|
-
input: Input,
|
|
293
|
-
prepare: () => Promise<Result>,
|
|
294
|
-
): Promise<Row>;
|
|
295
|
-
|
|
296
|
-
commitPrepared(
|
|
297
|
-
input: Input,
|
|
298
|
-
prepared: Result,
|
|
299
|
-
): Promise<Row>;
|
|
300
|
-
}
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
The existing rollout path calls `run` and preserves current behavior. The
|
|
304
|
-
coordinated path wins the claim, prepares once, then calls `commitPrepared`.
|
|
305
|
-
Both methods stay under the same application-service owner and enforce the same
|
|
306
|
-
authorization and transition rules.
|
|
307
|
-
|
|
308
|
-
When the transition permits it, implement `commitPrepared` as one SQL statement
|
|
309
|
-
that acquires a transaction-level advisory lock, re-reads the row, validates
|
|
310
|
-
its current state, and updates it. The statement's implicit transaction
|
|
311
|
-
releases the advisory lock automatically. This can remove several sequential
|
|
312
|
-
client/database round trips without replacing the existing database lock.
|
|
313
|
-
|
|
314
|
-
Do not make any of these substitutions:
|
|
315
|
-
|
|
316
|
-
- Do not assume a remote Ablo request joins a local Postgres transaction.
|
|
317
|
-
- Do not remove database constraints or locks during the coordination rollout.
|
|
318
|
-
- Do not prepare expensive work speculatively before the claim resolves.
|
|
319
|
-
- Do not assume direct SQL writers obey an Ablo claim. A claim coordinates only
|
|
320
|
-
callers routed through the participating operation.
|
|
321
|
-
- Do not use a claim as durable workflow state. A lease expires; workflow
|
|
322
|
-
progress must survive independently.
|
|
323
|
-
|
|
324
|
-
Measure the old and coordinated paths with the same inputs. Record cold and
|
|
325
|
-
warm claim acquire/release latency, database round-trip latency, database-lock
|
|
326
|
-
duration, end-to-end latency, duplicate work under contention, and recovery
|
|
327
|
-
after worker exit. Keep a per-operation switch to the old path until the new
|
|
328
|
-
path preserves behavior and improves the selected race at production
|
|
329
|
-
percentiles.
|
|
330
|
-
|
|
331
|
-
For a runnable GraphQL.js implementation and PostgreSQL race/crash proof, see
|
|
332
|
-
[GraphQL.js over an existing backend](./approaches/graphql/graphql-js.md).
|
|
333
|
-
For a domain-neutral hosted lease proof, see
|
|
334
|
-
[Verify hosted coordination separately](./examples/coordination-conformance.md).
|
|
335
|
-
For the same operation boundary applied to source-versioned document
|
|
336
|
-
processing, see
|
|
337
|
-
[Process an existing document once](./examples/existing-document-pipeline.md).
|
|
310
|
+
This page owns row-backed claims: `model.claim({ id })` reads and claims an Ablo
|
|
311
|
+
model row, and the handle carries fresh data. Identifier-only claims before an
|
|
312
|
+
existing authoritative service have a different persistence boundary; use the
|
|
313
|
+
[coordinate-existing-work guide](./coordinate-existing-work.md) for that form.
|
|
338
314
|
|
|
339
315
|
## Choosing correctly
|
|
340
316
|
|
package/docs/deployment.md
CHANGED
|
@@ -229,6 +229,24 @@ ABLO_API_KEY=sk_… npx ablo webhooks list # endpoints + delivery health
|
|
|
229
229
|
place to look when a mirror falls behind. [Webhooks](./webhooks.md) covers the
|
|
230
230
|
handler, the Standard Webhooks signature, and rolling a secret.
|
|
231
231
|
|
|
232
|
+
## Multi-stage schema changes
|
|
233
|
+
|
|
234
|
+
`ablo plan` is the read-only front door for source, active artifact, and
|
|
235
|
+
PostgreSQL together. For a live rename, type transition, or required-field
|
|
236
|
+
change, commit a deployment manifest and pass the same file to plan and push.
|
|
237
|
+
Its gates are `expand`, `dual_write`, `backfill`, `verify`, `switch`, and
|
|
238
|
+
`contract`; each names an owner, resource, dependencies, status, and action.
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
npx ablo plan --manifest ablo/deployment.json
|
|
242
|
+
npx ablo push --manifest ablo/deployment.json --yes
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
A live manifest cannot combine expand and contract for the same resource.
|
|
246
|
+
Contract belongs in a later manifest with its own recorded `approval`. A
|
|
247
|
+
backfill being finished is not contract evidence; verification and client-drain
|
|
248
|
+
gates must also be satisfied.
|
|
249
|
+
|
|
232
250
|
## What to watch once it is live
|
|
233
251
|
|
|
234
252
|
- **`ablo logs`:** commit activity as it happens, scoped by the key. A
|
|
@@ -263,7 +281,7 @@ and what each promises.
|
|
|
263
281
|
`ablo connect check` all green.
|
|
264
282
|
2. A secret `sk_` in the deploy environment as `ABLO_API_KEY` — never in a
|
|
265
283
|
browser bundle.
|
|
266
|
-
3. `ablo
|
|
284
|
+
3. `ablo plan` reviewed, followed by fingerprint-gated `ablo push --yes`.
|
|
267
285
|
4. `ablo status --json` gating the deploy on an empty `blockers` array.
|
|
268
286
|
5. Browser clients on a root-bound `pk_` or an `authEndpoint`, not a secret key.
|
|
269
287
|
6. Webhook endpoints registered at their deployed URLs, with the signing secret
|
package/docs/examples/{existing-document-pipeline.md → evidence-backed-document-pipeline.md}
RENAMED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Build an evidence-backed document pipeline
|
|
2
2
|
|
|
3
3
|
> This is an advanced evidence-backed state reference, not the default Ablo
|
|
4
4
|
> integration tutorial. If you are coordinating an operation that already owns
|
|
5
5
|
> its Postgres write, start with the
|
|
6
|
-
> [
|
|
6
|
+
> [coordinate existing work guide](../coordinate-existing-work.md).
|
|
7
7
|
|
|
8
8
|
> Coordinate expensive processing over an application-owned document without taking ownership of uploads or storage.
|
|
9
9
|
|
package/docs/faq.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# FAQ
|
|
2
|
+
|
|
3
|
+
> Short answers to the implementation choices developers encounter most often.
|
|
4
|
+
|
|
5
|
+
## Does Ablo replace PostgreSQL?
|
|
6
|
+
|
|
7
|
+
No. PostgreSQL remains the durable source of truth. Keep its schema, constraints,
|
|
8
|
+
transactions, row-level security, and short write locks. Ablo coordinates work
|
|
9
|
+
around that state and can route guarded writes into it.
|
|
10
|
+
|
|
11
|
+
## Does Ablo replace Redis locks?
|
|
12
|
+
|
|
13
|
+
It can replace an application-owned reservation layer, but it does not require a
|
|
14
|
+
rewrite. Ablo standardizes resource identity, participant identity, leases,
|
|
15
|
+
waiting, recovery, stale-work rejection, and visibility. Teams can begin by
|
|
16
|
+
coordinating an existing operation while its final transaction remains in the
|
|
17
|
+
application.
|
|
18
|
+
|
|
19
|
+
## Is Ablo only for agents?
|
|
20
|
+
|
|
21
|
+
No. Agents, workers, application services, and people can coordinate over the
|
|
22
|
+
same resources. The package-root client is suited to stateless server work; the
|
|
23
|
+
[React client](./react.md) adds live state and presence for human interfaces.
|
|
24
|
+
|
|
25
|
+
## Which client should I import?
|
|
26
|
+
|
|
27
|
+
Use the default export from `@abloatai/ablo` for agents, workers, route handlers,
|
|
28
|
+
and server operations. It uses ordinary request/response HTTP. Use
|
|
29
|
+
`@abloatai/ablo/react` when a live interface needs local synchronized state,
|
|
30
|
+
subscriptions, or presence.
|
|
31
|
+
|
|
32
|
+
## What is the difference between get and read?
|
|
33
|
+
|
|
34
|
+
`get({ id })` observes the current row. `read({ id })` captures the exact row
|
|
35
|
+
version as evidence for a later guarded write. Pass that returned row in the
|
|
36
|
+
mutation's `reads` array when the decision must be rejected if its premise
|
|
37
|
+
changed.
|
|
38
|
+
|
|
39
|
+
## When should I claim a resource?
|
|
40
|
+
|
|
41
|
+
Claim before slow or expensive work when another participant should not perform
|
|
42
|
+
conflicting work on the same business resource. Do not add a claim to every
|
|
43
|
+
write: a short, independent update can use its normal database and mutation
|
|
44
|
+
semantics.
|
|
45
|
+
|
|
46
|
+
## Is a claim a database lock?
|
|
47
|
+
|
|
48
|
+
No. A claim is a participant-scoped lease held across work that may outlive one
|
|
49
|
+
database transaction. It expires after heartbeat loss. PostgreSQL locks still
|
|
50
|
+
protect the short authoritative transaction.
|
|
51
|
+
|
|
52
|
+
## What if code writes directly to PostgreSQL?
|
|
53
|
+
|
|
54
|
+
Ablo observes the resulting change through the configured data source, but the
|
|
55
|
+
writer bypasses Ablo claims and request ordering. Keep database constraints for
|
|
56
|
+
rules that must apply to every writer.
|
|
57
|
+
|
|
58
|
+
## Are retries exactly once?
|
|
59
|
+
|
|
60
|
+
Ablo idempotency deduplicates the same Ablo request within its retention window.
|
|
61
|
+
It does not make external side effects exactly once. Use the external provider's
|
|
62
|
+
idempotency mechanism or an application-owned effect record.
|
|
63
|
+
|
|
64
|
+
## Does Ablo run long workflows?
|
|
65
|
+
|
|
66
|
+
No. Temporal, Inngest, queues, and application workers still own scheduling,
|
|
67
|
+
retries, and durable workflow progress. Ablo coordinates the shared state those
|
|
68
|
+
executions read and change.
|
|
69
|
+
|
|
70
|
+
## Do I need to understand fencing first?
|
|
71
|
+
|
|
72
|
+
No. Start from the public behavior: a claim expires, another participant can
|
|
73
|
+
take over, and an obsolete owner cannot use an old claim to commit through Ablo.
|
|
74
|
+
The implementation mechanism is documented for operators and advanced
|
|
75
|
+
integrations, not required for basic SDK use.
|
package/docs/guarantees.md
CHANGED
|
@@ -97,8 +97,9 @@ claim queues fairly behind the holder).
|
|
|
97
97
|
By default, a held claim rejects writes from other participants to the claimed
|
|
98
98
|
target. Contenders that call `claim` wait their turn; ordinary reads remain
|
|
99
99
|
open. While you hold a claim, the matching
|
|
100
|
-
`ablo.<model>.update({ id,
|
|
101
|
-
the row changed underneath you after
|
|
100
|
+
`ablo.<model>.update({ id, ..., claim })` proves ownership at write time and is
|
|
101
|
+
rejected with `AbloStaleContextError` if the row changed underneath you after
|
|
102
|
+
your claim point. Do not omit `claim` from a row-backed claimed write.
|
|
102
103
|
|
|
103
104
|
## Agent Runs
|
|
104
105
|
|
package/docs/idempotency.md
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
> Make a retried write safe: the same key never applies the same change twice.
|
|
4
4
|
|
|
5
|
+
This page owns idempotency for updates and retries, including changed intent
|
|
6
|
+
and the `idempotency_conflict` failure code.
|
|
7
|
+
|
|
5
8
|
An agent retries. A socket drops mid-commit, a worker restarts, a queue redelivers — and the write
|
|
6
9
|
you already sent arrives again. An idempotency key is how Ablo tells a retry from a new intention.
|
|
7
10
|
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Choose the Ablo operation
|
|
2
|
+
|
|
3
|
+
> Find one implementation route from the work you are doing, copy its smallest recipe, and open guarantee details only when you need recovery behavior.
|
|
4
|
+
|
|
5
|
+
Do not read the documentation front to back. Start with the operation your
|
|
6
|
+
application already has, then use one row below.
|
|
7
|
+
|
|
8
|
+
| What you are implementing | Start here | Add only when |
|
|
9
|
+
|---|---|---|
|
|
10
|
+
| Get one row by id or list rows with the installed client | [Basic usage](./basic-usage.md) | Use `read`, not `get`, only when a later Ablo write depends on that exact row. |
|
|
11
|
+
| Configure a stateless worker's identity, permissions, or lifecycle | [Agents](./agents.md) | Keep the worker on HTTP; live human interfaces use React. |
|
|
12
|
+
| Wrap an existing API, service, Postgres transaction, filesystem write, or Git merge | [Coordinate existing work](./coordinate-existing-work.md) | Keep the final write in its existing owner. |
|
|
13
|
+
| Expose an existing named operation through GraphQL.js | [GraphQL.js](./approaches/graphql/graphql-js.md) | Keep the resolver dependent on that operation, not directly on Ablo. |
|
|
14
|
+
| Hold an Ablo model row while slow work runs, then write it through Ablo | [Coordination](./coordination.md) | Pass the returned claim handle to the write. |
|
|
15
|
+
| Reject a write when an earlier decision input changed | [Concurrency convention](./concurrency-convention.md) | Pass the exact object returned by `read` through `reads`. |
|
|
16
|
+
| Apply several Ablo writes all-or-none | [Atomic commits](./api.md#atomic-commits) | Put every operation and every captured premise in one `commits.create`. |
|
|
17
|
+
| Make a retried Ablo write safe | [Idempotency](./idempotency.md) | Derive one key from the business event and reuse it only for the identical request. |
|
|
18
|
+
| Send email, charge money, call a provider, or write a file | Keep that effect in the application | Use the provider's key or an application outbox; an Ablo key covers only the Ablo mutation. |
|
|
19
|
+
| Add a live human interface | [React](./react.md) | Humans use the WebSocket/live plane; stateless workers stay on HTTP. |
|
|
20
|
+
|
|
21
|
+
## The four choices agents most often confuse
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
// Observe one current row. No later stale check.
|
|
25
|
+
const task = await ablo.tasks.get({ id });
|
|
26
|
+
|
|
27
|
+
// Declare a premise for one later Ablo write.
|
|
28
|
+
const premise = await ablo.tasks.read({ id });
|
|
29
|
+
if (!premise) throw new Error('task not found');
|
|
30
|
+
await ablo.tasks.update({ id, data, reads: [premise] });
|
|
31
|
+
|
|
32
|
+
// Hold an Ablo row across slow work. The final write goes through Ablo.
|
|
33
|
+
await using claim = await ablo.tasks.claim({ id });
|
|
34
|
+
await ablo.tasks.update({ id, data, claim });
|
|
35
|
+
|
|
36
|
+
// Coordinate row-free work whose final write stays in the application.
|
|
37
|
+
await using lease = await ablo.taskRuns.claim(id, {
|
|
38
|
+
contention: { mode: 'skip' },
|
|
39
|
+
});
|
|
40
|
+
if (lease) await existingTaskService.complete(id);
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Use only one of those shapes unless the operation genuinely has both a claimed
|
|
44
|
+
target and separate captured premises. Claims answer who may work; `reads`
|
|
45
|
+
answer whether evidence is still current; `commits.create` answers whether
|
|
46
|
+
several Ablo writes land together; the existing database transaction still
|
|
47
|
+
owns atomicity for application-owned writes.
|
|
48
|
+
|
|
49
|
+
## Before writing code
|
|
50
|
+
|
|
51
|
+
Answer these five questions:
|
|
52
|
+
|
|
53
|
+
1. Which existing operation am I preserving?
|
|
54
|
+
2. Does the final write belong to Ablo or to the application?
|
|
55
|
+
3. Is the coordination identity a model row or only a stable business id?
|
|
56
|
+
4. Which exact rows influenced the decision?
|
|
57
|
+
5. Which writes must land together?
|
|
58
|
+
|
|
59
|
+
If an answer is unknown, preserve the existing operation and database boundary.
|
|
60
|
+
Do not introduce claims, captured reads, or atomic commits merely because they
|
|
61
|
+
exist.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Ablo implementation index
|
|
2
|
+
|
|
3
|
+
> Route an implementation task to the smallest relevant Ablo guide before reading details.
|
|
4
|
+
|
|
5
|
+
Start with [Introduction](./index.md), then [Choose the Ablo operation](./implement.md). Its one routing table
|
|
6
|
+
separates ordinary reads, existing application writes, claims, captured reads,
|
|
7
|
+
atomic commits, retries, GraphQL, and external effects. Do not scan every page.
|
|
8
|
+
|
|
9
|
+
| Your task | Read first |
|
|
10
|
+
|---|---|
|
|
11
|
+
| Choose among nearby SDK and ownership patterns | [Choose the Ablo operation](./implement.md) |
|
|
12
|
+
| Add Ablo to existing work without replacing its API, transaction, filesystem write, or Git merge | [Coordinate existing work](./coordinate-existing-work.md) |
|
|
13
|
+
| Install Ablo and create a typed client | [Installation](./installation.md) |
|
|
14
|
+
| Read, write, and coordinate shared state | [Basic usage](./basic-usage.md) |
|
|
15
|
+
| Choose between a plain read, guarded read, claim, or atomic commit | [Concurrency convention](./concurrency-convention.md) |
|
|
16
|
+
| Look up an exact method, option, or error type | [API reference](./api.md) |
|
|
17
|
+
| Connect Ablo to an existing Postgres database | [Integration guide](./integration-guide.md) |
|
|
18
|
+
|
|
19
|
+
Follow links from that page only when its routing rule applies. Examples prove a
|
|
20
|
+
specific integration; they are not required reading for a first implementation.
|