Status: ACTIVE Previous: FAB-PLUGIN-SPEC-v2.6.md Date: 2026-03-16 Author: Claudia (Operations Director), with Rupert's design directives Plugin:
@rapido/fab
| Change | v2.6 | v2.7 |
|---|---|---|
| Task dispatch mechanism | openclaw gateway call agent → message to main session |
Isolated session per task via sessions_spawn |
| Dispatch service (FAB-3) | Sends trigger message to agent's boot session | Spawns dedicated sub-agent session with self-contained task prompt |
| Stale sweep (FAB-6) | Re-dispatches via gateway call (same mechanism) | Spawns fresh isolated session (kills stale session if trackable) |
| Stale threshold | 30 minutes | 2 hours (agents need execution time) |
| Task prompt delivery | Written to task-dispatch.json, agent reads on trigger |
Injected directly as session task parameter |
task-dispatch.json |
Central dispatch mechanism | Removed — no longer needed |
| Session tracking | None | session_id stored in tasks table for monitoring |
| §1.2 Architecture diagram | No session layer | Session spawn layer between dispatch and agent |
| §1.3 Data flow | Step 2: dispatch → file → gateway call | Step 2: dispatch → sessions_spawn |
| §5.2 Schema | No session tracking | session_id TEXT column added to tasks |
| §5.2 Schema (from v2.5.1) | No reason_blocked, completed_at, validated_at |
Added all three columns — reason_blocked set on blocked/cleared on unblock; completed_at auto-set on agent_done/done; validated_at auto-set on done |
| §5.2 Schema | No denormalized subtask counts | Added subtasks_total, subtasks_done — denormalized counters for dashboard/progress, updated on create and subtaskDone |
| Slack sync (from v2.5.1) | Description, Reason Blocked not synced | Full-field sync — description, reason_blocked, completed_at, validated_at all pushed to Slack |
| Subtask titles (from v2.5.1) | updateSlackSubtask() only pushed checkbox/status |
Title included — corrects [object Object] display bug |
| Dispatcher hardening (from v2.5.1) | Parallel triggers, no rollback | Sequential 5s delay, rollback on failure (assigned→new), error log |
| §8.2 Field mapping (from v2.5.1) | 12 rows | 16 rows — added description, reason_blocked, completed_at, validated_at |
| §7 Lifecycle | 5-step with file-based dispatch | 5-step with session-based dispatch |
| Manifest version | 2.6.0 | 2.7.0 |
- Slack is for humans only. No agent, orchestrator, or script calls the Slack API directly.
- Two modules, one plugin. Task and project modules are independent but share config, relay, and plugin lifecycle.
- Native tools, no exec. Agents call
fab_taskandfab_projectas LLM tools — no shell parsing.
@rapido/fab is an OpenClaw plugin written in TypeScript. It registers:
- Two agent tools —
fab_task(action routing) andfab_project(action routing) - Background services — dispatch, completion-detect, slack-sync, stale-sweep, state-push
- Bundled skills — task-worker, task-orchestrator, project-management
- State relay — standalone Node.js services for NAS deployment
┌──────────────────────────────────────────────────────────────┐
│ @rapido/fab (OpenClaw plugin) │
│ │
│ ┌─────────────────────┐ ┌──────────────────────────────┐ │
│ │ Task module │ │ Project module │ │
│ │ │ │ │ │
│ │ fab_task tool │ │ fab_project tool │ │
│ │ actions: │ │ actions: │ │
│ │ create, update, │ │ create, update, read, │ │
│ │ read, list, log │ │ list, gate │ │
│ │ │ │ │ │
│ │ SQLite DB (sql.js) │ │ projects.json │ │
│ └─────────────────────┘ └──────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Background services │ │
│ │ - task-dispatch (5 min) - task-stale-sweep (2 hrs) │ │
│ │ - task-completion-detect (2 min) - state-push (2 min) │ │
│ │ - task-slack-sync (2 min) │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Session dispatch layer (v2.7) │ │
│ │ Dispatch service spawns isolated sessions per task: │ │
│ │ sessions_spawn({ agentId, task: <prompt>, mode: run })│ │
│ │ Each task → clean context → task-worker skill loads │ │
│ │ No task-dispatch.json — prompt injected directly │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Bundled skills │ │
│ │ - task-worker (for assigned agents) │ │
│ │ - task-orchestrator (for Claudia) │ │
│ │ - project-management (for Claudia) │ │
│ └──────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
│ │
▼ ▼
┌───────────┐ ┌───────────────┐
│ Slack │ │ Synology NAS │
│ (task UI) │ │ state relay │
└───────────┘ └───────┬───────┘
│ wss://
┌─────▼─────┐
│ Dashboard │
│ (Vercel) │
└───────────┘- Task write: Agent calls
fab_task({ action: "update" })→ SQLite DB + JSON trace - Task create: Orchestrator calls
fab_task({ action: "create" })→ DB → dispatch service →sessions_spawn(isolated session per task) → agent executes in clean context - Task read: Agent calls
fab_task({ action: "read" })→ SQLite DB - Project write: Orchestrator calls
fab_project({ action: "create/update/gate" })→ projects.json - Project read: Any agent calls
fab_project({ action: "read/list" })→ projects.json - Sync: Background service → SQLite DB → Slack (one-way push, tasks only)
- State push: Background service → DB + projects.json → NAS relay → WebSocket → Dashboard
Modules are independent. A task's project field loosely references a project number — no foreign key enforcement. Either module can function without the other.
| ID | Component | Type | Role |
|---|---|---|---|
| FAB-1 | fab_task tool |
Agent tool | Task CRUD (5 actions) |
| FAB-2 | fab_project tool |
Agent tool | Project CRUD + gates (5 actions) |
| FAB-3 | Dispatch service | Background (5 min) | Dispatches new tasks via isolated session spawn |
| FAB-4 | Completion detector | Background (2 min) | Promotes → agent_done → spawns orchestrator |
| FAB-5 | Slack sync | Background (2 min) | DB → Slack (tasks only) |
| FAB-6 | Stale sweep | Background (2 hrs) | Re-dispatches stuck tasks via fresh session spawn |
| FAB-7 | State push | Background (2 min) | Pushes state to NAS relay |
| FAB-8 | State relay (receiver) | Standalone (NAS) | Receives state, writes files |
| FAB-9 | State relay (broadcaster) | Standalone (NAS) | WebSocket broadcast |
One tool, action routing. The action parameter routes to the handler. Every call requires caller and callerName.
| Action | Caller | Purpose |
|---|---|---|
create |
orchestrator | Create task + auto-dispatch |
update |
agent, orchestrator | Update status, subtasks, result |
read |
agent (own), orchestrator (all) | Read task with subtasks |
list |
agent (own), orchestrator (all) | List/filter tasks |
log |
orchestrator | View audit log |
{
"name": "fab_task",
"description": "Rapido FAB task management. Create, update, read, and list tasks.",
"parameters": {
"type": "object",
"required": ["action", "caller", "callerName"],
"properties": {
"action": { "type": "string", "enum": ["create", "update", "read", "list", "log"] },
"caller": { "type": "string", "enum": ["agent", "orchestrator"] },
"callerName": { "type": "string" },
"title": { "type": "string" },
"description": { "type": "string" },
"agent": { "type": "string" },
"priority": { "type": "string", "enum": ["critical", "high", "normal", "low", "batchable"] },
"project": { "type": "string" },
"type": { "type": "string", "enum": ["action", "decision", "review", "research"] },
"subtasks": { "type": "array", "items": { "type": "string" } },
"taskId": { "type": "string" },
"status": { "type": "string", "enum": ["in_progress", "blocked", "done", "archived"] },
"subtaskDone": { "type": "array", "items": { "type": "integer" } },
"reasonBlocked": { "type": "string" },
"result": { "type": "string" },
"validationNote": { "type": "string" },
"addSubtasks": { "type": "array", "items": { "type": "string" } },
"includeLog": { "type": "boolean" },
"assignee": { "type": "string" },
"filterStatus": { "type": "array", "items": { "type": "string" } },
"filterProject": { "type": "string" },
"limit": { "type": "integer" }
}
}
}Caller: Orchestrator only.
Required: title, agent
Defaults: priority: "normal", type: "action", subtasks: ["Confirm that task has been done"], dueAt: created_at + 1h
Subtask numbering: auto-prefixed "1. ...". Optional input:/output: prefixes preserved and used in prompts (§6).
Caller: Agent or Orchestrator.
Required: taskId
addSubtasks gate: Only allowed when task.status === 'agent_done'.
With includeLog: true, appends audit log.
Agent calls auto-filter to assignee = callerName.
Caller: Orchestrator only.
Same pattern as fab_task: one tool, action routing, caller/callerName required.
| Action | Caller | Purpose |
|---|---|---|
create |
orchestrator | Create project |
update |
orchestrator | Update status, notes, tags, owner |
read |
any | Read project details |
list |
any | List/filter projects |
gate |
orchestrator | Pass a gate (G1-G6) |
{
"name": "fab_project",
"description": "Rapido FAB project registry. Create, update, read, list projects, and pass gates.",
"parameters": {
"type": "object",
"required": ["action", "caller", "callerName"],
"properties": {
"action": { "type": "string", "enum": ["create", "update", "read", "list", "gate"] },
"caller": { "type": "string", "enum": ["agent", "orchestrator"] },
"callerName": { "type": "string" },
"number": { "type": "string", "description": "Project number (e.g. PRJ-013)" },
"title": { "type": "string" },
"description": { "type": "string" },
"owner": { "type": "string" },
"status": { "type": "string", "enum": ["new", "in_progress", "blocked", "closed"] },
"notes": { "type": "string" },
"agents": { "type": "array", "items": { "type": "string" } },
"workDirectory": { "type": "string" },
"repositories": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": { "type": "string" },
"branch": { "type": "string" },
"worktree": { "type": "string" }
}
}
},
"businessLine": { "type": "string" },
"tags": { "type": "array", "items": { "type": "string" } },
"gate": { "type": "string", "pattern": "^G[1-6]$" },
"force": { "type": "boolean" },
"filterStatus": { "type": "string" },
"filterOwner": { "type": "string" },
"filterTag": { "type": "string" }
}
}
}Caller: Orchestrator only.
Required: number, title, description, owner
Defaults: status: "new", gates: [], created/updated: today
Validation:
numbermust match/^(PRJ|RSH|IDEA)-\d{3}$/numbermust be unique in store
Caller: Orchestrator only.
Required: number
Rules:
- Only updates fields present in the call
status: "blocked"requiresnotesstatus: "closed"on PRJ-* requires G6 passed, orforce: true- Auto-sets
updatedto today
Caller: Any agent.
Required: number
Returns: Full project object as formatted text.
Caller: Any agent.
Optional filters: filterStatus, filterOwner, filterTag
Returns: Formatted table of matching projects.
Caller: Orchestrator only.
Required: number, gate
Behavior: Adds gate code to array (idempotent). Sorts by number.
Store: <dataPath>/projects.json (JSON array, atomic read/write)
interface Project {
number: string; // PRJ-013, RSH-001, IDEA-005
title: string;
description: string;
status: 'new' | 'in_progress' | 'blocked' | 'closed';
gates: string[]; // ["G1", "G2", "G3"]
owner: string;
agents?: string[];
workDirectory?: string;
repositories?: { url: string; branch: string; worktree?: string | null }[];
businessLine?: string;
tags?: string[];
notes?: string | null; // Required when blocked
created: string; // ISO 8601 date
updated: string; // ISO 8601 date
}| Code | Gate | Criteria |
|---|---|---|
| G1 | Plan | PROJECT-PLAN.md created |
| G2 | Requirements | Requirements documented |
| G3 | Design | Tech design + test strategy |
| G4 | Implementation | Core implementation complete |
| G5 | Verification | Testing done, acceptance met |
| G6 | Post-mortem | POST-MORTEM.md written |
Rules: Additive only. PRJ-* needs G6 to close (override: force: true). RSH-/IDEA- can close without G6.
| Caller | create |
update status |
update subtaskDone |
update result |
update validationNote |
update addSubtasks |
read |
list |
log |
|---|---|---|---|---|---|---|---|---|---|
agent |
❌ | in_progress, blocked |
✅ | ✅ | ❌ | ❌ | own | own | ❌ |
orchestrator |
✅ | done, archived |
❌ | ❌ | ✅ | ✅ | all | all | ✅ |
fab (internal) |
— | assigned, agent_done |
— | — | — | — | all | all | — |
| Caller | create |
update |
gate |
read |
list |
|---|---|---|---|---|---|
agent |
❌ | ❌ | ❌ | ✅ | ✅ |
orchestrator |
✅ | ✅ | ✅ | ✅ | ✅ |
- Agents CANNOT set
agent_doneordone. Only completion detector →agent_done. Only orchestrator →done. - Nobody calls Slack directly. Slack sync is background-only.
- Agents can only read/list their own tasks. No cross-agent reads.
- Agents cannot self-unblock.
- Only orchestrator writes projects.
| From | To | Who |
|---|---|---|
new |
assigned |
Dispatch service |
assigned |
in_progress |
Agent |
in_progress |
blocked |
Agent |
blocked |
assigned |
Stale sweep |
in_progress |
agent_done |
Completion detector |
agent_done |
done |
Orchestrator |
agent_done |
assigned |
Orchestrator (addSubtasks) |
done |
archived |
Orchestrator |
sql.js (WASM). DB persisted via db.export() + writeFileSync() after every write.
CREATE TABLE tasks (
task_id TEXT PRIMARY KEY, title TEXT NOT NULL, description TEXT,
type TEXT DEFAULT 'action', priority TEXT DEFAULT 'normal',
status TEXT DEFAULT 'new', assignee TEXT, project TEXT,
created_at TEXT NOT NULL, assigned_at TEXT, due_at TEXT,
completion_pct INTEGER DEFAULT 0, result_summary TEXT,
reason_blocked TEXT, -- v2.5.1: set on blocked, cleared on unblock
validation_note TEXT,
completed_at TEXT, -- v2.5.1: auto-set on agent_done/done
validated_at TEXT, -- v2.5.1: auto-set on done
subtasks_total INTEGER DEFAULT 0, -- v2.7: denormalized count for dashboard/progress
subtasks_done INTEGER DEFAULT 0, -- v2.7: denormalized count, updated on subtaskDone
slack_item_id TEXT, updated_at TEXT,
session_id TEXT -- v2.7: tracks the isolated session spawned for this task
);
CREATE TABLE subtasks (
task_id TEXT NOT NULL REFERENCES tasks(task_id),
position INTEGER NOT NULL, title TEXT NOT NULL,
todo_complete INTEGER DEFAULT 0, slack_item_id TEXT,
PRIMARY KEY (task_id, position)
);
CREATE TABLE task_log (
id INTEGER PRIMARY KEY AUTOINCREMENT, task_id TEXT NOT NULL,
caller TEXT NOT NULL, caller_name TEXT, action TEXT NOT NULL,
old_value TEXT, new_value TEXT, details TEXT, timestamp TEXT NOT NULL
);
CREATE TABLE meta (key TEXT PRIMARY KEY, value TEXT);
CREATE INDEX idx_tasks_status ON tasks(status);
CREATE INDEX idx_tasks_assignee ON tasks(assignee);
CREATE INDEX idx_tasks_updated ON tasks(updated_at);
CREATE INDEX idx_subtasks_task ON subtasks(task_id);
CREATE INDEX idx_log_task ON task_log(task_id);meta table, key next_task_id. Atomic via BEGIN IMMEDIATE.
- Subtask ID = position number. No renumbering on insert.
- Single-writer model. Empty start (no migration from old Slack data).
Prompts use 4 XML tags: <context>, <inputs>, <outputs>, <instructions>.
<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 login with OAuth2</description>
</inputs>
<outputs>
<output position="3">3. output: Write unit tests</output>
</outputs>
<instructions>
Implement login with OAuth2.
Subtasks:
<subtask position="1">1. Create login form</subtask>
<subtask position="2">2. Add validation</subtask>
<subtask position="3">3. output: Write unit tests</subtask>
</instructions>
</task>
Use fab_task to report progress. Always set caller: "agent", callerName: "devdas".
- Start: fab_task({ action: "update", taskId: "T-00068", status: "in_progress" })
- Subtask done: fab_task({ action: "update", taskId: "T-00068", subtaskDone: [1] })
- Result: fab_task({ action: "update", taskId: "T-00068", result: "..." })
- Blocked: fab_task({ action: "update", taskId: "T-00068", status: "blocked", reasonBlocked: "..." })<validation task_id="T-00068">
<context>
<title>Build login page</title>
<assignee>devdas</assignee>
<project>PRJ-012</project>
<status>agent_done</status>
</context>
<inputs>
<result_summary>Login with OAuth2 done. PR #42.</result_summary>
</inputs>
<outputs>
<subtask position="1" complete="true">1. Create login form ✅</subtask>
<output position="3" complete="true">3. output: Write unit tests ✅</output>
</outputs>
<instructions>
Validate. Use caller: "orchestrator", callerName: "claudia".
1. fab_task({ action: "read", taskId: "T-00068" })
2. If valid: fab_task({ action: "update", taskId: "T-00068", status: "done", validationNote: "..." })
3. If changes needed: fab_task({ action: "update", taskId: "T-00068", addSubtasks: ["Fix X"] })
</instructions>
</validation>Same structure as §6.1 with continuation="true", previous_result, orchestrator_note, and [NEW] subtask markers.
-
Create + Dispatch: Orchestrator creates → DB → dispatch service spawns isolated session per task:
sessions_spawn({ agentId: <agent-gateway-id>, task: <structured prompt from §6.1>, mode: "run", label: "fab-task-<taskId>", sandbox: "inherit" })The session receives the full task prompt directly — no file intermediary, no
task-dispatch.json. The dispatch service stores the returnedsession_idin the tasks table for monitoring. Status:new→assigned. -
Execution: Agent starts in a clean, isolated session with task-worker skill loaded →
in_progress→ works subtasks → sets result. The agent usesfab_tasktool calls to report progress (same as v2.6). -
Completion: Every 2 min: all subtasks done →
agent_done→ spawns orchestrator validation session (also isolated). -
Validation: Orchestrator reads →
doneoraddSubtasks(re-dispatch spawns a new isolated session with continuation prompt §6.3). -
Stale recovery: Every 2 hours. Stuck tasks → spawn fresh isolated session (previous session assumed dead). New
session_idreplaces old one in DB.
Problem (v2.6): Dispatch sent a message to the agent's main boot session via openclaw gateway call agent. This had three failure modes:
- Agent's main session had heavy context → trigger message got lost
- Multiple tasks competed for attention in the same session
- Stale sweep re-dispatched every 30 min → infinite loop when agent didn't pick up
Solution (v2.7): Each task gets its own isolated session (sessions_spawn with mode: "run"). Benefits:
- Clean context: Task-worker skill loads fresh, no competing messages
- Self-contained prompt: Full task details injected directly, no file reads needed
- Monitorable:
session_idstored in DB — can check session status - No
task-dispatch.json: Eliminated entirely — prompt goes straight to session - No re-dispatch loops: Stale sweep spawns a new session instead of messaging a dead one
The task-dispatch.json file in agent workspaces is removed in v2.7. It was the intermediary between the dispatcher and the agent — the dispatcher wrote task entries to it, then triggered the agent to read it. With isolated sessions, the task prompt is injected directly into the session's task parameter. No file needed.
Migration: On plugin startup, delete any existing task-dispatch.json files in agent workspaces (best-effort, non-blocking).
Every 2 min, incremental via listTasksUpdatedSince(last_sync_at).
| DB Field | Slack Column | Column ID |
|---|---|---|
| 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 |
| result_summary | Result Summary | Col0ALBUDP82J |
| description | Description | Col0ALMBCL0UB |
| reason_blocked | Reason Blocked | Col0ALTNLR1PC |
| completed_at | Completed At | Col0AL7JGPVK5 |
| validated_at | Validated At | Col0ALBUBR18E |
| todo_complete | Col00 checkbox | Col00 |
Assignee mapping: Human assignees → SlackId from config. Agent assignees → name as-is.
Shared NAS service. Multiple namespaces. One relay for all Rapido plugins.
Runs every 2 min. Builds combined state from task DB + projects.json.
Payload (rapido-fab-state.json):
{
"namespace": "fab",
"pushedAt": "2026-03-16T00:10:00Z",
"tasks": {
"total": 42,
"byStatus": { "new": 0, "assigned": 2, "in_progress": 3, "blocked": 1, "agent_done": 0, "done": 30, "archived": 6 },
"byAgent": { "devdas": { "active": 2, "blocked": 0 } },
"activeTasks": [ { "taskId": "T-00068", "title": "...", "status": "in_progress", "assignee": "devdas", "priority": "high", "completionPct": 66, "subtasks": [...] } ]
},
"projects": {
"total": 20,
"byStatus": { "new": 2, "in_progress": 10, "blocked": 1, "closed": 7 },
"activeProjects": [ { "number": "PRJ-013", "title": "...", "status": "in_progress", "owner": "claudia", "gates": ["G1","G2"], "updated": "2026-03-16" } ]
}
}Namespace-aware: POST /api/state/:namespace → rapido-<namespace>-state.json.
Security: bearer token, localhost-only, 1MB limit, atomic write.
Watches all rapido-*.json. WebSocket messages include namespace.
Endpoints: ws:///ws, GET /api/state, GET /api/state/:namespace, GET /health.
Generates token, prints Synology setup instructions, outputs config block.
rapido-fab/
├── openclaw.plugin.json
├── package.json
├── tsconfig.json
├── src/
│ ├── index.ts # register(api) — plugin entry
│ ├── config.ts # Shared config types
│ ├── task/
│ │ ├── db.ts # SQLite (sql.js)
│ │ ├── fab-task-tool.ts # fab_task tool — action routing
│ │ ├── permissions.ts # Task permission enforcement
│ │ ├── prompts.ts # Prompt generation (§6)
│ │ ├── handlers/
│ │ │ ├── create.ts
│ │ │ ├── update.ts
│ │ │ ├── read.ts
│ │ │ ├── list.ts
│ │ │ └── log.ts
│ │ └── services/
│ │ ├── dispatch.ts
│ │ ├── completion-detect.ts
│ │ ├── slack-sync.ts
│ │ └── stale-sweep.ts
│ └── project/
│ ├── fab-project-tool.ts # fab_project tool — action routing
│ ├── store.ts # projects.json read/write (atomic)
│ ├── validation.ts # Number pattern, gate rules
│ └── handlers/
│ ├── create.ts
│ ├── update.ts
│ ├── read.ts
│ ├── list.ts
│ └── gate.ts
│ └── services/
│ └── state-push.ts # Combined task + project state
├── relay/
│ ├── receiver.ts
│ ├── broadcaster.ts
│ ├── package.json
│ └── SETUP.md
├── skills/
│ ├── task-worker/SKILL.md
│ ├── task-orchestrator/SKILL.md
│ └── project-management/SKILL.md
└── tests/
├── setup.ts
├── task/
│ ├── handlers/
│ │ ├── create.test.ts
│ │ ├── update.test.ts
│ │ ├── read.test.ts
│ │ ├── list.test.ts
│ │ └── log.test.ts
│ ├── permissions.test.ts
│ ├── prompts.test.ts
│ ├── fab-task-tool.test.ts
│ └── state-push.test.ts
├── project/
│ ├── handlers/
│ │ ├── create.test.ts
│ │ ├── update.test.ts
│ │ ├── read.test.ts
│ │ ├── list.test.ts
│ │ └── gate.test.ts
│ ├── fab-project-tool.test.ts
│ ├── store.test.ts
│ └── validation.test.ts
└── config.test.ts{
"id": "rapido-fab",
"name": "Rapido FAB",
"description": "Unified operational tools for Rapido Full Agentic Business",
"version": "2.7.0",
"kind": "operations",
"skills": ["skills/task-worker", "skills/task-orchestrator", "skills/project-management"],
"configSchema": {
"type": "object",
"additionalProperties": false,
"properties": {
"orchestrator": { "type": "string", "description": "Orchestrator agent name (e.g. claudia)" },
"agents": {
"type": "array",
"items": {
"type": "object",
"properties": { "id": { "type": "string" }, "name": { "type": "string" } },
"required": ["id", "name"]
}
},
"humans": {
"type": "array",
"items": {
"type": "object",
"properties": { "name": { "type": "string" }, "slackId": { "type": "string" } },
"required": ["name", "slackId"]
}
},
"dbPath": { "type": "string" },
"projectsPath": { "type": "string" },
"tracePath": { "type": "string" },
"slack": {
"type": "object",
"properties": { "listId": { "type": "string" }, "tokenPath": { "type": "string" } }
},
"relay": {
"type": "object",
"properties": {
"url": { "type": "string" },
"token": { "type": "string" },
"namespace": { "type": "string", "default": "fab" }
}
}
}
}
}{
"plugins": {
"entries": {
"rapido-fab": {
"config": {
"orchestrator": "claudia",
"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": [
{ "name": "rupert", "slackId": "U06K407LVCY" }
],
"slack": { "listId": "F0ALE8DCW1F" },
"relay": {
"url": "http://192.168.1.100:3456",
"token": "env:FAB_RELAY_TOKEN",
"namespace": "fab"
}
}
}
}
}
}All errors return as tool response text:
ERROR: Permission denied. Caller 'agent' cannot create projects.
ERROR: Project PRJ-013 already exists.
ERROR: Number 'FOO-1' does not match pattern PRJ-NNN, RSH-NNN, or IDEA-NNN.
ERROR: Cannot close PRJ-013: gate G6 (Post-mortem) not passed. Use force: true to override.
ERROR: Setting status 'blocked' requires notes.- No UI. Dashboard is read-only via relay.
- No cross-module FK enforcement. Loose project references.
- No multi-writer for projects. Orchestrator only.
- No Slack sync for projects. Tasks only (for now).
- Background services API. If OpenClaw lacks native interval service registration, use
setIntervalinregister(api). - Dashboard UI. Separate Vercel repo. Consumes WebSocket.
- Human → DB sync. Phase 2.