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

View source markdown ↗ generated by claude · diagrams mermaid

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.

  • Core loop Assign → Execute → Prove → Verify → Close
  • Verticals Service · Sales · Logistics/Delivery
  • Surfaces Panel (SolidStart) + Mobile Web (Astro + Qwik)
  • AI routing ClawUI gateway — Anthropic, OpenAI, Google, Azure
  • Phases 4 phases (Core → AI + Approval → Polish → Offline)
  • Author Wayne Cheah · 2026-05-15

System Architecture

System architecture — two surfaces, one backend, one AI gateway, one database.
System architecture — two surfaces, one backend, one AI gateway, one database.

Frontend Stack

LayerPanel (Desktop)Mobile Web
FrameworkSolidStart (existing panel app)Astro (existing platform app)
IslandsSolid.jsQwik
Client stateTanStack QueryTanStack Query
Client cacheNone — always online assumedIndexedDB via FieldforceStorageAdapter (Phase 2)
PaginationOffset (20 / 50 / 100 per page)Cursor-based infinite scroll
i18nParaglide (en, zh, ms)Paraglide (en, zh, ms)
StylingTailwind + existing design systemTailwind + 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
CapabilityAdmin / OwnerManagerSupervisorField 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_tasks
id, 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_note
ff_activities
id, org_id, task_id, performed_by, activity_type ('note'|'photo'|'signature'|'status_update'|'checklist'), content, duration_minutes?, started_at?
ff_attachments
id, activity_id, file_url (S3 org-prefixed), file_type ('photo'|'signature'), ai_score 0–100?, ai_flags[]?ai_score/flags Phase 3
ff_task_embeddings
id, task_id, org_id, embedding vector(1536), updated_at — pgvector; generated from title + description + location + task_type; kept in separate table to keep ff_tasks lean
ff_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
    
Task status state machine. approved and cancelled are terminal. needs_revision loops back to in_progress — field team re-logs activity then re-submits.

AI Config Modes

ModeWho paysAI availableNotes
trialPlatform absorbs cost✅ LimitedDefault for new orgs. Capped at trial_credits_limit calls.
platformOrg billed by platform✅ FullOrg subscribes to AI add-on; uses platform's ClawUI key.
byokOrg pays provider directly✅ FullOrg supplies own API key. Platform charges no AI fee.
disabledAI features hidden from org UI entirely.

Audit Log Events

ActionTriggerbefore / after
TASK_CREATEDManager/Supervisor creates task— / task snapshot
TASK_ASSIGNEDTask assigned or reassignedprevious assignees / new assignees
TASK_STATUS_CHANGEDAny status transitionprevious status / new status
TASK_APPROVEDManager/Supervisor approvescompleted / approved
TASK_REJECTEDManager/Supervisor rejectscompleted / needs_revision + review_note
TASK_RESUBMITTEDField team re-submits after revisionneeds_revision / completed
TASK_CANCELLEDTask cancelledprevious status / cancelled
TASK_ESCALATEDOverdue auto-escalation triggered— / escalation target role
TASK_PRIORITY_CHANGEDPriority updatedprevious priority / new priority
TASK_DUE_DATE_CHANGEDDue date changedprevious 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

Manager / Supervisor view
  • 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
Field Team view
  • 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 typeVisual 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

Field Team landing
  • My tasks (today's + overdue first)
  • Quick status toggle per task (tap → confirm dialog)
  • Overdue badge immediately visible
Manager / Supervisor on mobile
  • 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

Log Activity
Note
Photo
Signature
Voice
Activity note
Duration (min)
Started at
Submit
Mobile Log Activity bottom sheet — Note tab shown. Photo tab: <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

ClawUI AI gateway routing by org AI mode. All four paths are centralized — no surface calls a provider SDK directly.
ClawUI AI gateway routing by org AI mode. All four paths are centralized — no surface calls a provider SDK directly.

MVP AI Features (Phase 2)

FeatureSurfaceDescription
Natural language task creationPanel + MobileParse free-text/voice → structured task form (title, type, priority, due date, location, assignees)
Smart assignment recommendationPanel + MobileSuggest top assignees by workload + past performance; proximity when lat/lng added Phase 2
Auto-categorization & priorityPanel + MobileInfer task_type + priority from description
AI checklist generationPanel + MobileGenerate task checklist from task_type + description; manager edits before publishing
Voice-to-text activity loggingMobileRecord voice note → transcribe → confirm text → save as activity
AI completion summaryPanelGenerate professional report from activities + photos
Predictive delay detectionPanelScore in-progress tasks by delay risk; surface to manager before deadline

Phase 3 & Phase 4 AI Features

FeaturePhaseDescription
Photo analysis & quality score3Analyze completion photos, score quality 0–100, flag issues
Smart checklist auto-tick3Detect completed items from photo/notes automatically
Route optimization3Suggest optimal task visit order for multi-task days
Anomaly detection3Flag fast completions, missing photos, wrong location patterns
Workload rebalancing3Detect imbalance, suggest specific task reassignments
Manager daily briefing3Morning AI summary: tasks due today, at-risk, pending approvals
Conversational task interface4Hall Chat integration — manage tasks via natural language chat

Supported AI Providers

ProviderModelsEmbedding
Anthropicclaude-opus-4-7, claude-sonnet-4-6, claude-haiku-4-5via third-party
OpenAIgpt-4o, gpt-4o-mini, o1text-embedding-3-large
Googlegemini-1.5-pro, gemini-1.5-flashtext-embedding-004
Azure OpenAIany deployed modelany 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

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.

TriggerRecipientChannel
Task assignedAssignee(s)Push + in-app
Task status → completedManager / SupervisorPush + in-app
Task rejected (needs_revision)Assignee(s)Push + in-app + feedback text
Task overdueAssignee(s) + ManagerPush + in-app
Task reassignedNew assigneePush + in-app
Task at risk (AI prediction)Manager / SupervisorIn-app only
Task cancelledAssignee(s)In-app only

Overdue Task Handling

Background job / cron checks: due_date < now AND status NOT IN (completed, approved, cancelled).

  1. Flag as overdue

    Add visual overdue flag — red badge in task list and task detail.

  2. Notify

    Push + in-app to assigned field team + manager/supervisor.

  3. Auto-escalate

    If overdue > 24h with no activity: escalate to next role up — supervisor → manager → admin. Logged as TASK_ESCALATED in audit trail.

  4. Surface proactively (AI)

    GET /ai/at-risk-tasks surfaces 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 { … }
  1. Phase 1 — Interface only

    FieldforceStorageAdapter interface defined. Components coded against it. No IndexedDB activated.

  2. Phase 2 — IndexedDB read

    IndexedDBAdapter (Dexie.js) activated. db.tasks populated (read-only cache). db.activities + db.attachments pending queues. db.sync_log for reconnect sync. Conflict resolution: last-write-wins on append-only activity logs.

  3. 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

  1. 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 · FieldforceStorageAdapter interface defined (IndexedDB schema inactive)

  2. 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

  3. 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

  4. 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_type per org · Per-subscription billing controls

SaaS Considerations

ConcernMVPFuture
Data isolationorg_id FK on all tables, enforced at API layerEncrypted per-org storage
File storageS3 with /{org_id}/fieldforce/ prefixPer-org quota enforcement
AI costPer-org tracking via ClawUIPer-plan AI usage limits
Rate limitingPer-org per-endpointPer-subscription tier
Feature flagfieldforce: boolean per org (default: true)Tiered feature unlocks
Audit trailActivity + audit logs on all status changesCompliance-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 (FieldforceStorageAdapter interface + 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_date appear 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 generated by 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.

  • Core loopAssign -> Execute -> Prove -> Verify -> Close
  • SurfacesSolidStart Panel + Astro/Qwik Mobile Web
  • TenancyAll data and routes scoped by org_id
  • AI accesstrial, platform, BYOK, disabled
  • Offline stanceAdapter defined now; sync deferred
  • StatusApproved for implementation planning

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.

Selected

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.

Deferred

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

Fieldforce Management architecture. The diagram shows Panel desktop and Mobile Web as two role-adaptive clients feeding into a shared org-scoped Fieldforce API. The backend routes AI work through ClawUI for cost, audit, provider mode, and failover, and persists tenant data to PostgreSQL tables including tasks, activities, attachments, audit logs, embeddings, and org AI config.
Two surfaces share one backend, AI gateway, and PostgreSQL tenant data model.
LayerPanel desktopMobile Web
FrameworkSolidStart, existing panel appAstro, existing platform app
Route root/app/fieldforce/*/fieldforce/*
IslandsSolid.jsQwik
FormsSolid FormsQwik forms
Client stateTanStack QueryTanStack Query
Client cacheNone; always-online assumedIndexedDB structure only in Phase 1; offline activation later
StylingTailwind + existing design systemTailwind + existing design system
i18nParaglide: en, zh, msParaglide: 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, and ff_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.

CapabilityAdmin / OwnerManagerSupervisorField Team
View all org tasksYesYesNoNo
View team tasksYesYesOwn teamNo
View own tasksYesYesYesYes
Create tasksYesYesOwn teamNo
Assign tasksYesYesOwn team membersNo
Approve / reject completionYesYesYesNo
Log activitiesNoNoNoYes
View activity logsYesYesOwn teamOwn
Access AI featuresYesYesYesVoice log only
Manage fieldforce settingsYesNoNoNo

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

ActionTriggerBefore / after
TASK_CREATEDManager or supervisor creates taskNone / task snapshot
TASK_ASSIGNEDTask assigned or reassignedPrevious assignees / new assignees
TASK_STATUS_CHANGEDAny status transitionPrevious status / new status
TASK_APPROVEDManager or supervisor approves completioncompleted / approved
TASK_REJECTEDManager or supervisor rejects completioncompleted / needs_revision + review note
TASK_RESUBMITTEDField team re-submits after revisionneeds_revision / completed
TASK_CANCELLEDTask cancelledPrevious status / cancelled
TASK_ESCALATEDOverdue auto-escalationNone / escalation target role
TASK_PRIORITY_CHANGEDPriority updatedPrevious priority / new priority
TASK_DUE_DATE_CHANGEDDue date extended or changedPrevious 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

  1. pending

    Task is created and waiting to start.

  2. in_progress

    Field team starts execution and logs work.

  3. completed

    Field team submits the task for review.

  4. Review branch

    Manager or supervisor moves the task to approved or needs_revision. Revision returns to in_progress and may loop through completion again.

  5. cancelled

    Available from any non-terminal state for manager/admin only.

Overdue handling

  1. Detect overdue work

    A scheduled job checks due_date < now and status NOT IN (completed, approved, cancelled).

  2. Flag and notify

    Add the visual overdue flag, then notify assigned field team and manager or supervisor by push and in-app notification.

  3. 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.

  4. Predict earlier

    The AI at-risk-tasks endpoint 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_activities and ff_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 typeVisual treatment
Task createdGrey dot, "Created by [name]"
Task assignedBlue dot, "Assigned to [names]"
Status changedYellow dot, status movement by actor
Note, photo, signatureTeal dot, expandable details with preview or thumbnail
Submitted for reviewPurple dot, submitted by actor
ApprovedGreen dot, approver and review note
RejectedRed dot, reviewer and feedback
ResubmittedOrange dot, resubmitted by actor
Overdue escalatedRed dot, escalation target role
CancelledGrey dot, cancellation actor

Mobile log activity bottom sheet

  1. Note

    Textarea plus Qwik form submit.

  2. Photo

    <input type="file" accept="image/*" capture="environment"> followed by S3 upload.

  3. Signature

    Canvas signature pad implemented as a Qwik island.

  4. 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.

TriggerRecipientChannel
Task assignedAssignee(s)Push + in-app
Task status -> completedManager / SupervisorPush + in-app
Task rejectedAssignee(s)Push + in-app + feedback text
Task overdueAssignee(s) + ManagerPush + in-app
Task reassignedNew assigneePush + in-app
Task at riskManager / SupervisorIn-app
Task cancelledAssignee(s)In-app

AI Model

AI access modes

ModeWho paysAI availableNotes
trialPlatform absorbs costLimitedDefault for new orgs; capped by trial_credits_limit.
platformOrg billed by platformFullUses platform ClawUI key.
byokOrg pays provider directlyFullOrg supplies its own API key; platform charges no AI fee.
disabledNoneNoAI features hidden from org UI.
  1. New organization

    Starts in trial mode with limited free credits.

  2. Platform mode

    Use platform key and platform default provider/model; charge org through the platform add-on.

  3. BYOK mode

    Decrypt org API key server-side and route to the org's selected provider/model.

  4. Disabled mode

    Return 403 for AI calls and hide AI features from the org UI.

Supported providers

ProviderModelsEmbedding
Anthropicclaude-opus-4-7, claude-sonnet-4-6, claude-haiku-4-5Via third party
OpenAIgpt-4o, gpt-4o-mini, o1text-embedding-3-large
Googlegemini-1.5-pro, gemini-1.5-flashtext-embedding-004
Azure OpenAIAny deployed modelAny deployed embedding model

AI feature roadmap

PhaseFeatures
MVPNatural language task creation, smart assignment, auto-categorization and priority, checklist generation, voice-to-text activity logging, completion summary, predictive delay detection.
Phase 2 AIPhoto analysis and quality score, smart checklist auto-tick, route optimization, anomaly detection, workload rebalancing, manager daily briefing.
Phase 3 AIConversational 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

  1. Embed the query

    User query is embedded through ClawUI, with the same org cost tracking as other AI calls.

  2. Search vectors

    Run cosine similarity against ff_task_embeddings, filtered by org_id and role scope.

  3. Fetch tasks

    Return top-N task IDs and fetch full task records.

  4. Fallback

    If embedding service is unavailable, use PostgreSQL tsvector full-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_date appear 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

  1. Phase 1

    Define the adapter and IndexedDB schema only. Do not activate sync.

  2. Phase 2

    Use Dexie.js stores for cached tasks, pending activities, attachments, and sync log.

  3. 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.astro for add-to-home-screen.

Implementation Phases

  1. 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.

  2. 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.

  3. 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.

  4. 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

ConcernMVPFuture
Data isolationorg_id FK on all tables, enforced at API layerEncrypted per-org storage
File storageS3 with /{org_id}/fieldforce/ prefixPer-org quota enforcement
AI costPer-org tracking via ClawUIPer-plan AI usage limits
Rate limitingPer-org per-endpointPer-subscription tier
Feature flagfieldforce: boolean per org, default trueTiered feature unlocks
Audit trailActivity logs on all status changesCompliance-grade audit in Phase 4

Resolved Questions

QuestionDecision
Qwik vs Solid for mobileQwik, 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.