ARC — Requirements Specification — REQ-ARC-01

ARC Freelance CRM
Requirements Specification

Derived directly from the ARC source application at E:/repos/arc (Authority Score -1, overrides all Nexus documentation per DOC_HIERARCHY v1.1). Every requirement is traceable to a specific source file and line range in the ARC repository. This document is the single source of truth for what ARC does and what NeXus must orchestrate or replicate.

Authority: AS -1 — This document derives requirements from live source code at E:/repos/arc. Where this document conflicts with any other Nexus document (including Tier 1), the source code wins. Every REQ cites file:line evidence.

Document ID
REQ-ARC-01
Version
2.0
Source
E:/repos/arc
Status
Active — AS -1

01 — System Overview

What ARC is and how it is structured

ARC is a freelance business management application. It is a four-container Docker Compose stack consisting of a Next.js 14 frontend, a FastAPI backend, a Python+Claude AI agent, and a PostgreSQL 16 database. The system manages the complete lifecycle of freelance client engagements: from initial email contact through project delivery, invoicing, and payment reconciliation.

ContainerTechnologyPortPurposeSource
frontendNext.js 14, TypeScript, Tailwind, React Query3000All user interaction. App Router. Server + client components.arc/frontend/
backendPython 3.12, FastAPI, SQLAlchemy 2.0, Alembic, Pydantic v28000REST API, business logic, Gmail/Calendar OAuth, PDF generation, bank CSV parsingarc/backend/
agentPython 3.12, FastAPI, Anthropic Claude SDK, APScheduler8001 (internal)AI functions, scheduled cron jobs, MemPalace persistent memoryarc/agent/
dbPostgreSQL 165432 (internal)Relational persistent store. UTF-8, Polish character support.Docker image postgres:16-alpine
ModuleNavigation LabelRouteFunctional Spec ID
AuthenticationLogin/loginUS-AUTH-01 to 03
DashboardDashboard/FUNC-DASH-01 to 05
Client ManagementClients/clientsFUNC-CLI-01 to 04
Project ManagementProjects/projectsFUNC-PRJ-01 to 05
Email IntegrationEmails/emailsFUNC-EML-01 to 06
CalendarCalendar/calendarFUNC-CAL-01 to 06
Offer GenerationOffers/offersFUNC-OFR-01 to 06
Invoice ManagementInvoices/invoicesFUNC-INV-01 to 05
Finance / BankFinance/bankFUNC-FIN-01 to 05
Settings & BackfillSettings/settingsUS-SET-01 to 03, US-BKF-01 to 08
Source: frontend/src/components/Sidebar.tsx#L21-L30 (navItems array), frontend/src/app/layout.tsx, backend/app/routers/__init__.py#L1-L13

02 — Data Model

All entities, their fields, and relationships

User
id UUID PK
username string
password_hash bcrypt
language pl|en
created_at timestamp
arc/backend/app/models/user.py
Client
id UUID PK
user_id UUID FK
name string(255)
notes text
is_active bool
is_deleted bool soft-delete
created_at / updated_at
arc/backend/app/models/client.py
ClientEmail
id UUID PK
client_id UUID FK
email_pattern string
is_wildcard bool
is_active bool
created_at
arc/backend/app/models/client.py
Project
id UUID PK
user_id / client_id UUIDs
name / description
status enum: LEAD | TALKS_INITIALIZED | OFFER_SENT | OFFER_ACCEPTED | IN_PROGRESS | REVIEW | COMPLETED | ON_HOLD | CANCELLED
waiting_on ME | CLIENT | NONE
priority_score int
estimated_value int cents
currency PLN default
started_at / estimated_completion
is_deleted soft-delete
arc/backend/app/models/project.py
Action
id UUID PK
project_id UUID FK
description text
action_type enum
priority_score 1-100
due_date timestamp
is_completed bool
created_by user|agent
arc/backend/app/models/action.py
Email
id UUID PK
gmail_id unique
thread_id
client_id / project_id
from_address / to_address
subject / body_text
sentiment enum
direction sent|received
date timestamp
is_indexed bool
arc/backend/app/models/email.py
CalendarEvent
id UUID PK
project_id UUID FK nullable
gcal_event_id string
title
event_type deadline|meeting|reminder
start_at / end_at
description
synced_at
arc/backend/app/models/calendar_event.py
Offer & OfferStep
Offer: id, project_id, status, total_amount, currency, version
OfferStep: id, offer_id, step_number, name, description, hours, hourly_rate, total
OfferVersion: id, offer_id, snapshot_json, version_number
OfferTemplate: id, name, html_content, is_default
arc/backend/app/models/offer.py
Invoice & InvoiceLineItem
Invoice: id, project_id, offer_id nullable, invoice_number, status, total_amount, due_date
Status enum: draft|sent|paid|overdue|cancelled
InvoiceLineItem: id, invoice_id, description, quantity, unit_price, total
InvoiceTemplate: id, name, html_content, is_default
arc/backend/app/models/invoice.py
BankTransaction
id UUID PK
import_batch_id
date
amount / currency
description / reference
matched_invoice_id nullable
match_status matched|unmatched|pending
arc/backend/app/models/bank_transaction.py
AgentMemory
id UUID PK
category string (e.g. "prompts", "style")
key string
value text
updated_at
Prompt overrides stored here by key
arc/backend/app/models/agent_memory.py
BackfillJob & DiscoveredAddress
BackfillJob: id, status, range_months, started_at, completed_at, email_count
DiscoveredAddress: id, job_id, email_address, domain, suggested_client_name, confidence_score, action
arc/backend/app/models/backfill.py

03 — Functional Requirements

Per module, derived from arc/docs/01_FUNCTIONAL_SPECIFICATION.md

3.1 Authentication

REQ-AUTH-01 Session-Based Authentication arc/backend/app/routers/auth.py • US-AUTH-01

The system authenticates via username and password. On success the backend creates a session record in the database and returns an httpOnly, Secure, SameSite=Strict cookie containing the session ID. All subsequent API requests from the frontend include this cookie automatically. The backend middleware validates the session on every protected endpoint. Session expiry is 24 hours.

  • Passwords stored as bcrypt hashes in PostgreSQL
  • No JWT tokens — session record approach with httpOnly cookie
  • Middleware rejects unauthenticated requests with HTTP 401
  • Frontend redirects to /login on 401 response
REQ-AUTH-02 Password Change arc/backend/app/routers/auth.py • US-AUTH-02

Authenticated users can change their password from the Settings page. The endpoint requires the current password for verification before accepting the new password. Password complexity rules apply (minimum 8 characters).

3.2 Dashboard

REQ-DASH-01 Priority Action Feed frontend/src/app/page.tsx#L54-L88 • backend/app/routers/dashboard.py#L21-L48

The dashboard displays a scored and ranked list of action items across all active projects. The feed shows the highest-priority items by default ("Top priorities"). A toggle switches to "All actions" showing the complete list.

  • Action types displayed: Response Pending (Client), Response Pending (Me), Follow-up, Invoice Due, Project Start, Offer Pending
  • Each card: client name, project name, action description, priority score (1-100), due date
  • Clicking a card navigates to the relevant project page
  • Scores computed by the agent's action_scorer cron (every 1 hour)
REQ-DASH-02 Project Status Grid frontend/src/app/page.tsx#L90-L115 • backend/app/routers/dashboard.py#L53-L73

Below the action feed, all active projects are shown as cards with color-coded status badges. Nine status states exist: Lead, Talks Initialized, Offer Sent, Offer Accepted, In Progress, Review, Completed, On Hold, Cancelled.

REQ-DASH-03 Calendar Widget, Email Activity, Financial Summary frontend/src/app/page.tsx#L117-L195 (emails+finance) • frontend/src/app/page.tsx#L197-L230 (upcoming 7 days) • backend/app/routers/dashboard.py#L75-L106
  • Mini calendar widget: upcoming 7 days of calendar events
  • Recent email activity: last 10 indexed emails with sender, subject, and sentiment indicator
  • Financial summary: outstanding invoices total, received this month, pending payments

3.3 Client Management

REQ-CLI-01 Client CRUD frontend/src/app/clients/page.tsx#L19-L120 • frontend/src/app/clients/[id]/page.tsx#L1-L200 • backend/app/routers/clients.py

Create, read, list, and edit clients. No hard-delete — soft-delete via is_deleted flag with deletion reason. Client list is searchable and sortable by: name, status, last contact date, active project count.

  • Fields: name (required), notes, email addresses, wildcard patterns
  • Email patterns support wildcards, e.g. *@company.com matches all addresses from that domain
  • Multiple email addresses per client (ClientEmail table, each can be active or inactive)
  • Client detail page tabs: Overview, Projects, Emails, Configuration

3.4 Project Management

REQ-PRJ-01 Project Lifecycle frontend/src/app/projects/page.tsx#L34-L44 (statuses array) • frontend/src/lib/types.ts#L46-L56 (ProjectStatus type) • backend/app/models/project.py

Projects belong to a client. A project tracks the complete engagement lifecycle across 9 statuses:

StatusMeaning
leadInitial contact, no commitment yet
talks_initializedConversation started, interest confirmed
offer_sentFormal offer delivered to client
offer_acceptedClient accepted the offer
in_progressActive work underway
reviewWork delivered, awaiting client review
completedProject closed, all deliverables accepted
on_holdTemporarily paused
cancelledProject cancelled by either party
REQ-PRJ-02 AI Action Items & Priority Scoring frontend/src/app/projects/[id]/page.tsx#L387-L425 (actions tab) • agent/app/agents.py#L140-L147 (score_actions) • backend/app/routers/actions.py

The agent automatically creates action items for each project after analyzing email threads. Each action is scored 1-100. The scoring formula considers:

  • Days since last contact with the client
  • Email sentiment trend (negative/urgent = higher priority)
  • Estimated project value
  • Time remaining to deadline
  • Waiting_on status (waiting on client = lower urgency than waiting on me)
REQ-PRJ-03 Project-Level Calendar Events frontend/src/app/projects/[id]/page.tsx#L290-L385 (deadlines + events section) • backend/app/routers/calendar.py

Each project has a "Deadlines & Events" section displaying calendar events filtered to that project. Users can create, edit, and delete events directly from the project page. Events created here appear on the global Calendar page and sync to Google Calendar. Deletion from the project page removes the local DB record but does not retroactively delete the Google Calendar entry.

3.5 Email Integration

REQ-EML-01 Gmail Indexing Via OAuth agent/app/google_sync.py (19167 bytes, sync_emails function) • frontend/src/app/emails/page.tsx#L42-L50 (sync button) • backend/app/routers/emails.py

The agent's email_sync cron job runs every 15 minutes (configurable via environment). It authenticates via Google OAuth2 and fetches all emails since the last sync timestamp. Emails are matched to clients using their configured email addresses and wildcard patterns.

  • Indexed emails receive the "UV-Indexed" label in Gmail
  • Original emails are NEVER deleted or moved in Gmail
  • Both received and sent emails are indexed (direction flag)
  • Deduplication by gmail_id field
  • Only plain text body stored — no binary attachments
REQ-EML-02 AI Email Draft Generation frontend/src/app/emails/[id]/page.tsx#L56-L67 (handleDraft) • agent/app/agents.py#L75-L100 (suggest_email_reply) • backend/app/routers/agent.py#L68-L131

From any email thread or project action, the user can request an AI-generated reply draft. The agent calls Claude with the full thread context plus a writing style profile learned from the user's sent emails. The draft is editable before sending. Sending uses the Gmail API directly via the backend.

REQ-EML-03 Email Sentiment Analysis agent/app/agents.py#L56-L60 (analyze_email) • frontend/src/app/emails/page.tsx#L36-L41 (sentimentColor map)

The sentiment_analyzer cron (every 30 minutes) runs Claude on all newly indexed emails. Each email receives a sentiment label: positive, neutral, negative, or urgent. Sentiment is displayed as a badge in thread view and email list.

3.6 Calendar Integration

REQ-CAL-01 FullCalendar View with Google Sync frontend/src/app/calendar/page.tsx#L16-L18 (dynamic import FullCalendar) • frontend/src/components/CalendarView.tsx • agent/app/google_sync.py (sync_calendar_events)

Calendar page uses FullCalendar.js. Events are color-coded by type. The calendar_sync cron (every 30 minutes) pulls events FROM Google Calendar into the local database. Events created in the ARC app are pushed immediately to Google Calendar.

REQ-CAL-02 AI Email-to-Calendar Parsing agent/app/agents.py#L169-L195 (parse_calendar_events) • frontend/src/app/calendar/page.tsx#L39-L48 (handleSyncFromEmails) • frontend/src/components/AgentLogModal.tsx

The calendar_from_emails cron (every 60 minutes) passes recent email threads to Claude using the PARSE_CALENDAR_SYSTEM prompt. Claude identifies meeting dates, deadlines, and reminders mentioned in conversation and returns structured events to add and event titles to remove. Events are created/deleted in both Google Calendar and the local database. Manually triggered from the "Parse from emails" button on the calendar page. No duplicates created (deduplication by title+date).

3.7 Offer Generation

REQ-OFR-01 Tabular Offer Editor with AI Suggestions frontend/src/app/offers/new/page.tsx#L19-L60 (Step interface + form state) • agent/app/agents.py#L103-L120 (suggest_offer_steps) • backend/app/routers/agent.py#L134-L157

Offers are created in a tabular form. Each row represents one project phase/step. Columns: Step number, Step name, Description, Hours (decimal), Hourly Rate (PLN), Row Total (auto-calculated). Maximum 15 rows. Grand total auto-updates.

"AI Suggest Steps" calls the agent's suggest_offer_steps endpoint. The agent analyzes the project description and historical offer steps to produce 5-7 suggested steps. The table is fully editable after suggestion.

REQ-OFR-02 HTML Template Editor & PDF Export arc/docs/01_FUNCTIONAL_SPECIFICATION.md FUNC-OFR-03 to 04 • US-OFR-05 to 07

Offer templates are stored as raw HTML/CSS in the database. The template editor provides a code editor (monospace) with live preview. Template variables use double-brace syntax: {{client_name}}, {{project_name}}, {{steps_table}}, {{total}}, {{date}}. PDF generation uses WeasyPrint on the backend. Multiple templates supported with a default flag.

REQ-OFR-03 Offer Versioning arc/docs/01_FUNCTIONAL_SPECIFICATION.md FUNC-OFR-06 • US-OFR-09

Each save creates a new version snapshot. All previous versions are stored in the OfferVersions table as JSON snapshots. Any old version can be viewed. The active offer reflects the most recent version.

3.8 Invoice Management

REQ-INV-01 Invoice Creation from Offer arc/docs/01_FUNCTIONAL_SPECIFICATION.md FUNC-INV-01 • US-INV-01 to 02

Invoices can be created standalone or from a linked offer. When created from an offer, offer steps are mapped to invoice line items with quantities and unit prices pre-filled. Invoice numbers are auto-generated. All fields are editable after auto-fill.

REQ-INV-02 AI Invoice Analysis arc/docs/01_FUNCTIONAL_SPECIFICATION.md FUNC-INV-02 • US-INV-03 • arc/agent/app/agents.py::analyze_invoice

An AI analysis side panel is available on the invoice detail page. The agent compares the invoice line items against the original offer and project timeline to surface insights like: "Hours exceeded estimate by X%", "Consider charging for scope changes", "Client historically pays within N days".

REQ-INV-03 Invoice Status Tracking arc/backend/app/models/invoice.py • FUNC-INV-05

Invoice status follows the lifecycle: Draft → Sent → Paid / Overdue / Cancelled. Paid status is set automatically when a matching bank transaction is found by the bank matching agent. Overdue is set when due_date passes without payment.

3.9 Finance & Bank Import

REQ-FIN-01 Bank CSV Import frontend/src/app/bank/page.tsx#L25-L36 (handleImport with FormData) • backend/app/routers/bank.py

Users upload bank statement CSV files on the Finance/Import page. Supported formats: Wise, PKO BP, mBank. The backend parses the CSV using pandas, previews the rows, and on confirmation writes them to the BankTransaction table. Import is append-only — no modification or deletion of transactions after import.

REQ-FIN-02 AI Transaction Matching agent/app/agents.py#L123-L134 (match_transactions) • frontend/src/lib/types.ts#L128-L137 (BankTransaction interface)

After every CSV import, the agent's match_transactions function runs. It matches each BankTransaction to an open Invoice by: reference field substring match, amount within a configurable tolerance (default 1%), and currency match. Matched invoices are automatically marked Paid. Unmatched transactions are flagged for manual review.

REQ-FIN-03 Monthly Analysis & Tax Reports arc/docs/01_FUNCTIONAL_SPECIFICATION.md FUNC-FIN-04 to 05 • US-FIN-04 to 05

Monthly analysis view shows bar chart (income trend), pie chart (client distribution), and tabular breakdown. Tax report view supports configurable period selection, Polish tax reporting format (lump sum ryczalt or general taxation), and export to CSV and/or PDF in Polish or English.

3.10 AI Agent System

REQ-AGT-01 Cron Scheduler arc/docs/01_FUNCTIONAL_SPECIFICATION.md FUNC-AGT • arc/agent/app/scheduler.py
Job NameIntervalFunctionSource
email_syncConfigurable envsync_emails() — Gmail OAuth fetch + labelarc/agent/app/google_sync.py
sentiment_analyzer30 minanalyze_email() on all new emails via Claudearc/agent/app/agents.py
action_scoringConfigurable envscore_actions() — recalculate all action prioritiesarc/agent/app/agents.py
calendar_sync30 minsync_calendar_events() — pull FROM Google Calendararc/agent/app/google_sync.py
calendar_from_emails60 minsync_calendar_from_emails() — AI parses emails, writes GCal eventsarc/agent/app/google_sync.py
REQ-AGT-02 AI Functions (Synchronous API Endpoints) arc/agent/app/main.py • FUNC-AGT
EndpointFunctionCalled By
POST /analyze-emailSentiment analysis on a single email textsentiment_analyzer cron
POST /suggest-emailGenerate reply draft matching user styleFrontend email thread reply button
POST /suggest-offer-stepsPropose 5-7 offer steps from project context + historyFrontend offer editor "AI Suggest"
POST /analyze-invoiceCompare invoice to offer + project, return insightsFrontend invoice detail panel
POST /match-transactionsMatch bank transactions to open invoicesBank import completion trigger
POST /score-actionsRecalculate priority scores for all pending actionsaction_scoring cron
POST /infer-statusInfer project status from email thread analysisstatus_inferrer cron (every 2hr)
POST /sync/calendar-from-emailsAI email-to-calendar event parsing and syncFrontend button + cron
REQ-AGT-03 MemPalace & Prompt Override System arc/agent/app/agents.py • arc/backend/app/models/agent_memory.py • US-PRM-01 to 03

All prompts have hardcoded defaults in arc/agent/app/prompts.py. The _get_prompt(key, default) helper queries the AgentMemory table first (category='prompts'). Custom values stored there override defaults without requiring a restart. The Settings → AI Prompts UI lists all prompts in collapsible cards with monospace editors. Users can save custom values or reset to defaults.

3.11 Gmail Backfill & Auto-Discovery

REQ-BKF-01 Historical Gmail Backfill arc/agent/app/backfill.py • US-BKF-01 to 07

Users can initiate a historical Gmail import from Settings. Configurable ranges: 1, 3, 6, or 12 months. Only one backfill job runs at a time. Re-running the same range skips already-imported emails (deduplication by gmail_id). Only plain text body stored, no attachments.

  • BackfillJob record tracks status, range, start/completion time, email count
  • Progress visible via polling the job status endpoint
  • Completed jobs show an "Emails →" link to /emails in the history table
REQ-BKF-02 Two-Pass Address Assessment arc/agent/app/backfill.py • US-BKF-02 to 04

After backfill completes, all email addresses are assessed for business relevance. Two-pass approach: First, heuristic filter eliminates obvious non-business addresses (noreply, newsletter, service notification patterns). Second, remaining candidates are sent to Claude in a single batch for business relevance scoring. Confidence scores (0.0-1.0) determine how addresses are grouped in the review UI.

Review UI groups addresses by suggested client name. User can edit the suggested name, check/uncheck for inclusion, or mark as "Merge with existing client". Batch client creation creates all approved clients and retroactively links historical emails.

04 — Non-Functional Requirements

Security, data integrity, internationalization

REQ-NFR-01 No-Delete Policy arc/docs/02_ARCHITECTURE.md section 6.3 • US-EML-08, US-FIN-06

No DELETE endpoints exist in the backend REST API for business data. All "removal" operations use soft-delete via is_active or is_deleted flags. Bank transactions are append-only. Emails are read-only indexed copies. Audit log captures all state changes.

REQ-NFR-02 Internationalization (PL/EN) arc/frontend/src/app/globals.css • arc/docs/02_ARCHITECTURE.md frontented stack • US-I18N-01 to 02

All UI text is available in Polish and English via i18next. Language selection persists per user. Tax reports and financial exports respect the language setting. Polish character support required in PostgreSQL (UTF-8 encoding).

REQ-NFR-03 Security: OAuth2 & Credential Storage arc/docs/02_ARCHITECTURE.md section 6
CredentialStorage
User passwordsbcrypt hash, PostgreSQL
Google OAuth2 tokensEncrypted in PostgreSQL, refresh token rotated
Claude API keyDocker environment variable / .env file (never in code)
Session secretsDocker environment variable

05 — API Surface Summary

All backend REST endpoints derived from arc/backend/app/routers/

RouterPrefixKey EndpointsSource File
auth/authPOST /login, POST /logout, POST /change-passwordarc/backend/app/routers/auth.py
clients/clientsGET / POST / GET :id / PATCH :id, GET :id/emails POST :id/emailsarc/backend/app/routers/clients.py
projects/projectsGET / POST / GET :id / PATCH :id / GET :id/actions / POST :id/actionsarc/backend/app/routers/projects.py
emails/emailsGET / GET :id / POST /sync (triggers agent sync)arc/backend/app/routers/emails.py
calendar/calendarGET /?project_id / POST / PATCH :id / DELETE :idarc/backend/app/routers/calendar.py
offers/offersGET / POST / GET :id / PATCH :id / POST :id/pdf / GET :id/versionsarc/backend/app/routers/offers.py
invoices/invoicesGET / POST / GET :id / PATCH :id / POST :id/pdf / PATCH :id/statusarc/backend/app/routers/invoices.py
bank/bankPOST /import / GET /transactions / GET /payments / GET /monthly / GET /taxarc/backend/app/routers/bank.py
dashboard/dashboardGET /actions / GET /projects / GET /emails / GET /financials / GET /calendararc/backend/app/routers/dashboard.py
templates/templatesGET / POST / PATCH :id (offer and invoice templates)arc/backend/app/routers/templates.py
agent/agentPOST /sync-emails / POST /parse-calendar / GET /logarc/backend/app/routers/agent.py (proxies to agent container)