Skip to content

feat(cli): per-column EQL v3 domain picker in stash schema build#755

Merged
tobyhede merged 12 commits into
mainfrom
worktree-schema-build-v3-picker
Jul 22, 2026
Merged

feat(cli): per-column EQL v3 domain picker in stash schema build#755
tobyhede merged 12 commits into
mainfrom
worktree-schema-build-v3-picker

Conversation

@tobyhede

@tobyhede tobyhede commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

What

Replaces the v2 capability-tuple column picker in stash schema build with a per-column EQL v3 domain picker, and deletes the SearchOp / allSearchOps / v3DomainFactory bridge.

Previously the introspect → pick → codegen path carried a v2 SearchOp[] tuple through an intermediate ColumnDef and translated it to a v3 domain name at codegen time. This moves the domain decision up into the interactive picker — the user chooses a concrete v3 domain per column (candidates filtered by the column's Postgres type) — stores it directly on ColumnDef, and has codegen read it verbatim.

Why

Unblocks the EQL v2 removal (#707, #751): no v2 capability vocabulary remains in the schema build path.

Behaviour

  • Each selected column picks a concrete v3 domain (TextSearch, IntegerOrd, TextEq, …) via a select prompt, with capability-descriptive hints.
  • Defaults preserve the previous posture: widest searchable domain per type (stringTextSearch, numberIntegerOrd, dateDateOrd).
  • booleanBoolean and jsonJson are single storage-only domains, assigned without a prompt.
  • pgTypeToDataType's coarse 5-type collapse is unchanged (out of scope: widening to the full numeric/date lattice).
  • No change to stash init / generatePlaceholderClient.

Tests

  • introspect.test.tspgTypeToDataType, candidateDomains, defaultDomain, and the rewritten selectTableColumns (every cancellation branch + eql-pre-selected path, @clack/prompts mocked).
  • utils-codegen.test.ts — both generators, plus an it.each round-trip over all 13 V3Domain values; asserts no searchOps residue.
  • build-flow.integration.test.ts — real buildSchemasFromDatabase → real generateClientFromSchemas with only pg and @clack/prompts mocked (multi-table loop, early-exit, empty-schema edges).

Verification

  • pnpm --filter stash build — clean, no type errors
  • pnpm --filter stash test — 815 pass (56 files)
  • pnpm run code:check — exit 0
  • CodeRabbit review — 0 findings

Docs / changeset

  • Updated the schema build description in skills/stash-cli/SKILL.md to describe the domain picker.
  • Added a stash patch changeset.

Summary by CodeRabbit

  • New Features
    • schema build now assigns a specific encryption domain to each selected column.
    • Boolean and JSON columns are automatically assigned their supported storage domain.
    • Generated clients now use the selected domain mappings for PostgreSQL and Drizzle integrations.
  • Documentation
    • Updated CLI guidance to describe per-column domain selection and defaults.
  • Bug Fixes
    • Preserved existing encrypted-column selections during schema setup.

Summary by CodeRabbit

  • New Features
    • schema build now lets you select an encryption domain for each column.
    • Boolean and JSON columns are automatically assigned compatible storage domains.
    • Generated clients now use the selected column domains for encryption configuration.
  • Documentation
    • Updated CLI guidance to describe the per-column domain selection workflow.

Summary by CodeRabbit

  • New Features
    • stash schema build now lets you choose an encryption domain for each selected column.
    • Boolean and JSON columns are assigned their supported domain automatically.
    • Generated encryption clients now use the selected EQL v3 domains for each column.
  • Documentation
    • Updated CLI guidance to describe per-column domain selection and generation flow.

@tobyhede
tobyhede requested a review from a team as a code owner July 22, 2026 03:49
@changeset-bot

changeset-bot Bot commented Jul 22, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 9bf2266

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 11 packages
Name Type
stash Patch
@cipherstash/basic-example Patch
@cipherstash/e2e Patch
@cipherstash/stack Patch
@cipherstash/stack-drizzle Patch
@cipherstash/stack-supabase Patch
@cipherstash/prisma-next Patch
@cipherstash/wizard Patch
@cipherstash/bench Patch
@cipherstash/test-kit Patch
@cipherstash/prisma-next-example Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@tobyhede, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 27 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9779e3ea-e16a-4a0e-8021-62d5121249e1

📥 Commits

Reviewing files that changed from the base of the PR and between 9a0f79b and 9bf2266.

📒 Files selected for processing (3)
  • packages/cli/src/commands/init/__tests__/utils-codegen.test.ts
  • packages/cli/src/commands/init/lib/__tests__/build-flow.integration.test.ts
  • packages/cli/src/commands/init/utils.ts
📝 Walkthrough

Walkthrough

stash schema build now assigns concrete EQL v3 domains per column, automatically handles boolean and JSON domains, and generates clients directly from those selections. The legacy SearchOp model and v3DomainFactory mapping are removed, with tests and documentation updated.

Changes

EQL v3 domain selection

Layer / File(s) Summary
Domain contract and interactive picker
packages/cli/src/commands/init/types.ts, packages/cli/src/commands/init/lib/introspect.ts, packages/cli/src/commands/init/lib/__tests__/introspect.test.ts
Column definitions now store V3Domain values; candidate, default, per-column selection, cancellation, and factory-drift behavior are implemented and tested.
Direct domain code generation
packages/cli/src/commands/init/utils.ts, packages/cli/src/commands/init/__tests__/utils-codegen.test.ts
PostgreSQL and Drizzle generators emit selected types.<Domain> factories, with coverage for every supported domain.
End-to-end validation and documentation
packages/cli/src/commands/init/lib/__tests__/build-flow.integration.test.ts, .changeset/schema-build-v3-domain-picker.md, skills/stash-cli/SKILL.md
Build-flow tests cover table traversal and empty schemas; the changeset and CLI documentation describe the v3 domain-selection flow.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant SchemaBuild
  participant Database
  participant ClientGenerator
  User->>SchemaBuild: start schema build
  SchemaBuild->>Database: introspect tables and columns
  Database-->>SchemaBuild: return PostgreSQL column metadata
  SchemaBuild->>User: prompt for column domains
  User-->>SchemaBuild: provide selected V3Domain values
  SchemaBuild->>ClientGenerator: generate schemas with selected domains
  ClientGenerator-->>User: output typed encryption client
Loading

Possibly related issues

  • cipherstash/stack issue 707 — Covers the CLI column-picker migration to concrete EQL v3 domains.
  • cipherstash/stack issue 751 — Tracks replacing SearchOp selection with per-column EQL v3 domain selection.

Possibly related PRs

Suggested reviewers: freshtonic

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: per-column EQL v3 domain selection in stash schema build.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch worktree-schema-build-v3-picker

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@tobyhede
tobyhede requested review from coderdan and freshtonic July 22, 2026 03:50

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.changeset/schema-build-v3-domain-picker.md:
- Around line 7-9: Revise the Boolean/Json capability descriptions in both
.changeset/schema-build-v3-domain-picker.md lines 7-9 and
skills/stash-cli/SKILL.md line 461: describe Boolean as storage-only, while
documenting Json’s encrypted-JSONB containment and selector query capabilities
instead of labeling it storage-only.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6fb60892-bdab-4cb3-bb61-c075bbf44c67

📥 Commits

Reviewing files that changed from the base of the PR and between 6ce5381 and da91354.

📒 Files selected for processing (8)
  • .changeset/schema-build-v3-domain-picker.md
  • packages/cli/src/commands/init/__tests__/utils-codegen.test.ts
  • packages/cli/src/commands/init/lib/__tests__/build-flow.integration.test.ts
  • packages/cli/src/commands/init/lib/__tests__/introspect.test.ts
  • packages/cli/src/commands/init/lib/introspect.ts
  • packages/cli/src/commands/init/types.ts
  • packages/cli/src/commands/init/utils.ts
  • skills/stash-cli/SKILL.md

Comment thread .changeset/schema-build-v3-domain-picker.md Outdated
tobyhede added 2 commits July 22, 2026 13:57
- reuse computed candidateDomains in the picker via defaultDomain(options)
- refresh stale InitState.schemas comment (search ops -> column domains)
- drop non-null assertion in build-flow integration test
Json's v3 domain supports encrypted-JSONB containment + selector queries;
only Boolean is storage-only. Fixes the changeset, the candidateDomains
doc comment, and the schema build skill copy, which had lumped the two.
Comment thread packages/cli/src/commands/init/lib/introspect.ts Outdated
Comment thread packages/cli/src/commands/init/lib/introspect.ts Outdated
Comment thread packages/cli/src/commands/init/lib/introspect.ts Outdated
Comment thread packages/cli/src/commands/init/types.ts

@coderdan coderdan 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.

Some minor suggestions but otherwise LGTM

Address PR #755 review (coderdan):
- Picker hints now name the operators/functions each domain answers.
  Corrected two inaccurate reviewer suggestions: TextMatch uses the
  eql_v3.matches() function, not the FTS @@ operator; Json supports @> and
  -> (plus scalar compare/order at a selector path), not <@ / ->>.
- Add a runtime drift guard asserting every domain the picker can offer is
  a real @cipherstash/stack/eql/v3 factory. Enforced by vitest (CI) because
  packages/cli has no tsc --noEmit step — a type-only alias is not checked.
  Answers the 'avoid drift' review point without deriving V3Domain from the
  40-key types superset (which would over-widen the curated 13).

@auxesis auxesis 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.

Test coverage review — v3 domain picker

Strong coverage overall. The new candidateDomains, defaultDomain(DataType), the rewritten selectTableColumns loop (all three cancellation branches, single-domain skip, eql pre-selection), and the 13-domain codegen round-trip over both generators are well exercised. Three verified gaps remain, all clustered on the default the picker offers — the behaviour the changeset and skills/stash-cli/SKILL.md explicitly promise ("defaulting to the widest searchable domain"). None is a blocker; all are cheap to close with the mocks already in place.

Review stats

Source Raw Survived
claude (claude-opus-4-8) [test-gap] 2 2
codex (gpt-5.5) [test-gap] 2 2

Merged to 3 unique findings. Cross-model overlap: 1 kept finding (the picker initialValue gap) was corroborated by both models.

Additional findings not posted inline

None — all 3 kept findings are posted inline (under the 8-comment cap).

Out-of-scope notes raised by claude, not posted (pre-existing code the diff does not touch): the connection-failure and p.confirm cancel branches of buildSchemasFromDatabase (introspect.ts), and generateClientFromSchemas having no prisma-next/default case.

Comment thread packages/cli/src/commands/init/lib/introspect.ts
Comment thread packages/cli/src/commands/init/lib/introspect.ts
Comment thread packages/cli/src/commands/init/utils.ts
Address @auxesis review threads on PR #755:
- assert the picker passes initialValue: defaultDomain(options) so the
  widest searchable domain is pre-selected (a regression to the narrowest
  otherwise passes the whole suite)
- assert defaultDomain's candidate-list (array) overload returns the last value
- cover the supabase codegen routing (shares the generic generator with
  postgresql; previously only postgresql/drizzle were exercised)

@auxesis auxesis 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.

@tobyhede thanks for this change, and thanks for addressing feedback.

…gen switch

Close the out-of-scope gaps noted (not posted inline) in the PR #755 review:
- generateClientFromSchemas now handles every Integration: prisma-next
  throws a clear error instead of silently returning undefined, so the
  switch is total and a future integration can't fall through to undefined.
- test buildSchemasFromDatabase's connection/introspection-failure branch
  (returns undefined, doesn't propagate) and the 'another table?' cancel
  branch (aborts, distinct from declining).

Internal robustness + coverage only; prisma-next is unreachable from
schema/build.ts (which produces only supabase|postgresql), so no
user-observable behaviour change — no changeset.
@tobyhede
tobyhede merged commit 2aeba94 into main Jul 22, 2026
10 checks passed
@tobyhede
tobyhede deleted the worktree-schema-build-v3-picker branch July 22, 2026 06:42
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.

3 participants