Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ npx @telivity/careerops init

≈5 minutes. Done.

Self-host, schema, deploy, and agent skill details: [docs/](docs/) · [CONTRIBUTING.md](CONTRIBUTING.md) · [web/README.md](web/README.md)
Self-host, schema, deploy, and agent skill details: [docs/](docs/) · [CONTRIBUTING.md](CONTRIBUTING.md) · [web/README.md](web/README.md) · [web/SCHEMA.md](web/SCHEMA.md)

---

Expand All @@ -112,7 +112,7 @@ Technical depth lives in `docs/` — not on the critical path for starring or tr
| Security model | [SECURITY.md](SECURITY.md) · [supabase/README.md](supabase/README.md) |
| Resume provenance | [docs/DOCTRINE_MEMORY.md](docs/DOCTRINE_MEMORY.md) |
| Transactional promotion | [docs/DOCTRINE_MEMORY.md](docs/DOCTRINE_MEMORY.md#promotion--bidirectional) |
| Database schema | [supabase/README.md](supabase/README.md) · [`supabase/schema.sql`](supabase/schema.sql) |
| Database schema | [supabase/README.md](supabase/README.md) · [`supabase/schema.sql`](supabase/schema.sql) · [SPA cheat-sheet](web/SCHEMA.md) |
| Plugin system | [docs/PLUGINS.md](docs/PLUGINS.md) |
| AI architecture / skill | [docs/SKILL.md](docs/SKILL.md) · [docs/CHAINS.md](docs/CHAINS.md) |
| Local-first design | [docs/LOCAL_FIRST.md](docs/LOCAL_FIRST.md) |
Expand Down
2 changes: 2 additions & 0 deletions web/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ Or from this folder: `npx vercel deploy --prod`

Point the app at a Supabase project that has the CareerOps tables (`mt_roles`, `mt_profiles`, `mt_reports`, `mt_accomplishments`, `mt_portfolio_items`, `mt_outcomes`, `mt_interview_events`, `mt_contacts`, …) and auth. Apply `supabase/schema.sql` or Phase 1–3 migrations under `supabase/migrations/`. Use your own project — do not reuse someone else’s demo credentials.

SPA table/bucket cheat-sheet (client-derived): [SCHEMA.md](SCHEMA.md).

Pure Career OS helpers used by the SPA live in `lib/` (bullet memory, cadence, ranking, resume sync, board pack, portfolio, advisor, career durability, interview events, offer compare, version timeline, contacts CRM, ATS comp, salary compare, enrich inbox) and are covered by `npm run test:career-os`.

### Export / import
Expand Down
120 changes: 120 additions & 0 deletions web/SCHEMA.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# SPA schema cheat-sheet

Tables and storage the CareerOps web SPA touches via Supabase client calls in `web/ui/*.mjs`.

This is a **client-derived** map for self-hosters — not a full DDL. Apply [`supabase/schema.sql`](../supabase/schema.sql) (or the migrations under `supabase/migrations/`) for the real schema + RLS.

Sources checked: `web/ui/state.mjs`, `web/ui/settings.mjs`, cross-checked with `supabase/schema.sql`.

---

## Minimal board install

These are enough for auth + kanban CRUD (no search / match / rewrite / memory):

| Table | Required? |
|-------|-----------|
| `mt_profiles` | Yes |
| `mt_roles` | Yes |
| `mt_reports` | Yes (versions / match artifacts) |
| `mt_events` | Optional (UI logs quietly if insert fails) |

Auth (`auth.users`) is required. Everything below is needed for full Career OS features.

---

## Tables the SPA calls (`sb.from(...)`)

### `mt_profiles`

- **Purpose:** One profile row per auth user (resume, prefs, BYO key flags, cadence, story bank).
- **Key:** `owner` (uuid → `auth.users`).
- **Columns clearly read/written in the client:**
- `owner`, `email`, `full_name`, `phone`, `linkedin`, `location`
- `resume_text`, `resume_struct`, `resume_struct_rev`, `structured_modified_at`, `resume_reconcile_needed`
- `target_titles`, `keywords`, `seniority`, `locations`, `ats_boards`, `onboarded`
- `ai_key`, `kimi_key`, `openai_base_url`, `openai_key`, `openai_model`
- `humanizer_email`, `humanizer_pw`
- `ai_key_on_file`, `kimi_key_on_file`, `humanizer_email_on_file`, `humanizer_pw_on_file`
- `bullet_memory_cadence`, `cadence_timezone`, `cadence_anchor`
- `last_entry_at`, `last_prompted_at`, `snoozed_until`
- `story_bank`, `target_band_min`, `target_band_max`, `target_band_currency`
- `created_at`, `updated_at` (mostly server-maintained)
- **Notes:** Self-host may store provider secrets as plaintext profile columns. Hosted/vault path uses edge functions + `mt_provider_secrets` (see below) and only surfaces `*_on_file` flags to the SPA.

### `mt_roles`

- **Purpose:** Job-search kanban cards.
- **Columns clearly read/written:**
- `id`, `owner`, `company`, `title`, `level`, `url`, `source`
- `fit_score`, `match_score`, `stage`, `ghost_risk`, `jd`, `notes`, `location`
- `sent_at`, `comp_range`, `comp_raw`
- `created_at`, `updated_at`
- **Notes:** Board stages are string values such as `sourced`, `researched`, `applied`, … plus a closed stage constant in the SPA — not a separate `status` / `col` column.

### `mt_reports`

- **Purpose:** Append-only artifacts (match, resume/cover drafts, jobscan, evaluate, interview, advisor, selection, …).
- **Columns clearly read/written:**
- `id`, `role_id`, `owner`, `kind`, `match_score`, `missing_keywords`
- `rewritten`, `jd_text`, `display_name`, `sent_at`, `created_at`
- **Notes:** For some `kind='jobscan'` rows, `jd_text` holds a **storage path** in the `reports` bucket (PDF), not JD text.

### `mt_accomplishments`

- **Purpose:** Bullet memory (provenance-first accomplishments).
- **Columns clearly read/written:** SPA uses `select('*')` / `upsert` of full rows for the owner — treat columns in `supabase/schema.sql` as authoritative (`body_original`, `body_current`, `revisions`, `status`, promotion/polish fields, tags, etc.).
- **Unknown from partial selects:** none for happy-path UI (loads `*`).

### `mt_portfolio_items`

- **Purpose:** Portfolio library (code / design / product).
- **Columns clearly read/written:** same pattern as accomplishments — `select('*')` / `upsert` per owner; see `schema.sql`.

### `mt_outcomes`

- **Purpose:** User-recorded offer / reject / withdraw / ghost outcomes.
- **Columns clearly read/written:** `select('*')` / `upsert` / `delete` by `owner` + `role_id` — see `schema.sql` (`kind`, amounts, currency, dates, notes, …).

### `mt_interview_events`

- **Purpose:** Scheduled interview rounds (prep drafts live in `mt_reports` with `kind='interview'`).
- **Columns clearly read/written:** `select('*')` / `insert` / `upsert` / `delete` — see `schema.sql` (`round`, `scheduled_at`, `type`, `notes`, `interviewer_name`, …).

### `mt_contacts`

- **Purpose:** Recruiter / network CRM (draft + log only; never auto-send).
- **Columns clearly read/written:** `select('*')` / `insert` / `upsert` — see `schema.sql` (`name`, `channel`, `company`, `role_ids`, `last_touch_at`, `notes`, …).

### `mt_events`

- **Purpose:** Lightweight action log (ids / action names only — not resume or JD text).
- **Columns in `schema.sql`:** `id`, `owner`, `kind`, `role_id`, `meta`, `created_at`.
- **Client insert shape today:** `{ action, role_id: null, meta }` (role board id goes in `meta.role_pk`). **Unknown / drift:** client field name `action` vs schema column `kind` — confirm against your deployed DB; inserts may no-op if columns differ. SPA treats failures as non-fatal.

---

## Storage buckets

| Bucket | SPA usage |
|--------|-----------|
| `reports` | Upload / signed URL / remove Jobscan (and similar) PDFs; path often stored on `mt_reports.jd_text` |

No other `sb.storage.from(...)` bucket names appear in `web/ui/*.mjs`.

---

## Present in `schema.sql` but not direct SPA `from()` targets

| Object | Notes |
|--------|-------|
| `mt_usage` | Daily search / AI counters — edge/backend; not queried from the SPA client list above |
| `mt_provider_secrets` | Encrypted BYO secrets when `CREDENTIALS_KEK` is set; SPA talks to `upsert_provider_secret` / `clear_provider_secret` edge functions, not this table directly |
| `ai_config` / `ai_config_v` | Service-role free-tier config — not client-readable |

---

## Related

- Full DDL + RLS: [`supabase/schema.sql`](../supabase/schema.sql) · [`supabase/README.md`](../supabase/README.md)
- Web configure / deploy: [`web/README.md`](README.md)