Skip to content

Instantly share code, notes, and snippets.

@RupertBarrow
Created March 15, 2026 21:57
Show Gist options
  • Select an option

  • Save RupertBarrow/1bd3bd2a46174110a1258946ec132aca to your computer and use it in GitHub Desktop.

Select an option

Save RupertBarrow/1bd3bd2a46174110a1258946ec132aca to your computer and use it in GitHub Desktop.
OTM Specification v2.3 — Rapido FAB Task Management (OpenClaw Plugin)

OTM Specification v2.3

Status: DRAFT — Pending Rupert review Previous: OTM-SPEC-v2.2.md Date: 2026-03-15 Author: Claudia (Operations Director), with Rupert's design directives Updated: Full rewrite as OpenClaw plugin architecture (TypeScript)


1. Architecture Overview

1.1 Core Principle: Slack Is for Humans Only

No agent, orchestrator, or script calls the Slack API directly. All Slack interactions go through the OTM Slack sync service, which owns the Slack connection exclusively.

1.2 Plugin Architecture

OTM is an OpenClaw plugin (@rapido/otm). It registers:

  • Agent toolsotm_task_update, otm_task_read (native LLM tool calls, no exec needed)
  • Background services — dispatch, completion-detect, slack-sync, stale-sweep (managed by Gateway lifecycle)
  • Bundled skillstask-worker, task-orchestrator (declared in plugin manifest)
  • Plugin config — agents list, humans list, Slack settings in plugins.entries.rapido-otm.config
┌─────────────┐     ┌──────────────────────────────────────┐     ┌───────┐
│   Agents    │────▶│  @rapido/otm  (OpenClaw plugin)      │────▶│ Slack │
│ Orchestrator│◀────│                                      │     │  (UI) │
└─────────────┘     │  ┌────────────┐  ┌────────────────┐  │     └───────┘
   agent tools      │  │  SQLite DB │  │ Background     │  │
                    │  │  (source   │  │ Services:      │  │
                    │  │  of truth) │  │ - dispatch     │  │
                    │  │            │  │ - completion   │  │
                    │  │            │  │ - slack-sync   │  │
                    │  │            │  │ - stale-sweep  │  │
                    │  └────────────┘  └────────────────┘  │
                    └──────────────────────────────────────┘

1.3 Data Flow

  1. Write path: Agent calls otm_task_update tool → plugin writes directly to SQLite DB + JSON trace file
  2. Create path: Orchestrator calls otm_task_create tool → DB write → immediate dispatch → agent triggered
  3. Read path: Agent calls otm_task_read tool → reads SQLite DB (instant, in-process)
  4. Sync path: Background service → SQLite DB → Slack (one-way push)
  5. Human path: Human → Slack UI → (Phase 2: Slack Events sync back to DB)

1.4 Components

ID Component Type Role
OTM-1 otm_task_create Agent tool Creates task in DB, triggers dispatch
OTM-2 otm_task_update Agent tool Updates DB directly. Writes JSON trace.
OTM-3 otm_task_read Agent tool Reads from SQLite DB
OTM-4 otm_task_list Agent tool Lists tasks with filters from DB
OTM-5 Dispatch service Background service (interval: 5 min) Dispatches new tasks, safety net
OTM-6 Completion detector Background service (interval: 2 min) Promotes completed tasks → agent_done → spawns orchestrator
OTM-7 Slack sync Background service (interval: 2 min) One-way sync: DB → Slack
OTM-8 Stale sweep Background service (interval: 30 min) Re-dispatches stuck tasks

2. Agent Tools

2.1 Design Principle

Agents interact with OTM via native LLM tool calls — no shell exec, no CLI parsing. The plugin registers JSON-schema tools that execute in-process with the Gateway. Every tool requires caller and callerName parameters for permission enforcement.

2.2 Tool Reference

Tool Caller Purpose
otm_task_create orchestrator Create a new task + dispatch
otm_task_update agent, orchestrator, otm Update status, mark subtasks, set result
otm_task_read agent (own), orchestrator (all) Read a single task with subtasks
otm_task_list agent (own), orchestrator (all) List tasks with filters
otm_task_log agent (own), orchestrator (all) View task audit log

Actor → Tool matrix:

Actor Tools available
Agent (devdas, archibald, etc.) otm_task_update, otm_task_read, otm_task_list
Orchestrator (claudia) otm_task_create, otm_task_update, otm_task_read, otm_task_list, otm_task_log

2.3 otm_task_create

Caller: Orchestrator only.

{
  "name": "otm_task_create",
  "parameters": {
    "caller": "orchestrator",
    "callerName": "claudia",
    "title": "Build login page",
    "description": "Implement the login page with OAuth2 support",
    "agent": "devdas",
    "priority": "high",
    "project": "PRJ-012",
    "type": "action",
    "subtasks": [
      "Create login form",
      "Add validation logic",
      "output: Write unit tests"
    ]
  }
}

Parameters:

Parameter Type Required Default Description
caller string Must be "orchestrator"
callerName string e.g. "claudia"
title string Short, actionable task name
description string Longer description with context
agent string Assignee (validated against config)
priority string "normal" critical|high|normal|low|batchable
project string Project identifier
type string "action" action|decision|review|research
subtasks string[] ["Confirm that task has been done"] Subtask texts. Auto-numbered.

Subtask numbering: Position number is prefixed to the title → "1. Create login form", "2. Add validation logic", etc.

Subtask prefixes: Subtasks can have optional prefixes (input:, output:) preserved in the title and reused as XML tags in prompts (§6):

  • "output: Write unit tests" → stored as "3. output: Write unit tests" → prompt: <output position="3">Write unit tests</output>
  • No prefix → prompt: <subtask position="1">Create login form</subtask>

Behavior:

  1. Validates caller === "orchestrator"
  2. Generates task ID (T-NNNNN) via atomic counter
  3. Writes task + subtasks directly to SQLite DB
  4. Triggers dispatch immediately (in-process)
  5. Due date defaults to created_at + 1 hour

Returns:

{
  "content": [{ "type": "text", "text": "✅ Task T-00068 created and dispatched to devdas\nTitle: Build login page\nSubtasks: 3" }]
}

2.4 otm_task_update

Caller: Agent, Orchestrator, or OTM.

{
  "name": "otm_task_update",
  "parameters": {
    "caller": "agent",
    "callerName": "devdas",
    "taskId": "T-00068",
    "status": "in_progress"
  }
}
{
  "name": "otm_task_update",
  "parameters": {
    "caller": "agent",
    "callerName": "devdas",
    "taskId": "T-00068",
    "subtaskDone": [1, 2]
  }
}
{
  "name": "otm_task_update",
  "parameters": {
    "caller": "agent",
    "callerName": "devdas",
    "taskId": "T-00068",
    "status": "blocked",
    "reasonBlocked": "Waiting on API credentials"
  }
}
{
  "name": "otm_task_update",
  "parameters": {
    "caller": "orchestrator",
    "callerName": "claudia",
    "taskId": "T-00068",
    "addSubtasks": ["Fix CSS on mobile", "Add error handling"]
  }
}

Parameters:

Parameter Type Required Description
caller string "agent"|"orchestrator"|"otm"
callerName string Name of the caller
taskId string Task ID (T-NNNNN)
status string New status (validated against permissions)
subtaskDone number[] Subtask positions to mark done. Agent-only.
reasonBlocked string Required with status: "blocked"
result string Result summary. Agent-only.
validationNote string Validation comment. Orchestrator-only.
addSubtasks string[] New subtask texts. Orchestrator-only. See §5.5.

Behavior:

  1. Validates caller permissions (§3)
  2. Writes update directly to SQLite DB
  3. Writes JSON trace file to data directory (traceability only)
  4. Logs action in task_log table

Returns success or error text.

2.5 otm_task_read

Caller: Agent (own tasks only) or Orchestrator (all tasks).

{
  "name": "otm_task_read",
  "parameters": {
    "caller": "agent",
    "callerName": "devdas",
    "taskId": "T-00068",
    "includeLog": false
  }
}

Returns JSON:

{
  "taskId": "T-00068",
  "title": "Build login page",
  "description": "Implement the login page with OAuth2 support",
  "type": "action",
  "priority": "high",
  "status": "in_progress",
  "assignee": "devdas",
  "project": "PRJ-012",
  "createdAt": "2026-03-15T18:00:00Z",
  "assignedAt": "2026-03-15T18:01:00Z",
  "dueAt": "2026-03-15T19:00:00Z",
  "completionPct": 33,
  "resultSummary": null,
  "validationNote": null,
  "subtasks": [
    { "position": 1, "title": "1. Create login form", "todoComplete": true },
    { "position": 2, "title": "2. Add validation logic", "todoComplete": false },
    { "position": 3, "title": "3. output: Write unit tests", "todoComplete": false }
  ]
}

With includeLog: true, adds an auditLog array:

{
  "auditLog": [
    { "timestamp": "2026-03-15T18:01:00Z", "caller": "otm", "callerName": "dispatch", "action": "status_change", "oldValue": "new", "newValue": "assigned" },
    { "timestamp": "2026-03-15T18:02:30Z", "caller": "agent", "callerName": "devdas", "action": "status_change", "oldValue": "assigned", "newValue": "in_progress" },
    { "timestamp": "2026-03-15T18:10:00Z", "caller": "agent", "callerName": "devdas", "action": "subtask_done", "newValue": "1", "details": "{\"title\":\"1. Create login form\"}" }
  ]
}

2.6 otm_task_list

{
  "name": "otm_task_list",
  "parameters": {
    "caller": "orchestrator",
    "callerName": "claudia",
    "status": ["in_progress", "blocked"],
    "assignee": "devdas",
    "project": "PRJ-012"
  }
}

Access control: Agents can only list their own tasks (assignee auto-set to callerName). Orchestrator can list all, optionally filtered.

2.7 otm_task_log

{
  "name": "otm_task_log",
  "parameters": {
    "caller": "orchestrator",
    "callerName": "claudia",
    "taskId": "T-00068",
    "limit": 50
  }
}

Same access control as otm_task_read.


3. Permissions Matrix

3.1 The caller Parameter

Every tool call MUST include caller and callerName. The bundled task-worker skill tells agents: "always call OTM tools with caller: 'agent' and callerName: '<your-name>'".

Caller Who task_create task_update (status) task_update (subtaskDone) task_update (result) task_update (validationNote) task_update (addSubtasks) task_read task_list
agent Executing agent in_progress, blocked own tasks own tasks
orchestrator Claudia done, archived all all
otm Background services assigned, agent_done all all

3.2 Hard Rules (enforced in tool execute())

  1. Agents CANNOT set agent_done or done. Only completion detector promotes to agent_done. Only orchestrator sets done.
  2. Orchestrator CANNOT set agent_done. This is OTM-internal.
  3. Nobody calls Slack directly. All reads via otm_task_read. All writes via otm_task_update. Slack sync is background-only.
  4. Permission checks happen in the tool itself — invalid caller+action combos return error before any DB write.
  5. Agents can only read/list their own tasks. No cross-agent reads.

3.3 Status Transitions

new ──[otm]──▶ assigned ──[agent]──▶ in_progress ──[otm]──▶ agent_done ──[orchestrator]──▶ done
                  ▲                       │                       │                         │
                  │                       ├──[agent]──▶ blocked   │                         │
                  │                       │               │       │                         ▼
                  │                       │    [otm/stale]─┘      │                      archived
                  │                       │                       │
                  └───────[orchestrator adds subtasks]─────────────┘
ID From To Who How
TT-01 new assigned OTM Dispatch service Automatic on dispatch
TT-02 assigned in_progress Agent status: "in_progress"
TT-03 in_progress blocked Agent status: "blocked", reasonBlocked: "..."
TT-04 blocked assigned Stale sweep service Automatic re-dispatch
TT-05 in_progress agent_done Completion detector All subtasks todo_complete=true
TT-06 agent_done done Orchestrator status: "done", validationNote: "..."
TT-07 agent_done assigned Orchestrator addSubtasks: [...] (re-dispatch, see §5.5)
TT-08 done archived Orchestrator status: "archived" → triggers Slack archive (see §7.2)

Removed: cancelled status. See §11 — task cancellation/abort is deferred. The orchestrator's tool for handling incorrect work is corrective subtasks (TT-07).


4. SQLite Database

4.1 Schema

-- Initialized empty on first run. No migration from Slack.

CREATE TABLE tasks (
  task_id         TEXT PRIMARY KEY,      -- T-NNNNN
  title           TEXT NOT NULL,
  description     TEXT,                  -- Optional longer description
  type            TEXT DEFAULT 'action', -- action | review | decision | research
  priority        TEXT DEFAULT 'normal', -- critical | high | normal | low | batchable
  status          TEXT DEFAULT 'new',    -- new | assigned | in_progress | blocked | agent_done | done | archived
  assignee        TEXT,
  project         TEXT,
  created_at      TEXT NOT NULL,         -- ISO 8601
  assigned_at     TEXT,
  due_at          TEXT,
  completion_pct  INTEGER DEFAULT 0,     -- Computed: (done subtasks / total subtasks) × 100
  result_summary  TEXT,
  validation_note TEXT,
  slack_item_id   TEXT,                  -- Set by Slack sync service
  updated_at      TEXT
);

CREATE TABLE subtasks (
  task_id         TEXT NOT NULL REFERENCES tasks(task_id),
  position        INTEGER NOT NULL,      -- 1-based. Also the subtask ID.
  title           TEXT NOT NULL,          -- Prefixed: "1. Create login form"
  todo_complete   INTEGER DEFAULT 0,     -- 0 or 1
  slack_item_id   TEXT,                  -- Set by Slack sync service
  PRIMARY KEY (task_id, position)
);

CREATE TABLE task_log (
  id              INTEGER PRIMARY KEY AUTOINCREMENT,
  task_id         TEXT NOT NULL,
  caller          TEXT NOT NULL,          -- agent | otm | orchestrator
  caller_name     TEXT,
  action          TEXT NOT NULL,          -- created | status_change | subtask_done | subtask_added | dispatched | comment
  old_value       TEXT,
  new_value       TEXT,
  details         TEXT,                   -- JSON for extra context
  timestamp       TEXT NOT NULL
);

CREATE INDEX idx_tasks_status ON tasks(status);
CREATE INDEX idx_tasks_assignee ON tasks(assignee);
CREATE INDEX idx_subtasks_task ON subtasks(task_id);
CREATE INDEX idx_log_task ON task_log(task_id);

Note on task_log: Structured logs are stored in the database (queryable by task, caller, action, time range). Standard .log files are still written for operational debugging of the plugin services.

4.2 Key Design Decisions

Subtask ID = position number. The position number is the subtask's identity, prefixed to the title: "1. Create login form".

Subtask insertion without renumbering. New subtasks get position = max(existing) + 1. Existing positions never change:

Before:  1. Create login form  ✅
         2. Add validation      ✅
         3. Write unit tests    ✅
         4. Deploy              ⬜

After:   1. Create login form  ✅
         2. Add validation      ✅
         3. Write unit tests    ✅
         5. Fix CSS on mobile   ⬜  [NEW]
         6. Add error handling  ⬜  [NEW]
         4. Deploy              ⬜

WAL mode (Write-Ahead Logging). SQLite journaling mode that allows concurrent readers while a writer is active. Changes are written to a WAL file first, then merged. otm_task_read never blocks on a concurrent otm_task_update. Essential because background services may run while agents are updating tasks.

Empty start. DB is initialized empty on plugin first load. Old Slack tasks are historical and not migrated.


5. Task Lifecycle

5.1 Phase 1: Creation + Dispatch

Step Who Action
1 Orchestrator Calls otm_task_create tool with title, agent, subtasks
2 Plugin Writes task + subtasks directly to SQLite DB
3 Plugin Triggers dispatch in-process → updates DB status=assigned → triggers agent via openclaw gateway call agent with structured prompt (§6.1)

Safety net: Dispatch service also runs every 5 min to catch any tasks left in new state.

5.2 Phase 2: Agent Execution

Step Who Action
4 Agent Receives structured prompt (§6.1) with task details and tool instructions
5 Agent Calls otm_task_update({ caller: "agent", callerName: "devdas", taskId: "T-00068", status: "in_progress" })
6 Agent Works on subtask 1 → calls otm_task_update({ ..., subtaskDone: [1] })
7 Agent (repeat for each subtask)
8 Agent Calls otm_task_update({ ..., result: "PR #42 opened." })

Agent can check progress: otm_task_read({ caller: "agent", callerName: "devdas", taskId: "T-00068" })

5.3 Phase 3: Completion Detection

Step Who Action
9 Completion detector service (every 2 min) Scans DB: tasks with status=in_progress where ALL subtasks todo_complete=true
10 Completion detector Updates DB: status=agent_done, completion_pct=100
11 Completion detector Spawns orchestrator sub-agent via openclaw gateway call agent with validation prompt (§6.2)

5.4 Phase 4: Orchestrator Validation

Step Who Action
12 Orchestrator Calls otm_task_read → reviews outputs
13a Orchestrator (✅ pass) Calls otm_task_update({ caller: "orchestrator", status: "done", validationNote: "Verified." })
13b Orchestrator (❌ needs changes) Calls otm_task_update({ caller: "orchestrator", addSubtasks: ["Fix X", "Fix Y"] }) → triggers re-dispatch (§5.5)

5.5 Subtask Addition + Re-dispatch Flow

When the orchestrator adds subtasks (after reviewing agent_done work):

  1. otm_task_update with addSubtasks writes directly to DB:
    • Creates new subtask rows: position = max(existing) + 1
    • Title prefixed with number and [NEW] label: "5. Fix CSS on mobile [NEW]"
    • Recalculates completion_pct
    • Resets status to assigned
    • Logs action: subtask_added
  2. Dispatch service picks up the task → dispatches to agent
  3. Agent receives a continuation prompt (§6.3) with ✅/⬜ markers and [NEW] labels

Key: No session continuity needed. The prompt self-contains all state.

5.6 Slack Sync (background)

Slack sync service runs every 2 min. See §7 for sync logic, field mapping, and archive handling.

5.7 Stale Recovery

Stale sweep service runs every 30 min. Tasks stuck in assigned/in_progress/blocked > 30 min → re-dispatch.


6. Structured Prompt Templates

Prompts sent to agents and the orchestrator use XML tags within plain text to identify key sections. These are not standalone XML documents — they are prompt text with structured tags: <context>, <inputs>, <outputs>, <instructions>.

6.1 Agent Task Prompt (generated by Dispatch service)

You have been assigned a task. Follow the instructions below.

<task id="T-00068" priority="high" type="action">

<context>
  <project>PRJ-012</project>
  <assignee>devdas</assignee>
  <created_at>2026-03-15T18:00:00Z</created_at>
  <due_at>2026-03-15T19:00:00Z</due_at>
</context>

<inputs>
  <title>Build login page</title>
  <description>Implement the login page with OAuth2 support for the web app</description>
</inputs>

<instructions>
  <subtask position="1">1. Create login form</subtask>
  <subtask position="2">2. Add validation logic</subtask>
  <output position="3">3. output: Write unit tests</output>
</instructions>

</task>

Use the otm_task_update tool to report progress:
- Set status to in_progress when you start
- Call with subtaskDone: [N] as you complete each subtask
- Set result when all subtasks are done
- Call with status: "blocked" and reasonBlocked if you get stuck
Use otm_task_read to check current task state.
Always use caller: "agent" and callerName: "<your-name>".

6.2 Orchestrator Validation Prompt (generated by Completion detector)

A task has been completed by an agent. Validate the outputs.

<validation task_id="T-00068">

<context>
  <title>Build login page</title>
  <assignee>devdas</assignee>
  <project>PRJ-012</project>
  <status>agent_done</status>
</context>

<outputs>
  <subtask position="1" complete="true">1. Create login form ✅</subtask>
  <subtask position="2" complete="true">2. Add validation logic ✅</subtask>
  <output position="3" complete="true">3. output: Write unit tests ✅</output>
</outputs>

<result_summary>Login page with OAuth2 implemented. PR #42.</result_summary>

<instructions>
  Validate the agent's work:
  1. Use otm_task_read to inspect the task
  2. Check that all outputs exist and meet requirements
  3. If valid: otm_task_update with status: "done", validationNote: "Your assessment"
  4. If changes needed: otm_task_update with addSubtasks: ["Fix X", "Fix Y"]
  Always use caller: "orchestrator" and callerName: "claudia".
</instructions>

</validation>

6.3 Agent Continuation Prompt (re-dispatch after subtask addition)

This is a continuation of previous work. The orchestrator reviewed your outputs
and added new subtasks. Focus on the uncompleted subtasks marked [NEW].
Completed subtasks are shown for context only — do not redo them.

<task id="T-00068" priority="high" type="action" continuation="true">

<context>
  <project>PRJ-012</project>
  <assignee>devdas</assignee>
  <previous_result>Login page with OAuth2 implemented. PR #42.</previous_result>
  <orchestrator_note>CSS issues on mobile. Missing error handling.</orchestrator_note>
</context>

<inputs>
  <title>Build login page</title>
  <description>Implement the login page with OAuth2 support for the web app</description>
</inputs>

<instructions>
  <subtask position="1" complete="true">1. Create login form ✅</subtask>
  <subtask position="2" complete="true">2. Add validation logic ✅</subtask>
  <output position="3" complete="true">3. output: Write unit tests ✅</output>
  <subtask position="5" complete="false" new="true">5. Fix CSS on mobile [NEW]</subtask>
  <subtask position="6" complete="false" new="true">6. Add error handling [NEW]</subtask>
  <subtask position="4" complete="false">4. Deploy</subtask>
</instructions>

</task>

Use otm_task_update to report progress. Always use caller: "agent" and callerName: "<your-name>".

7. Slack Sync Service

7.1 Plugin Architecture

Slack sync is the first external sync. The architecture supports future integrations (Asana, Jira, etc.) as additional services within the same plugin or as separate plugins.

7.2 Sync Logic

For each task where updated_at > last_sync_at:
  No slack_item_id                       → create in Slack, store ID back in DB
  Has slack_item_id with status=archived → archive Slack item (Slack API archive action)
  Has slack_item_id                      → update Slack item fields
  
For each subtask of synced task:
  Same create-or-update logic
  (Subtask archiving: TBD — may not apply in Slack Lists)

Update last_sync_at

7.3 Field Mapping (DB → Slack)

DB Field Slack Column Column ID Notes
task_id Task ID Col0ALVK2NA1E
title Title Col0AKKTBJJKZ
type Type Col0AKUV4BF6F
priority Priority Col0ALE8DKWPK
status Status Col0AL1B4UVLJ
assignee Assignee Col0AKZ9G5UAJ
project Project2 Col0ALZBS9C8Z
assigned_at Assigned At Col0AKYGNG0G7
due_at Due Date Col02
completion_pct Completion Col0AL5FJFNN8 Computed
result_summary Result Summary Col0ALBUDP82J
todo_complete (subtask) Col00 checkbox Col00

Archiving: When status is archived, the Slack sync service calls the Slack archive/delete API — not just a field update.

7.4 Human → DB Sync (Phase 2)

Not in v2.3. Changes go through the orchestrator who uses OTM tools.


8. Plugin Structure

8.1 Repository Layout

rapido-otm/
├── openclaw.plugin.json          # Plugin manifest
├── package.json
├── tsconfig.json
├── src/
│   ├── index.ts                  # register(api) — main plugin entry
│   ├── db.ts                     # SQLite module (better-sqlite3, WAL)
│   ├── config.ts                 # Plugin config types + validation
│   ├── permissions.ts            # Caller permission enforcement
│   ├── prompts.ts                # Structured prompt generation (§6)
│   ├── tools/
│   │   ├── task-create.ts        # otm_task_create tool
│   │   ├── task-update.ts        # otm_task_update tool
│   │   ├── task-read.ts          # otm_task_read tool
│   │   ├── task-list.ts          # otm_task_list tool
│   │   └── task-log.ts           # otm_task_log tool
│   └── services/
│       ├── dispatch.ts           # Dispatch service (5 min)
│       ├── completion-detect.ts  # Completion detector (2 min)
│       ├── slack-sync.ts         # Slack sync (2 min)
│       └── stale-sweep.ts        # Stale sweep (30 min)
├── skills/
│   ├── task-worker/
│   │   └── SKILL.md              # Bundled skill for agent task execution
│   └── task-orchestrator/
│       └── SKILL.md              # Bundled skill for orchestrator validation
└── tests/
    └── ...

8.2 Plugin Manifest (openclaw.plugin.json)

{
  "id": "rapido-otm",
  "name": "Rapido OTM",
  "description": "One-Time Mission task management for Rapido FAB",
  "version": "1.0.0",
  "kind": "task-management",
  "skills": ["skills/task-worker", "skills/task-orchestrator"],
  "configSchema": {
    "type": "object",
    "additionalProperties": false,
    "properties": {
      "agents": {
        "type": "array",
        "items": {
          "type": "object",
          "properties": {
            "id": { "type": "string" },
            "name": { "type": "string" }
          },
          "required": ["id", "name"]
        },
        "description": "List of agents that can receive tasks"
      },
      "humans": {
        "type": "array",
        "items": { "type": "string" },
        "description": "List of human names"
      },
      "dbPath": {
        "type": "string",
        "description": "SQLite database path (default: ~/Library/Application Support/rapido-fab/otm.db)"
      },
      "tracePath": {
        "type": "string",
        "description": "Path for JSON trace files"
      },
      "slack": {
        "type": "object",
        "properties": {
          "listId": { "type": "string" },
          "tokenPath": { "type": "string", "description": "Path to file containing Slack bot token" }
        }
      }
    }
  }
}

8.3 Plugin Config (in openclaw.json)

{
  "plugins": {
    "entries": {
      "rapido-otm": {
        "config": {
          "agents": [
            { "id": "devdas", "name": "devdas" },
            { "id": "archibald", "name": "archibald" },
            { "id": "main", "name": "claudia" },
            { "id": "frederic", "name": "frederic" },
            { "id": "salvatore", "name": "salvatore" },
            { "id": "sylvain", "name": "sylvain" }
          ],
          "humans": ["rupert"],
          "slack": {
            "listId": "F0ALE8DCW1F"
          }
        }
      }
    }
  }
}

8.4 File Locations

File Path
SQLite DB ~/Library/Application Support/rapido-fab/otm.db (configurable)
JSON trace files ~/Library/Application Support/rapido-fab/otm/task-updates/
Processed traces ~/Library/Application Support/rapido-fab/otm/processed/
Next task ID ~/Library/Application Support/rapido-fab/otm/next-task-id.json
Sync state ~/Library/Application Support/rapido-fab/otm/slack-sync-state.json
Operational logs ~/Library/Logs/rapido-fab/otm-*.log

9. Error Handling

9.1 Permission Denied

{ "content": [{ "type": "text", "text": "ERROR: Permission denied. Caller 'agent' cannot set status 'done'. Allowed: in_progress, blocked" }] }

9.2 Subtask Not Found

{ "content": [{ "type": "text", "text": "ERROR: Subtask 99 not found for task T-00068." }] }

9.3 Access Denied

{ "content": [{ "type": "text", "text": "ERROR: Access denied. Task T-00070 is assigned to archibald, not devdas." }] }

9.4 Wrong Caller for Create

{ "content": [{ "type": "text", "text": "ERROR: Permission denied. Only caller 'orchestrator' can create tasks." }] }

10. Changes from v2.2

Change v2.2 v2.3
Architecture Standalone CLI + launchd OpenClaw plugin (TypeScript, in-process)
Agent interaction Shell exec (otm task update ...) Native agent tools (otm_task_update)
Background jobs launchd plists Plugin background services (Gateway-managed)
Skills Standalone skill files Bundled with plugin (manifest skills: [...])
Config Separate ~/.config/rapido-fab/otm.json Plugin config in openclaw.json
Language JavaScript TypeScript
Data flow Files → Injector → DB Tool → DB (direct, in-process)
caller scope Only on update On every tool call
Subtask IDs Separate S1, S2 column Position number prefixed in title
DB bootstrap Migration from Slack Clean slate — empty DB
Subtask addition Not supported addSubtasks → re-dispatch (§5.5)
Cross-agent reads Unrestricted Blocked — agents read own tasks only
Task cancellation Defined (cancelled) Removed — open question (§11)
Prompt format Full XML documents Prompt text with XML tags

11. Open Questions

  1. Task cancellation / abort. Removed cancelled status. The orchestrator's tool for incorrect work is corrective subtasks (TT-07). For stopping mid-flight (especially batchable API calls), a future mechanism would need to: (a) kill the spawned agent session, (b) potentially cancel Anthropic Batch API calls, (c) handle side-effects. Deferred.

  2. Human → DB sync. When Rupert manually changes something in Slack, how does it reach the DB? Slack Events API? Periodic pull? — Phase 2.

  3. Subtask archiving in Slack. When a parent task is archived, do subtask items also get archived? Slack Lists behavior TBD.

  4. Plugin background services API. Need to confirm the exact OpenClaw plugin API for registering interval-based background services. If not natively supported, fall back to setInterval within the plugin's register(api) lifecycle.


12. Implementation Plan

  1. Create repo rapido-otm with TypeScript scaffold
  2. Implement plugin manifest + config schema
  3. Implement src/db.ts — SQLite module
  4. Implement src/permissions.ts — caller validation
  5. Implement agent tools: task-create, task-update, task-read, task-list, task-log
  6. Implement src/prompts.ts — structured prompt generation
  7. Implement background services: dispatch, completion-detect, slack-sync, stale-sweep
  8. Create bundled skills: task-worker, task-orchestrator
  9. Write tests
  10. Install plugin + configure in openclaw.json
  11. Create test task to validate pipeline
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment