skills: add the EQL v3 type → predicate → domain → index capability matrix - #908
skills: add the EQL v3 type → predicate → domain → index capability matrix#908coderdan wants to merge 1 commit into
Conversation
Picking the wrong `types.*` factory is silent at authoring time -- no type error, no runtime warning, just a predicate that never runs. The skills documented the capability suffixes and the families they apply to, but never the 40 concrete factories in one lookup, so answering "can `types.Double` do a range query" meant composing two tables and knowing the exceptions. It cannot: `types.Double` is storage-only. One row per factory: column domain, supported predicates, the extractor to index through, and whether it works on managed Postgres. Plus a note on which schema holds what, and why the Supabase grants have to cover `eql_v3` and `eql_v3_internal` both. Two corrections fell out of writing it: - The `Ord` vs `OrdOre` callout said the install "disables" the `_ord_ore` domains. Precisely: the bundle adds an always-raising CHECK, so a write fails -- unusable, not merely unindexed. It now says that, notes RDS and Aurora do support ORE while cloud-hosted Supabase does not, and points at `eql preflight` / `eql status` instead of asking the reader to guess. - The `stash-postgres` naming table omitted `types.TextOrdOre` entirely; its `<N>` shorthand covers only the numeric and temporal families. A test derives the matrix from the `types` namespace and fails if the skill disagrees -- every factory present once, mapped to the domain it builds, naming the extractors it emits and none it does not. Verified to fail on a wrong domain name and on a storage-only row claiming a predicate. Claude-Session: https://claude.ai/code/session_01AwM5Cm5ddasXozb6stxPR1
🦋 Changeset detectedLatest commit: c6514fe The changes in this PR will be included in the next version bump. This PR includes changesets to release 11 packages
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 |
freshtonic
left a comment
There was a problem hiding this comment.
Approve. This is the right artifact for the failure mode (silent-at-authoring-time factory choice → Ctrl-F for the literal factory name), and the pinning test makes it the rare skills table that cannot silently drift. CI green.
What I verified:
- The test's derivation is sound: it probes each factory's actual
getEqlType()andbuild().indexes, requires exactly one row per factory with none extra, and asserts extractors both positively and negatively. The negative half is the one that matters — it's what stops the matrix from ever telling someone to put aneq_termindex on a numeric_ordcolumn, the precise trap the prose calls out. - The
backticked()matcher genuinely closes the substring trap: in aTextOrdOrerow,`ord_term_ore`cannot false-match the`ord_term`needle (the char afterord_termis_, not a backtick), so the negative assertions are satisfiable and meaningful. I walked theTextOrd/TextOrdOre/IntegerOrdrows against their factories' index kinds and each holds. - Pin-over-generate is the right call here — the repo has no markdown codegen, the table is embedded in hand-authored prose, and
skill-supabase-apply.test.tsalready establishes the pattern. The author confirmed the test fails non-vacuously by mutating a domain name and a storage-only row; the section-scopedmatrixRows()(heading → closing paragraph) prevents an example elsewhere in the file satisfying a row. - The two corrections are accurate against the stack: the
eql_ore_unavailableCHECK means ⛔-unusable rather than⚠️ -unindexable on the 3.0.4 bundle (consistent with #907's model, which this stacks on), andtypes.TextOrdOregenuinely fell through thestash-postgresnaming table's<N>shorthand gap. - Cross-links from
stash-indexingandstash-postgresdesignate one canonical table with the others as views of it — the structure that prevents three tables drifting into three answers. Changeset present (stashpatch, correct for a skills-only change).
One cosmetic observation, not blocking: the "Managed Postgres" column header carries ⛔ on every ORE row while the callout below explains it is not a blanket managed-Postgres rule (RDS/Aurora clear the gate). The "see below" hedge in the cells covers it, but a header like "Unprivileged install" would make the column self-consistent if the table is ever revised.
Fixes #892. Stacked on #907 (→ #906) — this PR's diff is the last commit only.
The gap
Picking the wrong factory is silent at authoring time. There is no type error and no runtime warning — the predicate you wanted simply does not run, and you find out from a query. So the cost of not documenting it lands late, on someone who has already written the schema.
The skills had the pieces: a capability-suffix table, a family table, a naming rule, an indexing table in
stash-indexing, a query-domain table instash-postgres. What they did not have was one row you can look your factory up in. Answering "cantypes.Doubledo a range query" meant composing two tables and knowing the exceptions. It cannot —types.Doubleis storage-only.What this adds
A capability matrix high in
skills/stash-encryption/SKILL.md, inside### The types Namespace: one row per factory, all 40, with the Postgres column domain, the predicates it supports, the extractor to index it through, and whether it works on managed Postgres. Cross-linked fromstash-indexing("use it to pick the type; use this page to index it") andstash-postgres("this one is the SQL-side view of it").Rows are explicit rather than
types.<N>Ord-style shorthand on purpose: the reporter's move is Ctrl-F fortypes.Double, which only works if the literal string is on the page. The three things the table then calls out in prose are the ones the shorthand was hiding — that a bare factory name answers nothing, that the numeric_orddomains deliberately have noeq_termoverload (equality rides the injective ordering term, so adding an equality index there indexes a function that does not exist), and thatOrdandOrdOremint non-cross-comparable terms so you cannot swap one for the other without re-encrypting.Also the schema-layout note the issue asked for:
publicholds the column domains,eql_v3the query domains and operator functions,eql_v3_internalthe index-term types — a single encrypted comparison touches all three, which is why the Supabase grants have to cover the last two, and whypublicsurviving a reinstall is what keeps your columns.Two corrections
The⚠️ . The callout now also states that RDS and Aurora do support ORE while cloud-hosted Supabase does not (so this is not a blanket managed-Postgres rule), and points at
OrdvsOrdOrecallout was imprecise. It said the install "disables the_ord_oredomains it cannot support". Precisely: the bundle adds an always-raisingeql_ore_unavailableCHECK, so a write to such a column fails. Unusable, not merely unindexed — the matrix marks those rows ⛔ rather thanstash eql preflight/eql statusinstead of asking the reader to guess which case they are in.Note this is where skills: add a type → predicate → domain → index capability matrix for the EQL v3 types surface #892's own example table is out of date: it proposes "⚠️ usable, not indexable" for the ORE rows, which was true of EQL 3.0.2 (
stash-encryption: the_ord_oredomains are not disabled on managed Postgres — only the opclass is skipped #759) but is not true of 3.0.4, the pinned bundle. Same staleness as Detect the ORE-unavailable case at install time, and scaffold OPE domains on managed Postgres #891's premise; the table follows the bundle.stash-postgres's naming table omittedtypes.TextOrdOreentirely — its<N>shorthand expands over the numeric and temporal families only, so the text ORE domain fell through the gap between the explicit text rows and the generic ones. Added.Keeping it honest
The issue suggested generating the table from
types.ts. I pinned it instead: this repo has no markdown codegen, and the skills are hand-authored prose wrapped around their tables, so a generator would own a fragment of a file humans edit. Pinning is the pattern already used for skill content (skill-supabase-apply.test.ts), and it fails in the same place a generator would — the moment source and prose disagree.packages/stack/__tests__/eql-v3-capability-matrix-skill.test.tsderives from thetypesnamespace by probing each factory (getEqlType(),build().indexes) and asserts:eq_termindex on a numeric_ordcolumn;I confirmed it fails rather than passing vacuously, by breaking a domain name and by giving a storage-only row a predicate — both caught.
pnpm --filter @cipherstash/stack test(1050) andpnpm --filter stash test(1331) green.https://claude.ai/code/session_01AwM5Cm5ddasXozb6stxPR1