Baza danych — SQLite Schema

Informacje ogólne

Diagram ER (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
    }

23 tabele w 24 migracjach. Auth + projekty + dane workspace + warstwa intelligence + billing + beta gating + audit eksportu kontekstu. Wszystkie ograniczenia FK są egzekwowane.

Tabele

users — Użytkownicy

Kolumna Typ Opis
id TEXT PK UUID
email TEXT UNIQUE Email (wymagany)
password_hash TEXT Hash hasła (bcrypt)
role TEXT 'admin' lub 'user'
name TEXT Imię
avatar_url TEXT URL avatara
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 Sekret TOTP (2FA)
last_login TEXT ISO 8601
created_at TEXT ISO 8601

projects — Projekty

Kolumna Typ Opis
id INTEGER PK Auto
technical_name TEXT Nazwa techniczna (unikalna per owner)
owner_id TEXT FK→users Właściciel
display_name TEXT Nazwa wyświetlana
description TEXT Opis
color TEXT Kolor (hex)
icon TEXT Ikona
is_archived INTEGER 0/1
sort_order INTEGER Kolejność (domyślnie 999)
project_protocol TEXT Protokół projektu
created_at, updated_at TEXT Timestamps

UNIQUE(technical_name, owner_id)

account_settings — Ustawienia konta

Kolumna Typ Opis
user_id TEXT PK FK→users
anthropic_key TEXT Klucz Anthropic API
openai_key TEXT Klucz OpenAI API
name TEXT Imię
role TEXT Rola
auto_harvest INTEGER Automatyczne zbieranie skillów 0/1

chat_messages — Historia czatów

Kolumna Typ Opis
id INTEGER PK Auto
project_name TEXT Projekt
worker_id TEXT Worker
role TEXT 'user', 'assistant', 'system'
content TEXT Treść wiadomości (szyfrowana AES-256-GCM gdy encrypted=1)
encrypted INTEGER 0=plaintext, 1=vault-encrypted (Phase 45.3)
attachments TEXT Tablica JSON
timestamp TEXT ISO 8601
metadata TEXT Obiekt JSON

INDEX(project_name, worker_id, timestamp DESC)

recovery_keys — Klucze odzyskiwania (Phase 45.4)

Kolumna Typ Opis
id INTEGER PK Auto
user_id TEXT FK→users Właściciel
encrypted_key TEXT Zaszyfrowany master key
key_hint TEXT Podpowiedź (pierwsze 4 znaki)
created_at TEXT ISO 8601
used_at TEXT Kiedy użyto
revoked INTEGER 0=aktywny, 1=odwołany

INDEX(user_id)

github_links — Powiązania GitHub repo (Phase 49.3)

Kolumna Typ Opis
id INTEGER PK Auto
project_name TEXT Projekt Arc OS
owner TEXT Właściciel GitHub repo
repo TEXT Nazwa GitHub repo
webhook_secret TEXT UNIQUE 32-bajtowy hex do walidacji HMAC-SHA256
created_at TEXT ISO 8601
created_by TEXT FK→users Kto utworzył link

UNIQUE(project_name, owner, repo) — dozwolone multi-repo per projekt. INDEX(project_name), INDEX(webhook_secret)

github_events — Log zdarzeń GitHub (Phase 49.3.1)

Kolumna Typ Opis
id INTEGER PK Auto
link_id INTEGER FK→github_links CASCADE przy usunięciu linka
project_name TEXT Projekt Arc OS (zdenormalizowany dla szybkości zapytań)
event_type TEXT push, pull_request, workflow_run, issues
action TEXT pod-akcja (opened/closed/success/failure)
summary TEXT Sformatowany ciąg wyświetlany
url TEXT Głęboki link GitHub
actor TEXT Nazwa użytkownika GitHub
created_at TEXT ISO 8601

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

skills_global — Globalne skille

Kolumna Typ Opis
id INTEGER PK Auto
name TEXT UNIQUE Nazwa
description TEXT Opis
category TEXT Kategoria (domyślnie 'general')
content TEXT Zawartość skilla (Markdown)
triggers TEXT Tablica JSON wyzwalaczy
keywords TEXT Tablica JSON słów kluczowych
eval_rules TEXT Tablica JSON reguł
tool_code_ts TEXT Implementacja TypeScript
version INTEGER Wersja (auto-increment)
status TEXT 'active', 'draft', 'deprecated', 'archived'

skills_project_forks — Forki skillów

Kolumna Typ Opis
id INTEGER PK Auto
project_name TEXT Projekt
skill_id INTEGER FK→skills_global Nadrzędny skill (CASCADE)
content TEXT Dostosowana zawartość
triggers, keywords, eval_rules, tool_code_ts TEXT Nadpisania
version INTEGER Wersja forka

UNIQUE(project_name, skill_id)

skill_evolution_logs — Historia zmian skillów

Kolumna Typ Opis
id INTEGER PK
skill_id INTEGER FK CASCADE
project_name TEXT Projekt (nullable)
action TEXT 'created', 'modified', 'applied', 'reverted'
diff_summary TEXT Opis zmian
author TEXT Autor (domyślnie 'system')
metadata TEXT JSON

skill_update_requests — PR skillów (Sage)

Kolumna Typ Opis
id INTEGER PK
skill_id INTEGER FK CASCADE
proposed_by TEXT 'sage' (domyślnie)
status TEXT 'pending', 'approved', 'rejected', 'applied'
current_content TEXT Aktualna wersja
proposed_content TEXT Proponowana wersja
reason TEXT Powód zmiany

skill_benchmarks — Testy A/B skillów

Kolumna Typ Opis
id INTEGER PK
request_id INTEGER FK→skill_update_requests CASCADE
test_scenario TEXT Scenariusz testu
old_output, new_output TEXT Wyniki
score_old, score_new REAL Oceny
judgment_reason TEXT Uzasadnienie

pinned_notes — Przypięte notatki

Kolumna Typ Opis
id INTEGER PK
project_name TEXT Projekt
worker_id TEXT Worker
title TEXT Tytuł
body TEXT Treść
source_message_id INTEGER Odniesienie do chat_messages

project_phases — Fazy Roadmap

Kolumna Typ Opis
id INTEGER PK
project_name TEXT Projekt
phase_id INTEGER Numer fazy
phase_title TEXT Nazwa
status TEXT 'PLANNED', 'ACTIVE', 'COMPLETED', 'CANCELLED'
progress REAL 0.0–1.0
deadline TEXT ISO 8601

UNIQUE(project_name, phase_id)

activity_log — Dziennik aktywności

Kolumna Typ Opis
id INTEGER PK
project_name TEXT Projekt
actor TEXT Worker lub użytkownik
event_type TEXT Typ zdarzenia
title TEXT Opis
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 — Konfiguracje systemowe

Kolumna Typ Opis
key TEXT PK Klucz
value TEXT Wartość
description TEXT Opis

marketplace_analysis_cache — Cache marketplace

Cache analizy zgodności skillów z marketplace.

ephemeral_tokens — Tokeny krótkotrwałe (issue #27)

Persistent store dla jednorazowych tokenów OAuth state, resetowania hasła i weryfikacji email. Przed migracją 022 tokeny te żyły w in-memory Map i przepadały przy każdym restarcie mastera, psując logowanie przez Google/GitHub (invalid_state).

Kolumna Typ Opis
token TEXT PK 32-bajtowy hex (randomBytes(32))
type TEXT NOT NULL oauth_state | password_reset | email_verification
payload TEXT NOT NULL JSON: {provider} dla oauth_state, {email} dla pozostałych
expires_at INTEGER NOT NULL ms epoch — TTL: 10 min (oauth) / 30 min (reset) / 24 h (verify)

INDEX(type), INDEX(expires_at). Jednorazowe użycie: consume() w jednej transakcji odczytuje payload i usuwa wiersz.

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

SQLite SSOT dla issues zamiast per-projekt issues/issues.json. Plik pozostaje na dysku jako derived export (mirror), gitignored — DB jest autorytatywna.

Kolumna Typ Opis
project_name TEXT NOT NULL nazwa z registry; część PK razem z id
id INTEGER NOT NULL sekwencja 1-based per-projekt (max+1 przy insert)
title TEXT NOT NULL tytuł issue
body TEXT NOT NULL DEFAULT '' opis, Markdown
priority TEXT NOT NULL DEFAULT 'P2' P0 | P1 | P2 | P3
labels TEXT NOT NULL DEFAULT '[]' tablica JSON stringów
status TEXT NOT NULL DEFAULT 'open' draft | open | in_progress | blocked | deferred | closed (SQL default 'open' pozostaje — nowe zgłoszenia są wymuszane do draft na poziomie aplikacji)
assignee TEXT NULL id workera, który wziął zgłoszenie (migracja 036)
created_by TEXT NOT NULL DEFAULT 'legacy' id workera, który utworzył zgłoszenie (migracja 046 / #327) — do audytu (kto, kiedy, na kogo)
created_at TEXT NOT NULL timestamp ISO
updated_at TEXT NOT NULL timestamp ISO
closed_at TEXT NULL timestamp ISO przy status=closed
activity TEXT NOT NULL DEFAULT '[]' tablica JSON {ts, type, author, text} — append-only audit

PK (project_name, id) — każdy projekt ma własną niezależną przestrzeń id. INDEX (project_name, status) — najczęstszy filtr (tylko open). Operacje żyją w issueQueries (shared/db.ts): list/get/nextId/insert/upsert/replaceAll/bulkImport. replaceAll — atomowa transakcja; bulkImportINSERT OR IGNORE dla idempotentnego re-seeda.

Migration 036 (#187, 2026-05-23): ALTER TABLE project_issues ADD COLUMN assignee TEXT. Nowe statusy (in_progress, blocked, deferred) są walidowane na poziomie aplikacji (union IssueStatus w shared/db.ts), nie przez CHECK constraint (SQLite nie pozwala dodawać constraintów przez ALTER TABLE).

Migration 046 (#327, 2026-06-03): ALTER TABLE project_issues ADD COLUMN created_by TEXT NOT NULL DEFAULT 'legacy'. Lifecycle rozszerzony o nowy status draft. handleCreateIssue wymusza status='draft' gdy assignee=null (inaczej od razu 'open'), wymaga worker_id (body lub fallback z auth). handleUpdateIssue auto-promuje draft → open przy pierwszym przypisaniu assignee oraz odrzuca close dla zgłoszenia, które nigdy nie miało assignee (FR-ISS-101/102/103). Istniejące wiersze backfillowane przez DEFAULT jako 'legacy'.

onboarding_progress — Stan checklisty post-wizard (issue #56, Phase 54.1)

Derived cache per użytkownik nad strumieniem zdarzeń activity_log (SSOT). UI odczytuje jednym zapytaniem zamiast agregować po zdarzeniach. Replayable: dismiss/replay nie zmieniają state, tylko dismissed_at.

Kolumna Typ Opis
chat_id TEXT PRIMARY KEY id użytkownika (chat id z registry)
state TEXT NOT NULL DEFAULT '{}' JSON {workers, cli, skill, bot, issue}completed | skipped (brak klucza = pending)
completed_count INTEGER NOT NULL DEFAULT 0 derived cache — liczba kroków completed (0–5)
started_at TEXT NOT NULL DEFAULT (datetime('now')) utworzenie wiersza (pierwsze zdarzenie/dismiss/replay)
completed_at TEXT NULL timestamp ISO gdy wszystkie 5 kroków completed
dismissed_at TEXT NULL timestamp ISO gdy użytkownik zamknął checklistę
source TEXT NOT NULL DEFAULT 'web' web | cli — do atrybucji lejka (#58 arc tour)
updated_at TEXT NOT NULL DEFAULT (datetime('now')) timestamp mutacji

INDEX na completed_at dla analityki lejka (#61 Phase 54.6). Operacje żyją w onboardingQueries (shared/db.ts): getProgress/recordEvent/dismiss/replay. recordEvent jest idempotentny na (chat_id, step, status) — powtórne identyczne wywołanie zwraca changed=false i nie zapisuje do activity_log. Whitelist: 5 kroków × 2 statusy (completed, skipped). skipped NIE inkrementuje completed_count. Przejście skipped → completed dodaje do count. Wszystkie mutacje emitują zdarzenia do activity_log z event_type LIKE 'onboarding_%' — prawdziwy SSOT dla metryk lejka; kolumny tabeli to derived cache.

platform_audit_log — Ślad rotacji sekretów super-admina (Phase 57, Sentinel #103)

Append-only dziennik mutacji na platform-secrets vault przez UI Platform Settings. SSOT dla forensyki po incydentach ("który admin rotował klucz Anthropic o 03:14 UTC?"). Sam vault.json tego nie zapisuje — tylko wartości.

Kolumna Typ Opis
id INTEGER PRIMARY KEY AUTOINCREMENT porządek czasowy nawet przy clock skew
ts TEXT NOT NULL DEFAULT (datetime('now')) UTC po stronie serwera; atakujący nie może cofnąć daty
user_chat_id TEXT NOT NULL admin wykonujący akcję
user_email TEXT NULL snapshot z tabeli users w momencie akcji
action TEXT NOT NULL list | view | rotate | test | restart
key_name TEXT NOT NULL nazwa wpisu vault (allowlist w platform.ts); * dla list-all
ip TEXT NULL z CF-Connecting-IP / X-Real-IP / XFF tail
result TEXT NOT NULL success lub fail:<reason>
user_agent TEXT NULL ciąg UA, skrócony do 200 znaków

INDEXES: idx_platform_audit_ts (najnowsze wpisy po wszystkich kluczach), idx_platform_audit_user (ślad per admin), idx_platform_audit_key (historia rotacji per klucz). Niezmiennik append-only: brak handlerów UPDATE/DELETE; UI udostępnia read-only recent() + lastRotated() przez platformAuditQueries w shared/db.ts. Każda akcja (success+fail) zapisuje wiersz.

auth_events — Log zdarzeń uwierzytelniania użytkowników (Migration 027, issue #130)

Append-only dziennik zdarzeń auth do audytu bezpieczeństwa i badania incydentów. Oddzielny od platform_audit_log (admin-only vault) — ta tabela pokrywa flow auth użytkowników końcowych wraz z IP.

Kolumna Typ Opis
id INTEGER PRIMARY KEY AUTOINCREMENT porządek czasowy
ts TEXT NOT NULL DEFAULT (datetime('now')) UTC po stronie serwera
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 dla nieudanych prób, gdy użytkownik nie został zidentyfikowany
email TEXT NULL email użyty w próbie
ip TEXT NOT NULL DEFAULT 'unknown' CF-Connecting-IP → X-Real-IP → ostatni segment XFF
user_agent TEXT NULL ciąg UA
result TEXT NOT NULL success | failed | rate_limited | invalid_invite | already_registered | 2fa_required | invalid_token
meta TEXT NULL dodatkowy kontekst JSON (np. {isNew: true} dla OAuth)

INDEXES: idx_auth_events_ts (najnowsze najpierw), idx_auth_events_user (historia per użytkownik), idx_auth_events_ip (detekcja zagrożeń per IP), idx_auth_events_event (filtr event+result). authEventQueries.insert/recent w shared/db.ts. Logowane w master-bot/routes/auth.ts (register/login/oauth/magic-link) i master-bot/routes/cli.ts (device_code_approve).

Migracje

# Nazwa Faza Co dodaje
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 na wiadomościach workerów
014 timeline_events Phase 47 tabela timeline_events
015 e2ee_chat Phase 45.3 +kolumna encrypted w chat_messages
016 recovery_keys Phase 45.4 tabela recovery_keys
017 github_links Phase 49.3 tabela github_links (powiązania webhook)
018 github_events Phase 49.3.1 tabela github_events (log zdarzeń dla UI feed)
019 trial_credits Phase 50.1 +users.trial_granted_at, +projects.trial_mode, +projects.trial_tokens_remaining
020 subscriptions Phase 51 tabela subscriptions (plan, stripe_customer_id, status, feature_flags) + log idempotencji stripe_events
021 invites Phase 52.1 tabela invites (zapis do zamkniętej bety) — code, created_by, used_by, parent_invite_code, status, note
022 ephemeral_tokens issue #27 tabela ephemeral_tokens — persistent OAuth state / reset hasła / weryfikacja email, zamiast in-memory Map (fix invalid_state przy restarcie mastera)
023 project_issues Phase 53.14 / issue #53 tabela project_issues (composite PK project_name+id, JSON labels/activity) — przenosi przechowywanie issues z issues/issues.json do SQLite SSOT, JSON staje się derived mirror; eliminuje text-merge drift przy 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) — historia auditu eksportu per projekt dla Project Context Export + przełączniki polityk per projekt; alert przy ≥3 eksportach/24h gdy notify_on_export = true
025 onboarding_progress Phase 54.1 / issue #56 onboarding_progress (chat_id PK, JSON state per 5 kroków, completed_count derived, started_at/completed_at/dismissed_at, source web|cli) — derived cache nad strumieniem zdarzeń activity_log; SSOT = zdarzenia event_type LIKE 'onboarding_%'; recordEvent idempotentny, 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 ślad audytu dla mutacji super-admina przez UI Platform Settings na vault secrets. 3 indeksy: idx_platform_audit_ts/user/key. Bez UPDATE/DELETE — read-only przez 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 log zdarzeń auth + IP. Zdarzenia: signup/login/oauth_login/magic_link/device_code_approve. Wyniki: success/failed/rate_limited/invalid_invite/already_registered/2fa_required/invalid_token. 4 indeksy: ts/user_id/ip/event+result
028 managed_containers Phase 60 #135/#136 managed_containers (id TEXT PK (nazwa kontenera Docker), user_id, status (provisioning|ready|paused|suspended|deleted), server_ip, internal_port INT, claude_authed INT, github_authed INT, created_at, last_active) — Rejestr kontenerów Docker 1-na-użytkownika dla Standard Cloud. managedContainerQueries.findByUser/insert/updateStatus/setAuthFlag/touch/listActive w shared/db.ts. Endpointy: POST /api/crm/cloud/provision, GET /status, POST /deprovision w 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) — Lista oczekujących Early Access dla Standard Cloud. cloudWaitlistQueries.join/findByUser/list/invite/activate/decline/stats w shared/db.ts. Endpointy: POST /api/crm/cloud/waitlist (dołącz), GET /waitlist/status (własny status), GET /waitlist (lista admina), POST /waitlist/invite (admin aktywuje + upgraduje plan do 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) — Informacja zwrotna o jakości tłumaczeń per locale. Panel przeglądu admina pod /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())) — Log zużycia tokenów Claude per request. 3 indeksy: owner_id, project_name, created_at. Wypełniany przez child-bot/claude-runner.ts przez POST /api/internal/usage/log (fire-and-forget). Odpytywany przez GET /api/crm/account/usage → zwraca { rows: [...ostatnie 200], totals: { total, input, output } } dla BillingPage + UserDropdown UsageCard.
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)) — Dzienny licznik limitu szybkości dla czatu Pomocy AI w aplikacji (30 wiadomości/dzień). Odpytywany przez GET /api/crm/help/usage; aktualizowany przy POST /api/crm/help/chat.
033 arc_help_messages Phase 61 #153 arc_help_messages (id PK AUTOINCREMENT, user_id, role, text, sources JSON, created_at) — trwała historia czatu Arc Help per użytkownik (ostatnie 60).
034 skills_owner_project #157 ALTER TABLE skills_global ADD COLUMN owner_project TEXT — NULL=globalny, non-null=należy do projektu.
035 password_version Phase 64 #174 ALTER TABLE users ADD COLUMN password_version INTEGER DEFAULT 0 — inkrementowane przy zmianie hasła; claim pv w JWT walidowany przez crmAuthMiddleware, aby unieważnić stare tokeny.
036 issue_assignee #187 / 2026-05-23 ALTER TABLE project_issues ADD COLUMN assignee TEXT — nullable id workera. Umożliwia arc issue take <id> + rozszerzone statusy in_progress/blocked/deferred (walidowane na poziomie aplikacji).
046 issue_draft_and_created_by #327 / 2026-06-03 ALTER TABLE project_issues ADD COLUMN created_by TEXT NOT NULL DEFAULT 'legacy' — pole audytu "kto utworzył". Lifecycle += draft (union na poziomie aplikacji). handleCreateIssue wymusza draft bez assignee; handleUpdateIssue auto-promuje draft→open przy przypisaniu + odrzuca close dla nigdy nieprzypisanych (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) — formularz prośby o early access na stronie logowania.
038 plata_billing Phase 65 #202 / 2026-05-26 Dodaje kolumny Plata by mono do subscriptions: plata_card_token TEXT, plata_wallet_id TEXT, plata_masked_pan TEXT, next_billing_date TEXT, billing_failures INTEGER DEFAULT 0. Tworzy też tabelę idempotencji plata_events (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) — dostęp do skilli many-to-many między projektami.
040 drop_stripe #205 / 2026-05-26 DROP kolumny stripe_customer_id + DROP TABLE stripe_events. NB: kolumna stripe_subscription_id przetrwała tę migrację, bo SQLite odmawia DROP COLUMN na kolumnach z constraintem UNIQUE (po cichu przechwycone przez try/catch). Zob. migrację 041.
041 rebuild_subscriptions #205 / 2026-05-26 Follow-up do 040 — przebudowuje tabelę subscriptions bez osieroconej kolumny stripe_subscription_id wzorcem CREATE TABLE … AS. Idempotentna (pomijana, gdy kolumny już nie ma).
042 totp_backup_codes ALTER TABLE users ADD COLUMN totp_backup_codes TEXT — kody odzyskiwania TOTP.
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)) — magazyn metadanych przesłanych awatarów workerów. Pliki zapisywane w data/worker-avatars/. Serwowane przez 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)) — szablony workerów zapisane przez użytkownika. Endpointy: 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 na updated_at) — żywy stan runtime per worker. Zapisywany przez handleSetActiveRole (niezmiennik: jedna aktywna rola per projekt). Czytany przez GET /api/crm/projects/:name/workers/:id/runtime z fallbackiem staleness: status='working' + tmux martwy + updated_at > 10 min → wymuszane idle (detekcja crasha).
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 (tabela wirtualna vec0, embedding FLOAT[1024]). Podział: vec0 nie przechowuje efektywnie metadanych; workflow LIST/DELETE działają jako zwykły SQL. Rozszerzenie sqlite-vec auto-ładowane w initDb przed uruchomieniem migracji.
050 drop_notebook_id Phase 71.8 #365 / 2026-06-05 ALTER TABLE projects DROP COLUMN notebook_id. Wycofuje pole schematu NotebookLM bridge; wyszukiwanie semantyczne działa teraz przez self-hosted RAG (Cohere + sqlite-vec). Idempotentna przez sprawdzenie PRAGMA table_info, żeby świeże bazy z aktualnym schematem 001 nie wywaliły się na brakującej kolumnie.
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)) — dzienny licznik kwoty transkrypcji głosowej, odzwierciedla kształt migracji 032 arc_help_usage. Przybliżone sekundy (szacowane z rozmiaru uploadu w bajtach przy założeniu kodeka głosowego ~32 kbps); porównywane z miękkim limitem 60 min/dzień w handleVoiceTranscribe (shared/routes/voice.ts) przed każdą transkrypcją, aby jeden użytkownik nie zmonopolizował współdzielonej puli workerów whisper-server na 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) — jeden wiersz per przesłany plik spotkania. frames_json = tablica JSON {ts_ms, description} z Claude vision (Phase 73.4). summary_json = {tldr, key_points, action_items, decisions, topics, model, generated_at} z Claude Sonnet (Phase 73.5). Plik źródłowy usuwany z dysku po status='done' (decyzja CEO D4). Indeksy na (project, created_at DESC) i (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)) — kumulatywne liczniki per skill per projekt. trigger_count inkrementowany za każdym razem, gdy context router wstrzykuje skill; install_count przy save/create; session_count przy unikalnym wystąpieniu w sesji. Używane przez GET /api/crm/projects/:name/skills/usage.
057 notes Phase 78.1 #395 / 2026-06-07 4 tabele: notes (id PK, project_name, title, description, created_by, created_at, updated_at) — notatka jako kontener źródeł. 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) — jedno źródło per wiersz, content_text = wyekstrahowany tekst dla RAG. note_issue_links (note_id FK, issue_id, project_name, linked_at, PK(note_id,issue_id)) — powiązanie notatka↔zgłoszenie. note_chats (id PK, note_id FK→notes, role CHECK(user|assistant), content, created_at) — historia czatu z notatką. Indeksy: 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 tracker postępu dla workera w tle. Odizolowany od transcripts, żeby częste UPDATE-y (co kilka sekund podczas ffmpeg/whisper) nie obciążały indeksów głównej tabeli. Endpoint SSE streamuje tę tabelę z pollingiem 1s. Lifecycle statusów: queued → extracting_audio → transcribing → (video: extracting_frames → frames_extracted → vision_analyzing → vision_analyzed) → summarizing → summarized → embedding → done (lub failed na dowolnym kroku). Indeks na (status, updated_at).

Powiązania (Foreign Keys)

Wszystkie CASCADE — usunięcie skilla usuwa wszystkie forki, historię, PR i benchmarki.

Diagram ER (tekstowy)

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