Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
113 changes: 46 additions & 67 deletions .agents/skills/development/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,95 +10,74 @@ description: >

# VBI Development Handbook

This is the repository-level development handbook for VBI. The main file keeps
only the core principles; load the relevant references only when changing a
specific package, practice, or software-entropy risk area.

## Entropy Budget

Every change must reduce maintenance cost or at least avoid increasing it.
Before editing, complete the following:

1. Read the relevant code and confirm ownership.
2. Explicitly list any code smells found: duplicated code, long functions, large
files, dead exports, tight coupling, stale comments, generated files, legacy
compatibility aliases, shotgun edits, and temporary fields.
3. Choose the smallest entropy-reducing action: delete, simplify, extract, move
ownership, or update the source of truth and regenerate.
4. When deleting, also clean downstream references: imports, calls, types,
comments, tests, documentation, and generated artifacts.

Prefer deleting unused paths over keeping optional branches. Do not leave
commented-out code behind. Do not add compatibility aliases without a clear
migration reason.

## Repository Ownership

Unless a package script requires otherwise, run commands from the repository root.

- `packages/vbi`: VBIChartDSL, Builder, and collaborative state.
- `packages/vquery`: VQueryDSL-to-SQL and query execution.
- `packages/vseed`: VSeedDSL-to-VChart/VTable specs.
Repository-level rules for VBI work. Keep this file as the routing layer: use it
for the non-negotiable principles, then load only the reference that matches the
package, practice, or entropy risk in front of you.

## Core Rules

- Work from the owner and source of truth: DSL, Builder, Provider API, generator,
example JSON, or local utility.
- Reduce maintenance cost. Prefer deletion, simplification, extraction, or moving
ownership over adding compatibility layers.
- Use real needs to drive abstraction, deletion to fight entropy, and naming and
boundaries to make code explain itself. See
`references/software-entropy.md#optimization-habits`.
- Builder owns DSL mutation. UI, CLI, agent, app, and practice code should use
Builder or public package APIs instead of rebuilding internals.
- Generated artifacts are outputs, not primary fixes. Change the source and
regenerate when needed.
- Do not cross ownership boundaries casually: packages must not depend on apps,
and practices must not import another practice's private `src/*`.
- When deleting or renaming, clean imports, calls, types, comments, tests, docs,
generated references, and old names in the same change.

## Ownership Map

- `packages/vbi`: VBIChartDSL, Builder, dashboard/report/insight state, and
collaborative editing.
- `packages/vquery`: QueryDSL-to-SQL and query execution.
- `packages/vseed`: VSeed examples, lowering, and rendering specs.
- `packages/vbi-agent`: Builder Agent runtime and tool protocol.
- `packages/vbi-react`: React integration package.
- `apps/*`: Product applications, official website, backend, provider, and CLI.
- `practices/*`: Independent practice example applications.

If a change crosses multiple ownership boundaries, first decide whether the
boundary is wrong. Platform apps consume public package APIs; packages should not
know app, provider, page, or CLI implementation details.

## Sources of Truth
- `packages/vbi-react`: React integration.
- `apps/*`: product applications, docs website, backend, provider, and CLI.
- `practices/*`: independent practice examples. Treat
`practices/vbi-react-starter` as the `@visactor/vbi-react` integration starter,
separate from the self-contained practice apps.

- VBIChartDSL, VQueryDSL, and VSeedDSL drive core behavior.
- Provider owns platform resource access and Builder creation.
- Builder owns DSL mutation. UI, CLI, and agent layers should call Builder or
public package APIs instead of hand-writing internal DSL mutation logic.
- Modify the source module first. Derived documentation, tests, and build
artifacts must be updated through the owning generator.
- Do not use hand-edited generated files as the primary fix.
- Keep each practice independent: do not import `src/*` from another practice.
- Use public package APIs or local abstractions instead of reaching into
implementation details.
Run repository-level commands from the repo root unless a package script requires
otherwise.

## References

Load only relevant references:
Load only the relevant reference:

- `references/software-entropy.md`: Entropy audit workflow, VBI-specific code
smells, and the pre-edit checklist.
- `references/software-entropy.md`: maintainability, refactoring, cleanup,
deletion, generated-surface control, and optimization habits.
- `references/VBI.md`: `packages/vbi`, VBI DSL, Builder/sub-builder design,
headless logic boundaries, and TDD expectations.
- `references/website.md`: `apps/website`, Rspress 2 documentation, generated
API/example docs, and multilingual documentation synchronization.
- `references/website.md`: `apps/website`, Rspress docs, generated API/example
docs, and multilingual documentation synchronization.
- `references/vquery.md`: `packages/vquery`, QueryDSL-to-SQL, DuckDB execution,
example-driven tests, and 100% coverage expectations.
example-driven tests, and coverage expectations.
- `references/vseed.md`: `packages/vseed`, VSeed examples, and generated VSeed
documentation.
- `references/practice-minimalist.md`: `practices/minimalist`.
- `references/practice-standard.md`: `practices/standard`.
- `references/practice-streamlined.md`: `practices/streamlined`.
- `references/practice-professional.md`: `practices/professional`.

`practices/vbi-react-starter` is the `@visactor/vbi-react` package integration
starter. Treat it separately from the four self-contained practice apps.

## Validation

Repository-level gates after code changes:

```bash
pnpm run lint:check
pnpm run typecheck
```

When scripts exist in the relevant ownership scope, also run focused validation:
Prefer the narrowest proving command first, then repository gates when practical:

```bash
pnpm --filter <package-name> run test
pnpm --filter <package-name> run lint
pnpm --filter <package-name> run typecheck
pnpm run lint:check
pnpm run typecheck
```

If a change touches generated artifacts, run the generator first, then inspect
the generated diff. Clearly report any required validation that could not be run.
If generated artifacts are affected, run the generator first and inspect the
generated diff. Report any validation that could not run and why.
166 changes: 70 additions & 96 deletions .agents/skills/development/references/software-entropy.md
Original file line number Diff line number Diff line change
@@ -1,112 +1,86 @@
# Software Entropy Control

Use this reference when a task involves maintainability, refactoring, cleanup,
constraining LLM-generated code, or any change that could expand VBI's long-term
maintenance surface.
Use this reference for maintainability, refactoring, cleanup, generated-surface
control, and any change that could expand VBI's long-term maintenance cost.

## VBI Entropy Sources
Good code is carved down through repeated, deliberate refinement: use real needs
to drive abstraction, deletion to fight entropy, and naming and boundaries to
make code explain itself.

These are risk signals, not automatic defects:
## Entropy Signals

- Generated files can hide true ownership. Examples, documentation, API pages,
and aggregate tests must trace back to source types, examples, or generators.
- Large files concentrate behavioral knowledge. Current hotspots include VSeed
lowering/sandbox code, large data files in website guides, large generated
example tests, and dense UI panels in `practices/standard`.
- Legacy and TODO paths are mostly concentrated in DSL compatibility, filters,
axes, selector APIs, and demo drag/drop payloads.
- Cross-layer shortcuts are expensive: packages depending on app behavior, UI
rebuilding Builder internals, or practices sharing private `src/*` all increase
change cost.
- LLM output tends to add parallel abstractions instead of reusing Builder,
Provider, DSL, local connectors, or existing utilities inside a practice.
Treat these as risk signals to inspect, not automatic defects:

## Pre-Edit Checklist
- Generated files as the fix source instead of the generated output.
- Large files or methods concentrating unrelated behavior.
- Legacy branches, TODO paths, compatibility aliases, and stale comments.
- Cross-layer shortcuts: packages depending on apps, UI rebuilding Builder
internals, or practices importing another practice's `src/*`.
- Broad objects passed where a narrow capability or interface is enough.
- LLM-style parallel abstractions that ignore existing Builder, Provider, DSL,
connector, or local utility boundaries.

Before changing code, briefly write down these conclusions:
## Optimization Habits

- Question whether an existing design is necessary before preserving it.
- Delete or inline abstraction that does not remove concrete complexity.
- Let abstractions emerge from stable repetition, not imagined flexibility.
- Make names match true semantic scope; avoid broad names for narrow work.
- Split files by domain, builder type, or ownership boundary when concerns mix.
- Invert dependencies to the smallest interface needed by the caller.
- Keep APIs direct; avoid callbacks, fallbacks, generics, or configuration layers
unless they make real behavior simpler.
- Refine boldly: change, delete, and reshape code until intent is direct.

## Pre-Edit Questions

Before changing code, identify:

- Owner: package, app, practice, generator, or docs.
- Source of truth: DSL type, Builder method, Provider API, example JSON,
generator script, or local utility.
- Code smells: duplicated code, long methods, large components, dead exports,
stale comments, temporary fields, long parameter lists, data clumps, tight
coupling, and shotgun edits.
- Generated surface: which files must be regenerated instead of manually patched.
- Deletion impact: imports, calls, exports, tests, documentation, comments, and
generated references that must be removed at the same time.
- Validation: repository gates, plus focused tests or generators in the owning
scope.

## VBI Antipatterns

- Using hand-edited generated website docs, generated API pages, or generated
example tests as the primary fix.
- Adding compatibility aliases without a date, migration reason, and narrow test
protection.
- Letting `packages/*` import `apps/*`, practice code, page stores, CLI adapters,
provider internals, or test helpers.
- Letting one practice import another practice's `src/*`; either copy the idea
into a local utility or move the reusable API up into a package.
- UI code bypassing Builder or public package APIs and rebuilding VBIChartDSL,
VQueryDSL, or VSeedDSL mutations by hand.
- Passing broad prop bags through the UI layer instead of expressing data
boundaries with hooks, store selectors, or small local objects.
- Keeping commented-out code, unused style classes, dead tests, or stale
documentation after deleting an implementation.

## Refactoring Rules

- Follow first principles and produce intuitive designs and implementations.
- Follow the small-function, small-file principle: keep individual files under
100 lines and individual functions under 50 lines.
- Long Method: split methods/functions once they exceed 30-40 lines.
- Large Class/God Class: split any class or module that owns too many
responsibilities.
- Once a code smell is found, refactor or delete decisively. When deleting, clean
all related references at the same time, including imports, function calls,
variable references, type definitions, comments, tests, documentation, and
other downstream impact. Do not leave orphaned code or dead references.
- Common code smell to internalize and actively detect: Duplicated Code. If the
same or highly similar logic appears more than once, extract it into a
function, utility, or component.
- After refactoring, code must be simpler, more readable, and more focused in
responsibility. Follow the Single Source of Truth principle: VBIChartDSL,
VQueryDSL, and VSeedDSL drive core functionality.
- Tight Coupling: modules should not directly depend on concrete
implementations; use interfaces or dependency injection instead.
- Redundant Comments: delete comments when the code itself communicates the
meaning.
- Dead Code: delete anything that is not called, imported, or used by tests.
- Long Parameter List: use an object or configuration object once a function has
more than 4-5 parameters.
- Data Clumps: variables that always appear together should be wrapped in a class
or object.
- Temporary Field: refactor fields that are used only in some circumstances.
- Inappropriate Intimacy: modules should not know too much about each other's
internals.
- Divergent Change / Shotgun Surgery: if one change requires edits in many
places, module boundaries likely need to be redrawn.
- Before every change, scan the code and list all code smells found.
- During refactoring, prefer deletion over "keep it but comment it out."
- After deletion, verify that the code still compiles/runs and behavior is
unchanged or improved.
- In output, provide only the final clean code plus a short necessary note about
which code smells were removed.
- Do not use indecisive language such as "could keep it," "depends," or "for
compatibility" without a concrete migration reason.

Remember: Excellent code is not merely written; it is carved down. Be brave enough to delete code. Extreme simplicity is the highest standard.
- Smells: duplication, long method, large file, dead export, stale comment,
temporary field, long parameter list, data clump, tight coupling, shotgun edit.
- Generated surface: what must be regenerated instead of hand-patched.
- Deletion impact: imports, calls, exports, tests, docs, comments, and generated
references that must be removed together.
- Validation: focused owner checks plus repository gates when practical.

## Refactoring Actions

- Prefer deletion, simplification, extraction, or moving ownership over adding
new compatibility layers.
- Clean all downstream references when deleting; leave no orphaned imports,
calls, types, comments, tests, or docs.
- Split long methods around 30-40 lines and keep files focused; small files are
preferred when they map to real ownership.
- Extract duplicated logic only when the shared behavior is stable and named by
a real concept.
- Replace tight concrete dependencies with small interfaces or dependency
injection.
- Delete redundant comments when code and names communicate the same meaning.
- Replace long parameter lists or repeated data clumps with a small object.
- Redraw boundaries when one change causes shotgun edits across many modules.
- Do not use "compatibility" or "depends" as a reason without a concrete
migration need.

## VBI Boundaries

- VBIChartDSL, VQueryDSL, and VSeedDSL are core sources of truth.
- Builder owns DSL mutation. UI, CLI, agent, and app code should use Builder or
public package APIs instead of hand-writing internal mutations.
- Provider owns platform resource access and Builder creation.
- Practices must stay independent; move reusable behavior into a package or a
local utility instead of importing another practice's private source.

## Validation

Entropy-reduction work must prove both "it was deleted" and "behavior remains
Entropy-reduction work must prove both "it was removed" and "behavior remains
correct":

- Use `rg` to check deleted symbols, old names, TODO markers, compatibility
aliases, and deleted file names.
- When source changes affect generated files, run the generator before
repository gates.
- Run focused tests in the owning scope first, then run `pnpm run lint:check` and
`pnpm run typecheck`.
- Even for Markdown-only changes, run repository gates unless the user explicitly
narrows the validation scope.
- Use `rg` for deleted symbols, old names, TODO markers, compatibility aliases,
and deleted file names.
- Run generators before checks when source changes affect generated files.
- Run focused tests in the owning scope first, then `pnpm run lint:check` and
`pnpm run typecheck` when available.
- Report any validation that could not run and why.
4 changes: 2 additions & 2 deletions apps/vbi_cli/tests/provider-connectors.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { createVBIProviderWorkspace } from '@visactor/headless-bi-provider'

describe('provider workspace connectors', () => {
test('auto-registers the demo connector when opening a demo chart', async () => {
VBI.connectorMap.delete('demo')
VBI.connectors.unregister('demo')
const chartBuilder = VBI.chart.create(VBI.chart.createEmpty('demo'))
const chart = {
open: rs.fn(async () => chartBuilder),
Expand All @@ -29,7 +29,7 @@ describe('provider workspace connectors', () => {
})

test('builds seed dsl through the demo connector', async () => {
VBI.connectorMap.delete('demo')
VBI.connectors.unregister('demo')
const chartBuilder = VBI.chart.create(VBI.chart.createEmpty('demo'))
chartBuilder.chartType.changeChartType('bar')
chartBuilder.dimensions.add('area', (node) => node.setAlias('area').setEncoding('xAxis'))
Expand Down
2 changes: 1 addition & 1 deletion apps/vbi_cli/tests/provider-workspace.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ describe('createProviderWorkspace', () => {
})

expect(connectorId).toBe('cli-test-direct')
const connector = await VBI.getConnector('cli-test-direct')
const connector = await VBI.connectors.get('cli-test-direct')
await expect(connector.discoverSchema()).resolves.toEqual(schema)
})

Expand Down
4 changes: 2 additions & 2 deletions apps/vbi_provider/src/agent/connector-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const getConnectorId = (dsl: unknown) => {

const hasSchemaConnector = async (id: string) => {
try {
const connector = await VBI.getConnector(id)
const connector = await VBI.connectors.get(id)
return typeof connector.discoverSchema === 'function'
} catch {
return false
Expand All @@ -38,7 +38,7 @@ export const createVBIProviderConnectorRegistry = (
): VBIProviderConnectorRegistry => {
const register = (id: string, connector: VBIProviderConnectorRegistration) => {
if (!connector) throw new Error('connector registration is required')
VBI.registerConnector(id, connector as never)
VBI.connectors.register(id, connector as never)
return id
}
const ensureKnownConnector = async (connectorId: string) => {
Expand Down
2 changes: 1 addition & 1 deletion apps/vbi_provider/src/demo-connector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,6 @@ export const demoConnector: VBIConnector = {
}

export const registerDemoConnector = () => {
VBI.registerConnector(DEMO_CONNECTOR_ID, demoConnector)
VBI.connectors.register(DEMO_CONNECTOR_ID, demoConnector)
return DEMO_CONNECTOR_ID
}
Loading
Loading