Skip to main content
A task-writing agent that yields when a human is editing the same task.

Scenario

A product queue has tasks that humans and agents both update. They must not collide:
  • If the user is editing, the agent waits or yields.
  • If the agent is updating, the UI can show who is active.
  • If the task changes mid-run, the commit rejects instead of overwriting newer state.

Schema-Backed Worker

Use the same schema client the app uses. The worker takes a claim on the task — a disposable handle that grants exclusive, ordered access and hands back the freshest row off claim.data — then writes through ablo.tasks.update(...).
import Ablo from '@abloatai/ablo';
import { defineSchema, model, z } from '@abloatai/ablo/schema';

const schema = defineSchema({
  tasks: model({
    title: z.string(),
    status: z.enum(['todo', 'doing', 'done']),
  }),
});

const ablo = Ablo({ schema, apiKey: process.env.ABLO_API_KEY });

export async function markDone(taskId: string) {
  await ablo.ready();

  // Acquire the claim; anyone editing the same task queues behind us.
  await using claim = await ablo.tasks.claim({ id: taskId, action: 'update' });
  if (!claim.data) return { status: 'not_found' };
  if (claim.data.status === 'done') return { status: 'noop' };

  const updated = await ablo.tasks.update({
    id: taskId,
    data: { status: 'done' },
    wait: 'confirmed',
  });

  return { status: 'done', task: updated };
  // claim auto-releases as this function returns
}
Advanced schema-less workers can use the protocol client (Ablo({ apiKey }).commits.create(...)), but that is not the first integration path.

UI

'use client';

import { useAblo } from '@abloatai/ablo/react';

export function TaskRow({ task: serverTask }: Props) {
  const data = useAblo((ablo) => ablo.tasks.get(serverTask.id)) ?? serverTask;
  const holder = useAblo((ablo) => ablo.tasks.claim.state({ id: serverTask.id }));
  const agentActive = holder?.actorKind === 'agent';

  return (
    <div>
      <span>{data.title}</span>
      {agentActive ? <span>Agent is updating...</span> : null}
    </div>
  );
}

Why It Works

  • The claim holder and queue are visible on read and over the live stream.
  • Taking a claim serializes concurrent writers instead of letting them race.
  • The claim hands back the freshest claim.data, so the agent never decides on stale state.
  • Audit rows tie each accepted write back to the run that caused it.