You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(cli,scripts): per-integration query guidance, and linters that catch their own rot
Follow-up to the #772 review findings, addressing what the review of that
review turned up.
`stash init`'s setup prompt named `createEncryptionOperators` unconditionally.
That symbol is exported by `@cipherstash/stack-drizzle` alone, so a Supabase or
Prisma Next project was sent after a package that is not in its dependency
tree — the previous `protectOps.eq` was wrong for everyone, this was wrong for
three integrations out of four. Step 5 now branches through
`queryOperatorGuidance()`, following the `migrationCommands()` pattern already
in the file: `ops.eq` for Drizzle, the `encryptedSupabase` wrapper's own
filters for Supabase, the `eql*` column operators for Prisma Next, and
`client.encryptQuery(...)` for plain Postgres — which is also pointed at
`stash-encryption`, since it is the default integration and installs no
integration skill, making "see the integration skill" a dangling pointer.
The package-path linter reported an untracked-but-present package as "does not
exist" — finding 15's false alarm pointed the other way, at a directory sitting
right there on disk. The live set now unions `git ls-files --others
--exclude-standard`. `--directory` is deliberately not passed: it collapses an
all-ignored directory to one entry and would resurrect exactly the `dist/`-and-
`node_modules/` shells the linter exists to catch (verified both ways). git
failing now exits 2 with an actionable message instead of a raw ENOENT stack
trace and exit 1, which was indistinguishable from a genuine lint failure, and
an empty live set refuses to run rather than flagging every reference at once.
The `scans scripts/ but not its fixtures` test asserted the repo was clean —
byte-for-byte what the suite's first test already asserted, and passing whether
or not `scripts` was in TARGETS. It now plants an offender in the scanned
directory. Verified by mutation: it dies when `scripts` is dropped, as the
untracked-package test dies without the union and the git-failure test dies
when the exit code is flipped.
The runners linter now requires each allowlist entry to exist and to still
contain an unexcused `npx` literal. It immediately found a second stale entry
the sibling linter structurally cannot see, since it only matches the
`packages/<name>` shape: `setup-prompt.ts` has had no `npx` since its switch
moved to `utils.ts`. Removed.
Also corrects two claims: the test comment saying `packages/drizzle` never
existed in git history (it was added speculatively by c671560, became
load-bearing 31 minutes later with 9d259e6, and rotted when 413ca39 deleted
the package), and the `AnyEncryptedTable` doc comment still promising v3 is
"purely additive and no existing caller has to change" — true of decrypt, false
of encrypt since the v2 write overloads were removed.
return'query paths filter through the `encryptedSupabase` wrapper (`es.from("users").select(...).eq("email", value)`) — it encrypts filter operands for encrypted columns automatically; see the integration skill'
85
+
}
86
+
if(integration==='prisma-next'){
87
+
return'query paths use the `eql*` operators on the column inside `.where()` (`u.email.eqlEq(value)`, `eqlMatch`, `eqlGt`, …) — see the integration skill'
88
+
}
89
+
if(integration==='postgresql'){
90
+
return'query paths encrypt the search term first with `client.encryptQuery(value, { table, column, queryType })` and compare against the encrypted column — see the `stash-encryption` skill (a plain Postgres project gets no integration skill)'
91
+
}
92
+
return'query paths use the right operator (`ops.eq`, from `createEncryptionOperators(client)`) — see the integration skill'
93
+
}
94
+
71
95
functionbullet(line: string): string{
72
96
return`- ${line}`
73
97
}
@@ -280,7 +304,7 @@ export function renderImplementPrompt(ctx: SetupPromptContext): string {
280
304
"2. Edit the user's real schema file (`src/db/schema.ts` or wherever they keep it) to declare the new encrypted column. Use the patterns in the integration skill — the `types.*` domain factories from `@cipherstash/stack-drizzle` for Drizzle, and the `types.*` factories from `@cipherstash/stack/eql/v3` (via `encryptedTable`, passed as `schemas`) for Supabase. Encrypted columns must be **nullable `jsonb`** at creation time. Never `.notNull()`.",
281
305
`3. Generate the schema migration${migration ? ` — \`${migration.generate}\` (${migration.tool})` : " using the project's existing migration tooling"}.`,
282
306
`4. Show the user the generated SQL before applying${migration ? ` — \`${migration.apply}\`` : ''}.`,
283
-
'5. Wire the column through the application code: insert paths encrypt before write, select paths decrypt after read, query paths use the right operator (`ops.eq`, from `createEncryptionOperators(client)` — see the integration skill).',
307
+
`5. Wire the column through the application code: insert paths encrypt before write, select paths decrypt after read, ${queryOperatorGuidance(ctx.integration)}.`,
284
308
'6. Verify with a round-trip: insert a record, select it back, confirm the value decrypts and the search ops work.',
0 commit comments