# Selfwright

An open-core, local-first personal operating system for career, expertise, influence, and knowledge.

_Founder & architect_

## At a glance

**Problem:** Career tools optimize for volume and keyword-fit, not truth. Most career and job-search tools optimize for volume and keyword-fit. Selfwright optimizes for truthful, defensible, top-tier output, then compounds everything learned — evidence, stories, decisions, contacts, lessons — into a personal knowledge system.

**Approach:** A locked truth layer, honesty walls, and scored drifts as the moat. Four compounding goals, in build order: a career engine (truthful, ATS-tuned, archetype-tailored CVs and covers, auto-discovered roles), a coach (interview and networking prep, deliberate skill-gap closing), content (thought-leadership writing support), and expertise (becoming a functional and technical expert across the spaces it touches).

**Architecture:** API-first, DDD, modular monolith, hexagonal — TypeScript-first, Python on-demand. The domain core is pure TypeScript with zero framework, provider, or storage imports — everything external (LLM calls, storage, memory, rendering, notifications) is reached only through a port, implemented by a swappable adapter. The git-based truth layer is the state of record; a PostgreSQL projection (with pgvector) provides a rebuildable index — never the source of truth itself.

**Results:** 100% ATS pass rate, 4.6-4.7/5.0 holistic fit on acceptance roles. On real acceptance roles it holds a 100% ATS pass rate and 4.6-4.7/5.0 holistic fit on co-piloted cover letters and research — a hands-on proof point, not a slide, with a fitness-function suite that catches architectural drift and truth violations automatically.

## Metrics

- **Architecture:** API-first, DDD, modular monolith, hexagonal
- **ATS pass rate (acceptance roles):** 100%
- **Holistic fit (closeout roles):** 4.6-4.7 / 5.0

**Stack:** TypeScript, Hexagonal architecture (ports & adapters), Domain-Driven Design, PostgreSQL + pgvector, Claude (Anthropic) via a co-piloted generation path, Python (agents, on-demand)

On real acceptance roles, Selfwright holds a 100% ATS pass rate and 4.6–4.7/5.0 holistic fit on
co-piloted cover letters and research. Those numbers come from a fitness suite that grades every
output against the same criteria before it counts as done — not cherry-picked examples. The
framework is being prepared for open source.

What makes the numbers trustworthy is the architecture. Selfwright is a personal operating system
for career, expertise, and knowledge, built around one constraint: every output traces back to a
locked evidence registry.

## The platform

A locked truth layer, honesty walls, scored "drifts," and verifiable generation are the moat —
and the discipline generalizes well beyond the CV. The platform is built around four compounding
goals, in build order: (1) a **career engine** — apply to roles with truthful, ATS-tuned,
archetype-tailored CVs and covers, and auto-discover suitable roles; (2) a **coach** — sharper
interview and networking prep, deliberate skill-gap closing; (3) **content** — propose and support
thought-leadership writing; (4) **expertise** — become a functional and technical expert across
the software, data, and AI engineering spaces it touches.

## Architecture

Selfwright is built **API-first, Domain-Driven Design, Modular Monolith, Hexagonal (ports &
adapters)**, in a TypeScript-first stack, with Python added on-demand for AI agents. The
domain core is pure TypeScript with **zero** framework, provider, or storage imports — everything
external (LLM calls, storage, memory, rendering, notifications) is reached only through a port,
implemented by a swappable adapter. The git-based truth layer (identity, evidence registry,
skills, comp floors, honesty boundaries) is the state of record; a PostgreSQL projection (with
`pgvector`) provides a rebuildable index for semantic retrieval and reporting — never the source
of truth itself.

The core has zero provider imports by design, enforced by a fitness function (`FF-PORT-1`).
  Swapping the LLM path, the storage layer, or the harness (Claude Code, Cursor, OpenCode) is an
  adapter change — not a rewrite of the domain logic underneath.

Quality is enforced by TDD on the deterministic core (scoring, ATS, tailoring, drift
application), an eval harness on LLM-touching paths, CI grading gates, and a fitness-function
suite — including a **data-leak gate** as the top safety control, since the framework repo must
never contain the personal, PII-bearing truth layer. The framework/personal-data boundary is
clean by design: the framework is open-core and is being prepared for open source; personal data
lives only in the private `Selfwright-data` repository, which is never public.

## Decisions & trade-offs

**No LLM gateway, no API keys — co-piloted generation instead.**
The original design routed model calls through a LiteLLM gateway (tiered Claude Haiku → Sonnet →
Opus). That assumed either an API-key-billed provider or a self-hosted proxy in front of one. The
owner ruled both out on cost and complexity grounds versus an already-paid Claude subscription.
The redesign: Selfwright deterministically assembles a truth-grounded prompt and stops — no LLM
call, no network access. The Claude Code session already running Selfwright produces the text,
and a deterministic validator (`validateCoverArtifact` / `validateResearchArtifact`) gates the
result before it counts as done. The `LlmPort` interface stays in place as a dormant, optional
seam — an escape hatch (`ClaudeCliAdapter`, shelling `claude --print`) exists for future headless
automation, but nothing instantiates it by default. Trade-off accepted: `cover` and `research`
became multi-step flows (prompt → co-pilot fills it in → `--check`) instead of one-shot commands.

**Drift application as a governed operation, not a keyword union.**
"Drifts" are the one sanctioned, scored, ledgered exception to the truth floor — a way to
advisory-tune emphasis per company without fabricating anything. A prior implementation's
`inject_drifts` only unioned keywords and crashed on real, `career_plan`-shaped overlays. The fix:
an object-only schema, confidence-band gating, and structured provenance, so a drift is applied
as a governed, auditable operation rather than a silent text merge.

**A real regression caught by grounding validators against real documents, not synthetic
sentences.** `validateResearchArtifact` originally ran truth-tracing over an entire research
document against the personal evidence registry. A real company-research document is mostly
sentences about the target company (revenue, org structure, tech stack) that have no reason to
overlap with the candidate's evidence — so any genuine research artifact would fail the check. The
shipped test only ever exercised a single synthetic sentence, so the bug didn't surface until the
validator ran against real, multi-paragraph prose during the Phase 2 close-out. Fixed by scoping
truth-tracing to sentences that actually reference the candidate (first-person pronoun or name),
while honesty-boundary scanning still covers the full text.

**Deterministic tier first, LLM tier second.** Phase 1's definition of done was split into a
deterministic tier (ATS ≥ 0.80 on all six acceptance roles, `tailor` succeeding for every role
including drift-injecting overlays, zod-validated overlays, a non-degenerate fit score) and an
LLM tier (co-piloted cover/research passing the truth-trace validator with holistic fit ≥ 4.0).
Splitting the DoD this way meant the deterministic core could be proven solid before any
generation path existed to test against it.

## What this demonstrates

A real hexagonal/DDD system with an enforced port boundary. A fitness-function suite that catches
architectural drift and truth violations automatically. TDD on the deterministic core and an eval
harness on LLM paths — applied to a genuinely constrained problem: building useful AI-assisted
tooling without an API-key budget, by re-architecting around the harness already available.

On real acceptance roles it holds a 100% ATS pass rate and 4.6–4.7/5.0 holistic fit on
co-piloted cover letters and research.

The "Measured, not claimed" panel further up this page is a curated, real snapshot — not hand-typed prose.
  `scripts/import-selfwright-metrics.ts` reads the local Selfwright repo (fitness-suite output, unit-test
  counts, and the aggregated ATS/fit figures from its own acceptance-test docs), runs every group through an
  explicit deny-list (no pipeline volume, no application counts, no named target companies), and writes
  `content/metrics/selfwright.json`. The owner runs `pnpm import-selfwright-metrics` by hand to refresh it —
  deliberately not wired into CI, so a stale snapshot never silently regenerates against private data it
  shouldn't touch. If the file is missing, the panel renders nothing.

## Status

Selfwright is in incubation: Phase 0 (scaffold, safety nets) and Phase 1 (career-core parity)
are complete, Phase 2 (co-pilot close-out, scanner, skills/harness layer) is underway — the
scanner, skills, and slash-command surface have shipped; multi-harness adapters, the
Postgres/pgvector projection, and memory via mem0 remain open. It replaces and extends a prior
working system (`career_plan`), which stays live in parallel until feature parity is reached.

{/* TODO: add architecture screenshots and a short screen recording once there is a live,
     visual surface to show (currently CLI + MCP only). */}
