Skip to content

feat(shared): lint rule flags aiSearch indexes missing columns - #513

Merged
MarioCadenas merged 1 commit into
mainfrom
feat/ai-search-columns-lint
Aug 7, 2026
Merged

feat(shared): lint rule flags aiSearch indexes missing columns#513
MarioCadenas merged 1 commit into
mainfrom
feat/ai-search-columns-lint

Conversation

@MarioCadenas

@MarioCadenas MarioCadenas commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

What

Adds an appkit lint rule, ai-search-index-requires-columns, that flags an aiSearch plugin config whose indexes have no usable columns.

Why

In production the plugin does not auto-discover an index's columns — that's dev-only (ai-search.ts _autoDiscoverColumns, gated on NODE_ENV === "development"). So a config that omits columns works in dev but fails at query time in prod (Field 'columns' must be specified). appkit lint is the pre-deploy static catch, complementing the existing dev nudges (auto-discovery banner + manifest scaffolding.rules).

How

aiSearch({ indexes: { a: {...}, b: {...} } }) — "every index has non-empty columns" can't be expressed as a single ast-grep pattern, so this adds an optional find?: (root) => SgNode[] escape hatch to the Rule interface. Flat pattern rules and this structural rule share the same reporting path. lintFile now delegates to an exported lintSource() so rules can be tested against in-memory source.

Flagged (fail in prod):

  • bare aiSearch(), aiSearch({}), aiSearch({ indexes: {} }) — all fall back to the env-seeded default index, which has no columns
  • any index object missing columns, or with columns: []

Passed (can't statically prove bad — no false positives):

  • columns: SOME_CONST, indexes: buildIndexes(), aiSearch(dynamicConfig), spread index objects ({ ...base })

The violation points at the offending index alias, not the whole call.

Test plan

  • packages/shared/src/cli/commands/lint.test.ts — 14 vitest cases covering every pass/flag branch above
  • pnpm exec vitest run lint — 14/14 pass
  • pnpm -r typecheck — clean
  • pnpm exec biome check on changed files — clean
  • Manual: appkit lint over a temp server.ts with a bad aiSearch() flags it and exits 1

Manual testing + output

No columns added to the plugin:

createApp({
  plugins: [
    aiSearch(),
    server(),
  ],
}).catch(console.error);

or this

createApp({
  plugins: [
 aiSearch({ indexes: { default: { columns: [] } } }),
    server(),
  ],
}).catch(console.error);

will produce this output

➜ npm run lint:ast-grep

> appkit-pr-513-app@1.0.0 lint:ast-grep
> appkit lint

Scanning 11 TypeScript files...

Found 1 violation(s):

server/server.ts:6:5
  ai-search-index-requires-columns: AI Search index has no `columns`; it will fail in production (columns are auto-discovered only in dev). Set `columns` explicitly on each index.
  > aiSearch()

Columns added

createApp({
  plugins: [
    aiSearch({ indexes: { default: { columns: ['my_column', 'column'] } } }),
    server(),
  ],
}).catch(console.error);
➜ npm run lint:ast-grep

> appkit-pr-513-app@1.0.0 lint:ast-grep
> appkit lint

Scanning 11 TypeScript files...

No ast-grep lint violations found.

@MarioCadenas
MarioCadenas requested a review from a team as a code owner August 5, 2026 12:55
@MarioCadenas
MarioCadenas requested review from atilafassina and removed request for a team August 5, 2026 12:55
@MarioCadenas
MarioCadenas force-pushed the feat/ai-search-columns-lint branch from 39079f0 to 2c78d70 Compare August 5, 2026 13:38
Base automatically changed from feat/export-vector-search-plugin to main August 5, 2026 14:50
@MarioCadenas
MarioCadenas force-pushed the feat/ai-search-columns-lint branch from 2c78d70 to 1848b63 Compare August 5, 2026 15:02
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

📦 Bundle size report

Compared against bundle-size-baseline.json (main).

✅ No size changes vs the baseline.

@databricks/appkit

npm tarball (packed): 827 KB — gzipped download (dist + bin; excludes release-only docs/NOTICE).

dist raw gzip
JS (runtime) 855 KB 298 KB
Type declarations 314 KB 108 KB
Source maps 1.7 MB 557 KB
Other 11 KB 3.7 KB
Total 2.8 MB 967 KB
Per-entry composition (own code — deps external (as shipped))
Entry Initial (gz) Lazy (gz) Total (gz) node_modules (min) Own code (min)
. 87 KB 2.5 KB 90 KB external 285 KB
./beta 48 KB 459 B 48 KB external 140 KB
./type-generator 20 KB 0 B 20 KB external 57 KB

Chunks:

Entry Chunk Load Size (gz)
. index.js initial 83 KB
. utils.js initial 4.0 KB
. remote-tunnel-manager.js lazy 2.5 KB
./beta beta.js initial 32 KB
./beta stream-manager.js initial 5.8 KB
./beta wide-event-emitter.js initial 3.2 KB
./beta databricks.js initial 3.0 KB
./beta configuration.js initial 2.1 KB
./beta service-context.js initial 1.3 KB
./beta client.js initial 431 B
./beta client-options.js initial 220 B
./beta supervisor-api.js lazy 194 B
./beta databricks.js lazy 142 B
./beta index.js lazy 123 B
./type-generator index.js initial 20 KB

@databricks/appkit-ui

npm tarball (packed): 309 KB — gzipped download (dist + bin; excludes release-only docs/NOTICE).

dist raw gzip
JS (runtime) 362 KB 121 KB
Type declarations 209 KB 76 KB
Source maps 693 KB 227 KB
CSS 16 KB 3.3 KB
Total 1.3 MB 427 KB
Per-entry composition (consumer bundle — deps bundled, peerDeps external)
Entry Initial (gz) Lazy (gz) Total (gz) node_modules (min) Own code (min)
./js 4.3 KB 49 KB 54 KB 208 KB 12 KB
./js/beta 20 B 0 B 20 B 0 B 0 B
./react 429 KB 49 KB 478 KB 1.3 MB 168 KB
./react/beta 1.0 KB 0 B 1.0 KB 0 B 1.9 KB

Chunks:

Entry Chunk Load Size (gz)
./js index.js initial 4.2 KB
./js chunk initial 120 B
./js apache-arrow lazy 49 KB
./js/beta beta.js initial 20 B
./react index.js initial 427 KB
./react tslib initial 2.1 KB
./react apache-arrow lazy 49 KB
./react/beta beta.js initial 1.0 KB

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

🤖 AppKit PR bot

🔬 Run evals

Start an eval for this PR from the evals-monitor app: Go to Evals Monitor →

📦 Try this PR's app template

Scaffolds a new app from this PR's SDK build. Run it in any folder (requires the GitHub CLI — gh auth login — and the Databricks CLI):

gh run download 31169096353 -R databricks/appkit -n appkit-template-0.55.1-pr.1948e4a-feat-ai-search-columns-lint-513 -D appkit-pr-513 \
  && unzip -o "appkit-pr-513/appkit-template-0.55.1-pr.1948e4a-feat-ai-search-columns-lint-513.zip" -d "appkit-pr-513" \
  && databricks apps init --template "appkit-pr-513"

The template pins @databricks/appkit and @databricks/appkit-ui to tarballs built from this branch, so the scaffolded app runs against this PR's code.

Add ai-search-index-requires-columns to `appkit lint`. In production the
plugin does not auto-discover an index's columns (dev-only), so a missing
or empty `columns` fails at query time. This is the pre-deploy static
catch.

Uses a structural traversal via a new optional `find` escape hatch on
Rule, since "every index in indexes:{...} has non-empty columns" isn't a
single ast-grep pattern. Flags bare aiSearch(), aiSearch({}), empty
indexes, and any index missing columns or with columns:[]; passes
dynamic/const/spread configs to avoid false positives. Extracts
lintSource() so rules can be tested against in-memory source.

Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
@MarioCadenas
MarioCadenas force-pushed the feat/ai-search-columns-lint branch from 1848b63 to 842ff5f Compare August 7, 2026 10:11
@MarioCadenas
MarioCadenas enabled auto-merge (squash) August 7, 2026 10:35

@atilafassina atilafassina left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think i found one edge-case with a spread operator, but let's not defer this plugin on that. It delivers well despite a small false-positive that arguably would be against best practice anyway 🐑 it

@MarioCadenas
MarioCadenas merged commit 00866fa into main Aug 7, 2026
14 checks passed
@MarioCadenas
MarioCadenas deleted the feat/ai-search-columns-lint branch August 7, 2026 13:20
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.

2 participants