Skip to content

Decompose ghost into five decentralized tools (Phase 1–5)#64

Merged
nahiyankhan merged 24 commits intomainfrom
docs/decompose-five-tools
Apr 27, 2026
Merged

Decompose ghost into five decentralized tools (Phase 1–5)#64
nahiyankhan merged 24 commits intomainfrom
docs/decompose-five-tools

Conversation

@nahiyankhan
Copy link
Copy Markdown
Collaborator

@nahiyankhan nahiyankhan commented Apr 27, 2026

Summary

Decomposes the merged ghost-drift engine into five decentralized tools (map, expression, drift, fleet, ui). This PR carries plans, Phase 0 decisions, and Phase 1 + 2 + 3 implementation — the full decomposition.

Plans + decisions

  • Five plans under docs/ideas/ — each with schema, CLI surface, recipe outline, cross-tool integration, and a reality-check section grounded in existing implementation or prior art (audited against current packages/ghost-drift and the ~/Development/ghost-fleet orchestration repo).
  • docs/ideas/phase-0-decisions.md settles two blockers: per-tool skill bundles, no meta-emit; and the package layout (ghost-drift keeps its npm name and shrinks; new sibling packages for map/expression/fleet; @ghost/core is internal/private; thin top-level ghost meta-CLI deferred).

Phase 1 implementation

  • @ghost/core extraction (packages/ghost-core/) — embedding math, target resolver, parameterized skill-bundle loader, shared types. ghost-drift rewires imports through it. No behavior change. Patch changeset on ghost-drift.
  • ghost-map greenfield (packages/ghost-map/) — new package. Implements ghost map inventory (deterministic raw signals as JSON) and ghost map lint (zod-backed ghost.map/v1 validation + body-section partition). Hand-authored map.md fixture at the repo root. Defers describe, emit skill, and the recipe to a follow-up.
  • ghost-ui canonical conventions — adds meta.expression at registry.json top level, meta.expression_dimensions on 9 representative components (palette/spacing/typography/surfaces vocabulary), canonical map.md, README convention doc, draft .github/workflows/ghost-ci.yml (gated off; captures intended shape).

Phase 2 implementation

  • ghost-expression extraction (packages/ghost-expression/) — new package carrying the canonical artifact and emit verbs. Moves core/expression/, core/context/, emit-command.ts, the profile/schema recipes from ghost-drift. New CLI: lint, describe, diff (new verb wrapping the existing diffExpressions() library function), emit (review-command | context-bundle | skill). SKILL.md added. Profile recipe rewritten to consume map.md as optional input with inline-discovery fallback when missing.
  • ghost-drift trim — drops lint, describe, emit review-command, emit context-bundle from CLI (stub commands print migration messages pointing at ghost-expression and exit 2). Drift retains compare, ack, track, diverge, emit skill. New remediate.md recipe added; discover.md and generate.md dropped (per Phase 0 decision). SKILL.md updated. Library core/index.ts proxies all moved exports for one major-version cycle as a deprecation shim.
  • Dependency graph: @ghost/coreghost-expressionghost-drift. No circular deps.
  • Changesets: minor for ghost-expression (initial), major for ghost-drift (CLI surface change).

Phase 3 implementation

  • ghost-fleet greenfield (packages/ghost-fleet/) — read-only world model over a directory of (map.md, expression.md) members. Three CLI verbs: members (lists members + freshness), view (emits fleet.md + fleet.json to <dir>/reports/ with pairwise distances, group-by tables across five axes from map.md, tracks-graph from per-member .ghost-sync.json), emit skill.
  • ghost.fleet/v1 schema — pairwise distances array (not matrix); clusters live in body narrative, not frontmatter (matches prior-art's narrow JSON output). Body has three required skeleton sections (## World shape, ## Cohorts, ## Tracks) the skill recipe fills.
  • Skill bundle ships target.md only (monolithic-target reasoning). module.md / rollup.md deferred until the modular profile pathway lands in expression.
  • Fixture: test/fixtures/small-fleet/ with three members (cash-web, cash-android, ghost-ui) covering two platforms and two registry states; cash-web declares it tracks ghost-ui to exercise the tracks-graph.
  • Composite math relocation deferred: fleet uses compareExpressions from @ghost/core directly for pairwise; centroid + spread + clustering remain in ghost-drift/core/evolution/composite.ts. Defensible — clusters are skill-layer per the schema, so fleet's CLI doesn't need them. Move can happen later if a deterministic clustering verb is added.

Test plan

  • pnpm install clean
  • pnpm build clean
  • pnpm test219/219 across 25 files (was 196 pre-Phase 2, 201 pre-Phase 3; +18 fleet tests this phase)
  • pnpm check — biome + typecheck + file-sizes + docs frontmatter + CLI manifest
  • Lefthook pre-commit on every commit
  • Smoke: node packages/ghost-expression/dist/bin.js lint packages/ghost-ui/expression.md → 0/0/0
  • Smoke: node packages/ghost-drift/dist/bin.js compare --help → expected output
  • Smoke: node packages/ghost-drift/dist/bin.js lint → migration message
  • Smoke: node packages/ghost-fleet/dist/bin.js members <fixture> → 3 members listed
  • Smoke: node packages/ghost-fleet/dist/bin.js view <fixture> → fleet.md + fleet.json emitted
  • Manual review: canonical expression.md and map.md fixtures for ghost-ui
  • Manual review: refactored profile.md recipe (map-aware fallback)
  • Manual review: new remediate.md recipe shape
  • Manual review: drift's library backcompat shim — confirm one major cycle is the right deprecation window
  • Manual review: ghost.fleet/v1 schema + small-fleet fixture

Notes for review

  • Six Merge ... (Phase N) / feat / refactor commits keep each worktree's contribution separable for cherry-pick if the PR needs splitting.
  • core/evolution/vector.ts moved with embedding to @ghost/core (would have created a @ghost/core → ghost-drift → @ghost/core cycle otherwise). Re-exported through core/evolution/index.ts so drift's evolution surface is identical.
  • discover.md and generate.md recipes are dropped — not migrated to any package per Phase 0 decision.
  • Composite math (centroid, spread, clustering) stays in drift for now. Fleet's pairwise computation uses the lower-level compareExpressions from @ghost/core directly. Move to @ghost/core deferred until a tool needs it.
  • Explicitly deferred to next milestones (with reasoning in plans): --by-component flag on drift compare, .ghost-sync.json per-component schema, top-level ghost meta-CLI dispatcher, ghost-map describe + skill recipe, ghost-fleet tracks / temporal / --include-modules / --groupby axis filtering, multi-product expression mode: parameter on profile recipe, fleet's module.md / rollup.md skill fragments.

🤖 Generated with Claude Code

nahiyankhan and others added 11 commits April 27, 2026 01:05
Capture the architectural sketch for splitting ghost into five
decentralized tools so the design holds up under audit before any code
moves. Each plan includes schema, CLI surface, recipe outline,
cross-tool integration, and a reality-check section grounded in the
existing implementation or prior art.

- ghost map: structural mapper that emits map.md (a navigation card
  for any frontend repo, language-agnostic). Replaces the hand-authored
  YAML manifests in ~/Development/ghost-fleet.
- ghost expression: narrows the flagship to authoring -- lint /
  describe / diff / emit + the profile recipe. emit retains
  review-command, context-bundle, and skill (audit-confirmed).
- ghost drift: comparison + evolution + remediation. Registry-aware
  --by-component drift unlocks per-component attribution. Targeting
  is first-class via the existing SyncManifest.tracks plumbing.
- ghost fleet: world model over (map.md, expression.md) members.
  Captures the three-mode profiling pathway (target / module /
  rollup) the ghost-fleet prior art proves load-bearing.
- ghost ui: canonical witness that leans into shadcn registry.json
  with optional meta.expression and per-component dimension tags.
  Other tools light up component-level features when present, never
  require it.

All five plans use docs/ideas/ status: exploring per the directory
README. No code moves. Open questions surfaced (skill bundle strategy,
modular profile signaling, package layout) for follow-up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two blocking decisions resolved before Phase 1 worktrees can branch
cleanly:

1. Skill bundle strategy: per-tool, no meta-emit. Each package ships
   its own src/skill-bundle/. discover.md and generate.md recipes are
   dropped (not migrated to any package).
2. Package layout: ghost-drift keeps its npm name and shrinks; new
   sibling packages for map / expression / fleet; @ghost/core is
   internal/private; thin top-level ghost meta-CLI dispatches verbs.

The new decisions doc captures both calls plus the migration map
(which existing module lands where) and the Phase 1/2/3 worktree
sequencing. Future worktree agents reference one canonical source
instead of scrolling open questions across plans.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 1 of the five-tool decomposition makes ghost-ui the canonical
witness — the reference repo that proves the Ghost loop end-to-end.

- Add `packages/ghost-ui/map.md` (ghost.map/v1) — hand-authored navigation
  card per docs/ideas/ghost-map.md: identity, topology, conventions in
  prose; frontmatter declares platform, build system, registry, design
  system paths, ui_surface globs, and seven feature areas.
- Extend `registry.json` with two opportunistic, namespaced fields:
  `meta.expression` at the registry root pointing at expression.md, and
  `meta.expression_dimensions` on 9 representative components (alert,
  badge, button, card, dialog, input, popover, skeleton, tooltip)
  declaring which embedding dimensions each primarily expresses.
- Draft `.github/workflows/ghost-ci.yml` — gated `if: false` until the
  decomposed verbs (`ghost map lint`, `ghost expression lint`,
  `ghost drift compare --by-component`) exist. Block on lint, advisory
  on drift, per docs/ideas/ghost-ui.md.
- Document the convention near the top of `packages/ghost-ui/README.md`
  so other registries can adopt `meta.expression` without coupling.

Notes for review:
- Existing `packages/ghost-ui/expression.md` already lints clean against
  today's `ghost-drift lint` and is left untouched.
- Dimension vocabulary on registry items uses the four primary embedding
  blocks (palette / spacing / typography / surfaces); sub-dimensions like
  "radius" or "type-scale" are captured in `expression.md` decisions
  rather than registry-item tags. If future drift attribution wants to
  tag at the sub-dimension level, the field is just an array of strings —
  vocabulary can grow without a schema change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Move embedding math, target resolution, the parameterized skill-bundle
loader, and shared types into a new internal `@ghost/core` workspace
package. Drift now consumes them via `@ghost/core` while keeping its
public CLI and library surface unchanged.

This is Phase 1 of the five-tool decomposition (see
docs/ideas/phase-0-decisions.md). `@ghost/core` is private — workspace-only —
so the upcoming `ghost-expression`, `ghost-fleet`, and `ghost-map`
packages can depend on it without exposing a published surface.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the ghost-map workspace package as Phase 1 of the five-tool
decomposition (per docs/ideas/phase-0-decisions.md). Mirrors
ghost-drift's layout: cac-based CLI, bin.ts shebang entry, src/core
library surface, vitest tests, biome + strict TypeScript.

Two deterministic verbs ship today:

- `ghost-map inventory [path]` — emits raw signals as JSON: package
  manifests, language histogram, candidate config files, registry
  presence, top-level tree, and best-effort git remote/default
  branch. Pure filesystem + git-cli reads, no LLM, no network beyond
  the optional `git` invocation.
- `ghost-map lint [map.md]` — validates against ghost.map/v1
  (zod-backed frontmatter schema + Identity/Topology/Conventions body
  partition). Reports missing sections, out-of-order sections, empty
  sections, and frontmatter shape violations.

Out of scope for this milestone (deferred to follow-ups):
- `ghost-map describe` — section ranges + token estimates.
- `ghost-map emit skill` — depends on @ghost/core skill-bundle loader,
  which lands in a parallel Phase 1 worktree.
- The skill bundle itself (SKILL.md + map.md/schema.md references).

Also lands the canonical map.md fixture at the repo root, hand-authored
to lint clean — proving the schema works end to end on the ghost repo.

Notes / gaps to flag:
- `composition.frameworks`, `composition.rendering`,
  `composition.styling`, and `feature_areas` are not in the inventory
  output. Per docs/ideas/ghost-map.md they are LLM-synthesized by the
  recipe; the deterministic CLI only ships raw signals.
- ghost-map is marked private and added to the changeset ignore list
  for this bootstrap. It will flip to public when its first published
  release is ready (later phases).
- The fixture web-repo uses `coverage/` for the SKIP_DIRS test rather
  than `node_modules/` because the latter is gitignored repo-wide.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* worktree-agent-a2578757593a4fe66:
  refactor(core): extract @ghost/core package from ghost-drift
* worktree-agent-afa4e8ff97ce9c337:
  feat(ui): canonical map.md + registry meta.expression extensions
Move expression authoring (parse/compose/diff/lint/layout) and context
generation (review-command + context-bundle writers) into a new sibling
package, ghost-expression. The new package ships its own cac CLI with
four deterministic verbs — `lint`, `describe`, `diff` (new — wraps the
existing diffExpressions library function), and `emit <kind>` (kinds:
review-command, context-bundle, skill).

The ghost-expression skill bundle carries the profile.md recipe (now
map-aware: consumes map.md frontmatter when present, falls back to inline
discovery otherwise) plus the condensed schema reference and the
expression.template.md asset.

Drift's library surface is preserved via re-exports — consumers who
import loadExpression / lintExpression / diffExpressions / etc. from
ghost-drift continue to work; those names now proxy to ghost-expression.
Drift's CLI keeps its current 7-verb surface during this commit so the
build stays green; the follow-up commit trims it to drift-only verbs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Remove the expression authoring verbs (`lint`, `describe`, `emit
review-command`, `emit context-bundle`) from ghost-drift's CLI now that
they live in ghost-expression. Each removed verb stays registered as a
stub command that prints a migration message pointing at the new home,
so users on the old commands get a clear next step instead of a silent
crash. `ghost-drift emit` now only handles `skill`.

Drift's skill bundle drops `profile.md`, `discover.md`, `generate.md`,
`schema.md`, and the expression.template.md asset (those are now part of
the ghost-expression skill bundle), and gains a new `remediate.md`
recipe. Remediate is the natural follow-on to `review`: given drift
findings + the offending diff, propose the minimal targeted patches
that close the gap. SKILL.md, review.md, and verify.md are updated to
point at the new recipe and to use `ghost-expression` for any authoring
step they trigger.

Library consumers of ghost-drift can keep importing loadExpression /
lintExpression / diffExpressions / emitReviewCommand / writeContextBundle
etc. — drift's core/index.ts proxies those to ghost-expression for one
major-version cycle. New code should import directly from ghost-expression.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* worktree-agent-ad692d400fc4839b0:
  refactor(drift): trim ghost-drift to drift-only verbs; add remediate.md
  feat(expression): extract ghost-expression package from ghost-drift
@nahiyankhan nahiyankhan changed the title Phase 1: decompose ghost into five decentralized tools Decompose ghost into five decentralized tools (Phase 1 + 2) Apr 27, 2026
Adds `packages/ghost-fleet/` — a read-only elevation view across many
(map.md, expression.md) members. Three deterministic CLI verbs land in
this milestone: `members <dir>` (list registered members + freshness),
`view <dir>` (compute pairwise distances, group-by tables, tracks-graph
and emit fleet.md + fleet.json into <dir>/reports/), and `emit skill`
(install the agentskills.io bundle into a host agent).

The `ghost.fleet/v1` schema lives in src/core/schema.ts as a zod schema.
Per the plan, clusters are deliberately not in the frontmatter — they're
a body-narrative projection the skill recipe writes over the pairwise
distances + groupings the CLI emits. Body sections (## World shape, ##
Cohorts, ## Tracks) are emitted as TODO placeholders for the skill.

Pairwise distance computation uses `compareExpressions` from `@ghost/core`
directly. Centroid + spread + clustering primitives currently live in
`ghost-drift/src/core/evolution/composite.ts`; moving the math-only
functions into `@ghost/core` is deferred to a follow-up (this milestone
doesn't need them — clusters are skill-layer, not frontmatter).

`ghost fleet tracks`, `ghost fleet temporal`, `--include-modules`, and
group-by axis stacking are scoped out of this milestone per
`docs/ideas/ghost-fleet.md`. The skill bundle ships a single `target.md`
fragment for monolithic-target reasoning; module + rollup fragments
arrive when the modular profile pathway lands.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@nahiyankhan nahiyankhan changed the title Decompose ghost into five decentralized tools (Phase 1 + 2) Decompose ghost into five decentralized tools (Phase 1 + 2 + 3) Apr 27, 2026
Two schema mismatches caught during end-to-end validation against
ghost-map lint:

- Body section headings used h1 (#); spec requires h2 (##) per
  docs/ideas/ghost-map.md. Three lines updated.
- feature_areas[].sub_areas were objects ({name, paths}) but the schema
  defines them as string[] (product-surface name hints; paths live at
  the parent level). Two feature_areas (ui-primitives, ai-elements)
  collapsed; the per-sub-area path detail was already implicit in the
  shadcn `categories` field on each registry item.

Hand-authored fixture got these wrong; the deterministic linter caught
both. ghost-ui/map.md now lints clean alongside the root map.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@nahiyankhan nahiyankhan force-pushed the docs/decompose-five-tools branch from dc88849 to 5aca199 Compare April 27, 2026 13:46
nahiyankhan and others added 2 commits April 27, 2026 10:12
…on gaps

- reconcile expression-format docs with FrontmatterSchema (evidence is body, not frontmatter)
- fix design_system.location → design_system.paths in profile recipe
- inventory: Bazel/Maven/JVM + Ruby/Elixir/PHP manifest matchers (closes Bazel hard-fail)
- inventory: language-histogram platform inference for native stacks
- inventory: token-directory signal matchers (tokens/, design-tokens/, theme/, themes/)
- inventory: extend SKIP_DIRS with universal build/cache patterns + bazel-* prefix
- inventory: ancestor-aware candidate_config_files scoring (DS paths surface first)
- inventory: top_level_tree honors SKIP_DIRS (no more bazel-bin/, venv/, .gradle/ noise)
- update emit-expression-schema.mjs to import from the new ghost-expression dist path
- test fixtures for new detection paths: bazel-repo, ios-spm-repo, android-gradle-repo, flutter-repo, token-pipeline-repo, python-venv-repo

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* phase-4a-detection-fixes:
  fix(map,expression): Phase 4a — Tier 1 hard blockers + Tier 2 detection gaps
@nahiyankhan nahiyankhan changed the title Decompose ghost into five decentralized tools (Phase 1 + 2 + 3) Decompose ghost into five decentralized tools (Phase 1+2+3+4a) Apr 27, 2026
@nahiyankhan
Copy link
Copy Markdown
Collaborator Author

Phase 4a — Tier 1+2 detection fixes (commit 5f682d5, merge e7e2de7)

Seven fixes folded into one commit:

Tier 1 (hard blockers):

  • docs/expression-format.md reconciled with the strict FrontmatterSchemaevidence: removed from frontmatter examples, schema: root key removed, schema generation called out as v5
  • profile.md recipe: design_system.locationdesign_system.paths
  • Inventory manifest detection: added Bazel (WORKSPACE/MODULE.bazel/BUILD.bazel), Maven (pom.xml), Gemfile{,.lock}, *.gemspec, mix.exs, composer.json, Package.resolved, setup.py. Closes the Bazel-repo hard-fail.

Tier 2 (detection gaps):

  • Language-histogram-driven platform inference (Swift>40%+SPM/Xcode/Bazel → ios; Kotlin/Java + Gradle + AndroidManifest.xml → android; Dart+pubspec.yaml → flutter; multi-platform → mixed). Edge cases (KMP, RN, MAUI, Tauri) documented as future work.
  • Token-directory signal matchers (tokens/, design-tokens/, theme/, themes/)
  • SKIP_DIRS extended (universal patterns: venv, .venv, dist-*, bazel-*, .next, .nuxt, .svelte-kit, .turbo, .fleet)
  • candidate_config_files ancestor-aware scoring (DS-ancestor paths sort first; Code/DesignSystem/Color+Brand.swift > features/banking/Color+Banking.swift)

Test fixtures added (tiny marker-file repos): bazel-repo, ios-spm-repo, android-gradle-repo, flutter-repo, token-pipeline-repo, python-venv-repo. Inventory tests grew from 3 → 18.

Verification: 234/234 tests pass (was 219), all checks green, lefthook pre-commit clean.

Notable bonus fix: scripts/emit-expression-schema.mjs was importing from packages/ghost-drift/dist/... — broken since the Phase 2 schema migration. Now imports from packages/ghost-expression/dist/....

nahiyankhan and others added 2 commits April 27, 2026 10:36
…ed_files

- inventory: workspace expansion (package.json workspaces + apps/packages/libs/common one-level)
- inventory: build_system_hints alongside platform_hints (informational)
- map schema: platform and build_system accept arrays
- map schema: build_system enum extended (style-dictionary, maven, sbt, cmake)
- map schema: design_system.token_source (inline | external | mixed) + upstream ref
- map schema: design_system.derived_files alongside entry_files; entry_files now optional
- map lint: design-system-files-missing / upstream-missing / upstream-stranded soft checks
- expression: relax unused-palette to count role-binding citations (palette field hexes + inline hex citations in evidence strings)
- expression: shadowComplexity enum rename (none → deliberate-none) — breaking
- fleet: compute/types/cli updated to handle array-shape platform/build_system
- ghost-ui/map.md: declare token_source: inline (system ships its own tokens)

Tests: 254 passing (+20 over the 234 baseline). New fixtures:
workspace-repo, multi-platform-repo, external-tokens-repo, derived-tokens-repo.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* phase-4b-schema-enrichment:
  feat(map,expression): Phase 4b — schema arrays + token_source + derived_files
@nahiyankhan nahiyankhan changed the title Decompose ghost into five decentralized tools (Phase 1+2+3+4a) Decompose ghost into five decentralized tools (Phase 1–4) Apr 27, 2026
@nahiyankhan
Copy link
Copy Markdown
Collaborator Author

Phase 4b — schema enrichment (commit aeb2281, merge 25ad774)

Seven coupled changes across ghost-map and ghost-expression (with cross-package fallout into @ghost/core and ghost-fleet):

Tier 2 remaining:

  • Workspace expansion in inventory. Honors package.json:workspaces (array + {packages: []} shape) plus conventional apps/, packages/, libs/, common/ one-level descent. Deduped by absolute path. Root entries stay basenames; nested entries are POSIX-relative. Fixture proves a 6-manifest workspace surfaces correctly.
  • platform: T | T[] schema accepts both shapes. mixed kept for backcompat; [ios, android, web] preferred for clarity.
  • build_system: T | T[] — same shape. Enum extended with style-dictionary, maven, sbt, cmake. Cargo was already present.
  • New build_system_hints inventory field alongside platform_hints — separates platform from build-system signals (cleaner than my brief asked for).

Tier 3 enrichment:

  • design_system.token_source: inline | external | mixed + optional design_system.upstream (free-form: npm package, SPM ref, sibling path).
  • design_system.derived_files alongside entry_files. Token pipelines can declare both source-of-truth and built artifacts. entry_files now optional; new lint rule warns if both missing.
  • Three new lint rules: design-system-files-missing (warn), design-system-upstream-missing (warn when external without upstream), design-system-upstream-stranded (info when upstream set without external).
  • unused-palette lint relaxed — now counts citations in roles[].evidence and roles[].tokens.palette.{background,foreground,border}. Confirmed sufficient: ghost-ui/expression.md goes from 8 INFO to 0 without the opt-out flag.
  • shadowComplexity: nonedeliberate-none (only deliberate breaking change). Cross-package fallout: @ghost/core types + embedding union updated; tests cover schema rejection of legacy value.

Cross-package fallout the agent caught:

  • ghost-fleet/compute.ts updated to handle array-shape platform/build_system (cross-tabulates per value, formats multi-value cells as comma-separated).
  • @ghost/core shadowComplexity union updated.
  • Skill bundle assets, docs/expression-format.md, docs/ideas/ghost-map.md schema table all updated to reflect new shapes.

Verification: 254/254 tests pass (was 234, +20), all checks green, lefthook pre-commit clean. Both canonical fixtures (packages/ghost-ui/{map.md,expression.md}) lint at 0/0/0.

Changeset: ghost-expression: minor. Pre-publish status (0.0.0); the breaking shadowComplexity rename is called out in the changeset body but doesn't warrant major given the unpublished state.

nahiyankhan and others added 2 commits April 27, 2026 10:46
- detect repo kind from map.md frontmatter (token_source, registry, composition)
- ui-library mode (default, current guidance)
- token-pipeline mode (sample at layer level, not component)
- consumer mode (record what's internalized from upstream)
- library-mode feature_areas heuristic (component categories / token layers)
- reaffirm Phase 0 module-suffix decision; texture stays in Topology prose

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* phase-4c-recipe-branching:
  docs(expression): Phase 4c — branch profile recipe by repo kind
@nahiyankhan
Copy link
Copy Markdown
Collaborator Author

Phase 4c — profile recipe branching (commit 2758723, merge 7515e69)

Single recipe-doc edit. Profile recipe now branches by detected repo kind:

New Step 1.5 (detection, first-match-wins):

  1. token_source: externalconsumer mode
  2. style-dictionary framework or tokens/ dir without registry → token-pipeline mode
  3. registry.path set or styling implies code/CSS → ui-library mode (default)
  4. Multi-platform array → ui-library with coarser feature_areas + native module-suffix preference (*UI / *View / *Screen; skip *Fakes / *Mocks / *Tests)
  5. Tie-breaker: token-pipeline when entry_files are YAML/JSON graphs, ui-library when CSS/code

Branched sampling (Step 3):

  • ui-library: 6–10 component files, feature_areas = registry categories
  • token-pipeline: 3–5 component YAMLs walked through layer chain (component → semantic → base), feature_areas = token-architecture layers or platform sinks, light/dark surfaced together
  • consumer: 6–10 product UI files, record most-frequent upstream slugs + override patterns; don't fabricate hex

Phase 0 module-suffix decision reaffirmed — texture stays in Topology prose, surfaces in the recipe as a one-line sampling heuristic for native repos.

Recipe size: 112 → 108 lines (under the 150 cap).

Verification: 254/254 tests pass, all checks green, both ghost-ui canonical fixtures lint at 0/0/0.

Changeset: ghost-expression: patch (recipe-only, no schema or behavior changes).

nahiyankhan and others added 2 commits April 27, 2026 11:13
- skill-bundle schema.md: evidence is body, not frontmatter (Phase 4a missed
  this LLM-facing copy — agents following the profile recipe were hitting 10
  schema errors on first lint because the condensed reference still showed
  `decisions[].evidence:` as a frontmatter array).
- profile.md Step 5: explicit "evidence body-only" note so the recipe surfaces
  the constraint inline rather than relying on the schema reference.
- unused-palette: slug-binding propagation now actually works. A role binding
  like `roles[].tokens.palette.background = "{palette.dominant.primary}"` now
  resolves the reference and marks the underlying hex as cited. The 4b CHANGELOG
  claimed this but the code only checked literal hexes; multiple validation
  agents flagged the gap. Also widens role-palette literal collection to any
  slot key (preparing for Phase 5b's open-ended slot vocabulary).
- platform_hints: drop build-system / language / runtime leaks. The hint set
  is now constrained to the Platform enum (web/ios/android/desktop/flutter/
  mixed/other). `bazel` lives in build_system_hints only; `ruby`, `python`,
  `rust`, `go`, `jvm`, `php`, `elixir` no longer surface as platforms (they
  weren't valid platform values anyway). Adds a bazel-ruby-ios fixture
  modeled on real iOS monorepos that hit this leak.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- build_system enum: add vite, webpack, parcel, rollup, turbopack, esbuild,
  nx, turbo. Real consumer repos use Vite + pnpm + Nx; without these the
  recipe was forced to drop signal into prose. inventory.ts also detects
  these via a new BUILD_TOOL_MATCHERS table (config-file basename matching).

- design_system.upstream: accepts string | string[]. Consumers that pull
  from a fan-out of upstream packages (tokens + components + icons + glue)
  can now express that structurally instead of packing them into prose.

- roles[].tokens.palette: open-ended slot vocabulary. Was a strict three-key
  object (background/foreground/border) with `unknown keys reject`; now a
  Record<string, string>. Conventional vocabulary documented in schema.md
  and expression-format.md (background, foreground, surface, border,
  accent, muted, link); richer slot names (ring, popover, separator, …)
  no longer hard-error.

- broken-role-reference: accept opaque external token refs. Style-Dictionary-
  style consumer expressions can bind role slots to upstream tokens like
  `{base.color.brand.x.light}` without the linter rejecting them. Local refs
  (`{palette.dominant.X}` / `{palette.semantic.X}`) still validate; refs
  with recognized external heads (base/core/semantic/component/tokens/ref/
  sys) or 4+ dotted segments pass through as opaque.

- Tests: vite-nx-repo and multi-upstream-repo fixtures cover detection and
  lint paths; expression-side tests cover extended slot keys and external
  refs (262 tests, +5 since Phase 5a).

All changes are additive — existing valid map.md and expression.md files
keep validating. ghost-ui/{map.md,expression.md} continue to lint clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@nahiyankhan nahiyankhan changed the title Decompose ghost into five decentralized tools (Phase 1–4) Decompose ghost into five decentralized tools (Phase 1–5) Apr 27, 2026
@nahiyankhan
Copy link
Copy Markdown
Collaborator Author

Phase 5a + 5b — re-validation findings (commits c0601b4, 89babaa)

Two commits closing the gaps surfaced by the second-round validation pass.

Phase 5a — Tier A bug fixes

  • schema.md skill-bundle reference reconciled. The Phase 4a fix to docs/expression-format.md missed the LLM-facing condensed copy in the skill bundle — the file the recipe directs the agent to read. Removed decisions[].evidence from frontmatter examples, added body-side **Evidence:** examples, fixed the partition table.
  • profile.md Step 5 carries explicit "evidence body-only" warning.
  • unused-palette slug-binding propagation works. Added collectSlugBoundHexes() that walks roles[].tokens.palette.<slot> references through resolveTokenReference and marks the resolved hex as cited. (Existing role-evidence string handling from 4b was correct — only this propagation was missing.)
  • platform_hints no longer leaks build-system signals. derivePlatformHints now emits only Platform-enum values, with a defensive filter at the end. Bazel/Ruby/Python/Rust/Go/Elixir/PHP/JVM all dropped from platform_hints — they continue to surface in build_system_hints as appropriate.
  • New fixture: bazel-ruby-ios-repo modeling the leak case.

Phase 5b — Tier B schema gaps for real-world variety

  • build_system enum extended with vite, webpack, parcel, rollup, turbopack, esbuild, nx, turbo. New BUILD_TOOL_MATCHERS surface them in build_system_hints.
  • design_system.upstream accepts string | string[]. Multi-upstream consumers (token package + components + icons + glue) can list them honestly.
  • roles[].tokens.palette slot vocabulary opened. Was a strict 3-key object (background/foreground/border); now z.record(z.string(), z.string()). Conventional keys (background, foreground, surface, border, accent, muted, link) documented in schema.md and expression-format.md so agents reach for them first.
  • broken-role-reference accepts nested external token refs. Style-Dictionary-style refs like {base.color.brand.x.light} no longer rejected. New isExternalTokenReference heuristic recognizes heads (base, core, semantic, component, tokens, ref, sys) or 4+ dotted segments. Local palette.* refs still validate strictly.
  • New fixtures: vite-nx-repo, multi-upstream-repo.

Verification: 262 tests pass (was 254, +8 from new fixtures). All checks green. Both ghost-ui canonical fixtures lint at 0/0/0. Lefthook pre-commit clean on both commits.

Note: The brief's claim that 4b's role-evidence string handling didn't trigger turned out to be wrong — that part actually worked. The genuine gap was only slug-binding propagation, which is now fixed. The agent verified the existing role-evidence test path before changing anything.

nahiyankhan and others added 3 commits April 27, 2026 11:26
…on notes

Two prose-only clarifications surfaced by the second-round real-repo
validation:

- profile.md Step 1.5: token-pipeline takes precedence over both
  ui-library AND multi-platform when signals overlap. Pipelines often
  span multiple platforms by definition; the pipeline branch handles
  per-platform sinks via feature_areas. Prevents agents re-reading the
  rule order to be sure.
- ghost-map.md composition.styling row: for token-pipeline repos where
  the source is JSON/YAML and no rendering style is "primary," list
  output styles ordered source-style-first. Prevents fabrication when
  the standard "first is primary" rule doesn't fit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Companion to fb41ae7 — the second polish edit lost the race to a
linter on the first attempt. For token-pipeline repos where the
source-of-truth is JSON/YAML and no rendering style is "primary,"
list output styles ordered source-style-first. Prevents fabrication
when the standard "first is primary" rule doesn't fit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Workspace packages (@ghost/core, ghost-expression, ghost-drift,
ghost-fleet, ghost-map) declare their main/module/exports as
./dist/...; vite/vitest cannot resolve those entry points on a fresh
runner where dist/ does not exist. Locally tests pass because dist is
left over from prior development; pre-push runs build/test/check in
parallel so it doesn't trip there either. CI from a clean checkout
hits "Failed to resolve entry for package @ghost/core" on every
cross-package import.

Adding pnpm build before pnpm test in the test job. The lint job
already builds incidentally via pnpm typecheck (tsc --build) inside
pnpm check.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@nahiyankhan nahiyankhan merged commit d89b8a3 into main Apr 27, 2026
7 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