Skip to content

feat(cli): tighten column-selection UX in init / schema build#398

Closed
coderdan wants to merge 1 commit intomainfrom
dan/init-column-selection-ux
Closed

feat(cli): tighten column-selection UX in init / schema build#398
coderdan wants to merge 1 commit intomainfrom
dan/init-column-selection-ux

Conversation

@coderdan
Copy link
Copy Markdown
Contributor

@coderdan coderdan commented May 3, 2026

Closes #396.

Stacked on #395 — review #395 first.

Summary

Three closely related fixes to the per-table column picker used by stash init and stash schema build:

  • No more silent skip-throughs. Drops required: true on the multiselect and handles the empty case explicitly: warn-and-reprompt if no other tables have been configured this run, or offer "Skip encryption for the <x> table" as an explicit escape if priors exist.
  • Already-encrypted columns are no longer toggleable. Columns whose Postgres type is eql_v2_encrypted are surfaced above the prompt as a "will be kept as-is" note and merged into the schema automatically. clack has no disabled-row affordance, so this is the closest we get to "displayed but not selectable". If every column in a table is already encrypted, the multiselect is skipped and we just confirm "keep as-is?".
  • EQL-managed tables are filtered at the SQL level. eql_v2_* (e.g. eql_v2_configuration) is excluded from introspection so it never appears as a candidate — encrypting EQL's own configuration store would brick EQL.

Plus a small UX win: after a user picks ≥1 column, init reads back the selection (Encrypt 3 columns in "users" (email, name, and ssn)?) and lets them back out into the picker if they misclicked.

API change

selectTableColumns now returns a discriminated { kind: 'schema' | 'skip' | 'cancel' } so buildSchemasFromDatabase can tell "user skipped this one table" from "user cancelled the whole flow". The outer return shape (SchemaDef[] | undefined) is unchanged.

Out of scope

introspectDatabase still only scans the public schema. Filed #397 to track the broader question of multi-schema support — would expand the surface here meaningfully and is better as its own change.

Test plan

  • Unit tests cover the new pure helpers (buildColumnDefs, joinNames, pgTypeToDataType, allSearchOps) — 18 new tests in lib/__tests__/introspect.test.ts.
  • Full CLI suite green: 172 / 172.
  • Build + biome clean.
  • Smoke test: re-run stash init against a project that already has one EQL column on a re-run; confirm pre-encrypted columns appear as a "kept as-is" note (not in the picker), the multiselect requires ≥1 new pick, and the confirmation summary fires before moving on.
  • Smoke test: configure one table, then on the next iteration submit the multiselect with nothing selected; confirm the "Skip encryption for the <x> table?" prompt appears.
  • Smoke test: against a DB with EQL installed, confirm eql_v2_configuration does not appear in the table list.

@coderdan coderdan requested a review from a team as a code owner May 3, 2026 04:32
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 3, 2026

🦋 Changeset detected

Latest commit: 5898748

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

This PR includes changesets to release 2 packages
Name Type
stash Patch
@cipherstash/e2e 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
Copy link
Copy Markdown

coderabbitai Bot commented May 3, 2026

Warning

Rate limit exceeded

@coderdan has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 26 minutes and 37 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4fcfbd93-9077-4930-8c1b-0b535a857472

📥 Commits

Reviewing files that changed from the base of the PR and between 9a37cb4 and 5898748.

📒 Files selected for processing (3)
  • .changeset/cli-init-column-selection-ux.md
  • packages/cli/src/commands/init/lib/__tests__/introspect.test.ts
  • packages/cli/src/commands/init/lib/introspect.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dan/init-column-selection-ux

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get your free trial and get 200 agent minutes per Slack user (a $50 value).


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
Review rate limit: 0/1 reviews remaining, refill in 26 minutes and 37 seconds.

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

@coderdan coderdan force-pushed the dan/init-column-selection-ux branch from 76e37c0 to a48645c Compare May 3, 2026 13:34
@coderdan coderdan force-pushed the dan/init-agent-handoff branch from 583d1be to 5314d3f Compare May 3, 2026 23:30
@coderdan coderdan force-pushed the dan/init-column-selection-ux branch 4 times, most recently from 006b3c5 to 4f5fd07 Compare May 4, 2026 01:04
Base automatically changed from dan/init-agent-handoff to main May 4, 2026 01:06
@coderdan coderdan force-pushed the dan/init-column-selection-ux branch from 4f5fd07 to a535705 Compare May 4, 2026 01:07
- Lift eql_v2_encrypted columns out of the multiselect; show them as a
  "will be kept as-is" note and merge them into the schema automatically.
  Closest we can get to "displayed but not toggleable" given clack has
  no disabled-row affordance.
- Drop required:true. Empty submissions now route to an explicit recovery:
  warn-and-reprompt with no priors, or "Skip encryption for the <x> table?"
  confirm when at least one other table has been configured this run.
- Confirmation summary after >=1 column picked, with re-prompt on no.
- All-already-encrypted edge case: skip the multiselect and confirm
  "keep as-is?" so we never offer an empty picker.
- selectTableColumns return type is now a discriminated
  { kind: 'schema' | 'skip' | 'cancel' } so the outer loop tells skip
  from cancel.
- Filter eql_v2_* tables out of introspection. eql_v2_configuration is
  EQL's own configuration store; encrypting it would break EQL itself.
@coderdan coderdan force-pushed the dan/init-column-selection-ux branch from a535705 to 5898748 Compare May 4, 2026 01:12
@coderdan
Copy link
Copy Markdown
Contributor Author

coderdan commented May 4, 2026

Heads-up: this is now redundant — the column-picker this PR tightens has been removed entirely in #357.

The reshape: post-#395 the agent (Claude / Codex / Cursor / Windsurf / Cline / wizard) drives schema decisions in conversation after init hands off, not via an interactive picker at init time. build-schema.ts now writes a heavily-commented placeholder client and exits; the user's real schema files stay authoritative and the agent edits them as part of executing whichever flow the user picked (add new encrypted column / migrate existing column to encrypted).

That makes each fix here moot:

  • Silent skip-throughs: no multiselect to skip through.
  • "Already encrypted columns shown but not toggleable": no list to render.
  • "Filter eql_v2_* tables out of introspection": no introspection at this step.
  • The { kind: 'schema' | 'skip' | 'cancel' } discriminated return: caller is gone.

The underlying instinct here was right — making sure init can't accidentally re-encrypt or step on EQL's own tables. Those guards now live one layer down in the agent's orient-and-route prompt + the stash-encryption skill, which the agent reads before touching schema.

The smaller UX win (read-back-the-selection confirm) is also moot, but worth keeping in mind if we ever reintroduce a picker for a Proxy-mode workflow that doesn't have an agent in the loop.

#396 (the underlying bug this PR closes) is gone with the picker — proposing we close this PR as well-intended-but-superseded.

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.

stash init: column picker has three usability/safety issues

1 participant