Public: anyone can read this; the crew can change it.
An open issue the crew found done on main is held as done_on_main, not stalled, and never handed back to the planner #2508
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/2502-an-open-issue-the-crew-found-done-on-mai → main 2 commits fork 1 thread · 1 unresolved
1 file
changed +37
−0
test/crew-held.test.ts +37
| @@ -223,6 +223,43 @@ describe("the tick says why each ready issue cannot start", () => { | ||
| 223 | 223 | expect(replannable(again).map((i) => i.number)).toEqual([done.number]); |
| 224 | 224 | }); |
| 225 | 225 | |
| 226 | test("an issue the crew found done on main is held as done_on_main, not replannable", async () => { | |
| 227 | const { repo, mason } = await crewed(app, alice, "finished"); | |
| 228 | const oid = "a".repeat(40); | |
| 229 | const at = (id: string, ago: number) => | |
| 230 | app.db.query("UPDATE comment SET created_at = ?2 WHERE id = ?1").run(id, minutes(ago)); | |
| 231 | const say = (i: Issue, body: string, ago: number) => { | |
| 232 | const c = addComment(app, alice, getRepoById(app.db, repo.id)!, getIssueById(app.db, i.id)!, body); | |
| 233 | if (!c.ok) throw new Error(String(c.error)); | |
| 234 | at(c.value.id, ago); | |
| 235 | }; | |
| 236 | const done = issue(repo, "Already done"); | |
| 237 | ran(repo, done, mason, "declined: it is done", 30); | |
| 238 | say(done, `${DONE_NOTE}${oid}`, 20); | |
| 239 | const reparked = issue(repo, "Done, then parked"); | |
| 240 | ran(repo, reparked, mason, "declined: it is done", 30); | |
| 241 | say(reparked, `${DONE_NOTE}${oid}`, 20); | |
| 242 | say(reparked, `${PARK_NOTE}not so`, 10); | |
| 243 | ran(repo, reparked, mason, "declined: it is not done", 5); | |
| 244 | const spoken = issue(repo, "Done, then a word"); | |
| 245 | ran(repo, spoken, mason, "declined: it is done", 30); | |
| 246 | say(spoken, `${DONE_NOTE}${oid}`, 20); | |
| 247 | say(spoken, "Please try again", 10); | |
| 248 | ||
| 249 | const held = heldNow(app, getRepoById(app.db, repo.id)!); | |
| 250 | const named = (h: keyof typeof held.by) => held.by[h].map((i) => i.number); | |
| 251 | expect(named("done_on_main")).toContain(done.number); | |
| 252 | expect(named("stalled")).not.toContain(done.number); | |
| 253 | expect(replannable(held).map((i) => i.number)).not.toContain(done.number); | |
| 254 | // A later park is a newer word: it is the planner's to re-plan again. | |
| 255 | expect(named("done_on_main")).not.toContain(reparked.number); | |
| 256 | expect(replannable(held).map((i) => i.number)).toContain(reparked.number); | |
| 257 | // A person's comment after the done note does not make it startable. | |
| 258 | expect(held.startable.map((r) => r.issue.number)).not.toContain(spoken.number); | |
| 259 | expect(named("done_on_main")).toContain(spoken.number); | |
| 260 | expect(heldWords(held)).toContain("done_on_main="); | |
| 261 | }); | |
| 262 | ||
| 226 | 263 | test("nothing startable with a slot free asks the planner once, with the breakdown, whatever the plate", async () => { |
| 227 | 264 | const { repo, atlas, mason } = await crewed(app, alice, "stuck"); |
| 228 | 265 | const fresh = () => getRepoById(app.db, repo.id)!; |
new above
offline