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.
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.
| Container | Technology | Port | Purpose | Source |
|---|---|---|---|---|
| frontend | Next.js 14, TypeScript, Tailwind, React Query | 3000 | All user interaction. App Router. Server + client components. | arc/frontend/ |
| backend | Python 3.12, FastAPI, SQLAlchemy 2.0, Alembic, Pydantic v2 | 8000 | REST API, business logic, Gmail/Calendar OAuth, PDF generation, bank CSV parsing | arc/backend/ |
| agent | Python 3.12, FastAPI, Anthropic Claude SDK, APScheduler | 8001 (internal) | AI functions, scheduled cron jobs, MemPalace persistent memory | arc/agent/ |
| db | PostgreSQL 16 | 5432 (internal) | Relational persistent store. UTF-8, Polish character support. | Docker image postgres:16-alpine |
| Module | Navigation Label | Route | Functional Spec ID |
|---|---|---|---|
| Authentication | Login | /login | US-AUTH-01 to 03 |
| Dashboard | Dashboard | / | FUNC-DASH-01 to 05 |
| Client Management | Clients | /clients | FUNC-CLI-01 to 04 |
| Project Management | Projects | /projects | FUNC-PRJ-01 to 05 |
| Email Integration | Emails | /emails | FUNC-EML-01 to 06 |
| Calendar | Calendar | /calendar | FUNC-CAL-01 to 06 |
| Offer Generation | Offers | /offers | FUNC-OFR-01 to 06 |
| Invoice Management | Invoices | /invoices | FUNC-INV-01 to 05 |
| Finance / Bank | Finance | /bank | FUNC-FIN-01 to 05 |
| Settings & Backfill | Settings | /settings | US-SET-01 to 03, US-BKF-01 to 08 |
All entities, their fields, and relationships
id UUID PKusername stringpassword_hash bcryptlanguage pl|encreated_at timestamp
id UUID PKuser_id UUID FKname string(255)notes textis_active boolis_deleted bool soft-deletecreated_at / updated_at
id UUID PKclient_id UUID FKemail_pattern stringis_wildcard boolis_active boolcreated_at
id UUID PKuser_id / client_id UUIDsname / descriptionstatus enum: LEAD | TALKS_INITIALIZED | OFFER_SENT | OFFER_ACCEPTED | IN_PROGRESS | REVIEW | COMPLETED | ON_HOLD | CANCELLEDwaiting_on ME | CLIENT | NONEpriority_score intestimated_value int centscurrency PLN defaultstarted_at / estimated_completionis_deleted soft-delete
id UUID PKproject_id UUID FKdescription textaction_type enumpriority_score 1-100due_date timestampis_completed boolcreated_by user|agent
id UUID PKgmail_id uniquethread_idclient_id / project_idfrom_address / to_addresssubject / body_textsentiment enumdirection sent|receiveddate timestampis_indexed bool
id UUID PKproject_id UUID FK nullablegcal_event_id stringtitleevent_type deadline|meeting|reminderstart_at / end_atdescriptionsynced_at
id, project_id, status, total_amount, currency, versionid, offer_id, step_number, name, description, hours, hourly_rate, totalid, offer_id, snapshot_json, version_numberid, name, html_content, is_default
id, project_id, offer_id nullable, invoice_number, status, total_amount, due_dateid, invoice_id, description, quantity, unit_price, totalid, name, html_content, is_default
id UUID PKimport_batch_iddateamount / currencydescription / referencematched_invoice_id nullablematch_status matched|unmatched|pending
id UUID PKcategory string (e.g. "prompts", "style")key stringvalue textupdated_atid, status, range_months, started_at, completed_at, email_countid, job_id, email_address, domain, suggested_client_name, confidence_score, action
Per module, derived from arc/docs/01_FUNCTIONAL_SPECIFICATION.md
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.
/login on 401 responseAuthenticated 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).
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.
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.
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.
*@company.com matches all addresses from that domainProjects belong to a client. A project tracks the complete engagement lifecycle across 9 statuses:
| Status | Meaning |
|---|---|
| lead | Initial contact, no commitment yet |
| talks_initialized | Conversation started, interest confirmed |
| offer_sent | Formal offer delivered to client |
| offer_accepted | Client accepted the offer |
| in_progress | Active work underway |
| review | Work delivered, awaiting client review |
| completed | Project closed, all deliverables accepted |
| on_hold | Temporarily paused |
| cancelled | Project cancelled by either party |
The agent automatically creates action items for each project after analyzing email threads. Each action is scored 1-100. The scoring formula considers:
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.
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.
gmail_id fieldFrom 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.
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.
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.
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).
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.
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.
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.
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.
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".
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.
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.
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.
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.
| Job Name | Interval | Function | Source |
|---|---|---|---|
| email_sync | Configurable env | sync_emails() — Gmail OAuth fetch + label | arc/agent/app/google_sync.py |
| sentiment_analyzer | 30 min | analyze_email() on all new emails via Claude | arc/agent/app/agents.py |
| action_scoring | Configurable env | score_actions() — recalculate all action priorities | arc/agent/app/agents.py |
| calendar_sync | 30 min | sync_calendar_events() — pull FROM Google Calendar | arc/agent/app/google_sync.py |
| calendar_from_emails | 60 min | sync_calendar_from_emails() — AI parses emails, writes GCal events | arc/agent/app/google_sync.py |
| Endpoint | Function | Called By |
|---|---|---|
| POST /analyze-email | Sentiment analysis on a single email text | sentiment_analyzer cron |
| POST /suggest-email | Generate reply draft matching user style | Frontend email thread reply button |
| POST /suggest-offer-steps | Propose 5-7 offer steps from project context + history | Frontend offer editor "AI Suggest" |
| POST /analyze-invoice | Compare invoice to offer + project, return insights | Frontend invoice detail panel |
| POST /match-transactions | Match bank transactions to open invoices | Bank import completion trigger |
| POST /score-actions | Recalculate priority scores for all pending actions | action_scoring cron |
| POST /infer-status | Infer project status from email thread analysis | status_inferrer cron (every 2hr) |
| POST /sync/calendar-from-emails | AI email-to-calendar event parsing and sync | Frontend button + cron |
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.
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.
/emails in the history tableAfter 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.
Security, data integrity, internationalization
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.
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).
| Credential | Storage |
|---|---|
| User passwords | bcrypt hash, PostgreSQL |
| Google OAuth2 tokens | Encrypted in PostgreSQL, refresh token rotated |
| Claude API key | Docker environment variable / .env file (never in code) |
| Session secrets | Docker environment variable |
All backend REST endpoints derived from arc/backend/app/routers/
| Router | Prefix | Key Endpoints | Source File |
|---|---|---|---|
| auth | /auth | POST /login, POST /logout, POST /change-password | arc/backend/app/routers/auth.py |
| clients | /clients | GET / POST / GET :id / PATCH :id, GET :id/emails POST :id/emails | arc/backend/app/routers/clients.py |
| projects | /projects | GET / POST / GET :id / PATCH :id / GET :id/actions / POST :id/actions | arc/backend/app/routers/projects.py |
| emails | /emails | GET / GET :id / POST /sync (triggers agent sync) | arc/backend/app/routers/emails.py |
| calendar | /calendar | GET /?project_id / POST / PATCH :id / DELETE :id | arc/backend/app/routers/calendar.py |
| offers | /offers | GET / POST / GET :id / PATCH :id / POST :id/pdf / GET :id/versions | arc/backend/app/routers/offers.py |
| invoices | /invoices | GET / POST / GET :id / PATCH :id / POST :id/pdf / PATCH :id/status | arc/backend/app/routers/invoices.py |
| bank | /bank | POST /import / GET /transactions / GET /payments / GET /monthly / GET /tax | arc/backend/app/routers/bank.py |
| dashboard | /dashboard | GET /actions / GET /projects / GET /emails / GET /financials / GET /calendar | arc/backend/app/routers/dashboard.py |
| templates | /templates | GET / POST / PATCH :id (offer and invoice templates) | arc/backend/app/routers/templates.py |
| agent | /agent | POST /sync-emails / POST /parse-calendar / GET /log | arc/backend/app/routers/agent.py (proxies to agent container) |