Skip to content
Merged
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: 4 additions & 0 deletions docs/BOARD_PACK.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ Schema migrations are implemented in

## Export and import

### Sample pack (fictional)

Import [`fixtures/sample-board-pack.json`](fixtures/sample-board-pack.json) from **Settings → Your data** to populate a self-host board with seeded demo roles (Acme / Northwind / Contoso). Labeled **SEEDED / FICTIONAL** — no real PII, no API keys.

Board packs are created by `buildBoardPack` and read through
`importBoardPack` in `web/lib/board-pack.mjs`.

Expand Down
17 changes: 17 additions & 0 deletions docs/fixtures/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Fixtures

## `sample-board-pack.json`

**SEEDED / FICTIONAL** CareerOps board pack (`schema_version` 5) for demos and self-host smoke.

- Fake companies only: Acme Corp, Northwind Traders, Contoso
- Includes roles across stages, accomplishments, one interview event, one contact
- API keys / secrets are never present (and would be stripped by `buildBoardPack` anyway)

### Import

Settings → **Your data** → import board pack → choose this file.

Or from docs: see [BOARD_PACK.md](../BOARD_PACK.md).

Do **not** replace this with a real personal board.
159 changes: 159 additions & 0 deletions docs/fixtures/sample-board-pack.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
{
"format": "careerops-board-pack/v5",
"schema_version": 5,
"exported_at": "2026-08-21T00:00:00.000Z",
"doctrine": {
"no_auto_apply": true,
"no_invented_facts": true,
"resume_struct_canonical": true,
"memory_provenance": true,
"no_auto_send": true
},
"profile": {
"resume_struct": null,
"resume_struct_rev": 0,
"structured_modified_at": null,
"bullet_memory_cadence": "off",
"cadence_timezone": null,
"cadence_anchor": null,
"last_entry_at": null,
"target_band_min": 120000,
"target_band_max": 150000,
"target_band_currency": "USD"
},
"stories": "SEEDED DEMO · FICTIONAL DATA — Acme checkout rewrite, Northwind inventory API.",
"find_prefs": null,
"roles": [
{
"id": "seed-role-acme-fullstack",
"company": "Acme Corp",
"title": "Full Stack Developer",
"stage": "applied",
"url": "https://example.com/jobs/acme-fullstack",
"jd": "Build customer-facing APIs and a React dashboard. Paste real JDs in production — this text is fictional.",
"match_score": 78,
"ghost_risk": "low",
"level": "mid",
"location": "Remote · US",
"sent_at": "2026-07-20T15:00:00.000Z",
"comp_range": {
"min": 110000,
"max": 135000,
"currency": "USD",
"interval": "year",
"label": "USD 110k – 135k"
},
"comp_raw": "USD 110k – 135k"
},
{
"id": "seed-role-northwind-backend",
"company": "Northwind Traders",
"title": "Backend Engineer",
"stage": "interview",
"url": "https://example.com/jobs/northwind-backend",
"jd": "Own Postgres query paths and integration tests for inventory services. Fictional posting.",
"match_score": 84,
"ghost_risk": "unknown",
"level": "mid",
"location": "Hybrid",
"sent_at": null,
"comp_range": {
"min": 125000,
"max": 145000,
"currency": "USD",
"interval": "year",
"label": "USD 125k – 145k"
},
"comp_raw": "USD 125k – 145k"
},
{
"id": "seed-role-contoso-sourced",
"company": "Contoso",
"title": "Platform Engineer",
"stage": "sourced",
"url": "https://example.com/jobs/contoso-platform",
"jd": "CI/CD and internal developer platform. Fictional.",
"ghost_risk": "medium",
"level": "senior",
"location": "Remote",
"sent_at": null,
"comp_range": null,
"comp_raw": null
}
],
"materials": [],
"reports": [],
"accomplishments": [
{
"id": "seed-acc-1",
"body_original": "Refactored Acme checkout endpoints and cut p95 latency 35% (fictional metric for demo).",
"body_current": "Refactored Acme checkout endpoints and cut p95 latency 35% (fictional metric for demo).",
"revisions": [],
"status": "active",
"archived_at": null,
"role_id": null,
"employer": "Acme Corp",
"project": "Checkout rewrite",
"tags": ["performance", "api"],
"checked": true,
"promoted_role_id": null,
"promoted_bullet_id": null,
"promoted_at": null,
"promotion_snapshot": null,
"created_at": "2026-06-01T12:00:00.000Z",
"updated_at": "2026-06-01T12:00:00.000Z"
},
{
"id": "seed-acc-2",
"body_original": "Added integration tests covering Northwind inventory write path (fictional).",
"body_current": "Added integration tests covering Northwind inventory write path (fictional).",
"revisions": [],
"status": "inbox",
"archived_at": null,
"role_id": null,
"employer": "Northwind Traders",
"project": "Inventory API",
"tags": ["testing"],
"checked": false,
"promoted_role_id": null,
"promoted_bullet_id": null,
"promoted_at": null,
"promotion_snapshot": null,
"created_at": "2026-06-15T12:00:00.000Z",
"updated_at": "2026-06-15T12:00:00.000Z"
}
],
"portfolio": [],
"outcomes": {},
"interview_events": [
{
"id": "seed-iv-1",
"role_id": "seed-role-northwind-backend",
"round": 1,
"type": "screen",
"scheduled_at": "2026-08-10T18:00:00.000Z",
"notes": "Recruiter screen — fictional.",
"interviewer_name": "Alex Recruiter",
"created_at": null
}
],
"contacts": [
{
"id": "seed-contact-1",
"name": "Alex Recruiter",
"channel": "email",
"company": "Northwind Traders",
"role_ids": ["seed-role-northwind-backend"],
"last_touch_at": "2026-08-05T16:00:00.000Z",
"notes": "Fictional contact — outreach drafts only, never auto-send.",
"created_at": null
}
],
"extensions": {
"plugins": [],
"fields": {
"seed_note": "SEEDED / FICTIONAL — public-safe sample for Settings → Import demos"
},
"chain_runs": []
}
}
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"version": "1.2.1",
"description": "CareerOps public repo — dashboard SPA + agent skill",
"scripts": {
"test": "node scripts/privacy-scan.mjs && node scripts/smoke-web.mjs && node scripts/test-nav-sections.mjs && node scripts/test-board-layout.mjs && node scripts/test-find-match.mjs && node scripts/test-career-os.mjs && node scripts/test-schema-hardening.mjs && node scripts/test-promote-rpc.mjs && node scripts/test-credential-vault.mjs && node scripts/test-plugins.mjs && node scripts/test-chains.mjs && npm test --prefix packages/careerops",
"test": "node scripts/privacy-scan.mjs && node scripts/smoke-web.mjs && node scripts/test-nav-sections.mjs && node scripts/test-board-layout.mjs && node scripts/test-find-match.mjs && node scripts/test-career-os.mjs && node scripts/test-sample-board-pack.mjs && node scripts/test-schema-hardening.mjs && node scripts/test-promote-rpc.mjs && node scripts/test-credential-vault.mjs && node scripts/test-plugins.mjs && node scripts/test-chains.mjs && npm test --prefix packages/careerops",
"test:web": "node scripts/smoke-web.mjs && node scripts/test-nav-sections.mjs && node scripts/test-board-layout.mjs",
"test:nav": "node scripts/test-nav-sections.mjs",
"test:board-layout": "node scripts/test-board-layout.mjs",
Expand All @@ -22,7 +22,8 @@
"deploy": "bash scripts/deploy.sh",
"deploy:functions": "bash scripts/deploy-functions.sh",
"deploy:web": "bash scripts/deploy-web.sh",
"pack:careerops": "npm pack --prefix packages/careerops"
"pack:careerops": "npm pack --prefix packages/careerops",
"test:sample-pack": "node scripts/test-sample-board-pack.mjs"
},
"devDependencies": {
"playwright": "^1.62.0"
Expand Down
39 changes: 39 additions & 0 deletions scripts/test-sample-board-pack.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/usr/bin/env node
/**
* Ensures docs/fixtures/sample-board-pack.json is a real v5 pack that imports roles.
*/
import assert from 'node:assert/strict'
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import {
importBoardPack,
planBoardPackUpsert,
BOARD_PACK_SCHEMA_VERSION,
} from '../web/lib/board-pack.mjs'

const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
const fixturePath = path.join(root, 'docs/fixtures/sample-board-pack.json')

assert.ok(fs.existsSync(fixturePath), 'docs/fixtures/sample-board-pack.json must exist')
const raw = JSON.parse(fs.readFileSync(fixturePath, 'utf8'))
assert.equal(raw.schema_version, BOARD_PACK_SCHEMA_VERSION)
assert.equal(raw.format, `careerops-board-pack/v${BOARD_PACK_SCHEMA_VERSION}`)
const blob = JSON.stringify(raw)
assert.doesNotMatch(blob, /openai_key|anthropic_key|api_key|sk-/i)

const imported = importBoardPack(raw)
assert.ok(imported.roles.length >= 3, 'fixture must include demo roles')
assert.ok(imported.accomplishments.length >= 1)
const plan = planBoardPackUpsert(imported, {
existingRoleIds: [],
existingReportIds: [],
existingAccomplishmentIds: [],
existingPortfolioIds: [],
existingContactIds: [],
})
assert.ok(plan.roles.insert.length >= 3)
assert.match(imported.stories || '', /FICTIONAL|SEEDED|Acme/i)

console.log('ok sample board pack fixture imports roles (no secrets)')
console.log('\ntest-sample-board-pack passed')
1 change: 1 addition & 0 deletions web/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Pure Career OS helpers used by the SPA live in `lib/` (bullet memory, cadence, r
### Export / import

- **Board pack** (`CareerOps_board_pack.json`) — skill modes + Settings import (upsert). Schema v5 adds contacts, posted `comp_range`/`comp_raw`, and profile target band. API keys never exported or imported.
- **Sample pack** — import [`docs/fixtures/sample-board-pack.json`](../docs/fixtures/sample-board-pack.json) (SEEDED / FICTIONAL) from Settings → Your data to try a populated board without real job data.
- See the [Board Pack schema changelog](../docs/BOARD_PACK.md) for the v1 → v5 schema details.
- **Full JSON** / **CSV** — Settings → Your data.

Expand Down
Loading