@honua/mcp-server 0.0.20-alpha.0 → 0.1.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/README.md +92 -15
  2. package/certification/geospatial-mcp-schemas/PROVENANCE.md +15 -7
  3. package/certification/geospatial-mcp-schemas/conformance/manifest.schema.json +6 -0
  4. package/certification/geospatial-mcp-schemas/index.json +88 -9
  5. package/certification/geospatial-mcp-schemas/tools/apply_style_preset.schema.json +13 -9
  6. package/certification/geospatial-mcp-schemas/tools/buffer_features.schema.json +60 -0
  7. package/certification/geospatial-mcp-schemas/tools/edit_features.schema.json +74 -0
  8. package/certification/geospatial-mcp-schemas/tools/export_dataset.schema.json +60 -0
  9. package/certification/geospatial-mcp-schemas/tools/geocode_addresses.schema.json +34 -0
  10. package/certification/geospatial-mcp-schemas/tools/get_style.schema.json +13 -3
  11. package/certification/geospatial-mcp-schemas/tools/ingest_dataset.schema.json +45 -0
  12. package/certification/geospatial-mcp-schemas/tools/join_features.schema.json +73 -0
  13. package/certification/geospatial-mcp-schemas/tools/overlay_features.schema.json +53 -0
  14. package/certification/geospatial-mcp-schemas/tools/query_features.schema.json +20 -0
  15. package/certification/geospatial-mcp-schemas/tools/render_map.schema.json +5 -0
  16. package/certification/geospatial-mcp-schemas/tools/reproject_features.schema.json +49 -0
  17. package/certification/geospatial-mcp-schemas/tools/summarize_statistics.schema.json +71 -0
  18. package/dist/src/capability.d.ts +48 -0
  19. package/dist/src/capability.d.ts.map +1 -0
  20. package/dist/src/capability.js +44 -0
  21. package/dist/src/certification/census-data.d.ts +33 -0
  22. package/dist/src/certification/census-data.d.ts.map +1 -0
  23. package/dist/src/certification/census-data.js +506 -0
  24. package/dist/src/certification/census-fixture-client.d.ts +7 -0
  25. package/dist/src/certification/census-fixture-client.d.ts.map +1 -0
  26. package/dist/src/certification/census-fixture-client.js +226 -0
  27. package/dist/src/certification/certifier.d.ts +7 -1
  28. package/dist/src/certification/certifier.d.ts.map +1 -1
  29. package/dist/src/certification/certifier.js +52 -7
  30. package/dist/src/certification/cli.js +19 -4
  31. package/dist/src/certification/contracts.d.ts +41 -0
  32. package/dist/src/certification/contracts.d.ts.map +1 -1
  33. package/dist/src/certification/contracts.js +71 -0
  34. package/dist/src/certification/fixtures.d.ts +13 -0
  35. package/dist/src/certification/fixtures.d.ts.map +1 -1
  36. package/dist/src/certification/fixtures.js +28 -0
  37. package/dist/src/certification/json-schema.d.ts +23 -1
  38. package/dist/src/certification/json-schema.d.ts.map +1 -1
  39. package/dist/src/certification/json-schema.js +10 -2
  40. package/dist/src/certification/lifecycle.d.ts +14 -0
  41. package/dist/src/certification/lifecycle.d.ts.map +1 -0
  42. package/dist/src/certification/lifecycle.js +326 -0
  43. package/dist/src/certification/operator-catalog.d.ts +8 -0
  44. package/dist/src/certification/operator-catalog.d.ts.map +1 -1
  45. package/dist/src/certification/operator-catalog.js +189 -5
  46. package/dist/src/certification/report.d.ts.map +1 -1
  47. package/dist/src/certification/report.js +13 -0
  48. package/dist/src/certification/run.d.ts.map +1 -1
  49. package/dist/src/certification/run.js +1 -0
  50. package/dist/src/certification/schema-index.d.ts +25 -0
  51. package/dist/src/certification/schema-index.d.ts.map +1 -1
  52. package/dist/src/certification/schema-index.js +27 -0
  53. package/dist/src/certification/target.d.ts +9 -1
  54. package/dist/src/certification/target.d.ts.map +1 -1
  55. package/dist/src/certification/target.js +54 -2
  56. package/dist/src/eval/cli.js +4 -2
  57. package/dist/src/eval/corpus.d.ts +7 -0
  58. package/dist/src/eval/corpus.d.ts.map +1 -1
  59. package/dist/src/eval/corpus.js +16 -0
  60. package/dist/src/eval/drivers/deterministic.d.ts.map +1 -1
  61. package/dist/src/eval/drivers/deterministic.js +13 -0
  62. package/dist/src/eval/grade.d.ts.map +1 -1
  63. package/dist/src/eval/grade.js +20 -1
  64. package/dist/src/eval/report.d.ts +5 -0
  65. package/dist/src/eval/report.d.ts.map +1 -1
  66. package/dist/src/eval/report.js +3 -0
  67. package/dist/src/eval/runner.d.ts +6 -0
  68. package/dist/src/eval/runner.d.ts.map +1 -1
  69. package/dist/src/eval/runner.js +21 -7
  70. package/dist/src/eval/standalone-corpus.d.ts +3 -0
  71. package/dist/src/eval/standalone-corpus.d.ts.map +1 -0
  72. package/dist/src/eval/standalone-corpus.js +699 -0
  73. package/dist/src/eval/types.d.ts +25 -0
  74. package/dist/src/eval/types.d.ts.map +1 -1
  75. package/dist/src/index.d.ts +29 -5
  76. package/dist/src/index.d.ts.map +1 -1
  77. package/dist/src/index.js +41 -21
  78. package/dist/src/provenance.d.ts +44 -0
  79. package/dist/src/provenance.d.ts.map +1 -0
  80. package/dist/src/provenance.js +37 -0
  81. package/dist/src/resources/styles.d.ts.map +1 -1
  82. package/dist/src/resources/styles.js +36 -3
  83. package/dist/src/styles.d.ts +2 -0
  84. package/dist/src/styles.d.ts.map +1 -1
  85. package/dist/src/styles.js +29 -3
  86. package/dist/src/tools/apply-style-preset.d.ts.map +1 -1
  87. package/dist/src/tools/apply-style-preset.js +14 -2
  88. package/dist/src/tools/get-style.d.ts.map +1 -1
  89. package/dist/src/tools/get-style.js +24 -13
  90. package/dist/src/tools/list-services.d.ts.map +1 -1
  91. package/dist/src/tools/list-services.js +16 -1
  92. package/package.json +11 -2
@@ -0,0 +1,699 @@
1
+ import { CENSUS_SERVICE_ID } from "../certification/census-data.js";
2
+ /**
3
+ * PLATFORM-FREE (standalone) eval corpus — issue #369.
4
+ *
5
+ * 50+ GIS workflows run against a PLAIN public Esri FeatureServer (the recorded
6
+ * US Census 2020 apportionment layer; see `certification/census-data.ts`) with
7
+ * ZERO Honua-server surfaces. Unlike the analyst/operator corpora — which grade
8
+ * mostly on tool trajectory (structural) — every scenario here carries a SEMANTIC
9
+ * assertion: the answer must contain the correct feature count, the correct
10
+ * geographic fact, the right tool choice for an ambiguous ask, or a clarifying
11
+ * question when the request is ambiguous/unsupported. Because the fixture replays
12
+ * real recorded data, a wrong number or a hallucinated place name FAILS.
13
+ *
14
+ * Grounded anchors (verified live against services.arcgis.com):
15
+ * rows = 52 (50 states + DC + Puerto Rico)
16
+ * sum(Total_Pop_2020) = 335085841 sum(Seats_2020) = 435
17
+ * max pop = California (39576757, 52 seats) min pop = Wyoming (577719, 1 seat)
18
+ * states with >=20 seats = 4 (CA, TX, FL, NY) pop > 10,000,000 = 10 pop < 1,000,000 = 7
19
+ *
20
+ * The deterministic offline driver replays each `script` against the in-process
21
+ * census fixture surface and grades the synthesized answer — this is the CI
22
+ * control and it must pass every scenario. Live LLM drivers plan their own calls
23
+ * and are graded on the identical criteria.
24
+ *
25
+ * Selected via `HONUA_EVAL_CORPUS=standalone` / `--corpus standalone`; the runner
26
+ * swaps in the census fixture client for this corpus (see `runner.ts`).
27
+ */
28
+ const SVC = CENSUS_SERVICE_ID;
29
+ const L = 0;
30
+ export const STANDALONE_CORPUS = [
31
+ // ── Discovery (list_services) ────────────────────────────────────────────
32
+ {
33
+ id: "sa-discover-services",
34
+ title: "Discover services on a plain FeatureServer folder",
35
+ category: "discovery",
36
+ prompt: "What feature services can I query at this endpoint? Just list them.",
37
+ criteria: { requiredTools: ["honua_list_services"], answerMustInclude: [CENSUS_SERVICE_ID] },
38
+ script: [{ tool: "honua_list_services", args: {} }],
39
+ },
40
+ {
41
+ id: "sa-discover-details",
42
+ title: "Discover services with details",
43
+ category: "discovery",
44
+ prompt: "List the services here and include their type.",
45
+ criteria: { requiredTools: ["honua_list_services"], answerMustInclude: ["FeatureServer"] },
46
+ script: [{ tool: "honua_list_services", args: { includeDetails: true } }],
47
+ },
48
+ // ── Schema / grounding (describe_layer) ──────────────────────────────────
49
+ {
50
+ id: "sa-describe-layer",
51
+ title: "Inspect the layer schema before querying",
52
+ category: "grounding",
53
+ prompt: "Before I query, show me the fields and geometry type of layer 0 of the census service.",
54
+ criteria: {
55
+ requiredTools: ["honua_describe_layer"],
56
+ answerMustInclude: ["Total_Pop_2020", "esriGeometryPolygon"],
57
+ },
58
+ script: [{ tool: "honua_describe_layer", args: { serviceId: SVC, layerId: L } }],
59
+ },
60
+ {
61
+ id: "sa-describe-geometry",
62
+ title: "Confirm the geometry type",
63
+ category: "grounding",
64
+ prompt: "Are these features points, lines, or polygons?",
65
+ criteria: {
66
+ requiredTools: ["honua_describe_layer"],
67
+ answerMustInclude: ["esriGeometryPolygon"],
68
+ answerMustNotInclude: ["esriGeometryPoint", "esriGeometryPolyline"],
69
+ },
70
+ script: [{ tool: "honua_describe_layer", args: { serviceId: SVC, layerId: L } }],
71
+ },
72
+ {
73
+ id: "sa-describe-fields-population",
74
+ title: "Find the population field name",
75
+ category: "grounding",
76
+ prompt: "Which field holds the 2020 population? Show me the schema.",
77
+ criteria: { requiredTools: ["honua_describe_layer"], answerMustInclude: ["Total_Pop_2020"] },
78
+ script: [{ tool: "honua_describe_layer", args: { serviceId: SVC, layerId: L } }],
79
+ },
80
+ {
81
+ id: "sa-describe-fields-seats",
82
+ title: "Find the House-seats field name",
83
+ category: "grounding",
84
+ prompt: "What field records the number of apportioned House seats?",
85
+ criteria: { requiredTools: ["honua_describe_layer"], answerMustInclude: ["Seats_2020"] },
86
+ script: [{ tool: "honua_describe_layer", args: { serviceId: SVC, layerId: L } }],
87
+ },
88
+ // ── Counting (count_features) — semantic counts ──────────────────────────
89
+ {
90
+ id: "sa-count-all",
91
+ title: "Count all rows in the layer",
92
+ category: "count",
93
+ prompt: "How many features are in this layer? I only need the number, not the data.",
94
+ criteria: {
95
+ requiredTools: ["honua_count_features"],
96
+ forbiddenTools: ["honua_query_features"],
97
+ answerMustMatch: ['"count":\\s*52'],
98
+ },
99
+ script: [{ tool: "honua_count_features", args: { serviceId: SVC, layerId: L } }],
100
+ },
101
+ {
102
+ id: "sa-count-big-states",
103
+ title: "Count states with >= 20 House seats",
104
+ category: "count",
105
+ prompt: "How many states have 20 or more apportioned House seats?",
106
+ criteria: { requiredTools: ["honua_count_features"], answerMustMatch: ['"count":\\s*4'] },
107
+ script: [{ tool: "honua_count_features", args: { serviceId: SVC, layerId: L, where: "Seats_2020>=20" } }],
108
+ },
109
+ {
110
+ id: "sa-count-populous",
111
+ title: "Count rows with population over 10 million",
112
+ category: "count",
113
+ prompt: "How many of these areas have a 2020 population above 10 million?",
114
+ criteria: { requiredTools: ["honua_count_features"], answerMustMatch: ['"count":\\s*10'] },
115
+ script: [{ tool: "honua_count_features", args: { serviceId: SVC, layerId: L, where: "Total_Pop_2020>10000000" } }],
116
+ },
117
+ {
118
+ id: "sa-count-small",
119
+ title: "Count rows with population under 1 million",
120
+ category: "count",
121
+ prompt: "How many areas have fewer than one million residents?",
122
+ criteria: { requiredTools: ["honua_count_features"], answerMustMatch: ['"count":\\s*7'] },
123
+ script: [{ tool: "honua_count_features", args: { serviceId: SVC, layerId: L, where: "Total_Pop_2020<1000000" } }],
124
+ },
125
+ {
126
+ id: "sa-count-single-seat",
127
+ title: "Count single-seat delegations",
128
+ category: "count",
129
+ prompt: "How many states got exactly one House seat?",
130
+ criteria: { requiredTools: ["honua_count_features"], answerMustMatch: ['"count":\\s*6'] },
131
+ script: [{ tool: "honua_count_features", args: { serviceId: SVC, layerId: L, where: "Seats_2020=1" } }],
132
+ },
133
+ {
134
+ id: "sa-count-ge10-seats",
135
+ title: "Count states with >= 10 seats",
136
+ category: "count",
137
+ prompt: "How many states have at least ten House seats?",
138
+ criteria: { requiredTools: ["honua_count_features"], answerMustMatch: ['"count":\\s*13'] },
139
+ script: [{ tool: "honua_count_features", args: { serviceId: SVC, layerId: L, where: "Seats_2020>=10" } }],
140
+ },
141
+ {
142
+ id: "sa-count-california",
143
+ title: "Confirm California is a single row",
144
+ category: "count",
145
+ prompt: "Is there exactly one row for California (STUSPS = CA)?",
146
+ criteria: { requiredTools: ["honua_count_features"], answerMustMatch: ['"count":\\s*1'] },
147
+ script: [{ tool: "honua_count_features", args: { serviceId: SVC, layerId: L, where: "STUSPS='CA'" } }],
148
+ },
149
+ // ── Statistics (statistics) — semantic aggregates ────────────────────────
150
+ {
151
+ id: "sa-sum-population",
152
+ title: "Total population across all rows",
153
+ category: "analysis",
154
+ prompt: "What is the total 2020 population summed across every row?",
155
+ criteria: {
156
+ requiredTools: ["honua_statistics"],
157
+ answerMustInclude: ["sum_Total_Pop_2020"],
158
+ answerMustMatch: ["335085841"],
159
+ },
160
+ script: [
161
+ {
162
+ tool: "honua_statistics",
163
+ args: { serviceId: SVC, layerId: L, statisticType: "sum", onField: "Total_Pop_2020" },
164
+ },
165
+ ],
166
+ },
167
+ {
168
+ id: "sa-sum-seats",
169
+ title: "Total apportioned House seats",
170
+ category: "analysis",
171
+ prompt: "Add up the apportioned House seats. It should match the size of the House of Representatives.",
172
+ criteria: {
173
+ requiredTools: ["honua_statistics"],
174
+ answerMustInclude: ["sum_Seats_2020"],
175
+ answerMustMatch: ["\\b435\\b"],
176
+ },
177
+ script: [
178
+ { tool: "honua_statistics", args: { serviceId: SVC, layerId: L, statisticType: "sum", onField: "Seats_2020" } },
179
+ ],
180
+ },
181
+ {
182
+ id: "sa-max-population",
183
+ title: "Largest single population value",
184
+ category: "analysis",
185
+ prompt: "What is the largest 2020 population value in the layer?",
186
+ criteria: {
187
+ requiredTools: ["honua_statistics"],
188
+ answerMustInclude: ["max_Total_Pop_2020"],
189
+ answerMustMatch: ["39576757"],
190
+ },
191
+ script: [
192
+ {
193
+ tool: "honua_statistics",
194
+ args: { serviceId: SVC, layerId: L, statisticType: "max", onField: "Total_Pop_2020" },
195
+ },
196
+ ],
197
+ },
198
+ {
199
+ id: "sa-min-population",
200
+ title: "Smallest single population value",
201
+ category: "analysis",
202
+ prompt: "What is the smallest 2020 population value?",
203
+ criteria: {
204
+ requiredTools: ["honua_statistics"],
205
+ answerMustInclude: ["min_Total_Pop_2020"],
206
+ answerMustMatch: ["577719"],
207
+ },
208
+ script: [
209
+ {
210
+ tool: "honua_statistics",
211
+ args: { serviceId: SVC, layerId: L, statisticType: "min", onField: "Total_Pop_2020" },
212
+ },
213
+ ],
214
+ },
215
+ {
216
+ id: "sa-avg-population",
217
+ title: "Average population per row",
218
+ category: "analysis",
219
+ prompt: "What is the average 2020 population across the rows?",
220
+ criteria: {
221
+ requiredTools: ["honua_statistics"],
222
+ answerMustInclude: ["avg_Total_Pop_2020"],
223
+ answerMustMatch: ["6443958"],
224
+ },
225
+ script: [
226
+ {
227
+ tool: "honua_statistics",
228
+ args: { serviceId: SVC, layerId: L, statisticType: "avg", onField: "Total_Pop_2020" },
229
+ },
230
+ ],
231
+ },
232
+ {
233
+ id: "sa-avg-seats",
234
+ title: "Average seats per row",
235
+ category: "analysis",
236
+ prompt: "On average, how many House seats does each row have?",
237
+ criteria: { requiredTools: ["honua_statistics"], answerMustInclude: ["avg_Seats_2020"] },
238
+ script: [
239
+ { tool: "honua_statistics", args: { serviceId: SVC, layerId: L, statisticType: "avg", onField: "Seats_2020" } },
240
+ ],
241
+ },
242
+ // ── Query + geographic reasoning (query_features) ────────────────────────
243
+ {
244
+ id: "sa-largest-state",
245
+ title: "Which state has the most people",
246
+ category: "reasoning",
247
+ prompt: "Which state had the highest 2020 population? Give me the row.",
248
+ criteria: {
249
+ requiredTools: ["honua_query_features"],
250
+ answerMustInclude: ["California"],
251
+ answerMustMatch: ["39576757"],
252
+ answerMustNotInclude: ["Wyoming", "Texas"],
253
+ },
254
+ script: [
255
+ {
256
+ tool: "honua_query_features",
257
+ args: { serviceId: SVC, layerId: L, orderBy: "Total_Pop_2020 DESC", limit: 1 },
258
+ },
259
+ ],
260
+ },
261
+ {
262
+ id: "sa-smallest-state",
263
+ title: "Which state has the fewest people",
264
+ category: "reasoning",
265
+ prompt: "Which state had the smallest 2020 population?",
266
+ criteria: {
267
+ requiredTools: ["honua_query_features"],
268
+ answerMustInclude: ["Wyoming"],
269
+ answerMustMatch: ["577719"],
270
+ answerMustNotInclude: ["California"],
271
+ },
272
+ script: [
273
+ {
274
+ tool: "honua_query_features",
275
+ args: { serviceId: SVC, layerId: L, orderBy: "Total_Pop_2020 ASC", limit: 1 },
276
+ },
277
+ ],
278
+ },
279
+ {
280
+ id: "sa-top3-seats",
281
+ title: "Top three states by House seats",
282
+ category: "reasoning",
283
+ prompt: "List the three states with the most House seats, largest first.",
284
+ criteria: {
285
+ requiredTools: ["honua_query_features"],
286
+ answerMustInclude: ["California", "Texas", "Florida"],
287
+ answerMustNotInclude: ["Wyoming"],
288
+ },
289
+ script: [
290
+ {
291
+ tool: "honua_query_features",
292
+ args: { serviceId: SVC, layerId: L, orderBy: "Seats_2020 DESC,NAME ASC", limit: 3 },
293
+ },
294
+ ],
295
+ },
296
+ {
297
+ id: "sa-lookup-california",
298
+ title: "Look up California by abbreviation",
299
+ category: "query",
300
+ prompt: "Show me the row for California (STUSPS = CA), including population and seats.",
301
+ criteria: {
302
+ requiredTools: ["honua_query_features"],
303
+ answerMustInclude: ["California"],
304
+ answerMustMatch: ["39576757", '"Seats_2020":\\s*52'],
305
+ },
306
+ script: [{ tool: "honua_query_features", args: { serviceId: SVC, layerId: L, where: "STUSPS='CA'" } }],
307
+ },
308
+ {
309
+ id: "sa-lookup-texas",
310
+ title: "Look up Texas",
311
+ category: "query",
312
+ prompt: "What are Texas's 2020 population and seat count?",
313
+ criteria: {
314
+ requiredTools: ["honua_query_features"],
315
+ answerMustInclude: ["Texas"],
316
+ answerMustMatch: ["29183290", '"Seats_2020":\\s*38'],
317
+ },
318
+ script: [{ tool: "honua_query_features", args: { serviceId: SVC, layerId: L, where: "STUSPS='TX'" } }],
319
+ },
320
+ {
321
+ id: "sa-lookup-newyork",
322
+ title: "Look up New York",
323
+ category: "query",
324
+ prompt: "Give me New York's population and number of House seats.",
325
+ criteria: {
326
+ requiredTools: ["honua_query_features"],
327
+ answerMustInclude: ["New York"],
328
+ answerMustMatch: ["20215751", '"Seats_2020":\\s*26'],
329
+ },
330
+ script: [{ tool: "honua_query_features", args: { serviceId: SVC, layerId: L, where: "STUSPS='NY'" } }],
331
+ },
332
+ {
333
+ id: "sa-lookup-florida",
334
+ title: "Look up Florida",
335
+ category: "query",
336
+ prompt: "How many House seats did Florida get, and what was its population?",
337
+ criteria: {
338
+ requiredTools: ["honua_query_features"],
339
+ answerMustInclude: ["Florida"],
340
+ answerMustMatch: ["21570527", '"Seats_2020":\\s*28'],
341
+ },
342
+ script: [{ tool: "honua_query_features", args: { serviceId: SVC, layerId: L, where: "STUSPS='FL'" } }],
343
+ },
344
+ {
345
+ id: "sa-lookup-hawaii",
346
+ title: "Look up Hawaii",
347
+ category: "query",
348
+ prompt: "What is Hawaii's population and seat count?",
349
+ criteria: {
350
+ requiredTools: ["honua_query_features"],
351
+ answerMustInclude: ["Hawaii"],
352
+ answerMustMatch: ["1460137", '"Seats_2020":\\s*2'],
353
+ },
354
+ script: [{ tool: "honua_query_features", args: { serviceId: SVC, layerId: L, where: "STUSPS='HI'" } }],
355
+ },
356
+ {
357
+ id: "sa-lookup-wyoming",
358
+ title: "Look up Wyoming",
359
+ category: "query",
360
+ prompt: "What are Wyoming's population and seats?",
361
+ criteria: {
362
+ requiredTools: ["honua_query_features"],
363
+ answerMustInclude: ["Wyoming"],
364
+ answerMustMatch: ["577719", '"Seats_2020":\\s*1'],
365
+ },
366
+ script: [{ tool: "honua_query_features", args: { serviceId: SVC, layerId: L, where: "STUSPS='WY'" } }],
367
+ },
368
+ {
369
+ id: "sa-lookup-vermont",
370
+ title: "Look up Vermont",
371
+ category: "query",
372
+ prompt: "Show me Vermont's row.",
373
+ criteria: {
374
+ requiredTools: ["honua_query_features"],
375
+ answerMustInclude: ["Vermont"],
376
+ answerMustMatch: ["643503", '"Seats_2020":\\s*1'],
377
+ },
378
+ script: [{ tool: "honua_query_features", args: { serviceId: SVC, layerId: L, where: "STUSPS='VT'" } }],
379
+ },
380
+ {
381
+ id: "sa-sample-features",
382
+ title: "Fetch a small sample",
383
+ category: "query",
384
+ prompt: "Show me a couple of rows so I can see what the data looks like.",
385
+ criteria: { requiredTools: ["honua_query_features"], answerMustInclude: ["features", "NAME"] },
386
+ script: [{ tool: "honua_query_features", args: { serviceId: SVC, layerId: L, limit: 2 } }],
387
+ },
388
+ {
389
+ id: "sa-big-states-list",
390
+ title: "List the states with >= 20 seats",
391
+ category: "query",
392
+ prompt: "List every state with at least 20 House seats, most seats first.",
393
+ criteria: {
394
+ requiredTools: ["honua_query_features"],
395
+ answerMustInclude: ["California", "Texas", "Florida", "New York"],
396
+ },
397
+ script: [
398
+ {
399
+ tool: "honua_query_features",
400
+ args: { serviceId: SVC, layerId: L, where: "Seats_2020>=20", orderBy: "Seats_2020 DESC,NAME ASC" },
401
+ },
402
+ ],
403
+ },
404
+ // ── Extent (get_extent) ──────────────────────────────────────────────────
405
+ {
406
+ id: "sa-extent",
407
+ title: "Bounding box of the layer",
408
+ category: "query",
409
+ prompt: "What is the spatial extent / bounding box of the layer?",
410
+ criteria: { requiredTools: ["honua_get_extent"], answerMustInclude: ["extent"] },
411
+ script: [{ tool: "honua_get_extent", args: { serviceId: SVC, layerId: L } }],
412
+ },
413
+ {
414
+ id: "sa-extent-sr",
415
+ title: "Extent carries a spatial reference",
416
+ category: "query",
417
+ prompt: "Give me the extent and tell me the spatial reference wkid it is expressed in.",
418
+ criteria: { requiredTools: ["honua_get_extent"], answerMustMatch: ["102100|3857"] },
419
+ script: [{ tool: "honua_get_extent", args: { serviceId: SVC, layerId: L } }],
420
+ },
421
+ // ── Capability degradation: styling (get_style / apply_style_preset) ─────
422
+ {
423
+ id: "sa-style-list-unavailable",
424
+ title: "Styling catalog is honestly reported as unavailable",
425
+ category: "degradation",
426
+ prompt: "List the map styles available on this server so I can pick a basemap.",
427
+ criteria: {
428
+ requiredTools: ["honua_get_style"],
429
+ answerMustInclude: ["OGC API - Styles"],
430
+ answerMustMatch: ['"available":\\s*false'],
431
+ },
432
+ script: [{ tool: "honua_get_style", args: {} }],
433
+ },
434
+ {
435
+ id: "sa-style-get-unavailable",
436
+ title: "Resolving a specific style degrades gracefully",
437
+ category: "degradation",
438
+ prompt: "Give me the 'topographic' style definition from this endpoint.",
439
+ criteria: {
440
+ requiredTools: ["honua_get_style"],
441
+ answerMustMatch: ['"available":\\s*false'],
442
+ answerMustNotInclude: ["stylesheet applied", "mapbox-style"],
443
+ },
444
+ script: [{ tool: "honua_get_style", args: { styleId: "topographic" } }],
445
+ },
446
+ {
447
+ id: "sa-style-preset-unavailable",
448
+ title: "Applying a style preset degrades gracefully",
449
+ category: "degradation",
450
+ prompt: "Apply the 'topographic' style preset to a map for me.",
451
+ criteria: {
452
+ requiredTools: ["honua_apply_style_preset"],
453
+ answerMustInclude: ["OGC API - Styles"],
454
+ answerMustMatch: ['"available":\\s*false'],
455
+ },
456
+ script: [{ tool: "honua_apply_style_preset", args: { styleId: "topographic" } }],
457
+ },
458
+ {
459
+ id: "sa-style-guidance",
460
+ title: "Degradation includes a client-side workaround",
461
+ category: "degradation",
462
+ prompt: "I want to style this layer. What are my options on a plain FeatureServer?",
463
+ criteria: { requiredTools: ["honua_get_style"], answerMustInclude: ["client-side"] },
464
+ script: [{ tool: "honua_get_style", args: {} }],
465
+ },
466
+ // ── Capability-gap explanation (explain_capability_gap) ──────────────────
467
+ {
468
+ id: "sa-capability-gap-wmts",
469
+ title: "Explain an attribute-query gap on WMTS",
470
+ category: "grounding",
471
+ prompt: "Can I run an attribute query against a WMTS source? If not, what should I do instead?",
472
+ criteria: { requiredTools: ["honua_explain_capability_gap"], answerMustInclude: ["explanation"] },
473
+ script: [{ tool: "honua_explain_capability_gap", args: { capability: "query", protocol: "wmts" } }],
474
+ },
475
+ {
476
+ id: "sa-capability-gap-edit",
477
+ title: "Explain an edit-capability question",
478
+ category: "grounding",
479
+ prompt: "Can I apply edits over a WMTS tile source?",
480
+ criteria: { requiredTools: ["honua_explain_capability_gap"], answerMustInclude: ["explanation"] },
481
+ script: [{ tool: "honua_explain_capability_gap", args: { capability: "applyEdits", protocol: "wmts" } }],
482
+ },
483
+ // ── Multi-step workflows ─────────────────────────────────────────────────
484
+ {
485
+ id: "sa-count-then-query",
486
+ title: "Check cardinality, then sample",
487
+ category: "multi-step",
488
+ prompt: "How many rows are there? Then show me a couple of them.",
489
+ criteria: {
490
+ requiredTools: ["honua_count_features", "honua_query_features"],
491
+ expectedToolSequence: ["honua_count_features", "honua_query_features"],
492
+ answerMustMatch: ['"count":\\s*52'],
493
+ },
494
+ script: [
495
+ { tool: "honua_count_features", args: { serviceId: SVC, layerId: L } },
496
+ { tool: "honua_query_features", args: { serviceId: SVC, layerId: L, limit: 2 } },
497
+ ],
498
+ },
499
+ {
500
+ id: "sa-discover-describe-query",
501
+ title: "Discover → describe → query end-to-end",
502
+ category: "multi-step",
503
+ prompt: "I don't know this endpoint. Find a service, inspect its first layer, then show me the most populous row.",
504
+ criteria: {
505
+ requiredTools: ["honua_list_services", "honua_describe_layer", "honua_query_features"],
506
+ expectedToolSequence: ["honua_list_services", "honua_describe_layer", "honua_query_features"],
507
+ answerMustInclude: ["California"],
508
+ },
509
+ script: [
510
+ { tool: "honua_list_services", args: {} },
511
+ { tool: "honua_describe_layer", args: { serviceId: SVC, layerId: L } },
512
+ { tool: "honua_query_features", args: { serviceId: SVC, layerId: L, orderBy: "Total_Pop_2020 DESC", limit: 1 } },
513
+ ],
514
+ },
515
+ {
516
+ id: "sa-schema-then-stat",
517
+ title: "Ground the field name, then aggregate",
518
+ category: "multi-step",
519
+ prompt: "Find the population field, then total it across all rows.",
520
+ criteria: {
521
+ requiredTools: ["honua_describe_layer", "honua_statistics"],
522
+ expectedToolSequence: ["honua_describe_layer", "honua_statistics"],
523
+ answerMustMatch: ["335085841"],
524
+ },
525
+ script: [
526
+ { tool: "honua_describe_layer", args: { serviceId: SVC, layerId: L } },
527
+ {
528
+ tool: "honua_statistics",
529
+ args: { serviceId: SVC, layerId: L, statisticType: "sum", onField: "Total_Pop_2020" },
530
+ },
531
+ ],
532
+ },
533
+ {
534
+ id: "sa-count-then-list-big",
535
+ title: "Count then enumerate the big states",
536
+ category: "multi-step",
537
+ prompt: "How many states have 20+ seats, and which are they?",
538
+ criteria: {
539
+ requiredTools: ["honua_count_features", "honua_query_features"],
540
+ answerMustMatch: ['"count":\\s*4'],
541
+ answerMustInclude: ["California", "New York"],
542
+ },
543
+ script: [
544
+ { tool: "honua_count_features", args: { serviceId: SVC, layerId: L, where: "Seats_2020>=20" } },
545
+ {
546
+ tool: "honua_query_features",
547
+ args: { serviceId: SVC, layerId: L, where: "Seats_2020>=20", orderBy: "Seats_2020 DESC,NAME ASC" },
548
+ },
549
+ ],
550
+ },
551
+ // ── Tool-selection for ambiguous asks ────────────────────────────────────
552
+ {
553
+ id: "sa-select-count-not-query",
554
+ title: "Prefer count over query for a cardinality ask",
555
+ category: "tool-selection",
556
+ prompt: "I don't want the rows dumped — just tell me how many there are.",
557
+ criteria: {
558
+ requiredTools: ["honua_count_features"],
559
+ forbiddenTools: ["honua_query_features"],
560
+ answerMustMatch: ['"count":\\s*52'],
561
+ },
562
+ script: [{ tool: "honua_count_features", args: { serviceId: SVC, layerId: L } }],
563
+ },
564
+ {
565
+ id: "sa-select-stat-not-query",
566
+ title: "Prefer statistics over pulling every row",
567
+ category: "tool-selection",
568
+ prompt: "What's the total population? Don't fetch all the rows to add them up yourself.",
569
+ criteria: {
570
+ requiredTools: ["honua_statistics"],
571
+ forbiddenTools: ["honua_query_features"],
572
+ answerMustMatch: ["335085841"],
573
+ },
574
+ script: [
575
+ {
576
+ tool: "honua_statistics",
577
+ args: { serviceId: SVC, layerId: L, statisticType: "sum", onField: "Total_Pop_2020" },
578
+ },
579
+ ],
580
+ },
581
+ {
582
+ id: "sa-select-extent-not-query",
583
+ title: "Prefer extent over scanning geometries",
584
+ category: "tool-selection",
585
+ prompt: "What area does this layer cover? I just need the bounding box, not the shapes.",
586
+ criteria: {
587
+ requiredTools: ["honua_get_extent"],
588
+ forbiddenTools: ["honua_query_features"],
589
+ answerMustInclude: ["extent"],
590
+ },
591
+ script: [{ tool: "honua_get_extent", args: { serviceId: SVC, layerId: L } }],
592
+ },
593
+ {
594
+ id: "sa-select-describe-not-query",
595
+ title: "Prefer describe over query for a schema ask",
596
+ category: "tool-selection",
597
+ prompt: "What columns does this layer have? I don't need any actual records.",
598
+ criteria: {
599
+ requiredTools: ["honua_describe_layer"],
600
+ forbiddenTools: ["honua_query_features"],
601
+ answerMustInclude: ["Total_Pop_2020"],
602
+ },
603
+ script: [{ tool: "honua_describe_layer", args: { serviceId: SVC, layerId: L } }],
604
+ },
605
+ // ── Ambiguity / refusal / clarification ──────────────────────────────────
606
+ {
607
+ id: "sa-clarify-which-state",
608
+ title: "Ambiguous 'that state' → clarify",
609
+ category: "clarification",
610
+ prompt: "Show me the data for that state.",
611
+ criteria: { requiredTools: [], expectClarification: true },
612
+ script: [],
613
+ clarify: { question: "Which state would you like? There are 52 rows (50 states plus DC and Puerto Rico)." },
614
+ },
615
+ {
616
+ id: "sa-clarify-which-field",
617
+ title: "Ambiguous 'the number' → clarify",
618
+ category: "clarification",
619
+ prompt: "What's the number for California?",
620
+ criteria: { requiredTools: [], expectClarification: true },
621
+ script: [],
622
+ clarify: { question: "Which value for California — the 2020 population, or the apportioned House seats?" },
623
+ },
624
+ {
625
+ id: "sa-refuse-write",
626
+ title: "Read-only endpoint → refuse an edit",
627
+ category: "clarification",
628
+ prompt: "Update California's population to 40 million and save it.",
629
+ criteria: { requiredTools: [], expectClarification: true },
630
+ script: [],
631
+ clarify: {
632
+ question: "This is a read-only public FeatureServer, so I can't edit features here. Did you want to query or analyze the data instead?",
633
+ },
634
+ },
635
+ {
636
+ id: "sa-refuse-unsupported-format",
637
+ title: "Unsupported export → clarify",
638
+ category: "clarification",
639
+ prompt: "Render this as a 3D globe scene and email it to me.",
640
+ criteria: { requiredTools: [], expectClarification: true },
641
+ script: [],
642
+ clarify: {
643
+ question: "I can query, count, aggregate, and describe this FeatureServer, but I can't render 3D scenes or send email. What data would you like extracted?",
644
+ },
645
+ },
646
+ {
647
+ id: "sa-clarify-ambiguous-largest",
648
+ title: "'Largest' is ambiguous (area vs population) → clarify",
649
+ category: "clarification",
650
+ prompt: "Which is the largest state here?",
651
+ criteria: { requiredTools: [], expectClarification: true },
652
+ script: [],
653
+ clarify: {
654
+ question: "Largest by which measure — 2020 population, or number of House seats? (Land area isn't in this layer.)",
655
+ },
656
+ },
657
+ // ── Anti-hallucination guards ────────────────────────────────────────────
658
+ {
659
+ id: "sa-no-invent-most-populous",
660
+ title: "Most-populous answer must be grounded, not guessed",
661
+ category: "reasoning",
662
+ prompt: "Name the single most populous state in this dataset and its exact population.",
663
+ criteria: {
664
+ requiredTools: ["honua_query_features"],
665
+ answerMustInclude: ["California"],
666
+ answerMustMatch: ["39576757"],
667
+ answerMustNotInclude: ["Texas", "Florida", "New York"],
668
+ },
669
+ script: [
670
+ { tool: "honua_query_features", args: { serviceId: SVC, layerId: L, orderBy: "Total_Pop_2020 DESC", limit: 1 } },
671
+ ],
672
+ },
673
+ {
674
+ id: "sa-no-invent-seat-total",
675
+ title: "House-seat total must be the real 435",
676
+ category: "analysis",
677
+ prompt: "How many House seats are apportioned in total across the whole dataset?",
678
+ criteria: {
679
+ requiredTools: ["honua_statistics"],
680
+ answerMustMatch: ["\\b435\\b"],
681
+ answerMustNotInclude: ["438", "450", "400"],
682
+ },
683
+ script: [
684
+ { tool: "honua_statistics", args: { serviceId: SVC, layerId: L, statisticType: "sum", onField: "Seats_2020" } },
685
+ ],
686
+ },
687
+ {
688
+ id: "sa-no-invent-row-count",
689
+ title: "Row count must be the real 52",
690
+ category: "count",
691
+ prompt: "Exactly how many rows are in this apportionment layer?",
692
+ criteria: {
693
+ requiredTools: ["honua_count_features"],
694
+ answerMustMatch: ['"count":\\s*52'],
695
+ answerMustNotInclude: ["50 rows", "51 rows"],
696
+ },
697
+ script: [{ tool: "honua_count_features", args: { serviceId: SVC, layerId: L } }],
698
+ },
699
+ ];