Decompose ghost into five decentralized tools (Phase 1–5)#64
Decompose ghost into five decentralized tools (Phase 1–5)#64nahiyankhan merged 24 commits intomainfrom
Conversation
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
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>
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>
dc88849 to
5aca199
Compare
…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
|
Phase 4a — Tier 1+2 detection fixes (commit Seven fixes folded into one commit: Tier 1 (hard blockers):
Tier 2 (detection gaps):
Test fixtures added (tiny marker-file repos): Verification: 234/234 tests pass (was 219), all checks green, lefthook pre-commit clean. Notable bonus fix: |
…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
|
Phase 4b — schema enrichment (commit Seven coupled changes across Tier 2 remaining:
Tier 3 enrichment:
Cross-package fallout the agent caught:
Verification: 254/254 tests pass (was 234, +20), all checks green, lefthook pre-commit clean. Both canonical fixtures ( Changeset: |
- 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
|
Phase 4c — profile recipe branching (commit Single recipe-doc edit. Profile recipe now branches by detected repo kind: New Step 1.5 (detection, first-match-wins):
Branched sampling (Step 3):
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: |
- 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>
|
Phase 5a + 5b — re-validation findings (commits Two commits closing the gaps surfaced by the second-round validation pass. Phase 5a — Tier A bug fixes
Phase 5b — Tier B schema gaps for real-world variety
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. |
…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>
Summary
Decomposes the merged
ghost-driftengine 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
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 currentpackages/ghost-driftand the~/Development/ghost-fleetorchestration repo).docs/ideas/phase-0-decisions.mdsettles two blockers: per-tool skill bundles, no meta-emit; and the package layout (ghost-driftkeeps its npm name and shrinks; new sibling packages for map/expression/fleet;@ghost/coreis internal/private; thin top-levelghostmeta-CLI deferred).Phase 1 implementation
@ghost/coreextraction (packages/ghost-core/) — embedding math, target resolver, parameterized skill-bundle loader, shared types.ghost-driftrewires imports through it. No behavior change. Patch changeset onghost-drift.ghost-mapgreenfield (packages/ghost-map/) — new package. Implementsghost map inventory(deterministic raw signals as JSON) andghost map lint(zod-backedghost.map/v1validation + body-section partition). Hand-authoredmap.mdfixture at the repo root. Defersdescribe,emit skill, and the recipe to a follow-up.ghost-uicanonical conventions — addsmeta.expressionatregistry.jsontop level,meta.expression_dimensionson 9 representative components (palette/spacing/typography/surfaces vocabulary), canonicalmap.md, README convention doc, draft.github/workflows/ghost-ci.yml(gated off; captures intended shape).Phase 2 implementation
ghost-expressionextraction (packages/ghost-expression/) — new package carrying the canonical artifact and emit verbs. Movescore/expression/,core/context/,emit-command.ts, the profile/schema recipes fromghost-drift. New CLI:lint,describe,diff(new verb wrapping the existingdiffExpressions()library function),emit (review-command | context-bundle | skill). SKILL.md added. Profile recipe rewritten to consumemap.mdas optional input with inline-discovery fallback when missing.ghost-drifttrim — dropslint,describe,emit review-command,emit context-bundlefrom CLI (stub commands print migration messages pointing atghost-expressionand exit 2). Drift retainscompare,ack,track,diverge,emit skill. Newremediate.mdrecipe added;discover.mdandgenerate.mddropped (per Phase 0 decision). SKILL.md updated. Librarycore/index.tsproxies all moved exports for one major-version cycle as a deprecation shim.@ghost/core←ghost-expression←ghost-drift. No circular deps.minorforghost-expression(initial),majorforghost-drift(CLI surface change).Phase 3 implementation
ghost-fleetgreenfield (packages/ghost-fleet/) — read-only world model over a directory of (map.md, expression.md) members. Three CLI verbs:members(lists members + freshness),view(emitsfleet.md+fleet.jsonto<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/v1schema — 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.target.mdonly (monolithic-target reasoning).module.md/rollup.mddeferred until the modular profile pathway lands in expression.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.compareExpressionsfrom@ghost/coredirectly for pairwise; centroid + spread + clustering remain inghost-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 installcleanpnpm buildcleanpnpm test— 219/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 manifestnode packages/ghost-expression/dist/bin.js lint packages/ghost-ui/expression.md→ 0/0/0node packages/ghost-drift/dist/bin.js compare --help→ expected outputnode packages/ghost-drift/dist/bin.js lint→ migration messagenode packages/ghost-fleet/dist/bin.js members <fixture>→ 3 members listednode packages/ghost-fleet/dist/bin.js view <fixture>→ fleet.md + fleet.json emittedexpression.mdandmap.mdfixtures for ghost-uiprofile.mdrecipe (map-aware fallback)remediate.mdrecipe shapeghost.fleet/v1schema + small-fleet fixtureNotes for review
Merge ... (Phase N)/feat / refactorcommits keep each worktree's contribution separable for cherry-pick if the PR needs splitting.core/evolution/vector.tsmoved with embedding to@ghost/core(would have created a@ghost/core → ghost-drift → @ghost/corecycle otherwise). Re-exported throughcore/evolution/index.tsso drift's evolution surface is identical.discover.mdandgenerate.mdrecipes are dropped — not migrated to any package per Phase 0 decision.compareExpressionsfrom@ghost/coredirectly. Move to@ghost/coredeferred until a tool needs it.--by-componentflag on drift compare,.ghost-sync.jsonper-component schema, top-levelghostmeta-CLI dispatcher,ghost-map describe+ skill recipe,ghost-fleet tracks/temporal/--include-modules/--groupbyaxis filtering, multi-product expressionmode:parameter on profile recipe, fleet'smodule.md/rollup.mdskill fragments.🤖 Generated with Claude Code