Skip to content

BYOA refactor: fingerprint.md + deterministic primitives + ghost-drift skill#35

Merged
nahiyankhan merged 24 commits intomainfrom
refactor/fingerprint
Apr 20, 2026
Merged

BYOA refactor: fingerprint.md + deterministic primitives + ghost-drift skill#35
nahiyankhan merged 24 commits intomainfrom
refactor/fingerprint

Conversation

@nahiyankhan
Copy link
Copy Markdown
Collaborator

@nahiyankhan nahiyankhan commented Apr 20, 2026

Summary

Long-running refactor/fingerprint branch brought to a clean landing. After many intermediate shapes (expression.md, single-agent profile loop, 9-verb CLI), the branch ends on a BYOA (bring-your-own-agent) architecture. Three through-lines:

  • fingerprint.md is the canonical artifact. YAML frontmatter (49-dim embedding across palette/spacing/typography/surfaces) plus a three-layer prose body (Character → Signature → Decisions → Values). Final rename from expression.mdfingerprint.md lands in this branch; all Expression* types/functions become Fingerprint*. Strict zod is the only schema gate — version field gone.
  • CLI is deterministic primitives, no LLM. The in-process LLM stack is deleted outright: src/agents/, src/review/, src/generate/, src/verify/, src/llm/, the GitHub Action, the Anthropic/OpenAI deps, and the legacy skills/*/ directories. No CLI verb calls an LLM; no API key is required to run any command. ghost-mcp is trimmed to registry lookups only (drops review_files).
  • Judgement work moves to the host agent via a skill bundle. Profile, review, verify, generate, and discover are now recipes in an agentskills.io-compatible bundle (ghost-drift) that ships inside packages/ghost-cli/src/skill-bundle/. Host agents (Claude Code, Codex, Cursor, Goose, …) install it via ghost emit skill and drive the deterministic primitives themselves.

Final CLI surface — 6 verbs:

Verb Role
compare [...fingerprints] Pairwise (N=2, with --semantic / --temporal) or fleet (N≥3)
lint [fingerprint.md] Schema + body/frontmatter coherence gate
ack Record drift stance in .ghost-sync.json
adopt <fingerprint.md> Adopt a new parent baseline
diverge <dimension> Declare intentional divergence
emit <kind> Derive review-command, context-bundle, or skill artifact from fingerprint.md

Also in this branch (pre-BYOA work that survived):

  • Reference UI split out as packages/ghost-ui with a separate library build (dist-lib/ + shadcn registry.json).
  • Docs site consolidated under apps/docs (VitePress).
  • @ghost/mcp package exposes the UI registry to MCP clients.
  • Semantic roles (slot → token bindings) in the fingerprint schema.
  • Embedding fragmented to a sibling file alongside the prose.

Net diff vs main: 351 files, +9,857 / −13,362. The BYOA cut-down commit alone deleted ~5.5K LOC while adding ~2K (mostly the skill bundle + docs rewrite).

Test plan

  • pnpm install && pnpm build succeeds from a clean clone
  • pnpm test — vitest suite passes
  • pnpm check — biome + typecheck + file-size clean
  • ghost --help shows exactly 6 verbs (no profile/review/verify/generate/discover)
  • ghost lint fingerprint.md on a valid file exits 0; on a malformed one surfaces zod errors
  • ghost compare a.md b.md pairwise + --semantic + --temporal all render
  • ghost compare a.md b.md c.md triggers fleet mode (matrix + clusters)
  • ghost ack / ghost adopt parent.md / ghost diverge palette update .ghost-sync.json
  • ghost emit review-command writes a .claude/commands/*.md slash command
  • ghost emit context-bundle writes the context bundle
  • ghost emit skill installs the ghost-drift agentskills.io bundle into the host agent
  • Verify no CLI path imports @anthropic-ai/sdk or openai (grep -r in packages/ghost-cli and packages/ghost-core)
  • Verify no CLI path requires OPENAI_API_KEY / ANTHROPIC_API_KEY to run (only computeSemanticEmbedding library function should read them)

🤖 Generated with Claude Code

nahiyankhan and others added 24 commits April 20, 2026 12:35
Introduces a Markdown+YAML fingerprint file (expression.md) with a new
Values layer (Do/Don'ts) cited by ghost review. Legacy
.ghost-fingerprint.json still reads with a deprecation warning; CLI,
action, and parent resolution all route through loadExpression().

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Positions Ghost as pipeline infrastructure for AI UI generation:
expression.md grounds the generator, ghost review gates the output.

- `ghost context` emits SKILL.md / prompt.md / full bundle from an
  expression for grounding any downstream generator.
- `ghost generate` is the reference generator — loads expression,
  prompts the LLM, extracts HTML, self-reviews via ghost review, and
  retries with drift feedback (cap 3).
- `ghost verify` runs generate→review across a bundled 18-prompt
  standard suite, aggregates per-dimension drift (tight/leaky/
  uncaptured) and recommends where the expression under-specifies.

Each three-layer expression layer now has a concrete job in the loop:
Character→prompt, Signature→score, Decisions→lookup, Values→hard gate.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Dev pages (ghost-ui) and the root README still reflected the pre-expression
pipeline — no comply/discover/review/context/generate/verify, --emit writing
.ghost-fingerprint.json, scan-first getting-started flow.

- CLI reference: all 15 commands grouped by Profiling / Comparison &
  Compliance / Generation Loop / Evolution & Intent / Visualization
- Getting Started: zero-config profile → expression.md → review/compare/comply
  flow up front, config-driven scan moved to Advanced
- Concepts: added Generation Loop chapter, updated artifact grid to
  expression.md, retitled closing to "Fingerprint. Ground. Review. Repeat."
- Docs index + self-hosting: updated copy and connect-to-Ghost flow
- README: three-layer fingerprint section, generation-loop diagram,
  updated commands table, corrected package layout (cac, new command files,
  ghost-review skill, docs/)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Make the YAML frontmatter the single source of truth for every fingerprint
field — the body becomes a human-readable mirror rendered by the writer
and parsed only for diagnostic use. Gets rid of three conflicting precedence
rules across summary/traits/decisions/values, and makes the spec's
"frontmatter alone is enough for deterministic tools" claim actually true.

Contract changes (schema 1 files rejected, regenerate required):
- parseExpression reads every field from frontmatter; body never merges in
- writer emits all narrative fields (observation, decisions, values) to YAML
- `values` added to FINGERPRINT_KEYS so it round-trips via frontmatter
- `source: "unknown"` replaces silent "llm" default; type union extended
- loadExpression returns ParsedExpression (fingerprint + meta + body)
- EXPRESSION_SCHEMA_VERSION = 2 enforced on read with regenerate hint
- Zod FrontmatterSchema validates shape with field-path errors
- Line-oriented frontmatter scanner replaces regex (robust against hrules)
- `# Observation` prose and `# Prompt guide` parsing paths removed

New capabilities:
- `ghost lint [path]` — schema validity, body/frontmatter sync, broken
  evidence citations, unused palette colors; --strict / --off rule controls
- `ghost expr-diff <a> <b>` — semantic diff over decisions (by dimension),
  values, palette (by role), scalar tokens; complements vector `compare`
- `extends:` composition — child inherits from parent, decisions merged by
  dimension, palette by role; cycles detected; noExtends escape hatch
- `decisions/*.md` fragment auto-assembly — large systems can split rules
  across files, merged by dimension at load time
- schemas/expression.schema.json published for IDE autocomplete
- toJsonSchema() exported for consumers; regenerate via scripts/

Context bundle API:
- `--format skill|prompt|bundle` replaced with `--no-tokens` / `--readme` /
  `--prompt-only` flags; legacy format still honored one release
- tokens.css emitted by default with generator/source/timestamp header

Tests: 166 passing (from 133). New coverage for frontmatter authority,
schema gate, zod validation, body-sync lint, evidence citation lint,
semantic diff, extends composition (incl. cycles), decision fragments,
frontmatter scanner robustness against body hrules.

Docs: docs/expression-format.md rewritten to match shipped reality —
removes false "body wins" and "frontmatter-alone round-trip" claims,
documents extends/fragments/lint/diff, drops cut sections.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ata bag

Splits the 49-dim vector out of the root frontmatter into a sibling
`embedding.md` fragment referenced by ordinary markdown links in the
body — the same progressive-disclosure model agent skills use. The
index stays thin; the vector is cacheable but optional, recomputed
from the structural blocks when missing.

Also introduces a loose `metadata:` passthrough bag so LLMs can append
novel observations (e.g. `tone: magazine`) without a schema bump, while
the structural blocks (palette/spacing/typography/surfaces) stay
`.strict()`.

Loader resolution for embedding: inline frontmatter → body link →
conventional sibling → recompute via computeEmbedding.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Introduce a `roles[]` field on `DesignFingerprint` — the bridge from
abstract tokens to renderable output. A role names a semantic slot
("h1", "card", "button", "list-row") and binds tokens from the
existing dimensions (typography, spacing, surfaces, palette). This
closes the gap where fingerprints described which values *exist*
without saying *where they go*, which is the information a renderer
needs to treat an expression as generative input.

Roles live in the frontmatter alongside other machine-facts. Each
token sub-block is zod `.strict()` — unknown keys reject, keeping the
schema disciplined as it grows. The fingerprint agent's three-layer
prompt now carries a Layer 4 asking it to read component files and
emit slot bindings with evidence.

`extends:` composition merges roles by `name` (child wins per-slot,
parent-only roles preserved), mirroring how decisions and palette
roles already merge.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Introduce `ghost emit` and the underlying `emitReviewCommand` stage:
a deterministic generator that reads an `expression.md` and writes a
project-fitted design-review slash command (Markdown, ready to drop
into `.claude/commands/design-review.md`).

The generator is pure over the fingerprint — same expression, same
output. Scope is drift-only: off-palette hex, off-ramp spacing,
non-canonical radii and weights. Accessibility and universal review
live elsewhere. Output is styled after the Rams `/rams` command:
role prompt, per-dimension rule tables derived from this system's
actual tokens, output template, and guidelines.

Wires:
- `emitReviewCommand` stage + `EmitReviewInput` exported from core
- `registerEmitCommand` hooked into the CLI

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Collapse overlapping verbs into unified commands, keeping library APIs stable
so the GitHub Action and @ghost/core consumers are unaffected.

- `compare` now handles pairwise, fleet (N≥3 or --cluster), --semantic,
  --temporal, and --components (local vs registry). Subsumes the former
  `fleet`, `expr-diff`, and `diff` verbs.
- `review` now dispatches on scope (files | project | suite) from the first
  positional. Subsumes the former `comply` and `verify` verbs.
- `emit` now handles `review-command` and `context-bundle` kinds. Subsumes
  the former `context` verb.
- Delete legacy `scan` command + scan-only reporters (formatCLIReport,
  formatJSONReport) + orphaned types (DriftReport, DriftSummary,
  DesignSystemReport).
- Drop `--emit-legacy` flag from profile and the corresponding emitFormat
  option from ProfileOptions. Legacy .ghost-fingerprint.json stays
  read-only — writing it is no longer supported.

No deprecation aliases: hard break consistent with pre-1.0 iteration.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Concepts page still framed drift detection around the deleted `scan` verb
and used merged command names in the generation loop grid.

- Retitle Chapter 3 "The Scan" → "The Review". Reframe the three-lens
  tab component around `review`'s three scopes (files / project / suite)
  with fresh visuals per scope.
- Chapter 6 generation loop grid: replace `ghost context` with
  `ghost emit context-bundle` and `ghost verify` with `ghost review suite`.
- Closing artifact grid: fix ghost.config.ts description ("How to scan
  (optional)" → "Parent reference for component diff").
- Drift docs index: fix card copy (CLI verb list, getting-started hook).

Content only — no CLI or library changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pull the pure decision logic out of the compare and review command
handlers so it can be tested without spawning processes or mocking cac.
Locks in the behavior of the Phase 1–3 consolidation.

- New `packages/ghost-cli/src/compare-mode.ts` with `selectCompareMode`:
  returns ok/error discriminated union picking components | fleet |
  semantic | temporal | pairwise. bin.ts compare action dispatches off
  the result.
- Export `parseScope` from review-command.ts for testing.
- Expand vitest config to discover tests in all workspace packages.
- 26 new tests (12 parseScope, 14 selectCompareMode) — 188 → 214 total.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Prerequisite for Option A split (packages/ghost-ui becomes library-only;
apps/catalogue and apps/docs consume it). The SPA build is untouched so
the existing /tools, /ui routes keep working during migration.

- `src/index.ts` — public API: 49 UI primitives, 48 AI elements, theme
  primitives (ThemeProvider, ThemeControls, ThemeToggle, presets, utils),
  hooks, and cn.
- `vite.lib.config.ts` — Vite lib mode, ESM output to `dist-lib/`,
  preserve-modules so consumers tree-shake per-import, externalize every
  non-relative import (React, Radix, etc. stay as peerDependencies at
  consumer level).
- `tsconfig.lib.json` — emits .d.ts to `dist-lib/` via tsc. Excludes
  app-only sources (App.tsx, main.tsx, app/**, components/docs,
  components/theme-panel).
- `package.json` — drops `private`, adds `main`/`module`/`types`/`exports`
  (root + styles.css + tokens.css + fonts.css + registry.json subpaths),
  `files` allowlist, `sideEffects: ["**/*.css"]`, `build:lib` script.
- `.gitignore` — exclude `dist-lib/`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Option A step 2 of 3. @ghost/ui becomes library-only; apps/catalogue owns
the Vite SPA that consumes it. Drift docs (/tools/drift/*) ride along in
the catalogue for now — step 3 will migrate them to a dedicated apps/docs.

Moved to apps/catalogue/src/:
- App.tsx, main.tsx, app/ (all routes)
- components/docs/ (catalogue-specific demos, layouts, bento cards)
- components/theme-panel/ (runtime dev tool)
- components/theme/ThemeControls.tsx (panel wrapper; ThemeProvider + ThemeToggle stay in lib)
- contexts/, store/, types/
- lib/component-docs.ts, component-registry.ts, component-source.ts
  (registry metadata — catalogue-only)
- styles/dev-fonts.css (Cash Sans CDN — catalogue runtime only)

Left in packages/ghost-ui/src/ (library surface):
- components/ui/ (49 primitives)
- components/ai-elements/ (48 AI elements)
- components/theme/ThemeProvider, ThemeToggle
- hooks/ (5 shared hooks)
- lib/utils (cn + helpers), theme-defaults, theme-presets, theme-provider, theme-utils
- styles/main.css, font-faces.css (exported via @ghost/ui/styles.css)
- fonts/

Mechanical rewrites in 177 catalogue files:
- @/components/ui/* → @ghost/ui
- @/components/ai-elements/* → @ghost/ui
- @/components/theme/ThemeProvider|ThemeToggle → @ghost/ui
- @/lib/utils, theme-defaults, theme-presets, theme-provider, theme-utils → @ghost/ui
- @/hooks/use-* → @ghost/ui
- Duplicate @ghost/ui imports merged by biome

Plumbing:
- pnpm-workspace.yaml now includes apps/*
- justfile dev/build-ui/build-registry point at @ghost/catalogue + @ghost/ui
- biome override adds apps/catalogue to the linter-disabled list
- apps/catalogue/vite.config.ts reads DEPLOY_BASE env (github pages portability)
- ghost-ui gains scripts/resolve-dts-paths.mjs to rewrite @/ aliases in
  emitted .d.ts files to relative paths (replaces tsc-alias; artifactory
  was flaky)
- ghost-ui/package.json drops dev/build/preview (SPA-only scripts);
  ThemeControls removed from @ghost/ui index exports
- getComponentName, copyToClipboard, getRandomIndex now exported from
  @ghost/ui/lib/utils

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Option A step 3 of 3. The drift-tooling docs (getting-started, cli,
self-hosting + overview) are now markdown authored in a dedicated
VitePress app. The concepts page — heavy JSX + GSAP chapters,
interactive scan tabs, stance bubbles, fleet constellation — stays in
apps/catalogue because its value lives in the animation, not the prose.

Pages ported to apps/docs/ (markdown):
- index.md          (drift engine overview → VitePress `home` layout)
- getting-started.md
- cli.md
- self-hosting.md

Pages kept in apps/catalogue/:
- app/docs/concepts/page.tsx — route path stays /tools/drift/concepts

Catalogue route behaviour:
- /tools/drift (overview) → redirects to apps/docs `/`
- /tools/drift/getting-started → redirects to apps/docs `/getting-started`
- /tools/drift/cli → redirects to apps/docs `/cli`
- /tools/drift/self-hosting → redirects to apps/docs `/self-hosting`
- /tools/drift/concepts → still rendered by the catalogue

Redirect target is configurable via VITE_DOCS_BASE_URL; defaults to
`/docs/` (GitHub Pages layout) and is used by `<DocsRedirect>`.

VitePress config:
- `base` derived from DEPLOY_BASE env: "/" locally, "/ghost/docs/" under
  Pages, any subpath on Vercel. Catalogue owns the root.
- sidebar links the Concepts page cross-app so users can still reach it.
- `ignoreDeadLinks: [/\/tools\//]` — cross-app hrefs aren't checkable.

Deploy plumbing:
- justfile adds `dev-docs`, `build-docs`, and `build-pages` (the Pages
  stitcher — catalogue at /, docs at /docs/, 404.html for SPA fallback).
- .github/workflows/deploy-pages.yml rewritten: builds library,
  registry, catalogue (DEPLOY_BASE=/ghost/), docs (same base), stitches
  into dist/, uploads. Replaces the old packages/ghost-ui SPA build.
- main.tsx: BrowserRouter basename now reads Vite's built-in BASE_URL
  instead of a custom VITE_BASE_PATH.
- .npmrc pins `registry=https://registry.npmjs.org/` so installs work
  off-VPN; kept the @Anthropic-AI scoped override.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
On GH Pages the catalogue lives at /ghost/ and docs at /ghost/docs/.
Default DocsRedirect was pointing at /docs/ (root-absolute), which would
404 under a sub-path deploy. Derive the default from Vite's BASE_URL so
it tracks the catalogue's actual base; VITE_DOCS_BASE_URL still overrides
for a separate-project deploy (e.g. Vercel).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The VitePress split (522445e + 8aa88be) lost visual fidelity — the default
theme shares nothing with the Ghost design language — and the DocsRedirect
plus legacy /docs/* Navigate redirect created an infinite loop between the
two origins. Not worth the markdown-source upside.

Undo:
- Restore the four JSX drift docs pages (page, getting-started, cli,
  self-hosting) into apps/catalogue/src/app/docs/ from 522445e^.
- App.tsx: remove DocsRedirect, wire /tools/drift/* directly to the
  restored components.
- Delete apps/docs/ (VitePress app, config, four markdown files).
- Revert justfile: drop dev-docs/build-docs; simplify build-pages to a
  single catalogue build.
- Revert deploy-pages.yml: single catalogue upload, no stitching.

Architectural gain retained: drift docs live in an app (apps/catalogue),
not a library (packages/ghost-ui). That was the real concern. Moving to
a separate docs framework was a bridge too far.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The app is a docs site that includes the component catalogue as one
section, not a catalogue-first app. `apps/docs` reads truer.

- git mv apps/catalogue apps/docs
- package.json: @ghost/catalogue → @ghost/docs
- justfile, deploy-pages.yml, biome.json, ghost-ui/src/index.ts,
  ghost-ui/vite.lib.config.ts: update references
- README project-structure diagram rebuilt to reflect the current layout
  (apps/docs entry, reduced packages/ghost-ui to library-only, updated
  CLI file list to the 11-verb surface, removed dead scan/drift scanner
  references)
- CLAUDE.md packages table adds apps/docs and corrects ghost-ui's role

The word "catalogue" still names the components feature inside the site
(/ui/components) — it's no longer the app's name.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Naming was fractured across five layers — file (expression.md), type
(DesignFingerprint), CLI verb (profile), dir (src/fingerprint/), and
marketing ("fingerprint"). Pick one noun per layer and make "fingerprint"
disappear as a pervasive concept:

- type   : DesignFingerprint → Expression (plus sibling renames:
           EnrichedFingerprint, FingerprintComparison, FingerprintHistoryEntry,
           FingerprintValidation, FingerprintAgent → Expression*)
- dir    : packages/ghost-core/src/fingerprint/ → embedding/ (vector math)
- dir    : packages/ghost-core/test/fingerprint/ → test/embedding/
- files  : agents/fingerprint{,-agent}.ts → agents/expression{,-agent}.ts
- files  : llm/validate-fingerprint.ts → llm/validate-expression.ts
- skill  : skills/ghost-fingerprint/ → skills/ghost-profile/
- field  : fingerprintPath → expressionPath
- flag   : `ghost review -f, --fingerprint <path>` → `-e, --expression <path>`
- prose  : CLAUDE.md, README, docs/, apps/docs/*, skills/, MCP tool text

"Fingerprint" survives only as the lucide-react icon import on the docs
landing page, which is a library name not a Ghost concept.

Delete legacy .ghost-fingerprint.json reader entirely (not yet adopted, no
migration path needed):
- Remove LEGACY_FINGERPRINT_FILENAME constant and dual-format dispatch
  from expression/index.ts, review/pipeline.ts, evolution/parent.ts,
  action/index.ts
- loadExpression() now asserts .md and throws a clear migration error
- Drop the "parses .json files via legacy passthrough" test; add one that
  asserts the .md requirement instead

CLI dispatch coverage: parseEmitKind extracted from emit-command.ts and
covered by test/emit-kind.test.ts (6 tests). parseScope (12) and
selectCompareMode (14) were already tested — emit was the gap.

File-size exceptions updated for the renamed paths.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous commit accidentally included `.claude/scheduled_tasks.lock`.
Remove from tracking and gitignore the local harness state.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three follow-ups to close the renaming/CLI work on this branch:

1. Complete the `fingerprint` → `expression` rename. Prior commit left
   half the domain model under the old name. Rename core exports
   (`compareFingerprints` → `compareExpressions`, old semantic
   `compareExpressions` → `diffExpressions`, `formatFingerprint` →
   `formatExpression`, `fingerprintFromRegistry` → `expressionFromRegistry`,
   `describeFingerprint` → `describeExpression`, `emitFingerprint` →
   `emitExpression`, `validateFingerprint` → `validateExpression`,
   `FINGERPRINT_TOOLS` → `EXPRESSION_TOOLS`), move `reporters/fingerprint.ts`
   → `reporters/expression.ts`, rename shared field names
   (`ParsedExpression.fingerprint`, `FleetMember.fingerprint`,
   `ExpressionHistoryEntry.fingerprint`, `ComplianceInput.fingerprint`,
   `ReviewReport.fingerprint`, `Director.profile().fingerprint` → all
   `.expression`), and scrub the word from LLM prompts, CLI help strings,
   viz UI copy, MCP tool params, and the GitHub Action input. Leaves
   `refactor/fingerprint` branch name and lucide-react `Fingerprint` icon
   import untouched.

2. Bump expression.md schema 4 → 5. Move decision evidence from YAML
   frontmatter into the body under each `### Dimension` as a
   `**Evidence:**` bullet list. Frontmatter `decisions[]` keeps only
   `dimension` + optional `embedding`. Eliminates the visible overlap
   between the YAML `decisions:` block and the `# Decisions` prose —
   body is now authoritative for everything human-readable per dimension.
   Writer backticks citations; parser strips them for round-trip safety.
   Lint rules simplified (`orphan-dimension` replaces
   `missing-rationale`/`orphan-prose`; stray-evidence check retired).
   Migrate `packages/ghost-ui/expression.md`.

3. Split two flag-multiplexed subcommands back into their own verbs.
   `ghost compare --components` → `ghost drift` (local vs registry; takes
   no expression args, different mental model). `ghost review suite` →
   `ghost verify` (one expression in, aggregate out, different cost
   profile). Keep `compare` for N expressions across
   pairwise/fleet/semantic/temporal; keep `review files|project` unified.
   Net: 11 verbs → 13, but each verb has a single coherent input shape.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Remove DesignValues (Do/Don't) from the model and every emitter (types,
  parser, writer, diff, review/generate/context prompts, docs, reference
  expression.md).
- Delete Director (202 lines, 3 callers) — inline extract + ExpressionAgent
  in profileMultiTarget, review-command runProject, and bin.ts discover.
- Remove the vestigial --ai CLI flag; profile has been AI-only for a while
  and the flag was never read.
- Add compare(expressions, opts) as the one comparison entry point —
  discriminated union over pairwise/fleet with optional semantic/temporal
  enrichment. Delete compare-mode.ts (63-line dispatcher) and the no-op
  --cluster flag (fleet triggers on N>=3).

Net: -636 lines, 206 tests passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Collapse ExpressionAgent class + custom tool loop into runExpressionAgent
  (Agent SDK, Read/Glob/Grep). profileMultiTarget merges into profileTargets
  via a single staging dir (symlink per source).
- Flatten stages/: comply → review/, emit-review → context/, delete compare
  and the StageResult wrapper ceremony.
- Cut unused verbs: drift, viz, lint, generate. Drop profile --registry
  (shadcn-specific). Hide discover as experimental.
- Rewrite DiscoveryAgent as a plain function; BaseAgent and agents/tools
  both deleted. LLM chat types relocate to llm/types.ts.

54 files, +210 / -6,382. Tests green (185), typecheck + biome clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CI runs pnpm test without a prior build, so @ghost/core's
main: ./dist/index.js can't resolve. Point vitest at the source
entry directly — tests no longer depend on a build artifact,
and runs are faster.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rename expression.md → fingerprint.md and all Expression* types/functions
to Fingerprint* — the artifact Ghost leaves behind.

Delete the in-process LLM stack: src/{agents,review,generate,verify,llm}/,
the GitHub Action, legacy skills/*/, and Anthropic/OpenAI deps. Judgement
work (profile, review, verify, generate, discover) moves to host agent
harnesses via the ghost-drift skill bundle (agentskills.io spec) shipped
by `ghost emit skill`.

Final CLI: six deterministic primitives — compare, lint, ack, adopt,
diverge, emit. No API key required for any verb. Schema versioning
removed — strict zod is the only gate.

Also trims ghost-mcp to registry-only (drops review_files tool).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@nahiyankhan nahiyankhan force-pushed the refactor/fingerprint branch from 5425043 to 6ccca5a Compare April 20, 2026 16:36
@nahiyankhan nahiyankhan changed the title Refactor Ghost around expression.md and a single agent BYOA refactor: fingerprint.md + deterministic primitives + ghost-drift skill Apr 20, 2026
@nahiyankhan nahiyankhan merged commit 8752f0f into main Apr 20, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant