Skip to content

Commit 0e10acf

Browse files
authored
Merge pull request #98 from linuxfoundation/update-validate-search-filters-skill
skills: improve validate-search-filters with aggregation-first approach
2 parents 5a3dbe1 + e7ec2ce commit 0e10acf

1 file changed

Lines changed: 58 additions & 31 deletions

File tree

  • .agents/skills/validate-search-filters

.agents/skills/validate-search-filters/SKILL.md

Lines changed: 58 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,12 @@ and optionally apply fixes.
3131
accepts both `project_uid` and `committee_uid` can only send one at a time.
3232
- `handleSearchPastMeetingParticipants` and `handleSearchPastMeetingSummaries`
3333
are dedicated handlers — each owns its own filter logic independently.
34-
- When sampling documents, use `"size": 3` to keep output small. Use
35-
`_source` filtering to request only `tags` and `parent_refs` fields.
34+
- **Prefer count-only queries over random sampling.** A handful of random
35+
documents proves nothing — you can get lucky and see the right fields while
36+
90% of the corpus has them missing. Always run `"size": 0` prefix count
37+
queries first. Only pull sample documents (`"size": 3`) as a secondary
38+
debugging aid when a count is zero or surprising (e.g. to understand what
39+
fields are actually present on that resource type).
3640

3741
## Step 1 — Discover infrastructure
3842

@@ -64,19 +68,32 @@ Substitute the kubectl context as needed to target dev vs. prod.
6468

6569
## Step 2 — Enumerate search tools and their filter mappings
6670

67-
Read every `*Args` struct in `internal/tools/` and record how each filter
71+
**Do not rely solely on the reference table below — always grep the codebase
72+
first** to find every file that calls `QueryResources`. The table may be out of
73+
date if new tools have been added since it was last updated.
74+
75+
```bash
76+
grep -rEn "QueryResources|QueryResourcesPayload" internal/tools/ | grep -v "_test.go"
77+
```
78+
79+
For each file that appears, read the handler and record how each filter
6880
parameter is sent to the query service. The mechanisms are:
6981

7082
| Mechanism | Query service field | Index field |
7183
|---|---|---|
7284
| `payload.Parent = "<type>:<uid>"` | `Parent` | `parent_refs` |
7385
| `payload.Tags = ["<key>:<value>"]` | `Tags` | `tags` |
7486
| `payload.Filters = ["<field>:<value>"]` | `Filters` | top-level doc fields |
87+
| `payload.FiltersAll = ["<field>:<value>"]` | `FiltersAll` | top-level doc fields (AND semantics) |
7588
| `payload.Name = "<value>"` | `Name` | `name` (text search) |
7689
| `payload.DateField` / `DateFrom` / `DateTo` | date range | date fields |
7790

78-
Current tools using the query service SDK and their structured filter parameters
79-
(update this list if new tools are added):
91+
Only `Parent`, `Tags`, `Filters`, and `FiltersAll` are structural filters that
92+
map to indexed fields — these are the ones to validate. `Name` and date fields
93+
are query-time text/range operations and do not need index field verification.
94+
95+
Reference table of known tools and their structured filter parameters (verify
96+
against the grep output above before trusting this):
8097

8198
| Tool | Resource type | Parameter | Mechanism | Sent as |
8299
|---|---|---|---|---|
@@ -98,8 +115,13 @@ Current tools using the query service SDK and their structured filter parameters
98115
| `search_past_meeting_participants` | `v1_past_meeting_participant` | `project_uid` | Parent (fallback) | `project:<uid>` |
99116
| `search_past_meeting_summaries` | `v1_past_meeting_summary` | `past_meeting_id` | Parent (preferred) | `past_meeting:<meeting_and_occurrence_id>` |
100117
| `search_past_meeting_summaries` | `v1_past_meeting_summary` | `project_uid` | Parent (fallback) | `project:<uid>` |
101-
102-
Re-read the handler code to verify this table is current before proceeding.
118+
| `search_members` | `project_membership` | `project_uid` | FiltersAll | `project_uid:<uid>` |
119+
| `search_members` | `project_membership` | `b2b_org_uid` | FiltersAll | `b2b_org_uid:<uid>` |
120+
| `search_members` | `project_membership` | `tier_uid` | FiltersAll | `tier_uid:<uid>` |
121+
| `search_members` | `project_membership` | `tier_name` | FiltersAll | `tier_name:<name>` |
122+
| `search_members` | `project_membership` | `status` | FiltersAll | `status:Active` (hardcoded default) |
123+
| `get_membership_key_contacts` | `key_contact` | `membership_uid` | FiltersAll | `membership_uid:<uid>` |
124+
| `search_b2b_orgs` | `b2b_org` | *(none — Name only)* |||
103125

104126
## Step 3 — Fetch indexer contracts
105127

@@ -126,82 +148,87 @@ for each resource type. Record which tag keys and parent_ref prefixes the
126148
contract defines. If a URL 404s or has no contract doc, note it and continue —
127149
treat those filters as "no contract definition" in the report.
128150

129-
## Step 4 — Sample the live index
151+
## Step 4 — Count hits in the live index
152+
153+
For each filter parameter, run a **count-only query** (`"size": 0`) via the
154+
NATS box. This is the primary evidence step. Use the `$NATS_POD` and
155+
`$OPENSEARCH_BASEURL` variables set in Step 1.
130156

131-
For each resource type, run the following queries via the NATS box. Use the
132-
`$NATS_POD` and `$OPENSEARCH_BASEURL` variables set in Step 1.
157+
> **Note:** These queries omit `track_total_hits: true`, so `hits.total.value`
158+
> may be capped on very large indices. This is intentional — an approximate
159+
> count is sufficient to confirm a field is populated. Check `hits.total.relation`
160+
> in the response: `"eq"` means the count is exact; `"gte"` means it is a lower
161+
> bound and the true total is higher.
133162
134-
**Sample tags and parent_refs from 3 recently indexed documents (last 45 days):**
163+
**Count documents where a specific tag key has non-empty values (last 45 days):**
135164

136165
```bash
137166
kubectl exec -n lfx "$NATS_POD" -- \
138167
curl -s --max-time 15 -X GET "$OPENSEARCH_BASEURL/_search" \
139168
-H 'Content-Type: application/json' \
140169
-d '{
141-
"size": 3,
142-
"_source": ["tags", "parent_refs", "object_type"],
170+
"size": 0,
143171
"query": {
144172
"bool": {
145173
"must": [
146174
{ "term": { "object_type": "<RESOURCE_TYPE>" } },
175+
{ "prefix": { "tags": "<TAG_KEY>:" } },
147176
{ "range": { "updated_at": { "gte": "now-45d" } } }
177+
],
178+
"must_not": [
179+
{ "term": { "tags": "<TAG_KEY>:" } }
148180
]
149181
}
150182
}
151183
}'
152184
```
153185

154-
**Check whether a specific tag key has any non-empty values (last 45 days):**
186+
**Count documents where a specific parent_ref prefix exists (last 45 days):**
155187

156188
```bash
157189
kubectl exec -n lfx "$NATS_POD" -- \
158190
curl -s --max-time 15 -X GET "$OPENSEARCH_BASEURL/_search" \
159191
-H 'Content-Type: application/json' \
160192
-d '{
161-
"size": 1,
162-
"_source": ["tags"],
193+
"size": 0,
163194
"query": {
164195
"bool": {
165196
"must": [
166197
{ "term": { "object_type": "<RESOURCE_TYPE>" } },
167-
{ "prefix": { "tags": "<TAG_KEY>:" } },
198+
{ "prefix": { "parent_refs": "<PREFIX>:" } },
168199
{ "range": { "updated_at": { "gte": "now-45d" } } }
169-
],
170-
"must_not": [
171-
{ "term": { "tags": "<TAG_KEY>:" } }
172200
]
173201
}
174202
}
175203
}'
176204
```
177205

178-
**Check whether a specific parent_ref prefix exists (last 45 days):**
206+
Record the `hits.total.value` from each response. A non-zero count confirms the
207+
key/prefix is present in recently indexed data. If the count is zero but the
208+
resource type has older data, note it as "not seen in last 45 days" rather
209+
than immediately marking it broken.
210+
211+
**Only when a count is zero or surprising**, pull a small sample to understand
212+
what fields are actually present on that resource type:
179213

180214
```bash
181215
kubectl exec -n lfx "$NATS_POD" -- \
182216
curl -s --max-time 15 -X GET "$OPENSEARCH_BASEURL/_search" \
183217
-H 'Content-Type: application/json' \
184218
-d '{
185-
"size": 1,
186-
"_source": ["parent_refs"],
219+
"size": 3,
220+
"_source": ["tags", "parent_refs", "object_type"],
187221
"query": {
188222
"bool": {
189223
"must": [
190224
{ "term": { "object_type": "<RESOURCE_TYPE>" } },
191-
{ "prefix": { "parent_refs": "<PREFIX>:" } },
192225
{ "range": { "updated_at": { "gte": "now-45d" } } }
193226
]
194227
}
195228
}
196229
}'
197230
```
198231

199-
Record the `total.value` from each response. A non-zero value confirms the
200-
key/prefix is present in recently indexed data. If the count is zero but the
201-
resource type has older data, note it as "not seen in last 45 days" rather
202-
than immediately marking it broken — check the sample query to understand
203-
overall coverage before rendering a verdict.
204-
205232
## Step 5 — Build the truth table
206233

207234
Cross-reference: tool parameter → mechanism → contract definition → index
@@ -256,6 +283,6 @@ After applying fixes, run `make build` to confirm compilation succeeds.
256283

257284
## Step 8 — Verify fixes
258285

259-
Re-run the targeted `curl` queries from Step 4 against the corrected
286+
Re-run the count-only queries from Step 4 against the corrected
260287
mechanism to confirm non-zero results. Report before/after hit counts for
261288
each fixed filter.

0 commit comments

Comments
 (0)