База даних — SQLite Schema

Загальна інформація

ER Diagram (Phase 65)

erDiagram
    users ||--o{ projects : owns
    users ||--|| account_settings : has
    users ||--o{ recovery_keys : holds
    users ||--o{ subscriptions : pays
    users ||--o{ invites : creates
    users ||--o{ chat_messages : sends
    users ||--o{ trial_credits : claims
    users ||--o{ activity_log : generates

    projects ||--o{ project_phases : tracks
    projects ||--o{ pinned_notes : pins
    projects ||--o{ chat_messages : contains
    projects ||--o{ timeline_events : records
    projects ||--o{ github_links : links
    projects ||--o{ skill_benchmarks : runs
    projects ||--o{ marketplace_cache : caches

    github_links ||--o{ github_events : receives

    invites }o--|| users : "consumed by"
    subscriptions ||--o{ stripe_events : "logs idempotency"

    users {
        TEXT id PK
        TEXT email UK
        TEXT password_hash
        TEXT role "admin · user"
        TEXT google_id UK
        TEXT github_id UK
        INTEGER email_verified
        INTEGER two_factor_enabled
        TEXT last_login
    }

    projects {
        INTEGER id PK
        TEXT technical_name
        TEXT owner_id FK
        TEXT display_name
        INTEGER is_archived
    }

    chat_messages {
        INTEGER id PK
        TEXT project_name
        TEXT worker_id
        TEXT role "user · assistant · system"
        TEXT content "AES-256-GCM if encrypted=1"
        INTEGER encrypted
        TEXT timestamp
    }

    subscriptions {
        TEXT user_id PK
        TEXT plan "free · min · max · beta"
        TEXT stripe_customer_id
        TEXT status
    }

    invites {
        TEXT code PK "arc-XXXX-XXXX"
        TEXT created_by FK
        TEXT used_by FK
        TEXT parent_invite_code
        TEXT status "active · used · revoked"
    }

    recovery_keys {
        INTEGER id PK
        TEXT user_id FK
        TEXT encrypted_key
        TEXT key_hint "first 4 chars"
    }

    trial_credits {
        TEXT email PK
        INTEGER credits_used
        TEXT first_used_at
    }

30+ tables across 35 migrations. Auth + projects + workspace data + intelligence + billing + beta gating + context-export audit. All FK constraints enforced.

Таблиці

users — Користувачі

Колонка Тип Опис
id TEXT PK UUID
email TEXT UNIQUE Email (обов'язковий)
password_hash TEXT Хеш пароля (bcrypt)
role TEXT 'admin' або 'user'
name TEXT Ім'я
avatar_url TEXT URL аватара
google_id TEXT UNIQUE Google OAuth ID
github_id TEXT UNIQUE GitHub OAuth ID
email_verified INTEGER 0/1
two_factor_enabled INTEGER 0/1
totp_secret TEXT TOTP секрет (2FA)
last_login TEXT ISO 8601
created_at TEXT ISO 8601
password_version INTEGER Лічильник змін пароля (migration 035). Включений у JWT як pv; crmAuthMiddleware відхиляє старі токени після зміни пароля.

projects — Проєкти

Колонка Тип Опис
id INTEGER PK Auto
technical_name TEXT Технічне ім'я (унікальне per owner)
owner_id TEXT FK→users Власник
display_name TEXT Відображуване ім'я
description TEXT Опис
color TEXT Колір (hex)
icon TEXT Іконка
is_archived INTEGER 0/1
sort_order INTEGER Порядок (default 999)
project_protocol TEXT Протокол проєкту
created_at, updated_at TEXT Timestamps

UNIQUE(technical_name, owner_id)

account_settings — Налаштування акаунту

Колонка Тип Опис
user_id TEXT PK FK→users
anthropic_key TEXT Anthropic API ключ
openai_key TEXT OpenAI API ключ
name TEXT Ім'я
role TEXT Роль
auto_harvest INTEGER Автозбір навичок 0/1

chat_messages — Історія чатів

Колонка Тип Опис
id INTEGER PK Auto
project_name TEXT Проєкт
worker_id TEXT Воркер
role TEXT 'user', 'assistant', 'system'
content TEXT Текст повідомлення (зашифровано AES-256-GCM якщо encrypted=1)
encrypted INTEGER 0=plaintext, 1=vault-encrypted (Phase 45.3)
attachments TEXT JSON масив
timestamp TEXT ISO 8601
metadata TEXT JSON об'єкт

INDEX(project_name, worker_id, timestamp DESC)

recovery_keys — Ключі відновлення (Phase 45.4)

Колонка Тип Опис
id INTEGER PK Auto
user_id TEXT FK→users Власник
encrypted_key TEXT Зашифрований master key
key_hint TEXT Підказка (перші 4 символи)
created_at TEXT ISO 8601
used_at TEXT Коли використано
revoked INTEGER 0=активний, 1=відкликаний

INDEX(user_id)

github_links — Привязки GitHub repo (Phase 49.3)

Колонка Тип Опис
id INTEGER PK Auto
project_name TEXT Arc OS project
owner TEXT GitHub repo owner
repo TEXT GitHub repo name
webhook_secret TEXT UNIQUE 32-byte hex для HMAC-SHA256 validation
created_at TEXT ISO 8601
created_by TEXT FK→users Хто створив link

UNIQUE(project_name, owner, repo) — multi-repo per project allowed. INDEX(project_name), INDEX(webhook_secret)

github_events — Лог GitHub-подій (Phase 49.3.1)

Колонка Тип Опис
id INTEGER PK Auto
link_id INTEGER FK→github_links CASCADE on link delete
project_name TEXT Arc OS project (denormalized для query speed)
event_type TEXT push, pull_request, workflow_run, issues
action TEXT sub-action (opened/closed/success/failure)
summary TEXT Pre-formatted display string
url TEXT GitHub deep link
actor TEXT GitHub username
created_at TEXT ISO 8601

INDEX(project_name, created_at DESC), INDEX(link_id)

skills_global — Глобальні навички

Колонка Тип Опис
id INTEGER PK Auto
name TEXT UNIQUE Назва
description TEXT Опис
category TEXT Категорія (default 'general')
content TEXT Вміст навички (markdown)
triggers TEXT JSON масив тригерів
keywords TEXT JSON масив ключових слів
eval_rules TEXT JSON масив правил
tool_code_ts TEXT TypeScript реалізація
version INTEGER Версія (auto-increment)
status TEXT 'active', 'draft', 'deprecated', 'archived'
owner_project TEXT NULL=global/marketplace; project name=видимий тільки власнику (migration 034)

skills_project_forks — Форки навичок

Колонка Тип Опис
id INTEGER PK Auto
project_name TEXT Проєкт
skill_id INTEGER FK→skills_global Батьківська навичка (CASCADE)
content TEXT Кастомізований вміст
triggers, keywords, eval_rules, tool_code_ts TEXT Перевизначення
version INTEGER Версія форку

UNIQUE(project_name, skill_id)

skill_evolution_logs — Історія змін навичок

Колонка Тип Опис
id INTEGER PK
skill_id INTEGER FK CASCADE
project_name TEXT Проєкт (nullable)
action TEXT 'created', 'modified', 'applied', 'reverted'
diff_summary TEXT Опис змін
author TEXT Автор (default 'system')
metadata TEXT JSON

skill_update_requests — PR навичок (Sage)

Колонка Тип Опис
id INTEGER PK
skill_id INTEGER FK CASCADE
proposed_by TEXT 'sage' (default)
status TEXT 'pending', 'approved', 'rejected', 'applied'
current_content TEXT Поточна версія
proposed_content TEXT Запропонована версія
reason TEXT Причина зміни

skill_benchmarks — A/B тести навичок

Колонка Тип Опис
id INTEGER PK
request_id INTEGER FK→skill_update_requests CASCADE
test_scenario TEXT Сценарій тесту
old_output, new_output TEXT Результати
score_old, score_new REAL Оцінки
judgment_reason TEXT Обґрунтування

pinned_notes — Закріплені нотатки

Колонка Тип Опис
id INTEGER PK
project_name TEXT Проєкт
worker_id TEXT Воркер
title TEXT Заголовок
body TEXT Текст
source_message_id INTEGER Посилання на chat_messages

project_phases — Фази Roadmap

Колонка Тип Опис
id INTEGER PK
project_name TEXT Проєкт
phase_id INTEGER Номер фази
phase_title TEXT Назва
status TEXT 'PLANNED', 'ACTIVE', 'COMPLETED', 'CANCELLED'
progress REAL 0.0–1.0
deadline TEXT ISO 8601

UNIQUE(project_name, phase_id)

activity_log — Журнал активності

Колонка Тип Опис
id INTEGER PK
project_name TEXT Проєкт
actor TEXT Воркер або користувач
event_type TEXT Тип події
title TEXT Опис
metadata TEXT JSON
created_at TEXT ISO 8601

INDEX(created_at DESC), INDEX(project_name, created_at DESC), INDEX(event_type, created_at DESC), INDEX(actor, created_at DESC)

system_configs — Системні налаштування

Колонка Тип Опис
key TEXT PK Ключ
value TEXT Значення
description TEXT Опис

marketplace_analysis_cache — Кеш маркетплейсу

Кеш аналізу сумісності навичок з маркетплейсу.

ephemeral_tokens — Короткоживучі токени (issue #27)

Persistent store для one-time токенів OAuth state, password reset, email verification. До міграції 022 ці токени жили в in-memory Map і губилися при кожному рестарті master, ламаючи Google/GitHub login (invalid_state).

Колонка Тип Опис
token TEXT PK 32-байтний hex (randomBytes(32))
type TEXT NOT NULL oauth_state | password_reset | email_verification
payload TEXT NOT NULL JSON: {provider} для oauth_state, {email} для решти
expires_at INTEGER NOT NULL ms epoch — TTL: 10 хв (oauth) / 30 хв (reset) / 24 год (verify)

INDEX(type), INDEX(expires_at). Один-разове використання: consume() в одній транзакції читає payload і видаляє рядок.

project_issues — Issues per project (issue #53, Phase 53.14)

SQLite SSOT для issues замість per-project issues/issues.json. Файл лишається на диску як derived export (mirror), gitignored — DB авторитативна.

Колонка Тип Опис
project_name TEXT NOT NULL name з registry; складова PK з id
id INTEGER NOT NULL 1-based per-project sequence (max+1 на insert)
title TEXT NOT NULL заголовок issue
body TEXT NOT NULL DEFAULT '' опис, markdown
priority TEXT NOT NULL DEFAULT 'P2' P0 | P1 | P2 | P3
labels TEXT NOT NULL DEFAULT '[]' JSON array of strings
status TEXT NOT NULL DEFAULT 'open' draft | open | in_progress | blocked | deferred | closed (SQL default 'open' зберігається — нові issues форсяться у draft на app-рівні)
assignee TEXT NULL worker id що взяв issue (migration 036)
created_by TEXT NOT NULL DEFAULT 'legacy' worker id, хто створив issue (migration 046 / #327) — для audit (хто, коли, на кого)
created_at TEXT NOT NULL ISO timestamp
updated_at TEXT NOT NULL ISO timestamp
closed_at TEXT NULL ISO timestamp при status=closed
activity TEXT NOT NULL DEFAULT '[]' JSON array {ts, type, author, text} — append-only audit

PK (project_name, id) — кожен проєкт має свій незалежний id-простір. INDEX (project_name, status) — найчастіший фільтр (open only). Operations live in issueQueries (shared/db.ts): list/get/nextId/insert/upsert/replaceAll/bulkImport. replaceAll — атомарна транзакція; bulkImportINSERT OR IGNORE для idempotent re-seed.

Migration 036 (#187, 2026-05-23): ALTER TABLE project_issues ADD COLUMN assignee TEXT. Нові статуси (in_progress, blocked, deferred) валідуються на рівні додатку (IssueStatus union в shared/db.ts), не CHECK constraint (SQLite не дозволяє добавляти constraint через ALTER TABLE).

Migration 046 (#327, 2026-06-03): ALTER TABLE project_issues ADD COLUMN created_by TEXT NOT NULL DEFAULT 'legacy'. Lifecycle розширено новим статусом draft. handleCreateIssue форсить status='draft' коли assignee=null (інакше одразу 'open'), вимагає worker_id (body або auth fallback). handleUpdateIssue auto-promote draft → open при першому призначенні assignee та reject close для never-assigned issue (FR-ISS-101/102/103). Pre-existing rows backfill через DEFAULT як 'legacy'.

onboarding_progress — Post-wizard checklist state (issue #56, Phase 54.1)

Per-user derived cache над activity_log event stream (SSOT). UI читає одним запитом замість агрегації по подіях. Replayable: dismiss/replay не змінюють state, лише dismissed_at.

Колонка Тип Опис
chat_id TEXT PRIMARY KEY user id (registry chat id)
state TEXT NOT NULL DEFAULT '{}' JSON {workers, cli, skill, bot, issue}completed | skipped (відсутні ключі = pending)
completed_count INTEGER NOT NULL DEFAULT 0 derived cache count of completed steps (0–5)
started_at TEXT NOT NULL DEFAULT (datetime('now')) створення рядка (перша подія/dismiss/replay)
completed_at TEXT NULL ISO timestamp коли всі 5 steps completed
dismissed_at TEXT NULL ISO timestamp коли користувач закрив checklist
source TEXT NOT NULL DEFAULT 'web' web | cli — для funnel attribution (#58 arc tour)
updated_at TEXT NOT NULL DEFAULT (datetime('now')) mutation timestamp

INDEX на completed_at для funnel-аналітики (#61 Phase 54.6). Operations live in onboardingQueries (shared/db.ts): getProgress/recordEvent/dismiss/replay. recordEvent ідемпотентний на (chat_id, step, status) — повторний ідентичний виклик повертає changed=false і не пише в activity_log. Whitelist: 5 steps × 2 statuses (completed, skipped). skipped НЕ інкрементує completed_count. Транзиція skipped → completed додає до count. Усі мутації емітують події в activity_log з event_type LIKE 'onboarding_%' — справжній SSOT для funnel-метрик; колонки таблиці — derived cache.

platform_audit_log — Super-admin secret rotation trail (Phase 57, Sentinel #103)

Append-only журнал mutation'ів на vault platform-secrets через Platform Settings UI. SSOT для post-incident forensics ("який admin rotated Anthropic key о 03:14 UTC?"). Vault.json сам цього не пише — value-only.

Колонка Тип Опис
id INTEGER PRIMARY KEY AUTOINCREMENT temporal order навіть на clock skew
ts TEXT NOT NULL DEFAULT (datetime('now')) server-side UTC; attacker не може backdate
user_chat_id TEXT NOT NULL admin who acted
user_email TEXT NULL snapshot з users table на момент дії
action TEXT NOT NULL list | view | rotate | test | restart
key_name TEXT NOT NULL vault entry name (allowlist у platform.ts); * для list-all
ip TEXT NULL from CF-Connecting-IP / X-Real-IP / XFF tail
result TEXT NOT NULL success або fail:<reason>
user_agent TEXT NULL UA рядок, truncated 200 chars

INDEXES: idx_platform_audit_ts (recent across keys), idx_platform_audit_user (per-admin trail), idx_platform_audit_key (rotation history per key). Append-only invariant: no UPDATE/DELETE handlers; UI exposes read-only recent() + lastRotated() через platformAuditQueries у shared/db.ts. Кожна дія (success+fail) пише row.

auth_events — End-user authentication event log (Migration 027, issue #130)

Append-only журнал auth-подій для security auditing та incident investigation. Окрема від platform_audit_log (admin-only vault) — ця таблиця покриває end-user auth flow з IP.

Колонка Тип Опис
id INTEGER PRIMARY KEY AUTOINCREMENT temporal order
ts TEXT NOT NULL DEFAULT (datetime('now')) server-side UTC
event TEXT NOT NULL signup | login | oauth_login | magic_link | device_code_approve
provider TEXT NOT NULL DEFAULT 'password' password | google | github | magic_link | device_code
user_id TEXT NULL NULL для невдалих спроб де user не ідентифікований
email TEXT NULL email використаний у спробі
ip TEXT NOT NULL DEFAULT 'unknown' CF-Connecting-IP → X-Real-IP → XFF last segment
user_agent TEXT NULL UA рядок
result TEXT NOT NULL success | failed | rate_limited | invalid_invite | already_registered | 2fa_required | invalid_token
meta TEXT NULL JSON extra context (напр. {isNew: true} для OAuth)

INDEXES: idx_auth_events_ts (newest first), idx_auth_events_user (per-user history), idx_auth_events_ip (per-IP threat detection), idx_auth_events_event (event+result filter). authEventQueries.insert/recent у shared/db.ts. Логується у master-bot/routes/auth.ts (register/login/oauth/magic-link) та master-bot/routes/cli.ts (device_code_approve).

Міграції

# Назва Фаза Що додає
001 initial_schema Phase 40 users, projects, account_settings
002 project_protocol Phase 40.7 projects.project_protocol
003 chat_messages Phase 40.10 chat_messages
004 skill_system Phase 41 skills_global, skills_project_forks, skill_evolution_logs, skill_update_requests
005 skill_benchmarks Phase 40.11d skill_benchmarks
006 system_configs system_configs + FIGMA_CACHE_ROOT
007 marketplace_cache Phase 40.12.1 marketplace_analysis_cache
008 harvester Phase 40.13 +is_suggestion, +source_query, +auto_harvest
009 pinned_notes Phase 41.8 pinned_notes
010 project_phases Phase 44 project_phases
011 activity_log Phase 44.1 activity_log
012 performance_indexes Perf Audit +idx_activity_log_actor, +idx_chat_msg_role_ts
013 timeline_timestamps Phase 47 +timestamp на worker повідомленнях
014 timeline_events Phase 47 timeline_events таблиця
015 e2ee_chat Phase 45.3 +encrypted колонка на chat_messages
016 recovery_keys Phase 45.4 recovery_keys таблиця
017 github_links Phase 49.3 github_links таблиця (webhook bindings)
018 github_events Phase 49.3.1 github_events таблиця (event log для UI feed)
019 trial_credits Phase 50.1 +users.trial_granted_at, +projects.trial_mode, +projects.trial_tokens_remaining
020 subscriptions Phase 51 subscriptions table (plan, stripe_customer_id, status, feature_flags) + stripe_events idempotency log
021 invites Phase 52.1 invites table (closed beta enrollment) — code, created_by, used_by, parent_invite_code, status, note
022 ephemeral_tokens issue #27 ephemeral_tokens таблиця — persistent OAuth state / password reset / email verification, замість in-memory Map (фікс invalid_state на рестарті master)
023 project_issues Phase 53.14 / issue #53 project_issues таблиця (composite PK project_name+id, JSON labels/activity) — переносить issues storage з issues/issues.json у SQLite SSOT, JSON стає derived mirror; ліквідовує text-merge drift на deploy
024 context_exports Phase 56.4 + 56.5 / issues #100 + #101 export_audit_log (id PK, project_name, owner_id, exported_at, sections JSON, findings_critical/high/medium/low INT, bytes) + export_preferences (project_name PK, always_include_emails BOOL, auto_redact_critical BOOL DEFAULT 1, notify_on_export BOOL DEFAULT 0, updated_at) — за-проєктна audit history для Project Context Export + per-project policy toggles; alert при ≥3 expоrts/24h коли notify_on_export = true
025 onboarding_progress Phase 54.1 / issue #56 onboarding_progress (chat_id PK, JSON state per 5 steps, completed_count derived, started_at/completed_at/dismissed_at, source web|cli) — derived cache над activity_log event stream; SSOT = події event_type LIKE 'onboarding_%'; recordEvent ідемпотентний, dismiss/replay non-destructive
026 platform_audit_log Phase 57 / Sentinel #103 platform_audit_log (id PK AUTOINCREMENT, ts, user_chat_id, user_email, action, key_name, ip, result, user_agent) — append-only audit trail для super-admin Platform Settings UI mutation'ів на vault secrets. 3 indexes: idx_platform_audit_ts/user/key. No UPDATE/DELETE — read-only через platformAuditQueries.recent + lastRotated
027 auth_events issue #130 auth_events (id PK AUTOINCREMENT, ts, event, provider, user_id, email, ip, user_agent, result, meta JSON) — append-only IP + auth event log. Events: signup/login/oauth_login/magic_link/device_code_approve. Results: success/failed/rate_limited/invalid_invite/already_registered/2fa_required/invalid_token. 4 indexes: ts/user_id/ip/event+result
028 managed_containers Phase 60 #135/#136 managed_containers (id TEXT PK (docker container name), user_id, status (provisioning|ready|paused|suspended|deleted), server_ip, internal_port INT, claude_authed INT, github_authed INT, created_at, last_active) — 1-per-user Docker container registry for Standard Cloud. managedContainerQueries.findByUser/insert/updateStatus/setAuthFlag/touch/listActive у shared/db.ts. Endpoints: POST /api/crm/cloud/provision, GET /status, POST /deprovision у shared/routes/cloud.ts
029 cloud_waitlist Phase 60 #134 cloud_waitlist (id INT PK AUTOINCREMENT, user_id TEXT UNIQUE, email, position INT, status (waiting|invited|activated|declined), notes, joined_at, invited_at, activated_at) — Early Access waitlist for Standard Cloud.
030 translation_feedback Phase 59.4 #122 translation_feedback (id INT PK AUTOINCREMENT, locale, message_id, original, current_translation, suggestion, user_id, status (pending|accepted|rejected), created_at, reviewed_at).
031 token_usage_log Phase 63 #148 token_usage_log (id INT PK AUTOINCREMENT, project_name, owner_id, worker_id, input_tokens, output_tokens, cache_tokens, total_tokens, created_at INT) — Per-request Claude token usage. Populated by claude-runner.ts via /api/internal/usage/log.
032 arc_help_usage Phase 61 #147 arc_help_usage (user_id, date PK composite) — Daily rate-limit counter for Arc Help (30 msg/day).
033 arc_help_messages Phase 61 #153 arc_help_messages (id PK AUTOINCREMENT, user_id, role, text, sources JSON, created_at) — Persistent Arc Help chat history per user (last 60).
034 skills_owner_project #157 ALTER TABLE skills_global ADD COLUMN owner_project TEXT — NULL=global, non-null=owned by project.
035 password_version Phase 64 #174 ALTER TABLE users ADD COLUMN password_version INTEGER DEFAULT 0 — incremented on password change; JWT pv claim validated by crmAuthMiddleware to invalidate old tokens.
036 issue_assignee #187 / 2026-05-23 ALTER TABLE project_issues ADD COLUMN assignee TEXT — nullable worker id. Enables arc issue take <id> + in_progress/blocked/deferred extended statuses (validated at app level).
046 issue_draft_and_created_by #327 / 2026-06-03 ALTER TABLE project_issues ADD COLUMN created_by TEXT NOT NULL DEFAULT 'legacy' — audit field "хто створив". Lifecycle += draft (app-level union). handleCreateIssue форсить draft без assignee; handleUpdateIssue auto-promote draft→open при assign + reject close never-assigned (FR-ISS-101/102/103).
037 waitlist #197 / 2026-05-25 waitlist (id INT PK AUTOINCREMENT, email TEXT UNIQUE, message, status DEFAULT 'pending', created_at, updated_at) — early access request form on login page.
038 plata_billing Phase 65 #202 / 2026-05-26 Adds Plata by mono columns to subscriptions: plata_card_token TEXT, plata_wallet_id TEXT, plata_masked_pan TEXT, next_billing_date TEXT, billing_failures INTEGER DEFAULT 0. Also creates plata_events idempotency table (invoice_id PK, status, processed_at).
039 skill_project_grants #201 / 2026-05-26 skill_project_grants (skill_id INT FK→skills_global.id CASCADE, project_name TEXT, granted_at, PK composite) — many-to-many skill access across projects.
040 drop_stripe #205 / 2026-05-26 DROP stripe_customer_id column + DROP TABLE stripe_events. NB: stripe_subscription_id column survived this migration because SQLite refuses DROP COLUMN on UNIQUE-constrained columns (silently caught by try/catch). See migration 041.
041 rebuild_subscriptions #205 / 2026-05-26 Follow-up to 040 — rebuilds subscriptions table without the orphan stripe_subscription_id column via CREATE TABLE … AS pattern. Idempotent (skipped when column already absent).
042 totp_backup_codes ALTER TABLE users ADD COLUMN totp_backup_codes TEXT — TOTP recovery codes.
043 worker_avatars Phase D #304 / 2026-05-30 worker_avatars (id PK AUTOINCREMENT, project_name, worker_id, file_path, mime_type, created_at; UNIQUE(project_name, worker_id)) — metadata store for uploaded worker avatar images. Files saved to data/worker-avatars/. Serves via GET /api/crm/projects/:name/workers/:id/avatar.
044 worker_templates Phase I #304 / 2026-05-30 worker_templates (id PK AUTOINCREMENT, owner_id, name, description, config JSON, is_public, created_at; UNIQUE(owner_id, name)) — user-saved worker templates. Endpoints: GET/POST /api/crm/workers/templates, DELETE /api/crm/workers/templates/:id.
045 workers_runtime_state Phase F #306 / 2026-05-31 workers_runtime_state (project_name, worker_id, status, status_started_at, current_skill, current_task_id, updated_at; PK(project_name, worker_id), idx on updated_at) — live runtime state per worker. Written by handleSetActiveRole (single active role per project invariant). Read by GET /api/crm/projects/:name/workers/:id/runtime with staleness fallback: status='working' + tmux dead + updated_at > 10 min → coerced to idle (crash detection).
049 embeddings Phase 71.2 #359 / 2026-06-04 embeddings (id PK AUTOINCREMENT, project, doc_type wiki/issue/skill, doc_id, chunk_ix, text, created_at) + embeddings_vec (vec0 virtual table, embedding FLOAT[1024]). Split: vec0 can't hold metadata efficiently; LIST/DELETE workflows run as plain SQL. sqlite-vec extension auto-loaded in initDb before migration runs.
050 drop_notebook_id Phase 71.8 #365 / 2026-06-05 ALTER TABLE projects DROP COLUMN notebook_id. Decommissions NotebookLM bridge schema field; semantic search now runs through self-hosted RAG (Cohere + sqlite-vec). Idempotent via PRAGMA table_info check so fresh DBs with current 001 schema don't trip on missing column.
051 voice_usage Phase 62.4 #373 / 2026-06-05 voice_usage_log (user_id TEXT, date TEXT YYYY-MM-DD, seconds_used INTEGER DEFAULT 0, PRIMARY KEY(user_id, date)) — daily voice transcription quota counter, mirrors migration 032 arc_help_usage shape. Approximate seconds (estimated from upload byte size assuming ~32 kbps voice codec); compared against soft 60 min/day cap in handleVoiceTranscribe (shared/routes/voice.ts) before each transcribe to keep one user from monopolising the shared whisper-server worker pool on Contabo.
052 transcripts Phase 73.1 #377 / 2026-06-05 transcripts (id PK AUTOINCREMENT, project TEXT, owner_id TEXT, source_filename TEXT, source_bytes INTEGER, source_kind TEXT CHECK(audio|video), duration_seconds INTEGER, transcript_text TEXT, frames_json TEXT, summary_json TEXT, embed_to_rag INTEGER DEFAULT 1, status TEXT DEFAULT 'queued', error TEXT, created_at TEXT DEFAULT datetime('now'), completed_at TEXT) — one row per uploaded meeting file. frames_json = JSON array of {ts_ms, description} from Claude vision (Phase 73.4). summary_json = {tldr, key_points, action_items, decisions, topics, model, generated_at} from Claude Sonnet (Phase 73.5). Source file deleted from disk once status='done' (CEO decision D4). Indexes on (project, created_at DESC) and (owner_id, created_at DESC).
056 skill_usage #319 / 2026-06-07 skill_usage (skill_name TEXT, project_name TEXT DEFAULT '_global_', trigger_count INTEGER DEFAULT 0, install_count INTEGER DEFAULT 0, session_count INTEGER DEFAULT 0, last_triggered_at TEXT, updated_at TEXT, PRIMARY KEY(skill_name, project_name)) — cumulative counters per skill per project. trigger_count increments each time context router injects the skill; install_count on save/create; session_count on unique session appearance. Used by GET /api/crm/projects/:name/skills/usage.
057 notes Phase 78.1 #395 / 2026-06-07 4 таблиці: notes (id PK, project_name, title, description, created_by, created_at, updated_at) — нота як контейнер джерел. note_sources (id PK, note_id FK→notes, source_type CHECK(video|audio|youtube|web|pdf|docx|txt|image), filename, file_path, file_size, url, title, content_text, duration_seconds, status CHECK(queued|processing|done|error), error, processed_at, created_at) — одне джерело per рядок, content_text = витягнутий текст для RAG. note_issue_links (note_id FK, issue_id, project_name, linked_at, PK(note_id,issue_id)) — зв'язок нота↔задача. note_chats (id PK, note_id FK→notes, role CHECK(user|assistant), content, created_at) — історія чату з нотою. Indexes: notes(project_name,created_at DESC), note_sources(note_id), note_sources(status), note_issue_links(project_name,issue_id), note_chats(note_id,created_at).
053 transcript_jobs Phase 73.1 #377 / 2026-06-05 transcript_jobs (job_id TEXT PK, transcript_id INTEGER, status TEXT DEFAULT 'queued', progress_pct INTEGER DEFAULT 0, step_label TEXT, error TEXT, started_at TEXT DEFAULT datetime('now'), updated_at TEXT DEFAULT datetime('now')) — high-churn progress tracker for the background worker. Isolated from transcripts so frequent UPDATEs (every few seconds during ffmpeg/whisper) don't churn the main table indexes. SSE endpoint streams this table at 1s poll. Status lifecycle: queued → extracting_audio → transcribing → (video: extracting_frames → frames_extracted → vision_analyzing → vision_analyzed) → summarizing → summarized → embedding → done (or failed at any step). Index on (status, updated_at).

Зв'язки (Foreign Keys)

All CASCADE — видалення навички видаляє всі форки, історію, PR та бенчмарки.

ER-діаграма (текстова)

users ─1:N──> projects
            ├── chat_messages
            ├── pinned_notes
            ├── project_phases
            └── skills_project_forks ──> skills_global
                                          ├── skill_evolution_logs
                                          ├── skill_update_requests
                                          │    └── skill_benchmarks
                                          └── (standalone tables)
                                               ├── activity_log
                                               ├── system_configs
                                               └── marketplace_analysis_cache

| 029 | cloud_waitlist | Phase 60 #134 | cloud_waitlist (id INT PK AUTOINCREMENT, user_id TEXT UNIQUE, email, position INT, status (waiting|invited|activated|declined), notes, joined_at, invited_at, activated_at) — Early Access waitlist for Standard Cloud. cloudWaitlistQueries.join/findByUser/list/invite/activate/decline/stats у shared/db.ts. Endpoints: POST /api/crm/cloud/waitlist (join), GET /waitlist/status (user own), GET /waitlist (admin list), POST /waitlist/invite (admin activate + upgrades plan to cloud) | | 030 | translation_feedback | Phase 59.4 #122 | translation_feedback (id INT PK AUTOINCREMENT, locale TEXT, message_id TEXT, original TEXT, current_translation TEXT, suggestion TEXT, user_id TEXT, status (pending|accepted|rejected), created_at, reviewed_at) — Per-locale translation quality feedback. Admin review dashboard at /api/crm/translations/feedback (list/stats/accept/reject). | | 031 | token_usage_log | Phase 63 #148 | token_usage_log (id INT PK AUTOINCREMENT, project_name TEXT NOT NULL, owner_id TEXT NOT NULL, worker_id TEXT, input_tokens INT DEFAULT 0, output_tokens INT DEFAULT 0, cache_tokens INT DEFAULT 0, total_tokens INT DEFAULT 0, created_at INT DEFAULT (unixepoch())) — Per-request Claude token usage log. 3 indexes: owner_id, project_name, created_at. Populated by child-bot/claude-runner.ts via POST /api/internal/usage/log (fire-and-forget). Queried by GET /api/crm/account/usage → returns { rows: [...last 200], totals: { total, input, output } } for BillingPage + UserDropdown usage card. | | 032 | arc_help_usage | Phase 61 #147 | arc_help_usage (id INT PK AUTOINCREMENT, user_id TEXT NOT NULL, date TEXT NOT NULL (YYYY-MM-DD), count INT NOT NULL DEFAULT 0, PRIMARY KEY (user_id, date)) — Daily rate-limit counter for in-app AI Help chat (30 messages/day). Queried by GET /api/crm/help/usage; updated on POST /api/crm/help/chat. | | 033 | arc_help_messages | Phase 61 #153 | arc_help_messages (id INT PK AUTOINCREMENT, user_id TEXT NOT NULL, role TEXT NOT NULL CHECK(user|assistant), text TEXT NOT NULL, sources TEXT (JSON array, nullable), created_at TEXT DEFAULT datetime('now')) — Persistent Arc Help chat history per user. INDEX on (user_id, created_at DESC). arcHelpQueries.save/history(limit=60)/clearHistory in shared/db.ts. GET /api/crm/help/history returns last 60 messages (oldest-first). DELETE /api/crm/help/history clears all for user. Frontend loads on mount. | | 034 | skills_owner_project | #157 | ALTER TABLE skills_global ADD COLUMN owner_project TEXT DEFAULT NULL — NULL=global/marketplace (visible to all), non-null=owned by that project (filtered in listForProject). Backfill: skills with non-generic category (odoo, python, etc.) get owner_project=category. Generic categories: general/frontend/backend/devops/security/testing/database/api/mobile. DB skills now injected into Claude prompts via routeContextFromDb() in child-bot (#158). |