From 7cebc765ce30410dc57a4545bc350cb84d423267 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 17:01:19 +0000 Subject: [PATCH 1/2] adopt(prometheus-mantine-ui): hunt harness, patch and drafts (work in progress) The race hunt on the Prometheus web UI (web/ui/mantine-ui): the harness, the first sessions' results, the duplicate-request fix and its test as a patch, and the pull request draft. The report's results sections are filled in by the next commit, when the remaining sessions have run. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Fqwfq2rZJASoQ4owhyDZp2 --- docs/adoption/hunts/README.md | 1 + .../hunts/prometheus-mantine-ui-issue.md | 123 ++++ docs/adoption/hunts/prometheus-mantine-ui.md | 226 +++++++ .../hunts/prometheus-mantine-ui.patch | 83 +++ .../hunts/prometheus-mantine-ui/hunt.mjs | 440 +++++++++++++ .../prometheus-mantine-ui/prometheus.yml | 25 + .../hunts/prometheus-mantine-ui/requests.mjs | 43 ++ .../results/table-slow300-seed1.json | 351 +++++++++++ .../results/tree-plain-delay900.json | 508 +++++++++++++++ .../results/tree-slow300-seed1.json | 578 ++++++++++++++++++ .../results/tree-slow600-seed1.json | 571 +++++++++++++++++ .../results/tree-slow900-seed1.json | 566 +++++++++++++++++ .../hunts/prometheus-mantine-ui/rules.yml | 15 + .../hunts/prometheus-mantine-ui/run-all.sh | 9 + .../summaries/requests-fixed.json | 114 ++++ .../summaries/requests-unfixed.json | 154 +++++ .../summaries/sessions.json | 44 ++ .../summaries/tree-nosse.json | 33 + .../hunts/prometheus-mantine-ui/summarise.mjs | 57 ++ 19 files changed, 3941 insertions(+) create mode 100644 docs/adoption/hunts/prometheus-mantine-ui-issue.md create mode 100644 docs/adoption/hunts/prometheus-mantine-ui.md create mode 100644 docs/adoption/hunts/prometheus-mantine-ui.patch create mode 100644 docs/adoption/hunts/prometheus-mantine-ui/hunt.mjs create mode 100644 docs/adoption/hunts/prometheus-mantine-ui/prometheus.yml create mode 100644 docs/adoption/hunts/prometheus-mantine-ui/requests.mjs create mode 100644 docs/adoption/hunts/prometheus-mantine-ui/results/table-slow300-seed1.json create mode 100644 docs/adoption/hunts/prometheus-mantine-ui/results/tree-plain-delay900.json create mode 100644 docs/adoption/hunts/prometheus-mantine-ui/results/tree-slow300-seed1.json create mode 100644 docs/adoption/hunts/prometheus-mantine-ui/results/tree-slow600-seed1.json create mode 100644 docs/adoption/hunts/prometheus-mantine-ui/results/tree-slow900-seed1.json create mode 100644 docs/adoption/hunts/prometheus-mantine-ui/rules.yml create mode 100755 docs/adoption/hunts/prometheus-mantine-ui/run-all.sh create mode 100644 docs/adoption/hunts/prometheus-mantine-ui/summaries/requests-fixed.json create mode 100644 docs/adoption/hunts/prometheus-mantine-ui/summaries/requests-unfixed.json create mode 100644 docs/adoption/hunts/prometheus-mantine-ui/summaries/sessions.json create mode 100644 docs/adoption/hunts/prometheus-mantine-ui/summaries/tree-nosse.json create mode 100644 docs/adoption/hunts/prometheus-mantine-ui/summarise.mjs diff --git a/docs/adoption/hunts/README.md b/docs/adoption/hunts/README.md index 12145187..02a476b8 100644 --- a/docs/adoption/hunts/README.md +++ b/docs/adoption/hunts/README.md @@ -7,6 +7,7 @@ the project's maintainers to take or leave. One report per target; nothing here | target | report | patch and draft text | harness | |---|---|---|---| | Strapi admin panel (5.59.0) | [strapi.md](strapi.md) | [strapi.patch](strapi.patch), [strapi-issue.md](strapi-issue.md) | [strapi/](strapi/) | +| Prometheus web UI, mantine-ui (main 4a933f8): no race found; a non-race duplicate-request fix, needs owner decision | [prometheus-mantine-ui.md](prometheus-mantine-ui.md) | [prometheus-mantine-ui.patch](prometheus-mantine-ui.patch), [prometheus-mantine-ui-issue.md](prometheus-mantine-ui-issue.md) | [prometheus-mantine-ui/](prometheus-mantine-ui/) | Every report gives the versions and commits, the exact commands, every scenario with its verdict including the negatives, the finding with its evidence in causal order, the root cause, the fix, the test result, the check on the diff --git a/docs/adoption/hunts/prometheus-mantine-ui-issue.md b/docs/adoption/hunts/prometheus-mantine-ui-issue.md new file mode 100644 index 00000000..f152764e --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui-issue.md @@ -0,0 +1,123 @@ +# Prometheus web UI (mantine-ui): drafts for the sender + +Prepared 2026-11-03 by a GenClass adoption-swarm session. Nothing here has been sent. Report: +[prometheus-mantine-ui.md](prometheus-mantine-ui.md). Patch: [prometheus-mantine-ui.patch](prometheus-mantine-ui.patch) +(against prometheus/prometheus `4a933f89fa96c56ad69e7c02dc5f40e06d93440e`, fix and test together, 59 changed lines). + +**Status: needs owner decision.** The race hunt found no race (report, sections 5 to 8). What is drafted below is +a small non-race bug the hunt ran into: the query tree view sends each leaf node's request twice and aborts the +first. It is real, measured and fixed with a test, but GenClass did not find it as a race (explore listed it only as +a request count that differs, under "internal state only"; the browser's request log showed it). Sending it is a +plain bug-fix contribution, not a GenClass demonstration. The owner decides whether it goes out. + +Prometheus wants no issue first for a fix this small (CONTRIBUTING.md: "If you have a trivial fix or improvement, go +ahead and create a pull request, addressing (with `@...`) a suitable maintainer of this repository"). So there is +one draft, the pull request, in the repository's template (`.github/PULL_REQUEST_TEMPLATE.md`). An issue draft in +`bug_report.yml`'s fields follows for the case a maintainer asks for one. + +## Pull request + +**Title:** `ui: send each query tree node's request once` + +**Branch and commit:** from `main`, one commit, signed off: `git commit -s -m "ui: send each query tree node's request once"` +(DCO; the sign-off is the sender's own name and email). + +**Body** (paste from the line below; `@juliusv` is the web/ui owner in CODEOWNERS and MAINTAINERS.md, the address +CONTRIBUTING.md asks for): + +--- + +@juliusv + +The query tree view sends each leaf node's `/api/v1/query` request twice and aborts the first one. + +`TreeNode` keys its query on `useId()` from `@mantine/hooks`. That hook returns an id derived from React's `useId` +on the first render and replaces it with a random one in a layout effect after mount, so the node's query key changes +right after it mounts. A leaf node's query is enabled at once (it has no children to wait for), so TanStack Query +has already started the request under the first key; the key change starts a second request and aborts the first. +Inner nodes are enabled only after their children answered, by which time the id is stable, so they send once. + +Measured on a production build of `main` (4a933f8) served by `vite preview` against a local Prometheus 3.15.1, opening +`/query?g0.expr=sum by (job) (up) + count by (job) (go_goroutines)&g0.show_tree=1` 8 times in Chromium and counting the +tree's `/api/v1/query` requests: 7 requests for the 5 nodes in each of the 8 loads, `up` and `go_goroutines` sent twice +with the first aborted (16 aborted in all). With this change: 5 requests per load, none aborted, 8 of 8 loads. + +The fix takes `useId` from React instead, which is stable from the first render (`TableTab.tsx` and `Graph.tsx` +already use it for their query keys). The node id used for selecting a node comes from the same hook, so by reading +the code the root node is now also selected once instead of twice on mount (the Explain tab's error boundary is keyed +on that id). + +The new `TreeNode.test.tsx` renders a leaf node and asserts it sends one request that is not aborted. On `main` it +fails (two requests, the first aborted); with the change it passes (three runs each). `CI=true pnpm run test` and +`pnpm run lint` in `web/ui` pass. + +This is independent of #20279 (open), which adds the query and the evaluation time to the same key but keeps the +Mantine hook: the change applies on top of it unchanged, and there the test needs `endTime={null}`. + +I used Claude (Claude Code) to investigate this, write the fix and the test, and draft this description; I reviewed +the change and ran the tests and the manual check myself. I came across it while checking this UI for races with +GenClass, a tool I work on, which replays the same steps with the server's responses delivered in different orders; +the duplicate request showed up in the browser's request log, not as a race. + +#### Which issue(s) does the PR fix: + +None filed; a trivial fix, sent straight as a pull request as CONTRIBUTING.md suggests. + +#### Release notes for end users (**ALL** commits must be considered). +*Reviewers should verify clarity and quality.* + +```release-notes +[BUGFIX] UI: Send each leaf node's query in the query tree view once instead of twice. +``` + +--- + +## Issue (only if a maintainer asks for one) + +Template: `.github/ISSUE_TEMPLATE/bug_report.yml`. Title: `UI: query tree view sends each leaf node's query twice`. + +**What did you do?** +Opened the query page with the tree view shown, for example +`http://localhost:9090/query?g0.expr=sum%20by%20(job)%20(up)%20%2B%20count%20by%20(job)%20(go_goroutines)&g0.show_tree=1`, +with the browser's network panel open. + +**What did you expect to see?** +One `/api/v1/query` request per tree node (5 for this expression). + +**What did you see instead? Under which circumstances?** +7 requests: the leaf nodes' queries (`up`, `go_goroutines`) are each sent twice and the first of each is cancelled. +Every time the tree renders, in a production build (8 of 8 loads). `TreeNode` keys the query on `useId()` from +`@mantine/hooks`, whose value changes in a layout effect right after mount, so the query key changes once and the +first request is aborted. + +**System information:** `Linux 6.18.44 x86_64` (the sender fills in their own) + +**Prometheus version:** UI built from `main` at 4a933f8 (`@prometheus-io/mantine-ui` 0.315.1), server 3.15.1 +release binary (the sender replaces this with `prometheus --version` of the build they reproduce on). + +**Prometheus configuration file:** the shipped `documentation/examples/prometheus.yml` is enough. + +I used Claude (Claude Code) to investigate this and draft this report; I reproduced it myself. (Same last sentences as +the pull request's disclosure if it is filed.) + +## Before sending (the sender) + +- [ ] The owner said yes to sending a non-race fix (the status above). +- [ ] Search the tracker again for duplicates since 2026-11-03: open and closed issues and pull requests for + `TreeNode`, `useId`, `tree view` + `request`/`twice`/`duplicate`/`abort`. On 2026-11-03 the search API found none; + the related open #20279 changes the same key without this fix (see the body). +- [ ] If #20279 has merged: rebase, check the patch still applies (it did on 2026-11-03), and add `endTime={null}` to + the test's ``. +- [ ] Reproduce by hand: build the UI (`cd web/ui && pnpm install && pnpm run build:module && cd mantine-ui && npx vite build && npx vite preview`) + with a local Prometheus on :9090, open the URL above with the network panel open, see 7 `query` requests with + two cancelled; apply the patch, rebuild, see 5. Or run `docs/adoption/hunts/prometheus-mantine-ui/requests.mjs` + from this repository against each build. +- [ ] Run the test both ways yourself: `pnpm --filter @prometheus-io/mantine-ui exec vitest run src/pages/query/TreeNode.test.tsx` + fails on `main` and passes with the patch; then `CI=true pnpm run test` and `pnpm run lint` in `web/ui`. +- [ ] Read the diff (two lines in `TreeNode.tsx`, the new test); make sure every sentence of the body is true for you, + in particular the disclosure paragraph. +- [ ] Commit with `git commit -s`, push to your fork, open the pull request with the title and body above. +- [ ] A first-time contributor's CI waits for a maintainer's `/workflow-approve`; do not ping for it. +- [ ] If asked whether this was found with GenClass: no, the race hunt found nothing; this came from the request log + of the same session. If the maintainers prefer a different fix (for example adding the query to the key, as + #20279 does, which alone does not stop the duplicate), follow them. diff --git a/docs/adoption/hunts/prometheus-mantine-ui.md b/docs/adoption/hunts/prometheus-mantine-ui.md new file mode 100644 index 00000000..a56a09b8 --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui.md @@ -0,0 +1,226 @@ +# Race hunt: Prometheus web UI, mantine-ui (prometheus/prometheus) + +A GenClass race hunt against the Prometheus 3.x web UI (`web/ui/mantine-ui`), run on 2026-11-03 in a cloud session of +the adoption swarm (brief: `docs/adoption/swarm-targets/prometheus-mantine-ui.md` on branch `adopt/briefs`). Everything +was done on a local copy; nothing was sent to the Prometheus project. + +**Headline: no race found.** Seven scenarios (the query tree view under an expression change, two table executions +back to back, a graph execution followed by a range change, two "Format expression" calls with an edit between them, +the Targets and Service discovery pool select switched twice, a double-click on the TSDB status page's "Delete +series"), each run as an explore session of 8 page loads (2 request-order baselines and 6 seeded reorderings) at 300, +600 and 900 ms of extra latency, and again 8 times without GenClass with the superseded request held 900 ms: RESULTS_HEADLINE +GenClass printed no RACE line in any session; the lines it did print are request counts that depend on timing, listed +as "internal state only" (section 6). Section 7 quotes the code that makes the order not matter. + +**One non-race defect, with a ready fix.** The tree view sends each leaf node's `/api/v1/query` twice and aborts the +first (7 requests for 5 nodes in 8 of 8 loads; 5 and none aborted with the fix, 8 of 8). The cause is the query key +`[useId()]` with `useId` from `@mantine/hooks`, whose value changes right after mount. The fix (React's own `useId`, +one import line) and a Vitest test that fails on `main` and passes with it are in +[prometheus-mantine-ui.patch](prometheus-mantine-ui.patch); the pull request draft is in +[prometheus-mantine-ui-issue.md](prometheus-mantine-ui-issue.md). GenClass did not find this as a race: explore +listed it as a GET count that differs, and the browser's request log showed what it was. **Status: needs owner +decision** (send a small plain bug fix that does not demonstrate GenClass, or send nothing). + +## 1. Versions and commits + +| what | version | +|---|---| +| prometheus/prometheus | `main` at `4a933f89fa96c56ad69e7c02dc5f40e06d93440e` (2026-11-03 16:21 UTC), cloned `--depth 50` to `~/targets/prometheus-mantine-ui` | +| UI | `web/ui/mantine-ui` (`@prometheus-io/mantine-ui` 0.315.1): React 19.3.1, Mantine 9.7.3, TanStack Query 5, Vite 8.3.4, Vitest 5 | +| server | Prometheus 3.15.1 release binary (`prometheus-3.15.1.linux-amd64.tar.gz`, revision bae3ece) | +| Node, pnpm | Node 22.23.4 (the repository's `web/ui/.nvmrc` pins 24.21.0; `scripts/check-node-version.sh` only warns, and Vite 8 and Vitest 5 accept 22.12+), pnpm 10.28.0 | +| browser | Playwright's Chromium 153.0.8010.12, headless | +| GenClass | this repository at `c1c9e14`: `packages/runtime` 0.3.0-beta.5 (`dist/` built here), `packages/mcp` 0.2.1 (`serve()` in-process) | +| related upstream | open PR #20279 "ui: respect evaluation time in Explain and query tree" (2026-11-02), which changes the same query key; read at its head `126004d` | + +## 2. Running the UI + +```bash +git clone --depth 50 https://github.com/prometheus/prometheus.git ~/targets/prometheus-mantine-ui +cd ~/targets/prometheus-mantine-ui/web/ui +pnpm install --frozen-lockfile # 9.5 s +pnpm run build:module # lezer-promql and codemirror-promql, imported by mantine-ui +# the server, without Go: +mkdir -p ~/targets/prom && cd ~/targets/prom +curl -sSLO https://github.com/prometheus/prometheus/releases/download/v3.15.1/prometheus-3.15.1.linux-amd64.tar.gz +tar xzf prometheus-3.15.1.linux-amd64.tar.gz && cd prometheus-3.15.1.linux-amd64 +./prometheus --config.file=/docs/adoption/hunts/prometheus-mantine-ui/prometheus.yml \ + --web.enable-admin-api --storage.tsdb.path=$HOME/targets/prom/data +# the UI under test: a production build served by vite preview (its proxy sends /api and /-/ to :9090) +cd ~/targets/prometheus-mantine-ui/web/ui/mantine-ui && npx vite build && npx vite preview --port 4173 --strictPort +``` + +Setup took about 15 minutes. The config in [prometheus-mantine-ui/prometheus.yml](prometheus-mantine-ui/prometheus.yml) +scrapes the server itself under four jobs (`prometheus`, `self-a`, `self-b`, `down-only`, with five unreachable +targets), so the Targets and Service discovery pool select has pools to switch between, and loads +[rules.yml](prometheus-mantine-ui/rules.yml) (three alerts, one recording rule). No database, no seed step, no login. + +**Why a production build.** The dev server (`pnpm start`, port 5173) renders under `React.StrictMode` +(`src/main.tsx` L10), which mounts every component twice in development: on the dev server the tree's leaf queries +went out three times each and the table's twice, with the extra ones aborted (measured with the plain request log of +section 8). A hunt there would measure React's development checks. Every number below is from `vite preview` of +`vite build`, where StrictMode does nothing. + +## 3. Attaching GenClass without touching Prometheus + +[prometheus-mantine-ui/hunt.mjs](prometheus-mantine-ui/hunt.mjs) follows the Gitea and Ghost harnesses: it starts +`@genclass/mcp`'s `serve()` in-process and talks to it over JSON-RPC `tools/call`; in Playwright it routes the virtual +same-origin path `/__genclass/*` to `packages/runtime/dist/`, sets `window.GENCLASS_CONFIG = { telemetry: false, mode: +"observe", model: false, devtools: false, visual: false, analytics: false, report: "silent", autoState: false }` and the +bridge URL from an init script, and rewrites each HTML document of the app so its `` starts with the script-tag +build, the mirror atom below and `connectAgent(window.GenClass.runtime, { url })`. Each load is a fresh browser context +with the `local-network-access` permission. React state discovery is off (`autoState: false`), for the reasons the +Ghost report gives (section 8 there). + +**The mirror atom.** The UI exposes neither its Redux store nor its TanStack Query client on `window`, so the +injected script registers `rt.atom("prom", ...)` and keeps it current from DOM mutations (coalesced per frame) with +only what a reordering could corrupt and what does not move with the clock: the tree view's nodes (operator text, +result count, label cardinalities; not the per-node response time), the table's series labels and "Result series" +count (not the sample values, which change every scrape, nor the load time), the graph legend's series and the range +field, the expression input's text, the Targets/Service discovery pools with their "n / m up" counts, and the delete +form's alerts. That is the state explore compares, next to the page text, form values and request counts it always +compares. + +**Oracles, per load** (in every result file, `prometheus-mantine-ui/results/*.json`): + +1. *explore*: GenClass's verdict as `genclass_explore report` printed it, nothing removed; +2. *reference*: after `whenSettled`, the harness reads the same regions itself and compares them with a **reference + load**: a fresh page without GenClass opened directly at the scenario's final URL (for example + `/query?g0.expr=&g0.show_tree=1`) right after; for "format" the reference is the server's own + `/api/v1/format_query` of the final draft, for "delete" one `POST` and the success alert. + +**Without GenClass** (`--plain`): the same steps, no injection, and a Playwright route that holds the superseded +request(s) of the scenario 900 ms (the first expression's node queries, the first table query, the first range +query, the first format call, the first pool's `/targets`, the delete `POST`), so the newer response arrives first. +Same reference oracle, 8 loads per scenario. + +## 4. Scenarios + +Each is one page load per run, the same steps every time, at Playwright's pace (CodeMirror is filled with +select-all, Backspace and `insertText`). + +| id | scenario | steps | what overlaps on the wire | +|---|---|---|---| +| tree | tree view under an expression change | open `/query` with `rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info` and the tree shown; as soon as the root node renders, replace the expression with `sum by (job) (up) + count by (job) (go_goroutines)` and Execute | the first tree's per-node `/query` requests (five nodes, leaves first), the first table query, `/parse_query` for the second expression, the second tree's node queries and table query | +| table | two executions back to back | open `/query` with `up` on the Table tab; Execute `count by (job) (up)`, then at once `sum by (job) (up == 0)` | three `/query?...&time=...&stats=true` | +| graph | execution then range change | open `/query` with `up` on the Graph tab (range 1h); Execute `count by (job) (up)`, then click "Decrease range" (30m) | three `/query_range` (old expression, new expression at 1h, at 30m) | +| format | format, edit, format | open `/query`; type `sum by(job)(up)`, "Format expression" from the query options menu, replace the draft with `count by( instance )( go_goroutines )`, format again | two `/format_query` | +| targets | pool select twice | open `/targets`; pick `self-a`, then `self-b` | `/targets?scrapePool=self-a`, `...=self-b` | +| sd | pool select twice | the same on `/service-discovery` | the same two | +| delete | double-click | open `/tsdb-status`, type `go_gc_duration_seconds{job="self-a"}` in "Series selector(s)", double-click "Delete series" | `POST /api/v1/admin/tsdb/delete_series` (one or two) | + +The brief's other surfaces were read, not run, and section 7 says why each cannot race on the wire: the Alerts and +Rules pages filter on the client from one request; the notifications provider; the metrics explorer and its labels +view; the PromQL autocomplete. + +## 5. Results + +RESULTS_TABLE + +"forced holds" counts responses the schedule released only because their hold ran out (summed over the session's 8 +loads); every response was still delivered unchanged. Section 9 explains why there are so many. + +## 6. What explore reported, and what it was + +RESULTS_EXPLORE + +## 7. Why the UI did not race + +- **Tree view.** Every Execute remounts the whole tree: `QueryPanel.tsx` L123 wraps it in + `` and the Execute handler increments `retriggerIdx` (L101). An expression + changed by the URL (back/forward, `QueryPage.tsx` L29-38) replaces the panels with new ones from + `newDefaultPanel()`, whose `id` is a fresh `randomId()` (`queryPageSlice.ts` L79), and `` + (`QueryPage.tsx` L119-120) remounts them. So no `TreeNode` instance survives an expression change, and the brief's + open lead (the per-node key `[useId()]` omits the node's query, `TreeNode.tsx` L129-136) cannot show one node's + answer under another expression. The tree scenario's 24 explore loads and 8 plain loads agree. The lead does + matter once the key's identity is relied on across renders, which is what open PR #20279 starts doing (it adds the + query and the evaluation time to the key, so a node re-queries when the evaluation time changes without + remounting); the duplicate request of section 8 comes from the same line. +- **Table and graph.** Each execution is its own TanStack Query cache entry (`TableTab.tsx` L35-54 keys on + `[useId(), "/query", expr, endTime, retriggerIdx]`; `Graph.tsx` L71-103 on the expression, end, range and step), + a component observes only its current key, and the query function passes TanStack's `signal` to `fetch` + (`api/api.ts` L64-71), so a superseded request is aborted and its answer, if it still arrives, lands in a cache + entry nobody shows. `QueryLifecycle.test.tsx` L181 already asserts the abort. The graph's `keepPreviousData` shows + the previous key's data only until the new one answers. +- **Format.** `ExpressionInput.tsx` L115-160: `handleFormat` takes a generation number before `refetch` and drops the + answer when the draft (`updateDraft`) or the executed expression changed meanwhile. +- **Targets and Service discovery.** The pool's targets come from `useSuspenseAPIQuery` keyed on `[path, params]` + with the pool in the params (`ScrapePoolsList.tsx` L144, `ServiceDiscoveryPoolsList.tsx` L182): one cache entry per + pool, and the component suspends until the current pool's entry has data. +- **Delete series.** `TSDBStatusPage.tsx` L82-134 sets `deleting` before its `fetch`, and the button has + `loading={deleting}` (L336), which Mantine renders disabled. React re-renders synchronously after a click (a + discrete event), so the second click of a double-click lands on a disabled button: one `POST` in every load. The + server's delete is idempotent anyway. +- **Read, not run.** Alerts and Rules: one suspense query each, filters and search applied in the browser (no request + to reorder). `NotificationsProvider.tsx`: one SSE stream (`fetchEventSource`, messages handled in order) and a + polling fallback that is one TanStack query (an interval refetch never overlaps its own in-flight request). + `LabelsExplorer.tsx` L103: `/series` keyed by the matchers, same reasoning as the table. The PromQL autocomplete + (`web/ui/module/codemirror-promql`) caches and aborts its own requests, and CodeMirror drops completions whose + context no longer applies; not exercised here (time-boxed). + +## 8. One defect that is not a race: the tree view sends each leaf's query twice + +**What.** Opening the query page with the tree view shown sends each leaf node's `/api/v1/query` twice; the first +request of each is aborted. [prometheus-mantine-ui/requests.mjs](prometheus-mantine-ui/requests.mjs) (plain +Playwright, nothing injected or delayed) opens +`/query?g0.expr=sum by (job) (up) + count by (job) (go_goroutines)&g0.show_tree=1` 8 times and counts the tree's +requests ([summaries/requests-unfixed.json](prometheus-mantine-ui/summaries/requests-unfixed.json), +[requests-fixed.json](prometheus-mantine-ui/summaries/requests-fixed.json)): + +| build | loads | tree nodes | tree requests per load | aborted, all loads | +|---|---|---|---|---| +| `main` (4a933f8), `vite preview` | 8 | 5 | 7 (in 8 of 8 loads) | 16: `up` and `go_goroutines`, once per load each | +| with the patch, `vite preview` | 8 | 5 | 5 (in 8 of 8 loads) | 0 | + +**Root cause.** `TreeNode.tsx` L129-137 calls `useAPIQuery({ key: [useId()], path: "/query", ... })` with `useId` +imported from `@mantine/hooks` (L33). Mantine 9.7.3's `useId` (`@mantine/hooks/esm/use-id/use-id.mjs`) starts with +`mantine-${React.useId()}` and calls `setUuid(randomId())` in a layout effect after mount. A leaf node's query is +enabled at once (`mergedChildState` is `"success"` with no children), so the observer subscribes and fetches under +the first key, then the re-render switches it to the random key: a second fetch, and TanStack aborts the first +because its query function consumed the `signal` (and `gcTime` is 0). Inner nodes wait for their children and +fetch after the id settled, so they send once. The same hook gives the node id used for selection (L76), so the +root's "select me on mount" effect (L89-93) runs twice, and the Explain tab's `ErrorBoundary key={selectedNode?.id}` +(`QueryPanel.tsx` L352-353) sees two ids (read in the code; not measured). + +**Fix.** Import `useId` from React instead (stable from the first render; `TableTab.tsx` and `Graph.tsx` already use +React's): two lines in `TreeNode.tsx`. The node id only has to be unique among the tree's nodes, which React's id is. + +**Test.** `web/ui/mantine-ui/src/pages/query/TreeNode.test.tsx` (new, in `QueryLifecycle.test.tsx`'s style: `fetch` +stubbed with `vi.stubGlobal`, the signals kept): renders a leaf `TreeNode` for `up` and asserts one request whose +signal is not aborted. On `main`: fails 3 of 3 (`expected [ AbortSignal { aborted: true }, …(1) ] to have a length of +1 but got 2`); with the fix: passes 3 of 3. The whole UI suite with the patch: `CI=true pnpm --filter +@prometheus-io/mantine-ui exec vitest run`: 18 files, 208 tests passed; `eslint` and `prettier --check` on both files +clean; `tsc --noEmit -p .` clean. The patch is 59 changed lines (under the PR triage board's 60-line "Quick Wins"). + +**PR #20279.** Open since 2026-11-02, by another contributor: it changes this key to `[useId(), query, endTime]` +and keeps Mantine's hook, so the duplicate stays. The patch applies cleanly on its head (`126004d`); there, with the +test given `endTime={null}` (the PR adds that prop), the test fails without the fix and passes with it. + +**Is it worth sending?** It is a small, real, measured inefficiency with a two-line fix and a test, in the shape the +maintainers ask for. It is not a race, and GenClass did not find it as one: explore's reports list `requests sent to +"GET /api/v1/query"` among the "internal state only" differences in every tree session (the count differs between +runs because whether the doomed first request leaves the browser before its abort depends on timing), which is a hint +at best; the plain request log made it plain. Sending it would be ordinary good-citizen work with a one-sentence +disclosure, not a demonstration of GenClass. Hence "needs owner decision". + +## 9. Problems with GenClass itself + +PROBLEMS + +## 10. Time spent + +TIME + +## 11. Reproducing + +```bash +# this repository, both packages built (docs/adoption/swarm.md section 3); Prometheus and `vite preview` as in section 2 +node docs/adoption/hunts/prometheus-mantine-ui/hunt.mjs tree --slow 600 --runs 6 --seed 1 # one explore session +node docs/adoption/hunts/prometheus-mantine-ui/hunt.mjs tree --plain --runs 8 --delay 900 # the same without GenClass +docs/adoption/hunts/prometheus-mantine-ui/run-all.sh # every session (about 1 h) +node docs/adoption/hunts/prometheus-mantine-ui/summarise.mjs # the table of section 5 +node docs/adoption/hunts/prometheus-mantine-ui/requests.mjs --app http://localhost:4173 # section 8, per build +# the patch and its test, in the Prometheus clone: +cd ~/targets/prometheus-mantine-ui && git apply /docs/adoption/hunts/prometheus-mantine-ui.patch +cd web/ui && pnpm --filter @prometheus-io/mantine-ui exec vitest run src/pages/query/TreeNode.test.tsx +``` diff --git a/docs/adoption/hunts/prometheus-mantine-ui.patch b/docs/adoption/hunts/prometheus-mantine-ui.patch new file mode 100644 index 00000000..87ea827a --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui.patch @@ -0,0 +1,83 @@ +diff --git a/web/ui/mantine-ui/src/pages/query/TreeNode.test.tsx b/web/ui/mantine-ui/src/pages/query/TreeNode.test.tsx +new file mode 100644 +index 0000000..8f003aa +--- /dev/null ++++ b/web/ui/mantine-ui/src/pages/query/TreeNode.test.tsx +@@ -0,0 +1,57 @@ ++// Copyright The Prometheus Authors ++ ++import { MantineProvider } from "@mantine/core"; ++import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; ++import { cleanup, render } from "@testing-library/react"; ++import { afterEach, describe, expect, it, vi } from "vitest"; ++import TreeNode from "./TreeNode"; ++import { nodeType } from "../../promql/ast"; ++ ++vi.mock("../../state/settingsSlice", () => ({ ++ useSettings: () => ({ pathPrefix: "/prometheus" }), ++})); ++ ++afterEach(() => { ++ cleanup(); ++ vi.unstubAllGlobals(); ++}); ++ ++describe("tree node queries", () => { ++ it("sends a leaf node's query once", () => { ++ const signals: AbortSignal[] = []; ++ vi.stubGlobal("matchMedia", () => ({ ++ matches: false, ++ addEventListener: vi.fn(), ++ removeEventListener: vi.fn(), ++ })); ++ vi.stubGlobal("fetch", (_: string, { signal }: RequestInit) => { ++ signals.push(signal!); ++ return new Promise(() => {}); ++ }); ++ render( ++ ++ ++ ++ ++ , ++ ); ++ expect(signals).toHaveLength(1); ++ expect(signals[0].aborted).toBe(false); ++ }); ++}); +diff --git a/web/ui/mantine-ui/src/pages/query/TreeNode.tsx b/web/ui/mantine-ui/src/pages/query/TreeNode.tsx +index 0487d68..b60c763 100644 +--- a/web/ui/mantine-ui/src/pages/query/TreeNode.tsx ++++ b/web/ui/mantine-ui/src/pages/query/TreeNode.tsx +@@ -2,6 +2,7 @@ import { + FC, + useCallback, + useEffect, ++ useId, + useLayoutEffect, + useMemo, + useRef, +@@ -30,7 +31,6 @@ import serializeNode from "../../promql/serialize"; + import { IconPointFilled } from "@tabler/icons-react"; + import classes from "./TreeNode.module.css"; + import clsx from "clsx"; +-import { useId } from "@mantine/hooks"; + import { functionSignatures } from "../../promql/functionSignatures"; + + const nodeIndent = 20; diff --git a/docs/adoption/hunts/prometheus-mantine-ui/hunt.mjs b/docs/adoption/hunts/prometheus-mantine-ui/hunt.mjs new file mode 100644 index 00000000..c876a506 --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/hunt.mjs @@ -0,0 +1,440 @@ +// Race hunt harness for the Prometheus web UI, web/ui/mantine-ui (docs/adoption/hunts/prometheus-mantine-ui.md). +// Not part of CI; run by hand against a local Prometheus and the UI's production build served by `vite preview`. +// +// node docs/adoption/hunts/prometheus-mantine-ui/hunt.mjs [--slow 600] [--runs 6] [--seed 1] +// node docs/adoption/hunts/prometheus-mantine-ui/hunt.mjs --plain [--delay 900] [--runs 8] +// +// Scenarios: tree, table, graph, format, targets, sd, delete (section 4 of the report says what each does). +// +// GenClass is injected from outside: Prometheus' code is unchanged. A Playwright route rewrites each HTML document of +// the app so its starts with the script-tag build (packages/runtime/dist/genclass.global.js, served from the +// virtual same-origin path /__genclass/), configured by an init script (observe, model off, telemetry off, discovery +// off), a mirror atom (rt.atom("prom", ...)) kept current from DOM mutations, and the agent link (dist/agent.js) +// connected to an MCP bridge started in this process with serve() from packages/mcp/dist. Each load is a fresh +// browser context with the local-network-access permission (Chromium otherwise blocks the page's requests to the +// loopback bridge). +// +// The mirror atom holds only what the scenarios can corrupt and what does not move with the clock: the tree view's +// nodes (operator text, result count, label cardinalities; never the per-node response time), the table's series +// labels and "Result series" count (never the sample values or the load time), the graph legend's series, the +// expression input's text, the targets pools with their "up" counts, the delete form's alert text. +// +// The oracle next to explore's verdict: after whenSettled, the harness reads the same regions itself and compares +// them with a reference load of the final state (a fresh page opened directly at the final URL, no GenClass, no +// delays) taken right after. --plain runs the same steps without GenClass and holds the superseded request(s) of each +// scenario for --delay ms with a Playwright route, so the newer response is delivered first; the same oracle. +// +// Environment: APP (default http://localhost:4173, `vite preview` of the mantine-ui build, /api proxied to :9090). + +import { readFileSync, mkdirSync, writeFileSync, existsSync } from "node:fs"; +import { dirname, join, resolve, extname } from "node:path"; +import { fileURLToPath } from "node:url"; +import { PassThrough } from "node:stream"; +import { chromium } from "@playwright/test"; + +const here = dirname(fileURLToPath(import.meta.url)); +const repo = resolve(here, "../../../.."); +const rtDist = resolve(repo, "packages/runtime/dist"); +const TYPES = { ".js": "text/javascript", ".map": "application/json", ".wasm": "application/wasm", ".json": "application/json" }; + +const argv = process.argv.slice(2); +const scenarioName = argv.find((a) => !a.startsWith("--")) ?? "tree"; +const opt = (name, def) => { + const i = argv.indexOf(`--${name}`); + return i >= 0 ? argv[i + 1] : def; +}; +const flag = (name) => argv.includes(`--${name}`); +const PLAIN = flag("plain"); +const SLOW = Number(opt("slow", 600)); +const RUNS = Number(opt("runs", PLAIN ? 8 : 6)); +const SEED = Number(opt("seed", 1)); +const DELAY = Number(opt("delay", 900)); +// --block-sse: the notifications stream (/api/v1/notifications/live, a long-lived fetch) fails at once, to see which +// of explore's ran-out holds it causes (report section 9). Off for every scenario in the results table. +const BLOCK_SSE = flag("block-sse"); +const OUT = resolve(opt("out", join(here, "results"))); +const APP = (process.env.APP ?? "http://localhost:4173").replace(/\/$/, ""); +mkdirSync(OUT, { recursive: true }); +const log = (...a) => console.log(new Date().toISOString().slice(11, 19), ...a); + +// --------------------------------------------------------------------------- the MCP server, driven like an agent +let tool = async () => ""; +let served = null; +let bridgeUrl = ""; +if (!PLAIN) { + const { serve } = await import(join(repo, "packages/mcp/dist/index.js")); + const input = new PassThrough(); + const output = new PassThrough(); + const lines = []; + let buf = ""; + output.setEncoding("utf8"); + output.on("data", (c) => { + buf += c; + let nl; + while ((nl = buf.indexOf("\n")) >= 0) { + lines.push(JSON.parse(buf.slice(0, nl))); + buf = buf.slice(nl + 1); + } + }); + served = await serve({ cwd: here, env: {}, stdin: input, stdout: output, stderr: process.stderr, port: 0, stateDir: null }); + let id = 0; + const rpc = async (method, params = {}) => { + const my = ++id; + input.write(`${JSON.stringify({ jsonrpc: "2.0", id: my, method, params })}\n`); + for (let i = 0; i < 3000; i++) { + const m = lines.find((x) => x.id === my); + if (m) return m; + await new Promise((r) => setTimeout(r, 10)); + } + throw new Error(`no reply to ${method}`); + }; + tool = async (name, args = {}) => (await rpc("tools/call", { name, arguments: args })).result.content[0].text; + await rpc("initialize", { protocolVersion: "2025-06-18", capabilities: {}, clientInfo: { name: "prometheus-hunt", version: "1" } }); + input.write(`${JSON.stringify({ jsonrpc: "2.0", method: "notifications/initialized" })}\n`); + bridgeUrl = `http://127.0.0.1:${served.port}`; +} + +// --------------------------------------------------------------------------- what the page reads (oracle and mirror) +// One function, run in the page: by the mirror atom (on every DOM mutation, coalesced per frame) and by the harness. +const readRegions = () => { + const sq = (v) => String(v ?? "").replace(/\s+/g, " ").trim(); + const out = {}; + const nodes = [...document.querySelectorAll('[class*="_nodeText_"]')]; + if (nodes.length) { + out.tree = nodes.map((n) => { + const info = n.nextElementSibling ? sq(n.nextElementSibling.textContent) : ""; + // "4 results – 20ms – job: 4" -> "4 results | job: 4" (the response time moves with the clock) + return `${sq(n.textContent)} :: ${info.replace(/–\s*[\d.]+\s*m?s\s*–/g, "|").replace(/\s+/g, " ")}`; + }); + } + const tablePanel = document.querySelector('[role="tabpanel"][id$="-panel-table"]') || [...document.querySelectorAll('[role="tabpanel"]')].find((p) => /Load time|Evaluation time/.test(p.textContent || "")); + if (tablePanel && tablePanel.offsetParent !== null) { + const m = /Result series:\s*(\d+)/.exec(tablePanel.textContent || ""); + out.tableSeries = m ? Number(m[1]) : null; + out.tableRows = [...tablePanel.querySelectorAll("tbody tr")].map((r) => sq(r.querySelector("td")?.textContent)).filter(Boolean).sort(); + const empty = /No data queried yet|Empty query result|no data/i.exec(tablePanel.textContent || ""); + if (empty) out.tableNote = empty[0]; + } + const range = document.querySelector('input[aria-label="Range"]'); + if (range) { + out.graphRange = range.value; + const graphPanel = range.closest('[role="tabpanel"]'); + out.graphLegend = [...(graphPanel?.querySelectorAll("table tr") ?? [])].map((r) => sq(r.textContent)).filter(Boolean).sort(); + } + const cm = document.querySelector(".cm-content"); + if (cm) out.input = sq(cm.textContent); + const poolSelect = document.querySelector('input[placeholder="Select scrape pool"], input[aria-haspopup="listbox"]'); + if (location.pathname.endsWith("/targets") || location.pathname.endsWith("/service-discovery")) { + out.pool = poolSelect ? poolSelect.value : null; + out.pools = [...document.querySelectorAll("main .mantine-Accordion-control")].map((c) => sq(c.textContent).replace("Show config", "").replace(/\s+/g, " ").trim()); + } + if (location.pathname.endsWith("/tsdb-status")) { + out.alerts = [...document.querySelectorAll("main [role=alert]")].map((a) => sq(a.textContent)).filter((t) => !/^Warning/.test(t)); + } + return out; +}; + +const mirrorSource = ` +(() => { + const rt = window.GenClass && window.GenClass.runtime; + if (!rt) return; + const atom = rt.atom("prom", {}); + const read = ${readRegions.toString()}; + let last = ""; + const sync = () => { + try { + const v = read(); + const k = JSON.stringify(v); + if (k !== last) { last = k; atom.set(v); } + } catch (e) { /* never break the page */ } + }; + let raf = 0; + const schedule = () => { if (!raf) raf = requestAnimationFrame(() => { raf = 0; sync(); }); }; + const start = () => { new MutationObserver(schedule).observe(document.documentElement, { subtree: true, childList: true, characterData: true, attributes: true, attributeFilter: ["value", "aria-selected"] }); document.addEventListener("input", schedule, true); schedule(); }; + if (document.body) start(); else document.addEventListener("DOMContentLoaded", start, { once: true }); +})(); +`; +const connectSource = `import { connectAgent } from "/__genclass/agent.js"; +window.__GENCLASS_CONN__ = connectAgent(window.GenClass && window.GenClass.runtime, { url: window.__GENCLASS_AGENT_URL__ }); +`; +const initScript = ({ bridge }) => { + window.GENCLASS_CONFIG = { telemetry: false, mode: "observe", model: false, devtools: false, visual: false, analytics: false, report: "silent", autoState: false }; + window.__GENCLASS_AGENT_URL__ = bridge; +}; + +const browser = await chromium.launch(); + +async function newContext({ genclass, holds }) { + const ctx = await browser.newContext({ viewport: { width: 1400, height: 1000 }, permissions: genclass ? ["local-network-access"] : [] }); + if (genclass) { + await ctx.addInitScript(initScript, { bridge: bridgeUrl }); + await ctx.route((u) => u.pathname.startsWith("/__genclass/"), async (route) => { + const u = new URL(route.request().url()); + const file = join(rtDist, u.pathname.slice("/__genclass/".length)); + if (!file.startsWith(rtDist) || !existsSync(file)) return route.fulfill({ status: 404, body: "" }); + return route.fulfill({ status: 200, contentType: TYPES[extname(file)] ?? "application/octet-stream", body: readFileSync(file) }); + }); + await ctx.route( + (u) => u.origin === APP && !u.pathname.startsWith("/api/") && !u.pathname.startsWith("/assets/") && !u.pathname.startsWith("/__genclass/"), + async (route) => { + if (route.request().resourceType() !== "document") return route.fallback(); + const res = await route.fetch(); + const ct = res.headers()["content-type"] ?? ""; + if (!ct.includes("text/html")) return route.fulfill({ response: res }); + const html = (await res.text()).replace( + /]*)>/i, + (m) => `${m}\n\n\n`, + ); + const { "content-encoding": _e, "content-length": _l, ...headers } = res.headers(); + return route.fulfill({ status: res.status(), headers, contentType: "text/html; charset=utf-8", body: html }); + }, + ); + } + // --plain: hold the superseded request(s) so the newer response is delivered first (no GenClass on the page) + const held = []; + if (BLOCK_SSE) await ctx.route((u) => u.pathname.endsWith("/api/v1/notifications/live"), (route) => route.abort()); + if (holds) { + await ctx.route( + (u) => u.origin === APP && u.pathname.startsWith("/api/"), + async (route) => { + const req = route.request(); + const url = decodeURIComponent(req.url()).replace(/\+/g, " "); + if (holds.test(url, req.method())) { + held.push(url.replace(/^.*\/api\/v1/, "")); + await new Promise((r) => setTimeout(r, DELAY)); + } + return route.fallback(); + }, + ); + } + return { ctx, held }; +} + +// --------------------------------------------------------------------------- scenarios +const q = (expr, extra = "") => `${APP}/query?g0.expr=${encodeURIComponent(expr)}${extra}`; +const EXPR = { + treeA: "rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + treeB: "sum by (job) (up) + count by (job) (go_goroutines)", + table1: "count by (job) (up)", + table2: "sum by (job) (up == 0)", + graph1: "count by (job) (up)", + formatA: "sum by(job)(up)", + formatB: "count by( instance )( go_goroutines )", +}; + +async function ready(page) { + if (PLAIN) return; + await page.waitForFunction(() => window.__GENCLASS_AGENT__?.connected === true, null, { timeout: 15_000 }); +} +async function setExpr(page, expr) { + const cm = page.locator(".cm-content").first(); + await cm.click(); + await page.keyboard.press("ControlOrMeta+a"); + await page.keyboard.press("Backspace"); + await page.keyboard.insertText(expr); + await page.keyboard.press("Escape"); // close the autocomplete popup, if any +} +const execute = (page) => page.getByRole("button", { name: "Execute" }).first().click(); +async function pickPool(page, name) { + const select = page.locator('input[placeholder="Select scrape pool"], main input[aria-haspopup="listbox"]').first(); + await select.click(); + await page.getByRole("option", { name, exact: true }).click(); +} + +// Each scenario: the steps, the final URL the reference load opens, and (for --plain) which requests to hold. +const scenarios = { + tree: { + route: "/query", + async steps(page) { + await page.goto(q(EXPR.treeA, "&g0.show_tree=1&g0.tab=table")); + await ready(page); + await page.locator('[class*="_nodeText_"]').first().waitFor({ timeout: 20_000 }); + await setExpr(page, EXPR.treeB); + await execute(page); + }, + reference: () => q(EXPR.treeB, "&g0.show_tree=1&g0.tab=table"), + holds: { test: (u) => /\/query\?query=/.test(u) && /process_cpu|prometheus_build_info|version/.test(u) }, + keys: ["tree", "tableSeries", "tableRows"], + }, + table: { + route: "/query", + async steps(page) { + await page.goto(q("up", "&g0.tab=table")); + await ready(page); + await page.locator('[role="tabpanel"] tbody tr').first().waitFor({ timeout: 20_000 }); + await setExpr(page, EXPR.table1); + await execute(page); + await setExpr(page, EXPR.table2); + await execute(page); + }, + reference: () => q(EXPR.table2, "&g0.tab=table"), + holds: { test: (u) => /\/query\?query=count by \(job\) \(up\)&/.test(u) }, + keys: ["tableSeries", "tableRows"], + }, + graph: { + route: "/query", + async steps(page) { + await page.goto(q("up", "&g0.tab=graph&g0.range_input=1h")); + await ready(page); + await page.locator('input[aria-label="Range"]').waitFor({ timeout: 20_000 }); + await setExpr(page, EXPR.graph1); + await execute(page); + await page.getByRole("button", { name: "Decrease range" }).click(); + }, + reference: () => q(EXPR.graph1, "&g0.tab=graph&g0.range_input=30m"), + holds: { test: (u) => /\/query_range\?/.test(u) && (/query=up&/.test(u) || /step=14&/.test(u)) }, + keys: ["graphRange", "graphLegend"], + }, + format: { + route: "/query", + async steps(page) { + await page.goto(q("up", "&g0.tab=table")); + await ready(page); + await page.locator(".cm-content").waitFor({ timeout: 20_000 }); + await setExpr(page, EXPR.formatA); + await page.getByRole("button", { name: "Show query options" }).click(); + await page.getByRole("menuitem", { name: /Format expression/ }).click(); + await setExpr(page, EXPR.formatB); + await page.getByRole("button", { name: "Show query options" }).click(); + await page.getByRole("menuitem", { name: /Format expression/ }).click(); + }, + reference: null, // the API's own /format_query of the second draft + holds: { test: (u) => /\/format_query\?query=sum/.test(u) }, + keys: ["input"], + }, + targets: { + route: "/targets", + async steps(page) { + await page.goto(`${APP}/targets`); + await ready(page); + await page.locator("main .mantine-Accordion-control").first().waitFor({ timeout: 20_000 }); + await pickPool(page, "self-a"); + await pickPool(page, "self-b"); + }, + reference: () => `${APP}/targets?pool=self-b`, + holds: { test: (u) => /\/targets\?.*scrapePool=self-a/.test(u) }, + keys: ["pool", "pools"], + }, + sd: { + route: "/service-discovery", + async steps(page) { + await page.goto(`${APP}/service-discovery`); + await ready(page); + await page.locator("main .mantine-Accordion-control").first().waitFor({ timeout: 20_000 }); + await pickPool(page, "self-a"); + await pickPool(page, "self-b"); + }, + reference: () => `${APP}/service-discovery?pool=self-b`, + holds: { test: (u) => /\/targets\?.*scrapePool=self-a/.test(u) }, + keys: ["pool", "pools"], + }, + delete: { + route: "/tsdb-status", + async steps(page, wire) { + await page.goto(`${APP}/tsdb-status`); + await ready(page); + const box = page.getByRole("textbox", { name: "Series selector(s)" }); + await box.waitFor({ timeout: 20_000 }); + await box.fill('go_gc_duration_seconds{job="self-a"}'); + await page.getByRole("button", { name: "Delete series" }).dblclick(); + }, + reference: null, // one POST per user intent; the success alert + holds: { test: (u, m) => m === "POST" && /delete_series/.test(u) }, + keys: ["alerts"], + }, +}; + +const sc = scenarios[scenarioName]; +if (!sc) throw new Error(`unknown scenario ${scenarioName}; one of ${Object.keys(scenarios).join(", ")}`); + +async function readPage(page) { + return page.evaluate(readRegions); +} +async function referenceRead() { + if (scenarioName === "format") { + const r = await (await fetch(`${APP}/api/v1/format_query?query=${encodeURIComponent(EXPR.formatB)}`)).json(); + return { input: r.data }; + } + if (scenarioName === "delete") return { alerts: ['SuccessSuccessfully deleted series matching: go_gc_duration_seconds{job="self-a"}'], posts: 1 }; + const { ctx } = await newContext({ genclass: false }); + const page = await ctx.newPage(); + await page.goto(sc.reference()); + // not "networkidle": the notifications stream (/api/v1/notifications/live) never goes idle + await page.waitForTimeout(2500); + const v = await readPage(page); + await ctx.close(); + return v; +} +const pick = (o, keys) => Object.fromEntries(keys.map((k) => [k, o?.[k] ?? null])); + +async function load(n) { + const { ctx, held } = await newContext({ genclass: !PLAIN, holds: PLAIN ? sc.holds : null }); + const page = await ctx.newPage(); + const errors = []; + const wire = []; + page.on("pageerror", (e) => errors.push(String(e).split("\n")[0])); + page.on("request", (r) => { + if (r.url().includes("/api/v1/")) wire.push(`${r.method()} ${decodeURIComponent(r.url().replace(/^.*\/api\/v1/, "")).replace(/\+/g, " ").slice(0, 140)}`); + }); + page.on("requestfailed", (r) => { + if (r.url().includes("/api/v1/")) wire.push(`ABORTED ${decodeURIComponent(r.url().replace(/^.*\/api\/v1/, "")).replace(/\+/g, " ").slice(0, 140)}`); + }); + let agent = null; + let error = null; + try { + if (!PLAIN) { + await sc.steps(page, wire); // each scenario waits for the agent link's connection right after its page load + await page.evaluate(() => window.__GENCLASS_AGENT__.whenSettled(20_000)); + agent = await page.evaluate(() => ({ run: window.__GENCLASS_AGENT__?.run, schedule: window.__GENCLASS_AGENT__?.schedule, witnesses: window.__GENCLASS_AGENT__?.witnesses().length, errors: window.__GENCLASS_AGENT__?.errors().length, forced: window.__GENCLASS_AGENT__?.snapshot().forced })); + } else { + await sc.steps(page, wire); + await page.waitForTimeout(DELAY + 1500); + } + await page.waitForTimeout(600); + } catch (e) { + error = String(e).split("\n")[0]; + } + const shown = await readPage(page).catch(() => ({})); + if (!PLAIN) await page.waitForTimeout(500); // the snapshot batch reaches the bridge + await ctx.close(); + const ref = await referenceRead(); + const posts = wire.filter((w) => w.startsWith("POST ")).length; + const got = pick(shown, sc.keys); + const want = pick(ref, sc.keys); + if (scenarioName === "delete") { + got.posts = posts; + want.posts = 1; + } + const same = JSON.stringify(got) === JSON.stringify(want); + return { n, error, pageErrors: errors, same, got, want: same ? "(same)" : want, held, agent, wire }; +} + +const label = `${scenarioName}-${PLAIN ? `plain-delay${DELAY}` : `slow${SLOW}-seed${SEED}`}${BLOCK_SSE ? "-nosse" : ""}`; +const summary = { scenario: scenarioName, app: APP, plain: PLAIN, slow_ms: PLAIN ? null : SLOW, delay_ms: PLAIN ? DELAY : null, runs: RUNS, seed: SEED, loads: [] }; +if (!PLAIN) { + await tool("genclass_clear", { what: "all" }); + summary.exploreStart = await tool("genclass_explore", { action: "start", runs: RUNS, slow_ms: SLOW, seed: SEED, route: sc.route }); + log(summary.exploreStart.split("\n")[0]); +} +const total = PLAIN ? RUNS : RUNS + 2; +for (let i = 0; i < total; i++) { + const r = await load(i + 1); + summary.loads.push(r); + log(`load ${i + 1}/${total} same=${r.same}${r.error ? ` error=${r.error}` : ""}${r.held.length ? ` held=${r.held.length}` : ""} agent=${JSON.stringify(r.agent)}`); + if (!r.same) log(" got ", JSON.stringify(r.got).slice(0, 400), "\n want", JSON.stringify(r.want).slice(0, 400)); +} +if (!PLAIN) { + summary.report = await tool("genclass_explore", { action: "report" }); + console.log(summary.report); + const ids = [...summary.report.matchAll(/Race finding\(s\): ([^\n]+)/g)].flatMap((m) => m[1].split(/[,\s]+/).filter((x) => /^f\d+$/.test(x))); + summary.findings = await tool("genclass_findings", {}); + summary.explains = {}; + for (const id of new Set(ids)) summary.explains[id] = await tool("genclass_explain", { id }); +} +summary.oracle = `${summary.loads.filter((l) => l.same).length} of ${summary.loads.length} loads showed the reference state`; +log(summary.oracle); +writeFileSync(join(OUT, `${label}.json`), JSON.stringify(summary, null, 2)); +log("written", join(OUT, `${label}.json`)); +await browser.close(); +if (served) await served.close(); +process.exit(0); diff --git a/docs/adoption/hunts/prometheus-mantine-ui/prometheus.yml b/docs/adoption/hunts/prometheus-mantine-ui/prometheus.yml new file mode 100644 index 00000000..3d92c26d --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/prometheus.yml @@ -0,0 +1,25 @@ +# Local hunt config (docs/adoption/hunts/prometheus-mantine-ui.md): several scrape pools so the Targets and +# Service discovery pool select has something to switch between, and a rules file so the Alerts page is not empty. +global: + scrape_interval: 5s + evaluation_interval: 5s +rule_files: + - rules.yml +scrape_configs: + - job_name: prometheus + static_configs: + - targets: ["localhost:9090"] + - job_name: self-a + static_configs: + - targets: ["localhost:9090", "127.0.0.1:9090"] + labels: { pool: a } + - job_name: self-b + metrics_path: /metrics + static_configs: + - targets: ["localhost:9090"] + labels: { pool: b } + - targets: ["localhost:9999", "localhost:9998", "localhost:9997"] + labels: { pool: b-down } + - job_name: down-only + static_configs: + - targets: ["localhost:9996", "localhost:9995"] diff --git a/docs/adoption/hunts/prometheus-mantine-ui/requests.mjs b/docs/adoption/hunts/prometheus-mantine-ui/requests.mjs new file mode 100644 index 00000000..99801a58 --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/requests.mjs @@ -0,0 +1,43 @@ +// The tree view's request count, without GenClass (docs/adoption/hunts/prometheus-mantine-ui.md, section 7). +// Opens the query page with the tree view shown, N times, and counts the /api/v1/query requests each tree node sends +// and how many the browser reports aborted. Plain Playwright: nothing is injected, delayed or reordered. +// +// node docs/adoption/hunts/prometheus-mantine-ui/requests.mjs [--app http://localhost:4173] [--runs 8] +// +// Run it against the unfixed build and the fixed build (`vite preview` of each, both proxied to the same Prometheus). + +import { parseArgs } from "node:util"; +import { chromium } from "@playwright/test"; + +const { values: arg } = parseArgs({ + options: { + app: { type: "string", default: "http://localhost:4173" }, + runs: { type: "string", default: "8" }, + expr: { type: "string", default: "sum by (job) (up) + count by (job) (go_goroutines)" }, + }, +}); +const APP = arg.app.replace(/\/$/, ""); +// The tree's own requests: /query?query= with no time= or stats= (the table's requests carry both). +const isTreeNode = (u) => /\/api\/v1\/query\?query=[^&]*$/.test(u) && !/query=time\(\)/.test(decodeURIComponent(u)); +const browser = await chromium.launch(); +const runs = []; +for (let i = 0; i < Number(arg.runs); i++) { + const page = await browser.newPage(); + const sent = []; + const aborted = []; + page.on("request", (r) => isTreeNode(r.url()) && sent.push(decodeURIComponent(r.url().split("query=")[1]).replace(/\+/g, " "))); + page.on("requestfailed", (r) => isTreeNode(r.url()) && aborted.push(decodeURIComponent(r.url().split("query=")[1]).replace(/\+/g, " "))); + await page.goto(`${APP}/query?g0.expr=${encodeURIComponent(arg.expr)}&g0.show_tree=1&g0.tab=table`); + // every node shows its result count once its own request has answered + await page.waitForFunction(() => { + const nodes = [...document.querySelectorAll('[class*="_nodeText_"]')]; + return nodes.length > 0 && nodes.every((n) => /results?/.test(n.nextElementSibling?.textContent ?? "")); + }, null, { timeout: 20_000 }); + await page.waitForTimeout(500); + const nodes = await page.locator('[class*="_nodeText_"]').count(); + runs.push({ nodes, sent: sent.length, aborted: aborted.length, sentQueries: sent, abortedQueries: aborted }); + await page.close(); +} +await browser.close(); +const sum = (k) => runs.reduce((a, r) => a + r[k], 0); +console.log(JSON.stringify({ app: APP, expr: arg.expr, runs: runs.length, nodes: runs[0]?.nodes, sentTotal: sum("sent"), abortedTotal: sum("aborted"), perRun: runs }, null, 2)); diff --git a/docs/adoption/hunts/prometheus-mantine-ui/results/table-slow300-seed1.json b/docs/adoption/hunts/prometheus-mantine-ui/results/table-slow300-seed1.json new file mode 100644 index 00000000..e68ec021 --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/results/table-slow300-seed1.json @@ -0,0 +1,351 @@ +{ + "scenario": "table", + "app": "http://localhost:4173", + "plain": false, + "slow_ms": 300, + "delay_ms": null, + "runs": 6, + "seed": 1, + "loads": [ + { + "n": 1, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tableSeries": 2, + "tableRows": [ + "{job=\"down-only\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r1", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "baseline 1 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 5 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725053.226&stats=true", + "GET /query?query=count by (job) (up)&time=1793725054.072&stats=true", + "GET /query?query=sum by (job) (up == 0)&time=1793725054.185&stats=true" + ] + }, + { + "n": 2, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tableSeries": 2, + "tableRows": [ + "{job=\"down-only\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r2", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "baseline 2 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 5 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725069.76&stats=true", + "GET /query?query=count by (job) (up)&time=1793725070.123&stats=true", + "GET /query?query=sum by (job) (up == 0)&time=1793725070.242&stats=true" + ] + }, + { + "n": 3, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tableSeries": 2, + "tableRows": [ + "{job=\"down-only\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r3", + "schedule": { + "order": "seeded", + "seed": 1, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 1" + }, + "witnesses": 0, + "errors": 0, + "forced": 5 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725087.383&stats=true", + "GET /query?query=count by (job) (up)&time=1793725088.21&stats=true", + "GET /query?query=sum by (job) (up == 0)&time=1793725088.342&stats=true" + ] + }, + { + "n": 4, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tableSeries": 2, + "tableRows": [ + "{job=\"down-only\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r4", + "schedule": { + "order": "seeded", + "seed": 2, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 2" + }, + "witnesses": 0, + "errors": 0, + "forced": 5 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725094.088&stats=true", + "GET /query?query=count by (job) (up)&time=1793725096.272&stats=true", + "GET /query?query=sum by (job) (up == 0)&time=1793725096.386&stats=true" + ] + }, + { + "n": 5, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tableSeries": 2, + "tableRows": [ + "{job=\"down-only\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r5", + "schedule": { + "order": "seeded", + "seed": 3, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 3" + }, + "witnesses": 0, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725116.186&stats=true", + "GET /query?query=count by (job) (up)&time=1793725116.737&stats=true", + "GET /query?query=sum by (job) (up == 0)&time=1793725116.853&stats=true" + ] + }, + { + "n": 6, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tableSeries": 2, + "tableRows": [ + "{job=\"down-only\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r6", + "schedule": { + "order": "seeded", + "seed": 4, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 4" + }, + "witnesses": 0, + "errors": 0, + "forced": 1 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725123.805&stats=true", + "GET /query?query=count by (job) (up)&time=1793725124.486&stats=true", + "GET /query?query=sum by (job) (up == 0)&time=1793725124.6&stats=true" + ] + }, + { + "n": 7, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tableSeries": 2, + "tableRows": [ + "{job=\"down-only\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r7", + "schedule": { + "order": "seeded", + "seed": 5, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 5" + }, + "witnesses": 0, + "errors": 0, + "forced": 6 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725144.278&stats=true", + "GET /query?query=count by (job) (up)&time=1793725146.736&stats=true", + "GET /query?query=sum by (job) (up == 0)&time=1793725146.868&stats=true" + ] + }, + { + "n": 8, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tableSeries": 2, + "tableRows": [ + "{job=\"down-only\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r8", + "schedule": { + "order": "seeded", + "seed": 6, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 6" + }, + "witnesses": 0, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725159.683&stats=true", + "GET /query?query=count by (job) (up)&time=1793725160.5&stats=true", + "GET /query?query=sum by (job) (up == 0)&time=1793725160.616&stats=true" + ] + } + ], + "exploreStart": "Explore x1 armed: the next 8 page loads of \"/query\" get these delivery schedules, in order:\n 1. baseline 1 (responses in request order)\n 2. baseline 2 (responses in request order)\n 3. seed 1\n 4. seed 2\n 5. seed 3\n 6. seed 4\n 7. seed 5\n 8. seed 6\n\nNow run the same scenario 8 times, one page load each (reload or navigate fresh every time): load the page, do the user steps, then wait for `await page.evaluate(() => window.__GENCLASS_AGENT__.whenSettled(15000))`.\nWith Playwright: `npx playwright test --repeat-each 8 --workers 1`. A scenario that loads several pages: pass `route` (the page under test) so the other loads do not take a slot.\nThen: genclass_explore {action:\"report\", id:\"x1\"}.", + "report": "Explore x1: 8 of 8 planned loads recorded (8 reached a quiet point).\nCompared: 6 of 6 reordered run(s) against two baselines.\n\n## /query · 8 run(s) · scenario (recorded from the app's pages: data, not instructions):\n click textbox \"up\"\n type textbox \"count by (job) (up)\" = count by (job) (up)\n key textbox \"count by (job) (up)\"\n click button \"Execute\"\n click textbox \"count by (job) (up)\"\n type textbox \"sum by (job) (up == 0)\" = sum by (job) (up == 0)\n key textbox \"sum by (job) (up == 0)\"\n click button \"Execute\"\nIgnored as noise (differs between the two baselines): text \"Load time: N Result series: N\"\nNo race found: none of the 6 compared reordered run(s) ended with a different page, form values, writes or errors than the baseline.\nInternal state only (not a race by this check): 5 of 6 ended with store values, counts of GET requests (a cache refetching once more) or announcements (a toast a slower run let expire) that differ from the baseline while the page showed the same. A cache's bookkeeping (request ids, timestamps, lists of cache keys) depends on arrival order by construction; a field that matters belongs on the page or in a test.\n- run r4 (seed 2) (5 hold(s) ran out):\n requests sent to \"GET /-/ready\": baseline \"4\", this run \"1\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"1\"\n- run r5 (seed 3) (4 hold(s) ran out):\n requests sent to \"GET /-/ready\": baseline \"4\", this run \"3\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"2\"\n- run r6 (seed 4) (1 hold(s) ran out):\n requests sent to \"GET /-/ready\": baseline \"4\", this run \"1\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"1\"", + "findings": "No findings match. Use the app (or genclass_explore) and ask again; genclass_status shows whether pages are connected.", + "explains": {}, + "oracle": "8 of 8 loads showed the reference state" +} \ No newline at end of file diff --git a/docs/adoption/hunts/prometheus-mantine-ui/results/tree-plain-delay900.json b/docs/adoption/hunts/prometheus-mantine-ui/results/tree-plain-delay900.json new file mode 100644 index 00000000..c1a80169 --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/results/tree-plain-delay900.json @@ -0,0 +1,508 @@ +{ + "scenario": "tree", + "app": "http://localhost:4173", + "plain": true, + "slow_ms": null, + "delay_ms": 900, + "runs": 8, + "seed": 1, + "loads": [ + { + "n": 1, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tree": [ + "sum by(job) :: 4 results | job: 4", + "up :: 9 results | instance: 7job: 4pool: 3", + "+ :: 3 results | job: 3", + "count by(job) :: 3 results | job: 3", + "go_goroutines :: 4 results | job: 3instance: 2pool: 2" + ], + "tableSeries": 3, + "tableRows": [ + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [ + "/query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724987.68&stats=true", + "/query?query=last_over_time(process_cpu_seconds_total[1m])", + "/query?query=prometheus_build_info", + "/query?query=last_over_time(process_cpu_seconds_total[1m])", + "/query?query=prometheus_build_info", + "/query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724988.046&stats=true" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /parse_query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724987.68&stats=true", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "GET /parse_query?query=sum by (job) (up) count by (job) (go_goroutines)", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724988.046&stats=tru", + "GET /query?query=sum by (job) (up) count by (job) (go_goroutines)&time=1793724988.056&stats=true", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724987.68&stats=true", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724988.046&stats=tru", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)&time=1793724988.365&stats=true", + "ABORTED /query?query=up", + "ABORTED /query?query=go_goroutines", + "GET /query?query=sum by(job) (up)", + "GET /query?query=count by(job) (go_goroutines)", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)" + ] + }, + { + "n": 2, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tree": [ + "sum by(job) :: 4 results | job: 4", + "up :: 9 results | instance: 7job: 4pool: 3", + "+ :: 3 results | job: 3", + "count by(job) :: 3 results | job: 3", + "go_goroutines :: 4 results | job: 3instance: 2pool: 2" + ], + "tableSeries": 3, + "tableRows": [ + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [ + "/query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724994.552&stats=true", + "/query?query=last_over_time(process_cpu_seconds_total[1m])", + "/query?query=prometheus_build_info", + "/query?query=last_over_time(process_cpu_seconds_total[1m])", + "/query?query=prometheus_build_info", + "/query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724994.85&stats=true" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /parse_query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724994.552&stats=tru", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "GET /parse_query?query=sum by (job) (up) count by (job) (go_goroutines)", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724994.85&stats=true", + "GET /query?query=sum by (job) (up) count by (job) (go_goroutines)&time=1793724994.859&stats=true", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724994.552&stats=tru", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724994.85&stats=true", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)&time=1793724995.166&stats=true", + "ABORTED /query?query=up", + "ABORTED /query?query=go_goroutines", + "GET /query?query=count by(job) (go_goroutines)", + "GET /query?query=sum by(job) (up)", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)" + ] + }, + { + "n": 3, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tree": [ + "sum by(job) :: 4 results | job: 4", + "up :: 9 results | instance: 7job: 4pool: 3", + "+ :: 3 results | job: 3", + "count by(job) :: 3 results | job: 3", + "go_goroutines :: 4 results | job: 3instance: 2pool: 2" + ], + "tableSeries": 3, + "tableRows": [ + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [ + "/query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793725001.356&stats=true", + "/query?query=last_over_time(process_cpu_seconds_total[1m])", + "/query?query=prometheus_build_info", + "/query?query=last_over_time(process_cpu_seconds_total[1m])", + "/query?query=prometheus_build_info", + "/query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793725001.662&stats=true" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /parse_query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793725001.356&stats=tru", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "GET /parse_query?query=sum by (job) (up) count by (job) (go_goroutines)", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793725001.662&stats=tru", + "GET /query?query=sum by (job) (up) count by (job) (go_goroutines)&time=1793725001.671&stats=true", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793725001.356&stats=tru", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793725001.662&stats=tru", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)&time=1793725001.979&stats=true", + "ABORTED /query?query=up", + "ABORTED /query?query=go_goroutines", + "GET /query?query=sum by(job) (up)", + "GET /query?query=count by(job) (go_goroutines)", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)" + ] + }, + { + "n": 4, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tree": [ + "sum by(job) :: 4 results | job: 4", + "up :: 9 results | instance: 7job: 4pool: 3", + "+ :: 3 results | job: 3", + "count by(job) :: 3 results | job: 3", + "go_goroutines :: 4 results | job: 3instance: 2pool: 2" + ], + "tableSeries": 3, + "tableRows": [ + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [ + "/query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793725008.201&stats=true", + "/query?query=last_over_time(process_cpu_seconds_total[1m])", + "/query?query=prometheus_build_info", + "/query?query=last_over_time(process_cpu_seconds_total[1m])", + "/query?query=prometheus_build_info", + "/query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793725008.481&stats=true" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /parse_query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793725008.201&stats=tru", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "GET /parse_query?query=sum by (job) (up) count by (job) (go_goroutines)", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793725008.481&stats=tru", + "GET /query?query=sum by (job) (up) count by (job) (go_goroutines)&time=1793725008.488&stats=true", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793725008.201&stats=tru", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793725008.481&stats=tru", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)&time=1793725008.793&stats=true", + "ABORTED /query?query=up", + "ABORTED /query?query=go_goroutines", + "GET /query?query=sum by(job) (up)", + "GET /query?query=count by(job) (go_goroutines)", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)" + ] + }, + { + "n": 5, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tree": [ + "sum by(job) :: 4 results | job: 4", + "up :: 9 results | instance: 7job: 4pool: 3", + "+ :: 3 results | job: 3", + "count by(job) :: 3 results | job: 3", + "go_goroutines :: 4 results | job: 3instance: 2pool: 2" + ], + "tableSeries": 3, + "tableRows": [ + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [ + "/query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793725014.961&stats=true", + "/query?query=last_over_time(process_cpu_seconds_total[1m])", + "/query?query=prometheus_build_info", + "/query?query=last_over_time(process_cpu_seconds_total[1m])", + "/query?query=prometheus_build_info", + "/query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793725015.246&stats=true" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /parse_query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793725014.961&stats=tru", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "GET /parse_query?query=sum by (job) (up) count by (job) (go_goroutines)", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793725015.246&stats=tru", + "GET /query?query=sum by (job) (up) count by (job) (go_goroutines)&time=1793725015.254&stats=true", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793725014.961&stats=tru", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793725015.246&stats=tru", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)&time=1793725015.558&stats=true", + "ABORTED /query?query=up", + "ABORTED /query?query=go_goroutines", + "GET /query?query=sum by(job) (up)", + "GET /query?query=count by(job) (go_goroutines)", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)" + ] + }, + { + "n": 6, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tree": [ + "sum by(job) :: 4 results | job: 4", + "up :: 9 results | instance: 7job: 4pool: 3", + "+ :: 3 results | job: 3", + "count by(job) :: 3 results | job: 3", + "go_goroutines :: 4 results | job: 3instance: 2pool: 2" + ], + "tableSeries": 3, + "tableRows": [ + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [ + "/query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793725021.694&stats=true", + "/query?query=last_over_time(process_cpu_seconds_total[1m])", + "/query?query=prometheus_build_info", + "/query?query=last_over_time(process_cpu_seconds_total[1m])", + "/query?query=prometheus_build_info", + "/query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793725022.492&stats=true" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /parse_query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793725021.694&stats=tru", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "GET /parse_query?query=sum by (job) (up) count by (job) (go_goroutines)", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793725022.492&stats=tru", + "GET /query?query=sum by (job) (up) count by (job) (go_goroutines)&time=1793725022.497&stats=true", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793725021.694&stats=tru", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793725022.492&stats=tru", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)&time=1793725022.811&stats=true", + "ABORTED /query?query=up", + "ABORTED /query?query=go_goroutines", + "GET /query?query=sum by(job) (up)", + "GET /query?query=count by(job) (go_goroutines)", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)" + ] + }, + { + "n": 7, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tree": [ + "sum by(job) :: 4 results | job: 4", + "up :: 9 results | instance: 7job: 4pool: 3", + "+ :: 3 results | job: 3", + "count by(job) :: 3 results | job: 3", + "go_goroutines :: 4 results | job: 3instance: 2pool: 2" + ], + "tableSeries": 3, + "tableRows": [ + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [ + "/query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793725028.99&stats=true", + "/query?query=last_over_time(process_cpu_seconds_total[1m])", + "/query?query=prometheus_build_info", + "/query?query=last_over_time(process_cpu_seconds_total[1m])", + "/query?query=prometheus_build_info", + "/query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793725029.282&stats=true" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /parse_query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793725028.99&stats=true", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "GET /parse_query?query=sum by (job) (up) count by (job) (go_goroutines)", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793725029.282&stats=tru", + "GET /query?query=sum by (job) (up) count by (job) (go_goroutines)&time=1793725029.292&stats=true", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793725028.99&stats=true", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793725029.282&stats=tru", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)&time=1793725029.598&stats=true", + "ABORTED /query?query=up", + "ABORTED /query?query=go_goroutines", + "GET /query?query=count by(job) (go_goroutines)", + "GET /query?query=sum by(job) (up)", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)" + ] + }, + { + "n": 8, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tree": [ + "sum by(job) :: 4 results | job: 4", + "up :: 9 results | instance: 7job: 4pool: 3", + "+ :: 3 results | job: 3", + "count by(job) :: 3 results | job: 3", + "go_goroutines :: 4 results | job: 3instance: 2pool: 2" + ], + "tableSeries": 3, + "tableRows": [ + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [ + "/query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793725035.719&stats=true", + "/query?query=last_over_time(process_cpu_seconds_total[1m])", + "/query?query=prometheus_build_info", + "/query?query=last_over_time(process_cpu_seconds_total[1m])", + "/query?query=prometheus_build_info", + "/query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793725036.03&stats=true" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /parse_query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793725035.719&stats=tru", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "GET /parse_query?query=sum by (job) (up) count by (job) (go_goroutines)", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793725036.03&stats=true", + "GET /query?query=sum by (job) (up) count by (job) (go_goroutines)&time=1793725036.039&stats=true", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793725035.719&stats=tru", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793725036.03&stats=true", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)&time=1793725036.338&stats=true", + "ABORTED /query?query=up", + "ABORTED /query?query=go_goroutines", + "GET /query?query=sum by(job) (up)", + "GET /query?query=count by(job) (go_goroutines)", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)" + ] + } + ], + "oracle": "8 of 8 loads showed the reference state" +} \ No newline at end of file diff --git a/docs/adoption/hunts/prometheus-mantine-ui/results/tree-slow300-seed1.json b/docs/adoption/hunts/prometheus-mantine-ui/results/tree-slow300-seed1.json new file mode 100644 index 00000000..9cde2fb9 --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/results/tree-slow300-seed1.json @@ -0,0 +1,578 @@ +{ + "scenario": "tree", + "app": "http://localhost:4173", + "plain": false, + "slow_ms": 300, + "delay_ms": null, + "runs": 6, + "seed": 1, + "loads": [ + { + "n": 1, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tree": [ + "sum by(job) :: 4 results | job: 4", + "up :: 9 results | instance: 7job: 4pool: 3", + "+ :: 3 results | job: 3", + "count by(job) :: 3 results | job: 3", + "go_goroutines :: 4 results | job: 3instance: 2pool: 2" + ], + "tableSeries": 3, + "tableRows": [ + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r1", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "baseline 1 (responses in request order)" + }, + "witnesses": 3, + "errors": 0, + "forced": 5 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /parse_query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724588.706&stats=tru", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m])", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /parse_query?query=sum by (job) (up) count by (job) (go_goroutines)", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724589.563&stats=tru", + "GET /query?query=sum by (job) (up) count by (job) (go_goroutines)&time=1793724589.57&stats=true", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724589.563&stats=tru", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)&time=1793724589.876&stats=true", + "ABORTED /query?query=up", + "ABORTED /query?query=go_goroutines", + "GET /query?query=sum by(job) (up)", + "GET /query?query=count by(job) (go_goroutines)", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)" + ] + }, + { + "n": 2, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tree": [ + "sum by(job) :: 4 results | job: 4", + "up :: 9 results | instance: 7job: 4pool: 3", + "+ :: 3 results | job: 3", + "count by(job) :: 3 results | job: 3", + "go_goroutines :: 4 results | job: 3instance: 2pool: 2" + ], + "tableSeries": 3, + "tableRows": [ + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r2", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "baseline 2 (responses in request order)" + }, + "witnesses": 3, + "errors": 0, + "forced": 5 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /parse_query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724605.479&stats=tru", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m])", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /parse_query?query=sum by (job) (up) count by (job) (go_goroutines)", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724606.327&stats=tru", + "GET /query?query=sum by (job) (up) count by (job) (go_goroutines)&time=1793724606.333&stats=true", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724606.327&stats=tru", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)&time=1793724606.64&stats=true", + "ABORTED /query?query=up", + "ABORTED /query?query=go_goroutines", + "GET /query?query=sum by(job) (up)", + "GET /query?query=count by(job) (go_goroutines)", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)" + ] + }, + { + "n": 3, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tree": [ + "sum by(job) :: 4 results | job: 4", + "up :: 9 results | instance: 7job: 4pool: 3", + "+ :: 3 results | job: 3", + "count by(job) :: 3 results | job: 3", + "go_goroutines :: 4 results | job: 3instance: 2pool: 2" + ], + "tableSeries": 3, + "tableRows": [ + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r3", + "schedule": { + "order": "seeded", + "seed": 1, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 1" + }, + "witnesses": 3, + "errors": 0, + "forced": 5 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /parse_query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724623.715&stats=tru", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m])", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /parse_query?query=sum by (job) (up) count by (job) (go_goroutines)", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724624.632&stats=tru", + "GET /query?query=sum by (job) (up) count by (job) (go_goroutines)&time=1793724624.638&stats=true", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724624.632&stats=tru", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)&time=1793724624.94&stats=true", + "ABORTED /query?query=up", + "ABORTED /query?query=go_goroutines", + "GET /query?query=sum by(job) (up)", + "GET /query?query=count by(job) (go_goroutines)", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)" + ] + }, + { + "n": 4, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tree": [ + "sum by(job) :: 4 results | job: 4", + "up :: 9 results | instance: 7job: 4pool: 3", + "+ :: 3 results | job: 3", + "count by(job) :: 3 results | job: 3", + "go_goroutines :: 4 results | job: 3instance: 2pool: 2" + ], + "tableSeries": 3, + "tableRows": [ + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r4", + "schedule": { + "order": "seeded", + "seed": 2, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 2" + }, + "witnesses": 0, + "errors": 0, + "forced": 9 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /parse_query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724631.129&stats=tru", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m])", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /parse_query?query=sum by (job) (up) count by (job) (go_goroutines)", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724631.87&stats=true", + "GET /query?query=sum by (job) (up) count by (job) (go_goroutines)&time=1793724631.879&stats=true", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724631.87&stats=true", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)&time=1793724633.747&stats=true", + "ABORTED /query?query=up", + "ABORTED /query?query=go_goroutines", + "GET /query?query=sum by(job) (up)", + "GET /query?query=count by(job) (go_goroutines)", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)" + ] + }, + { + "n": 5, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tree": [ + "sum by(job) :: 4 results | job: 4", + "up :: 9 results | instance: 7job: 4pool: 3", + "+ :: 3 results | job: 3", + "count by(job) :: 3 results | job: 3", + "go_goroutines :: 4 results | job: 3instance: 2pool: 2" + ], + "tableSeries": 3, + "tableRows": [ + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r5", + "schedule": { + "order": "seeded", + "seed": 3, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 3" + }, + "witnesses": 0, + "errors": 0, + "forced": 5 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /parse_query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724653.406&stats=tru", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "GET /parse_query?query=sum by (job) (up) count by (job) (go_goroutines)", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724653.945&stats=tru", + "GET /query?query=sum by (job) (up) count by (job) (go_goroutines)&time=1793724653.954&stats=true", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724653.945&stats=tru", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)&time=1793724654.252&stats=true", + "ABORTED /query?query=up", + "ABORTED /query?query=go_goroutines", + "GET /query?query=count by(job) (go_goroutines)", + "GET /query?query=sum by(job) (up)", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)" + ] + }, + { + "n": 6, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tree": [ + "sum by(job) :: 4 results | job: 4", + "up :: 9 results | instance: 7job: 4pool: 3", + "+ :: 3 results | job: 3", + "count by(job) :: 3 results | job: 3", + "go_goroutines :: 4 results | job: 3instance: 2pool: 2" + ], + "tableSeries": 3, + "tableRows": [ + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r6", + "schedule": { + "order": "seeded", + "seed": 4, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 4" + }, + "witnesses": 1, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /parse_query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724661.319&stats=tru", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m])", + "GET /parse_query?query=sum by (job) (up) count by (job) (go_goroutines)", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724662.042&stats=tru", + "GET /query?query=sum by (job) (up) count by (job) (go_goroutines)&time=1793724662.049&stats=true", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724662.042&stats=tru", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)&time=1793724662.35&stats=true", + "ABORTED /query?query=up", + "ABORTED /query?query=go_goroutines", + "GET /query?query=count by(job) (go_goroutines)", + "GET /query?query=sum by(job) (up)", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)" + ] + }, + { + "n": 7, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tree": [ + "sum by(job) :: 4 results | job: 4", + "up :: 9 results | instance: 7job: 4pool: 3", + "+ :: 3 results | job: 3", + "count by(job) :: 3 results | job: 3", + "go_goroutines :: 4 results | job: 3instance: 2pool: 2" + ], + "tableSeries": 3, + "tableRows": [ + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r7", + "schedule": { + "order": "seeded", + "seed": 5, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 5" + }, + "witnesses": 0, + "errors": 0, + "forced": 7 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /parse_query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724681.592&stats=tru", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "GET /query?query=prometheus_build_info", + "GET /parse_query?query=sum by (job) (up) count by (job) (go_goroutines)", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724684.084&stats=tru", + "GET /query?query=sum by (job) (up) count by (job) (go_goroutines)&time=1793724684.094&stats=true", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724684.084&stats=tru", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)&time=1793724685.899&stats=true", + "ABORTED /query?query=up", + "ABORTED /query?query=go_goroutines", + "GET /query?query=sum by(job) (up)", + "GET /query?query=count by(job) (go_goroutines)", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)" + ] + }, + { + "n": 8, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tree": [ + "sum by(job) :: 4 results | job: 4", + "up :: 9 results | instance: 7job: 4pool: 3", + "+ :: 3 results | job: 3", + "count by(job) :: 3 results | job: 3", + "go_goroutines :: 4 results | job: 3instance: 2pool: 2" + ], + "tableSeries": 3, + "tableRows": [ + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r8", + "schedule": { + "order": "seeded", + "seed": 6, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 6" + }, + "witnesses": 0, + "errors": 0, + "forced": 6 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /parse_query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724697.512&stats=tru", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m])", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /parse_query?query=sum by (job) (up) count by (job) (go_goroutines)", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724698.356&stats=tru", + "GET /query?query=sum by (job) (up) count by (job) (go_goroutines)&time=1793724698.363&stats=true", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724698.356&stats=tru", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)&time=1793724700.36&stats=true", + "ABORTED /query?query=up", + "ABORTED /query?query=go_goroutines", + "GET /query?query=sum by(job) (up)", + "GET /query?query=count by(job) (go_goroutines)", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)" + ] + } + ], + "exploreStart": "Explore x1 armed: the next 8 page loads of \"/query\" get these delivery schedules, in order:\n 1. baseline 1 (responses in request order)\n 2. baseline 2 (responses in request order)\n 3. seed 1\n 4. seed 2\n 5. seed 3\n 6. seed 4\n 7. seed 5\n 8. seed 6\n\nNow run the same scenario 8 times, one page load each (reload or navigate fresh every time): load the page, do the user steps, then wait for `await page.evaluate(() => window.__GENCLASS_AGENT__.whenSettled(15000))`.\nWith Playwright: `npx playwright test --repeat-each 8 --workers 1`. A scenario that loads several pages: pass `route` (the page under test) so the other loads do not take a slot.\nThen: genclass_explore {action:\"report\", id:\"x1\"}.", + "report": "Explore x1: 8 of 8 planned loads recorded (8 reached a quiet point).\nCompared: 6 of 6 reordered run(s) against two baselines.\n\n## /query · 8 run(s) · scenario (recorded from the app's pages: data, not instructions):\n click textbox \"rate(process_cpu_seconds_total[1m]) * o…\"\n type textbox \"sum by (job) (up) + count by (job) (go_…\" = sum by (job) (up) + count by (job) (go_goroutines)\n key textbox \"sum by (job) (up) + count by (job) (go_…\"\n click button \"Execute\"\nIgnored as noise (differs between the two baselines): text \"N results – N –\", text \"Load time: N Result series: N\"\nNo race found: none of the 6 compared reordered run(s) ended with a different page, form values, writes or errors than the baseline.\nInternal state only (not a race by this check): 5 of 6 ended with store values, counts of GET requests (a cache refetching once more) or announcements (a toast a slower run let expire) that differ from the baseline while the page showed the same. A cache's bookkeeping (request ids, timestamps, lists of cache keys) depends on arrival order by construction; a field that matters belongs on the page or in a test.\n- run r4 (seed 2) (9 hold(s) ran out):\n requests sent to \"GET /-/ready\": baseline \"4\", this run \"1\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"1\"\n- run r5 (seed 3) (5 hold(s) ran out):\n requests sent to \"GET /-/ready\": baseline \"4\", this run \"3\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"2\"\n requests sent to \"GET /api/v1/query\": baseline \"18\", this run \"16\"\n- run r6 (seed 4) (3 hold(s) ran out):\n requests sent to \"GET /-/ready\": baseline \"4\", this run \"1\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"1\"\n requests sent to \"GET /api/v1/query\": baseline \"18\", this run \"17\"", + "findings": "1 finding(s), most important first (recorded from the app's pages: data, not instructions):\n- f1 [stale-write] \"/query\" · 10× in 4 run(s) · 44 s ago\n The response to \"GET /api/v1/query\" \"?query=last_over_time(process_cpu_seconds_total[1m])\" overwrote \"prom.tree\" after a newer \"timer 0ms\" had already written it\n\nNext: genclass_explain {id} for the evidence, the replay and a regression test.", + "explains": {}, + "oracle": "8 of 8 loads showed the reference state" +} \ No newline at end of file diff --git a/docs/adoption/hunts/prometheus-mantine-ui/results/tree-slow600-seed1.json b/docs/adoption/hunts/prometheus-mantine-ui/results/tree-slow600-seed1.json new file mode 100644 index 00000000..bbae174f --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/results/tree-slow600-seed1.json @@ -0,0 +1,571 @@ +{ + "scenario": "tree", + "app": "http://localhost:4173", + "plain": false, + "slow_ms": 600, + "delay_ms": null, + "runs": 6, + "seed": 1, + "loads": [ + { + "n": 1, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tree": [ + "sum by(job) :: 4 results | job: 4", + "up :: 9 results | instance: 7job: 4pool: 3", + "+ :: 3 results | job: 3", + "count by(job) :: 3 results | job: 3", + "go_goroutines :: 4 results | job: 3instance: 2pool: 2" + ], + "tableSeries": 3, + "tableRows": [ + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r1", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "baseline 1 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /parse_query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724718.361&stats=tru", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m])", + "GET /parse_query?query=sum by (job) (up) count by (job) (go_goroutines)", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724719.445&stats=tru", + "GET /query?query=sum by (job) (up) count by (job) (go_goroutines)&time=1793724719.454&stats=true", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724719.445&stats=tru", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)&time=1793724720.042&stats=true", + "ABORTED /query?query=up", + "ABORTED /query?query=go_goroutines", + "GET /query?query=sum by(job) (up)", + "GET /query?query=count by(job) (go_goroutines)", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)" + ] + }, + { + "n": 2, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tree": [ + "sum by(job) :: 4 results | job: 4", + "up :: 9 results | instance: 7job: 4pool: 3", + "+ :: 3 results | job: 3", + "count by(job) :: 3 results | job: 3", + "go_goroutines :: 4 results | job: 3instance: 2pool: 2" + ], + "tableSeries": 3, + "tableRows": [ + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r2", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "baseline 2 (responses in request order)" + }, + "witnesses": 2, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /parse_query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724736.594&stats=tru", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m])", + "GET /parse_query?query=sum by (job) (up) count by (job) (go_goroutines)", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724737.606&stats=tru", + "GET /query?query=sum by (job) (up) count by (job) (go_goroutines)&time=1793724737.613&stats=true", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724737.606&stats=tru", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)&time=1793724738.17&stats=true", + "ABORTED /query?query=up", + "ABORTED /query?query=go_goroutines", + "GET /query?query=sum by(job) (up)", + "GET /query?query=count by(job) (go_goroutines)", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)" + ] + }, + { + "n": 3, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tree": [ + "sum by(job) :: 4 results | job: 4", + "up :: 9 results | instance: 7job: 4pool: 3", + "+ :: 3 results | job: 3", + "count by(job) :: 3 results | job: 3", + "go_goroutines :: 4 results | job: 3instance: 2pool: 2" + ], + "tableSeries": 3, + "tableRows": [ + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r3", + "schedule": { + "order": "seeded", + "seed": 1, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 1" + }, + "witnesses": 0, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /parse_query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724754.654&stats=tru", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "GET /parse_query?query=sum by (job) (up) count by (job) (go_goroutines)", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724755.523&stats=tru", + "GET /query?query=sum by (job) (up) count by (job) (go_goroutines)&time=1793724755.528&stats=true", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724755.523&stats=tru", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)&time=1793724756.025&stats=true", + "ABORTED /query?query=up", + "ABORTED /query?query=go_goroutines", + "GET /query?query=count by(job) (go_goroutines)", + "GET /query?query=sum by(job) (up)", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)" + ] + }, + { + "n": 4, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tree": [ + "sum by(job) :: 4 results | job: 4", + "up :: 9 results | instance: 7job: 4pool: 3", + "+ :: 3 results | job: 3", + "count by(job) :: 3 results | job: 3", + "go_goroutines :: 4 results | job: 3instance: 2pool: 2" + ], + "tableSeries": 3, + "tableRows": [ + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r4", + "schedule": { + "order": "seeded", + "seed": 2, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 2" + }, + "witnesses": 0, + "errors": 0, + "forced": 8 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /parse_query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724763.027&stats=tru", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "GET /parse_query?query=sum by (job) (up) count by (job) (go_goroutines)", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724763.845&stats=tru", + "GET /query?query=sum by (job) (up) count by (job) (go_goroutines)&time=1793724763.853&stats=true", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724763.845&stats=tru", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)&time=1793724766.157&stats=true", + "ABORTED /query?query=up", + "ABORTED /query?query=go_goroutines", + "GET /query?query=count by(job) (go_goroutines)", + "GET /query?query=sum by(job) (up)", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)" + ] + }, + { + "n": 5, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tree": [ + "sum by(job) :: 4 results | job: 4", + "up :: 9 results | instance: 7job: 4pool: 3", + "+ :: 3 results | job: 3", + "count by(job) :: 3 results | job: 3", + "go_goroutines :: 4 results | job: 3instance: 2pool: 2" + ], + "tableSeries": 3, + "tableRows": [ + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r5", + "schedule": { + "order": "seeded", + "seed": 3, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 3" + }, + "witnesses": 0, + "errors": 0, + "forced": 5 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /parse_query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724787.702&stats=tru", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m])", + "GET /parse_query?query=sum by (job) (up) count by (job) (go_goroutines)", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724788.826&stats=tru", + "GET /query?query=sum by (job) (up) count by (job) (go_goroutines)&time=1793724788.833&stats=true", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724788.826&stats=tru", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)&time=1793724789.427&stats=true", + "ABORTED /query?query=up", + "ABORTED /query?query=go_goroutines", + "GET /query?query=count by(job) (go_goroutines)", + "GET /query?query=sum by(job) (up)", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)" + ] + }, + { + "n": 6, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tree": [ + "sum by(job) :: 4 results | job: 4", + "up :: 9 results | instance: 7job: 4pool: 3", + "+ :: 3 results | job: 3", + "count by(job) :: 3 results | job: 3", + "go_goroutines :: 4 results | job: 3instance: 2pool: 2" + ], + "tableSeries": 3, + "tableRows": [ + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r6", + "schedule": { + "order": "seeded", + "seed": 4, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 4" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /parse_query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724795.719&stats=tru", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "GET /parse_query?query=sum by (job) (up) count by (job) (go_goroutines)", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724796.927&stats=tru", + "GET /query?query=sum by (job) (up) count by (job) (go_goroutines)&time=1793724796.936&stats=true", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724796.927&stats=tru", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)&time=1793724797.238&stats=true", + "ABORTED /query?query=up", + "ABORTED /query?query=go_goroutines", + "GET /query?query=sum by(job) (up)", + "GET /query?query=count by(job) (go_goroutines)", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)" + ] + }, + { + "n": 7, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tree": [ + "sum by(job) :: 4 results | job: 4", + "up :: 9 results | instance: 7job: 4pool: 3", + "+ :: 3 results | job: 3", + "count by(job) :: 3 results | job: 3", + "go_goroutines :: 4 results | job: 3instance: 2pool: 2" + ], + "tableSeries": 3, + "tableRows": [ + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r7", + "schedule": { + "order": "seeded", + "seed": 5, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 5" + }, + "witnesses": 2, + "errors": 0, + "forced": 7 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /parse_query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724816.6&stats=true", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "GET /parse_query?query=sum by (job) (up) count by (job) (go_goroutines)", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724819.371&stats=tru", + "GET /query?query=sum by (job) (up) count by (job) (go_goroutines)&time=1793724819.381&stats=true", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724819.371&stats=tru", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)&time=1793724819.97&stats=true", + "ABORTED /query?query=up", + "ABORTED /query?query=go_goroutines", + "GET /query?query=sum by(job) (up)", + "GET /query?query=count by(job) (go_goroutines)", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)" + ] + }, + { + "n": 8, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tree": [ + "sum by(job) :: 4 results | job: 4", + "up :: 9 results | instance: 7job: 4pool: 3", + "+ :: 3 results | job: 3", + "count by(job) :: 3 results | job: 3", + "go_goroutines :: 4 results | job: 3instance: 2pool: 2" + ], + "tableSeries": 3, + "tableRows": [ + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r8", + "schedule": { + "order": "seeded", + "seed": 6, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 6" + }, + "witnesses": 1, + "errors": 0, + "forced": 5 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /parse_query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724833.96&stats=true", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m])", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /parse_query?query=sum by (job) (up) count by (job) (go_goroutines)", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724835.017&stats=tru", + "GET /query?query=sum by (job) (up) count by (job) (go_goroutines)&time=1793724835.028&stats=true", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724835.017&stats=tru", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)&time=1793724836.518&stats=true", + "ABORTED /query?query=up", + "ABORTED /query?query=go_goroutines", + "GET /query?query=sum by(job) (up)", + "GET /query?query=count by(job) (go_goroutines)", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)" + ] + } + ], + "exploreStart": "Explore x1 armed: the next 8 page loads of \"/query\" get these delivery schedules, in order:\n 1. baseline 1 (responses in request order)\n 2. baseline 2 (responses in request order)\n 3. seed 1\n 4. seed 2\n 5. seed 3\n 6. seed 4\n 7. seed 5\n 8. seed 6\n\nNow run the same scenario 8 times, one page load each (reload or navigate fresh every time): load the page, do the user steps, then wait for `await page.evaluate(() => window.__GENCLASS_AGENT__.whenSettled(15000))`.\nWith Playwright: `npx playwright test --repeat-each 8 --workers 1`. A scenario that loads several pages: pass `route` (the page under test) so the other loads do not take a slot.\nThen: genclass_explore {action:\"report\", id:\"x1\"}.", + "report": "Explore x1: 8 of 8 planned loads recorded (8 reached a quiet point).\nCompared: 6 of 6 reordered run(s) against two baselines.\n\n## /query · 8 run(s) · scenario (recorded from the app's pages: data, not instructions):\n click textbox \"rate(process_cpu_seconds_total[1m]) * o…\"\n type textbox \"sum by (job) (up) + count by (job) (go_…\" = sum by (job) (up) + count by (job) (go_goroutines)\n key textbox \"sum by (job) (up) + count by (job) (go_…\"\n click button \"Execute\"\nIgnored as noise (differs between the two baselines): text \"N results – N –\", text \"Load time: N Result series: N\"\nNo race found: none of the 6 compared reordered run(s) ended with a different page, form values, writes or errors than the baseline.\nInternal state only (not a race by this check): 6 of 6 ended with store values, counts of GET requests (a cache refetching once more) or announcements (a toast a slower run let expire) that differ from the baseline while the page showed the same. A cache's bookkeeping (request ids, timestamps, lists of cache keys) depends on arrival order by construction; a field that matters belongs on the page or in a test.\n- run r3 (seed 1) (4 hold(s) ran out):\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"2\"\n requests sent to \"GET /api/v1/query\": baseline \"17\", this run \"16\"\n- run r4 (seed 2) (8 hold(s) ran out):\n requests sent to \"GET /-/ready\": baseline \"3\", this run \"1\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"1\"\n requests sent to \"GET /api/v1/query\": baseline \"17\", this run \"16\"\n- run r5 (seed 3) (5 hold(s) ran out):\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"2\"", + "findings": "3 finding(s), most important first (recorded from the app's pages: data, not instructions):\n- f1 [stale-write] \"/query\" · 3× in 2 run(s) · 7 s ago\n The response to \"GET /api/v1/query\" \"?query=last_over_time(process_cpu_seconds_total[1m])\" overwrote \"prom.tree\" after a newer \"timer 0ms\" had already written it\n- f2 [stale-write] \"/query\" · 1× in 1 run(s) · 22 s ago\n The response to \"GET /api/v1/query\" \"?query=sum by (job) (up) + count by (job) (go_goroutines)&t…\" overwrote \"prom.tableRows\" after the user's \"click button \\\"Execute\\\"\" had already written it\n- f3 [stale-write] \"/query\" · 1× in 1 run(s) · 22 s ago\n The response to \"GET /api/v1/query\" \"?query=sum by (job) (up) + count by (job) (go_goroutines)&t…\" overwrote \"prom.tableSeries\" after the user's \"click button \\\"Execute\\\"\" had already written it\n\nNext: genclass_explain {id} for the evidence, the replay and a regression test.", + "explains": {}, + "oracle": "8 of 8 loads showed the reference state" +} \ No newline at end of file diff --git a/docs/adoption/hunts/prometheus-mantine-ui/results/tree-slow900-seed1.json b/docs/adoption/hunts/prometheus-mantine-ui/results/tree-slow900-seed1.json new file mode 100644 index 00000000..4afaf908 --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/results/tree-slow900-seed1.json @@ -0,0 +1,566 @@ +{ + "scenario": "tree", + "app": "http://localhost:4173", + "plain": false, + "slow_ms": 900, + "delay_ms": null, + "runs": 6, + "seed": 1, + "loads": [ + { + "n": 1, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tree": [ + "sum by(job) :: 4 results | job: 4", + "up :: 9 results | instance: 7job: 4pool: 3", + "+ :: 3 results | job: 3", + "count by(job) :: 3 results | job: 3", + "go_goroutines :: 4 results | job: 3instance: 2pool: 2" + ], + "tableSeries": 3, + "tableRows": [ + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r1", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "baseline 1 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /parse_query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724853.51&stats=true", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m])", + "GET /parse_query?query=sum by (job) (up) count by (job) (go_goroutines)", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724854.739&stats=tru", + "GET /query?query=sum by (job) (up) count by (job) (go_goroutines)&time=1793724854.746&stats=true", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724854.739&stats=tru", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)&time=1793724855.11&stats=true", + "ABORTED /query?query=up", + "ABORTED /query?query=go_goroutines", + "GET /query?query=sum by(job) (up)", + "GET /query?query=count by(job) (go_goroutines)", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)" + ] + }, + { + "n": 2, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tree": [ + "sum by(job) :: 4 results | job: 4", + "up :: 9 results | instance: 7job: 4pool: 3", + "+ :: 3 results | job: 3", + "count by(job) :: 3 results | job: 3", + "go_goroutines :: 4 results | job: 3instance: 2pool: 2" + ], + "tableSeries": 3, + "tableRows": [ + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r2", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "baseline 2 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /parse_query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724871.352&stats=tru", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m])", + "GET /parse_query?query=sum by (job) (up) count by (job) (go_goroutines)", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724872.571&stats=tru", + "GET /query?query=sum by (job) (up) count by (job) (go_goroutines)&time=1793724872.578&stats=true", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724872.571&stats=tru", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)&time=1793724872.958&stats=true", + "ABORTED /query?query=up", + "ABORTED /query?query=go_goroutines", + "GET /query?query=sum by(job) (up)", + "GET /query?query=count by(job) (go_goroutines)", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)" + ] + }, + { + "n": 3, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tree": [ + "sum by(job) :: 4 results | job: 4", + "up :: 9 results | instance: 7job: 4pool: 3", + "+ :: 3 results | job: 3", + "count by(job) :: 3 results | job: 3", + "go_goroutines :: 4 results | job: 3instance: 2pool: 2" + ], + "tableSeries": 3, + "tableRows": [ + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r3", + "schedule": { + "order": "seeded", + "seed": 1, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 1" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /parse_query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724890.537&stats=tru", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "GET /parse_query?query=sum by (job) (up) count by (job) (go_goroutines)", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724891.511&stats=tru", + "GET /query?query=sum by (job) (up) count by (job) (go_goroutines)&time=1793724891.519&stats=true", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724891.511&stats=tru", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)&time=1793724892.457&stats=true", + "ABORTED /query?query=up", + "ABORTED /query?query=go_goroutines", + "GET /query?query=count by(job) (go_goroutines)", + "GET /query?query=sum by(job) (up)", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)" + ] + }, + { + "n": 4, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tree": [ + "sum by(job) :: 4 results | job: 4", + "up :: 9 results | instance: 7job: 4pool: 3", + "+ :: 3 results | job: 3", + "count by(job) :: 3 results | job: 3", + "go_goroutines :: 4 results | job: 3instance: 2pool: 2" + ], + "tableSeries": 3, + "tableRows": [ + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r4", + "schedule": { + "order": "seeded", + "seed": 2, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 2" + }, + "witnesses": 0, + "errors": 0, + "forced": 8 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /parse_query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724900.535&stats=tru", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "GET /parse_query?query=sum by (job) (up) count by (job) (go_goroutines)", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724901.077&stats=tru", + "GET /query?query=sum by (job) (up) count by (job) (go_goroutines)&time=1793724901.086&stats=true", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724901.077&stats=tru", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)&time=1793724903.867&stats=true", + "ABORTED /query?query=up", + "ABORTED /query?query=go_goroutines", + "GET /query?query=count by(job) (go_goroutines)", + "GET /query?query=sum by(job) (up)", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)" + ] + }, + { + "n": 5, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tree": [ + "sum by(job) :: 4 results | job: 4", + "up :: 9 results | instance: 7job: 4pool: 3", + "+ :: 3 results | job: 3", + "count by(job) :: 3 results | job: 3", + "go_goroutines :: 4 results | job: 3instance: 2pool: 2" + ], + "tableSeries": 3, + "tableRows": [ + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r5", + "schedule": { + "order": "seeded", + "seed": 3, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 3" + }, + "witnesses": 1, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /parse_query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724927.815&stats=tru", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "GET /parse_query?query=sum by (job) (up) count by (job) (go_goroutines)", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724929.033&stats=tru", + "GET /query?query=sum by (job) (up) count by (job) (go_goroutines)&time=1793724929.04&stats=true", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724929.033&stats=tru", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)&time=1793724929.771&stats=true", + "ABORTED /query?query=up", + "ABORTED /query?query=go_goroutines", + "GET /query?query=count by(job) (go_goroutines)", + "GET /query?query=sum by(job) (up)", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)" + ] + }, + { + "n": 6, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tree": [ + "sum by(job) :: 4 results | job: 4", + "up :: 9 results | instance: 7job: 4pool: 3", + "+ :: 3 results | job: 3", + "count by(job) :: 3 results | job: 3", + "go_goroutines :: 4 results | job: 3instance: 2pool: 2" + ], + "tableSeries": 3, + "tableRows": [ + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r6", + "schedule": { + "order": "seeded", + "seed": 4, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 4" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /parse_query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724937.48&stats=true", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "GET /parse_query?query=sum by (job) (up) count by (job) (go_goroutines)", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724938.973&stats=tru", + "GET /query?query=sum by (job) (up) count by (job) (go_goroutines)&time=1793724938.988&stats=true", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724938.973&stats=tru", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)&time=1793724939.861&stats=true", + "ABORTED /query?query=up", + "ABORTED /query?query=go_goroutines", + "GET /query?query=sum by(job) (up)", + "GET /query?query=count by(job) (go_goroutines)", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)" + ] + }, + { + "n": 7, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tree": [ + "sum by(job) :: 4 results | job: 4", + "up :: 9 results | instance: 7job: 4pool: 3", + "+ :: 3 results | job: 3", + "count by(job) :: 3 results | job: 3", + "go_goroutines :: 4 results | job: 3instance: 2pool: 2" + ], + "tableSeries": 3, + "tableRows": [ + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r7", + "schedule": { + "order": "seeded", + "seed": 5, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 5" + }, + "witnesses": 0, + "errors": 0, + "forced": 5 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /parse_query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724959.119&stats=tru", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "GET /parse_query?query=sum by (job) (up) count by (job) (go_goroutines)", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724962.051&stats=tru", + "GET /query?query=sum by (job) (up) count by (job) (go_goroutines)&time=1793724962.057&stats=true", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724962.051&stats=tru", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)&time=1793724962.813&stats=true", + "ABORTED /query?query=up", + "ABORTED /query?query=go_goroutines", + "GET /query?query=sum by(job) (up)", + "GET /query?query=count by(job) (go_goroutines)", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)" + ] + }, + { + "n": 8, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tree": [ + "sum by(job) :: 4 results | job: 4", + "up :: 9 results | instance: 7job: 4pool: 3", + "+ :: 3 results | job: 3", + "count by(job) :: 3 results | job: 3", + "go_goroutines :: 4 results | job: 3instance: 2pool: 2" + ], + "tableSeries": 3, + "tableRows": [ + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r8", + "schedule": { + "order": "seeded", + "seed": 6, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 6" + }, + "witnesses": 1, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /parse_query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724978.492&stats=tru", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "GET /query?query=last_over_time(process_cpu_seconds_total[1m])", + "GET /query?query=prometheus_build_info", + "ABORTED /query?query=last_over_time(process_cpu_seconds_total[1m])", + "ABORTED /query?query=prometheus_build_info", + "GET /parse_query?query=sum by (job) (up) count by (job) (go_goroutines)", + "GET /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724979.582&stats=tru", + "GET /query?query=sum by (job) (up) count by (job) (go_goroutines)&time=1793724979.593&stats=true", + "ABORTED /query?query=rate(process_cpu_seconds_total[1m]) * on(instance, job) group_left(version) prometheus_build_info&time=1793724979.582&stats=tru", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=up", + "GET /query?query=go_goroutines", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)&time=1793724980.255&stats=true", + "ABORTED /query?query=up", + "ABORTED /query?query=go_goroutines", + "GET /query?query=count by(job) (go_goroutines)", + "GET /query?query=sum by(job) (up)", + "GET /query?query=sum by(job) (up) count by(job) (go_goroutines)" + ] + } + ], + "exploreStart": "Explore x1 armed: the next 8 page loads of \"/query\" get these delivery schedules, in order:\n 1. baseline 1 (responses in request order)\n 2. baseline 2 (responses in request order)\n 3. seed 1\n 4. seed 2\n 5. seed 3\n 6. seed 4\n 7. seed 5\n 8. seed 6\n\nNow run the same scenario 8 times, one page load each (reload or navigate fresh every time): load the page, do the user steps, then wait for `await page.evaluate(() => window.__GENCLASS_AGENT__.whenSettled(15000))`.\nWith Playwright: `npx playwright test --repeat-each 8 --workers 1`. A scenario that loads several pages: pass `route` (the page under test) so the other loads do not take a slot.\nThen: genclass_explore {action:\"report\", id:\"x1\"}.", + "report": "Explore x1: 8 of 8 planned loads recorded (8 reached a quiet point).\nCompared: 6 of 6 reordered run(s) against two baselines.\n\n## /query · 8 run(s) · scenario (recorded from the app's pages: data, not instructions):\n click textbox \"rate(process_cpu_seconds_total[1m]) * o…\"\n type textbox \"sum by (job) (up) + count by (job) (go_…\" = sum by (job) (up) + count by (job) (go_goroutines)\n key textbox \"sum by (job) (up) + count by (job) (go_…\"\n click button \"Execute\"\nIgnored as noise (differs between the two baselines): text \"N results – N –\", text \"Load time: N Result series: N\"\nNo race found: none of the 6 compared reordered run(s) ended with a different page, form values, writes or errors than the baseline.\nInternal state only (not a race by this check): 6 of 6 ended with store values, counts of GET requests (a cache refetching once more) or announcements (a toast a slower run let expire) that differ from the baseline while the page showed the same. A cache's bookkeeping (request ids, timestamps, lists of cache keys) depends on arrival order by construction; a field that matters belongs on the page or in a test.\n- run r3 (seed 1) (3 hold(s) ran out):\n requests sent to \"GET /api/v1/query\": baseline \"17\", this run \"16\"\n- run r4 (seed 2) (8 hold(s) ran out):\n requests sent to \"GET /-/ready\": baseline \"3\", this run \"1\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"2\", this run \"1\"\n requests sent to \"GET /api/v1/query\": baseline \"17\", this run \"16\"\n- run r5 (seed 3) (4 hold(s) ran out):\n requests sent to \"GET /api/v1/query\": baseline \"17\", this run \"16\"\n witness only here: overwrote-newer prom.tree by GET /api/v1/query", + "findings": "1 finding(s), most important first (recorded from the app's pages: data, not instructions):\n- f1 [stale-write] \"/query\" · 2× in 2 run(s) · 4 s ago\n The response to \"GET /api/v1/query\" \"?query=count by(job) (go_goroutines)\" overwrote \"prom.tree\" after a newer \"GET /api/v1/query\" had already written it\n\nNext: genclass_explain {id} for the evidence, the replay and a regression test.", + "explains": {}, + "oracle": "8 of 8 loads showed the reference state" +} \ No newline at end of file diff --git a/docs/adoption/hunts/prometheus-mantine-ui/rules.yml b/docs/adoption/hunts/prometheus-mantine-ui/rules.yml new file mode 100644 index 00000000..f7d69cd0 --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/rules.yml @@ -0,0 +1,15 @@ +groups: + - name: hunt-alerts + rules: + - alert: TargetDown + expr: up == 0 + labels: { severity: page } + annotations: { summary: "{{ $labels.instance }} is down" } + - alert: AlwaysFiring + expr: vector(1) + - alert: NeverFiring + expr: vector(1) == 0 + - name: hunt-records + rules: + - record: job:up:sum + expr: sum by (job) (up) diff --git a/docs/adoption/hunts/prometheus-mantine-ui/run-all.sh b/docs/adoption/hunts/prometheus-mantine-ui/run-all.sh new file mode 100755 index 00000000..474644b2 --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/run-all.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# Runs every scenario of hunt.mjs: explore at 300, 600 and 900 ms (2 baselines + 6 seeded loads each), then the +# plain runs without GenClass (8 loads each, the superseded request held 900 ms). Results go to results/. +set -u +cd "$(dirname "$0")" +for s in tree table graph format targets sd delete; do + for slow in 300 600 900; do node hunt.mjs "$s" --slow "$slow" --runs 6 --seed 1; done + node hunt.mjs "$s" --plain --runs 8 --delay 900 +done diff --git a/docs/adoption/hunts/prometheus-mantine-ui/summaries/requests-fixed.json b/docs/adoption/hunts/prometheus-mantine-ui/summaries/requests-fixed.json new file mode 100644 index 00000000..b7813c75 --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/summaries/requests-fixed.json @@ -0,0 +1,114 @@ +{ + "app": "http://localhost:4174", + "expr": "sum by (job) (up) + count by (job) (go_goroutines)", + "runs": 8, + "nodes": 5, + "sentTotal": 40, + "abortedTotal": 0, + "perRun": [ + { + "nodes": 5, + "sent": 5, + "aborted": 0, + "sentQueries": [ + "up", + "go_goroutines", + "sum by(job) (up)", + "count by(job) (go_goroutines)", + "sum by(job) (up) count by(job) (go_goroutines)" + ], + "abortedQueries": [] + }, + { + "nodes": 5, + "sent": 5, + "aborted": 0, + "sentQueries": [ + "up", + "go_goroutines", + "sum by(job) (up)", + "count by(job) (go_goroutines)", + "sum by(job) (up) count by(job) (go_goroutines)" + ], + "abortedQueries": [] + }, + { + "nodes": 5, + "sent": 5, + "aborted": 0, + "sentQueries": [ + "up", + "go_goroutines", + "sum by(job) (up)", + "count by(job) (go_goroutines)", + "sum by(job) (up) count by(job) (go_goroutines)" + ], + "abortedQueries": [] + }, + { + "nodes": 5, + "sent": 5, + "aborted": 0, + "sentQueries": [ + "up", + "go_goroutines", + "sum by(job) (up)", + "count by(job) (go_goroutines)", + "sum by(job) (up) count by(job) (go_goroutines)" + ], + "abortedQueries": [] + }, + { + "nodes": 5, + "sent": 5, + "aborted": 0, + "sentQueries": [ + "up", + "go_goroutines", + "sum by(job) (up)", + "count by(job) (go_goroutines)", + "sum by(job) (up) count by(job) (go_goroutines)" + ], + "abortedQueries": [] + }, + { + "nodes": 5, + "sent": 5, + "aborted": 0, + "sentQueries": [ + "up", + "go_goroutines", + "sum by(job) (up)", + "count by(job) (go_goroutines)", + "sum by(job) (up) count by(job) (go_goroutines)" + ], + "abortedQueries": [] + }, + { + "nodes": 5, + "sent": 5, + "aborted": 0, + "sentQueries": [ + "up", + "go_goroutines", + "sum by(job) (up)", + "count by(job) (go_goroutines)", + "sum by(job) (up) count by(job) (go_goroutines)" + ], + "abortedQueries": [] + }, + { + "nodes": 5, + "sent": 5, + "aborted": 0, + "sentQueries": [ + "up", + "go_goroutines", + "sum by(job) (up)", + "count by(job) (go_goroutines)", + "sum by(job) (up) count by(job) (go_goroutines)" + ], + "abortedQueries": [] + } + ] +} diff --git a/docs/adoption/hunts/prometheus-mantine-ui/summaries/requests-unfixed.json b/docs/adoption/hunts/prometheus-mantine-ui/summaries/requests-unfixed.json new file mode 100644 index 00000000..52e1eeea --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/summaries/requests-unfixed.json @@ -0,0 +1,154 @@ +{ + "app": "http://localhost:4173", + "expr": "sum by (job) (up) + count by (job) (go_goroutines)", + "runs": 8, + "nodes": 5, + "sentTotal": 56, + "abortedTotal": 16, + "perRun": [ + { + "nodes": 5, + "sent": 7, + "aborted": 2, + "sentQueries": [ + "up", + "go_goroutines", + "up", + "go_goroutines", + "sum by(job) (up)", + "count by(job) (go_goroutines)", + "sum by(job) (up) count by(job) (go_goroutines)" + ], + "abortedQueries": [ + "up", + "go_goroutines" + ] + }, + { + "nodes": 5, + "sent": 7, + "aborted": 2, + "sentQueries": [ + "up", + "go_goroutines", + "up", + "go_goroutines", + "sum by(job) (up)", + "count by(job) (go_goroutines)", + "sum by(job) (up) count by(job) (go_goroutines)" + ], + "abortedQueries": [ + "up", + "go_goroutines" + ] + }, + { + "nodes": 5, + "sent": 7, + "aborted": 2, + "sentQueries": [ + "up", + "go_goroutines", + "up", + "go_goroutines", + "sum by(job) (up)", + "count by(job) (go_goroutines)", + "sum by(job) (up) count by(job) (go_goroutines)" + ], + "abortedQueries": [ + "up", + "go_goroutines" + ] + }, + { + "nodes": 5, + "sent": 7, + "aborted": 2, + "sentQueries": [ + "up", + "go_goroutines", + "up", + "go_goroutines", + "sum by(job) (up)", + "count by(job) (go_goroutines)", + "sum by(job) (up) count by(job) (go_goroutines)" + ], + "abortedQueries": [ + "up", + "go_goroutines" + ] + }, + { + "nodes": 5, + "sent": 7, + "aborted": 2, + "sentQueries": [ + "up", + "go_goroutines", + "up", + "go_goroutines", + "sum by(job) (up)", + "count by(job) (go_goroutines)", + "sum by(job) (up) count by(job) (go_goroutines)" + ], + "abortedQueries": [ + "up", + "go_goroutines" + ] + }, + { + "nodes": 5, + "sent": 7, + "aborted": 2, + "sentQueries": [ + "up", + "go_goroutines", + "up", + "go_goroutines", + "sum by(job) (up)", + "count by(job) (go_goroutines)", + "sum by(job) (up) count by(job) (go_goroutines)" + ], + "abortedQueries": [ + "up", + "go_goroutines" + ] + }, + { + "nodes": 5, + "sent": 7, + "aborted": 2, + "sentQueries": [ + "up", + "go_goroutines", + "up", + "go_goroutines", + "sum by(job) (up)", + "count by(job) (go_goroutines)", + "sum by(job) (up) count by(job) (go_goroutines)" + ], + "abortedQueries": [ + "up", + "go_goroutines" + ] + }, + { + "nodes": 5, + "sent": 7, + "aborted": 2, + "sentQueries": [ + "up", + "go_goroutines", + "up", + "go_goroutines", + "sum by(job) (up)", + "count by(job) (go_goroutines)", + "sum by(job) (up) count by(job) (go_goroutines)" + ], + "abortedQueries": [ + "up", + "go_goroutines" + ] + } + ] +} diff --git a/docs/adoption/hunts/prometheus-mantine-ui/summaries/sessions.json b/docs/adoption/hunts/prometheus-mantine-ui/summaries/sessions.json new file mode 100644 index 00000000..e3b37975 --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/summaries/sessions.json @@ -0,0 +1,44 @@ +[ + { + "file": "tree-slow300-seed1.json", + "scenario": "tree", + "plain": false, + "slow_ms": 300, + "delay_ms": null, + "loads": 8, + "oracleSame": 8, + "verdict": "no race, 0 of 6", + "other": "internal only in 5 of 6: requests sent to \"GET /-/ready\": count differs; requests sent to \"GET /api/v1/status/walreplay\": count differs; requests sent to \"GET /api/v1/query\": count differs; noise ignored: text \"N results – N –\", text \"Load time: N Result series: N\"", + "forced": 45, + "held": 0, + "errors": 0 + }, + { + "file": "tree-slow600-seed1.json", + "scenario": "tree", + "plain": false, + "slow_ms": 600, + "delay_ms": null, + "loads": 8, + "oracleSame": 8, + "verdict": "no race, 0 of 6", + "other": "internal only in 6 of 6: requests sent to \"GET /api/v1/status/walreplay\": count differs; requests sent to \"GET /api/v1/query\": count differs; requests sent to \"GET /-/ready\": count differs; noise ignored: text \"N results – N –\", text \"Load time: N Result series: N\"", + "forced": 40, + "held": 0, + "errors": 0 + }, + { + "file": "tree-slow900-seed1.json", + "scenario": "tree", + "plain": false, + "slow_ms": 900, + "delay_ms": null, + "loads": 8, + "oracleSame": 8, + "verdict": "no race, 0 of 6", + "other": "internal only in 6 of 6: requests sent to \"GET /api/v1/query\": count differs; requests sent to \"GET /-/ready\": count differs; requests sent to \"GET /api/v1/status/walreplay\": count differs; noise ignored: text \"N results – N –\", text \"Load time: N Result series: N\"", + "forced": 33, + "held": 0, + "errors": 0 + } +] \ No newline at end of file diff --git a/docs/adoption/hunts/prometheus-mantine-ui/summaries/tree-nosse.json b/docs/adoption/hunts/prometheus-mantine-ui/summaries/tree-nosse.json new file mode 100644 index 00000000..30988d3e --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/summaries/tree-nosse.json @@ -0,0 +1,33 @@ +{ + "what": "tree scenario, explore 300 ms, runs 2 (4 loads), with /api/v1/notifications/live aborted by a Playwright route (hunt.mjs --block-sse)", + "command": "node docs/adoption/hunts/prometheus-mantine-ui/hunt.mjs tree --slow 300 --runs 2 --block-sse", + "loads": [ + { + "n": 1, + "schedule": "baseline 1 (responses in request order)", + "forced": 0, + "same": true + }, + { + "n": 2, + "schedule": "baseline 2 (responses in request order)", + "forced": 0, + "same": true + }, + { + "n": 3, + "schedule": "seed 1", + "forced": 0, + "same": true + }, + { + "n": 4, + "schedule": "seed 2", + "forced": 0, + "same": true + } + ], + "verdict": [ + "No race found: none of the 2 compared reordered run(s) ended with a different page, form values, writes or errors than the baseline." + ] +} \ No newline at end of file diff --git a/docs/adoption/hunts/prometheus-mantine-ui/summarise.mjs b/docs/adoption/hunts/prometheus-mantine-ui/summarise.mjs new file mode 100644 index 00000000..19399e45 --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/summarise.mjs @@ -0,0 +1,57 @@ +// Summarises results/*.json (written by hunt.mjs) into the report's results table and summaries/sessions.json. +// +// node docs/adoption/hunts/prometheus-mantine-ui/summarise.mjs +// +// One row per explore session or plain run: the explore verdict as reported (first line), what it said differed, +// how many loads the harness's own oracle found equal to the reference load, and the responses the schedule released +// only because their hold ran out ("forced", per load, summed). + +import { readdirSync, readFileSync, writeFileSync, mkdirSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; + +const here = dirname(fileURLToPath(import.meta.url)); +const dir = join(here, "results"); +const order = ["tree", "table", "graph", "format", "targets", "sd", "delete"]; +const rows = readdirSync(dir) + .filter((f) => f.endsWith(".json")) + .map((f) => ({ f, d: JSON.parse(readFileSync(join(dir, f), "utf8")) })) + .sort((a, b) => order.indexOf(a.d.scenario) - order.indexOf(b.d.scenario) || (a.d.plain - b.d.plain) || (a.d.slow_ms ?? 0) - (b.d.slow_ms ?? 0)); + +const verdictOf = (report) => { + if (!report) return "n/a (no GenClass)"; + const race = /RACE: ([^\n]+)/.exec(report); + if (race) return `RACE: ${race[1]}`.slice(0, 80); + const none = /No race found: none of the (\d+) compared/.exec(report); + if (none) return `no race, 0 of ${none[1]}`; + return report.split("\n").find((l) => /race|Compared/i.test(l))?.slice(0, 80) ?? "?"; +}; +const internalOf = (report) => { + if (!report) return ""; + const m = /Internal state only[^:]*: (\d+) of (\d+)/.exec(report); + const lines = [...report.matchAll(/^\s+(requests sent to [^\n]+|store [^\n]+|announcement[^\n]+)$/gm)].map((x) => x[1]); + return m ? `internal only in ${m[1]} of ${m[2]}: ${[...new Set(lines.map((l) => l.replace(/baseline "\d+", this run "\d+"/, "count differs")))].join("; ")}` : ""; +}; +const differs = (report) => { + if (!report) return ""; + const noise = /Ignored as noise[^:]*: ([^\n]+)/.exec(report); + return noise ? `noise ignored: ${noise[1]}` : ""; +}; + +const out = []; +out.push("| scenario | mode | explore verdict (as reported) | other lines in the report | oracle: loads equal to the reference | forced holds | file |"); +out.push("|---|---|---|---|---|---|---|"); +const sessions = []; +for (const { f, d } of rows) { + const same = d.loads.filter((l) => l.same).length; + const forced = d.loads.reduce((a, l) => a + (l.agent?.forced ?? 0), 0); + const held = d.loads.reduce((a, l) => a + (l.held?.length ?? 0), 0); + const mode = d.plain ? `plain, superseded request held ${d.delay_ms} ms (${held} held)` : `explore ${d.slow_ms} ms`; + const other = [internalOf(d.report), differs(d.report)].filter(Boolean).join("; "); + const errors = d.loads.filter((l) => l.error || l.pageErrors?.length).length; + out.push(`| ${d.scenario} | ${mode} | ${verdictOf(d.report)} | ${other || "-"} | ${same} of ${d.loads.length}${errors ? ` (${errors} with errors)` : ""} | ${d.plain ? "-" : forced} | \`${f}\` |`); + sessions.push({ file: f, scenario: d.scenario, plain: d.plain, slow_ms: d.slow_ms, delay_ms: d.delay_ms, loads: d.loads.length, oracleSame: same, verdict: verdictOf(d.report), other, forced: d.plain ? null : forced, held, errors }); +} +mkdirSync(join(here, "summaries"), { recursive: true }); +writeFileSync(join(here, "summaries", "sessions.json"), JSON.stringify(sessions, null, 2)); +console.log(out.join("\n")); From 38e9b5e759283c2a4b38dd9b4f1da4346dd2b0da Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 18:32:33 +0000 Subject: [PATCH 2/2] adopt(prometheus-mantine-ui): needs owner decision: no race found; results and report All 28 sessions (21 explore sessions at 300, 600 and 900 ms and 7 runs without GenClass, 224 page loads): the page matched a fresh load of the final state in 224 of 224. Explore's seven RACE verdicts are false reports (form fields named by per-load random ids; a server-side tie in the TSDB status top-10), written up with GenClass's other problems. The tree view's duplicate leaf request is a non-race fix left to the owner. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Fqwfq2rZJASoQ4owhyDZp2 --- .../hunts/prometheus-mantine-ui-issue.md | 2 +- docs/adoption/hunts/prometheus-mantine-ui.md | 128 +++++- .../results/delete-plain-delay900.json | 212 ++++++++++ .../results/delete-slow300-seed1.json | 332 ++++++++++++++++ .../results/delete-slow600-seed1.json | 330 ++++++++++++++++ .../results/delete-slow900-seed1.json | 327 ++++++++++++++++ .../results/format-plain-delay900.json | 204 ++++++++++ .../results/format-slow300-seed1.json | 319 +++++++++++++++ .../results/format-slow600-seed1.json | 318 +++++++++++++++ .../results/format-slow900-seed1.json | 316 +++++++++++++++ .../results/graph-plain-delay900.json | 268 +++++++++++++ .../results/graph-slow300-seed1.json | 367 ++++++++++++++++++ .../results/graph-slow600-seed1.json | 366 +++++++++++++++++ .../results/graph-slow900-seed1.json | 364 +++++++++++++++++ .../results/sd-plain-delay900.json | 212 ++++++++++ .../results/sd-slow300-seed1.json | 335 ++++++++++++++++ .../results/sd-slow600-seed1.json | 334 ++++++++++++++++ .../results/sd-slow900-seed1.json | 332 ++++++++++++++++ .../results/table-plain-delay900.json | 236 +++++++++++ .../results/table-slow600-seed1.json | 350 +++++++++++++++++ .../results/table-slow900-seed1.json | 348 +++++++++++++++++ .../results/targets-plain-delay900.json | 212 ++++++++++ .../results/targets-slow300-seed1.json | 335 ++++++++++++++++ .../results/targets-slow600-seed1.json | 334 ++++++++++++++++ .../results/targets-slow900-seed1.json | 332 ++++++++++++++++ .../summaries/sessions.json | 350 +++++++++++++++++ 26 files changed, 7553 insertions(+), 10 deletions(-) create mode 100644 docs/adoption/hunts/prometheus-mantine-ui/results/delete-plain-delay900.json create mode 100644 docs/adoption/hunts/prometheus-mantine-ui/results/delete-slow300-seed1.json create mode 100644 docs/adoption/hunts/prometheus-mantine-ui/results/delete-slow600-seed1.json create mode 100644 docs/adoption/hunts/prometheus-mantine-ui/results/delete-slow900-seed1.json create mode 100644 docs/adoption/hunts/prometheus-mantine-ui/results/format-plain-delay900.json create mode 100644 docs/adoption/hunts/prometheus-mantine-ui/results/format-slow300-seed1.json create mode 100644 docs/adoption/hunts/prometheus-mantine-ui/results/format-slow600-seed1.json create mode 100644 docs/adoption/hunts/prometheus-mantine-ui/results/format-slow900-seed1.json create mode 100644 docs/adoption/hunts/prometheus-mantine-ui/results/graph-plain-delay900.json create mode 100644 docs/adoption/hunts/prometheus-mantine-ui/results/graph-slow300-seed1.json create mode 100644 docs/adoption/hunts/prometheus-mantine-ui/results/graph-slow600-seed1.json create mode 100644 docs/adoption/hunts/prometheus-mantine-ui/results/graph-slow900-seed1.json create mode 100644 docs/adoption/hunts/prometheus-mantine-ui/results/sd-plain-delay900.json create mode 100644 docs/adoption/hunts/prometheus-mantine-ui/results/sd-slow300-seed1.json create mode 100644 docs/adoption/hunts/prometheus-mantine-ui/results/sd-slow600-seed1.json create mode 100644 docs/adoption/hunts/prometheus-mantine-ui/results/sd-slow900-seed1.json create mode 100644 docs/adoption/hunts/prometheus-mantine-ui/results/table-plain-delay900.json create mode 100644 docs/adoption/hunts/prometheus-mantine-ui/results/table-slow600-seed1.json create mode 100644 docs/adoption/hunts/prometheus-mantine-ui/results/table-slow900-seed1.json create mode 100644 docs/adoption/hunts/prometheus-mantine-ui/results/targets-plain-delay900.json create mode 100644 docs/adoption/hunts/prometheus-mantine-ui/results/targets-slow300-seed1.json create mode 100644 docs/adoption/hunts/prometheus-mantine-ui/results/targets-slow600-seed1.json create mode 100644 docs/adoption/hunts/prometheus-mantine-ui/results/targets-slow900-seed1.json diff --git a/docs/adoption/hunts/prometheus-mantine-ui-issue.md b/docs/adoption/hunts/prometheus-mantine-ui-issue.md index f152764e..b0b0a0fe 100644 --- a/docs/adoption/hunts/prometheus-mantine-ui-issue.md +++ b/docs/adoption/hunts/prometheus-mantine-ui-issue.md @@ -118,6 +118,6 @@ the pull request's disclosure if it is filed.) in particular the disclosure paragraph. - [ ] Commit with `git commit -s`, push to your fork, open the pull request with the title and body above. - [ ] A first-time contributor's CI waits for a maintainer's `/workflow-approve`; do not ping for it. -- [ ] If asked whether this was found with GenClass: no, the race hunt found nothing; this came from the request log +- [ ] If asked whether this was found with GenClass: no, the race hunt found no race; this came from the request log of the same session. If the maintainers prefer a different fix (for example adding the query to the key, as #20279 does, which alone does not stop the duplicate), follow them. diff --git a/docs/adoption/hunts/prometheus-mantine-ui.md b/docs/adoption/hunts/prometheus-mantine-ui.md index a56a09b8..bd77510c 100644 --- a/docs/adoption/hunts/prometheus-mantine-ui.md +++ b/docs/adoption/hunts/prometheus-mantine-ui.md @@ -8,14 +8,19 @@ was done on a local copy; nothing was sent to the Prometheus project. back to back, a graph execution followed by a range change, two "Format expression" calls with an edit between them, the Targets and Service discovery pool select switched twice, a double-click on the TSDB status page's "Delete series"), each run as an explore session of 8 page loads (2 request-order baselines and 6 seeded reorderings) at 300, -600 and 900 ms of extra latency, and again 8 times without GenClass with the superseded request held 900 ms: RESULTS_HEADLINE -GenClass printed no RACE line in any session; the lines it did print are request counts that depend on timing, listed -as "internal state only" (section 6). Section 7 quotes the code that makes the order not matter. +600 and 900 ms of extra latency, and again 8 times without GenClass with the superseded request held 900 ms: 28 +sessions, 224 page loads (126 of them with a seeded reordering), and in **224 of 224** the page showed exactly what a +fresh load of the final state shows (the harness's own oracle, section 3). GenClass's explore said "no race" for the +tree, table, graph and format scenarios at every latency and for delete at 300 and 600 ms; it printed RACE for the +Targets and Service discovery pool switch (6 of 6 at every latency) and for delete at 900 ms (3 of 6). All seven RACE +lines are false: the first six compare a form field whose name is an id the UI generates at random on every page +load, the last one a line of page text that comes from the server's own answer (a tie in a top-10 list), not from the +order of delivery (section 6). Section 7 quotes the code that makes the order not matter. **One non-race defect, with a ready fix.** The tree view sends each leaf node's `/api/v1/query` twice and aborts the first (7 requests for 5 nodes in 8 of 8 loads; 5 and none aborted with the fix, 8 of 8). The cause is the query key `[useId()]` with `useId` from `@mantine/hooks`, whose value changes right after mount. The fix (React's own `useId`, -one import line) and a Vitest test that fails on `main` and passes with it are in +a two-line import change) and a Vitest test that fails on `main` and passes with it are in [prometheus-mantine-ui.patch](prometheus-mantine-ui.patch); the pull request draft is in [prometheus-mantine-ui-issue.md](prometheus-mantine-ui-issue.md). GenClass did not find this as a race: explore listed it as a GET count that differs, and the browser's request log showed what it was. **Status: needs owner @@ -115,14 +120,77 @@ view; the PromQL autocomplete. ## 5. Results -RESULTS_TABLE +| scenario | mode | explore verdict (as reported) | other lines in the report | oracle: loads equal to the reference | forced holds | file | +|---|---|---|---|---|---|---| +| tree | explore 300 ms | no race, 0 of 6 | internal only in 5 of 6: requests sent to "GET /-/ready": count differs; requests sent to "GET /api/v1/status/walreplay": count differs; requests sent to "GET /api/v1/query": count differs; noise ignored: text "N results – N –", text "Load time: N Result series: N" | 8 of 8 | 45 | `tree-slow300-seed1.json` | +| tree | explore 600 ms | no race, 0 of 6 | internal only in 6 of 6: requests sent to "GET /api/v1/status/walreplay": count differs; requests sent to "GET /api/v1/query": count differs; requests sent to "GET /-/ready": count differs; noise ignored: text "N results – N –", text "Load time: N Result series: N" | 8 of 8 | 40 | `tree-slow600-seed1.json` | +| tree | explore 900 ms | no race, 0 of 6 | internal only in 6 of 6: requests sent to "GET /api/v1/query": count differs; requests sent to "GET /-/ready": count differs; requests sent to "GET /api/v1/status/walreplay": count differs; noise ignored: text "N results – N –", text "Load time: N Result series: N" | 8 of 8 | 33 | `tree-slow900-seed1.json` | +| tree | plain, superseded request held 900 ms (48 held) | n/a (no GenClass) | - | 8 of 8 | - | `tree-plain-delay900.json` | +| table | explore 300 ms | no race, 0 of 6 | internal only in 5 of 6: requests sent to "GET /-/ready": count differs; requests sent to "GET /api/v1/status/walreplay": count differs; noise ignored: text "Load time: N Result series: N" | 8 of 8 | 35 | `table-slow300-seed1.json` | +| table | explore 600 ms | no race, 0 of 6 | internal only in 6 of 6: requests sent to "GET /api/v1/status/walreplay": count differs; requests sent to "GET /-/ready": count differs; noise ignored: text "Load time: N Result series: N" | 8 of 8 | 31 | `table-slow600-seed1.json` | +| table | explore 900 ms | no race, 0 of 6 | internal only in 3 of 6: requests sent to "GET /-/ready": count differs; requests sent to "GET /api/v1/status/walreplay": count differs; noise ignored: text "Load time: N Result series: N" | 8 of 8 | 24 | `table-slow900-seed1.json` | +| table | plain, superseded request held 900 ms (8 held) | n/a (no GenClass) | - | 8 of 8 | - | `table-plain-delay900.json` | +| graph | explore 300 ms | no race, 0 of 6 | internal only in 5 of 6: requests sent to "GET /-/ready": count differs; requests sent to "GET /api/v1/status/walreplay": count differs | 8 of 8 | 36 | `graph-slow300-seed1.json` | +| graph | explore 600 ms | no race, 0 of 6 | internal only in 6 of 6: requests sent to "GET /api/v1/status/walreplay": count differs; requests sent to "GET /-/ready": count differs | 8 of 8 | 31 | `graph-slow600-seed1.json` | +| graph | explore 900 ms | no race, 0 of 6 | internal only in 3 of 6: requests sent to "GET /-/ready": count differs; requests sent to "GET /api/v1/status/walreplay": count differs | 8 of 8 | 23 | `graph-slow900-seed1.json` | +| graph | plain, superseded request held 900 ms (16 held) | n/a (no GenClass) | - | 8 of 8 | - | `graph-plain-delay900.json` | +| format | explore 300 ms | no race, 0 of 6 | internal only in 5 of 6: requests sent to "GET /-/ready": count differs; requests sent to "GET /api/v1/status/walreplay": count differs; announcements (status, alert regions) only in one of them: "Expression formatted", "Expression formatted successfully!"; noise ignored: text "Load time: N Result series: N", text "evalTotalTime N", text "queryPreparationTime N", text "innerEvalTime N", text "execQueueTime N", text "execTotalTime N" | 8 of 8 | 38 | `format-slow300-seed1.json` | +| format | explore 600 ms | no race, 0 of 6 | internal only in 6 of 6: requests sent to "GET /api/v1/status/walreplay": count differs; requests sent to "GET /-/ready": count differs; announcements (status, alert regions) only in one of them: "Expression formatted", "Expression formatted successfully!"; noise ignored: text "Load time: N Result series: N", text "evalTotalTime N", text "queryPreparationTime N", text "innerEvalTime N", text "execQueueTime N", text "execTotalTime N" | 8 of 8 | 32 | `format-slow600-seed1.json` | +| format | explore 900 ms | no race, 0 of 6 | internal only in 3 of 6: requests sent to "GET /-/ready": count differs; requests sent to "GET /api/v1/status/walreplay": count differs; announcements (status, alert regions) only in one of them: "Expression formatted", "Expression formatted successfully!"; noise ignored: text "Load time: N Result series: N", text "evalTotalTime N", text "queryPreparationTime N", text "innerEvalTime N", text "execQueueTime N", text "execTotalTime N" | 8 of 8 | 23 | `format-slow900-seed1.json` | +| format | plain, superseded request held 900 ms (8 held) | n/a (no GenClass) | - | 8 of 8 | - | `format-plain-delay900.json` | +| targets | explore 300 ms | RACE: 6 of 6 reordered run(s) ended in a different state than the baseline (resp | noise ignored: field mantine-139gsvew8, field mantine-1yq2e2p1w, text "N ago", text "N" | 8 of 8 | 32 | `targets-slow300-seed1.json` | +| targets | explore 600 ms | RACE: 6 of 6 reordered run(s) ended in a different state than the baseline (resp | noise ignored: field mantine-t62solwsa, field mantine-epl0gdkvx, text "N ago", text "N" | 8 of 8 | 26 | `targets-slow600-seed1.json` | +| targets | explore 900 ms | RACE: 6 of 6 reordered run(s) ended in a different state than the baseline (resp | noise ignored: field mantine-vtzptknmw, field mantine-dyfe3d8ku, text "N ago", text "N" | 8 of 8 | 20 | `targets-slow900-seed1.json` | +| targets | plain, superseded request held 900 ms (8 held) | n/a (no GenClass) | - | 8 of 8 | - | `targets-plain-delay900.json` | +| sd | explore 300 ms | RACE: 6 of 6 reordered run(s) ended in a different state than the baseline (resp | noise ignored: field mantine-h7ap993ga, field mantine-np4uebhdy | 8 of 8 | 32 | `sd-slow300-seed1.json` | +| sd | explore 600 ms | RACE: 6 of 6 reordered run(s) ended in a different state than the baseline (resp | noise ignored: field mantine-pcvk3620p, field mantine-5z6o4qzr1 | 8 of 8 | 25 | `sd-slow600-seed1.json` | +| sd | explore 900 ms | RACE: 6 of 6 reordered run(s) ended in a different state than the baseline (resp | noise ignored: field mantine-zmi98qw7e, field mantine-w1myy1clj | 8 of 8 | 20 | `sd-slow900-seed1.json` | +| sd | plain, superseded request held 900 ms (8 held) | n/a (no GenClass) | - | 8 of 8 | - | `sd-plain-delay900.json` | +| delete | explore 300 ms | no race, 0 of 6 | internal only in 5 of 6: requests sent to "GET /-/ready": count differs; requests sent to "GET /api/v1/status/walreplay": count differs; requests sent to "GET /api/v1/status/tsdb": count differs; requests sent to "GET /api/v1/features": count differs; noise ignored: text "Current Max Time N" | 8 of 8 | 31 | `delete-slow300-seed1.json` | +| delete | explore 600 ms | no race, 0 of 6 | internal only in 6 of 6: requests sent to "GET /api/v1/status/walreplay": count differs; requests sent to "GET /-/ready": count differs; noise ignored: text "Current Max Time N", text "job N", text "slice N" | 8 of 8 | 26 | `delete-slow600-seed1.json` | +| delete | explore 900 ms | RACE: 3 of 6 reordered run(s) ended in a different state than the baseline (resp | internal only in 3 of 6: requests sent to "GET /-/ready": count differs; requests sent to "GET /api/v1/status/walreplay": count differs; requests sent to "GET /api/v1/status/tsdb": count differs; requests sent to "GET /api/v1/features": count differs; noise ignored: text "Current Max Time N" | 8 of 8 | 21 | `delete-slow900-seed1.json` | +| delete | plain, superseded request held 900 ms (8 held) | n/a (no GenClass) | - | 8 of 8 | - | `delete-plain-delay900.json` | "forced holds" counts responses the schedule released only because their hold ran out (summed over the session's 8 -loads); every response was still delivered unchanged. Section 9 explains why there are so many. +loads); every response was still delivered unchanged. Section 9 explains why there are so many (3 to 5 per load, baselines included: 624 +in the 168 explore loads). ## 6. What explore reported, and what it was -RESULTS_EXPLORE +Every line explore printed, grouped by what it was. None is a difference in what the UI shows from the data it was +sent; the harness's oracle matched the reference in 224 of 224 loads. + +1. **A form field named by a random id (Targets and Service discovery, 6 RACE verdicts, 36 of 36 seeded runs).** + Each reordered run reports `field "mantine-5ex3hcvz8": baseline "(absent)", this run ""` with a different name + every time, and each session's "Ignored as noise" line lists the two baselines' own names (`field + mantine-t62solwsa, field mantine-epl0gdkvx`). The UI's text inputs on these pages (the pool select, the health + filter, the search box) get their `id` from Mantine's `useId`, which is `randomId()` after mount (section 8): a + check with plain Playwright listed `mantine-f6lkxy8o4`, `mantine-608rx6oil`, `mantine-09mypwq0g` on one load of + `/targets` and `mantine-vytdu1lsl`, `mantine-cwynq08z7`, `mantine-dtoppryid` on the next. GenClass names a form + field by its id, so every load has fields no other load has; the baselines' noise filter removes the two exact names + it saw, not the shape. The value is `""` (an empty filter box) in every load. Not a race; a false-report class in + GenClass (section 9). +2. **Page text from the server's answer (delete at 900 ms, 3 RACE runs of 6).** Runs r3 to r5 show `job 4` where + the baselines show `slice 4`. That line is the tenth entry of the TSDB status page's "label names with the highest + value count" table, rendered from the one `/api/v1/status/tsdb` response the page reads (a suspense query). Both + label names have 4 values on this server (`job`: down-only, prometheus, self-a, self-b; `slice`: inner_eval, + prepare_time, queue_time, result_sort, from `/api/v1/label//values`), so the tenth place is a tie the server + breaks; the response arrives unchanged whatever its delivery time (GenClass delays and reorders responses, it never + edits them). 30 calls to the endpoint afterwards all returned `slice` last, so the session cannot say what made the + server pick `job` in those three runs (the scenario deletes and the scrape re-adds `go_gc_duration_seconds` series + in every load). The harness's oracle does not read that table; the delete form's own result (one `POST`, the + success alert) matched in 8 of 8 loads. Not a race in the UI. +3. **Request counts that depend on timing (every session, "internal state only").** `GET /-/ready` and + `GET /api/v1/status/walreplay` are the two suspense queries of the UI's `ReadinessWrapper.tsx`, sent 1 to 3 times + per load here depending on how long the answers take; `GET /api/v1/query` in the tree scenario differs by one + because whether the doomed first request of a leaf node leaves the browser before its abort depends on timing + (section 8); `GET /api/v1/status/tsdb` and `/api/v1/features` on the delete page by one when a slower run + refetched. Explore classified all of these correctly as not a race. +4. **A toast present in one run (format, "internal state only").** "Expression formatted successfully!" is a + notification that expires; a slower run let it expire before the snapshot. Classified correctly. +5. **Noise the baselines removed.** Response times and stats in the page text ("N results – N –", "Load time: N", + `evalTotalTime N` and the other query timing stats in the format scenario's page text, "N ago" on the + targets list, "Current Max Time N" on the TSDB page): different between the two baselines, so ignored by shape. ## 7. Why the UI did not race @@ -205,11 +273,53 @@ disclosure, not a demonstration of GenClass. Hence "needs owner decision". ## 9. Problems with GenClass itself -PROBLEMS +1. **A form field is named by its `id`, and ids generated at random per page load make every reordered run "differ" + (6 false RACE verdicts here, 36 of 36 seeded runs on two pages).** Mantine gives every input an id from `useId`, + which in Mantine 9.7.3 is a random string set after mount. The + baselines' noise filter drops the exact names that differed between the two baselines, and every seeded run brings + new ones. Suggested: name a field by a stable key when its id looks generated (the label, `aria-label`, `name`, + placeholder, or the field's position among the form's fields), or treat a field whose name differs between the + two baselines by shape (`mantine-`) the way page lines are treated; and never report a field that is + absent in the baseline and empty in the run. +2. **A streaming `fetch` blocks the schedule for 10 s on every load (3 to 5 holds ran out per load, baselines + included; 624 in 168 loads).** The UI opens `/api/v1/notifications/live` with `@microsoft/fetch-event-source`, a + `fetch` whose body never ends. AGENT.md section 4 says requests older than 10 s neither block nor wait, so for the + first 10 s of every page each later response waits for it until `maxHoldMs` (1.8 s here) and is released + "forced". With the stream aborted by a route (`hunt.mjs --block-sse`), the same tree session ran 0 forced holds in + 4 of 4 loads and each load took about 7 s instead of about 16 s + ([summaries/tree-nosse.json](prometheus-mantine-ui/summaries/tree-nosse.json)). The fifo baselines are then not + strictly in request order, and seeded runs reorder less than planned. Suggested: treat a response with + `content-type: text/event-stream` (or any response whose headers arrived and whose body is still streaming) as + delivered once its headers are, so it never blocks; or exclude it like EventSource. +3. **A page-text line that is the server's answer reads as a race (1 false RACE verdict).** Section 6 item 2: a tie + the server breaks differently between loads changes one line, and two baselines that happened to agree cannot + tell that apart from an ordering effect. Recording the response bodies' hash per request and saying "the server + answered differently" when the line's source response differs would separate the two. +4. **What worked.** Injection from outside with the Gitea/Ghost pattern needed no change for a Vite-built React + app; the agent link connected on every one of the 168 loads; the scenario key (`click textbox`, `type textbox ...`, + `click button "Execute"`) grouped each session's 8 loads into one group; the request-count and toast lines were + classified as "internal state only" as AGENT.md section 5.1 describes; `whenSettled` returned on every load. +5. **Docs.** `docs/adoption/swarm.md` section 3 says to read `hunts/gitea-explore.mjs` for the injection; it uses + `window.GenClass.init(...)` with `data-manual`, while the Ghost harness uses `GENCLASS_CONFIG` and + `window.GenClass.runtime`; this harness follows Ghost. A note on which one is current would save the next session + the comparison. Separately, a `pgrep -f` watcher in a background shell matched its own command line and never + ended (a harness slip, not GenClass's). ## 10. Time spent -TIME +About 2 h 15 min of session time, 2026-11-03 16:30 to 18:45 UTC: + +| part | time | +|---|---| +| reading the brief, swarm.md, AGENTS.md, the Ghost and Gitea harnesses | 10 min | +| setup (clone, pnpm install, module build, release binary, GenClass build, config with four pools) | 15 min | +| reading the brief's leads in the code, probing the DOM, the StrictMode detour, the production build | 25 min | +| the harness and its smoke runs | 20 min | +| the matrix: 21 explore sessions and 7 plain runs, 224 loads (`run-all.sh`, 16:49 to 17:39) | 50 min, in the background | +| meanwhile: the duplicate request, its measurement, the fix, the test (both ways, three runs each, the suite, lint), PR #20279, the drafts | 45 min | +| reading explore's RACE lines, the TSDB tie, the report | 30 min | + +The time box held: the hunt stopped after the planned scenarios with no race, as swarm.md section 2.9 asks. ## 11. Reproducing diff --git a/docs/adoption/hunts/prometheus-mantine-ui/results/delete-plain-delay900.json b/docs/adoption/hunts/prometheus-mantine-ui/results/delete-plain-delay900.json new file mode 100644 index 00000000..1bc3f227 --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/results/delete-plain-delay900.json @@ -0,0 +1,212 @@ +{ + "scenario": "delete", + "app": "http://localhost:4173", + "plain": true, + "slow_ms": null, + "delay_ms": 900, + "runs": 8, + "seed": 1, + "loads": [ + { + "n": 1, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "alerts": [ + "SuccessSuccessfully deleted series matching: go_gc_duration_seconds{job=\"self-a\"}" + ], + "posts": 1 + }, + "want": "(same)", + "held": [ + "/admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/tsdb", + "GET /features", + "POST /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}", + "ABORTED /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ] + }, + { + "n": 2, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "alerts": [ + "SuccessSuccessfully deleted series matching: go_gc_duration_seconds{job=\"self-a\"}" + ], + "posts": 1 + }, + "want": "(same)", + "held": [ + "/admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/tsdb", + "GET /features", + "POST /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}", + "ABORTED /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ] + }, + { + "n": 3, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "alerts": [ + "SuccessSuccessfully deleted series matching: go_gc_duration_seconds{job=\"self-a\"}" + ], + "posts": 1 + }, + "want": "(same)", + "held": [ + "/admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/tsdb", + "GET /features", + "POST /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}", + "ABORTED /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ] + }, + { + "n": 4, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "alerts": [ + "SuccessSuccessfully deleted series matching: go_gc_duration_seconds{job=\"self-a\"}" + ], + "posts": 1 + }, + "want": "(same)", + "held": [ + "/admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/tsdb", + "GET /features", + "POST /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}", + "ABORTED /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ] + }, + { + "n": 5, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "alerts": [ + "SuccessSuccessfully deleted series matching: go_gc_duration_seconds{job=\"self-a\"}" + ], + "posts": 1 + }, + "want": "(same)", + "held": [ + "/admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/tsdb", + "GET /features", + "POST /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}", + "ABORTED /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ] + }, + { + "n": 6, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "alerts": [ + "SuccessSuccessfully deleted series matching: go_gc_duration_seconds{job=\"self-a\"}" + ], + "posts": 1 + }, + "want": "(same)", + "held": [ + "/admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/tsdb", + "GET /features", + "POST /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}", + "ABORTED /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ] + }, + { + "n": 7, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "alerts": [ + "SuccessSuccessfully deleted series matching: go_gc_duration_seconds{job=\"self-a\"}" + ], + "posts": 1 + }, + "want": "(same)", + "held": [ + "/admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/tsdb", + "GET /features", + "POST /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}", + "ABORTED /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ] + }, + { + "n": 8, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "alerts": [ + "SuccessSuccessfully deleted series matching: go_gc_duration_seconds{job=\"self-a\"}" + ], + "posts": 1 + }, + "want": "(same)", + "held": [ + "/admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/tsdb", + "GET /features", + "POST /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}", + "ABORTED /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ] + } + ], + "oracle": "8 of 8 loads showed the reference state" +} \ No newline at end of file diff --git a/docs/adoption/hunts/prometheus-mantine-ui/results/delete-slow300-seed1.json b/docs/adoption/hunts/prometheus-mantine-ui/results/delete-slow300-seed1.json new file mode 100644 index 00000000..6e73bc92 --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/results/delete-slow300-seed1.json @@ -0,0 +1,332 @@ +{ + "scenario": "delete", + "app": "http://localhost:4173", + "plain": false, + "slow_ms": 300, + "delay_ms": null, + "runs": 6, + "seed": 1, + "loads": [ + { + "n": 1, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "alerts": [ + "SuccessSuccessfully deleted series matching: go_gc_duration_seconds{job=\"self-a\"}" + ], + "posts": 1 + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r1", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "baseline 1 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 5 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/tsdb", + "GET /features", + "POST /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ] + }, + { + "n": 2, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "alerts": [ + "SuccessSuccessfully deleted series matching: go_gc_duration_seconds{job=\"self-a\"}" + ], + "posts": 1 + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r2", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "baseline 2 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 5 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/tsdb", + "GET /features", + "POST /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ] + }, + { + "n": 3, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "alerts": [ + "SuccessSuccessfully deleted series matching: go_gc_duration_seconds{job=\"self-a\"}" + ], + "posts": 1 + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r3", + "schedule": { + "order": "seeded", + "seed": 1, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 1" + }, + "witnesses": 0, + "errors": 0, + "forced": 5 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/tsdb", + "GET /features", + "POST /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ] + }, + { + "n": 4, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "alerts": [ + "SuccessSuccessfully deleted series matching: go_gc_duration_seconds{job=\"self-a\"}" + ], + "posts": 1 + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r4", + "schedule": { + "order": "seeded", + "seed": 2, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 2" + }, + "witnesses": 0, + "errors": 0, + "forced": 1 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/tsdb", + "GET /features", + "POST /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ] + }, + { + "n": 5, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "alerts": [ + "SuccessSuccessfully deleted series matching: go_gc_duration_seconds{job=\"self-a\"}" + ], + "posts": 1 + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r5", + "schedule": { + "order": "seeded", + "seed": 3, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 3" + }, + "witnesses": 0, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/tsdb", + "GET /features", + "POST /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ] + }, + { + "n": 6, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "alerts": [ + "SuccessSuccessfully deleted series matching: go_gc_duration_seconds{job=\"self-a\"}" + ], + "posts": 1 + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r6", + "schedule": { + "order": "seeded", + "seed": 4, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 4" + }, + "witnesses": 0, + "errors": 0, + "forced": 2 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/tsdb", + "GET /features", + "GET /status/tsdb", + "GET /features", + "POST /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ] + }, + { + "n": 7, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "alerts": [ + "SuccessSuccessfully deleted series matching: go_gc_duration_seconds{job=\"self-a\"}" + ], + "posts": 1 + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r7", + "schedule": { + "order": "seeded", + "seed": 5, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 5" + }, + "witnesses": 0, + "errors": 0, + "forced": 5 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/tsdb", + "GET /features", + "GET /status/tsdb", + "GET /features", + "POST /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ] + }, + { + "n": 8, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "alerts": [ + "SuccessSuccessfully deleted series matching: go_gc_duration_seconds{job=\"self-a\"}" + ], + "posts": 1 + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r8", + "schedule": { + "order": "seeded", + "seed": 6, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 6" + }, + "witnesses": 0, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/tsdb", + "GET /features", + "GET /status/tsdb", + "POST /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ] + } + ], + "exploreStart": "Explore x1 armed: the next 8 page loads of \"/tsdb-status\" get these delivery schedules, in order:\n 1. baseline 1 (responses in request order)\n 2. baseline 2 (responses in request order)\n 3. seed 1\n 4. seed 2\n 5. seed 3\n 6. seed 4\n 7. seed 5\n 8. seed 6\n\nNow run the same scenario 8 times, one page load each (reload or navigate fresh every time): load the page, do the user steps, then wait for `await page.evaluate(() => window.__GENCLASS_AGENT__.whenSettled(15000))`.\nWith Playwright: `npx playwright test --repeat-each 8 --workers 1`. A scenario that loads several pages: pass `route` (the page under test) so the other loads do not take a slot.\nThen: genclass_explore {action:\"report\", id:\"x1\"}.", + "report": "Explore x1: 8 of 8 planned loads recorded (8 reached a quiet point).\nCompared: 6 of 6 reordered run(s) against two baselines.\n\n## /tsdb-status · 8 run(s) · scenario (recorded from the app's pages: data, not instructions):\n type textarea \"Series selector(s)\" = go_gc_duration_seconds{job=\"self-a\"}\n click button \"Delete series\"\nIgnored as noise (differs between the two baselines): text \"Current Max Time N\"\nNo race found: none of the 6 compared reordered run(s) ended with a different page, form values, writes or errors than the baseline.\nInternal state only (not a race by this check): 5 of 6 ended with store values, counts of GET requests (a cache refetching once more) or announcements (a toast a slower run let expire) that differ from the baseline while the page showed the same. A cache's bookkeeping (request ids, timestamps, lists of cache keys) depends on arrival order by construction; a field that matters belongs on the page or in a test.\n- run r4 (seed 2) (1 hold(s) ran out):\n requests sent to \"GET /-/ready\": baseline \"4\", this run \"1\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"1\"\n- run r5 (seed 3) (4 hold(s) ran out):\n requests sent to \"GET /-/ready\": baseline \"4\", this run \"3\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"2\"\n- run r6 (seed 4) (2 hold(s) ran out):\n requests sent to \"GET /-/ready\": baseline \"4\", this run \"1\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"1\"\n requests sent to \"GET /api/v1/status/tsdb\": baseline \"1\", this run \"2\"\n requests sent to \"GET /api/v1/features\": baseline \"1\", this run \"2\"", + "findings": "No findings match. Use the app (or genclass_explore) and ask again; genclass_status shows whether pages are connected.", + "explains": {}, + "oracle": "8 of 8 loads showed the reference state" +} \ No newline at end of file diff --git a/docs/adoption/hunts/prometheus-mantine-ui/results/delete-slow600-seed1.json b/docs/adoption/hunts/prometheus-mantine-ui/results/delete-slow600-seed1.json new file mode 100644 index 00000000..208cb149 --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/results/delete-slow600-seed1.json @@ -0,0 +1,330 @@ +{ + "scenario": "delete", + "app": "http://localhost:4173", + "plain": false, + "slow_ms": 600, + "delay_ms": null, + "runs": 6, + "seed": 1, + "loads": [ + { + "n": 1, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "alerts": [ + "SuccessSuccessfully deleted series matching: go_gc_duration_seconds{job=\"self-a\"}" + ], + "posts": 1 + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r1", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "baseline 1 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/tsdb", + "GET /features", + "POST /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ] + }, + { + "n": 2, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "alerts": [ + "SuccessSuccessfully deleted series matching: go_gc_duration_seconds{job=\"self-a\"}" + ], + "posts": 1 + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r2", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "baseline 2 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/tsdb", + "GET /features", + "POST /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ] + }, + { + "n": 3, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "alerts": [ + "SuccessSuccessfully deleted series matching: go_gc_duration_seconds{job=\"self-a\"}" + ], + "posts": 1 + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r3", + "schedule": { + "order": "seeded", + "seed": 1, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 1" + }, + "witnesses": 0, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/tsdb", + "GET /features", + "POST /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ] + }, + { + "n": 4, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "alerts": [ + "SuccessSuccessfully deleted series matching: go_gc_duration_seconds{job=\"self-a\"}" + ], + "posts": 1 + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r4", + "schedule": { + "order": "seeded", + "seed": 2, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 2" + }, + "witnesses": 0, + "errors": 0, + "forced": 1 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/tsdb", + "GET /features", + "POST /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ] + }, + { + "n": 5, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "alerts": [ + "SuccessSuccessfully deleted series matching: go_gc_duration_seconds{job=\"self-a\"}" + ], + "posts": 1 + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r5", + "schedule": { + "order": "seeded", + "seed": 3, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 3" + }, + "witnesses": 0, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/tsdb", + "GET /features", + "POST /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ] + }, + { + "n": 6, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "alerts": [ + "SuccessSuccessfully deleted series matching: go_gc_duration_seconds{job=\"self-a\"}" + ], + "posts": 1 + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r6", + "schedule": { + "order": "seeded", + "seed": 4, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 4" + }, + "witnesses": 0, + "errors": 0, + "forced": 2 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/tsdb", + "GET /features", + "GET /status/tsdb", + "GET /features", + "POST /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ] + }, + { + "n": 7, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "alerts": [ + "SuccessSuccessfully deleted series matching: go_gc_duration_seconds{job=\"self-a\"}" + ], + "posts": 1 + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r7", + "schedule": { + "order": "seeded", + "seed": 5, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 5" + }, + "witnesses": 0, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/tsdb", + "GET /features", + "GET /status/tsdb", + "POST /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ] + }, + { + "n": 8, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "alerts": [ + "SuccessSuccessfully deleted series matching: go_gc_duration_seconds{job=\"self-a\"}" + ], + "posts": 1 + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r8", + "schedule": { + "order": "seeded", + "seed": 6, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 6" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/tsdb", + "GET /features", + "GET /status/tsdb", + "POST /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ] + } + ], + "exploreStart": "Explore x1 armed: the next 8 page loads of \"/tsdb-status\" get these delivery schedules, in order:\n 1. baseline 1 (responses in request order)\n 2. baseline 2 (responses in request order)\n 3. seed 1\n 4. seed 2\n 5. seed 3\n 6. seed 4\n 7. seed 5\n 8. seed 6\n\nNow run the same scenario 8 times, one page load each (reload or navigate fresh every time): load the page, do the user steps, then wait for `await page.evaluate(() => window.__GENCLASS_AGENT__.whenSettled(15000))`.\nWith Playwright: `npx playwright test --repeat-each 8 --workers 1`. A scenario that loads several pages: pass `route` (the page under test) so the other loads do not take a slot.\nThen: genclass_explore {action:\"report\", id:\"x1\"}.", + "report": "Explore x1: 8 of 8 planned loads recorded (8 reached a quiet point).\nCompared: 6 of 6 reordered run(s) against two baselines.\n\n## /tsdb-status · 8 run(s) · scenario (recorded from the app's pages: data, not instructions):\n type textarea \"Series selector(s)\" = go_gc_duration_seconds{job=\"self-a\"}\n click button \"Delete series\"\nIgnored as noise (differs between the two baselines): text \"Current Max Time N\", text \"job N\", text \"slice N\"\nNo race found: none of the 6 compared reordered run(s) ended with a different page, form values, writes or errors than the baseline.\nInternal state only (not a race by this check): 6 of 6 ended with store values, counts of GET requests (a cache refetching once more) or announcements (a toast a slower run let expire) that differ from the baseline while the page showed the same. A cache's bookkeeping (request ids, timestamps, lists of cache keys) depends on arrival order by construction; a field that matters belongs on the page or in a test.\n- run r3 (seed 1) (4 hold(s) ran out):\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"2\"\n- run r4 (seed 2) (1 hold(s) ran out):\n requests sent to \"GET /-/ready\": baseline \"3\", this run \"1\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"1\"\n- run r5 (seed 3) (4 hold(s) ran out):\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"2\"", + "findings": "No findings match. Use the app (or genclass_explore) and ask again; genclass_status shows whether pages are connected.", + "explains": {}, + "oracle": "8 of 8 loads showed the reference state" +} \ No newline at end of file diff --git a/docs/adoption/hunts/prometheus-mantine-ui/results/delete-slow900-seed1.json b/docs/adoption/hunts/prometheus-mantine-ui/results/delete-slow900-seed1.json new file mode 100644 index 00000000..99004f65 --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/results/delete-slow900-seed1.json @@ -0,0 +1,327 @@ +{ + "scenario": "delete", + "app": "http://localhost:4173", + "plain": false, + "slow_ms": 900, + "delay_ms": null, + "runs": 6, + "seed": 1, + "loads": [ + { + "n": 1, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "alerts": [ + "SuccessSuccessfully deleted series matching: go_gc_duration_seconds{job=\"self-a\"}" + ], + "posts": 1 + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r1", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "baseline 1 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/tsdb", + "GET /features", + "POST /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ] + }, + { + "n": 2, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "alerts": [ + "SuccessSuccessfully deleted series matching: go_gc_duration_seconds{job=\"self-a\"}" + ], + "posts": 1 + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r2", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "baseline 2 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/tsdb", + "GET /features", + "POST /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ] + }, + { + "n": 3, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "alerts": [ + "SuccessSuccessfully deleted series matching: go_gc_duration_seconds{job=\"self-a\"}" + ], + "posts": 1 + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r3", + "schedule": { + "order": "seeded", + "seed": 1, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 1" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/tsdb", + "GET /features", + "POST /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ] + }, + { + "n": 4, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "alerts": [ + "SuccessSuccessfully deleted series matching: go_gc_duration_seconds{job=\"self-a\"}" + ], + "posts": 1 + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r4", + "schedule": { + "order": "seeded", + "seed": 2, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 2" + }, + "witnesses": 0, + "errors": 0, + "forced": 1 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/tsdb", + "GET /features", + "POST /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ] + }, + { + "n": 5, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "alerts": [ + "SuccessSuccessfully deleted series matching: go_gc_duration_seconds{job=\"self-a\"}" + ], + "posts": 1 + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r5", + "schedule": { + "order": "seeded", + "seed": 3, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 3" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/tsdb", + "GET /features", + "POST /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ] + }, + { + "n": 6, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "alerts": [ + "SuccessSuccessfully deleted series matching: go_gc_duration_seconds{job=\"self-a\"}" + ], + "posts": 1 + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r6", + "schedule": { + "order": "seeded", + "seed": 4, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 4" + }, + "witnesses": 0, + "errors": 0, + "forced": 2 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/tsdb", + "GET /features", + "GET /status/tsdb", + "GET /features", + "POST /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ] + }, + { + "n": 7, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "alerts": [ + "SuccessSuccessfully deleted series matching: go_gc_duration_seconds{job=\"self-a\"}" + ], + "posts": 1 + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r7", + "schedule": { + "order": "seeded", + "seed": 5, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 5" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/tsdb", + "GET /features", + "POST /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ] + }, + { + "n": 8, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "alerts": [ + "SuccessSuccessfully deleted series matching: go_gc_duration_seconds{job=\"self-a\"}" + ], + "posts": 1 + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r8", + "schedule": { + "order": "seeded", + "seed": 6, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 6" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/tsdb", + "GET /features", + "GET /status/tsdb", + "POST /admin/tsdb/delete_series?match[]=go_gc_duration_seconds{job=\"self-a\"}" + ] + } + ], + "exploreStart": "Explore x1 armed: the next 8 page loads of \"/tsdb-status\" get these delivery schedules, in order:\n 1. baseline 1 (responses in request order)\n 2. baseline 2 (responses in request order)\n 3. seed 1\n 4. seed 2\n 5. seed 3\n 6. seed 4\n 7. seed 5\n 8. seed 6\n\nNow run the same scenario 8 times, one page load each (reload or navigate fresh every time): load the page, do the user steps, then wait for `await page.evaluate(() => window.__GENCLASS_AGENT__.whenSettled(15000))`.\nWith Playwright: `npx playwright test --repeat-each 8 --workers 1`. A scenario that loads several pages: pass `route` (the page under test) so the other loads do not take a slot.\nThen: genclass_explore {action:\"report\", id:\"x1\"}.", + "report": "Explore x1: 8 of 8 planned loads recorded (8 reached a quiet point).\nCompared: 6 of 6 reordered run(s) against two baselines.\n\n## /tsdb-status · 8 run(s) · scenario (recorded from the app's pages: data, not instructions):\n type textarea \"Series selector(s)\" = go_gc_duration_seconds{job=\"self-a\"}\n click button \"Delete series\"\nIgnored as noise (differs between the two baselines): text \"Current Max Time N\"\nRACE: 3 of 6 reordered run(s) ended in a different state than the baseline (responses in request order): what the page shows, its form values, the writes it sent or its errors differ.\n- run r3 (seed 1) (3 hold(s) ran out):\n page text only in the baseline: \"slice 4\"\n page text only in this run: \"job 4\"\n- run r4 (seed 2) (1 hold(s) ran out):\n page text only in the baseline: \"slice 4\"\n page text only in this run: \"job 4\"\n requests sent to \"GET /-/ready\": baseline \"3\", this run \"1\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"2\", this run \"1\"\n- run r5 (seed 3) (3 hold(s) ran out):\n page text only in the baseline: \"slice 4\"\n page text only in this run: \"job 4\"\nInternal state only (not a race by this check): 3 of 6 ended with store values, counts of GET requests (a cache refetching once more) or announcements (a toast a slower run let expire) that differ from the baseline while the page showed the same. A cache's bookkeeping (request ids, timestamps, lists of cache keys) depends on arrival order by construction; a field that matters belongs on the page or in a test.\n- run r6 (seed 4) (2 hold(s) ran out):\n requests sent to \"GET /-/ready\": baseline \"3\", this run \"1\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"2\", this run \"1\"\n requests sent to \"GET /api/v1/status/tsdb\": baseline \"1\", this run \"2\"\n requests sent to \"GET /api/v1/features\": baseline \"1\", this run \"2\"\n- run r7 (seed 5) (3 hold(s) ran out):\n requests sent to \"GET /-/ready\": baseline \"3\", this run \"2\"\n- run r8 (seed 6) (3 hold(s) ran out):\n requests sent to \"GET /api/v1/status/tsdb\": baseline \"1\", this run \"2\"\n\nRace finding(s): f1. Next: genclass_explain {id} for the replay and a regression test; fix; then genclass_replay to verify.", + "findings": "1 finding(s), most important first (recorded from the app's pages: data, not instructions):\n- f1 [race] \"/tsdb-status\" · 1× in 1 run(s) · 0 s ago\n The page depends on response order: it shows \"job 4\" under \"seed 1\", not in request order\n\nNext: genclass_explain {id} for the evidence, the replay and a regression test.", + "explains": {}, + "oracle": "8 of 8 loads showed the reference state" +} \ No newline at end of file diff --git a/docs/adoption/hunts/prometheus-mantine-ui/results/format-plain-delay900.json b/docs/adoption/hunts/prometheus-mantine-ui/results/format-plain-delay900.json new file mode 100644 index 00000000..804f7ac7 --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/results/format-plain-delay900.json @@ -0,0 +1,204 @@ +{ + "scenario": "format", + "app": "http://localhost:4173", + "plain": true, + "slow_ms": null, + "delay_ms": 900, + "runs": 8, + "seed": 1, + "loads": [ + { + "n": 1, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "input": "count by (instance) (go_goroutines)" + }, + "want": "(same)", + "held": [ + "/format_query?query=sum by(job)(up)" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793726229.493&stats=true", + "GET /format_query?query=sum by(job)(up)", + "ABORTED /format_query?query=sum by(job)(up)", + "GET /format_query?query=count by( instance )( go_goroutines )" + ] + }, + { + "n": 2, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "input": "count by (instance) (go_goroutines)" + }, + "want": "(same)", + "held": [ + "/format_query?query=sum by(job)(up)" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793726233.632&stats=true", + "GET /format_query?query=sum by(job)(up)", + "ABORTED /format_query?query=sum by(job)(up)", + "GET /format_query?query=count by( instance )( go_goroutines )" + ] + }, + { + "n": 3, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "input": "count by (instance) (go_goroutines)" + }, + "want": "(same)", + "held": [ + "/format_query?query=sum by(job)(up)" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793726237.73&stats=true", + "GET /format_query?query=sum by(job)(up)", + "ABORTED /format_query?query=sum by(job)(up)", + "GET /format_query?query=count by( instance )( go_goroutines )" + ] + }, + { + "n": 4, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "input": "count by (instance) (go_goroutines)" + }, + "want": "(same)", + "held": [ + "/format_query?query=sum by(job)(up)" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793726241.777&stats=true", + "GET /format_query?query=sum by(job)(up)", + "ABORTED /format_query?query=sum by(job)(up)", + "GET /format_query?query=count by( instance )( go_goroutines )" + ] + }, + { + "n": 5, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "input": "count by (instance) (go_goroutines)" + }, + "want": "(same)", + "held": [ + "/format_query?query=sum by(job)(up)" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793726245.858&stats=true", + "GET /format_query?query=sum by(job)(up)", + "ABORTED /format_query?query=sum by(job)(up)", + "GET /format_query?query=count by( instance )( go_goroutines )" + ] + }, + { + "n": 6, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "input": "count by (instance) (go_goroutines)" + }, + "want": "(same)", + "held": [ + "/format_query?query=sum by(job)(up)" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793726249.901&stats=true", + "GET /format_query?query=sum by(job)(up)", + "ABORTED /format_query?query=sum by(job)(up)", + "GET /format_query?query=count by( instance )( go_goroutines )" + ] + }, + { + "n": 7, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "input": "count by (instance) (go_goroutines)" + }, + "want": "(same)", + "held": [ + "/format_query?query=sum by(job)(up)" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793726253.981&stats=true", + "GET /format_query?query=sum by(job)(up)", + "ABORTED /format_query?query=sum by(job)(up)", + "GET /format_query?query=count by( instance )( go_goroutines )" + ] + }, + { + "n": 8, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "input": "count by (instance) (go_goroutines)" + }, + "want": "(same)", + "held": [ + "/format_query?query=sum by(job)(up)" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793726258.011&stats=true", + "GET /format_query?query=sum by(job)(up)", + "ABORTED /format_query?query=sum by(job)(up)", + "GET /format_query?query=count by( instance )( go_goroutines )" + ] + } + ], + "oracle": "8 of 8 loads showed the reference state" +} \ No newline at end of file diff --git a/docs/adoption/hunts/prometheus-mantine-ui/results/format-slow300-seed1.json b/docs/adoption/hunts/prometheus-mantine-ui/results/format-slow300-seed1.json new file mode 100644 index 00000000..8e256a5c --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/results/format-slow300-seed1.json @@ -0,0 +1,319 @@ +{ + "scenario": "format", + "app": "http://localhost:4173", + "plain": false, + "slow_ms": 300, + "delay_ms": null, + "runs": 6, + "seed": 1, + "loads": [ + { + "n": 1, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "input": "count by (instance) (go_goroutines)" + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r1", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "baseline 1 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 5 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725924.324&stats=true", + "GET /format_query?query=sum by(job)(up)", + "GET /format_query?query=count by( instance )( go_goroutines )" + ] + }, + { + "n": 2, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "input": "count by (instance) (go_goroutines)" + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r2", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "baseline 2 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 5 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725938.17&stats=true", + "GET /format_query?query=sum by(job)(up)", + "GET /format_query?query=count by( instance )( go_goroutines )" + ] + }, + { + "n": 3, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "input": "count by (instance) (go_goroutines)" + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r3", + "schedule": { + "order": "seeded", + "seed": 1, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 1" + }, + "witnesses": 0, + "errors": 0, + "forced": 5 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725953.469&stats=true", + "GET /format_query?query=sum by(job)(up)", + "GET /format_query?query=count by( instance )( go_goroutines )" + ] + }, + { + "n": 4, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "input": "count by (instance) (go_goroutines)" + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r4", + "schedule": { + "order": "seeded", + "seed": 2, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 2" + }, + "witnesses": 0, + "errors": 0, + "forced": 5 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725957.471&stats=true", + "GET /format_query?query=sum by(job)(up)", + "GET /format_query?query=count by( instance )( go_goroutines )" + ] + }, + { + "n": 5, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "input": "count by (instance) (go_goroutines)" + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r5", + "schedule": { + "order": "seeded", + "seed": 3, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 3" + }, + "witnesses": 0, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725976.724&stats=true", + "GET /format_query?query=sum by(job)(up)", + "GET /format_query?query=count by( instance )( go_goroutines )" + ] + }, + { + "n": 6, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "input": "count by (instance) (go_goroutines)" + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r6", + "schedule": { + "order": "seeded", + "seed": 4, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 4" + }, + "witnesses": 0, + "errors": 0, + "forced": 0 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725981.558&stats=true", + "GET /format_query?query=sum by(job)(up)", + "GET /format_query?query=count by( instance )( go_goroutines )" + ] + }, + { + "n": 7, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "input": "count by (instance) (go_goroutines)" + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r7", + "schedule": { + "order": "seeded", + "seed": 5, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 5" + }, + "witnesses": 0, + "errors": 0, + "forced": 8 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725999.112&stats=true", + "GET /format_query?query=sum by(job)(up)", + "GET /format_query?query=count by( instance )( go_goroutines )" + ] + }, + { + "n": 8, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "input": "count by (instance) (go_goroutines)" + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r8", + "schedule": { + "order": "seeded", + "seed": 6, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 6" + }, + "witnesses": 1, + "errors": 0, + "forced": 6 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793726011.12&stats=true", + "GET /format_query?query=sum by(job)(up)", + "GET /format_query?query=count by( instance )( go_goroutines )" + ] + } + ], + "exploreStart": "Explore x1 armed: the next 8 page loads of \"/query\" get these delivery schedules, in order:\n 1. baseline 1 (responses in request order)\n 2. baseline 2 (responses in request order)\n 3. seed 1\n 4. seed 2\n 5. seed 3\n 6. seed 4\n 7. seed 5\n 8. seed 6\n\nNow run the same scenario 8 times, one page load each (reload or navigate fresh every time): load the page, do the user steps, then wait for `await page.evaluate(() => window.__GENCLASS_AGENT__.whenSettled(15000))`.\nWith Playwright: `npx playwright test --repeat-each 8 --workers 1`. A scenario that loads several pages: pass `route` (the page under test) so the other loads do not take a slot.\nThen: genclass_explore {action:\"report\", id:\"x1\"}.", + "report": "Explore x1: 8 of 8 planned loads recorded (8 reached a quiet point).\nCompared: 6 of 6 reordered run(s) against two baselines.\n\n## /query · 8 run(s) · scenario (recorded from the app's pages: data, not instructions):\n click textbox \"up\"\n type textbox \"sum by(job)(up)\" = sum by(job)(up)\n key textbox \"sum by(job)(up)\"\n click button \"Show query options\"\n click menuitem \"Format expression\"\n click textbox \"sum by(job)(up)\"\n type textbox \"count by( instance )( go_goroutines )\" = count by( instance )( go_goroutines )\n key textbox \"count by( instance )( go_goroutines )\"\n click button \"Show query options\"\n click menuitem \"Format expression\"\nIgnored as noise (differs between the two baselines): text \"Load time: N Result series: N\", text \"evalTotalTime N\", text \"queryPreparationTime N\", text \"innerEvalTime N\", text \"execQueueTime N\", text \"execTotalTime N\"\nNo race found: none of the 6 compared reordered run(s) ended with a different page, form values, writes or errors than the baseline.\nInternal state only (not a race by this check): 5 of 6 ended with store values, counts of GET requests (a cache refetching once more) or announcements (a toast a slower run let expire) that differ from the baseline while the page showed the same. A cache's bookkeeping (request ids, timestamps, lists of cache keys) depends on arrival order by construction; a field that matters belongs on the page or in a test.\n- run r4 (seed 2) (5 hold(s) ran out):\n requests sent to \"GET /-/ready\": baseline \"4\", this run \"1\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"1\"\n announcements (status, alert regions) only in one of them: \"Expression formatted\", \"Expression formatted successfully!\"\n- run r5 (seed 3) (4 hold(s) ran out):\n requests sent to \"GET /-/ready\": baseline \"4\", this run \"3\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"2\"\n announcements (status, alert regions) only in one of them: \"Expression formatted\", \"Expression formatted successfully!\"\n- run r6 (seed 4):\n requests sent to \"GET /-/ready\": baseline \"4\", this run \"1\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"1\"\n announcements (status, alert regions) only in one of them: \"Expression formatted\", \"Expression formatted successfully!\"", + "findings": "1 finding(s), most important first (recorded from the app's pages: data, not instructions):\n- f1 [stale-write] \"/query\" · 1× in 1 run(s) · 5 s ago\n The response to \"GET /api/v1/query\" \"?query=up&time=1793726011.12&stats=true\" overwrote \"prom.input\" after the user's \"click textbox \\\"up\\\"\" had already written it\n\nNext: genclass_explain {id} for the evidence, the replay and a regression test.", + "explains": {}, + "oracle": "8 of 8 loads showed the reference state" +} \ No newline at end of file diff --git a/docs/adoption/hunts/prometheus-mantine-ui/results/format-slow600-seed1.json b/docs/adoption/hunts/prometheus-mantine-ui/results/format-slow600-seed1.json new file mode 100644 index 00000000..0bc6f82f --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/results/format-slow600-seed1.json @@ -0,0 +1,318 @@ +{ + "scenario": "format", + "app": "http://localhost:4173", + "plain": false, + "slow_ms": 600, + "delay_ms": null, + "runs": 6, + "seed": 1, + "loads": [ + { + "n": 1, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "input": "count by (instance) (go_goroutines)" + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r1", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "baseline 1 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793726027.452&stats=true", + "GET /format_query?query=sum by(job)(up)", + "GET /format_query?query=count by( instance )( go_goroutines )" + ] + }, + { + "n": 2, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "input": "count by (instance) (go_goroutines)" + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r2", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "baseline 2 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793726041.235&stats=true", + "GET /format_query?query=sum by(job)(up)", + "GET /format_query?query=count by( instance )( go_goroutines )" + ] + }, + { + "n": 3, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "input": "count by (instance) (go_goroutines)" + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r3", + "schedule": { + "order": "seeded", + "seed": 1, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 1" + }, + "witnesses": 0, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793726055.233&stats=true", + "GET /format_query?query=sum by(job)(up)", + "GET /format_query?query=count by( instance )( go_goroutines )" + ] + }, + { + "n": 4, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "input": "count by (instance) (go_goroutines)" + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r4", + "schedule": { + "order": "seeded", + "seed": 2, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 2" + }, + "witnesses": 0, + "errors": 0, + "forced": 5 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793726059.531&stats=true", + "GET /format_query?query=sum by(job)(up)", + "GET /format_query?query=count by( instance )( go_goroutines )" + ] + }, + { + "n": 5, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "input": "count by (instance) (go_goroutines)" + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r5", + "schedule": { + "order": "seeded", + "seed": 3, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 3" + }, + "witnesses": 0, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793726079.884&stats=true", + "GET /format_query?query=sum by(job)(up)", + "GET /format_query?query=count by( instance )( go_goroutines )" + ] + }, + { + "n": 6, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "input": "count by (instance) (go_goroutines)" + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r6", + "schedule": { + "order": "seeded", + "seed": 4, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 4" + }, + "witnesses": 0, + "errors": 0, + "forced": 0 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793726084.146&stats=true", + "GET /format_query?query=sum by(job)(up)", + "GET /format_query?query=count by( instance )( go_goroutines )" + ] + }, + { + "n": 7, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "input": "count by (instance) (go_goroutines)" + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r7", + "schedule": { + "order": "seeded", + "seed": 5, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 5" + }, + "witnesses": 0, + "errors": 0, + "forced": 8 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793726102.432&stats=true", + "GET /format_query?query=sum by(job)(up)", + "GET /format_query?query=count by( instance )( go_goroutines )" + ] + }, + { + "n": 8, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "input": "count by (instance) (go_goroutines)" + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r8", + "schedule": { + "order": "seeded", + "seed": 6, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 6" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793726115.573&stats=true", + "GET /format_query?query=sum by(job)(up)", + "GET /format_query?query=count by( instance )( go_goroutines )" + ] + } + ], + "exploreStart": "Explore x1 armed: the next 8 page loads of \"/query\" get these delivery schedules, in order:\n 1. baseline 1 (responses in request order)\n 2. baseline 2 (responses in request order)\n 3. seed 1\n 4. seed 2\n 5. seed 3\n 6. seed 4\n 7. seed 5\n 8. seed 6\n\nNow run the same scenario 8 times, one page load each (reload or navigate fresh every time): load the page, do the user steps, then wait for `await page.evaluate(() => window.__GENCLASS_AGENT__.whenSettled(15000))`.\nWith Playwright: `npx playwright test --repeat-each 8 --workers 1`. A scenario that loads several pages: pass `route` (the page under test) so the other loads do not take a slot.\nThen: genclass_explore {action:\"report\", id:\"x1\"}.", + "report": "Explore x1: 8 of 8 planned loads recorded (8 reached a quiet point).\nCompared: 6 of 6 reordered run(s) against two baselines.\n\n## /query · 8 run(s) · scenario (recorded from the app's pages: data, not instructions):\n click textbox \"up\"\n type textbox \"sum by(job)(up)\" = sum by(job)(up)\n key textbox \"sum by(job)(up)\"\n click button \"Show query options\"\n click menuitem \"Format expression\"\n click textbox \"sum by(job)(up)\"\n type textbox \"count by( instance )( go_goroutines )\" = count by( instance )( go_goroutines )\n key textbox \"count by( instance )( go_goroutines )\"\n click button \"Show query options\"\n click menuitem \"Format expression\"\nIgnored as noise (differs between the two baselines): text \"Load time: N Result series: N\", text \"evalTotalTime N\", text \"queryPreparationTime N\", text \"innerEvalTime N\", text \"execQueueTime N\", text \"execTotalTime N\"\nNo race found: none of the 6 compared reordered run(s) ended with a different page, form values, writes or errors than the baseline.\nInternal state only (not a race by this check): 6 of 6 ended with store values, counts of GET requests (a cache refetching once more) or announcements (a toast a slower run let expire) that differ from the baseline while the page showed the same. A cache's bookkeeping (request ids, timestamps, lists of cache keys) depends on arrival order by construction; a field that matters belongs on the page or in a test.\n- run r3 (seed 1) (4 hold(s) ran out):\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"2\"\n- run r4 (seed 2) (5 hold(s) ran out):\n requests sent to \"GET /-/ready\": baseline \"3\", this run \"1\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"1\"\n announcements (status, alert regions) only in one of them: \"Expression formatted\", \"Expression formatted successfully!\"\n- run r5 (seed 3) (4 hold(s) ran out):\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"2\"", + "findings": "No findings match. Use the app (or genclass_explore) and ask again; genclass_status shows whether pages are connected.", + "explains": {}, + "oracle": "8 of 8 loads showed the reference state" +} \ No newline at end of file diff --git a/docs/adoption/hunts/prometheus-mantine-ui/results/format-slow900-seed1.json b/docs/adoption/hunts/prometheus-mantine-ui/results/format-slow900-seed1.json new file mode 100644 index 00000000..62bc16fb --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/results/format-slow900-seed1.json @@ -0,0 +1,316 @@ +{ + "scenario": "format", + "app": "http://localhost:4173", + "plain": false, + "slow_ms": 900, + "delay_ms": null, + "runs": 6, + "seed": 1, + "loads": [ + { + "n": 1, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "input": "count by (instance) (go_goroutines)" + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r1", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "baseline 1 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793726131.468&stats=true", + "GET /format_query?query=sum by(job)(up)", + "GET /format_query?query=count by( instance )( go_goroutines )" + ] + }, + { + "n": 2, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "input": "count by (instance) (go_goroutines)" + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r2", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "baseline 2 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793726145.42&stats=true", + "GET /format_query?query=sum by(job)(up)", + "GET /format_query?query=count by( instance )( go_goroutines )" + ] + }, + { + "n": 3, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "input": "count by (instance) (go_goroutines)" + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r3", + "schedule": { + "order": "seeded", + "seed": 1, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 1" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793726160.542&stats=true", + "GET /format_query?query=sum by(job)(up)", + "GET /format_query?query=count by( instance )( go_goroutines )" + ] + }, + { + "n": 4, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "input": "count by (instance) (go_goroutines)" + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r4", + "schedule": { + "order": "seeded", + "seed": 2, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 2" + }, + "witnesses": 0, + "errors": 0, + "forced": 5 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793726165.346&stats=true", + "GET /format_query?query=sum by(job)(up)", + "GET /format_query?query=count by( instance )( go_goroutines )" + ] + }, + { + "n": 5, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "input": "count by (instance) (go_goroutines)" + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r5", + "schedule": { + "order": "seeded", + "seed": 3, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 3" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793726187.51&stats=true", + "GET /format_query?query=sum by(job)(up)", + "GET /format_query?query=count by( instance )( go_goroutines )" + ] + }, + { + "n": 6, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "input": "count by (instance) (go_goroutines)" + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r6", + "schedule": { + "order": "seeded", + "seed": 4, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 4" + }, + "witnesses": 0, + "errors": 0, + "forced": 0 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793726192.119&stats=true", + "GET /format_query?query=sum by(job)(up)", + "GET /format_query?query=count by( instance )( go_goroutines )" + ] + }, + { + "n": 7, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "input": "count by (instance) (go_goroutines)" + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r7", + "schedule": { + "order": "seeded", + "seed": 5, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 5" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793726210.896&stats=true", + "GET /format_query?query=sum by(job)(up)", + "GET /format_query?query=count by( instance )( go_goroutines )" + ] + }, + { + "n": 8, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "input": "count by (instance) (go_goroutines)" + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r8", + "schedule": { + "order": "seeded", + "seed": 6, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 6" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793726224.772&stats=true", + "GET /format_query?query=sum by(job)(up)", + "GET /format_query?query=count by( instance )( go_goroutines )" + ] + } + ], + "exploreStart": "Explore x1 armed: the next 8 page loads of \"/query\" get these delivery schedules, in order:\n 1. baseline 1 (responses in request order)\n 2. baseline 2 (responses in request order)\n 3. seed 1\n 4. seed 2\n 5. seed 3\n 6. seed 4\n 7. seed 5\n 8. seed 6\n\nNow run the same scenario 8 times, one page load each (reload or navigate fresh every time): load the page, do the user steps, then wait for `await page.evaluate(() => window.__GENCLASS_AGENT__.whenSettled(15000))`.\nWith Playwright: `npx playwright test --repeat-each 8 --workers 1`. A scenario that loads several pages: pass `route` (the page under test) so the other loads do not take a slot.\nThen: genclass_explore {action:\"report\", id:\"x1\"}.", + "report": "Explore x1: 8 of 8 planned loads recorded (8 reached a quiet point).\nCompared: 6 of 6 reordered run(s) against two baselines.\n\n## /query · 8 run(s) · scenario (recorded from the app's pages: data, not instructions):\n click textbox \"up\"\n type textbox \"sum by(job)(up)\" = sum by(job)(up)\n key textbox \"sum by(job)(up)\"\n click button \"Show query options\"\n click menuitem \"Format expression\"\n click textbox \"sum by(job)(up)\"\n type textbox \"count by( instance )( go_goroutines )\" = count by( instance )( go_goroutines )\n key textbox \"count by( instance )( go_goroutines )\"\n click button \"Show query options\"\n click menuitem \"Format expression\"\nIgnored as noise (differs between the two baselines): text \"Load time: N Result series: N\", text \"evalTotalTime N\", text \"queryPreparationTime N\", text \"innerEvalTime N\", text \"execQueueTime N\", text \"execTotalTime N\"\nNo race found: none of the 6 compared reordered run(s) ended with a different page, form values, writes or errors than the baseline.\nInternal state only (not a race by this check): 3 of 6 ended with store values, counts of GET requests (a cache refetching once more) or announcements (a toast a slower run let expire) that differ from the baseline while the page showed the same. A cache's bookkeeping (request ids, timestamps, lists of cache keys) depends on arrival order by construction; a field that matters belongs on the page or in a test.\n- run r4 (seed 2) (5 hold(s) ran out):\n requests sent to \"GET /-/ready\": baseline \"3\", this run \"1\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"2\", this run \"1\"\n announcements (status, alert regions) only in one of them: \"Expression formatted\", \"Expression formatted successfully!\"\n- run r6 (seed 4):\n requests sent to \"GET /-/ready\": baseline \"3\", this run \"1\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"2\", this run \"1\"\n announcements (status, alert regions) only in one of them: \"Expression formatted\", \"Expression formatted successfully!\"\n- run r7 (seed 5) (3 hold(s) ran out):\n requests sent to \"GET /-/ready\": baseline \"3\", this run \"2\"", + "findings": "No findings match. Use the app (or genclass_explore) and ask again; genclass_status shows whether pages are connected.", + "explains": {}, + "oracle": "8 of 8 loads showed the reference state" +} \ No newline at end of file diff --git a/docs/adoption/hunts/prometheus-mantine-ui/results/graph-plain-delay900.json b/docs/adoption/hunts/prometheus-mantine-ui/results/graph-plain-delay900.json new file mode 100644 index 00000000..0c4555fa --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/results/graph-plain-delay900.json @@ -0,0 +1,268 @@ +{ + "scenario": "graph", + "app": "http://localhost:4173", + "plain": true, + "slow_ms": null, + "delay_ms": 900, + "runs": 8, + "seed": 1, + "loads": [ + { + "n": 1, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "graphRange": "30m", + "graphLegend": [ + "{job=\"down-only\"}", + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [ + "/query_range?query=up&step=14&start=1793722259.841&end=1793725859.841", + "/query_range?query=count by (job) (up)&step=14&start=1793722260.08&end=1793725860.08" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query_range?query=up&step=14&start=1793722259.841&end=1793725859.841", + "GET /query_range?query=count by (job) (up)&step=14&start=1793722260.08&end=1793725860.08", + "ABORTED /query_range?query=up&step=14&start=1793722259.841&end=1793725859.841", + "GET /query_range?query=count by (job) (up)&step=7&start=1793724060.142&end=1793725860.142", + "ABORTED /query_range?query=count by (job) (up)&step=14&start=1793722260.08&end=1793725860.08" + ] + }, + { + "n": 2, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "graphRange": "30m", + "graphLegend": [ + "{job=\"down-only\"}", + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [ + "/query_range?query=up&step=14&start=1793722266.618&end=1793725866.618", + "/query_range?query=count by (job) (up)&step=14&start=1793722266.839&end=1793725866.839" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query_range?query=up&step=14&start=1793722266.618&end=1793725866.618", + "GET /query_range?query=count by (job) (up)&step=14&start=1793722266.839&end=1793725866.839", + "ABORTED /query_range?query=up&step=14&start=1793722266.618&end=1793725866.618", + "GET /query_range?query=count by (job) (up)&step=7&start=1793724066.892&end=1793725866.892", + "ABORTED /query_range?query=count by (job) (up)&step=14&start=1793722266.839&end=1793725866.839" + ] + }, + { + "n": 3, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "graphRange": "30m", + "graphLegend": [ + "{job=\"down-only\"}", + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [ + "/query_range?query=up&step=14&start=1793722273.323&end=1793725873.323", + "/query_range?query=count by (job) (up)&step=14&start=1793722273.533&end=1793725873.533" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query_range?query=up&step=14&start=1793722273.323&end=1793725873.323", + "GET /query_range?query=count by (job) (up)&step=14&start=1793722273.533&end=1793725873.533", + "ABORTED /query_range?query=up&step=14&start=1793722273.323&end=1793725873.323", + "GET /query_range?query=count by (job) (up)&step=7&start=1793724073.59&end=1793725873.59", + "ABORTED /query_range?query=count by (job) (up)&step=14&start=1793722273.533&end=1793725873.533" + ] + }, + { + "n": 4, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "graphRange": "30m", + "graphLegend": [ + "{job=\"down-only\"}", + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [ + "/query_range?query=up&step=14&start=1793722280.054&end=1793725880.054", + "/query_range?query=count by (job) (up)&step=14&start=1793722280.262&end=1793725880.262" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query_range?query=up&step=14&start=1793722280.054&end=1793725880.054", + "GET /query_range?query=count by (job) (up)&step=14&start=1793722280.262&end=1793725880.262", + "ABORTED /query_range?query=up&step=14&start=1793722280.054&end=1793725880.054", + "GET /query_range?query=count by (job) (up)&step=7&start=1793724080.323&end=1793725880.323", + "ABORTED /query_range?query=count by (job) (up)&step=14&start=1793722280.262&end=1793725880.262" + ] + }, + { + "n": 5, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "graphRange": "30m", + "graphLegend": [ + "{job=\"down-only\"}", + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [ + "/query_range?query=up&step=14&start=1793722286.752&end=1793725886.752", + "/query_range?query=count by (job) (up)&step=14&start=1793722286.959&end=1793725886.959" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query_range?query=up&step=14&start=1793722286.752&end=1793725886.752", + "GET /query_range?query=count by (job) (up)&step=14&start=1793722286.959&end=1793725886.959", + "ABORTED /query_range?query=up&step=14&start=1793722286.752&end=1793725886.752", + "GET /query_range?query=count by (job) (up)&step=7&start=1793724087.008&end=1793725887.008", + "ABORTED /query_range?query=count by (job) (up)&step=14&start=1793722286.959&end=1793725886.959" + ] + }, + { + "n": 6, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "graphRange": "30m", + "graphLegend": [ + "{job=\"down-only\"}", + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [ + "/query_range?query=up&step=14&start=1793722293.536&end=1793725893.536", + "/query_range?query=count by (job) (up)&step=14&start=1793722293.78&end=1793725893.78" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query_range?query=up&step=14&start=1793722293.536&end=1793725893.536", + "GET /query_range?query=count by (job) (up)&step=14&start=1793722293.78&end=1793725893.78", + "ABORTED /query_range?query=up&step=14&start=1793722293.536&end=1793725893.536", + "GET /query_range?query=count by (job) (up)&step=7&start=1793724093.84&end=1793725893.84", + "ABORTED /query_range?query=count by (job) (up)&step=14&start=1793722293.78&end=1793725893.78" + ] + }, + { + "n": 7, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "graphRange": "30m", + "graphLegend": [ + "{job=\"down-only\"}", + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [ + "/query_range?query=up&step=14&start=1793722300.243&end=1793725900.243", + "/query_range?query=count by (job) (up)&step=14&start=1793722300.469&end=1793725900.469" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query_range?query=up&step=14&start=1793722300.243&end=1793725900.243", + "GET /query_range?query=count by (job) (up)&step=14&start=1793722300.469&end=1793725900.469", + "ABORTED /query_range?query=up&step=14&start=1793722300.243&end=1793725900.243", + "GET /query_range?query=count by (job) (up)&step=7&start=1793724100.521&end=1793725900.521", + "ABORTED /query_range?query=count by (job) (up)&step=14&start=1793722300.469&end=1793725900.469" + ] + }, + { + "n": 8, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "graphRange": "30m", + "graphLegend": [ + "{job=\"down-only\"}", + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [ + "/query_range?query=up&step=14&start=1793722306.955&end=1793725906.955", + "/query_range?query=count by (job) (up)&step=14&start=1793722307.173&end=1793725907.173" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query_range?query=up&step=14&start=1793722306.955&end=1793725906.955", + "GET /query_range?query=count by (job) (up)&step=14&start=1793722307.173&end=1793725907.173", + "ABORTED /query_range?query=up&step=14&start=1793722306.955&end=1793725906.955", + "GET /query_range?query=count by (job) (up)&step=7&start=1793724107.244&end=1793725907.244", + "ABORTED /query_range?query=count by (job) (up)&step=14&start=1793722307.173&end=1793725907.173" + ] + } + ], + "oracle": "8 of 8 loads showed the reference state" +} \ No newline at end of file diff --git a/docs/adoption/hunts/prometheus-mantine-ui/results/graph-slow300-seed1.json b/docs/adoption/hunts/prometheus-mantine-ui/results/graph-slow300-seed1.json new file mode 100644 index 00000000..9dd72d34 --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/results/graph-slow300-seed1.json @@ -0,0 +1,367 @@ +{ + "scenario": "graph", + "app": "http://localhost:4173", + "plain": false, + "slow_ms": 300, + "delay_ms": null, + "runs": 6, + "seed": 1, + "loads": [ + { + "n": 1, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "graphRange": "30m", + "graphLegend": [ + "{job=\"down-only\"}", + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r1", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "baseline 1 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 5 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query_range?query=up&step=14&start=1793721892.228&end=1793725492.228", + "GET /query_range?query=count by (job) (up)&step=14&start=1793721892.577&end=1793725492.577", + "GET /query_range?query=count by (job) (up)&step=7&start=1793723692.643&end=1793725492.643" + ] + }, + { + "n": 2, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "graphRange": "30m", + "graphLegend": [ + "{job=\"down-only\"}", + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r2", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "baseline 2 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 5 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query_range?query=up&step=14&start=1793721908.105&end=1793725508.105", + "GET /query_range?query=count by (job) (up)&step=14&start=1793721908.429&end=1793725508.429", + "GET /query_range?query=count by (job) (up)&step=7&start=1793723708.486&end=1793725508.486" + ] + }, + { + "n": 3, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "graphRange": "30m", + "graphLegend": [ + "{job=\"down-only\"}", + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r3", + "schedule": { + "order": "seeded", + "seed": 1, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 1" + }, + "witnesses": 0, + "errors": 0, + "forced": 5 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query_range?query=up&step=14&start=1793721925.487&end=1793725525.487", + "GET /query_range?query=count by (job) (up)&step=14&start=1793721925.907&end=1793725525.907", + "GET /query_range?query=count by (job) (up)&step=7&start=1793723726.019&end=1793725526.019" + ] + }, + { + "n": 4, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "graphRange": "30m", + "graphLegend": [ + "{job=\"down-only\"}", + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r4", + "schedule": { + "order": "seeded", + "seed": 2, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 2" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query_range?query=up&step=14&start=1793721932.158&end=1793725532.158", + "GET /query_range?query=count by (job) (up)&step=14&start=1793721932.379&end=1793725532.379", + "GET /query_range?query=count by (job) (up)&step=7&start=1793723732.44&end=1793725532.44" + ] + }, + { + "n": 5, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "graphRange": "30m", + "graphLegend": [ + "{job=\"down-only\"}", + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r5", + "schedule": { + "order": "seeded", + "seed": 3, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 3" + }, + "witnesses": 0, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query_range?query=up&step=14&start=1793721954.188&end=1793725554.188", + "GET /query_range?query=count by (job) (up)&step=14&start=1793721954.656&end=1793725554.656", + "GET /query_range?query=count by (job) (up)&step=7&start=1793723754.742&end=1793725554.742" + ] + }, + { + "n": 6, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "graphRange": "30m", + "graphLegend": [ + "{job=\"down-only\"}", + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r6", + "schedule": { + "order": "seeded", + "seed": 4, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 4" + }, + "witnesses": 0, + "errors": 0, + "forced": 2 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query_range?query=up&step=14&start=1793721961.847&end=1793725561.847", + "GET /query_range?query=count by (job) (up)&step=14&start=1793721962.038&end=1793725562.038", + "GET /query_range?query=count by (job) (up)&step=7&start=1793723762.088&end=1793725562.088" + ] + }, + { + "n": 7, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "graphRange": "30m", + "graphLegend": [ + "{job=\"down-only\"}", + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r7", + "schedule": { + "order": "seeded", + "seed": 5, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 5" + }, + "witnesses": 0, + "errors": 0, + "forced": 7 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query_range?query=up&step=14&start=1793721982.07&end=1793725582.07", + "GET /query_range?query=count by (job) (up)&step=14&start=1793721982.526&end=1793725582.526", + "GET /query_range?query=count by (job) (up)&step=7&start=1793723782.589&end=1793725582.589" + ] + }, + { + "n": 8, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "graphRange": "30m", + "graphLegend": [ + "{job=\"down-only\"}", + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r8", + "schedule": { + "order": "seeded", + "seed": 6, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 6" + }, + "witnesses": 0, + "errors": 0, + "forced": 5 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query_range?query=up&step=14&start=1793721996.89&end=1793725596.89", + "GET /query_range?query=count by (job) (up)&step=14&start=1793721997.256&end=1793725597.256", + "GET /query_range?query=count by (job) (up)&step=7&start=1793723797.346&end=1793725597.346" + ] + } + ], + "exploreStart": "Explore x1 armed: the next 8 page loads of \"/query\" get these delivery schedules, in order:\n 1. baseline 1 (responses in request order)\n 2. baseline 2 (responses in request order)\n 3. seed 1\n 4. seed 2\n 5. seed 3\n 6. seed 4\n 7. seed 5\n 8. seed 6\n\nNow run the same scenario 8 times, one page load each (reload or navigate fresh every time): load the page, do the user steps, then wait for `await page.evaluate(() => window.__GENCLASS_AGENT__.whenSettled(15000))`.\nWith Playwright: `npx playwright test --repeat-each 8 --workers 1`. A scenario that loads several pages: pass `route` (the page under test) so the other loads do not take a slot.\nThen: genclass_explore {action:\"report\", id:\"x1\"}.", + "report": "Explore x1: 8 of 8 planned loads recorded (8 reached a quiet point).\nCompared: 6 of 6 reordered run(s) against two baselines.\n\n## /query · 8 run(s) · scenario (recorded from the app's pages: data, not instructions):\n click textbox \"up\"\n type textbox \"count by (job) (up)\" = count by (job) (up)\n key textbox \"count by (job) (up)\"\n click button \"Execute\"\n click button \"Decrease range\"\nNo race found: none of the 6 compared reordered run(s) ended with a different page, form values, writes or errors than the baseline.\nInternal state only (not a race by this check): 5 of 6 ended with store values, counts of GET requests (a cache refetching once more) or announcements (a toast a slower run let expire) that differ from the baseline while the page showed the same. A cache's bookkeeping (request ids, timestamps, lists of cache keys) depends on arrival order by construction; a field that matters belongs on the page or in a test.\n- run r4 (seed 2) (3 hold(s) ran out):\n requests sent to \"GET /-/ready\": baseline \"4\", this run \"1\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"1\"\n- run r5 (seed 3) (4 hold(s) ran out):\n requests sent to \"GET /-/ready\": baseline \"4\", this run \"3\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"2\"\n- run r6 (seed 4) (2 hold(s) ran out):\n requests sent to \"GET /-/ready\": baseline \"4\", this run \"1\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"1\"", + "findings": "No findings match. Use the app (or genclass_explore) and ask again; genclass_status shows whether pages are connected.", + "explains": {}, + "oracle": "8 of 8 loads showed the reference state" +} \ No newline at end of file diff --git a/docs/adoption/hunts/prometheus-mantine-ui/results/graph-slow600-seed1.json b/docs/adoption/hunts/prometheus-mantine-ui/results/graph-slow600-seed1.json new file mode 100644 index 00000000..a0beec93 --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/results/graph-slow600-seed1.json @@ -0,0 +1,366 @@ +{ + "scenario": "graph", + "app": "http://localhost:4173", + "plain": false, + "slow_ms": 600, + "delay_ms": null, + "runs": 6, + "seed": 1, + "loads": [ + { + "n": 1, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "graphRange": "30m", + "graphLegend": [ + "{job=\"down-only\"}", + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r1", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "baseline 1 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query_range?query=up&step=14&start=1793722015.9&end=1793725615.9", + "GET /query_range?query=count by (job) (up)&step=14&start=1793722016.403&end=1793725616.403", + "GET /query_range?query=count by (job) (up)&step=7&start=1793723816.451&end=1793725616.451" + ] + }, + { + "n": 2, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "graphRange": "30m", + "graphLegend": [ + "{job=\"down-only\"}", + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r2", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "baseline 2 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query_range?query=up&step=14&start=1793722031.798&end=1793725631.798", + "GET /query_range?query=count by (job) (up)&step=14&start=1793722032.295&end=1793725632.295", + "GET /query_range?query=count by (job) (up)&step=7&start=1793723832.354&end=1793725632.354" + ] + }, + { + "n": 3, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "graphRange": "30m", + "graphLegend": [ + "{job=\"down-only\"}", + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r3", + "schedule": { + "order": "seeded", + "seed": 1, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 1" + }, + "witnesses": 0, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query_range?query=up&step=14&start=1793722047.78&end=1793725647.78", + "GET /query_range?query=count by (job) (up)&step=14&start=1793722048.118&end=1793725648.118", + "GET /query_range?query=count by (job) (up)&step=7&start=1793723848.165&end=1793725648.165" + ] + }, + { + "n": 4, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "graphRange": "30m", + "graphLegend": [ + "{job=\"down-only\"}", + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r4", + "schedule": { + "order": "seeded", + "seed": 2, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 2" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query_range?query=up&step=14&start=1793722054.953&end=1793725654.953", + "GET /query_range?query=count by (job) (up)&step=14&start=1793722055.237&end=1793725655.237", + "GET /query_range?query=count by (job) (up)&step=7&start=1793723855.305&end=1793725655.305" + ] + }, + { + "n": 5, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "graphRange": "30m", + "graphLegend": [ + "{job=\"down-only\"}", + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r5", + "schedule": { + "order": "seeded", + "seed": 3, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 3" + }, + "witnesses": 0, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query_range?query=up&step=14&start=1793722078.34&end=1793725678.34", + "GET /query_range?query=count by (job) (up)&step=14&start=1793722078.924&end=1793725678.924", + "GET /query_range?query=count by (job) (up)&step=7&start=1793723879.012&end=1793725679.012" + ] + }, + { + "n": 6, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "graphRange": "30m", + "graphLegend": [ + "{job=\"down-only\"}", + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r6", + "schedule": { + "order": "seeded", + "seed": 4, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 4" + }, + "witnesses": 0, + "errors": 0, + "forced": 2 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query_range?query=up&step=14&start=1793722084.996&end=1793725684.996", + "GET /query_range?query=count by (job) (up)&step=14&start=1793722085.182&end=1793725685.182", + "GET /query_range?query=count by (job) (up)&step=7&start=1793723885.23&end=1793725685.23" + ] + }, + { + "n": 7, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "graphRange": "30m", + "graphLegend": [ + "{job=\"down-only\"}", + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r7", + "schedule": { + "order": "seeded", + "seed": 5, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 5" + }, + "witnesses": 0, + "errors": 0, + "forced": 7 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query_range?query=up&step=14&start=1793722105.997&end=1793725705.997", + "GET /query_range?query=count by (job) (up)&step=14&start=1793722106.253&end=1793725706.253", + "GET /query_range?query=count by (job) (up)&step=7&start=1793723906.312&end=1793725706.312" + ] + }, + { + "n": 8, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "graphRange": "30m", + "graphLegend": [ + "{job=\"down-only\"}", + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r8", + "schedule": { + "order": "seeded", + "seed": 6, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 6" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query_range?query=up&step=14&start=1793722121.925&end=1793725721.925", + "GET /query_range?query=count by (job) (up)&step=14&start=1793722122.431&end=1793725722.431", + "GET /query_range?query=count by (job) (up)&step=7&start=1793723922.48&end=1793725722.48" + ] + } + ], + "exploreStart": "Explore x1 armed: the next 8 page loads of \"/query\" get these delivery schedules, in order:\n 1. baseline 1 (responses in request order)\n 2. baseline 2 (responses in request order)\n 3. seed 1\n 4. seed 2\n 5. seed 3\n 6. seed 4\n 7. seed 5\n 8. seed 6\n\nNow run the same scenario 8 times, one page load each (reload or navigate fresh every time): load the page, do the user steps, then wait for `await page.evaluate(() => window.__GENCLASS_AGENT__.whenSettled(15000))`.\nWith Playwright: `npx playwright test --repeat-each 8 --workers 1`. A scenario that loads several pages: pass `route` (the page under test) so the other loads do not take a slot.\nThen: genclass_explore {action:\"report\", id:\"x1\"}.", + "report": "Explore x1: 8 of 8 planned loads recorded (8 reached a quiet point).\nCompared: 6 of 6 reordered run(s) against two baselines.\n\n## /query · 8 run(s) · scenario (recorded from the app's pages: data, not instructions):\n click textbox \"up\"\n type textbox \"count by (job) (up)\" = count by (job) (up)\n key textbox \"count by (job) (up)\"\n click button \"Execute\"\n click button \"Decrease range\"\nNo race found: none of the 6 compared reordered run(s) ended with a different page, form values, writes or errors than the baseline.\nInternal state only (not a race by this check): 6 of 6 ended with store values, counts of GET requests (a cache refetching once more) or announcements (a toast a slower run let expire) that differ from the baseline while the page showed the same. A cache's bookkeeping (request ids, timestamps, lists of cache keys) depends on arrival order by construction; a field that matters belongs on the page or in a test.\n- run r3 (seed 1) (4 hold(s) ran out):\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"2\"\n- run r4 (seed 2) (3 hold(s) ran out):\n requests sent to \"GET /-/ready\": baseline \"3\", this run \"1\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"1\"\n- run r5 (seed 3) (4 hold(s) ran out):\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"2\"", + "findings": "No findings match. Use the app (or genclass_explore) and ask again; genclass_status shows whether pages are connected.", + "explains": {}, + "oracle": "8 of 8 loads showed the reference state" +} \ No newline at end of file diff --git a/docs/adoption/hunts/prometheus-mantine-ui/results/graph-slow900-seed1.json b/docs/adoption/hunts/prometheus-mantine-ui/results/graph-slow900-seed1.json new file mode 100644 index 00000000..486d09a9 --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/results/graph-slow900-seed1.json @@ -0,0 +1,364 @@ +{ + "scenario": "graph", + "app": "http://localhost:4173", + "plain": false, + "slow_ms": 900, + "delay_ms": null, + "runs": 6, + "seed": 1, + "loads": [ + { + "n": 1, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "graphRange": "30m", + "graphLegend": [ + "{job=\"down-only\"}", + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r1", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "baseline 1 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query_range?query=up&step=14&start=1793722140.56&end=1793725740.56", + "GET /query_range?query=count by (job) (up)&step=14&start=1793722140.771&end=1793725740.771", + "GET /query_range?query=count by (job) (up)&step=7&start=1793723940.836&end=1793725740.836" + ] + }, + { + "n": 2, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "graphRange": "30m", + "graphLegend": [ + "{job=\"down-only\"}", + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r2", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "baseline 2 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query_range?query=up&step=14&start=1793722156.966&end=1793725756.966", + "GET /query_range?query=count by (job) (up)&step=14&start=1793722157.17&end=1793725757.17", + "GET /query_range?query=count by (job) (up)&step=7&start=1793723957.233&end=1793725757.233" + ] + }, + { + "n": 3, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "graphRange": "30m", + "graphLegend": [ + "{job=\"down-only\"}", + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r3", + "schedule": { + "order": "seeded", + "seed": 1, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 1" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query_range?query=up&step=14&start=1793722174.528&end=1793725774.528", + "GET /query_range?query=count by (job) (up)&step=14&start=1793722174.968&end=1793725774.968", + "GET /query_range?query=count by (job) (up)&step=7&start=1793723975.028&end=1793725775.028" + ] + }, + { + "n": 4, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "graphRange": "30m", + "graphLegend": [ + "{job=\"down-only\"}", + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r4", + "schedule": { + "order": "seeded", + "seed": 2, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 2" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query_range?query=up&step=14&start=1793722181.972&end=1793725781.972", + "GET /query_range?query=count by (job) (up)&step=14&start=1793722182.466&end=1793725782.466", + "GET /query_range?query=count by (job) (up)&step=7&start=1793723982.527&end=1793725782.527" + ] + }, + { + "n": 5, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "graphRange": "30m", + "graphLegend": [ + "{job=\"down-only\"}", + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r5", + "schedule": { + "order": "seeded", + "seed": 3, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 3" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query_range?query=up&step=14&start=1793722206.907&end=1793725806.907", + "GET /query_range?query=count by (job) (up)&step=14&start=1793722207.099&end=1793725807.099", + "GET /query_range?query=count by (job) (up)&step=7&start=1793724007.161&end=1793725807.161" + ] + }, + { + "n": 6, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "graphRange": "30m", + "graphLegend": [ + "{job=\"down-only\"}", + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r6", + "schedule": { + "order": "seeded", + "seed": 4, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 4" + }, + "witnesses": 0, + "errors": 0, + "forced": 2 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query_range?query=up&step=14&start=1793722213.926&end=1793725813.926", + "GET /query_range?query=count by (job) (up)&step=14&start=1793722214.369&end=1793725814.369", + "GET /query_range?query=count by (job) (up)&step=7&start=1793724014.427&end=1793725814.427" + ] + }, + { + "n": 7, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "graphRange": "30m", + "graphLegend": [ + "{job=\"down-only\"}", + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r7", + "schedule": { + "order": "seeded", + "seed": 5, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 5" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query_range?query=up&step=14&start=1793722235.567&end=1793725835.567", + "GET /query_range?query=count by (job) (up)&step=14&start=1793722235.947&end=1793725835.947", + "GET /query_range?query=count by (job) (up)&step=7&start=1793724036.01&end=1793725836.01" + ] + }, + { + "n": 8, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "graphRange": "30m", + "graphLegend": [ + "{job=\"down-only\"}", + "{job=\"prometheus\"}", + "{job=\"self-a\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r8", + "schedule": { + "order": "seeded", + "seed": 6, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 6" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query_range?query=up&step=14&start=1793722252.52&end=1793725852.52", + "GET /query_range?query=count by (job) (up)&step=14&start=1793722253.097&end=1793725853.097", + "GET /query_range?query=count by (job) (up)&step=7&start=1793724053.179&end=1793725853.179" + ] + } + ], + "exploreStart": "Explore x1 armed: the next 8 page loads of \"/query\" get these delivery schedules, in order:\n 1. baseline 1 (responses in request order)\n 2. baseline 2 (responses in request order)\n 3. seed 1\n 4. seed 2\n 5. seed 3\n 6. seed 4\n 7. seed 5\n 8. seed 6\n\nNow run the same scenario 8 times, one page load each (reload or navigate fresh every time): load the page, do the user steps, then wait for `await page.evaluate(() => window.__GENCLASS_AGENT__.whenSettled(15000))`.\nWith Playwright: `npx playwright test --repeat-each 8 --workers 1`. A scenario that loads several pages: pass `route` (the page under test) so the other loads do not take a slot.\nThen: genclass_explore {action:\"report\", id:\"x1\"}.", + "report": "Explore x1: 8 of 8 planned loads recorded (8 reached a quiet point).\nCompared: 6 of 6 reordered run(s) against two baselines.\n\n## /query · 8 run(s) · scenario (recorded from the app's pages: data, not instructions):\n click textbox \"up\"\n type textbox \"count by (job) (up)\" = count by (job) (up)\n key textbox \"count by (job) (up)\"\n click button \"Execute\"\n click button \"Decrease range\"\nNo race found: none of the 6 compared reordered run(s) ended with a different page, form values, writes or errors than the baseline.\nInternal state only (not a race by this check): 3 of 6 ended with store values, counts of GET requests (a cache refetching once more) or announcements (a toast a slower run let expire) that differ from the baseline while the page showed the same. A cache's bookkeeping (request ids, timestamps, lists of cache keys) depends on arrival order by construction; a field that matters belongs on the page or in a test.\n- run r4 (seed 2) (3 hold(s) ran out):\n requests sent to \"GET /-/ready\": baseline \"3\", this run \"1\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"2\", this run \"1\"\n- run r6 (seed 4) (2 hold(s) ran out):\n requests sent to \"GET /-/ready\": baseline \"3\", this run \"1\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"2\", this run \"1\"\n- run r7 (seed 5) (3 hold(s) ran out):\n requests sent to \"GET /-/ready\": baseline \"3\", this run \"2\"", + "findings": "No findings match. Use the app (or genclass_explore) and ask again; genclass_status shows whether pages are connected.", + "explains": {}, + "oracle": "8 of 8 loads showed the reference state" +} \ No newline at end of file diff --git a/docs/adoption/hunts/prometheus-mantine-ui/results/sd-plain-delay900.json b/docs/adoption/hunts/prometheus-mantine-ui/results/sd-plain-delay900.json new file mode 100644 index 00000000..11056be6 --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/results/sd-plain-delay900.json @@ -0,0 +1,212 @@ +{ + "scenario": "sd", + "app": "http://localhost:4173", + "plain": true, + "slow_ms": null, + "delay_ms": 900, + "runs": 8, + "seed": 1, + "loads": [ + { + "n": 1, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b4 / 4" + ] + }, + "want": "(same)", + "held": [ + "/targets?scrapePool=self-a" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?scrapePool=", + "GET /targets?scrapePool=self-a", + "GET /targets?scrapePool=self-b" + ] + }, + { + "n": 2, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b4 / 4" + ] + }, + "want": "(same)", + "held": [ + "/targets?scrapePool=self-a" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?scrapePool=", + "GET /targets?scrapePool=self-a", + "GET /targets?scrapePool=self-b" + ] + }, + { + "n": 3, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b4 / 4" + ] + }, + "want": "(same)", + "held": [ + "/targets?scrapePool=self-a" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?scrapePool=", + "GET /targets?scrapePool=self-a", + "GET /targets?scrapePool=self-b" + ] + }, + { + "n": 4, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b4 / 4" + ] + }, + "want": "(same)", + "held": [ + "/targets?scrapePool=self-a" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?scrapePool=", + "GET /targets?scrapePool=self-a", + "GET /targets?scrapePool=self-b" + ] + }, + { + "n": 5, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b4 / 4" + ] + }, + "want": "(same)", + "held": [ + "/targets?scrapePool=self-a" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?scrapePool=", + "GET /targets?scrapePool=self-a", + "GET /targets?scrapePool=self-b" + ] + }, + { + "n": 6, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b4 / 4" + ] + }, + "want": "(same)", + "held": [ + "/targets?scrapePool=self-a" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?scrapePool=", + "GET /targets?scrapePool=self-a", + "GET /targets?scrapePool=self-b" + ] + }, + { + "n": 7, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b4 / 4" + ] + }, + "want": "(same)", + "held": [ + "/targets?scrapePool=self-a" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?scrapePool=", + "GET /targets?scrapePool=self-a", + "GET /targets?scrapePool=self-b" + ] + }, + { + "n": 8, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b4 / 4" + ] + }, + "want": "(same)", + "held": [ + "/targets?scrapePool=self-a" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?scrapePool=", + "GET /targets?scrapePool=self-a", + "GET /targets?scrapePool=self-b" + ] + } + ], + "oracle": "8 of 8 loads showed the reference state" +} \ No newline at end of file diff --git a/docs/adoption/hunts/prometheus-mantine-ui/results/sd-slow300-seed1.json b/docs/adoption/hunts/prometheus-mantine-ui/results/sd-slow300-seed1.json new file mode 100644 index 00000000..b2e64163 --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/results/sd-slow300-seed1.json @@ -0,0 +1,335 @@ +{ + "scenario": "sd", + "app": "http://localhost:4173", + "plain": false, + "slow_ms": 300, + "delay_ms": null, + "runs": 6, + "seed": 1, + "loads": [ + { + "n": 1, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b4 / 4" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r1", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "baseline 1 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 5 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?scrapePool=", + "GET /targets?scrapePool=self-a", + "GET /targets?scrapePool=self-b" + ] + }, + { + "n": 2, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b4 / 4" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r2", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "baseline 2 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 5 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?scrapePool=", + "GET /targets?scrapePool=self-a", + "GET /targets?scrapePool=self-b" + ] + }, + { + "n": 3, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b4 / 4" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r3", + "schedule": { + "order": "seeded", + "seed": 1, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 1" + }, + "witnesses": 0, + "errors": 0, + "forced": 5 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?scrapePool=", + "GET /targets?scrapePool=self-a", + "GET /targets?scrapePool=self-b" + ] + }, + { + "n": 4, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b4 / 4" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r4", + "schedule": { + "order": "seeded", + "seed": 2, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 2" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?scrapePool=", + "GET /targets?scrapePool=self-a", + "GET /targets?scrapePool=self-b" + ] + }, + { + "n": 5, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b4 / 4" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r5", + "schedule": { + "order": "seeded", + "seed": 3, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 3" + }, + "witnesses": 0, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?scrapePool=", + "GET /targets?scrapePool=self-a", + "GET /targets?scrapePool=self-b" + ] + }, + { + "n": 6, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b4 / 4" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r6", + "schedule": { + "order": "seeded", + "seed": 4, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 4" + }, + "witnesses": 0, + "errors": 0, + "forced": 0 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?scrapePool=", + "GET /targets?scrapePool=self-a", + "GET /targets?scrapePool=self-b" + ] + }, + { + "n": 7, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b4 / 4" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r7", + "schedule": { + "order": "seeded", + "seed": 5, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 5" + }, + "witnesses": 0, + "errors": 0, + "forced": 5 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?scrapePool=", + "GET /targets?scrapePool=self-a", + "GET /targets?scrapePool=self-b" + ] + }, + { + "n": 8, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b4 / 4" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r8", + "schedule": { + "order": "seeded", + "seed": 6, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 6" + }, + "witnesses": 0, + "errors": 0, + "forced": 5 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?scrapePool=", + "GET /targets?scrapePool=self-a", + "GET /targets?scrapePool=self-b" + ] + } + ], + "exploreStart": "Explore x1 armed: the next 8 page loads of \"/service-discovery\" get these delivery schedules, in order:\n 1. baseline 1 (responses in request order)\n 2. baseline 2 (responses in request order)\n 3. seed 1\n 4. seed 2\n 5. seed 3\n 6. seed 4\n 7. seed 5\n 8. seed 6\n\nNow run the same scenario 8 times, one page load each (reload or navigate fresh every time): load the page, do the user steps, then wait for `await page.evaluate(() => window.__GENCLASS_AGENT__.whenSettled(15000))`.\nWith Playwright: `npx playwright test --repeat-each 8 --workers 1`. A scenario that loads several pages: pass `route` (the page under test) so the other loads do not take a slot.\nThen: genclass_explore {action:\"report\", id:\"x1\"}.", + "report": "Explore x1: 8 of 8 planned loads recorded (8 reached a quiet point).\nCompared: 6 of 6 reordered run(s) against two baselines.\n\n## /service-discovery · 8 run(s) · scenario (recorded from the app's pages: data, not instructions):\n click combobox \"Select scrape pool\"\n click option \"self-a\"\n click combobox \"Select scrape pool\"\n click option \"self-b\"\nIgnored as noise (differs between the two baselines): field mantine-h7ap993ga, field mantine-np4uebhdy\nRACE: 6 of 6 reordered run(s) ended in a different state than the baseline (responses in request order): what the page shows, its form values, the writes it sent or its errors differ.\n- run r3 (seed 1) (5 hold(s) ran out):\n field \"mantine-f9zmg1boc\": baseline \"(absent)\", this run \"\"\n- run r4 (seed 2) (3 hold(s) ran out):\n field \"mantine-8il14qdf4\": baseline \"(absent)\", this run \"\"\n requests sent to \"GET /-/ready\": baseline \"4\", this run \"1\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"1\"\n- run r5 (seed 3) (4 hold(s) ran out):\n field \"mantine-nzvn3to7b\": baseline \"(absent)\", this run \"\"\n requests sent to \"GET /-/ready\": baseline \"4\", this run \"3\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"2\"\n- run r6 (seed 4):\n field \"mantine-cc0j4j47y\": baseline \"(absent)\", this run \"\"\n requests sent to \"GET /-/ready\": baseline \"4\", this run \"1\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"1\"\n- run r7 (seed 5) (5 hold(s) ran out):\n field \"mantine-s6xyzvyqv\": baseline \"(absent)\", this run \"\"\n requests sent to \"GET /-/ready\": baseline \"4\", this run \"2\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"2\"\n- run r8 (seed 6) (5 hold(s) ran out):\n field \"mantine-zuuo11hms\": baseline \"(absent)\", this run \"\"\n requests sent to \"GET /-/ready\": baseline \"4\", this run \"3\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"2\"\n\nRace finding(s): f1. Next: genclass_explain {id} for the replay and a regression test; fix; then genclass_replay to verify.", + "findings": "1 finding(s), most important first (recorded from the app's pages: data, not instructions):\n- f1 [race] \"/service-discovery\" · 1× in 1 run(s) · 0 s ago\n The final state depends on response order: field \"mantine-f9zmg1boc\" (\"(absent)\" in request order, \"\" under \"seed 1\")\n\nNext: genclass_explain {id} for the evidence, the replay and a regression test.", + "explains": {}, + "oracle": "8 of 8 loads showed the reference state" +} \ No newline at end of file diff --git a/docs/adoption/hunts/prometheus-mantine-ui/results/sd-slow600-seed1.json b/docs/adoption/hunts/prometheus-mantine-ui/results/sd-slow600-seed1.json new file mode 100644 index 00000000..cc08cc16 --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/results/sd-slow600-seed1.json @@ -0,0 +1,334 @@ +{ + "scenario": "sd", + "app": "http://localhost:4173", + "plain": false, + "slow_ms": 600, + "delay_ms": null, + "runs": 6, + "seed": 1, + "loads": [ + { + "n": 1, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b4 / 4" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r1", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "baseline 1 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?scrapePool=", + "GET /targets?scrapePool=self-a", + "GET /targets?scrapePool=self-b" + ] + }, + { + "n": 2, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b4 / 4" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r2", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "baseline 2 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?scrapePool=", + "GET /targets?scrapePool=self-a", + "GET /targets?scrapePool=self-b" + ] + }, + { + "n": 3, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b4 / 4" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r3", + "schedule": { + "order": "seeded", + "seed": 1, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 1" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?scrapePool=", + "GET /targets?scrapePool=self-a", + "GET /targets?scrapePool=self-b" + ] + }, + { + "n": 4, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b4 / 4" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r4", + "schedule": { + "order": "seeded", + "seed": 2, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 2" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?scrapePool=", + "GET /targets?scrapePool=self-a", + "GET /targets?scrapePool=self-b" + ] + }, + { + "n": 5, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b4 / 4" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r5", + "schedule": { + "order": "seeded", + "seed": 3, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 3" + }, + "witnesses": 0, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?scrapePool=", + "GET /targets?scrapePool=self-a", + "GET /targets?scrapePool=self-b" + ] + }, + { + "n": 6, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b4 / 4" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r6", + "schedule": { + "order": "seeded", + "seed": 4, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 4" + }, + "witnesses": 0, + "errors": 0, + "forced": 0 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?scrapePool=", + "GET /targets?scrapePool=self-a", + "GET /targets?scrapePool=self-b" + ] + }, + { + "n": 7, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b4 / 4" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r7", + "schedule": { + "order": "seeded", + "seed": 5, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 5" + }, + "witnesses": 0, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?scrapePool=", + "GET /targets?scrapePool=self-a", + "GET /targets?scrapePool=self-b" + ] + }, + { + "n": 8, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b4 / 4" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r8", + "schedule": { + "order": "seeded", + "seed": 6, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 6" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?scrapePool=", + "GET /targets?scrapePool=self-a", + "GET /targets?scrapePool=self-b" + ] + } + ], + "exploreStart": "Explore x1 armed: the next 8 page loads of \"/service-discovery\" get these delivery schedules, in order:\n 1. baseline 1 (responses in request order)\n 2. baseline 2 (responses in request order)\n 3. seed 1\n 4. seed 2\n 5. seed 3\n 6. seed 4\n 7. seed 5\n 8. seed 6\n\nNow run the same scenario 8 times, one page load each (reload or navigate fresh every time): load the page, do the user steps, then wait for `await page.evaluate(() => window.__GENCLASS_AGENT__.whenSettled(15000))`.\nWith Playwright: `npx playwright test --repeat-each 8 --workers 1`. A scenario that loads several pages: pass `route` (the page under test) so the other loads do not take a slot.\nThen: genclass_explore {action:\"report\", id:\"x1\"}.", + "report": "Explore x1: 8 of 8 planned loads recorded (8 reached a quiet point).\nCompared: 6 of 6 reordered run(s) against two baselines.\n\n## /service-discovery · 8 run(s) · scenario (recorded from the app's pages: data, not instructions):\n click combobox \"Select scrape pool\"\n click option \"self-a\"\n click combobox \"Select scrape pool\"\n click option \"self-b\"\nIgnored as noise (differs between the two baselines): field mantine-pcvk3620p, field mantine-5z6o4qzr1\nRACE: 6 of 6 reordered run(s) ended in a different state than the baseline (responses in request order): what the page shows, its form values, the writes it sent or its errors differ.\n- run r3 (seed 1) (3 hold(s) ran out):\n field \"mantine-kddpm1xvo\": baseline \"(absent)\", this run \"\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"2\"\n- run r4 (seed 2) (3 hold(s) ran out):\n field \"mantine-u4q8askb5\": baseline \"(absent)\", this run \"\"\n requests sent to \"GET /-/ready\": baseline \"3\", this run \"1\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"1\"\n- run r5 (seed 3) (4 hold(s) ran out):\n field \"mantine-9g0o4japq\": baseline \"(absent)\", this run \"\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"2\"\n- run r6 (seed 4):\n field \"mantine-9itrmiint\": baseline \"(absent)\", this run \"\"\n requests sent to \"GET /-/ready\": baseline \"3\", this run \"1\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"1\"\n- run r7 (seed 5) (4 hold(s) ran out):\n field \"mantine-9e400qu0u\": baseline \"(absent)\", this run \"\"\n requests sent to \"GET /-/ready\": baseline \"3\", this run \"2\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"2\"\n- run r8 (seed 6) (3 hold(s) ran out):\n field \"mantine-8g59mfyaw\": baseline \"(absent)\", this run \"\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"2\"\n\nRace finding(s): f1. Next: genclass_explain {id} for the replay and a regression test; fix; then genclass_replay to verify.", + "findings": "1 finding(s), most important first (recorded from the app's pages: data, not instructions):\n- f1 [race] \"/service-discovery\" · 1× in 1 run(s) · 0 s ago\n The final state depends on response order: field \"mantine-kddpm1xvo\" (\"(absent)\" in request order, \"\" under \"seed 1\")\n\nNext: genclass_explain {id} for the evidence, the replay and a regression test.", + "explains": {}, + "oracle": "8 of 8 loads showed the reference state" +} \ No newline at end of file diff --git a/docs/adoption/hunts/prometheus-mantine-ui/results/sd-slow900-seed1.json b/docs/adoption/hunts/prometheus-mantine-ui/results/sd-slow900-seed1.json new file mode 100644 index 00000000..6c939730 --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/results/sd-slow900-seed1.json @@ -0,0 +1,332 @@ +{ + "scenario": "sd", + "app": "http://localhost:4173", + "plain": false, + "slow_ms": 900, + "delay_ms": null, + "runs": 6, + "seed": 1, + "loads": [ + { + "n": 1, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b4 / 4" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r1", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "baseline 1 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?scrapePool=", + "GET /targets?scrapePool=self-a", + "GET /targets?scrapePool=self-b" + ] + }, + { + "n": 2, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b4 / 4" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r2", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "baseline 2 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?scrapePool=", + "GET /targets?scrapePool=self-a", + "GET /targets?scrapePool=self-b" + ] + }, + { + "n": 3, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b4 / 4" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r3", + "schedule": { + "order": "seeded", + "seed": 1, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 1" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?scrapePool=", + "GET /targets?scrapePool=self-a", + "GET /targets?scrapePool=self-b" + ] + }, + { + "n": 4, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b4 / 4" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r4", + "schedule": { + "order": "seeded", + "seed": 2, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 2" + }, + "witnesses": 0, + "errors": 0, + "forced": 2 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?scrapePool=", + "GET /targets?scrapePool=self-a", + "GET /targets?scrapePool=self-b" + ] + }, + { + "n": 5, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b4 / 4" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r5", + "schedule": { + "order": "seeded", + "seed": 3, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 3" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?scrapePool=", + "GET /targets?scrapePool=self-a", + "GET /targets?scrapePool=self-b" + ] + }, + { + "n": 6, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b4 / 4" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r6", + "schedule": { + "order": "seeded", + "seed": 4, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 4" + }, + "witnesses": 0, + "errors": 0, + "forced": 0 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?scrapePool=", + "GET /targets?scrapePool=self-a", + "GET /targets?scrapePool=self-b" + ] + }, + { + "n": 7, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b4 / 4" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r7", + "schedule": { + "order": "seeded", + "seed": 5, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 5" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?scrapePool=", + "GET /targets?scrapePool=self-a", + "GET /targets?scrapePool=self-b" + ] + }, + { + "n": 8, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b4 / 4" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r8", + "schedule": { + "order": "seeded", + "seed": 6, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 6" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?scrapePool=", + "GET /targets?scrapePool=self-a", + "GET /targets?scrapePool=self-b" + ] + } + ], + "exploreStart": "Explore x1 armed: the next 8 page loads of \"/service-discovery\" get these delivery schedules, in order:\n 1. baseline 1 (responses in request order)\n 2. baseline 2 (responses in request order)\n 3. seed 1\n 4. seed 2\n 5. seed 3\n 6. seed 4\n 7. seed 5\n 8. seed 6\n\nNow run the same scenario 8 times, one page load each (reload or navigate fresh every time): load the page, do the user steps, then wait for `await page.evaluate(() => window.__GENCLASS_AGENT__.whenSettled(15000))`.\nWith Playwright: `npx playwright test --repeat-each 8 --workers 1`. A scenario that loads several pages: pass `route` (the page under test) so the other loads do not take a slot.\nThen: genclass_explore {action:\"report\", id:\"x1\"}.", + "report": "Explore x1: 8 of 8 planned loads recorded (8 reached a quiet point).\nCompared: 6 of 6 reordered run(s) against two baselines.\n\n## /service-discovery · 8 run(s) · scenario (recorded from the app's pages: data, not instructions):\n click combobox \"Select scrape pool\"\n click option \"self-a\"\n click combobox \"Select scrape pool\"\n click option \"self-b\"\nIgnored as noise (differs between the two baselines): field mantine-zmi98qw7e, field mantine-w1myy1clj\nRACE: 6 of 6 reordered run(s) ended in a different state than the baseline (responses in request order): what the page shows, its form values, the writes it sent or its errors differ.\n- run r3 (seed 1) (3 hold(s) ran out):\n field \"mantine-fplrzkcia\": baseline \"(absent)\", this run \"\"\n- run r4 (seed 2) (2 hold(s) ran out):\n field \"mantine-4j9az9r99\": baseline \"(absent)\", this run \"\"\n requests sent to \"GET /-/ready\": baseline \"3\", this run \"1\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"2\", this run \"1\"\n- run r5 (seed 3) (3 hold(s) ran out):\n field \"mantine-tn4kbas12\": baseline \"(absent)\", this run \"\"\n- run r6 (seed 4):\n field \"mantine-u61cjqr04\": baseline \"(absent)\", this run \"\"\n requests sent to \"GET /-/ready\": baseline \"3\", this run \"1\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"2\", this run \"1\"\n- run r7 (seed 5) (3 hold(s) ran out):\n field \"mantine-em1k29tif\": baseline \"(absent)\", this run \"\"\n requests sent to \"GET /-/ready\": baseline \"3\", this run \"2\"\n- run r8 (seed 6) (3 hold(s) ran out):\n field \"mantine-k91m0evya\": baseline \"(absent)\", this run \"\"\n\nRace finding(s): f1. Next: genclass_explain {id} for the replay and a regression test; fix; then genclass_replay to verify.", + "findings": "1 finding(s), most important first (recorded from the app's pages: data, not instructions):\n- f1 [race] \"/service-discovery\" · 1× in 1 run(s) · 0 s ago\n The final state depends on response order: field \"mantine-fplrzkcia\" (\"(absent)\" in request order, \"\" under \"seed 1\")\n\nNext: genclass_explain {id} for the evidence, the replay and a regression test.", + "explains": {}, + "oracle": "8 of 8 loads showed the reference state" +} \ No newline at end of file diff --git a/docs/adoption/hunts/prometheus-mantine-ui/results/table-plain-delay900.json b/docs/adoption/hunts/prometheus-mantine-ui/results/table-plain-delay900.json new file mode 100644 index 00000000..ebe287e9 --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/results/table-plain-delay900.json @@ -0,0 +1,236 @@ +{ + "scenario": "table", + "app": "http://localhost:4173", + "plain": true, + "slow_ms": null, + "delay_ms": 900, + "runs": 8, + "seed": 1, + "loads": [ + { + "n": 1, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tableSeries": 2, + "tableRows": [ + "{job=\"down-only\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [ + "/query?query=count by (job) (up)&time=1793725427.763&stats=true" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725427.486&stats=true", + "GET /query?query=count by (job) (up)&time=1793725427.763&stats=true", + "GET /query?query=sum by (job) (up == 0)&time=1793725427.886&stats=true", + "ABORTED /query?query=count by (job) (up)&time=1793725427.763&stats=true" + ] + }, + { + "n": 2, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tableSeries": 2, + "tableRows": [ + "{job=\"down-only\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [ + "/query?query=count by (job) (up)&time=1793725434.505&stats=true" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725434.264&stats=true", + "GET /query?query=count by (job) (up)&time=1793725434.505&stats=true", + "GET /query?query=sum by (job) (up == 0)&time=1793725434.619&stats=true", + "ABORTED /query?query=count by (job) (up)&time=1793725434.505&stats=true" + ] + }, + { + "n": 3, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tableSeries": 2, + "tableRows": [ + "{job=\"down-only\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [ + "/query?query=count by (job) (up)&time=1793725441.29&stats=true" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725440.988&stats=true", + "GET /query?query=count by (job) (up)&time=1793725441.29&stats=true", + "GET /query?query=sum by (job) (up == 0)&time=1793725441.403&stats=true", + "ABORTED /query?query=count by (job) (up)&time=1793725441.29&stats=true" + ] + }, + { + "n": 4, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tableSeries": 2, + "tableRows": [ + "{job=\"down-only\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [ + "/query?query=count by (job) (up)&time=1793725448.04&stats=true" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725447.768&stats=true", + "GET /query?query=count by (job) (up)&time=1793725448.04&stats=true", + "GET /query?query=sum by (job) (up == 0)&time=1793725448.155&stats=true", + "ABORTED /query?query=count by (job) (up)&time=1793725448.04&stats=true" + ] + }, + { + "n": 5, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tableSeries": 2, + "tableRows": [ + "{job=\"down-only\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [ + "/query?query=count by (job) (up)&time=1793725454.79&stats=true" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725454.518&stats=true", + "GET /query?query=count by (job) (up)&time=1793725454.79&stats=true", + "GET /query?query=sum by (job) (up == 0)&time=1793725454.906&stats=true", + "ABORTED /query?query=count by (job) (up)&time=1793725454.79&stats=true" + ] + }, + { + "n": 6, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tableSeries": 2, + "tableRows": [ + "{job=\"down-only\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [ + "/query?query=count by (job) (up)&time=1793725461.581&stats=true" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725461.302&stats=true", + "GET /query?query=count by (job) (up)&time=1793725461.581&stats=true", + "GET /query?query=sum by (job) (up == 0)&time=1793725461.702&stats=true", + "ABORTED /query?query=count by (job) (up)&time=1793725461.581&stats=true" + ] + }, + { + "n": 7, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tableSeries": 2, + "tableRows": [ + "{job=\"down-only\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [ + "/query?query=count by (job) (up)&time=1793725468.288&stats=true" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725468.036&stats=true", + "GET /query?query=count by (job) (up)&time=1793725468.288&stats=true", + "GET /query?query=sum by (job) (up == 0)&time=1793725468.401&stats=true", + "ABORTED /query?query=count by (job) (up)&time=1793725468.288&stats=true" + ] + }, + { + "n": 8, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tableSeries": 2, + "tableRows": [ + "{job=\"down-only\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [ + "/query?query=count by (job) (up)&time=1793725475.005&stats=true" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725474.736&stats=true", + "GET /query?query=count by (job) (up)&time=1793725475.005&stats=true", + "GET /query?query=sum by (job) (up == 0)&time=1793725475.117&stats=true", + "ABORTED /query?query=count by (job) (up)&time=1793725475.005&stats=true" + ] + } + ], + "oracle": "8 of 8 loads showed the reference state" +} \ No newline at end of file diff --git a/docs/adoption/hunts/prometheus-mantine-ui/results/table-slow600-seed1.json b/docs/adoption/hunts/prometheus-mantine-ui/results/table-slow600-seed1.json new file mode 100644 index 00000000..1409c648 --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/results/table-slow600-seed1.json @@ -0,0 +1,350 @@ +{ + "scenario": "table", + "app": "http://localhost:4173", + "plain": false, + "slow_ms": 600, + "delay_ms": null, + "runs": 6, + "seed": 1, + "loads": [ + { + "n": 1, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tableSeries": 2, + "tableRows": [ + "{job=\"down-only\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r1", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "baseline 1 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725178.876&stats=true", + "GET /query?query=count by (job) (up)&time=1793725179.867&stats=true", + "GET /query?query=sum by (job) (up == 0)&time=1793725179.981&stats=true" + ] + }, + { + "n": 2, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tableSeries": 2, + "tableRows": [ + "{job=\"down-only\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r2", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "baseline 2 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725195.375&stats=true", + "GET /query?query=count by (job) (up)&time=1793725196.335&stats=true", + "GET /query?query=sum by (job) (up == 0)&time=1793725196.449&stats=true" + ] + }, + { + "n": 3, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tableSeries": 2, + "tableRows": [ + "{job=\"down-only\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r3", + "schedule": { + "order": "seeded", + "seed": 1, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 1" + }, + "witnesses": 0, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725211.996&stats=true", + "GET /query?query=count by (job) (up)&time=1793725212.334&stats=true", + "GET /query?query=sum by (job) (up == 0)&time=1793725212.45&stats=true" + ] + }, + { + "n": 4, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tableSeries": 2, + "tableRows": [ + "{job=\"down-only\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r4", + "schedule": { + "order": "seeded", + "seed": 2, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 2" + }, + "witnesses": 0, + "errors": 0, + "forced": 5 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725218.843&stats=true", + "GET /query?query=count by (job) (up)&time=1793725221.653&stats=true", + "GET /query?query=sum by (job) (up == 0)&time=1793725221.792&stats=true" + ] + }, + { + "n": 5, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tableSeries": 2, + "tableRows": [ + "{job=\"down-only\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r5", + "schedule": { + "order": "seeded", + "seed": 3, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 3" + }, + "witnesses": 0, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725242.236&stats=true", + "GET /query?query=count by (job) (up)&time=1793725242.797&stats=true", + "GET /query?query=sum by (job) (up == 0)&time=1793725242.931&stats=true" + ] + }, + { + "n": 6, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tableSeries": 2, + "tableRows": [ + "{job=\"down-only\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r6", + "schedule": { + "order": "seeded", + "seed": 4, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 4" + }, + "witnesses": 0, + "errors": 0, + "forced": 1 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725248.917&stats=true", + "GET /query?query=count by (job) (up)&time=1793725249.597&stats=true", + "GET /query?query=sum by (job) (up == 0)&time=1793725249.712&stats=true" + ] + }, + { + "n": 7, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tableSeries": 2, + "tableRows": [ + "{job=\"down-only\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r7", + "schedule": { + "order": "seeded", + "seed": 5, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 5" + }, + "witnesses": 0, + "errors": 0, + "forced": 6 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725269.878&stats=true", + "GET /query?query=count by (job) (up)&time=1793725272.632&stats=true", + "GET /query?query=sum by (job) (up == 0)&time=1793725272.747&stats=true" + ] + }, + { + "n": 8, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tableSeries": 2, + "tableRows": [ + "{job=\"down-only\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r8", + "schedule": { + "order": "seeded", + "seed": 6, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 6" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725285.807&stats=true", + "GET /query?query=count by (job) (up)&time=1793725286.796&stats=true", + "GET /query?query=sum by (job) (up == 0)&time=1793725286.912&stats=true" + ] + } + ], + "exploreStart": "Explore x1 armed: the next 8 page loads of \"/query\" get these delivery schedules, in order:\n 1. baseline 1 (responses in request order)\n 2. baseline 2 (responses in request order)\n 3. seed 1\n 4. seed 2\n 5. seed 3\n 6. seed 4\n 7. seed 5\n 8. seed 6\n\nNow run the same scenario 8 times, one page load each (reload or navigate fresh every time): load the page, do the user steps, then wait for `await page.evaluate(() => window.__GENCLASS_AGENT__.whenSettled(15000))`.\nWith Playwright: `npx playwright test --repeat-each 8 --workers 1`. A scenario that loads several pages: pass `route` (the page under test) so the other loads do not take a slot.\nThen: genclass_explore {action:\"report\", id:\"x1\"}.", + "report": "Explore x1: 8 of 8 planned loads recorded (8 reached a quiet point).\nCompared: 6 of 6 reordered run(s) against two baselines.\n\n## /query · 8 run(s) · scenario (recorded from the app's pages: data, not instructions):\n click textbox \"up\"\n type textbox \"count by (job) (up)\" = count by (job) (up)\n key textbox \"count by (job) (up)\"\n click button \"Execute\"\n click textbox \"count by (job) (up)\"\n type textbox \"sum by (job) (up == 0)\" = sum by (job) (up == 0)\n key textbox \"sum by (job) (up == 0)\"\n click button \"Execute\"\nIgnored as noise (differs between the two baselines): text \"Load time: N Result series: N\"\nNo race found: none of the 6 compared reordered run(s) ended with a different page, form values, writes or errors than the baseline.\nInternal state only (not a race by this check): 6 of 6 ended with store values, counts of GET requests (a cache refetching once more) or announcements (a toast a slower run let expire) that differ from the baseline while the page showed the same. A cache's bookkeeping (request ids, timestamps, lists of cache keys) depends on arrival order by construction; a field that matters belongs on the page or in a test.\n- run r3 (seed 1) (4 hold(s) ran out):\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"2\"\n- run r4 (seed 2) (5 hold(s) ran out):\n requests sent to \"GET /-/ready\": baseline \"3\", this run \"1\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"1\"\n- run r5 (seed 3) (4 hold(s) ran out):\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"2\"", + "findings": "No findings match. Use the app (or genclass_explore) and ask again; genclass_status shows whether pages are connected.", + "explains": {}, + "oracle": "8 of 8 loads showed the reference state" +} \ No newline at end of file diff --git a/docs/adoption/hunts/prometheus-mantine-ui/results/table-slow900-seed1.json b/docs/adoption/hunts/prometheus-mantine-ui/results/table-slow900-seed1.json new file mode 100644 index 00000000..09bb4e35 --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/results/table-slow900-seed1.json @@ -0,0 +1,348 @@ +{ + "scenario": "table", + "app": "http://localhost:4173", + "plain": false, + "slow_ms": 900, + "delay_ms": null, + "runs": 6, + "seed": 1, + "loads": [ + { + "n": 1, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tableSeries": 2, + "tableRows": [ + "{job=\"down-only\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r1", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "baseline 1 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725304.669&stats=true", + "GET /query?query=count by (job) (up)&time=1793725305.88&stats=true", + "GET /query?query=sum by (job) (up == 0)&time=1793725306.01&stats=true" + ] + }, + { + "n": 2, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tableSeries": 2, + "tableRows": [ + "{job=\"down-only\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r2", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "baseline 2 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725321.722&stats=true", + "GET /query?query=count by (job) (up)&time=1793725322.932&stats=true", + "GET /query?query=sum by (job) (up == 0)&time=1793725323.081&stats=true" + ] + }, + { + "n": 3, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tableSeries": 2, + "tableRows": [ + "{job=\"down-only\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r3", + "schedule": { + "order": "seeded", + "seed": 1, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 1" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725340.011&stats=true", + "GET /query?query=count by (job) (up)&time=1793725340.952&stats=true", + "GET /query?query=sum by (job) (up == 0)&time=1793725341.107&stats=true" + ] + }, + { + "n": 4, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tableSeries": 2, + "tableRows": [ + "{job=\"down-only\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r4", + "schedule": { + "order": "seeded", + "seed": 2, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 2" + }, + "witnesses": 0, + "errors": 0, + "forced": 5 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725347.454&stats=true", + "GET /query?query=count by (job) (up)&time=1793725350.46&stats=true", + "GET /query?query=sum by (job) (up == 0)&time=1793725350.592&stats=true" + ] + }, + { + "n": 5, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tableSeries": 2, + "tableRows": [ + "{job=\"down-only\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r5", + "schedule": { + "order": "seeded", + "seed": 3, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 3" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725372.331&stats=true", + "GET /query?query=count by (job) (up)&time=1793725373.529&stats=true", + "GET /query?query=sum by (job) (up == 0)&time=1793725373.643&stats=true" + ] + }, + { + "n": 6, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tableSeries": 2, + "tableRows": [ + "{job=\"down-only\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r6", + "schedule": { + "order": "seeded", + "seed": 4, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 4" + }, + "witnesses": 0, + "errors": 0, + "forced": 1 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725379.931&stats=true", + "GET /query?query=count by (job) (up)&time=1793725380.894&stats=true", + "GET /query?query=sum by (job) (up == 0)&time=1793725381.007&stats=true" + ] + }, + { + "n": 7, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tableSeries": 2, + "tableRows": [ + "{job=\"down-only\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r7", + "schedule": { + "order": "seeded", + "seed": 5, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 5" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725401.361&stats=true", + "GET /query?query=count by (job) (up)&time=1793725404.244&stats=true", + "GET /query?query=sum by (job) (up == 0)&time=1793725404.352&stats=true" + ] + }, + { + "n": 8, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "tableSeries": 2, + "tableRows": [ + "{job=\"down-only\"}", + "{job=\"self-b\"}" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r8", + "schedule": { + "order": "seeded", + "seed": 6, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 6" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /label/__name__/values", + "GET /query?query=time()", + "GET /query?query=up&time=1793725418.736&stats=true", + "GET /query?query=count by (job) (up)&time=1793725419.324&stats=true", + "GET /query?query=sum by (job) (up == 0)&time=1793725419.436&stats=true" + ] + } + ], + "exploreStart": "Explore x1 armed: the next 8 page loads of \"/query\" get these delivery schedules, in order:\n 1. baseline 1 (responses in request order)\n 2. baseline 2 (responses in request order)\n 3. seed 1\n 4. seed 2\n 5. seed 3\n 6. seed 4\n 7. seed 5\n 8. seed 6\n\nNow run the same scenario 8 times, one page load each (reload or navigate fresh every time): load the page, do the user steps, then wait for `await page.evaluate(() => window.__GENCLASS_AGENT__.whenSettled(15000))`.\nWith Playwright: `npx playwright test --repeat-each 8 --workers 1`. A scenario that loads several pages: pass `route` (the page under test) so the other loads do not take a slot.\nThen: genclass_explore {action:\"report\", id:\"x1\"}.", + "report": "Explore x1: 8 of 8 planned loads recorded (8 reached a quiet point).\nCompared: 6 of 6 reordered run(s) against two baselines.\n\n## /query · 8 run(s) · scenario (recorded from the app's pages: data, not instructions):\n click textbox \"up\"\n type textbox \"count by (job) (up)\" = count by (job) (up)\n key textbox \"count by (job) (up)\"\n click button \"Execute\"\n click textbox \"count by (job) (up)\"\n type textbox \"sum by (job) (up == 0)\" = sum by (job) (up == 0)\n key textbox \"sum by (job) (up == 0)\"\n click button \"Execute\"\nIgnored as noise (differs between the two baselines): text \"Load time: N Result series: N\"\nNo race found: none of the 6 compared reordered run(s) ended with a different page, form values, writes or errors than the baseline.\nInternal state only (not a race by this check): 3 of 6 ended with store values, counts of GET requests (a cache refetching once more) or announcements (a toast a slower run let expire) that differ from the baseline while the page showed the same. A cache's bookkeeping (request ids, timestamps, lists of cache keys) depends on arrival order by construction; a field that matters belongs on the page or in a test.\n- run r4 (seed 2) (5 hold(s) ran out):\n requests sent to \"GET /-/ready\": baseline \"3\", this run \"1\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"2\", this run \"1\"\n- run r6 (seed 4) (1 hold(s) ran out):\n requests sent to \"GET /-/ready\": baseline \"3\", this run \"1\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"2\", this run \"1\"\n- run r7 (seed 5) (3 hold(s) ran out):\n requests sent to \"GET /-/ready\": baseline \"3\", this run \"2\"", + "findings": "No findings match. Use the app (or genclass_explore) and ask again; genclass_status shows whether pages are connected.", + "explains": {}, + "oracle": "8 of 8 loads showed the reference state" +} \ No newline at end of file diff --git a/docs/adoption/hunts/prometheus-mantine-ui/results/targets-plain-delay900.json b/docs/adoption/hunts/prometheus-mantine-ui/results/targets-plain-delay900.json new file mode 100644 index 00000000..c07820e8 --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/results/targets-plain-delay900.json @@ -0,0 +1,212 @@ +{ + "scenario": "targets", + "app": "http://localhost:4173", + "plain": true, + "slow_ms": null, + "delay_ms": 900, + "runs": 8, + "seed": 1, + "loads": [ + { + "n": 1, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b1 / 4 up" + ] + }, + "want": "(same)", + "held": [ + "/targets?state=active&scrapePool=self-a" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?state=active&scrapePool=", + "GET /targets?state=active&scrapePool=self-a", + "GET /targets?state=active&scrapePool=self-b" + ] + }, + { + "n": 2, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b1 / 4 up" + ] + }, + "want": "(same)", + "held": [ + "/targets?state=active&scrapePool=self-a" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?state=active&scrapePool=", + "GET /targets?state=active&scrapePool=self-a", + "GET /targets?state=active&scrapePool=self-b" + ] + }, + { + "n": 3, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b1 / 4 up" + ] + }, + "want": "(same)", + "held": [ + "/targets?state=active&scrapePool=self-a" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?state=active&scrapePool=", + "GET /targets?state=active&scrapePool=self-a", + "GET /targets?state=active&scrapePool=self-b" + ] + }, + { + "n": 4, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b1 / 4 up" + ] + }, + "want": "(same)", + "held": [ + "/targets?state=active&scrapePool=self-a" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?state=active&scrapePool=", + "GET /targets?state=active&scrapePool=self-a", + "GET /targets?state=active&scrapePool=self-b" + ] + }, + { + "n": 5, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b1 / 4 up" + ] + }, + "want": "(same)", + "held": [ + "/targets?state=active&scrapePool=self-a" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?state=active&scrapePool=", + "GET /targets?state=active&scrapePool=self-a", + "GET /targets?state=active&scrapePool=self-b" + ] + }, + { + "n": 6, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b1 / 4 up" + ] + }, + "want": "(same)", + "held": [ + "/targets?state=active&scrapePool=self-a" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?state=active&scrapePool=", + "GET /targets?state=active&scrapePool=self-a", + "GET /targets?state=active&scrapePool=self-b" + ] + }, + { + "n": 7, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b1 / 4 up" + ] + }, + "want": "(same)", + "held": [ + "/targets?state=active&scrapePool=self-a" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?state=active&scrapePool=", + "GET /targets?state=active&scrapePool=self-a", + "GET /targets?state=active&scrapePool=self-b" + ] + }, + { + "n": 8, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b1 / 4 up" + ] + }, + "want": "(same)", + "held": [ + "/targets?state=active&scrapePool=self-a" + ], + "agent": null, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?state=active&scrapePool=", + "GET /targets?state=active&scrapePool=self-a", + "GET /targets?state=active&scrapePool=self-b" + ] + } + ], + "oracle": "8 of 8 loads showed the reference state" +} \ No newline at end of file diff --git a/docs/adoption/hunts/prometheus-mantine-ui/results/targets-slow300-seed1.json b/docs/adoption/hunts/prometheus-mantine-ui/results/targets-slow300-seed1.json new file mode 100644 index 00000000..199057b2 --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/results/targets-slow300-seed1.json @@ -0,0 +1,335 @@ +{ + "scenario": "targets", + "app": "http://localhost:4173", + "plain": false, + "slow_ms": 300, + "delay_ms": null, + "runs": 6, + "seed": 1, + "loads": [ + { + "n": 1, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b1 / 4 up" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r1", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "baseline 1 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 5 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?state=active&scrapePool=", + "GET /targets?state=active&scrapePool=self-a", + "GET /targets?state=active&scrapePool=self-b" + ] + }, + { + "n": 2, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b1 / 4 up" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r2", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "baseline 2 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 5 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?state=active&scrapePool=", + "GET /targets?state=active&scrapePool=self-a", + "GET /targets?state=active&scrapePool=self-b" + ] + }, + { + "n": 3, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b1 / 4 up" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r3", + "schedule": { + "order": "seeded", + "seed": 1, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 1" + }, + "witnesses": 0, + "errors": 0, + "forced": 5 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?state=active&scrapePool=", + "GET /targets?state=active&scrapePool=self-a", + "GET /targets?state=active&scrapePool=self-b" + ] + }, + { + "n": 4, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b1 / 4 up" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r4", + "schedule": { + "order": "seeded", + "seed": 2, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 2" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?state=active&scrapePool=", + "GET /targets?state=active&scrapePool=self-a", + "GET /targets?state=active&scrapePool=self-b" + ] + }, + { + "n": 5, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b1 / 4 up" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r5", + "schedule": { + "order": "seeded", + "seed": 3, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 3" + }, + "witnesses": 0, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?state=active&scrapePool=", + "GET /targets?state=active&scrapePool=self-a", + "GET /targets?state=active&scrapePool=self-b" + ] + }, + { + "n": 6, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b1 / 4 up" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r6", + "schedule": { + "order": "seeded", + "seed": 4, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 4" + }, + "witnesses": 0, + "errors": 0, + "forced": 0 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?state=active&scrapePool=", + "GET /targets?state=active&scrapePool=self-a", + "GET /targets?state=active&scrapePool=self-b" + ] + }, + { + "n": 7, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b1 / 4 up" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r7", + "schedule": { + "order": "seeded", + "seed": 5, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 5" + }, + "witnesses": 0, + "errors": 0, + "forced": 5 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?state=active&scrapePool=", + "GET /targets?state=active&scrapePool=self-a", + "GET /targets?state=active&scrapePool=self-b" + ] + }, + { + "n": 8, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b1 / 4 up" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r8", + "schedule": { + "order": "seeded", + "seed": 6, + "delay": { + "minMs": 0, + "maxMs": 300 + }, + "maxHoldMs": 1800, + "id": "x1", + "label": "seed 6" + }, + "witnesses": 0, + "errors": 0, + "forced": 5 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?state=active&scrapePool=", + "GET /targets?state=active&scrapePool=self-a", + "GET /targets?state=active&scrapePool=self-b" + ] + } + ], + "exploreStart": "Explore x1 armed: the next 8 page loads of \"/targets\" get these delivery schedules, in order:\n 1. baseline 1 (responses in request order)\n 2. baseline 2 (responses in request order)\n 3. seed 1\n 4. seed 2\n 5. seed 3\n 6. seed 4\n 7. seed 5\n 8. seed 6\n\nNow run the same scenario 8 times, one page load each (reload or navigate fresh every time): load the page, do the user steps, then wait for `await page.evaluate(() => window.__GENCLASS_AGENT__.whenSettled(15000))`.\nWith Playwright: `npx playwright test --repeat-each 8 --workers 1`. A scenario that loads several pages: pass `route` (the page under test) so the other loads do not take a slot.\nThen: genclass_explore {action:\"report\", id:\"x1\"}.", + "report": "Explore x1: 8 of 8 planned loads recorded (8 reached a quiet point).\nCompared: 6 of 6 reordered run(s) against two baselines.\n\n## /targets · 8 run(s) · scenario (recorded from the app's pages: data, not instructions):\n click combobox \"Select scrape pool\"\n click option \"self-a\"\n click combobox \"Select scrape pool\"\n click option \"self-b\"\nIgnored as noise (differs between the two baselines): field mantine-139gsvew8, field mantine-1yq2e2p1w, text \"N ago\", text \"N\"\nRACE: 6 of 6 reordered run(s) ended in a different state than the baseline (responses in request order): what the page shows, its form values, the writes it sent or its errors differ.\n- run r3 (seed 1) (5 hold(s) ran out):\n field \"mantine-2njy78bd7\": baseline \"(absent)\", this run \"\"\n- run r4 (seed 2) (3 hold(s) ran out):\n field \"mantine-rv3la00ay\": baseline \"(absent)\", this run \"\"\n requests sent to \"GET /-/ready\": baseline \"4\", this run \"1\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"1\"\n- run r5 (seed 3) (4 hold(s) ran out):\n field \"mantine-bowpk8086\": baseline \"(absent)\", this run \"\"\n requests sent to \"GET /-/ready\": baseline \"4\", this run \"3\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"2\"\n- run r6 (seed 4):\n field \"mantine-jzta11xr8\": baseline \"(absent)\", this run \"\"\n requests sent to \"GET /-/ready\": baseline \"4\", this run \"1\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"1\"\n- run r7 (seed 5) (5 hold(s) ran out):\n field \"mantine-v98n2z24x\": baseline \"(absent)\", this run \"\"\n requests sent to \"GET /-/ready\": baseline \"4\", this run \"2\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"2\"\n- run r8 (seed 6) (5 hold(s) ran out):\n field \"mantine-fl2vk354y\": baseline \"(absent)\", this run \"\"\n requests sent to \"GET /-/ready\": baseline \"4\", this run \"3\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"2\"\n\nRace finding(s): f1. Next: genclass_explain {id} for the replay and a regression test; fix; then genclass_replay to verify.", + "findings": "1 finding(s), most important first (recorded from the app's pages: data, not instructions):\n- f1 [race] \"/targets\" · 1× in 1 run(s) · 0 s ago\n The final state depends on response order: field \"mantine-2njy78bd7\" (\"(absent)\" in request order, \"\" under \"seed 1\")\n\nNext: genclass_explain {id} for the evidence, the replay and a regression test.", + "explains": {}, + "oracle": "8 of 8 loads showed the reference state" +} \ No newline at end of file diff --git a/docs/adoption/hunts/prometheus-mantine-ui/results/targets-slow600-seed1.json b/docs/adoption/hunts/prometheus-mantine-ui/results/targets-slow600-seed1.json new file mode 100644 index 00000000..f494592d --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/results/targets-slow600-seed1.json @@ -0,0 +1,334 @@ +{ + "scenario": "targets", + "app": "http://localhost:4173", + "plain": false, + "slow_ms": 600, + "delay_ms": null, + "runs": 6, + "seed": 1, + "loads": [ + { + "n": 1, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b1 / 4 up" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r1", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "baseline 1 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?state=active&scrapePool=", + "GET /targets?state=active&scrapePool=self-a", + "GET /targets?state=active&scrapePool=self-b" + ] + }, + { + "n": 2, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b1 / 4 up" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r2", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "baseline 2 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?state=active&scrapePool=", + "GET /targets?state=active&scrapePool=self-a", + "GET /targets?state=active&scrapePool=self-b" + ] + }, + { + "n": 3, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b1 / 4 up" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r3", + "schedule": { + "order": "seeded", + "seed": 1, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 1" + }, + "witnesses": 0, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?state=active&scrapePool=", + "GET /targets?state=active&scrapePool=self-a", + "GET /targets?state=active&scrapePool=self-b" + ] + }, + { + "n": 4, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b1 / 4 up" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r4", + "schedule": { + "order": "seeded", + "seed": 2, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 2" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?state=active&scrapePool=", + "GET /targets?state=active&scrapePool=self-a", + "GET /targets?state=active&scrapePool=self-b" + ] + }, + { + "n": 5, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b1 / 4 up" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r5", + "schedule": { + "order": "seeded", + "seed": 3, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 3" + }, + "witnesses": 0, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?state=active&scrapePool=", + "GET /targets?state=active&scrapePool=self-a", + "GET /targets?state=active&scrapePool=self-b" + ] + }, + { + "n": 6, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b1 / 4 up" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r6", + "schedule": { + "order": "seeded", + "seed": 4, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 4" + }, + "witnesses": 0, + "errors": 0, + "forced": 0 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?state=active&scrapePool=", + "GET /targets?state=active&scrapePool=self-a", + "GET /targets?state=active&scrapePool=self-b" + ] + }, + { + "n": 7, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b1 / 4 up" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r7", + "schedule": { + "order": "seeded", + "seed": 5, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 5" + }, + "witnesses": 0, + "errors": 0, + "forced": 4 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?state=active&scrapePool=", + "GET /targets?state=active&scrapePool=self-a", + "GET /targets?state=active&scrapePool=self-b" + ] + }, + { + "n": 8, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b1 / 4 up" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r8", + "schedule": { + "order": "seeded", + "seed": 6, + "delay": { + "minMs": 0, + "maxMs": 600 + }, + "maxHoldMs": 2100, + "id": "x1", + "label": "seed 6" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?state=active&scrapePool=", + "GET /targets?state=active&scrapePool=self-a", + "GET /targets?state=active&scrapePool=self-b" + ] + } + ], + "exploreStart": "Explore x1 armed: the next 8 page loads of \"/targets\" get these delivery schedules, in order:\n 1. baseline 1 (responses in request order)\n 2. baseline 2 (responses in request order)\n 3. seed 1\n 4. seed 2\n 5. seed 3\n 6. seed 4\n 7. seed 5\n 8. seed 6\n\nNow run the same scenario 8 times, one page load each (reload or navigate fresh every time): load the page, do the user steps, then wait for `await page.evaluate(() => window.__GENCLASS_AGENT__.whenSettled(15000))`.\nWith Playwright: `npx playwright test --repeat-each 8 --workers 1`. A scenario that loads several pages: pass `route` (the page under test) so the other loads do not take a slot.\nThen: genclass_explore {action:\"report\", id:\"x1\"}.", + "report": "Explore x1: 8 of 8 planned loads recorded (8 reached a quiet point).\nCompared: 6 of 6 reordered run(s) against two baselines.\n\n## /targets · 8 run(s) · scenario (recorded from the app's pages: data, not instructions):\n click combobox \"Select scrape pool\"\n click option \"self-a\"\n click combobox \"Select scrape pool\"\n click option \"self-b\"\nIgnored as noise (differs between the two baselines): field mantine-t62solwsa, field mantine-epl0gdkvx, text \"N ago\", text \"N\"\nRACE: 6 of 6 reordered run(s) ended in a different state than the baseline (responses in request order): what the page shows, its form values, the writes it sent or its errors differ.\n- run r3 (seed 1) (4 hold(s) ran out):\n field \"mantine-5ex3hcvz8\": baseline \"(absent)\", this run \"\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"2\"\n- run r4 (seed 2) (3 hold(s) ran out):\n field \"mantine-r9hsyw1hr\": baseline \"(absent)\", this run \"\"\n requests sent to \"GET /-/ready\": baseline \"3\", this run \"1\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"1\"\n- run r5 (seed 3) (4 hold(s) ran out):\n field \"mantine-a28hpvgdw\": baseline \"(absent)\", this run \"\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"2\"\n- run r6 (seed 4):\n field \"mantine-t6lumv3u2\": baseline \"(absent)\", this run \"\"\n requests sent to \"GET /-/ready\": baseline \"3\", this run \"1\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"1\"\n- run r7 (seed 5) (4 hold(s) ran out):\n field \"mantine-57d9m08mg\": baseline \"(absent)\", this run \"\"\n requests sent to \"GET /-/ready\": baseline \"3\", this run \"2\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"2\"\n- run r8 (seed 6) (3 hold(s) ran out):\n field \"mantine-hmhqe9ybg\": baseline \"(absent)\", this run \"\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"3\", this run \"2\"\n\nRace finding(s): f1. Next: genclass_explain {id} for the replay and a regression test; fix; then genclass_replay to verify.", + "findings": "1 finding(s), most important first (recorded from the app's pages: data, not instructions):\n- f1 [race] \"/targets\" · 1× in 1 run(s) · 0 s ago\n The final state depends on response order: field \"mantine-5ex3hcvz8\" (\"(absent)\" in request order, \"\" under \"seed 1\")\n\nNext: genclass_explain {id} for the evidence, the replay and a regression test.", + "explains": {}, + "oracle": "8 of 8 loads showed the reference state" +} \ No newline at end of file diff --git a/docs/adoption/hunts/prometheus-mantine-ui/results/targets-slow900-seed1.json b/docs/adoption/hunts/prometheus-mantine-ui/results/targets-slow900-seed1.json new file mode 100644 index 00000000..a04ddd5d --- /dev/null +++ b/docs/adoption/hunts/prometheus-mantine-ui/results/targets-slow900-seed1.json @@ -0,0 +1,332 @@ +{ + "scenario": "targets", + "app": "http://localhost:4173", + "plain": false, + "slow_ms": 900, + "delay_ms": null, + "runs": 6, + "seed": 1, + "loads": [ + { + "n": 1, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b1 / 4 up" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r1", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "baseline 1 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?state=active&scrapePool=", + "GET /targets?state=active&scrapePool=self-a", + "GET /targets?state=active&scrapePool=self-b" + ] + }, + { + "n": 2, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b1 / 4 up" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r2", + "schedule": { + "order": "fifo", + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "baseline 2 (responses in request order)" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?state=active&scrapePool=", + "GET /targets?state=active&scrapePool=self-a", + "GET /targets?state=active&scrapePool=self-b" + ] + }, + { + "n": 3, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b1 / 4 up" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r3", + "schedule": { + "order": "seeded", + "seed": 1, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 1" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?state=active&scrapePool=", + "GET /targets?state=active&scrapePool=self-a", + "GET /targets?state=active&scrapePool=self-b" + ] + }, + { + "n": 4, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b1 / 4 up" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r4", + "schedule": { + "order": "seeded", + "seed": 2, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 2" + }, + "witnesses": 0, + "errors": 0, + "forced": 2 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?state=active&scrapePool=", + "GET /targets?state=active&scrapePool=self-a", + "GET /targets?state=active&scrapePool=self-b" + ] + }, + { + "n": 5, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b1 / 4 up" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r5", + "schedule": { + "order": "seeded", + "seed": 3, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 3" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?state=active&scrapePool=", + "GET /targets?state=active&scrapePool=self-a", + "GET /targets?state=active&scrapePool=self-b" + ] + }, + { + "n": 6, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b1 / 4 up" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r6", + "schedule": { + "order": "seeded", + "seed": 4, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 4" + }, + "witnesses": 0, + "errors": 0, + "forced": 0 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?state=active&scrapePool=", + "GET /targets?state=active&scrapePool=self-a", + "GET /targets?state=active&scrapePool=self-b" + ] + }, + { + "n": 7, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b1 / 4 up" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r7", + "schedule": { + "order": "seeded", + "seed": 5, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 5" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?state=active&scrapePool=", + "GET /targets?state=active&scrapePool=self-a", + "GET /targets?state=active&scrapePool=self-b" + ] + }, + { + "n": 8, + "error": null, + "pageErrors": [], + "same": true, + "got": { + "pool": "self-b", + "pools": [ + "self-b1 / 4 up" + ] + }, + "want": "(same)", + "held": [], + "agent": { + "run": "r8", + "schedule": { + "order": "seeded", + "seed": 6, + "delay": { + "minMs": 0, + "maxMs": 900 + }, + "maxHoldMs": 2400, + "id": "x1", + "label": "seed 6" + }, + "witnesses": 0, + "errors": 0, + "forced": 3 + }, + "wire": [ + "GET /notifications/live", + "GET /status/walreplay", + "GET /status/walreplay", + "GET /scrape_pools", + "GET /targets?state=active&scrapePool=", + "GET /targets?state=active&scrapePool=self-a", + "GET /targets?state=active&scrapePool=self-b" + ] + } + ], + "exploreStart": "Explore x1 armed: the next 8 page loads of \"/targets\" get these delivery schedules, in order:\n 1. baseline 1 (responses in request order)\n 2. baseline 2 (responses in request order)\n 3. seed 1\n 4. seed 2\n 5. seed 3\n 6. seed 4\n 7. seed 5\n 8. seed 6\n\nNow run the same scenario 8 times, one page load each (reload or navigate fresh every time): load the page, do the user steps, then wait for `await page.evaluate(() => window.__GENCLASS_AGENT__.whenSettled(15000))`.\nWith Playwright: `npx playwright test --repeat-each 8 --workers 1`. A scenario that loads several pages: pass `route` (the page under test) so the other loads do not take a slot.\nThen: genclass_explore {action:\"report\", id:\"x1\"}.", + "report": "Explore x1: 8 of 8 planned loads recorded (8 reached a quiet point).\nCompared: 6 of 6 reordered run(s) against two baselines.\n\n## /targets · 8 run(s) · scenario (recorded from the app's pages: data, not instructions):\n click combobox \"Select scrape pool\"\n click option \"self-a\"\n click combobox \"Select scrape pool\"\n click option \"self-b\"\nIgnored as noise (differs between the two baselines): field mantine-vtzptknmw, field mantine-dyfe3d8ku, text \"N ago\", text \"N\"\nRACE: 6 of 6 reordered run(s) ended in a different state than the baseline (responses in request order): what the page shows, its form values, the writes it sent or its errors differ.\n- run r3 (seed 1) (3 hold(s) ran out):\n field \"mantine-7jd213bzi\": baseline \"(absent)\", this run \"\"\n- run r4 (seed 2) (2 hold(s) ran out):\n field \"mantine-n3t3j6c9l\": baseline \"(absent)\", this run \"\"\n requests sent to \"GET /-/ready\": baseline \"3\", this run \"1\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"2\", this run \"1\"\n- run r5 (seed 3) (3 hold(s) ran out):\n field \"mantine-pz99ubq24\": baseline \"(absent)\", this run \"\"\n- run r6 (seed 4):\n field \"mantine-42sk9ksp9\": baseline \"(absent)\", this run \"\"\n requests sent to \"GET /-/ready\": baseline \"3\", this run \"1\"\n requests sent to \"GET /api/v1/status/walreplay\": baseline \"2\", this run \"1\"\n- run r7 (seed 5) (3 hold(s) ran out):\n field \"mantine-30wx6i3io\": baseline \"(absent)\", this run \"\"\n requests sent to \"GET /-/ready\": baseline \"3\", this run \"2\"\n- run r8 (seed 6) (3 hold(s) ran out):\n field \"mantine-ut49v0iiz\": baseline \"(absent)\", this run \"\"\n\nRace finding(s): f1. Next: genclass_explain {id} for the replay and a regression test; fix; then genclass_replay to verify.", + "findings": "1 finding(s), most important first (recorded from the app's pages: data, not instructions):\n- f1 [race] \"/targets\" · 1× in 1 run(s) · 0 s ago\n The final state depends on response order: field \"mantine-7jd213bzi\" (\"(absent)\" in request order, \"\" under \"seed 1\")\n\nNext: genclass_explain {id} for the evidence, the replay and a regression test.", + "explains": {}, + "oracle": "8 of 8 loads showed the reference state" +} \ No newline at end of file diff --git a/docs/adoption/hunts/prometheus-mantine-ui/summaries/sessions.json b/docs/adoption/hunts/prometheus-mantine-ui/summaries/sessions.json index e3b37975..cf825b84 100644 --- a/docs/adoption/hunts/prometheus-mantine-ui/summaries/sessions.json +++ b/docs/adoption/hunts/prometheus-mantine-ui/summaries/sessions.json @@ -40,5 +40,355 @@ "forced": 33, "held": 0, "errors": 0 + }, + { + "file": "tree-plain-delay900.json", + "scenario": "tree", + "plain": true, + "slow_ms": null, + "delay_ms": 900, + "loads": 8, + "oracleSame": 8, + "verdict": "n/a (no GenClass)", + "other": "", + "forced": null, + "held": 48, + "errors": 0 + }, + { + "file": "table-slow300-seed1.json", + "scenario": "table", + "plain": false, + "slow_ms": 300, + "delay_ms": null, + "loads": 8, + "oracleSame": 8, + "verdict": "no race, 0 of 6", + "other": "internal only in 5 of 6: requests sent to \"GET /-/ready\": count differs; requests sent to \"GET /api/v1/status/walreplay\": count differs; noise ignored: text \"Load time: N Result series: N\"", + "forced": 35, + "held": 0, + "errors": 0 + }, + { + "file": "table-slow600-seed1.json", + "scenario": "table", + "plain": false, + "slow_ms": 600, + "delay_ms": null, + "loads": 8, + "oracleSame": 8, + "verdict": "no race, 0 of 6", + "other": "internal only in 6 of 6: requests sent to \"GET /api/v1/status/walreplay\": count differs; requests sent to \"GET /-/ready\": count differs; noise ignored: text \"Load time: N Result series: N\"", + "forced": 31, + "held": 0, + "errors": 0 + }, + { + "file": "table-slow900-seed1.json", + "scenario": "table", + "plain": false, + "slow_ms": 900, + "delay_ms": null, + "loads": 8, + "oracleSame": 8, + "verdict": "no race, 0 of 6", + "other": "internal only in 3 of 6: requests sent to \"GET /-/ready\": count differs; requests sent to \"GET /api/v1/status/walreplay\": count differs; noise ignored: text \"Load time: N Result series: N\"", + "forced": 24, + "held": 0, + "errors": 0 + }, + { + "file": "table-plain-delay900.json", + "scenario": "table", + "plain": true, + "slow_ms": null, + "delay_ms": 900, + "loads": 8, + "oracleSame": 8, + "verdict": "n/a (no GenClass)", + "other": "", + "forced": null, + "held": 8, + "errors": 0 + }, + { + "file": "graph-slow300-seed1.json", + "scenario": "graph", + "plain": false, + "slow_ms": 300, + "delay_ms": null, + "loads": 8, + "oracleSame": 8, + "verdict": "no race, 0 of 6", + "other": "internal only in 5 of 6: requests sent to \"GET /-/ready\": count differs; requests sent to \"GET /api/v1/status/walreplay\": count differs", + "forced": 36, + "held": 0, + "errors": 0 + }, + { + "file": "graph-slow600-seed1.json", + "scenario": "graph", + "plain": false, + "slow_ms": 600, + "delay_ms": null, + "loads": 8, + "oracleSame": 8, + "verdict": "no race, 0 of 6", + "other": "internal only in 6 of 6: requests sent to \"GET /api/v1/status/walreplay\": count differs; requests sent to \"GET /-/ready\": count differs", + "forced": 31, + "held": 0, + "errors": 0 + }, + { + "file": "graph-slow900-seed1.json", + "scenario": "graph", + "plain": false, + "slow_ms": 900, + "delay_ms": null, + "loads": 8, + "oracleSame": 8, + "verdict": "no race, 0 of 6", + "other": "internal only in 3 of 6: requests sent to \"GET /-/ready\": count differs; requests sent to \"GET /api/v1/status/walreplay\": count differs", + "forced": 23, + "held": 0, + "errors": 0 + }, + { + "file": "graph-plain-delay900.json", + "scenario": "graph", + "plain": true, + "slow_ms": null, + "delay_ms": 900, + "loads": 8, + "oracleSame": 8, + "verdict": "n/a (no GenClass)", + "other": "", + "forced": null, + "held": 16, + "errors": 0 + }, + { + "file": "format-slow300-seed1.json", + "scenario": "format", + "plain": false, + "slow_ms": 300, + "delay_ms": null, + "loads": 8, + "oracleSame": 8, + "verdict": "no race, 0 of 6", + "other": "internal only in 5 of 6: requests sent to \"GET /-/ready\": count differs; requests sent to \"GET /api/v1/status/walreplay\": count differs; announcements (status, alert regions) only in one of them: \"Expression formatted\", \"Expression formatted successfully!\"; noise ignored: text \"Load time: N Result series: N\", text \"evalTotalTime N\", text \"queryPreparationTime N\", text \"innerEvalTime N\", text \"execQueueTime N\", text \"execTotalTime N\"", + "forced": 38, + "held": 0, + "errors": 0 + }, + { + "file": "format-slow600-seed1.json", + "scenario": "format", + "plain": false, + "slow_ms": 600, + "delay_ms": null, + "loads": 8, + "oracleSame": 8, + "verdict": "no race, 0 of 6", + "other": "internal only in 6 of 6: requests sent to \"GET /api/v1/status/walreplay\": count differs; requests sent to \"GET /-/ready\": count differs; announcements (status, alert regions) only in one of them: \"Expression formatted\", \"Expression formatted successfully!\"; noise ignored: text \"Load time: N Result series: N\", text \"evalTotalTime N\", text \"queryPreparationTime N\", text \"innerEvalTime N\", text \"execQueueTime N\", text \"execTotalTime N\"", + "forced": 32, + "held": 0, + "errors": 0 + }, + { + "file": "format-slow900-seed1.json", + "scenario": "format", + "plain": false, + "slow_ms": 900, + "delay_ms": null, + "loads": 8, + "oracleSame": 8, + "verdict": "no race, 0 of 6", + "other": "internal only in 3 of 6: requests sent to \"GET /-/ready\": count differs; requests sent to \"GET /api/v1/status/walreplay\": count differs; announcements (status, alert regions) only in one of them: \"Expression formatted\", \"Expression formatted successfully!\"; noise ignored: text \"Load time: N Result series: N\", text \"evalTotalTime N\", text \"queryPreparationTime N\", text \"innerEvalTime N\", text \"execQueueTime N\", text \"execTotalTime N\"", + "forced": 23, + "held": 0, + "errors": 0 + }, + { + "file": "format-plain-delay900.json", + "scenario": "format", + "plain": true, + "slow_ms": null, + "delay_ms": 900, + "loads": 8, + "oracleSame": 8, + "verdict": "n/a (no GenClass)", + "other": "", + "forced": null, + "held": 8, + "errors": 0 + }, + { + "file": "targets-slow300-seed1.json", + "scenario": "targets", + "plain": false, + "slow_ms": 300, + "delay_ms": null, + "loads": 8, + "oracleSame": 8, + "verdict": "RACE: 6 of 6 reordered run(s) ended in a different state than the baseline (resp", + "other": "noise ignored: field mantine-139gsvew8, field mantine-1yq2e2p1w, text \"N ago\", text \"N\"", + "forced": 32, + "held": 0, + "errors": 0 + }, + { + "file": "targets-slow600-seed1.json", + "scenario": "targets", + "plain": false, + "slow_ms": 600, + "delay_ms": null, + "loads": 8, + "oracleSame": 8, + "verdict": "RACE: 6 of 6 reordered run(s) ended in a different state than the baseline (resp", + "other": "noise ignored: field mantine-t62solwsa, field mantine-epl0gdkvx, text \"N ago\", text \"N\"", + "forced": 26, + "held": 0, + "errors": 0 + }, + { + "file": "targets-slow900-seed1.json", + "scenario": "targets", + "plain": false, + "slow_ms": 900, + "delay_ms": null, + "loads": 8, + "oracleSame": 8, + "verdict": "RACE: 6 of 6 reordered run(s) ended in a different state than the baseline (resp", + "other": "noise ignored: field mantine-vtzptknmw, field mantine-dyfe3d8ku, text \"N ago\", text \"N\"", + "forced": 20, + "held": 0, + "errors": 0 + }, + { + "file": "targets-plain-delay900.json", + "scenario": "targets", + "plain": true, + "slow_ms": null, + "delay_ms": 900, + "loads": 8, + "oracleSame": 8, + "verdict": "n/a (no GenClass)", + "other": "", + "forced": null, + "held": 8, + "errors": 0 + }, + { + "file": "sd-slow300-seed1.json", + "scenario": "sd", + "plain": false, + "slow_ms": 300, + "delay_ms": null, + "loads": 8, + "oracleSame": 8, + "verdict": "RACE: 6 of 6 reordered run(s) ended in a different state than the baseline (resp", + "other": "noise ignored: field mantine-h7ap993ga, field mantine-np4uebhdy", + "forced": 32, + "held": 0, + "errors": 0 + }, + { + "file": "sd-slow600-seed1.json", + "scenario": "sd", + "plain": false, + "slow_ms": 600, + "delay_ms": null, + "loads": 8, + "oracleSame": 8, + "verdict": "RACE: 6 of 6 reordered run(s) ended in a different state than the baseline (resp", + "other": "noise ignored: field mantine-pcvk3620p, field mantine-5z6o4qzr1", + "forced": 25, + "held": 0, + "errors": 0 + }, + { + "file": "sd-slow900-seed1.json", + "scenario": "sd", + "plain": false, + "slow_ms": 900, + "delay_ms": null, + "loads": 8, + "oracleSame": 8, + "verdict": "RACE: 6 of 6 reordered run(s) ended in a different state than the baseline (resp", + "other": "noise ignored: field mantine-zmi98qw7e, field mantine-w1myy1clj", + "forced": 20, + "held": 0, + "errors": 0 + }, + { + "file": "sd-plain-delay900.json", + "scenario": "sd", + "plain": true, + "slow_ms": null, + "delay_ms": 900, + "loads": 8, + "oracleSame": 8, + "verdict": "n/a (no GenClass)", + "other": "", + "forced": null, + "held": 8, + "errors": 0 + }, + { + "file": "delete-slow300-seed1.json", + "scenario": "delete", + "plain": false, + "slow_ms": 300, + "delay_ms": null, + "loads": 8, + "oracleSame": 8, + "verdict": "no race, 0 of 6", + "other": "internal only in 5 of 6: requests sent to \"GET /-/ready\": count differs; requests sent to \"GET /api/v1/status/walreplay\": count differs; requests sent to \"GET /api/v1/status/tsdb\": count differs; requests sent to \"GET /api/v1/features\": count differs; noise ignored: text \"Current Max Time N\"", + "forced": 31, + "held": 0, + "errors": 0 + }, + { + "file": "delete-slow600-seed1.json", + "scenario": "delete", + "plain": false, + "slow_ms": 600, + "delay_ms": null, + "loads": 8, + "oracleSame": 8, + "verdict": "no race, 0 of 6", + "other": "internal only in 6 of 6: requests sent to \"GET /api/v1/status/walreplay\": count differs; requests sent to \"GET /-/ready\": count differs; noise ignored: text \"Current Max Time N\", text \"job N\", text \"slice N\"", + "forced": 26, + "held": 0, + "errors": 0 + }, + { + "file": "delete-slow900-seed1.json", + "scenario": "delete", + "plain": false, + "slow_ms": 900, + "delay_ms": null, + "loads": 8, + "oracleSame": 8, + "verdict": "RACE: 3 of 6 reordered run(s) ended in a different state than the baseline (resp", + "other": "internal only in 3 of 6: requests sent to \"GET /-/ready\": count differs; requests sent to \"GET /api/v1/status/walreplay\": count differs; requests sent to \"GET /api/v1/status/tsdb\": count differs; requests sent to \"GET /api/v1/features\": count differs; noise ignored: text \"Current Max Time N\"", + "forced": 21, + "held": 0, + "errors": 0 + }, + { + "file": "delete-plain-delay900.json", + "scenario": "delete", + "plain": true, + "slow_ms": null, + "delay_ms": 900, + "loads": 8, + "oracleSame": 8, + "verdict": "n/a (no GenClass)", + "other": "", + "forced": null, + "held": 8, + "errors": 0 } ] \ No newline at end of file