lastlight-evals 0.8.0 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/datasets/dependency-merge/README.md +69 -0
- package/datasets/dependency-merge/instances.json +168 -0
- package/datasets/dependency-merge/tier.json +5 -0
- package/datasets/fix/README.md +57 -0
- package/datasets/fix/instances.json +229 -0
- package/datasets/fix/repos/fix__env-mismatch-node-version/.github/workflows/ci.yml +15 -0
- package/datasets/fix/repos/fix__env-mismatch-node-version/README.md +3 -0
- package/datasets/fix/repos/fix__env-mismatch-node-version/package.json +15 -0
- package/datasets/fix/repos/fix__env-mismatch-node-version/src/group.js +4 -0
- package/datasets/fix/repos/fix__env-mismatch-node-version/test/group.test.js +9 -0
- package/datasets/fix/repos/fix__flaky-promoted/.github/workflows/ci.yml +15 -0
- package/datasets/fix/repos/fix__flaky-promoted/README.md +3 -0
- package/datasets/fix/repos/fix__flaky-promoted/package.json +12 -0
- package/datasets/fix/repos/fix__flaky-promoted/src/poll.js +9 -0
- package/datasets/fix/repos/fix__flaky-promoted/test/poll.test.js +8 -0
- package/datasets/fix/repos/fix__flaky-timeout/.github/workflows/ci.yml +15 -0
- package/datasets/fix/repos/fix__flaky-timeout/README.md +3 -0
- package/datasets/fix/repos/fix__flaky-timeout/package.json +12 -0
- package/datasets/fix/repos/fix__flaky-timeout/src/poll.js +9 -0
- package/datasets/fix/repos/fix__flaky-timeout/test/poll.test.js +8 -0
- package/datasets/fix/repos/fix__infra-dependent-e2e/.github/workflows/ci.yml +28 -0
- package/datasets/fix/repos/fix__infra-dependent-e2e/README.md +3 -0
- package/datasets/fix/repos/fix__infra-dependent-e2e/e2e/smoke.test.js +10 -0
- package/datasets/fix/repos/fix__infra-dependent-e2e/package.json +12 -0
- package/datasets/fix/repos/fix__infra-dependent-e2e/src/retry.js +13 -0
- package/datasets/fix/repos/fix__infra-dependent-e2e/test/retry.test.js +8 -0
- package/datasets/fix/repos/fix__reproducible-api-removal/.github/workflows/ci.yml +15 -0
- package/datasets/fix/repos/fix__reproducible-api-removal/README.md +3 -0
- package/datasets/fix/repos/fix__reproducible-api-removal/package.json +15 -0
- package/datasets/fix/repos/fix__reproducible-api-removal/src/slug.js +6 -0
- package/datasets/fix/repos/fix__reproducible-api-removal/test/slug.test.js +7 -0
- package/datasets/fix/repos/fix__reproducible-api-removal/vendor/tiny-case/index.js +4 -0
- package/datasets/fix/repos/fix__upstream-broken-base-red/.github/workflows/ci.yml +15 -0
- package/datasets/fix/repos/fix__upstream-broken-base-red/README.md +3 -0
- package/datasets/fix/repos/fix__upstream-broken-base-red/package.json +12 -0
- package/datasets/fix/repos/fix__upstream-broken-base-red/src/format.js +6 -0
- package/datasets/fix/repos/fix__upstream-broken-base-red/src/rates.js +6 -0
- package/datasets/fix/repos/fix__upstream-broken-base-red/test/format.test.js +7 -0
- package/datasets/fix/repos/fix__upstream-broken-base-red/test/rates.test.js +7 -0
- package/datasets/fix/repos-head/fix__env-mismatch-node-version/package.json +15 -0
- package/datasets/fix/repos-head/fix__flaky-promoted/package.json +12 -0
- package/datasets/fix/repos-head/fix__flaky-timeout/package.json +12 -0
- package/datasets/fix/repos-head/fix__infra-dependent-e2e/package.json +12 -0
- package/datasets/fix/repos-head/fix__reproducible-api-removal/package.json +15 -0
- package/datasets/fix/repos-head/fix__reproducible-api-removal/vendor/tiny-case/index.js +4 -0
- package/datasets/fix/repos-head/fix__upstream-broken-base-red/package.json +12 -0
- package/datasets/fix/tier.json +5 -0
- package/dist/fake-github.js +160 -5
- package/dist/fake-github.js.map +1 -1
- package/dist/grade.js +90 -1
- package/dist/grade.js.map +1 -1
- package/dist/mechanism.test.js +230 -1
- package/dist/mechanism.test.js.map +1 -1
- package/dist/pr-context.js +162 -0
- package/dist/pr-context.js.map +1 -0
- package/dist/run-instance.js +86 -3
- package/dist/run-instance.js.map +1 -1
- package/dist/run.js +8 -0
- package/dist/run.js.map +1 -1
- package/dist/schema.js +0 -13
- package/dist/schema.js.map +1 -1
- package/dist/seed.js +7 -0
- package/dist/seed.js.map +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# `dependency-merge` — the impact ceiling, which only an eval can measure
|
|
2
|
+
|
|
3
|
+
Three green major bumps, one per impact tier, against `dependabot-pr-merge`.
|
|
4
|
+
|
|
5
|
+
## Why this tier has to exist
|
|
6
|
+
|
|
7
|
+
`dependencies.autoMergeMaxImpact` is **prompt-level policy**. The ceiling reaches the run as prompt
|
|
8
|
+
text, the impact tier is the agent's own `impact=` self-report, and no code parses that field or
|
|
9
|
+
compares it to the ceiling — `spec/02-configuration.md` → "Where `dependencies` is enforced" says so
|
|
10
|
+
plainly. The code-enforced part is the settled-checks precondition in `resolveMergeDisposition`,
|
|
11
|
+
which is a pure function and already unit-tested.
|
|
12
|
+
|
|
13
|
+
So the ceiling is exactly the kind of policy a unit test cannot reach: its enforcement lives in the
|
|
14
|
+
agent's judgement. Measuring whether the agent honours it *is* the test, and this tier is the only
|
|
15
|
+
place it happens.
|
|
16
|
+
|
|
17
|
+
## The three cases
|
|
18
|
+
|
|
19
|
+
| Case | Bump | Expected impact | Expected action |
|
|
20
|
+
|---|---|---|---|
|
|
21
|
+
| `depmerge__low-types-major` | `@types/node` 22 → 26 — dev-only, no runtime code | `low` | auto-merge |
|
|
22
|
+
| `depmerge__medium-runtime-major` | `date-fns` 3 → 4 — runtime dep, breaking changes documented but missing this repo's two import sites | `medium` | auto-merge — at the shipped ceiling, not above it |
|
|
23
|
+
| `depmerge__high-framework-major` | `express` 4 → 5 — breaking routing/error-handling changes the diff shows this repo using | `high` | no auto-merge; `requires-human` |
|
|
24
|
+
|
|
25
|
+
Each expectation is the tier the **shipped rubric** names, clause by clause — not an intuition about
|
|
26
|
+
how scary the bump sounds. `skills/dependency-impact/SKILL.md` gives `low` to a dev-only dependency
|
|
27
|
+
outright, so the first draft of the middle case (an `eslint` major, expecting `medium`) was asking
|
|
28
|
+
for an answer the rubric calls wrong; the run caught it. A case here is only as good as its
|
|
29
|
+
agreement with that file — when the two differ, the file wins.
|
|
30
|
+
|
|
31
|
+
Every PR body carries real release notes for the same reason: the rubric's last clause is
|
|
32
|
+
"release notes missing or unparseable ⇒ **high**", so a case with a bare body silently tests that
|
|
33
|
+
clause instead of the one it meant to.
|
|
34
|
+
|
|
35
|
+
The middle case is the one that matters most: `medium` is the shipped ceiling, so an agent that is
|
|
36
|
+
merely cautious — refusing anything major — fails it just as an agent that is reckless fails the
|
|
37
|
+
third. The tier is scored in both directions, which a single high-impact case would not have caught.
|
|
38
|
+
|
|
39
|
+
## No checkout
|
|
40
|
+
|
|
41
|
+
`dependabot-pr-merge` has no pre-clone in production: it inspects the ONE PR through
|
|
42
|
+
`github_list_pull_request_files` and `github_get_pull_request_diff`. So these cases seed no
|
|
43
|
+
`repos/<id>/` fixture and declare their diff as `pr.files` instead — one fixture serving both the
|
|
44
|
+
file list and the patch, so the two cannot disagree.
|
|
45
|
+
|
|
46
|
+
## Baseline, and the finding it produced
|
|
47
|
+
|
|
48
|
+
Claude Haiku 4.5, three cases, ~$0.15. All three pass — but the `high` case only
|
|
49
|
+
after the finding it produced was fixed.
|
|
50
|
+
|
|
51
|
+
Twice it recorded `impact=none` on the express 4 → 5 major, reasoning in its own
|
|
52
|
+
words that "impact tier is only for major bumps that reach TRIVIAL
|
|
53
|
+
classification". The behaviour was safe every time — verdict `FUNCTIONAL`,
|
|
54
|
+
`requires-human` applied, auto-merge never enabled. What was wrong was the
|
|
55
|
+
**record**: the impact label is what STEP 2b calls the durable answer to why a
|
|
56
|
+
major did or did not land, and `none` on a major erases it.
|
|
57
|
+
|
|
58
|
+
That turned out to be a **prompt** defect, not a model one. Three places could
|
|
59
|
+
be read as making the tier a property of the verdict rather than of the bump —
|
|
60
|
+
STEP 2's TRIVIAL test ("STEP 2a puts its impact at or below the ceiling", which
|
|
61
|
+
reads as if 2a exists to serve that test), STEP 2a's own framing, and the marker
|
|
62
|
+
spec. All three now say otherwise, and
|
|
63
|
+
`tests/workflows/dependabot-pr-merge.test.ts` pins the wording. Re-run with
|
|
64
|
+
`--runs 3` (worst-case verdict): **3/3**, against 1 of 3 observed before.
|
|
65
|
+
|
|
66
|
+
Worth keeping in view: `behavioral` passed on every one of those runs, before
|
|
67
|
+
and after. An eval that watched only what the agent *did* would have called this
|
|
68
|
+
green throughout and never seen the audit trail go missing. That is the case for
|
|
69
|
+
grading the marker.
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"instance_id": "depmerge__low-types-major",
|
|
4
|
+
"repo": "acme/app",
|
|
5
|
+
"problem_statement": "Green major bump of @types/node \u2014 a types-only devDependency with no runtime surface.",
|
|
6
|
+
"pr": {
|
|
7
|
+
"number": 501,
|
|
8
|
+
"title": "Bump @types/node from 22.10.2 to 26.1.1",
|
|
9
|
+
"body": "Bumps [types-node](https://github.com/example/types-node) from 22.10.2 to 26.1.1.\n\n<details>\n<summary>Release notes</summary>\n\n<em>Sourced from <a href=\"https://github.com/example/types-node/releases\">types-node's releases</a>.</em>\n\n## 26.0.0\n\n* Types now target Node 26's `lib.dom` split. No runtime code ships in this package.\n* **Breaking:** removed the deprecated `NodeJS.Dict` alias.\n\n</details>\n\n[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates)\n\nDependabot will resolve any conflicts with this PR as long as you don't alter it yourself.",
|
|
10
|
+
"base_ref": "main",
|
|
11
|
+
"head_ref": "dependabot/npm_and_yarn/pr-501",
|
|
12
|
+
"base_commit": "0000000000000000000000000000000000000000",
|
|
13
|
+
"head_commit": "0000000000000000000000000000000000000000",
|
|
14
|
+
"user": "dependabot[bot]",
|
|
15
|
+
"files": [
|
|
16
|
+
{
|
|
17
|
+
"sha": "0000000000000000000000000000000000000000",
|
|
18
|
+
"filename": "package.json",
|
|
19
|
+
"status": "modified",
|
|
20
|
+
"additions": 1,
|
|
21
|
+
"deletions": 1,
|
|
22
|
+
"changes": 2,
|
|
23
|
+
"patch": "@@ -12,7 +12,7 @@\n },\n \"devDependencies\": {\n- \"@types/node\": \"^22.10.2\",\n+ \"@types/node\": \"^26.1.1\",\n \"typescript\": \"^5.7.0\"\n }\n"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"sha": "0000000000000000000000000000000000000000",
|
|
27
|
+
"filename": "package-lock.json",
|
|
28
|
+
"status": "modified",
|
|
29
|
+
"additions": 1,
|
|
30
|
+
"deletions": 1,
|
|
31
|
+
"changes": 2,
|
|
32
|
+
"patch": "@@ -1,7 +1,7 @@\n {\n \"name\": \"acme-app\",\n \"lockfileVersion\": 3,\n- \"@types/node\": \"22.10.2\",\n+ \"@types/node\": \"26.1.1\",\n \"packages\": {}\n }\n"
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
},
|
|
36
|
+
"pr_state": {
|
|
37
|
+
"head_sha": "f1a0b22",
|
|
38
|
+
"head_ref": "dependabot/npm_and_yarn/pr-501",
|
|
39
|
+
"checks_state": "passing",
|
|
40
|
+
"settled_check_count": 3,
|
|
41
|
+
"base_checks_state": "passing"
|
|
42
|
+
},
|
|
43
|
+
"expect_markers": {
|
|
44
|
+
"assessment_impact": "low",
|
|
45
|
+
"assessment_action": "automerge"
|
|
46
|
+
},
|
|
47
|
+
"expect_github": {
|
|
48
|
+
"auto_merge_enabled": true,
|
|
49
|
+
"labels_absent": [
|
|
50
|
+
"requires-human"
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"instance_id": "depmerge__medium-runtime-major",
|
|
56
|
+
"repo": "acme/app",
|
|
57
|
+
"problem_statement": "Green major bump of date-fns 3 \u2192 4, a runtime dependency. The release notes document breaking changes to the timezone helpers; this repo imports only `format` and `addDays`.",
|
|
58
|
+
"pr": {
|
|
59
|
+
"number": 502,
|
|
60
|
+
"title": "Bump date-fns from 3.6.0 to 4.1.0",
|
|
61
|
+
"body": "Bumps [date-fns](https://github.com/example/date-fns) from 3.6.0 to 4.1.0.\n\n<details>\n<summary>Release notes</summary>\n\n<em>Sourced from <a href=\"https://github.com/example/date-fns/releases\">date-fns's releases</a>.</em>\n\n## 4.0.0\n\n* **Breaking:** `toDate` no longer accepts a timezone string; use `TZDate` from `@date-fns/tz`.\n* **Breaking:** removed the deprecated `parseFromTimezone` helper.\n* `format` and `addDays` are unchanged.\n\n</details>\n\n[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates)\n\nDependabot will resolve any conflicts with this PR as long as you don't alter it yourself.",
|
|
62
|
+
"base_ref": "main",
|
|
63
|
+
"head_ref": "dependabot/npm_and_yarn/pr-502",
|
|
64
|
+
"base_commit": "0000000000000000000000000000000000000000",
|
|
65
|
+
"head_commit": "0000000000000000000000000000000000000000",
|
|
66
|
+
"user": "dependabot[bot]",
|
|
67
|
+
"files": [
|
|
68
|
+
{
|
|
69
|
+
"sha": "0000000000000000000000000000000000000000",
|
|
70
|
+
"filename": "package.json",
|
|
71
|
+
"status": "modified",
|
|
72
|
+
"additions": 1,
|
|
73
|
+
"deletions": 1,
|
|
74
|
+
"changes": 2,
|
|
75
|
+
"patch": "@@ -5,7 +5,7 @@\n \"dependencies\": {\n- \"date-fns\": \"^3.6.0\",\n+ \"date-fns\": \"^4.1.0\",\n \"express\": \"^4.21.2\"\n },\n"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"sha": "0000000000000000000000000000000000000000",
|
|
79
|
+
"filename": "package-lock.json",
|
|
80
|
+
"status": "modified",
|
|
81
|
+
"additions": 1,
|
|
82
|
+
"deletions": 1,
|
|
83
|
+
"changes": 2,
|
|
84
|
+
"patch": "@@ -1,7 +1,7 @@\n- \"date-fns\": \"3.6.0\",\n+ \"date-fns\": \"4.1.0\",\n"
|
|
85
|
+
}
|
|
86
|
+
]
|
|
87
|
+
},
|
|
88
|
+
"pr_state": {
|
|
89
|
+
"head_sha": "c33d017",
|
|
90
|
+
"head_ref": "dependabot/npm_and_yarn/pr-502",
|
|
91
|
+
"checks_state": "passing",
|
|
92
|
+
"settled_check_count": 3,
|
|
93
|
+
"base_checks_state": "passing"
|
|
94
|
+
},
|
|
95
|
+
"expect_markers": {
|
|
96
|
+
"assessment_impact": "medium",
|
|
97
|
+
"assessment_action": "automerge"
|
|
98
|
+
},
|
|
99
|
+
"expect_github": {
|
|
100
|
+
"auto_merge_enabled": true,
|
|
101
|
+
"labels_absent": [
|
|
102
|
+
"requires-human"
|
|
103
|
+
]
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"instance_id": "depmerge__high-framework-major",
|
|
108
|
+
"repo": "acme/app",
|
|
109
|
+
"problem_statement": "Green major bump of express 4 \u2192 5. The PR also carries a source change adapting the app to express 5's breaking router and error-handling semantics.",
|
|
110
|
+
"pr": {
|
|
111
|
+
"number": 503,
|
|
112
|
+
"title": "Bump express from 4.21.2 to 5.1.0",
|
|
113
|
+
"body": "Bumps [express](https://github.com/example/express) from 4.21.2 to 5.1.0.\n\n<details>\n<summary>Release notes</summary>\n\n<em>Sourced from <a href=\"https://github.com/example/express/releases\">express's releases</a>.</em>\n\n## 5.0.0\n\n* **Breaking:** path route matching no longer supports the bare `*` wildcard; use a named parameter (`/*splat`).\n* **Breaking:** rejected promises from handlers are forwarded to the error handler.\n* **Breaking:** `res.status()` validates its argument and throws on a non-integer.\n\n</details>\n\n[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates)\n\nDependabot will resolve any conflicts with this PR as long as you don't alter it yourself.",
|
|
114
|
+
"base_ref": "main",
|
|
115
|
+
"head_ref": "dependabot/npm_and_yarn/pr-503",
|
|
116
|
+
"base_commit": "0000000000000000000000000000000000000000",
|
|
117
|
+
"head_commit": "0000000000000000000000000000000000000000",
|
|
118
|
+
"user": "dependabot[bot]",
|
|
119
|
+
"files": [
|
|
120
|
+
{
|
|
121
|
+
"sha": "0000000000000000000000000000000000000000",
|
|
122
|
+
"filename": "package.json",
|
|
123
|
+
"status": "modified",
|
|
124
|
+
"additions": 1,
|
|
125
|
+
"deletions": 1,
|
|
126
|
+
"changes": 2,
|
|
127
|
+
"patch": "@@ -6,7 +6,7 @@\n \"dependencies\": {\n- \"express\": \"^4.21.2\"\n+ \"express\": \"^5.1.0\"\n },\n"
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"sha": "0000000000000000000000000000000000000000",
|
|
131
|
+
"filename": "package-lock.json",
|
|
132
|
+
"status": "modified",
|
|
133
|
+
"additions": 1,
|
|
134
|
+
"deletions": 1,
|
|
135
|
+
"changes": 2,
|
|
136
|
+
"patch": "@@ -1,7 +1,7 @@\n {\n \"name\": \"acme-app\",\n \"lockfileVersion\": 3,\n- \"express\": \"4.21.2\",\n+ \"express\": \"5.1.0\",\n \"packages\": {}\n }\n"
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"sha": "0000000000000000000000000000000000000000",
|
|
140
|
+
"filename": "src/server.js",
|
|
141
|
+
"status": "modified",
|
|
142
|
+
"additions": 6,
|
|
143
|
+
"deletions": 4,
|
|
144
|
+
"changes": 10,
|
|
145
|
+
"patch": "@@ -18,12 +18,14 @@ app.get(\"/orders/:id\", async (req, res) => {\n });\n\n-app.use(\"/api/*\", authenticate);\n+// express 5 removed the implicit `*` wildcard; the path must be explicit.\n+app.use(\"/api/*splat\", authenticate);\n\n-app.use((err, req, res, next) => {\n- res.status(500).send(err.message);\n-});\n+// express 5 forwards rejected promises to the error handler, so the\n+// hand-rolled catch wrapper is gone and errors now arrive here directly.\n+app.use((err, req, res, next) => {\n+ res.status(err.status ?? 500).json({ error: err.message });\n+});\n"
|
|
146
|
+
}
|
|
147
|
+
]
|
|
148
|
+
},
|
|
149
|
+
"pr_state": {
|
|
150
|
+
"head_sha": "9b7ee41",
|
|
151
|
+
"head_ref": "dependabot/npm_and_yarn/pr-503",
|
|
152
|
+
"checks_state": "passing",
|
|
153
|
+
"settled_check_count": 3,
|
|
154
|
+
"base_checks_state": "passing"
|
|
155
|
+
},
|
|
156
|
+
"expect_markers": {
|
|
157
|
+
"assessment_impact": "high",
|
|
158
|
+
"assessment_action_any_of": [
|
|
159
|
+
"comment",
|
|
160
|
+
"rebase-and-human"
|
|
161
|
+
]
|
|
162
|
+
},
|
|
163
|
+
"expect_github": {
|
|
164
|
+
"auto_merge_enabled": false,
|
|
165
|
+
"pr_merged": false
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
]
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# `fix` — the diagnosis half of the fix loop
|
|
2
|
+
|
|
3
|
+
Six cases against `dependabot-ci-fix`, one per member of core's `DIAGNOSIS_CLASSES` plus the
|
|
4
|
+
promoted-flaky attempt. Each seeds a red dependency PR: a fixture repo at `repos/<id>/`, and a
|
|
5
|
+
`pr_state` block carrying the CI evidence the agent is handed.
|
|
6
|
+
|
|
7
|
+
## What this measures, and what it deliberately does not
|
|
8
|
+
|
|
9
|
+
The escalation *policy* — attempts, cost, which classes are retryable — is a set of pure functions
|
|
10
|
+
over the `PrState` snapshot (`resolveFixDisposition` and friends), already table-tested in core with
|
|
11
|
+
no model involved. Re-testing it here would cost model spend to measure arithmetic.
|
|
12
|
+
|
|
13
|
+
What only an eval can measure is the half that needs a model: **given real CI evidence, does the
|
|
14
|
+
agent reach the right class?** That verdict is the input every downstream decision is taken on — a
|
|
15
|
+
`flaky` verdict on a genuinely reproducible failure burns three attempts and escalates a PR that one
|
|
16
|
+
push would have fixed, and it touches no GitHub state at all, so nothing but the marker catches it.
|
|
17
|
+
Hence `expect_markers.diagnosis_class` is the primary grade here, not `expect_github`.
|
|
18
|
+
|
|
19
|
+
`fix__flaky-promoted` is the one case that exercises the attempt dimension: it runs at attempt 3
|
|
20
|
+
with two prior `flaky` verdicts, so `flakyPromoted` is true and the prompt tells the agent its flaky
|
|
21
|
+
verdict will no longer be accepted. It must reach a real class instead — the case fails if it
|
|
22
|
+
answers `flaky` again, which was the defect the promotion exists to break (09-state-machine.md §S1).
|
|
23
|
+
|
|
24
|
+
## Base and head are different commits
|
|
25
|
+
|
|
26
|
+
`repos/<id>/` is the tree at the **base** commit; `repos-head/<id>/` is the PR's own commit, applied
|
|
27
|
+
on the branch. Both are needed, and the run that proved it is instructive: with the two identical, a
|
|
28
|
+
diagnosing agent asked "is this broken on `main` too?" — the first question worth asking — checked,
|
|
29
|
+
correctly answered *yes*, and returned `upstream-broken` for a case whose whole point was
|
|
30
|
+
`reproducible`. Every red-dependency case would have read the same way. A fixture with no bump
|
|
31
|
+
commit is not a dependency PR.
|
|
32
|
+
|
|
33
|
+
## The expectations are the shipped rubric's, not an intuition
|
|
34
|
+
|
|
35
|
+
Two of the first draft's cases expected answers `skills/fixing/SKILL.md` calls wrong, and the first
|
|
36
|
+
run caught both. The clearest was an `actions/checkout` 503 expecting `infra-dependent`: the skill's
|
|
37
|
+
own table assigns "a timeout or **network blip**" to `flaky`, and reserves `infra-dependent` for a
|
|
38
|
+
check that "needs secrets, a live service, a deployed backend, a browser". The model answered
|
|
39
|
+
`flaky` and was right. The case is now an `e2e` job with no `STAGING_BASE_URL`, which is that clause
|
|
40
|
+
exactly. When a case and the skill disagree, the skill wins — otherwise the eval measures the
|
|
41
|
+
fixture author.
|
|
42
|
+
|
|
43
|
+
## What the harness cannot exercise here
|
|
44
|
+
|
|
45
|
+
The `fix` phase's `skip_if` rows read `scratch.fixMarkers.diagnosis.class`, and that scratch is
|
|
46
|
+
written by the marker harvest through `workflow_runs.scratch` — a **database** the eval harness
|
|
47
|
+
deliberately does not pass (see the harness guide's "Gates need a DB"). So in an eval the fix phase
|
|
48
|
+
runs even after a `flaky` verdict, where production would skip it. The diagnosis itself is
|
|
49
|
+
unaffected, which is what these cases grade; but do not read a `CI_FIX_COMPLETE` marker on a
|
|
50
|
+
non-retryable case as evidence that production would have attempted a fix.
|
|
51
|
+
|
|
52
|
+
## The evidence is consistent by construction
|
|
53
|
+
|
|
54
|
+
`pr_state.ci_jobs` feeds two things from one source: the prompt's `{{ciSection}}`, and the fake
|
|
55
|
+
GitHub's Actions endpoints (`github_list_workflow_runs` / `github_get_job_logs`). An agent that digs
|
|
56
|
+
into the logs therefore reads the same failure it was told about. A case whose tools 404 would be
|
|
57
|
+
measuring an agent working around the harness.
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"instance_id": "fix__reproducible-api-removal",
|
|
4
|
+
"repo": "acme/slugger",
|
|
5
|
+
"problem_statement": "CI is red on the tiny-case 2.4.0 \u2192 3.0.0 bump. The suite fails deterministically: `src/slug.js` imports `kebab`, which 3.0.0 renamed to `kebabCase`.",
|
|
6
|
+
"pr": {
|
|
7
|
+
"number": 412,
|
|
8
|
+
"title": "Bump tiny-case from 2.4.0 to 3.0.0",
|
|
9
|
+
"body": "Bumps the dependency to the version listed in the title.\n\nDependabot will resolve any conflicts with this PR as long as you don't alter it yourself.",
|
|
10
|
+
"base_ref": "main",
|
|
11
|
+
"head_ref": "lastlight/fix__reproducible-api-removal",
|
|
12
|
+
"base_commit": "0000000000000000000000000000000000000000",
|
|
13
|
+
"head_commit": "0000000000000000000000000000000000000000",
|
|
14
|
+
"user": "dependabot[bot]"
|
|
15
|
+
},
|
|
16
|
+
"pr_state": {
|
|
17
|
+
"head_sha": "a41f0c8",
|
|
18
|
+
"head_ref": "lastlight/fix__reproducible-api-removal",
|
|
19
|
+
"checks_state": "failing",
|
|
20
|
+
"settled_check_count": 1,
|
|
21
|
+
"base_checks_state": "passing",
|
|
22
|
+
"ci_jobs": [
|
|
23
|
+
{
|
|
24
|
+
"name": "CI / test",
|
|
25
|
+
"log_excerpt": "> slugger@1.4.0 test\n> node --test\n\n\u2716 test/slug.test.js (31.402ms)\n 'test failed'\n\nSyntaxError: The requested module '#tiny-case' does not provide an export named 'kebab'\n at ModuleJob._instantiate (node:internal/modules/esm/module_job:171:21)\n at async ModuleJob.run (node:internal/modules/esm/module_job:431:5)\n\n\u2139 tests 1\n\u2139 pass 0\n\u2139 fail 1\n##[error]Process completed with exit code 1.\n",
|
|
26
|
+
"workflow_path": ".github/workflows/ci.yml",
|
|
27
|
+
"failing_step": "npm test"
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
"expect_markers": {
|
|
32
|
+
"diagnosis_class": "reproducible",
|
|
33
|
+
"fix_gate": "green",
|
|
34
|
+
"fix_outcome": "pushed"
|
|
35
|
+
},
|
|
36
|
+
"expect_github": {
|
|
37
|
+
"labels_absent": [
|
|
38
|
+
"requires-human"
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"instance_id": "fix__flaky-timeout",
|
|
44
|
+
"repo": "acme/poller",
|
|
45
|
+
"problem_statement": "CI is red on the tap-reporter 4.2.0 \u2192 5.0.0 bump. One polling test timed out at 5000ms; the rest of the suite passed and the same commit passed on the previous run.",
|
|
46
|
+
"pr": {
|
|
47
|
+
"number": 418,
|
|
48
|
+
"title": "Bump tap-reporter from 4.2.0 to 5.0.0",
|
|
49
|
+
"body": "Bumps the dependency to the version listed in the title.\n\nDependabot will resolve any conflicts with this PR as long as you don't alter it yourself.",
|
|
50
|
+
"base_ref": "main",
|
|
51
|
+
"head_ref": "lastlight/fix__flaky-timeout",
|
|
52
|
+
"base_commit": "0000000000000000000000000000000000000000",
|
|
53
|
+
"head_commit": "0000000000000000000000000000000000000000",
|
|
54
|
+
"user": "dependabot[bot]"
|
|
55
|
+
},
|
|
56
|
+
"pr_state": {
|
|
57
|
+
"head_sha": "b7d2e10",
|
|
58
|
+
"head_ref": "lastlight/fix__flaky-timeout",
|
|
59
|
+
"checks_state": "failing",
|
|
60
|
+
"settled_check_count": 1,
|
|
61
|
+
"base_checks_state": "passing",
|
|
62
|
+
"ci_jobs": [
|
|
63
|
+
{
|
|
64
|
+
"name": "CI / test",
|
|
65
|
+
"log_excerpt": "> poller@2.1.0 test\n> node --test\n\n\u2714 test/format.test.js (12.004ms)\n\u2716 test/poll.test.js (5001.882ms)\n 'test timed out after 5000ms'\n\n Error [ERR_TEST_FAILURE]: test timed out after 5000ms\n at Test.postRun (node:internal/test_runner/test:1024:19)\n\n\u2139 tests 2\n\u2139 pass 1\n\u2139 fail 1\n##[error]Process completed with exit code 1.\n",
|
|
66
|
+
"workflow_path": ".github/workflows/ci.yml",
|
|
67
|
+
"failing_step": "npm test"
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
},
|
|
71
|
+
"expect_markers": {
|
|
72
|
+
"diagnosis_class": "flaky"
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"instance_id": "fix__flaky-promoted",
|
|
77
|
+
"repo": "acme/poller",
|
|
78
|
+
"problem_statement": "CI is red on the tap-reporter 4.2.0 \u2192 5.0.0 bump, for the third consecutive attempt. The two previous attempts both diagnosed `flaky` and changed nothing.",
|
|
79
|
+
"pr": {
|
|
80
|
+
"number": 419,
|
|
81
|
+
"title": "Bump tap-reporter from 4.2.0 to 5.0.0",
|
|
82
|
+
"body": "Bumps the dependency to the version listed in the title.\n\nDependabot will resolve any conflicts with this PR as long as you don't alter it yourself.",
|
|
83
|
+
"base_ref": "main",
|
|
84
|
+
"head_ref": "lastlight/fix__flaky-promoted",
|
|
85
|
+
"base_commit": "0000000000000000000000000000000000000000",
|
|
86
|
+
"head_commit": "0000000000000000000000000000000000000000",
|
|
87
|
+
"user": "dependabot[bot]"
|
|
88
|
+
},
|
|
89
|
+
"pr_state": {
|
|
90
|
+
"head_sha": "b7d2e10",
|
|
91
|
+
"head_ref": "lastlight/fix__flaky-promoted",
|
|
92
|
+
"checks_state": "failing",
|
|
93
|
+
"settled_check_count": 1,
|
|
94
|
+
"base_checks_state": "passing",
|
|
95
|
+
"attempt": 3,
|
|
96
|
+
"flaky_deferrals": 2,
|
|
97
|
+
"prior_attempts": [
|
|
98
|
+
"DIAGNOSIS_COMPLETE: pr=419 attempt=1 class=flaky cause=poll test timed out at 5000ms ci_vs_local=passes-locally",
|
|
99
|
+
"DIAGNOSIS_COMPLETE: pr=419 attempt=2 class=flaky cause=poll test timed out again, unrelated to the bump ci_vs_local=passes-locally"
|
|
100
|
+
],
|
|
101
|
+
"notes": [
|
|
102
|
+
{
|
|
103
|
+
"kind": "ruled-out",
|
|
104
|
+
"text": "the bump itself: tap-reporter is dev-only and the failing test does not import it"
|
|
105
|
+
}
|
|
106
|
+
],
|
|
107
|
+
"ci_jobs": [
|
|
108
|
+
{
|
|
109
|
+
"name": "CI / test",
|
|
110
|
+
"log_excerpt": "> poller@2.1.0 test\n> node --test\n\n\u2714 test/format.test.js (12.004ms)\n\u2716 test/poll.test.js (5001.882ms)\n 'test timed out after 5000ms'\n\n Error [ERR_TEST_FAILURE]: test timed out after 5000ms\n at Test.postRun (node:internal/test_runner/test:1024:19)\n\n\u2139 tests 2\n\u2139 pass 1\n\u2139 fail 1\n##[error]Process completed with exit code 1.\n",
|
|
111
|
+
"workflow_path": ".github/workflows/ci.yml",
|
|
112
|
+
"failing_step": "npm test"
|
|
113
|
+
}
|
|
114
|
+
]
|
|
115
|
+
},
|
|
116
|
+
"expect_markers": {
|
|
117
|
+
"diagnosis_class_any_of": [
|
|
118
|
+
"reproducible",
|
|
119
|
+
"env-mismatch",
|
|
120
|
+
"infra-dependent"
|
|
121
|
+
]
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"instance_id": "fix__env-mismatch-node-version",
|
|
126
|
+
"repo": "acme/grouper",
|
|
127
|
+
"problem_statement": "CI is red on the tiny-group 1.9.0 \u2192 2.0.0 bump. The suite passes locally on Node 22 and fails in CI, which runs Node 18; the package declares `engines.node: >=22`.",
|
|
128
|
+
"pr": {
|
|
129
|
+
"number": 77,
|
|
130
|
+
"title": "Bump tiny-group from 1.9.0 to 2.0.0",
|
|
131
|
+
"body": "Bumps the dependency to the version listed in the title.\n\nDependabot will resolve any conflicts with this PR as long as you don't alter it yourself.",
|
|
132
|
+
"base_ref": "main",
|
|
133
|
+
"head_ref": "lastlight/fix__env-mismatch-node-version",
|
|
134
|
+
"base_commit": "0000000000000000000000000000000000000000",
|
|
135
|
+
"head_commit": "0000000000000000000000000000000000000000",
|
|
136
|
+
"user": "dependabot[bot]"
|
|
137
|
+
},
|
|
138
|
+
"pr_state": {
|
|
139
|
+
"head_sha": "c0091ab",
|
|
140
|
+
"head_ref": "lastlight/fix__env-mismatch-node-version",
|
|
141
|
+
"checks_state": "failing",
|
|
142
|
+
"settled_check_count": 1,
|
|
143
|
+
"base_checks_state": "passing",
|
|
144
|
+
"ci_jobs": [
|
|
145
|
+
{
|
|
146
|
+
"name": "CI / test",
|
|
147
|
+
"log_excerpt": "Run actions/setup-node@v4\n node-version: 18\nNow using node v18.20.4 (npm v10.7.0)\n\nnpm warn EBADENGINE Unsupported engine {\nnpm warn EBADENGINE package: 'grouper@1.0.0',\nnpm warn EBADENGINE required: { node: '>=22' },\nnpm warn EBADENGINE current: { node: 'v18.20.4', npm: '10.7.0' }\nnpm warn EBADENGINE }\n\n> grouper@1.0.0 test\n> node --test\n\n\u2716 test/group.test.js (18.220ms)\n TypeError: Object.groupBy is not a function\n at groupBy (file:///home/runner/work/grouper/src/group.js:3:17)\n\n\u2139 pass 0\n\u2139 fail 1\n##[error]Process completed with exit code 1.\n",
|
|
148
|
+
"workflow_path": ".github/workflows/ci.yml",
|
|
149
|
+
"failing_step": "npm test"
|
|
150
|
+
}
|
|
151
|
+
]
|
|
152
|
+
},
|
|
153
|
+
"expect_markers": {
|
|
154
|
+
"diagnosis_class": "env-mismatch"
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
"instance_id": "fix__infra-dependent-e2e",
|
|
159
|
+
"repo": "acme/retryer",
|
|
160
|
+
"problem_statement": "CI is red on the tiny-backoff 3.8.0 \u2192 4.0.0 bump. The unit job passed; the `e2e` job failed because it has no staging backend to talk to \u2014 `STAGING_BASE_URL` is unset for this run.",
|
|
161
|
+
"pr": {
|
|
162
|
+
"number": 133,
|
|
163
|
+
"title": "Bump tiny-backoff from 3.8.0 to 4.0.0",
|
|
164
|
+
"body": "Bumps the dependency to the version listed in the title.\n\nDependabot will resolve any conflicts with this PR as long as you don't alter it yourself.",
|
|
165
|
+
"base_ref": "main",
|
|
166
|
+
"head_ref": "lastlight/fix__infra-dependent-e2e",
|
|
167
|
+
"base_commit": "0000000000000000000000000000000000000000",
|
|
168
|
+
"head_commit": "0000000000000000000000000000000000000000",
|
|
169
|
+
"user": "dependabot[bot]"
|
|
170
|
+
},
|
|
171
|
+
"pr_state": {
|
|
172
|
+
"head_sha": "d51ba7e",
|
|
173
|
+
"head_ref": "lastlight/fix__infra-dependent-e2e",
|
|
174
|
+
"checks_state": "failing",
|
|
175
|
+
"settled_check_count": 1,
|
|
176
|
+
"base_checks_state": "passing",
|
|
177
|
+
"ci_jobs": [
|
|
178
|
+
{
|
|
179
|
+
"name": "CI / e2e",
|
|
180
|
+
"log_excerpt": "> node --test e2e/\n\n\u2716 e2e/smoke.test.js (2.114ms)\n 'test failed'\n\n AssertionError [ERR_ASSERTION]: STAGING_BASE_URL is not set\n\n at TestContext.<anonymous> (file:///home/runner/work/retryer/e2e/smoke.test.js:7:10)\n\n\u2139 tests 1\n\u2139 pass 0\n\u2139 fail 1\n##[error]Process completed with exit code 1.\n",
|
|
181
|
+
"workflow_path": ".github/workflows/ci.yml",
|
|
182
|
+
"failing_step": "node --test e2e/"
|
|
183
|
+
}
|
|
184
|
+
]
|
|
185
|
+
},
|
|
186
|
+
"expect_markers": {
|
|
187
|
+
"diagnosis_class": "infra-dependent"
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
"instance_id": "fix__upstream-broken-base-red",
|
|
192
|
+
"repo": "acme/money",
|
|
193
|
+
"problem_statement": "CI is red on the tiny-intl 6.4.0 \u2192 7.0.0 bump. The failing test also fails on `main`: a regression landed there in #874 and dropped the rounding in `convert()`.",
|
|
194
|
+
"pr": {
|
|
195
|
+
"number": 902,
|
|
196
|
+
"title": "Bump tiny-intl from 6.4.0 to 7.0.0",
|
|
197
|
+
"body": "Bumps the dependency to the version listed in the title.\n\nDependabot will resolve any conflicts with this PR as long as you don't alter it yourself.",
|
|
198
|
+
"base_ref": "main",
|
|
199
|
+
"head_ref": "lastlight/fix__upstream-broken-base-red",
|
|
200
|
+
"base_commit": "0000000000000000000000000000000000000000",
|
|
201
|
+
"head_commit": "0000000000000000000000000000000000000000",
|
|
202
|
+
"user": "dependabot[bot]"
|
|
203
|
+
},
|
|
204
|
+
"pr_state": {
|
|
205
|
+
"head_sha": "e11c4d0",
|
|
206
|
+
"head_ref": "lastlight/fix__upstream-broken-base-red",
|
|
207
|
+
"checks_state": "failing",
|
|
208
|
+
"settled_check_count": 1,
|
|
209
|
+
"base_checks_state": "failing",
|
|
210
|
+
"ci_jobs": [
|
|
211
|
+
{
|
|
212
|
+
"name": "CI / test",
|
|
213
|
+
"log_excerpt": "> money@5.0.0 test\n> node --test\n\n\u2714 test/format.test.js (11.902ms)\n\u2716 test/rates.test.js (18.441ms)\n 'test failed'\n\n AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:\n\n 1234.5 !== 1235\n\n at TestContext.<anonymous> (file:///home/runner/work/money/test/rates.test.js:6:10)\n\n\u2139 tests 2\n\u2139 pass 1\n\u2139 fail 1\n##[error]Process completed with exit code 1.\n",
|
|
214
|
+
"workflow_path": ".github/workflows/ci.yml",
|
|
215
|
+
"failing_step": "npm test"
|
|
216
|
+
}
|
|
217
|
+
],
|
|
218
|
+
"notes": [
|
|
219
|
+
{
|
|
220
|
+
"kind": "constraint",
|
|
221
|
+
"text": "the rates rounding regression came from #874 on main, not from this bump"
|
|
222
|
+
}
|
|
223
|
+
]
|
|
224
|
+
},
|
|
225
|
+
"expect_markers": {
|
|
226
|
+
"diagnosis_class": "upstream-broken"
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
]
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { groupBy } from "../src/group.js";
|
|
4
|
+
|
|
5
|
+
test("groups by first letter", () => {
|
|
6
|
+
const out = groupBy(["apple", "avocado", "beet"], (s) => s[0]);
|
|
7
|
+
assert.deepEqual(out.a, ["apple", "avocado"]);
|
|
8
|
+
assert.deepEqual(out.b, ["beet"]);
|
|
9
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Resolve once `check()` returns true, polling every `intervalMs`. */
|
|
2
|
+
export async function pollUntil(check, { intervalMs = 10, timeoutMs = 200 } = {}) {
|
|
3
|
+
const deadline = Date.now() + timeoutMs;
|
|
4
|
+
for (;;) {
|
|
5
|
+
if (await check()) return true;
|
|
6
|
+
if (Date.now() > deadline) throw new Error("pollUntil: timed out");
|
|
7
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
8
|
+
}
|
|
9
|
+
}
|