specguard-mcp 0.1.39 → 0.1.41
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.
- package/README.md +76 -15
- package/dist/src/config.d.ts +2 -2
- package/dist/src/config.js +1 -1
- package/dist/src/support/specguard-api.js +1 -1
- package/dist/src/tools/args.d.ts +11 -0
- package/dist/src/tools/args.js +20 -0
- package/dist/src/tools/args.js.map +1 -1
- package/dist/src/tools/find-tests-near-behavior.js +27 -2
- package/dist/src/tools/find-tests-near-behavior.js.map +1 -1
- package/dist/src/tools/index.d.ts +1 -1
- package/dist/src/tools/index.js +1 -1
- package/dist/src/tools/list-repositories.d.ts +5 -3
- package/dist/src/tools/list-repositories.js +17 -9
- package/dist/src/tools/list-repositories.js.map +1 -1
- package/dist/src/tools/repository-overview.d.ts +6 -6
- package/dist/src/tools/repository-overview.js +93 -18
- package/dist/src/tools/repository-overview.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -40,7 +40,7 @@ refuses to boot and takes the tools that needed no configuration down with it.
|
|
|
40
40
|
| `SPECGUARD_TIMEOUT_MS` | HTTP tools | `30000` | how long a call to SpecGuard may take |
|
|
41
41
|
|
|
42
42
|
`SPECGUARD_ENDPOINT` and `SPECGUARD_API_KEY` are the same variables
|
|
43
|
-
[`specguard-
|
|
43
|
+
[`specguard-ruby`](https://github.com/yatfa-ai/specguard-ruby) uses to ship a run, so a repository
|
|
44
44
|
that already posts telemetry to SpecGuard already has them. `SPECGUARD_USER_API_KEY` is **not** one
|
|
45
45
|
of them — the gem has no notion of a user key — so that one is minted and set here for the first
|
|
46
46
|
time.
|
|
@@ -199,14 +199,15 @@ before any request is made.
|
|
|
199
199
|
| `repeated_description` | open ONE repeated description and list the examples that all share it |
|
|
200
200
|
| `unstable_test` | open ONE flaky test and list its outcome run by run across the window, newest run first (needs `branch`) |
|
|
201
201
|
| `commit_sha` | anchor the answer on ONE named run instead of the repository's newest one — every run-grain block moves with it, `history` does not |
|
|
202
|
+
| `layer` | narrow ONLY `latest_run.slowest_examples` to one declared layer (`unit`, `integration`, `request`, `system` or `undeclared`) — see below |
|
|
202
203
|
| `unannotated_examples` | `true` to list the individual tests carrying no `@intent` — the examples behind the annotated ratio, each labelled with what SpecGuard reads of it — and, in the same answer, which areas carry the most of them |
|
|
203
204
|
|
|
204
205
|
`branch` narrows `history` only — `latest_run` always names the repository's newest run, which on a
|
|
205
206
|
busy repo may be on another branch. That is a property of the endpoint, not of this bridge — and
|
|
206
207
|
`commit_sha` is the remedy: it names WHICH RUN to describe, where `branch` asks about a series. Every
|
|
207
|
-
run-grain block re-anchors together (`latest_run` and its rollups, the
|
|
208
|
-
`spec_directory_files`, `spec_file_examples`, `repeated_description_examples`, `unannotated_examples
|
|
209
|
-
— `shards`, both growth windows, `previous_test_run`); `history` does not, so the
|
|
208
|
+
run-grain block re-anchors together (`latest_run` and its rollups, the five run-grain drill-ins —
|
|
209
|
+
`spec_directory_files`, `spec_file_examples`, `repeated_description_examples`, `unannotated_examples`,
|
|
210
|
+
`slowest_examples` — `shards`, both growth windows, `previous_test_run`); `history` does not, so the
|
|
210
211
|
`history[0] == latest_run` identity holds
|
|
211
212
|
on a default call and is **not** expected to hold under an explicit ask. Nor does `unstable_test_runs`,
|
|
212
213
|
which is read over the branch window rather than off the anchored run. (`unannotated_directories`,
|
|
@@ -224,8 +225,8 @@ successful response is about the commit you named.
|
|
|
224
225
|
it: `unstable_tests` (which tests failed intermittently across the window rather than consistently)
|
|
225
226
|
and `directory_growth` (how each area moved between the two **endpoints** of the window). The
|
|
226
227
|
per-area comparisons against the **previous run** — `directory_run_growth` at the example-count
|
|
227
|
-
grain
|
|
228
|
-
branch at all: they scope to the latest run's own branch by construction, so a plain unparameterised
|
|
228
|
+
grain, `directory_runtime_growth` at the runtime grain, and `layer_run_growth` for the declared-layer
|
|
229
|
+
mix — are a different question and take no branch at all: they scope to the latest run's own branch by construction, so a plain unparameterised
|
|
229
230
|
call already carries them. The two grains are independent, which is why both ship: making an
|
|
230
231
|
existing spec slow adds zero examples and shows up only in the runtime pair, and splitting one slow
|
|
231
232
|
spec into four fast ones is `+3` examples and *less* time.
|
|
@@ -233,8 +234,8 @@ spec into four fast ones is `+3` examples and *less* time.
|
|
|
233
234
|
`spec_directories` ranks the heaviest areas but stops at the area grain, so it says *where* the time
|
|
234
235
|
went and not *which files* spent it. `spec_directory` is the next question: pass a path exactly as
|
|
235
236
|
served in `latest_run.spec_directories.rows[].path` and `latest_run.spec_directory_files` opens with
|
|
236
|
-
the files in that one directory (`total_seconds`, `recorded_count`, `timed_count`
|
|
237
|
-
**area's** own `file_count`/`recorded_count`/`timed_count` and the `limit` the row list was cut at —
|
|
237
|
+
the files in that one directory (`total_seconds`, `recorded_count`, `timed_count`, `layer_counts`
|
|
238
|
+
each), plus the **area's** own `layer_counts`, `file_count`/`recorded_count`/`timed_count` and the `limit` the row list was cut at —
|
|
238
239
|
those totals describe the whole area, not the returned page, so don't re-derive them from `rows`.
|
|
239
240
|
Omit the argument and the key is `null`, meaning *you did not ask*; an area the run recorded nothing
|
|
240
241
|
for answers `rows: []` rather than an error, so a renamed or deleted directory is an empty result and
|
|
@@ -250,7 +251,8 @@ two are the answer to the question the area-grain comparisons dead-end on — `s
|
|
|
250
251
|
most and not *which examples* inside them spent it. `spec_file` is the next question: pass a path
|
|
251
252
|
exactly as served in `latest_run.spec_files.rows[].path` and `latest_run.spec_file_examples` opens
|
|
252
253
|
with up to 50 of that file's individual examples, cut by **duration** (`name`, `file_path`,
|
|
253
|
-
`line_number`, `spec_file_path`, `duration_seconds`, `outcome`
|
|
254
|
+
`line_number`, `spec_file_path`, `duration_seconds`, `outcome`, `intent_layer`, `declared_intent`
|
|
255
|
+
each — eight fields), plus the **file's** own
|
|
254
256
|
`recorded_count`/`timed_count` and the `limit` the row list was cut at — those totals describe the
|
|
255
257
|
whole file, not the returned page, so don't re-derive them from `rows`. Omit the argument and the
|
|
256
258
|
key is `null`, meaning *you did not ask*; a path that matched nothing answers `rows: []` rather than
|
|
@@ -260,7 +262,7 @@ an error, so a renamed or deleted spec file and a stale bookmark are empty resul
|
|
|
260
262
|
ranking — but names the description and the files it was seen in, not *which* examples say the same
|
|
261
263
|
thing. `repeated_description` is the next question: pass a description exactly as served in
|
|
262
264
|
`latest_run.repeated_descriptions.rows[].name` and `latest_run.repeated_description_examples` opens
|
|
263
|
-
with up to 25 of that group's members (the same
|
|
265
|
+
with up to 25 of that group's members (the same eight fields), plus the **group's** own
|
|
264
266
|
`recorded_count`/`timed_count` and the `limit` the row list was cut at — again totals for the whole
|
|
265
267
|
group and not for the returned page. This is the **only** way to reach a group's members:
|
|
266
268
|
`slowest_examples` is the run-wide top ten and rarely contains them, and walking `spec_file` over
|
|
@@ -281,6 +283,14 @@ description's rows run by run in window order, **newest run first**, up to 200 (
|
|
|
281
283
|
`recorded_count`/`reported_outcome_count`/`unreported_outcome_count`, the window's `run_count` and
|
|
282
284
|
the `limit` the row list was cut at.
|
|
283
285
|
|
|
286
|
+
Every row of `unstable_tests.rows[]` and of `unstable_tests.shared_description_rows[]` also carries
|
|
287
|
+
`declared_layers` — the sorted distinct set of `@intent layer:` values that test's examples declared
|
|
288
|
+
anywhere in the window. `[]` means no run in the window declared one: it is not `unreadable` and not
|
|
289
|
+
a measured `unit`. A partially annotated test lists only what was declared, and more than one entry
|
|
290
|
+
means the test changed layer mid-window. It is declared-only — the stored column, never inferred from
|
|
291
|
+
the spec path or `files_seen`. The `unstable_test_runs` drill-in rows do **not** carry a layer, so
|
|
292
|
+
read `declared_layers` off the ranking row.
|
|
293
|
+
|
|
284
294
|
Note the two ways it differs from the drill-ins above. The answer lands **inside** the flakiness
|
|
285
295
|
block — `unstable_tests.unstable_test_runs`, not under `latest_run.*` — and `branch` is a hard
|
|
286
296
|
prerequisite rather than a suggestion: `unstable_tests` is `null` without it, so `unstable_test` sent
|
|
@@ -316,7 +326,51 @@ never render that subtraction as blindness. A derived reading is genuinely weake
|
|
|
316
326
|
one — no preconditions, a behavior written for a test runner's output rather than declared, and a
|
|
317
327
|
layer inferred from the directory — so report it as inferred and never as equivalent. And
|
|
318
328
|
`authored` never replaces `annotated_ratio`: "how much of this suite has a human-written intent" is
|
|
319
|
-
still that figure, off the run's own counters.
|
|
329
|
+
still that figure, off the run's own counters.
|
|
330
|
+
|
|
331
|
+
**Which layer the suite declares is a third, separate question**, answered on every response by
|
|
332
|
+
`latest_run.layer_counts`, a sibling of `intent_readings`: five operands — `unit`, `integration`,
|
|
333
|
+
`request`, `system` and `undeclared` — that sum to `intent_readings.recorded`. It is `null`, **not
|
|
334
|
+
five zeros**, when the run stored no per-example rows, because a zero would read as a measured none.
|
|
335
|
+
The layers are **declared only** — an `@intent` annotation said so — and **never inferred from the
|
|
336
|
+
directory path**: a test under `spec/models` that declared nothing is `undeclared`, and `undeclared`
|
|
337
|
+
is **not** `unreadable` (a test can be perfectly readable and declare no layer). The operands carry no
|
|
338
|
+
pyramid verdict; reading the mix is yours. **Where the machine time went by layer** is
|
|
339
|
+
`latest_run.layer_durations`, a sibling of `layer_counts` with the same five keys, each
|
|
340
|
+
`{total_seconds, timed_count}`. `total_seconds` is `null`, **not 0**, for a layer with no timed example,
|
|
341
|
+
so an untimed layer is not a free one; `timed_count` (at most that layer's `layer_counts` entry) is the
|
|
342
|
+
real denominator for any average. The whole key is `null` when the run stored no per-example rows. It is
|
|
343
|
+
a **sum** of example durations — machine time and **not wall clock** — so it can exceed
|
|
344
|
+
`duration_seconds` on a parallel or sharded run. The layer is **declared only**, never inferred from the
|
|
345
|
+
path, and the operands carry no pyramid verdict. **Which tests those are**: pass `layer`
|
|
346
|
+
(`unit`, `integration`, `request`, `system` or `undeclared`) to narrow `latest_run.slowest_examples`
|
|
347
|
+
— and only that block — to one declared layer; it is echoed as `slowest_examples.layer` only when
|
|
348
|
+
asked, `recorded_count`/`timed_count`/`reported_outcome_count` become that layer's own counts, any
|
|
349
|
+
other value is read by the server as no ask (so it is forwarded unvalidated), and `rows: []` with
|
|
350
|
+
`recorded_count: 0` means that layer has no examples while `slowest_examples: null` means the run
|
|
351
|
+
recorded nothing. **How the declared-layer mix moved since the previous
|
|
352
|
+
run** is `layer_run_growth`, with its contract block `layer_run_growth_window` — both **top-level
|
|
353
|
+
siblings of `directory_run_growth`**, not inside `latest_run` (the delta is not in
|
|
354
|
+
`latest_run.layer_counts`). They compare the latest run with the previous run **on the latest run's own
|
|
355
|
+
branch**, so no `branch` is needed, and `commit_sha` re-anchors them with the rest of the run-grain
|
|
356
|
+
blocks. `layer_run_growth` is `null` in **every** non-comparable state — **never a block of zeros**, so
|
|
357
|
+
`null` is not "no change" — and `layer_run_growth_window.state` names why: `latest_unmeasured`,
|
|
358
|
+
`previous_unmeasured`, `assembled_differently`, `neither_recorded`, `previous_unrecorded`,
|
|
359
|
+
`latest_unrecorded`, or `comparable`, plus `no_latest_run` (CI never reported) and `no_previous_run`
|
|
360
|
+
(the first run on the branch). `layer_run_growth_window.comparable` is true exactly when
|
|
361
|
+
`layer_run_growth` is non-null; the window also carries `basis`, `branch`, `anchor_commit_sha` and
|
|
362
|
+
`baseline_commit_sha`. When comparable, `layer_run_growth` is
|
|
363
|
+
`{layers, baseline_recorded_count, anchor_recorded_count}`, where `layers` holds `unit`, `integration`,
|
|
364
|
+
`request`, `system` and `undeclared`, each `{baseline_count, anchor_count, change}`. A layer that did not
|
|
365
|
+
move reads `change: 0` — a **measured zero**, not an absence — and the five `change` values sum to
|
|
366
|
+
`anchor_recorded_count` minus `baseline_recorded_count`. The layers are **declared only**, never inferred
|
|
367
|
+
from the path, `undeclared` is **not** `unreadable`, and the operands carry no pyramid verdict. The same
|
|
368
|
+
`layer_counts` rides each row of
|
|
369
|
+
`spec_directories`, the `spec_directory_files` area block and each of its file rows, and every
|
|
370
|
+
per-example row of `slowest_examples`, `spec_file_examples` and `repeated_description_examples`
|
|
371
|
+
carries `intent_layer` and `declared_intent` (`{entity, action, behavior}` or `null`, declared only).
|
|
372
|
+
|
|
373
|
+
The `unannotated_examples` flag is the one argument
|
|
320
374
|
here that is a **flag rather than a name** — pass `true`, not a value — because it opens a
|
|
321
375
|
*population* rather than a pick: `total_specs` minus `annotated_specs` is a subtraction, and a
|
|
322
376
|
subtraction has no line to name. Which population is still yours to choose: sent alone the flag
|
|
@@ -324,8 +378,8 @@ opens the whole run, and sent **together with** `spec_file` or `spec_directory`
|
|
|
324
378
|
file, that area, or the AND of the two — those two keep opening their own blocks as well, so
|
|
325
379
|
narrowing this one is additional rather than instead. `latest_run.unannotated_examples` opens with
|
|
326
380
|
up to 100 of the unannotated examples **of whatever you asked for** (`name`, `file_path`,
|
|
327
|
-
`line_number`, `spec_file_path`, `reading` and `derived_intent` each — six fields,
|
|
328
|
-
per-example drill-ins'
|
|
381
|
+
`line_number`, `spec_file_path`, `reading` and `derived_intent` each — six fields, unlike the
|
|
382
|
+
per-example drill-ins' eight: no `duration_seconds`, no `outcome`, no `intent_layer`), plus that
|
|
329
383
|
same population's own `recorded_count`, `derived_count` and `unreadable_count`, the `limit` the row
|
|
330
384
|
list was cut at, and
|
|
331
385
|
`spec_file`/`spec_directory` **echoed back** as the server read them — `null` for each one you did
|
|
@@ -500,7 +554,7 @@ blank value is no ask: passing an empty string and omitting the argument make th
|
|
|
500
554
|
| --- | --- |
|
|
501
555
|
| `q` | keep only repositories whose `full_name` (`org/repo`) contains this substring, case-insensitively. A plain substring, not a pattern — the LIKE wildcards `%`/`_` are escaped server-side, so `org/my_repo` matches itself. A match-less ask is an empty list, not an error |
|
|
502
556
|
| `role` | `"owned"` or `"shared"` — one half of the list's mix: the repositories this person owns, or the ones shared with them. Mind the spelling: the *ask* values are `owned`/`shared`, while each entry's `role` field reads `owner`/`member` — `role: "owner"` is not a valid ask and settles to no ask (full list, no error). Under the `sga_…` agent key the ask settles to no ask for every value: ownership is a person fact and the key speaks for nobody, so the full granted list is served |
|
|
503
|
-
| `sort` | `"stale"` — re-order stalest-first: repositories CI has never ingested a run for first, then least-recently-ingested, with `full_name` breaking ties so two calls agree element for element. The same entries, a different order; the default order stays `full_name` ascending |
|
|
557
|
+
| `sort` | `"stale"` — re-order stalest-first: repositories CI has never ingested a run for first, then least-recently-ingested, with `full_name` breaking ties so two calls agree element for element. `"annotated"` — re-order least-annotated share first: a suite measured at 0-of-N leads, repositories with no run or an unmeasured suite come last (never ranked as 0%), and `full_name` breaks ties. The same entries, a different order; the default order stays `full_name` ascending |
|
|
504
558
|
|
|
505
559
|
The body comes back as SpecGuard serves it — `{"repositories": […]}`, each entry carrying `id`,
|
|
506
560
|
`full_name`, `name`, `registered_at`, `role`, `delivery_health` and `latest_run`, ordered by
|
|
@@ -726,6 +780,13 @@ its last-known path and the weight the latest run measured.
|
|
|
726
780
|
| --- | --- |
|
|
727
781
|
| `behavior` | **required** — the behavior phrase, in plain words (e.g. "rejects an expired password reset token"). Trimmed and sent as `near`. Blank or whitespace-only is refused before any request, and so is a phrase containing a NUL (`\u0000`) character: the server would read either as no ask and answer the whole overview with `near: null`, which would look like an answered ask |
|
|
728
782
|
| `repository` | ask THIS repository (its numeric id from `list_repositories`) under the **agent key** (`SPECGUARD_AGENT_API_KEY`, `sga_…`), or — when no agent key is set — the **user key** (`SPECGUARD_USER_API_KEY`, `sgu_…`), instead of the one the `sgk_…` key resolves to — omit it for the default, `sgk_`-keyed ask |
|
|
783
|
+
| `limit` | optional integer page size, **1–50** — omit it for the default page of 10. Sent as `&limit=<n>` only when supplied, so an ask without it is the same request as before. The server clamps the ask to 50 and reports the value it applied as `limit` in the `near` block; a non-integer, zero or negative value is refused before any request |
|
|
784
|
+
|
|
785
|
+
**The answer is a page, not the set.** Read `truncated` and `limit` in the returned `near` block:
|
|
786
|
+
`limit` is the page size the server applied, and `truncated: true` means **more matches exist than
|
|
787
|
+
were returned** — a full page of 10 may be "10 of 37". When `truncated` is true, re-ask the **same
|
|
788
|
+
phrase** with a larger `limit` (max 50) before paraphrasing it: a repeated phrase is cache-served and
|
|
789
|
+
free, while each paraphrase is a novel phrase and one billed embed.
|
|
729
790
|
|
|
730
791
|
**What the answer is not.** It ranks *stored* tests nearest the phrase and **never answers "is this
|
|
731
792
|
already tested?"** — it never gates a write and never issues a verdict. A hit near the phrase is not
|
|
@@ -1194,7 +1255,7 @@ npm run typecheck # types only
|
|
|
1194
1255
|
## Related repositories
|
|
1195
1256
|
|
|
1196
1257
|
- [`specguard`](https://github.com/yatfa-ai/specguard) — the platform: ingest API + Hotwire dashboard
|
|
1197
|
-
- [`specguard-
|
|
1258
|
+
- [`specguard-ruby`](https://github.com/yatfa-ai/specguard-ruby) — Ruby client (RSpec formatter, Minitest reporter + `@intent` linter)
|
|
1198
1259
|
- [`open-test-intent`](https://github.com/yatfa-ai/open-test-intent) — the annotation protocol SpecGuard consumes
|
|
1199
1260
|
|
|
1200
1261
|
## License
|
package/dist/src/config.d.ts
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
*
|
|
20
20
|
* == SPECGUARD_ENDPOINT, with SPECGUARD_URL as an accepted alias
|
|
21
21
|
*
|
|
22
|
-
* `SPECGUARD_ENDPOINT` is the name the shipped `specguard-
|
|
22
|
+
* `SPECGUARD_ENDPOINT` is the name the shipped `specguard-ruby` gem already
|
|
23
23
|
* reads, so a repository whose CI posts runs to SpecGuard has it set — that is
|
|
24
24
|
* the whole reason this server borrows the name rather than coining one. The
|
|
25
25
|
* SPGD-310 brief writes it as `SPECGUARD_URL`, so that spelling is accepted
|
|
@@ -222,7 +222,7 @@ export declare const REPOSITORY_CREDENTIAL: Credential;
|
|
|
222
222
|
/**
|
|
223
223
|
* The `sgu_` key: a person, and a variable nothing else in the toolchain reads.
|
|
224
224
|
*
|
|
225
|
-
* `specguard-
|
|
225
|
+
* `specguard-ruby` ships runs with an `sgk_` key and has no notion of this one,
|
|
226
226
|
* so an operator who already has CI reporting to SpecGuard does NOT already
|
|
227
227
|
* have this variable — which is why the message says where to mint one rather
|
|
228
228
|
* than assuming it is lying around.
|
package/dist/src/config.js
CHANGED
|
@@ -47,7 +47,7 @@ export const REPOSITORY_CREDENTIAL = {
|
|
|
47
47
|
/**
|
|
48
48
|
* The `sgu_` key: a person, and a variable nothing else in the toolchain reads.
|
|
49
49
|
*
|
|
50
|
-
* `specguard-
|
|
50
|
+
* `specguard-ruby` ships runs with an `sgk_` key and has no notion of this one,
|
|
51
51
|
* so an operator who already has CI reporting to SpecGuard does NOT already
|
|
52
52
|
* have this variable — which is why the message says where to mint one rather
|
|
53
53
|
* than assuming it is lying around.
|
|
@@ -272,7 +272,7 @@ async function requestUserAgent() {
|
|
|
272
272
|
* ONE TOTAL BUDGET, not one per phase. `requestTimeoutMs` bounds the whole call:
|
|
273
273
|
* headers and body share it, so a response whose headers took 29s of a 30s
|
|
274
274
|
* budget has 1s left in which to deliver its body. The sibling transport in
|
|
275
|
-
* `specguard-
|
|
275
|
+
* `specguard-ruby` (`lib/specguard/client/transport.rb`) gives each phase its own
|
|
276
276
|
* full `@timeout` because `Net::HTTP` exposes exactly that knob and no other;
|
|
277
277
|
* here the deadline is ours to place, and a single total is both stricter and
|
|
278
278
|
* the thing an operator who set one number actually meant.
|
package/dist/src/tools/args.d.ts
CHANGED
|
@@ -66,6 +66,17 @@ export declare function optionalString(value: unknown, field: string): string |
|
|
|
66
66
|
*/
|
|
67
67
|
export declare function requireString(value: unknown, field: string): string;
|
|
68
68
|
export declare function optionalBoolean(value: unknown, field: string): boolean | undefined;
|
|
69
|
+
/**
|
|
70
|
+
* A positive integer, or nothing.
|
|
71
|
+
*
|
|
72
|
+
* Accepts only a finite JSON number with no fractional part that is >= 1. A
|
|
73
|
+
* numeric string ("10"), a float (2.5), zero, a negative, NaN and Infinity all
|
|
74
|
+
* throw `ArgumentError` — the same class every other shape check here throws.
|
|
75
|
+
* No upper bound is applied: a ceiling is the callee's to own (the server
|
|
76
|
+
* clamps and reports the value it applied), and the one a tool's schema
|
|
77
|
+
* advertises is the schema's to declare, not this helper's to duplicate.
|
|
78
|
+
*/
|
|
79
|
+
export declare function optionalPositiveInteger(value: unknown, field: string): number | undefined;
|
|
69
80
|
/**
|
|
70
81
|
* An optional array of non-blank strings, or nothing.
|
|
71
82
|
*
|
package/dist/src/tools/args.js
CHANGED
|
@@ -93,6 +93,26 @@ export function optionalBoolean(value, field) {
|
|
|
93
93
|
throw new ArgumentError(`\`${field}\` must be a boolean.`);
|
|
94
94
|
return value;
|
|
95
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* A positive integer, or nothing.
|
|
98
|
+
*
|
|
99
|
+
* Accepts only a finite JSON number with no fractional part that is >= 1. A
|
|
100
|
+
* numeric string ("10"), a float (2.5), zero, a negative, NaN and Infinity all
|
|
101
|
+
* throw `ArgumentError` — the same class every other shape check here throws.
|
|
102
|
+
* No upper bound is applied: a ceiling is the callee's to own (the server
|
|
103
|
+
* clamps and reports the value it applied), and the one a tool's schema
|
|
104
|
+
* advertises is the schema's to declare, not this helper's to duplicate.
|
|
105
|
+
*/
|
|
106
|
+
export function optionalPositiveInteger(value, field) {
|
|
107
|
+
if (value === undefined || value === null)
|
|
108
|
+
return undefined;
|
|
109
|
+
if (typeof value !== "number" || !Number.isInteger(value)) {
|
|
110
|
+
throw new ArgumentError(`\`${field}\` must be an integer.`);
|
|
111
|
+
}
|
|
112
|
+
if (value < 1)
|
|
113
|
+
throw new ArgumentError(`\`${field}\` must be at least 1.`);
|
|
114
|
+
return value;
|
|
115
|
+
}
|
|
96
116
|
/**
|
|
97
117
|
* An optional array of non-blank strings, or nothing.
|
|
98
118
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"args.js","sourceRoot":"","sources":["../../../src/tools/args.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"args.js","sourceRoot":"","sources":["../../../src/tools/args.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ArgumentError } from "../errors.js";
|
|
2
2
|
import { getJsonObject, repositoryTarget } from "../support/specguard-api.js";
|
|
3
|
-
import { optionalString, requireString } from "./args.js";
|
|
3
|
+
import { optionalPositiveInteger, optionalString, requireString } from "./args.js";
|
|
4
4
|
/**
|
|
5
5
|
* `GET /api/v1/repository?near=<behavior phrase>` as a tool — the bridge half
|
|
6
6
|
* of roadmap SPGD-1102 ("Ask the suite map"), slice 3. The platform half
|
|
@@ -45,6 +45,12 @@ const findTestsNearBehavior = {
|
|
|
45
45
|
"The server embeds your phrase and ranks the repository's stored test identities by similarity, " +
|
|
46
46
|
"returning the top hits in the `near` block — each with its similarity, `signal_source`, last-known " +
|
|
47
47
|
"path and the weight the latest run measured. " +
|
|
48
|
+
"THE ANSWER IS A PAGE, NOT THE SET: the default page is 10 hits. READ `truncated` AND `limit` in the " +
|
|
49
|
+
"returned `near` block — `limit` is the page size the server APPLIED (it clamps your ask to 50) and " +
|
|
50
|
+
"`truncated: true` means MORE matches exist than were returned, so a full page of 10 may be \"10 of " +
|
|
51
|
+
"37\". When `truncated` is true, re-ask the SAME phrase with a larger optional `limit` (max 50) " +
|
|
52
|
+
"BEFORE paraphrasing it: a repeated phrase is cache-served and free, while each paraphrase is a " +
|
|
53
|
+
"novel phrase and one billed embed. " +
|
|
48
54
|
"WHAT THE ANSWER IS NOT: it ranks STORED tests nearest the phrase and NEVER answers \"is this " +
|
|
49
55
|
"already tested?\". It never gates a write and never issues a verdict — a hit near the phrase is not " +
|
|
50
56
|
"coverage, and an empty answer is not proof of absence. `similarity_floor` is the near-duplicate " +
|
|
@@ -108,6 +114,18 @@ const findTestsNearBehavior = {
|
|
|
108
114
|
"indistinguishable from one that does not exist. " +
|
|
109
115
|
"Omit it — or pass a blank — and the call is the singular one under SPECGUARD_API_KEY.",
|
|
110
116
|
},
|
|
117
|
+
limit: {
|
|
118
|
+
type: "integer",
|
|
119
|
+
minimum: 1,
|
|
120
|
+
maximum: 50,
|
|
121
|
+
description: "OPTIONAL page size: how many hits to return in the `near` block. Omit it for the " +
|
|
122
|
+
"default page of 10. The server clamps the ask to 50 and reports the value it applied " +
|
|
123
|
+
"as `limit` in the `near` block, beside `truncated` — read both: `truncated: true` " +
|
|
124
|
+
"means more matches exist than were returned, and asking again with a larger `limit` " +
|
|
125
|
+
"(same phrase, so cache-served and free) is cheaper than paraphrasing it (a novel " +
|
|
126
|
+
"phrase, one billed embed). Must be an integer >= 1; anything else is refused before " +
|
|
127
|
+
"any request.",
|
|
128
|
+
},
|
|
111
129
|
},
|
|
112
130
|
required: ["behavior"],
|
|
113
131
|
additionalProperties: false,
|
|
@@ -123,8 +141,15 @@ const findTestsNearBehavior = {
|
|
|
123
141
|
"like an answered ask. Remove the character and send the phrase again.");
|
|
124
142
|
}
|
|
125
143
|
const repository = optionalString(args["repository"], "repository");
|
|
144
|
+
const limit = optionalPositiveInteger(args["limit"], "limit");
|
|
126
145
|
const { api, path } = repositoryTarget(context.config, repository);
|
|
127
|
-
|
|
146
|
+
// `limit` rides the wire only when supplied, so an ask without it is
|
|
147
|
+
// byte-identical to the request this tool made before `limit` existed. The
|
|
148
|
+
// server owns the clamp (50) and reports the applied value in `near.limit`.
|
|
149
|
+
const query = { near: behavior };
|
|
150
|
+
if (limit !== undefined)
|
|
151
|
+
query["limit"] = String(limit);
|
|
152
|
+
const overview = await getJsonObject(api, path, query, context.fetch);
|
|
128
153
|
return {
|
|
129
154
|
text: JSON.stringify(overview, null, 2),
|
|
130
155
|
structured: overview,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"find-tests-near-behavior.js","sourceRoot":"","sources":["../../../src/tools/find-tests-near-behavior.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC9E,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"find-tests-near-behavior.js","sourceRoot":"","sources":["../../../src/tools/find-tests-near-behavior.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,7 +12,7 @@ import type { ToolDefinition } from "./types.js";
|
|
|
12
12
|
* == What is in the bootstrap, and why only these two
|
|
13
13
|
*
|
|
14
14
|
* - `lint_intent_annotations` wraps `specguard-lint` (shipped:
|
|
15
|
-
* `specguard-
|
|
15
|
+
* `specguard-ruby/bin/specguard-lint`, SPGD-12 §1).
|
|
16
16
|
* - `get_repository_overview` wraps `GET /api/v1/repository` (shipped:
|
|
17
17
|
* `specguard/config/routes.rb`).
|
|
18
18
|
*
|
package/dist/src/tools/index.js
CHANGED
|
@@ -32,7 +32,7 @@ import updateRepositoryMemberPermissions from "./update-repository-member-permis
|
|
|
32
32
|
* == What is in the bootstrap, and why only these two
|
|
33
33
|
*
|
|
34
34
|
* - `lint_intent_annotations` wraps `specguard-lint` (shipped:
|
|
35
|
-
* `specguard-
|
|
35
|
+
* `specguard-ruby/bin/specguard-lint`, SPGD-12 §1).
|
|
36
36
|
* - `get_repository_overview` wraps `GET /api/v1/repository` (shipped:
|
|
37
37
|
* `specguard/config/routes.rb`).
|
|
38
38
|
*
|
|
@@ -61,7 +61,9 @@ import type { ToolDefinition } from "./types.js";
|
|
|
61
61
|
* and `?role=shared` (the exact partition of the accessible set into owned and
|
|
62
62
|
* shared halves), and `?sort=stale` (never-ingested repositories first, then
|
|
63
63
|
* least-recently-ingested, `github_full_name` breaking ties so the order is
|
|
64
|
-
* deterministic)
|
|
64
|
+
* deterministic) or `?sort=annotated` (least-annotated share first; a
|
|
65
|
+
* repository with no run or an unmeasured suite last, never ranked as 0%; a
|
|
66
|
+
* suite measured at 0-of-N first; `github_full_name` breaking ties). The concern's own comment says the endpoint reads them "for
|
|
65
67
|
* a machine" — and this bridge IS that machine, so they are forwarded here
|
|
66
68
|
* rather than re-invented.
|
|
67
69
|
*
|
|
@@ -159,8 +161,8 @@ import type { ToolDefinition } from "./types.js";
|
|
|
159
161
|
* The order is `full_name` ascending, which the controller picks as the only
|
|
160
162
|
* column a client can page or diff against without SpecGuard promising an id
|
|
161
163
|
* ordering it has not designed — and `?sort=stale` re-sequences exactly that
|
|
162
|
-
* loaded set rather than issuing a different query
|
|
163
|
-
* change, only their order does. It is stated here for the same reason the
|
|
164
|
+
* loaded set rather than issuing a different query (as does `?sort=annotated`),
|
|
165
|
+
* so the entries never change, only their order does. It is stated here for the same reason the
|
|
164
166
|
* other tool states its orders: a list whose order is a coincidence and a list
|
|
165
167
|
* whose order is a contract look identical in a response body.
|
|
166
168
|
*/
|
|
@@ -62,7 +62,9 @@ import { optionalString } from "./args.js";
|
|
|
62
62
|
* and `?role=shared` (the exact partition of the accessible set into owned and
|
|
63
63
|
* shared halves), and `?sort=stale` (never-ingested repositories first, then
|
|
64
64
|
* least-recently-ingested, `github_full_name` breaking ties so the order is
|
|
65
|
-
* deterministic)
|
|
65
|
+
* deterministic) or `?sort=annotated` (least-annotated share first; a
|
|
66
|
+
* repository with no run or an unmeasured suite last, never ranked as 0%; a
|
|
67
|
+
* suite measured at 0-of-N first; `github_full_name` breaking ties). The concern's own comment says the endpoint reads them "for
|
|
66
68
|
* a machine" — and this bridge IS that machine, so they are forwarded here
|
|
67
69
|
* rather than re-invented.
|
|
68
70
|
*
|
|
@@ -160,8 +162,8 @@ import { optionalString } from "./args.js";
|
|
|
160
162
|
* The order is `full_name` ascending, which the controller picks as the only
|
|
161
163
|
* column a client can page or diff against without SpecGuard promising an id
|
|
162
164
|
* ordering it has not designed — and `?sort=stale` re-sequences exactly that
|
|
163
|
-
* loaded set rather than issuing a different query
|
|
164
|
-
* change, only their order does. It is stated here for the same reason the
|
|
165
|
+
* loaded set rather than issuing a different query (as does `?sort=annotated`),
|
|
166
|
+
* so the entries never change, only their order does. It is stated here for the same reason the
|
|
165
167
|
* other tool states its orders: a list whose order is a coincidence and a list
|
|
166
168
|
* whose order is a contract look identical in a response body.
|
|
167
169
|
*/
|
|
@@ -198,9 +200,11 @@ const listRepositories = {
|
|
|
198
200
|
"`role: \"owned\"` or `\"shared\"` (one half of the owned/shared mix — note the ask is " +
|
|
199
201
|
"spelled `owned`, not the response field's `owner`; under the AGENT key this ask settles " +
|
|
200
202
|
"to the no-ask, because ownership is a person fact and the key speaks for nobody), and " +
|
|
201
|
-
"`sort
|
|
203
|
+
"`sort` (`\"stale\"`: repositories " +
|
|
202
204
|
"CI has never ingested a run for first, then least-recently-ingested, `full_name` " +
|
|
203
|
-
"breaking ties
|
|
205
|
+
"breaking ties; `\"annotated\"`: least-annotated share first, repositories with no run " +
|
|
206
|
+
"or an unmeasured suite last — never ranked as 0% — a suite measured at 0-of-N first, " +
|
|
207
|
+
"`full_name` breaking ties). Omit them all and the request is the plain full list. " +
|
|
204
208
|
"Ordered by `full_name` ascending unless `sort` asks otherwise, and stable across calls " +
|
|
205
209
|
"either way. " +
|
|
206
210
|
"The set is exactly what the key behind it may see — a repository outside the credential's " +
|
|
@@ -252,16 +256,20 @@ const listRepositories = {
|
|
|
252
256
|
},
|
|
253
257
|
sort: {
|
|
254
258
|
type: "string",
|
|
255
|
-
enum: ["stale"],
|
|
256
|
-
description: "Re-order the list
|
|
259
|
+
enum: ["stale", "annotated"],
|
|
260
|
+
description: "Re-order the list. The default order is `full_name` ascending, which " +
|
|
257
261
|
"is stable across calls but says nothing about what needs attention; `stale` puts " +
|
|
258
262
|
"the repositories CI has NEVER ingested a run for FIRST (never-ingested is the " +
|
|
259
263
|
"stalest state on this list, not a zero), then least-recently-ingested, newest last, " +
|
|
260
264
|
"with `github_full_name` breaking ties so two calls with the same data agree element " +
|
|
261
265
|
"for element. " +
|
|
266
|
+
"`annotated` puts the LEAST-annotated suites first (annotated share of the latest " +
|
|
267
|
+
"run, ascending): a suite measured at 0-of-N leads, and repositories CI has never " +
|
|
268
|
+
"reported a run for, or whose suite was not measured, come LAST — an unmeasured suite " +
|
|
269
|
+
"is never ranked as 0% — with `github_full_name` breaking ties. " +
|
|
262
270
|
"It re-sequences the loaded set rather than issuing a different query: the same " +
|
|
263
|
-
"entries, a different order. `stale`
|
|
264
|
-
"is deliberately no word for the default, because omitting the argument already " +
|
|
271
|
+
"entries, a different order. `stale` and `annotated` are the sole sort words the endpoint " +
|
|
272
|
+
"names — there is deliberately no word for the default, because omitting the argument already " +
|
|
265
273
|
"means it. " +
|
|
266
274
|
"Any other value a schema-bypassing client sends settles to the server's no-ask clamp " +
|
|
267
275
|
"(default order, no error); blank (or null) is no ask, byte-identical to omitting the " +
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list-repositories.js","sourceRoot":"","sources":["../../../src/tools/list-repositories.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,2BAA2B,EAAE,MAAM,6BAA6B,CAAC;AACzF,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAG3C
|
|
1
|
+
{"version":3,"file":"list-repositories.js","sourceRoot":"","sources":["../../../src/tools/list-repositories.ts"],"names":[],"mappings":"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"}
|
|
@@ -173,12 +173,12 @@ import type { ToolDefinition } from "./types.js";
|
|
|
173
173
|
* one CHOOSES THAT RUN, which the controller states in those terms: `?branch=`
|
|
174
174
|
* asks about a SERIES, this asks WHICH RUN. It is read once, in that memo, so
|
|
175
175
|
* every run-grain block moves together — `latest_run` and its rollups, the
|
|
176
|
-
*
|
|
177
|
-
* `repeated_description_examples
|
|
178
|
-
* rung documented below
|
|
179
|
-
* `previous_test_run`.
|
|
176
|
+
* five RUN-GRAIN drill-ins (`spec_directory_files`, `spec_file_examples`,
|
|
177
|
+
* `repeated_description_examples`, `unannotated_examples` — the flag-style
|
|
178
|
+
* rung documented below — and `slowest_examples`, which `layer` narrows),
|
|
179
|
+
* `shards`, both growth windows and `previous_test_run`.
|
|
180
180
|
*
|
|
181
|
-
* That is
|
|
181
|
+
* That is five of the SIX drill-ins on this tool, and the excluded one is
|
|
182
182
|
* worth naming because it is the composition an agent will actually try:
|
|
183
183
|
* `unstable_test_runs` is read over the BRANCH WINDOW (`history_runs`), not off
|
|
184
184
|
* the anchored run, so it does not move with this parameter. Sent together,
|
|
@@ -325,7 +325,7 @@ import type { ToolDefinition } from "./types.js";
|
|
|
325
325
|
* is an ABSENCE of data. Serving `rows: []` here instead would spend a
|
|
326
326
|
* distinction a client has no other way to make.
|
|
327
327
|
*
|
|
328
|
-
* THE `commit_sha` ROSTERS above and in README.md
|
|
328
|
+
* THE `commit_sha` ROSTERS above and in README.md COUNT ONE KEY PER DRILL-IN PARAMETER (five, with `layer`), and
|
|
329
329
|
* the reason is the roster's UNIT, not anything about this block's shape. That
|
|
330
330
|
* roster carries ONE REPRESENTATIVE KEY PER DRILL-IN PARAMETER, not one entry
|
|
331
331
|
* per response key: `spec_directory` opens THREE blocks (see its own
|
|
@@ -174,12 +174,12 @@ import { optionalBoolean, optionalString } from "./args.js";
|
|
|
174
174
|
* one CHOOSES THAT RUN, which the controller states in those terms: `?branch=`
|
|
175
175
|
* asks about a SERIES, this asks WHICH RUN. It is read once, in that memo, so
|
|
176
176
|
* every run-grain block moves together — `latest_run` and its rollups, the
|
|
177
|
-
*
|
|
178
|
-
* `repeated_description_examples
|
|
179
|
-
* rung documented below
|
|
180
|
-
* `previous_test_run`.
|
|
177
|
+
* five RUN-GRAIN drill-ins (`spec_directory_files`, `spec_file_examples`,
|
|
178
|
+
* `repeated_description_examples`, `unannotated_examples` — the flag-style
|
|
179
|
+
* rung documented below — and `slowest_examples`, which `layer` narrows),
|
|
180
|
+
* `shards`, both growth windows and `previous_test_run`.
|
|
181
181
|
*
|
|
182
|
-
* That is
|
|
182
|
+
* That is five of the SIX drill-ins on this tool, and the excluded one is
|
|
183
183
|
* worth naming because it is the composition an agent will actually try:
|
|
184
184
|
* `unstable_test_runs` is read over the BRANCH WINDOW (`history_runs`), not off
|
|
185
185
|
* the anchored run, so it does not move with this parameter. Sent together,
|
|
@@ -326,7 +326,7 @@ import { optionalBoolean, optionalString } from "./args.js";
|
|
|
326
326
|
* is an ABSENCE of data. Serving `rows: []` here instead would spend a
|
|
327
327
|
* distinction a client has no other way to make.
|
|
328
328
|
*
|
|
329
|
-
* THE `commit_sha` ROSTERS above and in README.md
|
|
329
|
+
* THE `commit_sha` ROSTERS above and in README.md COUNT ONE KEY PER DRILL-IN PARAMETER (five, with `layer`), and
|
|
330
330
|
* the reason is the roster's UNIT, not anything about this block's shape. That
|
|
331
331
|
* roster carries ONE REPRESENTATIVE KEY PER DRILL-IN PARAMETER, not one entry
|
|
332
332
|
* per response key: `spec_directory` opens THREE blocks (see its own
|
|
@@ -480,7 +480,7 @@ const getRepositoryOverview = {
|
|
|
480
480
|
"which areas of the suite grew or shrank and which got slower or faster since the previous run " +
|
|
481
481
|
"on the same branch (the per-area comparisons, at BOTH the example-count grain and the " +
|
|
482
482
|
"runtime grain — an area where an existing spec was made slow gains no examples and appears " +
|
|
483
|
-
"only in the runtime one), " +
|
|
483
|
+
"only in the runtime one — and how the run-wide declared-layer mix moved, `layer_run_growth`), " +
|
|
484
484
|
"the recent run history for growth over time, and the branches that have runs. " +
|
|
485
485
|
"Everything above is answered about ONE repository, and WHICH one has two spellings: by " +
|
|
486
486
|
"default it is the repository the configured sgk_… repository key resolves to, and passing " +
|
|
@@ -496,6 +496,12 @@ const getRepositoryOverview = {
|
|
|
496
496
|
"`repeated_description` to see the examples that all share one repeated description, or " +
|
|
497
497
|
"`unstable_test` (alongside `branch`) to see one flaky test's outcome run by run, which is " +
|
|
498
498
|
"the only way to tell a regression from genuine flakiness. " +
|
|
499
|
+
"Every row of `unstable_tests.rows[]` AND of `unstable_tests.shared_description_rows[]` also " +
|
|
500
|
+
"carries `declared_layers` — the sorted DISTINCT set of `@intent layer:` values that test's " +
|
|
501
|
+
"examples declared anywhere in the window. `[]` means no run in the window declared one: NOT " +
|
|
502
|
+
"`unreadable`, and NOT a measured `unit`. A partially annotated test lists only what was " +
|
|
503
|
+
"declared, and more than one entry means the test changed layer mid-window. It is " +
|
|
504
|
+
"DECLARED-ONLY — the stored column, never inferred from the spec path or `files_seen`. " +
|
|
499
505
|
"All of those describe the repository's NEWEST run, which on a busy repository may be another " +
|
|
500
506
|
"branch's: pass `commit_sha` to be answered about ONE named run instead — after pushing a " +
|
|
501
507
|
"commit and waiting for CI, say — then read `run_anchor` to confirm which run you were served, " +
|
|
@@ -511,6 +517,43 @@ const getRepositoryOverview = {
|
|
|
511
517
|
"is the ONLY figure on this endpoint that means tests SpecGuard can say nothing about. Never " +
|
|
512
518
|
"read `total_specs - annotated_specs` as that population — on a suite that has never been " +
|
|
513
519
|
"annotated it is the whole suite, and almost all of it is readable. " +
|
|
520
|
+
"WHICH LAYER THE SUITE DECLARES IS A THIRD, SEPARATE QUESTION, answered on every response by " +
|
|
521
|
+
"`latest_run.layer_counts`, a sibling of `intent_readings`: five operands — `unit`, " +
|
|
522
|
+
"`integration`, `request`, `system` and `undeclared` — that sum to `intent_readings.recorded`. " +
|
|
523
|
+
"It is `null`, NOT five zeros, when the run stored no per-example rows, because a zero would " +
|
|
524
|
+
"read as a measured none. The layers are DECLARED only — an `@intent` annotation said so — and " +
|
|
525
|
+
"NEVER inferred from the directory path, so a test under `spec/models` that declared nothing " +
|
|
526
|
+
"is `undeclared`; and `undeclared` is NOT `unreadable` (a test can be perfectly readable and " +
|
|
527
|
+
"declare no layer). The operands carry no pyramid verdict; reading the mix is yours. " +
|
|
528
|
+
"WHERE THE MACHINE TIME WENT BY LAYER is `latest_run.layer_durations`, a sibling of `layer_counts` " +
|
|
529
|
+
"with the same five keys, each `{total_seconds, timed_count}`. `total_seconds` is `null`, NOT 0, for " +
|
|
530
|
+
"a layer with no timed example, so an untimed layer is not a free one; `timed_count` (at most that " +
|
|
531
|
+
"layer's `layer_counts` entry) is the real denominator for any average. The whole key is `null` when " +
|
|
532
|
+
"the run stored no per-example rows. It is a SUM of example durations — machine time and NOT wall " +
|
|
533
|
+
"clock — so it can exceed `duration_seconds` on a parallel or sharded run. The layer is DECLARED " +
|
|
534
|
+
"only, never inferred from the path, and the operands carry no pyramid verdict. " +
|
|
535
|
+
"HOW THE DECLARED-LAYER MIX MOVED SINCE THE PREVIOUS RUN is `layer_run_growth`, with its contract " +
|
|
536
|
+
"block `layer_run_growth_window` — both TOP-LEVEL siblings of `directory_run_growth`, NOT inside " +
|
|
537
|
+
"`latest_run` (the delta is not in `latest_run.layer_counts`). They compare the latest run with the " +
|
|
538
|
+
"previous run ON THE LATEST RUN'S OWN BRANCH, so no `branch` is needed, and `commit_sha` re-anchors " +
|
|
539
|
+
"them with the rest of the run-grain blocks exactly as it does the area pair. `layer_run_growth` is " +
|
|
540
|
+
"`null` in EVERY non-comparable state — NEVER a block of zeros, so `null` is not \"no change\" — and " +
|
|
541
|
+
"`layer_run_growth_window.state` names why: `latest_unmeasured`, `previous_unmeasured`, " +
|
|
542
|
+
"`assembled_differently`, `neither_recorded`, `previous_unrecorded`, `latest_unrecorded`, or " +
|
|
543
|
+
"`comparable`, plus `no_latest_run` (CI never reported) and `no_previous_run` (the first run on the " +
|
|
544
|
+
"branch). `layer_run_growth_window.comparable` is true exactly when `layer_run_growth` is non-null; " +
|
|
545
|
+
"the window also carries `basis`, `branch`, `anchor_commit_sha` and `baseline_commit_sha`. When " +
|
|
546
|
+
"comparable, `layer_run_growth` is `{layers, baseline_recorded_count, anchor_recorded_count}`, where " +
|
|
547
|
+
"`layers` holds `unit`, `integration`, `request`, `system` and `undeclared`, each " +
|
|
548
|
+
"`{baseline_count, anchor_count, change}`. A layer that did not move reads `change: 0` — a measured " +
|
|
549
|
+
"zero, not an absence — and the five `change` values sum to `anchor_recorded_count` minus " +
|
|
550
|
+
"`baseline_recorded_count`. The layers are DECLARED only, never inferred from the path, and " +
|
|
551
|
+
"`undeclared` is NOT `unreadable`; the operands carry no pyramid verdict, so reading the shift is " +
|
|
552
|
+
"yours. The same " +
|
|
553
|
+
"`layer_counts` rides each row of `spec_directories`, the `spec_directory_files` area block " +
|
|
554
|
+
"and each of its file rows, and every per-example row of `slowest_examples`, " +
|
|
555
|
+
"`spec_file_examples` and `repeated_description_examples` carries `intent_layer` and " +
|
|
556
|
+
"`declared_intent` (`{entity, action, behavior}` or `null`, declared only). " +
|
|
514
557
|
"TWO BLOCKS COME BACK ON EVERY RESPONSE — no parameter to pass, no flag to set — and they " +
|
|
515
558
|
"answer what everything above silently depends on: is SpecGuard still being fed? " +
|
|
516
559
|
"`delivery_health` is why the figures may be STALE: `refusing` is a comparison of stamps, not a " +
|
|
@@ -651,7 +694,8 @@ const getRepositoryOverview = {
|
|
|
651
694
|
"`unstable_tests` (which tests failed intermittently across the window rather than " +
|
|
652
695
|
"consistently) and `directory_growth` (how each area moved between the two ENDPOINTS of " +
|
|
653
696
|
"that window). The per-area comparisons against the PREVIOUS RUN — `directory_run_growth` " +
|
|
654
|
-
"and `directory_runtime_growth
|
|
697
|
+
"and `directory_runtime_growth`, and the declared-layer mix pair `layer_run_growth` / " +
|
|
698
|
+
"`layer_run_growth_window` — need no branch and take none; they scope to the latest " +
|
|
655
699
|
"run's own branch by construction, so a plain call already carries them.",
|
|
656
700
|
},
|
|
657
701
|
spec_directory: {
|
|
@@ -660,7 +704,9 @@ const getRepositoryOverview = {
|
|
|
660
704
|
"went by directory but not which files inside it spent it. Use a path exactly as served " +
|
|
661
705
|
"in `latest_run.spec_directories.rows[].path`. Asking populates " +
|
|
662
706
|
"`latest_run.spec_directory_files` — the spec files in that one directory with their " +
|
|
663
|
-
"`total_seconds`/`recorded_count`/`timed_count
|
|
707
|
+
"`total_seconds`/`recorded_count`/`timed_count` and a `layer_counts` split of the declared " +
|
|
708
|
+
"layers (the same five operands as `latest_run.layer_counts`), plus the AREA's own " +
|
|
709
|
+
"`layer_counts`, `file_count`, " +
|
|
664
710
|
"`recorded_count`, `timed_count` and the `limit` the row list was cut at (the totals " +
|
|
665
711
|
"describe the whole area, not the returned page, so do not re-derive them from `rows`). " +
|
|
666
712
|
"The one ask opens THREE blocks, each in its own grain: `spec_directory_files` for which " +
|
|
@@ -687,7 +733,8 @@ const getRepositoryOverview = {
|
|
|
687
733
|
"`latest_run.spec_files.rows[].path`. Asking populates " +
|
|
688
734
|
"`latest_run.spec_file_examples` — up to 50 of that file's individual examples cut by " +
|
|
689
735
|
"DURATION, each with `name`, `file_path`, `line_number`, `spec_file_path`, " +
|
|
690
|
-
"`duration_seconds
|
|
736
|
+
"`duration_seconds`, `outcome`, `intent_layer` and `declared_intent` (EIGHT fields), " +
|
|
737
|
+
"plus the FILE's own `recorded_count` and " +
|
|
691
738
|
"`timed_count` and the `limit` the row list was cut at (the totals describe the whole " +
|
|
692
739
|
"file, not the returned page, so do not re-derive them from `rows`). " +
|
|
693
740
|
"SENT TOGETHER WITH `unannotated_examples`, it also narrows THAT worklist to this file — " +
|
|
@@ -704,8 +751,8 @@ const getRepositoryOverview = {
|
|
|
704
751
|
"the same thing. Use a description exactly as served in " +
|
|
705
752
|
"`latest_run.repeated_descriptions.rows[].name`. Asking populates " +
|
|
706
753
|
"`latest_run.repeated_description_examples` — up to 25 of that group's members, each " +
|
|
707
|
-
"with `name`, `file_path`, `line_number`, `spec_file_path`, `duration_seconds
|
|
708
|
-
"`outcome`, plus the GROUP's own `recorded_count` and `timed_count` and the `limit` " +
|
|
754
|
+
"with `name`, `file_path`, `line_number`, `spec_file_path`, `duration_seconds`, " +
|
|
755
|
+
"`outcome`, `intent_layer` and `declared_intent` (EIGHT fields), plus the GROUP's own `recorded_count` and `timed_count` and the `limit` " +
|
|
709
756
|
"the row list was cut at (the totals describe the whole group, not the returned page, " +
|
|
710
757
|
"so do not re-derive them from `rows`). This is the ONLY way to reach a group's " +
|
|
711
758
|
"members: `slowest_examples` is the run-wide top ten and rarely contains them, and " +
|
|
@@ -742,6 +789,8 @@ const getRepositoryOverview = {
|
|
|
742
789
|
"`commit_sha`/`test_run_id`, never off its index: a run that recorded nothing under " +
|
|
743
790
|
"the description contributes no row and a description carried by two examples in one " +
|
|
744
791
|
"run contributes two, so `rows` is not one entry per run. " +
|
|
792
|
+
"These drill-in rows do NOT carry a layer: read `declared_layers` off the ranking row " +
|
|
793
|
+
"(`unstable_tests.rows[]`), not here. " +
|
|
745
794
|
"`branch` IS REQUIRED WITH IT, unlike every other argument here: `unstable_tests` is " +
|
|
746
795
|
"served only for a branch-narrowed window, so this parameter sent alone leaves the " +
|
|
747
796
|
"whole containing block `null` and there is nothing to drill into — not an empty " +
|
|
@@ -754,15 +803,15 @@ const getRepositoryOverview = {
|
|
|
754
803
|
commit_sha: {
|
|
755
804
|
type: "string",
|
|
756
805
|
description: "Anchor the whole answer on ONE run, naming it by commit sha. This is a DIFFERENT KIND " +
|
|
757
|
-
"OF ASK from every other argument here: the
|
|
806
|
+
"OF ASK from every other argument here: the drill-in arguments narrow what is served ABOUT a run " +
|
|
758
807
|
"that was already chosen for you, and this one CHOOSES THAT RUN. `branch` asks about a " +
|
|
759
808
|
"SERIES; this asks WHICH RUN. Use a sha exactly as served in " +
|
|
760
809
|
"`latest_run.commit_sha`, `history[].commit_sha` or " +
|
|
761
810
|
"`unstable_tests.unstable_test_runs.rows[].commit_sha`. " +
|
|
762
811
|
"Everything at run grain re-anchors together: `latest_run` and its five rollups, the " +
|
|
763
|
-
"
|
|
764
|
-
"`repeated_description_examples` and `
|
|
765
|
-
"growth windows and `previous_test_run`. " +
|
|
812
|
+
"five RUN-GRAIN drill-ins (`spec_directory_files`, `spec_file_examples`, " +
|
|
813
|
+
"`repeated_description_examples`, `unannotated_examples` and `slowest_examples` — the " +
|
|
814
|
+
"block `layer` narrows), `shards`, both per-area growth windows and `previous_test_run`. " +
|
|
766
815
|
"`unstable_test_runs` is the one drill-in that does NOT move with " +
|
|
767
816
|
"it: it is read over the branch window rather than off the anchored run, so sending " +
|
|
768
817
|
"this with `unstable_test` still gives you that test across the whole window. " +
|
|
@@ -823,8 +872,9 @@ const getRepositoryOverview = {
|
|
|
823
872
|
"one is additional, not instead. " +
|
|
824
873
|
"Asking populates `latest_run.unannotated_examples` — up to 100 of the unannotated " +
|
|
825
874
|
"examples OF WHATEVER YOU ASKED FOR, each with `name`, `file_path`, `line_number` and " +
|
|
826
|
-
"`spec_file_path`, `reading` and `derived_intent` (SIX fields: no `duration_seconds
|
|
827
|
-
"no `
|
|
875
|
+
"`spec_file_path`, `reading` and `derived_intent` (SIX fields: no `duration_seconds`, no " +
|
|
876
|
+
"`outcome` and no `intent_layer`, unlike the per-example drill-ins above, which carry " +
|
|
877
|
+
"EIGHT), plus that same population's own " +
|
|
828
878
|
"`recorded_count`, `derived_count` and `unreadable_count`, the " +
|
|
829
879
|
"`limit` the row list was cut at, and `spec_file`/`spec_directory` ECHOED BACK as the " +
|
|
830
880
|
"server READ them — `null` for each one you did not send. " +
|
|
@@ -888,6 +938,26 @@ const getRepositoryOverview = {
|
|
|
888
938
|
"same as omitting it and sends nothing at all: declining is not sending, which is how " +
|
|
889
939
|
"every other argument here is declined too — none of them has an \"off\" value.",
|
|
890
940
|
},
|
|
941
|
+
layer: {
|
|
942
|
+
type: "string",
|
|
943
|
+
description: "Narrow ONE block to one DECLARED test layer: `latest_run.slowest_examples` — the " +
|
|
944
|
+
"run-wide slowest-examples ranking — and NOTHING else. It does NOT narrow " +
|
|
945
|
+
"`spec_file_examples`, `spec_directory_files`, `unannotated_examples` or " +
|
|
946
|
+
"`repeated_description_examples`. Use it after `latest_run.layer_durations` shows one " +
|
|
947
|
+
"layer holding most of the machine time (say `request`) and you need to know WHICH " +
|
|
948
|
+
"tests. Values: `unit`, `integration`, `request`, `system` or `undeclared` (a test that " +
|
|
949
|
+
"declared no layer). Any other value is read by the server as NO ASK — never a 400 or " +
|
|
950
|
+
"404 — so it is forwarded unvalidated and a typo silently returns the un-narrowed " +
|
|
951
|
+
"ranking: check that `slowest_examples.layer` is echoed back. That echo is present ONLY " +
|
|
952
|
+
"when a layer was asked (the key is ABSENT otherwise). With a layer asked, " +
|
|
953
|
+
"`recorded_count`, `timed_count` and `reported_outcome_count` are that LAYER's own " +
|
|
954
|
+
"counts (`recorded_count` equals `layer_counts[layer]`, `timed_count` equals " +
|
|
955
|
+
"`layer_durations[layer].timed_count`), not the run's. A layer with no examples answers " +
|
|
956
|
+
"`rows: []` and `recorded_count: 0`, while `slowest_examples: null` still means the run " +
|
|
957
|
+
"recorded nothing at all. Layers are DECLARED only (an `@intent` annotation said so), " +
|
|
958
|
+
"never inferred from the file path. It is at RUN GRAIN, so it moves with `commit_sha`. " +
|
|
959
|
+
"A blank value sends nothing.",
|
|
960
|
+
},
|
|
891
961
|
},
|
|
892
962
|
additionalProperties: false,
|
|
893
963
|
},
|
|
@@ -902,6 +972,10 @@ const getRepositoryOverview = {
|
|
|
902
972
|
const repeatedDescription = optionalString(args["repeated_description"], "repeated_description");
|
|
903
973
|
const unstableTest = optionalString(args["unstable_test"], "unstable_test");
|
|
904
974
|
const commitSha = optionalString(args["commit_sha"], "commit_sha");
|
|
975
|
+
// Forwarded UNVALIDATED, deliberately: the server reads any value outside
|
|
976
|
+
// the five declared layers as no ask (never a 400/404), so an enum check
|
|
977
|
+
// here would only add a second, divergent opinion about what is valid.
|
|
978
|
+
const layer = optionalString(args["layer"], "layer");
|
|
905
979
|
// A BOOLEAN, and deliberately not stringified below. The server reads only
|
|
906
980
|
// whether the key is PRESENT — `?unannotated_examples=false` opens the block
|
|
907
981
|
// exactly as `=true` does — and `getJson` omits only `undefined`, so sending
|
|
@@ -926,6 +1000,7 @@ const getRepositoryOverview = {
|
|
|
926
1000
|
repeated_description: repeatedDescription,
|
|
927
1001
|
unstable_test: unstableTest,
|
|
928
1002
|
commit_sha: commitSha,
|
|
1003
|
+
layer,
|
|
929
1004
|
unannotated_examples: unannotatedExamples === true ? "true" : undefined,
|
|
930
1005
|
}, context.fetch);
|
|
931
1006
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repository-overview.js","sourceRoot":"","sources":["../../../src/tools/repository-overview.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAG5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmdG;AACH,MAAM,qBAAqB,GAAmB;IAC5C,IAAI,EAAE,yBAAyB;IAE/B,KAAK,EAAE,mCAAmC;IAE1C,WAAW,EACT,2FAA2F;QAC3F,6FAA6F;QAC7F,yFAAyF;QACzF,+FAA+F;QAC/F,8FAA8F;QAC9F,gEAAgE;QAChE,gGAAgG;QAChG,wFAAwF;QACxF,6FAA6F;QAC7F,
|
|
1
|
+
{"version":3,"file":"repository-overview.js","sourceRoot":"","sources":["../../../src/tools/repository-overview.ts"],"names":[],"mappings":"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"}
|