dsh-pstack 0.2.0-beta.1 → 0.2.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (261) hide show
  1. package/LICENSE +2 -3
  2. package/README.md +135 -84
  3. package/README.zh.md +185 -0
  4. package/cordis.patch.yml +97 -27
  5. package/dist/host-contract-CUPSpeNk.d.mts +22 -0
  6. package/dist/index.d.mts +49 -238
  7. package/dist/index.mjs +250 -123
  8. package/dist/models.d.mts +44 -0
  9. package/dist/models.mjs +201 -0
  10. package/dist/paths-BT77khwm.mjs +48 -0
  11. package/dist/pstack/LICENSE +21 -0
  12. package/dist/pstack/README.md +291 -0
  13. package/dist/pstack/agents/poteto-agent.md +9 -0
  14. package/{assets → dist/pstack}/docs/guide/01-setup.md +13 -11
  15. package/{assets → dist/pstack}/docs/guide/02-poteto-mode.md +4 -4
  16. package/{assets → dist/pstack}/docs/guide/04-design.md +4 -4
  17. package/{assets → dist/pstack}/docs/guide/05-build-and-clean.md +2 -2
  18. package/{assets → dist/pstack}/docs/guide/06-verify-and-ship.md +13 -3
  19. package/{assets → dist/pstack}/docs/guide/07-overnight.md +15 -13
  20. package/{assets → dist/pstack}/docs/guide/09-make-it-yours.md +6 -2
  21. package/{assets → dist/pstack}/docs/guide/10-recipes-and-pitfalls.md +7 -5
  22. package/{assets → dist/pstack}/docs/guide/README.md +1 -1
  23. package/dist/pstack/docs/harness/dsh.md +98 -0
  24. package/{assets → dist/pstack}/skills/architect/SKILL.md +12 -12
  25. package/dist/pstack/skills/arena/SKILL.md +50 -0
  26. package/{assets → dist/pstack}/skills/automate-me/SKILL.md +14 -14
  27. package/{assets → dist/pstack}/skills/benchmark-checklist/SKILL.md +1 -1
  28. package/{assets → dist/pstack}/skills/blast-radius/SKILL.md +4 -6
  29. package/{assets → dist/pstack}/skills/correct/SKILL.md +1 -1
  30. package/{assets → dist/pstack}/skills/create-formal-verification-skill/SKILL.md +4 -2
  31. package/{assets → dist/pstack}/skills/create-verification-skill/SKILL.md +5 -5
  32. package/dist/pstack/skills/figure-it-out/SKILL.md +54 -0
  33. package/dist/pstack/skills/how/SKILL.md +38 -0
  34. package/{assets → dist/pstack}/skills/in-your-own-words/SKILL.md +1 -1
  35. package/dist/pstack/skills/interrogate/SKILL.md +33 -0
  36. package/{assets → dist/pstack}/skills/ketchup/SKILL.md +3 -5
  37. package/dist/pstack/skills/ketchup-video/SKILL.md +29 -0
  38. package/{assets → dist/pstack}/skills/maintain-formal-verification-skill/SKILL.md +1 -1
  39. package/{assets → dist/pstack}/skills/maintain-verification-skill/SKILL.md +3 -3
  40. package/dist/pstack/skills/make-bot-ui/SKILL.md +117 -0
  41. package/dist/pstack/skills/no-comments/SKILL.md +26 -0
  42. package/{assets → dist/pstack}/skills/poteto-help/SKILL.md +34 -31
  43. package/{assets → dist/pstack}/skills/poteto-help/references/prompting.md +1 -1
  44. package/{assets → dist/pstack}/skills/poteto-help/references/recipes.md +3 -3
  45. package/{assets → dist/pstack}/skills/poteto-mode/SKILL.md +20 -19
  46. package/dist/pstack/skills/poteto-mode/playbooks/authoring-a-skill.md +12 -0
  47. package/dist/pstack/skills/poteto-mode/playbooks/autonomous-run.md +13 -0
  48. package/dist/pstack/skills/poteto-mode/playbooks/autopilot-full.md +25 -0
  49. package/dist/pstack/skills/poteto-mode/playbooks/autopilot-stack.md +8 -0
  50. package/{assets → dist/pstack}/skills/poteto-mode/playbooks/babysit.md +4 -4
  51. package/dist/pstack/skills/poteto-mode/playbooks/bug-fix.md +15 -0
  52. package/{assets → dist/pstack}/skills/poteto-mode/playbooks/eval.md +3 -3
  53. package/dist/pstack/skills/poteto-mode/playbooks/feature.md +21 -0
  54. package/{assets → dist/pstack}/skills/poteto-mode/playbooks/hillclimb.md +3 -3
  55. package/{assets → dist/pstack}/skills/poteto-mode/playbooks/investigation.md +2 -2
  56. package/{assets → dist/pstack}/skills/poteto-mode/playbooks/multi-phase-plan.md +23 -23
  57. package/{assets → dist/pstack}/skills/poteto-mode/playbooks/opening-a-pr.md +6 -6
  58. package/dist/pstack/skills/poteto-mode/playbooks/orchestrate.md +25 -0
  59. package/{assets → dist/pstack}/skills/poteto-mode/playbooks/pause-safely.md +2 -2
  60. package/dist/pstack/skills/poteto-mode/playbooks/perf-issue.md +25 -0
  61. package/{assets → dist/pstack}/skills/poteto-mode/playbooks/prototype.md +4 -4
  62. package/dist/pstack/skills/poteto-mode/playbooks/refactoring.md +16 -0
  63. package/dist/pstack/skills/poteto-mode/playbooks/runtime-forensics.md +11 -0
  64. package/dist/pstack/skills/poteto-mode/playbooks/session-pickup.md +11 -0
  65. package/{assets → dist/pstack}/skills/poteto-mode/playbooks/shipping.md +2 -2
  66. package/{assets → dist/pstack}/skills/poteto-mode/playbooks/trace-forensics.md +2 -2
  67. package/dist/pstack/skills/poteto-mode/playbooks/visual-parity.md +11 -0
  68. package/dist/pstack/skills/poteto-mode/playbooks/worktree-cleanup.md +12 -0
  69. package/{assets → dist/pstack}/skills/poteto-mode/scripts/check-plan.mjs +6 -8
  70. package/dist/pstack/skills/poteto-mode/scripts/check-plan.test.mjs +128 -0
  71. package/{assets/skills/poteto-mode/scripts/check-plan.test.mjs → dist/pstack/skills/poteto-mode/scripts/expect.mjs} +12 -101
  72. package/dist/pstack/skills/poteto-mode/scripts/support.mjs +93 -0
  73. package/dist/pstack/skills/poteto-mode/scripts/watch-pr/cli.mjs +123 -0
  74. package/dist/pstack/skills/poteto-mode/scripts/watch-pr/cli.test.mjs +217 -0
  75. package/dist/pstack/skills/poteto-mode/scripts/watch-pr/fakes.test-helper.mjs +119 -0
  76. package/dist/pstack/skills/poteto-mode/scripts/watch-pr/github.mjs +626 -0
  77. package/dist/pstack/skills/poteto-mode/scripts/watch-pr/github.test.mjs +521 -0
  78. package/dist/pstack/skills/poteto-mode/scripts/watch-pr/policy.mjs +657 -0
  79. package/dist/pstack/skills/poteto-mode/scripts/watch-pr/policy.test.mjs +523 -0
  80. package/dist/pstack/skills/poteto-mode/scripts/watch-pr/render.mjs +106 -0
  81. package/dist/pstack/skills/poteto-mode/scripts/watch-pr/types.mjs +9 -0
  82. package/dist/pstack/skills/poteto-mode/scripts/watch-pr/watch-pr.mjs +5 -0
  83. package/{assets → dist/pstack}/skills/poteto-mode/scripts/worktree-audit.mjs +15 -22
  84. package/dist/pstack/skills/poteto-mode/scripts/worktree-audit.test.mjs +134 -0
  85. package/{assets → dist/pstack}/skills/principle-attack-the-premise/SKILL.md +1 -1
  86. package/{assets → dist/pstack}/skills/principle-boundary-discipline/SKILL.md +1 -1
  87. package/{assets → dist/pstack}/skills/principle-encode-lessons-in-structure/SKILL.md +1 -1
  88. package/{assets → dist/pstack}/skills/principle-exhaust-the-design-space/SKILL.md +1 -1
  89. package/{assets → dist/pstack}/skills/principle-experience-first/SKILL.md +1 -1
  90. package/{assets → dist/pstack}/skills/principle-explain-the-number/SKILL.md +3 -2
  91. package/{assets → dist/pstack}/skills/principle-fix-root-causes/SKILL.md +1 -1
  92. package/{assets → dist/pstack}/skills/principle-laziness-protocol/SKILL.md +1 -1
  93. package/{assets → dist/pstack}/skills/principle-make-operations-idempotent/SKILL.md +1 -1
  94. package/{assets → dist/pstack}/skills/principle-migrate-callers-then-delete-legacy-apis/SKILL.md +1 -1
  95. package/{assets → dist/pstack}/skills/principle-minimize-reader-load/SKILL.md +1 -1
  96. package/{assets → dist/pstack}/skills/principle-model-the-domain/SKILL.md +1 -1
  97. package/{assets → dist/pstack}/skills/principle-never-block-on-the-human/SKILL.md +1 -1
  98. package/{assets → dist/pstack}/skills/principle-outcome-oriented-execution/SKILL.md +1 -1
  99. package/{assets → dist/pstack}/skills/principle-prove-it-works/SKILL.md +2 -2
  100. package/{assets → dist/pstack}/skills/principle-redesign-from-first-principles/SKILL.md +1 -1
  101. package/{assets → dist/pstack}/skills/principle-separate-before-serializing-shared-state/SKILL.md +1 -1
  102. package/{assets → dist/pstack}/skills/principle-sequence-verifiable-units/SKILL.md +1 -1
  103. package/{assets → dist/pstack}/skills/principle-subtract-before-you-add/SKILL.md +1 -1
  104. package/{assets → dist/pstack}/skills/principle-test-behavior-not-implementation/SKILL.md +1 -1
  105. package/{assets → dist/pstack}/skills/principle-type-system-discipline/SKILL.md +1 -1
  106. package/dist/pstack/skills/recall/SKILL.md +37 -0
  107. package/dist/pstack/skills/reflect/SKILL.md +71 -0
  108. package/{assets → dist/pstack}/skills/reflect/references/divergent-reviewer.md +4 -4
  109. package/{assets → dist/pstack}/skills/reflect/references/judgment-reviewer.md +2 -2
  110. package/{assets → dist/pstack}/skills/reflect/references/synthesizer.md +5 -5
  111. package/{assets → dist/pstack}/skills/reflect/references/tooling-reviewer.md +2 -2
  112. package/dist/pstack/skills/setup-pstack/SKILL.md +80 -0
  113. package/{assets → dist/pstack}/skills/sharpen/SKILL.md +1 -1
  114. package/{assets → dist/pstack}/skills/show-me-your-work/SKILL.md +8 -8
  115. package/{assets → dist/pstack}/skills/show-me-your-work/scripts/log.mjs +1 -3
  116. package/dist/pstack/skills/show-me-your-work/scripts/log.test.mjs +56 -0
  117. package/dist/pstack/skills/statechart/SKILL.md +82 -0
  118. package/dist/pstack/skills/statechart/references/render.md +93 -0
  119. package/dist/pstack/skills/statechart/scripts/backends/cfg.mjs +31 -0
  120. package/dist/pstack/skills/statechart/scripts/backends/mermaid.mjs +186 -0
  121. package/dist/pstack/skills/statechart/scripts/backends/plain-text.mjs +62 -0
  122. package/dist/pstack/skills/statechart/scripts/backends/report.mjs +84 -0
  123. package/dist/pstack/skills/statechart/scripts/frontends/config.mjs +268 -0
  124. package/dist/pstack/skills/statechart/scripts/frontends/dot.mjs +67 -0
  125. package/dist/pstack/skills/statechart/scripts/frontends/marks.mjs +94 -0
  126. package/dist/pstack/skills/statechart/scripts/frontends/messages.mjs +48 -0
  127. package/dist/pstack/skills/statechart/scripts/frontends/spec.mjs +101 -0
  128. package/dist/pstack/skills/statechart/scripts/frontends/trace.mjs +39 -0
  129. package/dist/pstack/skills/statechart/scripts/frontends/value.mjs +180 -0
  130. package/dist/pstack/skills/statechart/scripts/frontends/verdict.mjs +40 -0
  131. package/dist/pstack/skills/statechart/scripts/frontends/view.mjs +99 -0
  132. package/dist/pstack/skills/statechart/scripts/ir.mjs +277 -0
  133. package/dist/pstack/skills/statechart/scripts/passes/annotate.mjs +92 -0
  134. package/dist/pstack/skills/statechart/scripts/passes/label.mjs +210 -0
  135. package/dist/pstack/skills/statechart/scripts/passes/lift.mjs +84 -0
  136. package/dist/pstack/skills/statechart/scripts/passes/loop-check.mjs +47 -0
  137. package/dist/pstack/skills/statechart/scripts/passes/paths.mjs +72 -0
  138. package/dist/pstack/skills/statechart/scripts/passes/reach.mjs +135 -0
  139. package/dist/pstack/skills/statechart/scripts/passes/region-check.mjs +82 -0
  140. package/dist/pstack/skills/statechart/scripts/passes/regions.mjs +268 -0
  141. package/dist/pstack/skills/statechart/scripts/pipeline.mjs +72 -0
  142. package/dist/pstack/skills/statechart/scripts/run.mjs +440 -0
  143. package/dist/pstack/skills/statechart/scripts/statechart.mjs +155 -0
  144. package/dist/pstack/skills/statechart/scripts/statechart.test.mjs +198 -0
  145. package/dist/pstack/skills/statechart/scripts/worker.mjs +31 -0
  146. package/dist/pstack/skills/swarm/SKILL.md +43 -0
  147. package/{assets → dist/pstack}/skills/tdd/SKILL.md +1 -1
  148. package/{assets → dist/pstack}/skills/teach/SKILL.md +2 -2
  149. package/{assets → dist/pstack}/skills/technical-writing/SKILL.md +1 -1
  150. package/{assets → dist/pstack}/skills/typescript-best-practices/SKILL.md +3 -6
  151. package/{assets → dist/pstack}/skills/why/SKILL.md +8 -8
  152. package/dist/script.d.mts +9 -111
  153. package/dist/script.mjs +32 -329
  154. package/dist/skill.d.mts +38 -0
  155. package/dist/skill.mjs +183 -0
  156. package/dist/skills/authored/decision-audit/SKILL.md +99 -0
  157. package/dist/skills/authored/decision-audit/SKILL.zh.md +99 -0
  158. package/dist/skills/authored/decision-audit/references/recover-commit.md +17 -0
  159. package/dist/skills/authored/decision-audit/references/recover-commit.zh.md +17 -0
  160. package/dist/skills/authored/decision-write/SKILL.md +131 -0
  161. package/dist/skills/authored/decision-write/SKILL.zh.md +131 -0
  162. package/dist/skills/authored/decision-write/references/land.md +48 -0
  163. package/dist/skills/authored/decision-write/references/land.zh.md +48 -0
  164. package/dist/skills/authored/setup-project-docs/SKILL.md +58 -0
  165. package/dist/skills/authored/setup-project-docs/SKILL.zh.md +58 -0
  166. package/dist/skills/authored/setup-project-docs/references/decision-conventions.md +11 -0
  167. package/dist/skills/authored/setup-project-docs/references/decision-conventions.zh.md +11 -0
  168. package/dist/skills/vendored/retro/SKILL.md +44 -0
  169. package/dist/skills/vendored/retro/agents/openai.yaml +5 -0
  170. package/dist/skills/vendored/writing-for-agents/SKILL-MECHANICS.md +22 -0
  171. package/dist/skills/vendored/writing-for-agents/SKILL.md +81 -0
  172. package/dist/skills/vendored/writing-for-agents/agents/openai.yaml +3 -0
  173. package/dist/subagent.d.mts +56 -193
  174. package/dist/subagent.mjs +443 -741
  175. package/locale/en.json +3 -2
  176. package/locale/zh.json +3 -2
  177. package/package.json +63 -66
  178. package/assets/agents/poteto-agent.md +0 -8
  179. package/assets/docs/harness/dsh.md +0 -60
  180. package/assets/docs/pstack-models.example.md +0 -54
  181. package/assets/skills/arena/SKILL.md +0 -50
  182. package/assets/skills/figure-it-out/SKILL.md +0 -54
  183. package/assets/skills/how/SKILL.md +0 -38
  184. package/assets/skills/interrogate/SKILL.md +0 -71
  185. package/assets/skills/interrogate/references/code-quality-review.md +0 -47
  186. package/assets/skills/interrogate/references/lead-judgment.md +0 -39
  187. package/assets/skills/interrogate/references/reviewer-prompt.md +0 -62
  188. package/assets/skills/interrogate/references/rubric.md +0 -77
  189. package/assets/skills/no-comments/SKILL.md +0 -26
  190. package/assets/skills/poteto-mode/playbooks/authoring-a-skill.md +0 -12
  191. package/assets/skills/poteto-mode/playbooks/autonomous-run.md +0 -15
  192. package/assets/skills/poteto-mode/playbooks/autopilot-full.md +0 -25
  193. package/assets/skills/poteto-mode/playbooks/autopilot-stack.md +0 -12
  194. package/assets/skills/poteto-mode/playbooks/bug-fix.md +0 -15
  195. package/assets/skills/poteto-mode/playbooks/feature.md +0 -21
  196. package/assets/skills/poteto-mode/playbooks/orchestrate.md +0 -112
  197. package/assets/skills/poteto-mode/playbooks/perf-issue.md +0 -25
  198. package/assets/skills/poteto-mode/playbooks/refactoring.md +0 -16
  199. package/assets/skills/poteto-mode/playbooks/runtime-forensics.md +0 -11
  200. package/assets/skills/poteto-mode/playbooks/session-pickup.md +0 -11
  201. package/assets/skills/poteto-mode/playbooks/visual-parity.md +0 -11
  202. package/assets/skills/poteto-mode/playbooks/worktree-cleanup.md +0 -20
  203. package/assets/skills/poteto-mode/scripts/orch/orch.mjs +0 -4282
  204. package/assets/skills/poteto-mode/scripts/orch/orch.test.mjs +0 -2042
  205. package/assets/skills/poteto-mode/scripts/watch-pr/cli.test.mjs +0 -5101
  206. package/assets/skills/poteto-mode/scripts/watch-pr/github.test.mjs +0 -1177
  207. package/assets/skills/poteto-mode/scripts/watch-pr/policy.test.mjs +0 -1441
  208. package/assets/skills/poteto-mode/scripts/watch-pr/watch-pr.mjs +0 -4601
  209. package/assets/skills/recall/SKILL.md +0 -37
  210. package/assets/skills/reflect/SKILL.md +0 -69
  211. package/assets/skills/setup-pstack/SKILL.md +0 -67
  212. package/assets/skills/swarm/SKILL.md +0 -43
  213. package/dist/frontmatter-BQX3ekwP.mjs +0 -229
  214. package/dist/frontmatter-BQX3ekwP.mjs.map +0 -1
  215. package/dist/history.d.mts +0 -429
  216. package/dist/history.mjs +0 -1129
  217. package/dist/history.mjs.map +0 -1
  218. package/dist/host-contract-o47JH_r8.d.mts +0 -527
  219. package/dist/index.mjs.map +0 -1
  220. package/dist/paths-D_JlVmBk.mjs +0 -42
  221. package/dist/paths-D_JlVmBk.mjs.map +0 -1
  222. package/dist/script.mjs.map +0 -1
  223. package/dist/subagent.mjs.map +0 -1
  224. /package/{static/icon.png → assets/logo.png} +0 -0
  225. /package/{assets → dist/pstack}/agents/comment-sicko.md +0 -0
  226. /package/{assets → dist/pstack}/docs/guide/03-understand.md +0 -0
  227. /package/{assets → dist/pstack}/docs/guide/08-principles.md +0 -0
  228. /package/{assets → dist/pstack}/docs/guide/images/design.jpg +0 -0
  229. /package/{assets → dist/pstack}/docs/guide/images/overnight.jpg +0 -0
  230. /package/{assets → dist/pstack}/docs/guide/images/recipes.jpg +0 -0
  231. /package/{assets → dist/pstack}/docs/guide/images/router.jpg +0 -0
  232. /package/{assets → dist/pstack}/docs/guide/images/understanding.jpg +0 -0
  233. /package/{assets → dist/pstack}/docs/guide/images/verification.jpg +0 -0
  234. /package/{assets → dist/pstack}/skills/architect/references/design-red-flags.md +0 -0
  235. /package/{assets → dist/pstack}/skills/architect/references/rationale-template.md +0 -0
  236. /package/{assets → dist/pstack}/skills/architect/references/runner-prompt.md +0 -0
  237. /package/{assets → dist/pstack}/skills/bro/SKILL.md +0 -0
  238. /package/{assets → dist/pstack}/skills/create-verification-skill/references/feature-map-example/README.md +0 -0
  239. /package/{assets → dist/pstack}/skills/create-verification-skill/references/feature-map-example/create-note.md +0 -0
  240. /package/{assets → dist/pstack}/skills/create-verification-skill/references/feature-map-example/search.md +0 -0
  241. /package/{assets → dist/pstack}/skills/how/references/explainer-prompt.md +0 -0
  242. /package/{assets → dist/pstack}/skills/how/references/explorer-prompt.md +0 -0
  243. /package/{assets → dist/pstack}/skills/poteto-mode/references/bugbot-triage.md +0 -0
  244. /package/{assets → dist/pstack}/skills/principle-build-the-lever/SKILL.md +0 -0
  245. /package/{assets → dist/pstack}/skills/principle-foundational-thinking/SKILL.md +0 -0
  246. /package/{assets → dist/pstack}/skills/principle-guard-the-context-window/SKILL.md +0 -0
  247. /package/{assets → dist/pstack}/skills/show-me-your-work/references/decision-log-template.tsv +0 -0
  248. /package/{assets → dist/pstack}/skills/typescript-best-practices/references/patterns.md +0 -0
  249. /package/{assets → dist/pstack}/skills/unslop/SKILL.md +0 -0
  250. /package/{assets → dist/pstack}/skills/why/references/epistemics.md +0 -0
  251. /package/{assets → dist/pstack}/skills/why/references/investigator-prompt.md +0 -0
  252. /package/{assets → dist/pstack}/skills/why/references/source-playbook.md +0 -0
  253. /package/{assets → dist/pstack}/skills/why/references/sources/code-archaeology.md +0 -0
  254. /package/{assets → dist/pstack}/skills/why/references/sources/databricks.md +0 -0
  255. /package/{assets → dist/pstack}/skills/why/references/sources/datadog.md +0 -0
  256. /package/{assets → dist/pstack}/skills/why/references/sources/incident-postmortem.md +0 -0
  257. /package/{assets → dist/pstack}/skills/why/references/sources/linear.md +0 -0
  258. /package/{assets → dist/pstack}/skills/why/references/sources/notion.md +0 -0
  259. /package/{assets → dist/pstack}/skills/why/references/sources/sentry.md +0 -0
  260. /package/{assets → dist/pstack}/skills/why/references/sources/slack.md +0 -0
  261. /package/{assets → dist/pstack}/skills/why/references/synthesizer-prompt.md +0 -0
@@ -0,0 +1,521 @@
1
+ import { expect } from "../expect.mjs";
2
+ import "../support.mjs";
3
+ import { parsePrNumber } from "./types.mjs";
4
+ import { ChecksUnavailable, WatcherQueryError, buildReviewThreads, mapRollupNode, orderStack, parseFastCheck, parsePullRequest, parseReviewThreadPage, resolveChecks, resolveContext, resolveReviewThreads } from "./github.mjs";
5
+ import { failedCheck, fakeReader, passingCheck, pendingCheck } from "./fakes.test-helper.mjs";
6
+ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
7
+ import { tmpdir } from "node:os";
8
+ import { join } from "node:path";
9
+ import { spawnSync } from "node:child_process";
10
+ import { describe, it } from "node:test";
11
+ // runtime/skills/poteto-mode/scripts/watch-pr/github.test.ts
12
+ const githubModule = JSON.stringify(import.meta.dirname + (import.meta.url.endsWith(".ts") ? "/github.ts" : "/github.mjs"));
13
+ const context = {
14
+ owner: "owner",
15
+ repo: "repo",
16
+ number: parsePrNumber(42)
17
+ };
18
+ describe("checks fallback chain", () => {
19
+ it("uses a non-empty fast-path result without a rollup query", async () => {
20
+ const reader = fakeReader({ fastPath: {
21
+ kind: "checks",
22
+ checks: [passingCheck("fast")]
23
+ } });
24
+ const read = await resolveChecks(reader, context);
25
+ expect(read.source).toBe("gh-pr-checks");
26
+ expect(read.checks.map((check) => check.name)).toEqual(["fast"]);
27
+ expect(reader.calls).toEqual(["checksFastPath"]);
28
+ });
29
+ it("paginates GraphQL when the fast path is unusable", async () => {
30
+ const reader = fakeReader({
31
+ fastPath: {
32
+ kind: "unusable",
33
+ exitCode: 8,
34
+ stderr: ""
35
+ },
36
+ rollupPages: [{
37
+ checks: [passingCheck("first")],
38
+ endCursor: "next"
39
+ }, {
40
+ checks: [failedCheck("second")],
41
+ endCursor: null
42
+ }]
43
+ });
44
+ const read = await resolveChecks(reader, context);
45
+ expect(read.source).toBe("graphql-rollup");
46
+ expect(read.checks.map((check) => check.name)).toEqual(["first", "second"]);
47
+ expect(reader.calls).toEqual([
48
+ "checksFastPath",
49
+ "checkRollupPage:null",
50
+ "checkRollupPage:next"
51
+ ]);
52
+ });
53
+ it("falls back when valid fast-path JSON represented an empty list", async () => {
54
+ const reader = fakeReader({
55
+ fastPath: {
56
+ kind: "checks",
57
+ checks: []
58
+ },
59
+ rollupPages: [{
60
+ checks: [pendingCheck("fallback")],
61
+ endCursor: null
62
+ }]
63
+ });
64
+ expect((await resolveChecks(reader, context)).checks[0].name).toBe("fallback");
65
+ expect(reader.calls).toEqual(["checksFastPath", "checkRollupPage:null"]);
66
+ });
67
+ it("fails closed when both paths are empty", async () => {
68
+ const reader = fakeReader({ fastPath: {
69
+ kind: "unusable",
70
+ exitCode: 8,
71
+ stderr: "credential cannot read checks"
72
+ } });
73
+ await expect(resolveChecks(reader, context)).rejects.toBeInstanceOf(ChecksUnavailable);
74
+ expect(reader.calls).toEqual(["checksFastPath", "checkRollupPage:null"]);
75
+ });
76
+ });
77
+ describe("rollup node mapping", () => {
78
+ it("maps terminal and non-terminal CheckRun states fail closed", () => {
79
+ for (const [status, conclusion, kind, reportedState] of [
80
+ [
81
+ "IN_PROGRESS",
82
+ null,
83
+ "pending",
84
+ "PENDING"
85
+ ],
86
+ [
87
+ "COMPLETED",
88
+ "SUCCESS",
89
+ "passed",
90
+ "SUCCESS"
91
+ ],
92
+ [
93
+ "COMPLETED",
94
+ "NEUTRAL",
95
+ "skipped",
96
+ "NEUTRAL"
97
+ ],
98
+ [
99
+ "COMPLETED",
100
+ "SKIPPED",
101
+ "skipped",
102
+ "SKIPPED"
103
+ ],
104
+ [
105
+ "COMPLETED",
106
+ "ACTION_REQUIRED",
107
+ "failed",
108
+ "ACTION_REQUIRED"
109
+ ],
110
+ [
111
+ "COMPLETED",
112
+ "TIMED_OUT",
113
+ "failed",
114
+ "FAILURE"
115
+ ],
116
+ [
117
+ "COMPLETED",
118
+ "FUTURE_VALUE",
119
+ "failed",
120
+ "FAILURE"
121
+ ]
122
+ ]) expect(mapRollupNode({
123
+ __typename: "CheckRun",
124
+ name: "ci",
125
+ status,
126
+ conclusion
127
+ })).toMatchObject({
128
+ kind,
129
+ reportedState
130
+ });
131
+ });
132
+ it("classifies an in-progress Code Review Gate from the rollup as the gate", () => {
133
+ expect(mapRollupNode({
134
+ __typename: "CheckRun",
135
+ name: "Code Review Gate",
136
+ status: "IN_PROGRESS",
137
+ conclusion: null
138
+ })).toMatchObject({ kind: "code-review-gate" });
139
+ expect(mapRollupNode({
140
+ __typename: "StatusContext",
141
+ context: "Code Review Gate",
142
+ state: "PENDING"
143
+ })).toMatchObject({ kind: "code-review-gate" });
144
+ });
145
+ it("classifies a Code Review Gate waiting on an owner approval as the gate", () => {
146
+ expect(mapRollupNode({
147
+ __typename: "CheckRun",
148
+ name: "Code Review Gate",
149
+ status: "COMPLETED",
150
+ conclusion: "ACTION_REQUIRED",
151
+ description: "1 of 2 policies"
152
+ })).toMatchObject({
153
+ kind: "code-review-gate",
154
+ reportedState: "ACTION_REQUIRED"
155
+ });
156
+ });
157
+ it("classifies a fast-path Code Review Gate waiting on an owner approval as the gate", () => {
158
+ expect(parseFastCheck({
159
+ name: "Code Review Gate",
160
+ state: "ACTION_REQUIRED",
161
+ bucket: "fail"
162
+ })).toMatchObject({
163
+ kind: "code-review-gate",
164
+ reportedState: "ACTION_REQUIRED"
165
+ });
166
+ expect(parseFastCheck({
167
+ name: "ci",
168
+ state: "ACTION_REQUIRED",
169
+ bucket: "fail"
170
+ })).toMatchObject({
171
+ kind: "failed",
172
+ reportedState: "ACTION_REQUIRED"
173
+ });
174
+ });
175
+ it("maps StatusContext states and drops unknown typenames", () => {
176
+ expect(mapRollupNode({
177
+ __typename: "StatusContext",
178
+ context: "ci",
179
+ state: "EXPECTED"
180
+ })).toMatchObject({
181
+ kind: "pending",
182
+ reportedState: "PENDING"
183
+ });
184
+ expect(mapRollupNode({
185
+ __typename: "StatusContext",
186
+ context: "ci",
187
+ state: "FUTURE_VALUE"
188
+ })).toMatchObject({
189
+ kind: "failed",
190
+ reportedState: "FUTURE_VALUE"
191
+ });
192
+ expect(mapRollupNode({ __typename: "FutureNode" })).toBeNull();
193
+ });
194
+ });
195
+ describe("closed enum parsing", () => {
196
+ const rawPullRequest = {
197
+ mergeable: "MERGEABLE",
198
+ mergeStateStatus: "CLEAN",
199
+ reviewDecision: "APPROVED",
200
+ headRefOid: "head",
201
+ headRefName: "feature",
202
+ baseRefName: "main",
203
+ state: "OPEN",
204
+ mergedAt: null,
205
+ isDraft: false
206
+ };
207
+ it("accepts mergeStateStatus CONFLICTING", () => {
208
+ expect(parsePullRequest({
209
+ ...rawPullRequest,
210
+ mergeStateStatus: "CONFLICTING"
211
+ }, context).mergeStateStatus).toBe("CONFLICTING");
212
+ });
213
+ it("reads gh's empty reviewDecision as no decision rather than a parse failure", () => {
214
+ expect(parsePullRequest({
215
+ ...rawPullRequest,
216
+ reviewDecision: ""
217
+ }, context).reviewDecision).toBeNull();
218
+ });
219
+ it("still rejects an unknown reviewDecision", () => {
220
+ expect(() => parsePullRequest({
221
+ ...rawPullRequest,
222
+ reviewDecision: "MAYBE"
223
+ }, context)).toThrow(WatcherQueryError);
224
+ });
225
+ it("rejects unknown enum values as retryable errors carrying the raw value", () => {
226
+ try {
227
+ parsePullRequest({
228
+ ...rawPullRequest,
229
+ mergeStateStatus: "FUTURE_STATE"
230
+ }, context);
231
+ throw new Error("expected parser to throw");
232
+ } catch (error) {
233
+ expect(error).toBeInstanceOf(WatcherQueryError);
234
+ if (!(error instanceof WatcherQueryError)) throw error;
235
+ expect(error.failure).toMatchObject({
236
+ kind: "missing-key",
237
+ retryable: true,
238
+ rawValue: "\"FUTURE_STATE\""
239
+ });
240
+ }
241
+ });
242
+ });
243
+ it("annotates Bugbot threads with distinct review-pass counts", () => {
244
+ const threads = buildReviewThreads(parseReviewThreadPage({ data: { repository: { pullRequest: { reviewThreads: {
245
+ pageInfo: {
246
+ hasNextPage: false,
247
+ endCursor: null
248
+ },
249
+ nodes: [
250
+ {
251
+ id: "one",
252
+ isResolved: false,
253
+ comments: { nodes: [{
254
+ body: "RUN_ID: run-1",
255
+ createdAt: "now",
256
+ path: "a.ts",
257
+ line: 1,
258
+ author: { login: "bugbot" }
259
+ }] }
260
+ },
261
+ {
262
+ id: "two",
263
+ isResolved: false,
264
+ comments: { nodes: [{
265
+ body: "CURSOR_AUTOMATION_ID: run-2 severity high",
266
+ createdAt: "now",
267
+ path: null,
268
+ line: null,
269
+ author: { login: "cursor" }
270
+ }] }
271
+ },
272
+ {
273
+ id: "resolved",
274
+ isResolved: true,
275
+ comments: { nodes: [{
276
+ body: "RUN_ID: run-3",
277
+ createdAt: "now",
278
+ path: null,
279
+ line: null,
280
+ author: { login: "bugbot" }
281
+ }] }
282
+ }
283
+ ]
284
+ } } } } }).threads);
285
+ expect(threads).toHaveLength(2);
286
+ expect(threads.map((thread) => thread.isBugbot)).toEqual([true, true]);
287
+ expect(threads.map((thread) => thread.bugbotReviewPasses)).toEqual([3, 3]);
288
+ });
289
+ describe("review thread pagination", () => {
290
+ const thread = (id, resolved = false) => ({
291
+ id,
292
+ firstComment: {
293
+ authorLogin: "someone",
294
+ body: "please fix",
295
+ path: "a.ts",
296
+ line: 1,
297
+ createdAt: "now"
298
+ },
299
+ resolved
300
+ });
301
+ it("keeps walking pages until the cursor is exhausted", async () => {
302
+ const reader = fakeReader({ threadPages: [
303
+ {
304
+ threads: [thread("one")],
305
+ endCursor: "next"
306
+ },
307
+ {
308
+ threads: [thread("two")],
309
+ endCursor: "last"
310
+ },
311
+ {
312
+ threads: [thread("three")],
313
+ endCursor: null
314
+ }
315
+ ] });
316
+ const threads = await resolveReviewThreads(reader, context);
317
+ expect(threads.map((item) => item.id)).toEqual([
318
+ "one",
319
+ "two",
320
+ "three"
321
+ ]);
322
+ expect(reader.calls).toEqual([
323
+ "reviewThreadPage:null",
324
+ "reviewThreadPage:next",
325
+ "reviewThreadPage:last"
326
+ ]);
327
+ });
328
+ it("sees an unresolved thread that sits past the first page", async () => {
329
+ const reader = fakeReader({ threadPages: [{
330
+ threads: [thread("early", true)],
331
+ endCursor: "next"
332
+ }, {
333
+ threads: [thread("late")],
334
+ endCursor: null
335
+ }] });
336
+ const threads = await resolveReviewThreads(reader, context);
337
+ expect(threads.map((item) => item.id)).toEqual(["late"]);
338
+ });
339
+ it("stops after a single page when the cursor is exhausted", async () => {
340
+ const reader = fakeReader({ threadPages: [{
341
+ threads: [thread("only")],
342
+ endCursor: null
343
+ }] });
344
+ expect((await resolveReviewThreads(reader, context)).length).toBe(1);
345
+ expect(reader.calls).toEqual(["reviewThreadPage:null"]);
346
+ });
347
+ it("counts Bugbot review passes across every page", async () => {
348
+ const bugbot = (id, runId) => ({
349
+ id,
350
+ firstComment: {
351
+ authorLogin: "bugbot",
352
+ body: `RUN_ID: ${runId}`,
353
+ path: "a.ts",
354
+ line: 1,
355
+ createdAt: "now"
356
+ },
357
+ resolved: false
358
+ });
359
+ const reader = fakeReader({ threadPages: [{
360
+ threads: [bugbot("one", "run-1")],
361
+ endCursor: "next"
362
+ }, {
363
+ threads: [bugbot("two", "run-2")],
364
+ endCursor: null
365
+ }] });
366
+ const threads = await resolveReviewThreads(reader, context);
367
+ expect(threads.map((item) => item.bugbotReviewPasses)).toEqual([2, 2]);
368
+ });
369
+ it("drops resolved threads from later pages too", async () => {
370
+ const reader = fakeReader({ threadPages: [{
371
+ threads: [thread("one")],
372
+ endCursor: "next"
373
+ }, {
374
+ threads: [thread("two", true)],
375
+ endCursor: null
376
+ }] });
377
+ expect((await resolveReviewThreads(reader, context)).map((item) => item.id)).toEqual(["one"]);
378
+ });
379
+ it("rejects a page that omits pageInfo instead of assuming one page", async () => {
380
+ const response = { data: { repository: { pullRequest: { reviewThreads: { nodes: [] } } } } };
381
+ expect(() => parseReviewThreadPage(response)).toThrow(WatcherQueryError);
382
+ });
383
+ });
384
+ describe("context and stack discovery", () => {
385
+ it("returns a fully explicit context without any reader call", async () => {
386
+ const reader = fakeReader();
387
+ expect(await resolveContext({
388
+ reader,
389
+ owner: "explicit",
390
+ repo: "repo",
391
+ pr: context.number
392
+ })).toEqual({
393
+ owner: "explicit",
394
+ repo: "repo",
395
+ number: context.number
396
+ });
397
+ expect(reader.calls).toEqual([]);
398
+ });
399
+ it("uses the local origin before currentPr for an explicit number", async () => {
400
+ const reader = fakeReader({ origin: {
401
+ owner: "local",
402
+ repo: "checkout"
403
+ } });
404
+ expect(await resolveContext({
405
+ reader,
406
+ owner: null,
407
+ repo: null,
408
+ pr: context.number
409
+ })).toEqual({
410
+ owner: "local",
411
+ repo: "checkout",
412
+ number: context.number
413
+ });
414
+ expect(reader.calls).toEqual(["originRepo"]);
415
+ });
416
+ it("orders the connected stack bottom-to-top", () => {
417
+ const ordered = orderStack(context, [
418
+ {
419
+ number: parsePrNumber(41),
420
+ headRefName: "base-feature",
421
+ isCrossRepository: false,
422
+ baseRefName: "main"
423
+ },
424
+ {
425
+ number: context.number,
426
+ headRefName: "feature",
427
+ isCrossRepository: false,
428
+ baseRefName: "base-feature"
429
+ },
430
+ {
431
+ number: parsePrNumber(43),
432
+ headRefName: "upstack",
433
+ isCrossRepository: false,
434
+ baseRefName: "feature"
435
+ }
436
+ ]);
437
+ expect(ordered.map((item) => Number(item.number))).toEqual([
438
+ 41,
439
+ 42,
440
+ 43
441
+ ]);
442
+ });
443
+ it("does not use a fork's same-named head as a stack parent", () => {
444
+ const ordered = orderStack(context, [{
445
+ number: parsePrNumber(41),
446
+ headRefName: "base-feature",
447
+ baseRefName: "main",
448
+ isCrossRepository: true
449
+ }, {
450
+ number: context.number,
451
+ headRefName: "feature",
452
+ baseRefName: "base-feature",
453
+ isCrossRepository: false
454
+ }]);
455
+ expect(ordered.map((item) => Number(item.number))).toEqual([42]);
456
+ });
457
+ it("does not attach upstream branch children to a fork head", () => {
458
+ const ordered = orderStack(context, [{
459
+ number: context.number,
460
+ headRefName: "feature",
461
+ baseRefName: "main",
462
+ isCrossRepository: true
463
+ }, {
464
+ number: parsePrNumber(43),
465
+ headRefName: "upstack",
466
+ baseRefName: "feature",
467
+ isCrossRepository: false
468
+ }]);
469
+ expect(ordered.map((item) => Number(item.number))).toEqual([42]);
470
+ });
471
+ it("terminates when an unrelated fork opens main against main", () => {
472
+ const program = `import { orderStack } from ${githubModule};
473
+ console.log(JSON.stringify(orderStack(${JSON.stringify(context)}, [
474
+ { number: 41, headRefName: "main", baseRefName: "main", isCrossRepository: true },
475
+ { number: 42, headRefName: "feature", baseRefName: "main", isCrossRepository: false }
476
+ ])));`;
477
+ const child = spawnSync(process.execPath, ["-e", program], {
478
+ encoding: "utf8",
479
+ timeout: 2e3
480
+ });
481
+ expect(child.error).toBeUndefined();
482
+ expect(child.status).toBe(0);
483
+ expect(JSON.parse(child.stdout).map((item) => item.number)).toEqual([42]);
484
+ });
485
+ it("requests and parses fork identity from gh before stack discovery", () => {
486
+ const directory = mkdtempSync(join(tmpdir(), "watch-pr-fork-"));
487
+ try {
488
+ writeFileSync(join(directory, "gh"), `#!/bin/sh
489
+ case "$*" in
490
+ *number,headRefName,baseRefName,isCrossRepository*) ;;
491
+ *) exit 2 ;;
492
+ esac
493
+ printf '%s\\n' '[{"number":42,"headRefName":"feature","baseRefName":"main","isCrossRepository":true}]'
494
+ `, { mode: 493 });
495
+ const program = `import { GhGitHubReader } from ${githubModule};
496
+ console.log(JSON.stringify(await new GhGitHubReader().openPullRequests({ owner: "owner", repo: "repo" })));`;
497
+ const child = spawnSync(process.execPath, ["-e", program], {
498
+ encoding: "utf8",
499
+ timeout: 2e3,
500
+ env: {
501
+ ...process.env,
502
+ PATH: directory + ":" + process.env.PATH
503
+ }
504
+ });
505
+ expect(child.error).toBeUndefined();
506
+ expect(child.status).toBe(0);
507
+ expect(JSON.parse(child.stdout)).toEqual([{
508
+ number: 42,
509
+ headRefName: "feature",
510
+ baseRefName: "main",
511
+ isCrossRepository: true
512
+ }]);
513
+ } finally {
514
+ rmSync(directory, {
515
+ recursive: true,
516
+ force: true
517
+ });
518
+ }
519
+ });
520
+ });
521
+ export {};