Fieldforce Management — Design Spec
Fieldforce Management is a multi-tenant SaaS module for dispatching tasks to field teams, tracking execution, and verifying completion with AI — supporting service, sales, and logistics verticals. Two surfaces (Panel desktop + Mobile Web) share one org-scoped backend, with ClawUI routing all AI calls across Anthropic, OpenAI, Google, and Azure providers with per-org cost tracking and audit logging.
- Status Approved
Fieldforce Management is a multi-tenant SaaS module enabling organizations to dispatch tasks to field teams, track execution, log completion proof (notes, photos, signatures), and verify quality — with AI powering the entire workflow from task creation to completion analysis. Two surfaces (Panel desktop + Mobile Web) share a single org-scoped backend; ClawUI routes all AI calls with per-org cost tracking, audit logging, and multi-provider failover.
System Architecture
Frontend Stack
| Layer | Panel (Desktop) | Mobile Web |
|---|---|---|
| Framework | SolidStart (existing panel app) | Astro (existing platform app) |
| Islands | Solid.js | Qwik |
| Client state | TanStack Query | TanStack Query |
| Client cache | None — always online assumed | IndexedDB via FieldforceStorageAdapter (Phase 2) |
| Pagination | Offset (20 / 50 / 100 per page) | Cursor-based infinite scroll |
| i18n | Paraglide (en, zh, ms) | Paraglide (en, zh, ms) |
| Styling | Tailwind + existing design system | Tailwind + existing design system |
Roles & Access Control
Admin / Owner
org-wide- All tasks, activities, audit logs org-wide
- Create, assign, approve, cancel tasks
- Manage fieldforce settings + AI config
- Full AI feature access
Manager
org-wide- All org tasks + activity logs
- Create, assign, approve/reject tasks
- Full AI feature access
- Cannot manage fieldforce settings
Supervisor
team-scoped- Own team's tasks + activity logs
- Create + assign within own team
- Approve/reject completion
- Full AI feature access
Field Team
own tasks only- View own assigned tasks
- Log activities (note, photo, signature, voice)
- Update own task status
- AI: voice-to-text logging only
Full capability matrix
| Capability | Admin / Owner | Manager | Supervisor | Field Team |
|---|---|---|---|---|
| View all org tasks | ✅ | ✅ | ❌ | ❌ |
| View team tasks | ✅ | ✅ | ✅ (own team) | ❌ |
| View own tasks | ✅ | ✅ | ✅ | ✅ |
| Create tasks | ✅ | ✅ | ✅ (own team) | ❌ |
| Assign tasks | ✅ | ✅ | ✅ (own team members) | ❌ |
| Approve / reject completion | ✅ | ✅ | ✅ | ❌ |
| Log activities | ❌ | ❌ | ❌ | ✅ |
| View activity logs | ✅ | ✅ | ✅ (own team) | ✅ (own) |
| Access AI features | ✅ | ✅ | ✅ | ✅ (voice only) |
| Manage fieldforce settings | ✅ | ❌ | ❌ | ❌ |
Data Model
Core Tables
ff_tasksid, org_id, task_type ('service'|'sales'|'logistics'|string), title, description, location, due_date, priority ('low'|'medium'|'high'), status, created_by, assigned_to[] (JSONB), ai_checklist[] (JSONB), review_noteff_activitiesid, org_id, task_id, performed_by, activity_type ('note'|'photo'|'signature'|'status_update'|'checklist'), content, duration_minutes?, started_at?ff_attachmentsid, activity_id, file_url (S3 org-prefixed), file_type ('photo'|'signature'), ai_score 0–100?, ai_flags[]?— ai_score/flags Phase 3ff_task_embeddingsid, task_id, org_id, embedding vector(1536), updated_at— pgvector; generated fromtitle + description + location + task_type; kept in separate table to keepff_tasksleanff_audit_logs- Append-only.
id, org_id, user_id, user_name, user_role, action (AuditAction), entity_type 'FIELDFORCE_TASK', entity_id, before (JSONB)?, after (JSONB)?, details (JSONB)?, ip_address, user_agent, session_id, request_id org_ai_configs- Org-level, shared across all AI modules.
id, org_id (unique), mode ('trial'|'platform'|'byok'|'disabled'), provider, api_key_encrypted (AES-256), model, trial_credits_used, trial_credits_limit
Task Status Flow
flowchart LR
P[pending] --> IP[in_progress]
IP --> C[completed]
C --> A[approved]
C --> NR[needs_revision]
NR --> IP
P -.->|"cancel\nadmin/manager only"| CAN[cancelled]
IP -.->|"cancel"| CAN
C -.->|"cancel"| CAN
NR -.->|"cancel"| CAN
approved and cancelled are terminal. needs_revision loops back to in_progress — field team re-logs activity then re-submits.AI Config Modes
| Mode | Who pays | AI available | Notes |
|---|---|---|---|
trial | Platform absorbs cost | ✅ Limited | Default for new orgs. Capped at trial_credits_limit calls. |
platform | Org billed by platform | ✅ Full | Org subscribes to AI add-on; uses platform's ClawUI key. |
byok | Org pays provider directly | ✅ Full | Org supplies own API key. Platform charges no AI fee. |
disabled | — | ❌ | AI features hidden from org UI entirely. |
Audit Log Events
| Action | Trigger | before / after |
|---|---|---|
TASK_CREATED | Manager/Supervisor creates task | — / task snapshot |
TASK_ASSIGNED | Task assigned or reassigned | previous assignees / new assignees |
TASK_STATUS_CHANGED | Any status transition | previous status / new status |
TASK_APPROVED | Manager/Supervisor approves | completed / approved |
TASK_REJECTED | Manager/Supervisor rejects | completed / needs_revision + review_note |
TASK_RESUBMITTED | Field team re-submits after revision | needs_revision / completed |
TASK_CANCELLED | Task cancelled | previous status / cancelled |
TASK_ESCALATED | Overdue auto-escalation triggered | — / escalation target role |
TASK_PRIORITY_CHANGED | Priority updated | previous priority / new priority |
TASK_DUE_DATE_CHANGED | Due date changed | previous due_date / new due_date |
Audit decorator pattern (Go implementation)
type AuditedFieldforceService struct {
inner *FieldforceService
auditHelper *AuditHelper
}
func (s *AuditedFieldforceService) ApproveTask(ctx context.Context, taskID string, note string) error {
task, _ := s.inner.taskRepo.FindByID(ctx, taskID)
before := task.Status // capture BEFORE
err := s.inner.ApproveTask(ctx, taskID, note)
s.auditHelper.LogTaskApproval(ctx, TaskAuditParams{
TaskID: taskID,
Before: before,
After: "approved",
Details: map[string]any{"review_note": note},
}) // non-blocking — audit errors don't fail operation
return err
}
API Design
Task Endpoints
POST /api/organizations/:org_id/fieldforce/tasks
Body: { title, description, task_type, assigned_to[], due_date, priority, location? }
GET /api/organizations/:org_id/fieldforce/tasks
?status&task_type&assignee_id&due_before&priority&q (full-text)&sort_by&sort_dir
&page&page_size (Panel — offset) | &cursor&limit (Mobile — cursor)
Returns: { tasks: Task[], total?, next_cursor? }
GET /api/organizations/:org_id/fieldforce/tasks/:task_id
Returns: Task + { activities: Activity[], attachments: Attachment[] }
PATCH /api/organizations/:org_id/fieldforce/tasks/:task_id
Body: { status?, assigned_to[]?, priority?, due_date?, … }
DELETE /api/organizations/:org_id/fieldforce/tasks/:task_id
Soft-cancel. Requires manager/admin role.
Activity Endpoints
POST /api/organizations/:org_id/fieldforce/activities
Body: { task_id, activity_type, content, duration_minutes?, started_at? }
GET /api/organizations/:org_id/fieldforce/activities
?task_id&performed_by
POST /api/organizations/:org_id/fieldforce/activities/:activity_id/attachments
Multipart or presigned S3 URL flow.
AI Endpoints
POST /api/organizations/:org_id/fieldforce/ai/parse-task
Body: { prompt: string }
Returns: Partial<Task> (title, description, task_type, priority, due_date, location, assignee suggestions)
POST /api/organizations/:org_id/fieldforce/ai/suggest-assignees
Body: { task_id or partial task fields }
Returns: { suggestions: [{ user_id, name, reason, score }] }
POST /api/organizations/:org_id/fieldforce/ai/completion-summary
Body: { task_id }
Returns: { summary: string }
POST /api/organizations/:org_id/fieldforce/ai/transcribe-voice
Body: { audio_blob }
Returns: { transcript: string }
GET /api/organizations/:org_id/fieldforce/ai/at-risk-tasks
Returns: [{ task_id, risk_score, reason }]
Audit Log Endpoints
GET /api/organizations/:org_id/fieldforce/audit
Scoped by role: admin/manager = all org; supervisor = own team only.
?task_id&actor_id&action&from&to&page&page_size
Returns: { logs: AuditLog[], total }
GET /api/organizations/:org_id/fieldforce/audit/tasks/:task_id
Full audit trail for one task. Returns: AuditLog[]
# No write endpoints — audit logs are system-generated only.
AI Config Endpoints
GET /api/organizations/:org_id/ai-config # api_key_encrypted NEVER returned
PUT /api/organizations/:org_id/ai-config # Admin/Owner only
DELETE /api/organizations/:org_id/ai-config/api-key # Remove BYOK key, revert to platform/trial
# Platform admin only:
GET /api/admin/ai-config/defaults
PUT /api/admin/ai-config/defaults # Set default provider, model, trial credits limit
Panel Features (Desktop)
Home Dashboard — /app/fieldforce
- Summary cards: Total tasks · Pending · In Progress · Overdue (red) · Needs Revision
- Approval queue: tasks awaiting review
- At-risk tasks (AI): flagged tasks likely to miss deadline
- Recent activity feed
- My tasks today (sorted by priority + due_date)
- Next due task highlighted
- Quick status update inline
Task List — /app/fieldforce/tasks
- Full table: title, task_type, assignee(s), priority, due_date, status
- Full-text search across title + description + assignee name
- Filters: status, task_type, priority, assignee, due date range
- Sort: due_date, priority, created_at, status
- Offset pagination — configurable page size: 20 / 50 / 100
- Bulk actions: reassign, cancel, change priority, extend deadline
- Overdue tasks: visual red flag + row highlight
Task Detail — /app/fieldforce/tasks/:id
- Full task metadata · Assigned team members with avatars · Location string (clickable → open in maps)
- AI-generated checklist — editable before publishing
- Approval panel (manager/supervisor): approve or reject with feedback text
- On rejection: status →
needs_revision, feedback surfaced to field team - AI completion summary button — one-click professional report generation
- Audit trail tab: full event history for this task (actor, action, before/after, timestamp)
- Visual Task Timeline: merged chronological view of
ff_activities+ff_audit_logs, read-only for all roles
Visual Task Timeline — event color coding
| Event type | Visual treatment |
|---|---|
| Task created | ● Grey — "Created by [name]" |
| Task assigned | ● Blue — "Assigned to [names]" |
| Status changed | ● Yellow — "Moved to In Progress by [name]" |
| Note logged | ● Teal — note preview, expand to read full |
| Photo attached | ● Teal — thumbnail inline, click to enlarge |
| Signature captured | ● Teal — signature preview |
| Submitted for review | ● Purple — "Submitted by [name]" |
| Approved | ● Green — "Approved by [name]" + review note |
| Rejected | ● Red — "Rejected by [name]" + review note |
| Resubmitted | ● Orange — "Resubmitted by [name]" |
| Overdue escalated | ● Red — "Escalated to [role]" |
| Cancelled | ● Grey — "Cancelled by [name]" |
Timeline renders on Panel (full width) and Mobile Web (stacked cards, same data). Read-only for all roles.
Create Task — /app/fieldforce/tasks/new
Standard form vs AI mode
Two modes: Standard (manual fill) and AI mode (natural language → pre-filled form).
AI mode parses free text such as "Visit Acme Corp tomorrow for AC repair, assign to Sarah, high priority" into structured task fields. After form fill, AI smart assignment shows top 3 recommended assignees with reasoning (workload, proximity, past performance) — manager selects or overrides. AI auto-generates a checklist from task_type + description; manager edits before saving.
AI mode is hidden entirely when org AI config is disabled.
Audit Log — /app/fieldforce/audit
Access: Admin / Owner / Manager only. Table view: actor, action, task, before → after, timestamp. Filters: action type, actor, task_id, date range. Offset pagination. Each row expandable for full before/after JSON diff. Approval events color-coded. Export to CSV (Phase 2).
Mobile Web Features
Home — /fieldforce
- My tasks (today's + overdue first)
- Quick status toggle per task (tap → confirm dialog)
- Overdue badge immediately visible
- Summary strip: counts by status
- Approval queue shortlist
My Tasks — /fieldforce/tasks
- Task cards with large tap targets
- Cursor-based infinite scroll
- Filter chips: All / Pending / In Progress / Overdue
- Sort: Due date / Priority
Task Detail — /fieldforce/tasks/:id
- Task context: what, where, when, assigned by
- AI checklist with tap-to-tick items
- Visual Task Timeline — stacked card format optimized for mobile scroll; color-coded dots per event; photos as inline thumbnails; tap any card to expand full detail
- Log Activity button → bottom sheet
Log Activity — Bottom Sheet
<input type="file" capture="environment"> → S3 upload. Signature tab: canvas pad (Qwik island). Voice tab: record → AI transcribe → confirm text → save as note.Status Update
Field team taps status toggle → confirm dialog → API call → manager/supervisor notified. completed status triggers push + in-app notification to manager/supervisor for review.
AI Features
MVP AI Features (Phase 2)
| Feature | Surface | Description |
|---|---|---|
| Natural language task creation | Panel + Mobile | Parse free-text/voice → structured task form (title, type, priority, due date, location, assignees) |
| Smart assignment recommendation | Panel + Mobile | Suggest top assignees by workload + past performance; proximity when lat/lng added Phase 2 |
| Auto-categorization & priority | Panel + Mobile | Infer task_type + priority from description |
| AI checklist generation | Panel + Mobile | Generate task checklist from task_type + description; manager edits before publishing |
| Voice-to-text activity logging | Mobile | Record voice note → transcribe → confirm text → save as activity |
| AI completion summary | Panel | Generate professional report from activities + photos |
| Predictive delay detection | Panel | Score in-progress tasks by delay risk; surface to manager before deadline |
Phase 3 & Phase 4 AI Features
| Feature | Phase | Description |
|---|---|---|
| Photo analysis & quality score | 3 | Analyze completion photos, score quality 0–100, flag issues |
| Smart checklist auto-tick | 3 | Detect completed items from photo/notes automatically |
| Route optimization | 3 | Suggest optimal task visit order for multi-task days |
| Anomaly detection | 3 | Flag fast completions, missing photos, wrong location patterns |
| Workload rebalancing | 3 | Detect imbalance, suggest specific task reassignments |
| Manager daily briefing | 3 | Morning AI summary: tasks due today, at-risk, pending approvals |
| Conversational task interface | 4 | Hall Chat integration — manage tasks via natural language chat |
Supported AI Providers
| Provider | Models | Embedding |
|---|---|---|
| Anthropic | claude-opus-4-7, claude-sonnet-4-6, claude-haiku-4-5 | via third-party |
| OpenAI | gpt-4o, gpt-4o-mini, o1 | text-embedding-3-large |
| gemini-1.5-pro, gemini-1.5-flash | text-embedding-004 | |
| Azure OpenAI | any deployed model | any deployed embedding model |
Platform admin sets the default provider + model for platform/trial modes. Orgs on BYOK select their own provider and model. Org Admin/Owner configures via Panel → Org Settings → AI.
Search & Pagination
Semantic search with hybrid fallback
pgvector cosine similarity search on ff_task_embeddings, scoped by org_id and role.
Allows natural queries like "broken AC unit at shopping mall" to return relevant tasks without exact keyword match. Embeddings generated from title + description + location + task_type on task create/update. Same ClawUI middleware — org cost tracking applies to embedding calls.
Fallback: PostgreSQL tsvector full-text search on title + description when embedding service is unavailable. Search never breaks. Sort controls apply after semantic ranking — results first ranked by similarity, then sorted within the result set.
- Panel URL (offset)
GET /fieldforce/tasks?q=broken+ac+unit&page=2&page_size=50&sort_by=due_date&sort_dir=asc- Mobile URL (cursor)
GET /fieldforce/tasks?q=broken+ac+unit&cursor=<opaque_token>&limit=20- Sort controls
due_date,priority,created_at,status,updated_at
Notifications
Reuses existing push notification infrastructure (PushNotificationToggle in Astro app) + in-app notification feed.
| Trigger | Recipient | Channel |
|---|---|---|
| Task assigned | Assignee(s) | Push + in-app |
| Task status → completed | Manager / Supervisor | Push + in-app |
| Task rejected (needs_revision) | Assignee(s) | Push + in-app + feedback text |
| Task overdue | Assignee(s) + Manager | Push + in-app |
| Task reassigned | New assignee | Push + in-app |
| Task at risk (AI prediction) | Manager / Supervisor | In-app only |
| Task cancelled | Assignee(s) | In-app only |
Overdue Task Handling
Background job / cron checks: due_date < now AND status NOT IN (completed, approved, cancelled).
-
Flag as overdue
Add visual
overdueflag — red badge in task list and task detail. -
Notify
Push + in-app to assigned field team + manager/supervisor.
-
Auto-escalate
If overdue > 24h with no activity: escalate to next role up — supervisor → manager → admin. Logged as
TASK_ESCALATEDin audit trail. -
Surface proactively (AI)
GET /ai/at-risk-taskssurfaces tasks likely to miss deadline before they become overdue — shown in manager dashboard.
Offline-First Architecture (Mobile Web Only)
// Defined once in Phase 1. Components only ever call this interface.
interface FieldforceStorageAdapter {
getTasks(orgId: string): Promise<Task[]>
getTask(taskId: string): Promise<Task | null>
cacheTasks(tasks: Task[]): Promise<void>
queueActivity(activity: PendingActivity): Promise<void>
queueAttachment(attachment: PendingAttachment): Promise<void>
getSyncQueue(): Promise<PendingSync[]>
clearSynced(ids: string[]): Promise<void>
}
// Phase 2: IndexedDB (Dexie.js)
class IndexedDBAdapter implements FieldforceStorageAdapter { … }
// Phase 4: Electric SQL (drop-in replacement)
class ElectricSQLAdapter implements FieldforceStorageAdapter { … }
-
Phase 1 — Interface only
FieldforceStorageAdapterinterface defined. Components coded against it. No IndexedDB activated. -
Phase 2 — IndexedDB read
IndexedDBAdapter(Dexie.js) activated.db.taskspopulated (read-only cache).db.activities+db.attachmentspending queues.db.sync_logfor reconnect sync. Conflict resolution: last-write-wins on append-only activity logs. -
Phase 4 — Electric SQL
Implement
ElectricSQLAdapter, swap in DI container, one-time data migration (IndexedDB → SQLite on app load). Real-time PostgreSQL sync, full offline read/write.
Implementation Phases
-
Phase 1 — Core Task Dispatch (Weeks 1–3)
DB schema (all 6 tables) · Task CRUD · Activity creation · Audit logs for: created, assigned, status changed, cancelled · Panel: task list, task detail with audit tab, create form, activity log overview, audit log page · Mobile: my tasks, task detail, text note logging · Notifications: assigned + completed · Calendar: tasks on
/team/calendar· i18n: en/zh/ms ·FieldforceStorageAdapterinterface defined (IndexedDB schema inactive) -
Phase 2 — AI Core + Approval (Weeks 4–6)
All 7 MVP AI features via ClawUI · Photo upload (camera + S3) · Signature pad (canvas Qwik island) · Manager approval workflow (approve / reject / needs_revision) · Remaining audit events · Overdue cron + escalation · At-risk task notifications · Panel: AI task creation mode, smart assignment panel, completion summary · Mobile: voice logging, AI checklist tick
-
Phase 3 — Polish + Phase 2 AI (Weeks 7–8)
Photo analysis + quality score · Route optimization · Anomaly detection · Workload rebalancing · Manager daily briefing · Mobile UX refinement (gesture tuning, touch targets) · Qwik bundle analysis + optimization · IndexedDB read-only task cache activated
-
Phase 4 — Offline + Advanced (Future)
Full offline sync (ElectricSQLAdapter + real-time PostgreSQL sync) · Hall Chat conversational interface · Analytics / reporting dashboard · Data export (CSV, PDF) · Location map pins · Custom
task_typeper org · Per-subscription billing controls
SaaS Considerations
| Concern | MVP | Future |
|---|---|---|
| Data isolation | org_id FK on all tables, enforced at API layer | Encrypted per-org storage |
| File storage | S3 with /{org_id}/fieldforce/ prefix | Per-org quota enforcement |
| AI cost | Per-org tracking via ClawUI | Per-plan AI usage limits |
| Rate limiting | Per-org per-endpoint | Per-subscription tier |
| Feature flag | fieldforce: boolean per org (default: true) | Tiered feature unlocks |
| Audit trail | Activity + audit logs on all status changes | Compliance-grade audit (Phase 4) |
Resolved Open Questions
- Qwik vs Solid for mobile?
- Qwik — already in Astro app, consistent with existing platform
- One portal or two?
- Two surfaces (Panel + Mobile), same API, role controls access
- Offline in MVP?
- Architecture ready (
FieldforceStorageAdapterinterface + IndexedDB schema defined), no sync logic in MVP - Location format?
- Plain address string in MVP; lat/lng map pin Phase 2
- Task type?
task_type: 'service' | 'sales' | 'logistics' | string— open string allows custom types- Supervisor scope?
- Team-scoped. Admin/Owner sees all.
- Module activation?
- Default ON, feature flag per org — platform admin can disable
- AI routing?
- All AI calls through ClawUI middleware — no surface calls a provider directly
- Calendar integration?
- Tasks with
due_dateappear on existing/team/calendar; color-coded by priority; click navigates to task detail
Variants 1
Alternate renderings of the same source markdown. The canonical version above is what powers the archive and search; variants are kept side-by-side for comparison.
codex
Summary
Fieldforce Management adds a default-on, org-scoped dispatch and proof-of-work module for service, sales, and logistics teams. The core bet is one backend and permission model, with desktop Panel and Mobile Web optimized for different work modes, and ClawUI governing every AI call for cost, audit, provider routing, and BYOK.
Key Decisions
Two surfaces, one backend
Panel and Mobile Web expose the same capabilities through one org-scoped API.
Panel optimizes for dense management workflows: tables, filters, side-by-side views, bulk actions, audit review, and offset pagination. Mobile optimizes for field execution: touch targets, camera capture, signature capture, quick status changes, and cursor infinite scroll.
Rejected as implicit non-goal: a single universal UI. The spec states that role controls what a user sees, while surface controls how they see it.
Reuse the existing audit infrastructure
Fieldforce audit logs use the existing Go audit logging infrastructure, decorator pattern, and AuditHelper.
The business service stays focused on task behavior while the audited wrapper captures before/after snapshots and writes append-only logs. Audit errors are non-blocking and must not fail the user operation.
No bespoke audit write model is introduced. There are no application-level update or delete endpoints for audit logs.
Design mobile offline behind an adapter
Components must never call IndexedDB directly; they call FieldforceStorageAdapter.
The adapter keeps Phase 1 online-first implementation compatible with Phase 2 IndexedDB queues and a Phase 4 Electric SQL replacement without component rewrites.
Rejected: direct component-level IndexedDB usage, which would make the Electric SQL migration invasive.
Shared API, distinct UIs
- Backend
- One org-scoped Fieldforce API
- Panel
- Dense management workflows
- Mobile
- Touch-first execution workflows
Preserves one permission, audit, task, and AI model while allowing each client to fit its usage context.
Full offline-first MVP
- Phase 1
- Adapter and schema only
- Phase 2
- IndexedDB read cache and write queues
- Phase 4
- Electric SQL sync
The MVP keeps Panel always-online and avoids shipping sync logic before field usage validates the need.
Architecture
| Layer | Panel desktop | Mobile Web |
|---|---|---|
| Framework | SolidStart, existing panel app | Astro, existing platform app |
| Route root | /app/fieldforce/* | /fieldforce/* |
| Islands | Solid.js | Qwik |
| Forms | Solid Forms | Qwik forms |
| Client state | TanStack Query | TanStack Query |
| Client cache | None; always-online assumed | IndexedDB structure only in Phase 1; offline activation later |
| Styling | Tailwind + existing design system | Tailwind + existing design system |
| i18n | Paraglide: en, zh, ms | Paraglide: en, zh, ms |
- Backend
- Bun + Go, reusing existing auth, organizations, members, teams, and permissions.
- API root
/api/organizations/:org_id/fieldforce/...- AI layer
- All AI calls route through ClawUI middleware for cost tracking, audit logging, failover, and provider selection.
- Database
- PostgreSQL with new
ff_tasks,ff_activities,ff_attachments,ff_task_embeddings, andff_audit_logs.
Roles And Access
Existing organization roles get fieldforce-specific scopes. Supervisors are team-scoped; managers and admin/owner are org-wide; field team users operate on their own tasks and activity logs.
| Capability | Admin / Owner | Manager | Supervisor | Field Team |
|---|---|---|---|---|
| View all org tasks | Yes | Yes | No | No |
| View team tasks | Yes | Yes | Own team | No |
| View own tasks | Yes | Yes | Yes | Yes |
| Create tasks | Yes | Yes | Own team | No |
| Assign tasks | Yes | Yes | Own team members | No |
| Approve / reject completion | Yes | Yes | Yes | No |
| Log activities | No | No | No | Yes |
| View activity logs | Yes | Yes | Own team | Own |
| Access AI features | Yes | Yes | Yes | Voice log only |
| Manage fieldforce settings | Yes | No | No | No |
Data Model
Core Fieldforce tables
Tasks: ff_tasks
Task {
id: string
org_id: string
task_type: 'service' | 'sales' | 'logistics' | string
title: string
description: string
location: string?
due_date: Date
priority: 'low' | 'medium' | 'high'
status: TaskStatus
created_by: string
assigned_to: string[]
ai_checklist: string[]?
review_note: string?
created_at: Date
updated_at: Date
}
Activities: ff_activities
Activity {
id: string
org_id: string
task_id: string
performed_by: string
activity_type: 'note' | 'photo' | 'signature' | 'status_update' | 'checklist'
content: string
duration_minutes: number?
started_at: Date?
created_at: Date
}
Attachments: ff_attachments
Attachment {
id: string
activity_id: string
file_url: string
file_type: 'photo' | 'signature'
ai_score: number?
ai_flags: string[]?
uploaded_at: Date
}
AI, search, and audit tables
Task embeddings: ff_task_embeddings
TaskEmbedding {
id: string
task_id: string
org_id: string
embedding: vector(1536)
updated_at: Date
}
Org AI config: org_ai_configs
OrgAIConfig {
id: string
org_id: string
mode: 'platform' | 'byok' | 'trial' | 'disabled'
provider: 'anthropic' | 'openai' | 'google' | 'azure_openai' | null
api_key_encrypted: string?
model: string?
trial_credits_used: number
trial_credits_limit: number
created_at: Date
updated_at: Date
}
Audit logs: ff_audit_logs
AuditLog {
id: string
org_id: string
user_id: string
user_name: string
user_role: string
action: AuditAction
entity_type: 'FIELDFORCE_TASK'
entity_id: string
before: object?
after: object?
details: object?
ip_address: string?
user_agent: string?
session_id: string?
request_id: string?
created_at: Date
}
Captured audit events
| Action | Trigger | Before / after |
|---|---|---|
TASK_CREATED | Manager or supervisor creates task | None / task snapshot |
TASK_ASSIGNED | Task assigned or reassigned | Previous assignees / new assignees |
TASK_STATUS_CHANGED | Any status transition | Previous status / new status |
TASK_APPROVED | Manager or supervisor approves completion | completed / approved |
TASK_REJECTED | Manager or supervisor rejects completion | completed / needs_revision + review note |
TASK_RESUBMITTED | Field team re-submits after revision | needs_revision / completed |
TASK_CANCELLED | Task cancelled | Previous status / cancelled |
TASK_ESCALATED | Overdue auto-escalation | None / escalation target role |
TASK_PRIORITY_CHANGED | Priority updated | Previous priority / new priority |
TASK_DUE_DATE_CHANGED | Due date extended or changed | Previous due date / new due date |
// Wrap FieldforceService with audit decorator; business logic stays unchanged.
type AuditedFieldforceService struct {
inner *FieldforceService
auditHelper *AuditHelper
}
func (s *AuditedFieldforceService) ApproveTask(ctx context.Context, taskID string, note string) error {
task, _ := s.inner.taskRepo.FindByID(ctx, taskID)
before := task.Status
err := s.inner.ApproveTask(ctx, taskID, note)
s.auditHelper.LogTaskApproval(ctx, TaskAuditParams{
TaskID: taskID,
Before: before,
After: "approved",
Details: map[string]any{"review_note": note},
})
return err
}
Workflow And State
Task status flow
pendingTask is created and waiting to start.
in_progressField team starts execution and logs work.
completedField team submits the task for review.
- Review branch
Manager or supervisor moves the task to
approvedorneeds_revision. Revision returns toin_progressand may loop through completion again. cancelledAvailable from any non-terminal state for manager/admin only.
Overdue handling
- Detect overdue work
A scheduled job checks
due_date < nowandstatus NOT IN (completed, approved, cancelled). - Flag and notify
Add the visual overdue flag, then notify assigned field team and manager or supervisor by push and in-app notification.
- Escalate after inactivity
If a task remains overdue for more than 24 hours with no activity, escalate to the next role up: supervisor -> manager -> admin.
- Predict earlier
The AI
at-risk-tasksendpoint surfaces tasks likely to miss their deadline before they become overdue.
API Design
All routes are org-scoped. Every endpoint enforces req.user.org_id === req.params.org_id.
Task endpoints
POST /api/organizations/:org_id/fieldforce/tasks
GET /api/organizations/:org_id/fieldforce/tasks
GET /api/organizations/:org_id/fieldforce/tasks/:task_id
PATCH /api/organizations/:org_id/fieldforce/tasks/:task_id
DELETE /api/organizations/:org_id/fieldforce/tasks/:task_id
Activity and attachment endpoints
POST /api/organizations/:org_id/fieldforce/activities
GET /api/organizations/:org_id/fieldforce/activities
POST /api/organizations/:org_id/fieldforce/activities/:activity_id/attachments
AI endpoints
POST /api/organizations/:org_id/fieldforce/ai/parse-task
POST /api/organizations/:org_id/fieldforce/ai/suggest-assignees
POST /api/organizations/:org_id/fieldforce/ai/completion-summary
POST /api/organizations/:org_id/fieldforce/ai/transcribe-voice
GET /api/organizations/:org_id/fieldforce/ai/at-risk-tasks
Audit and AI config endpoints
GET /api/organizations/:org_id/fieldforce/audit
GET /api/organizations/:org_id/fieldforce/audit/tasks/:task_id
GET /api/organizations/:org_id/ai-config
PUT /api/organizations/:org_id/ai-config
DELETE /api/organizations/:org_id/ai-config/api-key
GET /api/admin/ai-config/defaults
PUT /api/admin/ai-config/defaults
Product Surfaces
Panel home
desktop- Manager/supervisor summary cards: total, pending, in progress, overdue, needs revision.
- Approval queue, AI at-risk tasks, and recent activity feed.
- Field team view shows today's own tasks, next due task, and quick inline status update.
Panel task management
desktop- Columns: title, type, assignees, priority, due date, status.
- Full-text search, filters, sort controls, offset pagination, and bulk actions.
- Overdue tasks get a red flag and row highlight.
Task detail
shared data- Metadata, assignees, map link, editable AI checklist, approval panel, and completion summary action.
- Audit trail tab shows all task events.
- Timeline merges
ff_activitiesandff_audit_logs.
Mobile task execution
touch-first- My tasks list, task cards, large tap targets, cursor infinite scroll.
- Filter chips: all, pending, in progress, overdue.
- Task detail includes AI checklist, visual timeline, and log activity bottom sheet.
Visual task timeline
| Event type | Visual treatment |
|---|---|
| Task created | Grey dot, "Created by [name]" |
| Task assigned | Blue dot, "Assigned to [names]" |
| Status changed | Yellow dot, status movement by actor |
| Note, photo, signature | Teal dot, expandable details with preview or thumbnail |
| Submitted for review | Purple dot, submitted by actor |
| Approved | Green dot, approver and review note |
| Rejected | Red dot, reviewer and feedback |
| Resubmitted | Orange dot, resubmitted by actor |
| Overdue escalated | Red dot, escalation target role |
| Cancelled | Grey dot, cancellation actor |
Mobile log activity bottom sheet
- Note
Textarea plus Qwik form submit.
- Photo
<input type="file" accept="image/*" capture="environment">followed by S3 upload. - Signature
Canvas signature pad implemented as a Qwik island.
- Voice
Record, transcribe with AI, confirm text, and save as note.
Notifications
Notifications use the existing push notification infrastructure, including PushNotificationToggle in the Astro app, plus the in-app notification feed.
| Trigger | Recipient | Channel |
|---|---|---|
| Task assigned | Assignee(s) | Push + in-app |
| Task status -> completed | Manager / Supervisor | Push + in-app |
| Task rejected | Assignee(s) | Push + in-app + feedback text |
| Task overdue | Assignee(s) + Manager | Push + in-app |
| Task reassigned | New assignee | Push + in-app |
| Task at risk | Manager / Supervisor | In-app |
| Task cancelled | Assignee(s) | In-app |
AI Model
AI access modes
| Mode | Who pays | AI available | Notes |
|---|---|---|---|
trial | Platform absorbs cost | Limited | Default for new orgs; capped by trial_credits_limit. |
platform | Org billed by platform | Full | Uses platform ClawUI key. |
byok | Org pays provider directly | Full | Org supplies its own API key; platform charges no AI fee. |
disabled | None | No | AI features hidden from org UI. |
- New organization
Starts in trial mode with limited free credits.
- Platform mode
Use platform key and platform default provider/model; charge org through the platform add-on.
- BYOK mode
Decrypt org API key server-side and route to the org's selected provider/model.
- Disabled mode
Return
403for AI calls and hide AI features from the org UI.
Supported providers
| Provider | Models | Embedding |
|---|---|---|
| Anthropic | claude-opus-4-7, claude-sonnet-4-6, claude-haiku-4-5 | Via third party |
| OpenAI | gpt-4o, gpt-4o-mini, o1 | text-embedding-3-large |
gemini-1.5-pro, gemini-1.5-flash | text-embedding-004 | |
| Azure OpenAI | Any deployed model | Any deployed embedding model |
AI feature roadmap
| Phase | Features |
|---|---|
| MVP | Natural language task creation, smart assignment, auto-categorization and priority, checklist generation, voice-to-text activity logging, completion summary, predictive delay detection. |
| Phase 2 AI | Photo analysis and quality score, smart checklist auto-tick, route optimization, anomaly detection, workload rebalancing, manager daily briefing. |
| Phase 3 AI | Conversational task interface through Hall Chat. |
- Org admin UI
- Panel -> Org Settings -> AI: mode, trial credits, API key entry/removal, BYOK provider/model.
- Platform admin UI
- Admin panel -> AI Defaults: default provider, model, trial credit limit per org, global AI enable/disable.
Search, Pagination, And Calendar
Semantic search flow
- Embed the query
User query is embedded through ClawUI, with the same org cost tracking as other AI calls.
- Search vectors
Run cosine similarity against
ff_task_embeddings, filtered byorg_idand role scope. - Fetch tasks
Return top-N task IDs and fetch full task records.
- Fallback
If embedding service is unavailable, use PostgreSQL
tsvectorfull-text search on title and description.
GET /fieldforce/tasks?q=broken+ac+unit&page=2&page_size=50&sort_by=due_date&sort_dir=asc
GET /fieldforce/tasks?q=broken+ac+unit&cursor=<opaque_token>&limit=20
- Panel pagination
- Offset pagination with configurable page size: 20, 50, or 100.
- Mobile pagination
- Cursor-based infinite scroll.
- Sort controls
due_date,priority,created_at,status,updated_at.- Calendar integration
- Tasks with
due_dateappear on/team/calendar; field team sees assigned tasks and managers see all tasks.
Offline Architecture
interface FieldforceStorageAdapter {
getTasks(orgId: string): Promise<Task[]>
getTask(taskId: string): Promise<Task | null>
cacheTasks(tasks: Task[]): Promise<void>
queueActivity(activity: PendingActivity): Promise<void>
queueAttachment(attachment: PendingAttachment): Promise<void>
getSyncQueue(): Promise<PendingSync[]>
clearSynced(ids: string[]): Promise<void>
}
class IndexedDBAdapter implements FieldforceStorageAdapter { ... }
class ElectricSQLAdapter implements FieldforceStorageAdapter { ... }
Offline migration path
- Phase 1
Define the adapter and IndexedDB schema only. Do not activate sync.
- Phase 2
Use
Dexie.jsstores for cached tasks, pending activities, attachments, and sync log. - Phase 4
Implement
ElectricSQLAdapter, swap the DI binding, and migrate IndexedDB data into SQLite on app load.
- IndexedDB stores
db.tasks,db.activities,db.attachments,db.sync_log.- Phase 2 reads
- Tasks are pre-cached from last fetch and readable offline.
- Phase 2 writes
- Activities are queued via adapter and synced when reconnected.
- Conflict model
- Last-write-wins for activity logs, which are append-only.
- PWA
- Use existing
InstallPrompt.astrofor add-to-home-screen.
Implementation Phases
-
Phase 1: Core dispatch + logging
Weeks 1-3. Ship schema, task/activity/audit APIs, core Panel and Mobile views, notifications for assigned/completed, feature flag, i18n, calendar integration, and inactive mobile storage schema.
-
Phase 2: AI + proof + approval
Weeks 4-6. Add ClawUI AI endpoints, photo upload, signature pad, approval/rejection workflow, additional audit writes, overdue escalation, and at-risk notifications.
-
Phase 3: Polish + advanced AI
Weeks 7-8. Add photo analysis, route optimization, anomaly detection, workload rebalancing, manager daily briefing, mobile UX tuning, bundle optimization, and read-only task cache.
-
Phase 4: Offline + advanced
Future. Add full mobile offline sync, Hall Chat, analytics, CSV/PDF export, billing controls, map pins, and custom org task types.
SaaS Considerations
| Concern | MVP | Future |
|---|---|---|
| Data isolation | org_id FK on all tables, enforced at API layer | Encrypted per-org storage |
| File storage | S3 with /{org_id}/fieldforce/ prefix | Per-org quota enforcement |
| AI cost | Per-org tracking via ClawUI | Per-plan AI usage limits |
| Rate limiting | Per-org per-endpoint | Per-subscription tier |
| Feature flag | fieldforce: boolean per org, default true | Tiered feature unlocks |
| Audit trail | Activity logs on all status changes | Compliance-grade audit in Phase 4 |
Resolved Questions
| Question | Decision |
|---|---|
| Qwik vs Solid for mobile | Qwik, because it is already in the Astro app. |
| One portal or two? | Two surfaces, same API; role controls access. |
| Offline in MVP? | Architecture ready with IndexedDB schema; no sync logic in MVP. |
| Location format? | Plain address string in MVP; lat/lng map pin in Phase 2. |
| Task type? | 'service' | 'sales' | 'logistics' | string. |
| Supervisor scope? | Team-scoped; admin/owner sees all. |
| Module activation? | Default on with per-org feature flag. |
| AI routing? | All AI calls go through ClawUI middleware. |
| Calendar? | Tasks with due_date appear on existing /team/calendar. |