@coreplane/switchboard 1.260.3 → 1.260.4

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 (121) hide show
  1. package/dist/assets/deploy/cloudflare-resident/Dockerfile +11 -2
  2. package/dist/assets/package-lock.json +3 -3
  3. package/dist/assets/package.json +4 -3
  4. package/dist/assets/project.json +4 -0
  5. package/dist/assets/source.json +3 -3
  6. package/dist/assets/src/core/coordinator/driver.ts +4 -4
  7. package/dist/assets/src/core/runFriction.ts +1 -1
  8. package/dist/assets/src/core/ship/contract.ts +1 -1
  9. package/dist/assets/src/core/ship/coordinator.ts +31 -31
  10. package/dist/assets/src/core/ship/renewal.ts +3 -1
  11. package/dist/assets/src/execution/bashTimeout.ts +1 -1
  12. package/dist/assets/src/execution/sandboxErrors.ts +6 -6
  13. package/dist/assets/web/dist/.vite/manifest.json +488 -488
  14. package/dist/assets/web/dist/assets/{AppShell-CSXDCV4x.js → AppShell-DPJVhUGP.js} +1 -1
  15. package/dist/assets/web/dist/assets/{CostChart-DsuPS2g7.js → CostChart-4TeDdbvJ.js} +1 -1
  16. package/dist/assets/web/dist/assets/CostsPage-8HDgvuWB.js +1 -0
  17. package/dist/assets/web/dist/assets/{DeliveryPage-62qCSLvk.js → DeliveryPage-BaemWKW8.js} +1 -1
  18. package/dist/assets/web/dist/assets/{HomePage-Dk3HRBc3.js → HomePage-fgKQHLaO.js} +1 -1
  19. package/dist/assets/web/dist/assets/{InputMenu-BdZXnibG.js → InputMenu-BRV35UWP.js} +1 -1
  20. package/dist/assets/web/dist/assets/{MetricsPage-o5fSaCTe.js → MetricsPage-BQBuIuYU.js} +1 -1
  21. package/dist/assets/web/dist/assets/{NotFoundPage-DHuWyjlq.js → NotFoundPage-CDjMfCcj.js} +1 -1
  22. package/dist/assets/web/dist/assets/{PendingTurnRow-CeTGOXUy.js → PendingTurnRow-vuWiDzvz.js} +1 -1
  23. package/dist/assets/web/dist/assets/{PlanePage-RD0M6O6W.js → PlanePage-DXMJf8FB.js} +1 -1
  24. package/dist/assets/web/dist/assets/{ResidentDetailPage-Dl2KId95.js → ResidentDetailPage-DdC8yU_S.js} +1 -1
  25. package/dist/assets/web/dist/assets/{ResidentsIndexPage-DBy43I1R.js → ResidentsIndexPage-CSM-KODv.js} +1 -1
  26. package/dist/assets/web/dist/assets/{RunFoldRow-CwHDL-Mo.js → RunFoldRow-Csik3wD6.js} +1 -1
  27. package/dist/assets/web/dist/assets/{RunRoutePage-Du1HqLZs.js → RunRoutePage-jo6NZxsP.js} +3 -3
  28. package/dist/assets/web/dist/assets/{RunsIndexPage-BZphy3Hr.js → RunsIndexPage-Dx1Y4sSq.js} +1 -1
  29. package/dist/assets/web/dist/assets/{RunsTabs-kHbbxVHx.js → RunsTabs-C7Ua3XX5.js} +1 -1
  30. package/dist/assets/web/dist/assets/{ScheduledPage-B7jh898t.js → ScheduledPage-BuB_NZyR.js} +1 -1
  31. package/dist/assets/web/dist/assets/{SettingSelect-BfID5nF4.js → SettingSelect-CEeA4iuC.js} +1 -1
  32. package/dist/assets/web/dist/assets/{SettingsPage-3uLEsiiE.js → SettingsPage-Cn96jB6Q.js} +1 -1
  33. package/dist/assets/web/dist/assets/{SilentTurn-CKxSxnQC.js → SilentTurn-D2cJfpEq.js} +1 -1
  34. package/dist/assets/web/dist/assets/{SlackMark-CtiHrNjr.js → SlackMark-DB0MGiF5.js} +1 -1
  35. package/dist/assets/web/dist/assets/{StatusDot-x7vcK0iE.js → StatusDot-DDHUCPVJ.js} +1 -1
  36. package/dist/assets/web/dist/assets/{Tooltip-wVXCWhMp.js → Tooltip-B4VIHkAI.js} +1 -1
  37. package/dist/assets/web/dist/assets/{UnitRoutePage-V5BXK7FH.js → UnitRoutePage-C-2DfjLI.js} +1 -1
  38. package/dist/assets/web/dist/assets/{angular-html-C8b5YTHf.js → angular-html-BRRhsdQE.js} +1 -1
  39. package/dist/assets/web/dist/assets/{angular-ts-C3f4WYNe.js → angular-ts-BFj-mqCm.js} +1 -1
  40. package/dist/assets/web/dist/assets/{apl-DSHxUJc3.js → apl-CsBuy1IL.js} +1 -1
  41. package/dist/assets/web/dist/assets/{astro-BSGCCBP6.js → astro-cVaqxBZq.js} +1 -1
  42. package/dist/assets/web/dist/assets/{blade-DFeYrMKk.js → blade-MEP5mWha.js} +1 -1
  43. package/dist/assets/web/dist/assets/{browser-B6T5hjfR.js → browser-CCImen5O.js} +1 -1
  44. package/dist/assets/web/dist/assets/{c-L_QOnFVv.js → c-D4NGQ_Pd.js} +1 -1
  45. package/dist/assets/web/dist/assets/{chapel-DLQWUwhu.js → chapel-BkWImsIy.js} +1 -1
  46. package/dist/assets/web/dist/assets/{cobol-1DlMmrEO.js → cobol-RPDSPSET.js} +1 -1
  47. package/dist/assets/web/dist/assets/{coffee-BTB6os6N.js → coffee-DZ5z7oVX.js} +1 -1
  48. package/dist/assets/web/dist/assets/{cpp-BvJI2z3u.js → cpp-CrBtIaRZ.js} +1 -1
  49. package/dist/assets/web/dist/assets/{crystal-B11wGW_h.js → crystal-DJesYi6f.js} +1 -1
  50. package/dist/assets/web/dist/assets/{css-BiIWx5Pw.js → css-DO9mjau-.js} +1 -1
  51. package/dist/assets/web/dist/assets/{dist-C-XGnvM4.js → dist-Cf1CmCSK.js} +2 -2
  52. package/dist/assets/web/dist/assets/{edge-nSglEXaD.js → edge-C3VzbSW0.js} +1 -1
  53. package/dist/assets/web/dist/assets/{elixir-x7o8qMl8.js → elixir-BkD_Vhfe.js} +1 -1
  54. package/dist/assets/web/dist/assets/{elm-DGrRyqTQ.js → elm-KIM0mFPk.js} +1 -1
  55. package/dist/assets/web/dist/assets/{erb--_dh58Nw.js → erb-D2ruCqb2.js} +1 -1
  56. package/dist/assets/web/dist/assets/{eventSource-Bj_dZf6M.js → eventSource-Cv0AFlMi.js} +1 -1
  57. package/dist/assets/web/dist/assets/{git-rebase-DbpUXwyg.js → git-rebase-CnfbQTxA.js} +1 -1
  58. package/dist/assets/web/dist/assets/{glimmer-js-BG0CuZG5.js → glimmer-js-CbiwFXWH.js} +1 -1
  59. package/dist/assets/web/dist/assets/{glimmer-ts-CQIl5qyR.js → glimmer-ts-DV8ii0Pv.js} +1 -1
  60. package/dist/assets/web/dist/assets/{glsl-9PJNQTcw.js → glsl-CgmznWkA.js} +1 -1
  61. package/dist/assets/web/dist/assets/{graphql-CzVTzjEI.js → graphql-BK-bBYfH.js} +1 -1
  62. package/dist/assets/web/dist/assets/{hack-BhCmAz3Q.js → hack-DnwTTP5n.js} +1 -1
  63. package/dist/assets/web/dist/assets/{haml-BmI12COR.js → haml-DAVJsWwT.js} +1 -1
  64. package/dist/assets/web/dist/assets/{handlebars-Cib-ISTS.js → handlebars-voOVfxFL.js} +1 -1
  65. package/dist/assets/web/dist/assets/{html-D3lDapvI.js → html-3FTqwaPk.js} +1 -1
  66. package/dist/assets/web/dist/assets/{html-derivative-DRPY8vOK.js → html-derivative-CSlhLDQn.js} +1 -1
  67. package/dist/assets/web/dist/assets/{http-B9VttK6m.js → http-Vx4ilPfS.js} +1 -1
  68. package/dist/assets/web/dist/assets/{hurl-C0saNJYj.js → hurl-DxyZDE3R.js} +1 -1
  69. package/dist/assets/web/dist/assets/{java-BCFbtYAj.js → java-nWqdjNJG.js} +1 -1
  70. package/dist/assets/web/dist/assets/{javascript-nwBjnrhh.js → javascript-Dr82HOwp.js} +1 -1
  71. package/dist/assets/web/dist/assets/{jinja-CMYO9byN.js → jinja-BhjLrPOt.js} +1 -1
  72. package/dist/assets/web/dist/assets/{jison-Cko8HnE_.js → jison-DilGIUa8.js} +1 -1
  73. package/dist/assets/web/dist/assets/{json-Dzv__YsX.js → json-CrOcvmhJ.js} +1 -1
  74. package/dist/assets/web/dist/assets/{jsx-CTFxagih.js → jsx-BW_RX-fo.js} +1 -1
  75. package/dist/assets/web/dist/assets/{julia-_Xvten1-.js → julia-CWH071DB.js} +1 -1
  76. package/dist/assets/web/dist/assets/{just-bLps4qOu.js → just-DkhhoWBy.js} +1 -1
  77. package/dist/assets/web/dist/assets/{latex-D6UaYsUP.js → latex-cuHJ4TUT.js} +1 -1
  78. package/dist/assets/web/dist/assets/{liquid-CoDIeULa.js → liquid-DG6afYQN.js} +1 -1
  79. package/dist/assets/web/dist/assets/{lua-D1KFRnVf.js → lua-C5CMnCPS.js} +1 -1
  80. package/dist/assets/web/dist/assets/{main-DAL--hyj.js → main-DxPqaFu9.js} +3 -3
  81. package/dist/assets/web/dist/assets/{marko-B5N5XcOw.js → marko-Dy2_DuC4.js} +1 -1
  82. package/dist/assets/web/dist/assets/{mdc-uxzjQO3q.js → mdc-BMKb_EeA.js} +1 -1
  83. package/dist/assets/web/dist/assets/{nginx-BMJxs2TK.js → nginx-og0RgMKf.js} +1 -1
  84. package/dist/assets/web/dist/assets/{nim-DqQ3-8p1.js → nim-CQxTQQP0.js} +1 -1
  85. package/dist/assets/web/dist/assets/{org-DsY6LK59.js → org-b5lkq_Pl.js} +1 -1
  86. package/dist/assets/web/dist/assets/{perl--pq9iJFp.js → perl-D9X5_i_J.js} +1 -1
  87. package/dist/assets/web/dist/assets/{php-RHCMbsM2.js → php-CwUxvLX0.js} +1 -1
  88. package/dist/assets/web/dist/assets/{pug-CFNq_FCp.js → pug-uOIOExr0.js} +1 -1
  89. package/dist/assets/web/dist/assets/{qml-C9Acx9E8.js → qml-g5RoADf-.js} +1 -1
  90. package/dist/assets/web/dist/assets/{r-DxlU8FBd.js → r-5LdAopD3.js} +1 -1
  91. package/dist/assets/web/dist/assets/{razor-CLJ2kKih.js → razor-DwrvFLYQ.js} +1 -1
  92. package/dist/assets/web/dist/assets/{regexp-CzmdGoEp.js → regexp-dGtAaxC-.js} +1 -1
  93. package/dist/assets/web/dist/assets/{rst-D9yuxBET.js → rst-wFdW0brr.js} +1 -1
  94. package/dist/assets/web/dist/assets/{ruby-CIRcEvnD.js → ruby-DPglN6I9.js} +1 -1
  95. package/dist/assets/web/dist/assets/{sas-LOl-uUPh.js → sas-CUbCs40d.js} +1 -1
  96. package/dist/assets/web/dist/assets/{scss-BX2_sZHY.js → scss-B-DUiI1c.js} +1 -1
  97. package/dist/assets/web/dist/assets/{shellscript-4d-QvKkY.js → shellscript-lGOV7Ram.js} +1 -1
  98. package/dist/assets/web/dist/assets/{shellsession-DhB16UGy.js → shellsession-BDWtAAjr.js} +1 -1
  99. package/dist/assets/web/dist/assets/{soy-Cm1xTUBr.js → soy-zod_dcOy.js} +1 -1
  100. package/dist/assets/web/dist/assets/{sql-BsfcBJdu.js → sql-BG0HSSoM.js} +1 -1
  101. package/dist/assets/web/dist/assets/{sseReplay-BqgOggyQ.js → sseReplay-BzoeVxZB.js} +1 -1
  102. package/dist/assets/web/dist/assets/{stata-Dwat3BSw.js → stata-BVUMLp1W.js} +1 -1
  103. package/dist/assets/web/dist/assets/{surrealql-P4rtxzAW.js → surrealql-DiSUbHKF.js} +1 -1
  104. package/dist/assets/web/dist/assets/{svelte-DTcsi8S8.js → svelte-DZenT45Y.js} +1 -1
  105. package/dist/assets/web/dist/assets/{templ-D-kP0Tg5.js → templ-BWKRNM0-.js} +1 -1
  106. package/dist/assets/web/dist/assets/{tex-D-4vuedK.js → tex-CbMzBlkK.js} +1 -1
  107. package/dist/assets/web/dist/assets/{ts-tags-jEEWYxsu.js → ts-tags-CZsVorDN.js} +1 -1
  108. package/dist/assets/web/dist/assets/{tsx-CFnlrVzT.js → tsx-R1r3mH4W.js} +1 -1
  109. package/dist/assets/web/dist/assets/{twig-D5rGrPjs.js → twig-BjPMyjKH.js} +1 -1
  110. package/dist/assets/web/dist/assets/{typescript-nYIQ35or.js → typescript-Bx-66VN_.js} +1 -1
  111. package/dist/assets/web/dist/assets/{typst-3-UX8dna.js → typst-fW037Eo6.js} +1 -1
  112. package/dist/assets/web/dist/assets/{vue-CLIPIX03.js → vue-D1V_GNxW.js} +1 -1
  113. package/dist/assets/web/dist/assets/{vue-html-Ci9CpkHU.js → vue-html-C-4pTZ-s.js} +1 -1
  114. package/dist/assets/web/dist/assets/{vue-vine-DL5pEYYk.js → vue-vine-C-QsX3hR.js} +1 -1
  115. package/dist/assets/web/dist/assets/{wallClock-DmaC1sKA.js → wallClock-x0hjr0J_.js} +1 -1
  116. package/dist/assets/web/dist/assets/{xml-bO80qikC.js → xml-BJTJXPgC.js} +1 -1
  117. package/dist/assets/web/dist/assets/{xsl-BPze60Ej.js → xsl-Xc9lukqe.js} +1 -1
  118. package/dist/assets/web/dist/assets/{yaml-BQUSon_o.js → yaml-Dw-FMsQa.js} +1 -1
  119. package/dist/cli.js +134 -146
  120. package/package.json +1 -1
  121. package/dist/assets/web/dist/assets/CostsPage-BaeWnm-o.js +0 -1
@@ -152,10 +152,19 @@ RUN rm -f /usr/local/bin/bun \
152
152
  # The build asserts the binary with the probe the extract script uses at run
153
153
  # time (`command -v`): `unsquashfs -version` exits 1 on squashfs-tools 4.5 (this
154
154
  # base) when no filesystem is named, and failed the 1.2.0 image build that way.
155
+ #
156
+ # ripgrep (`rg`) + fd: the grep/find tools call these binaries directly. Ubuntu
157
+ # packages fd as `fd-find` with the binary named `fdfind`, so the image exposes
158
+ # the conventional `fd` name from /usr/local/bin (ahead of /usr/bin on PATH).
159
+ # Both are image tools, never downloaded by a run; their PATH probes make a
160
+ # missing package or link fail the image build instead of every resident run.
155
161
  RUN apt-get update \
156
- && apt-get install -y --no-install-recommends squashfs-tools \
162
+ && apt-get install -y --no-install-recommends squashfs-tools ripgrep fd-find \
163
+ && ln -s /usr/bin/fdfind /usr/local/bin/fd \
157
164
  && rm -rf /var/lib/apt/lists/* \
158
- && command -v unsquashfs >/dev/null
165
+ && command -v unsquashfs >/dev/null \
166
+ && command -v rg >/dev/null \
167
+ && command -v fd >/dev/null
159
168
 
160
169
  # The toolchain for what a run BUILDS and LOOKS AT — the same layer as the
161
170
  # sandbox image's (deploy/cloudflare-sandbox/Dockerfile);
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "switchboard",
3
- "version": "1.260.3",
3
+ "version": "1.260.4",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "switchboard",
9
- "version": "1.260.3",
9
+ "version": "1.260.4",
10
10
  "license": "Apache-2.0",
11
11
  "workspaces": [
12
12
  "web",
@@ -20445,7 +20445,7 @@
20445
20445
  },
20446
20446
  "packages/switchboard": {
20447
20447
  "name": "@coreplane/switchboard",
20448
- "version": "1.260.3",
20448
+ "version": "1.260.4",
20449
20449
  "license": "Apache-2.0",
20450
20450
  "dependencies": {
20451
20451
  "@earendil-works/pi-ai": "0.85.1",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "switchboard",
3
- "version": "1.260.3",
3
+ "version": "1.260.4",
4
4
  "private": true,
5
5
  "description": "Mention it in Slack and an agent reviews the PR, ships the fix, or answers the question — on the model you choose, with its tools running where you decide.",
6
6
  "license": "Apache-2.0",
@@ -39,9 +39,9 @@
39
39
  "cli": "tsx src/cli.ts",
40
40
  "verify": "npm run verify:root && npm run verify --workspaces --if-present && npm run check:site",
41
41
  "verify:root": "npm run check:consistency && npm run typecheck && npm run lint && npm run format:check && npm test && npm run check:dist",
42
- "check:consistency": "npm run check:lockfile && npm run check:sandbox-pair && npm run skills:check && npm run check:deps-drift && npm run licenses:check && npm run docs:check && npm run pr-title:check && npm run specs:check && npm run decisions:check && npm run hygiene:check && npm run vocabulary:check && npm run check:project-facts && npm run check:registry-drift && npm run agents:check && npm run clock:check && npm run screenshots:check",
42
+ "check:consistency": "npm run check:lockfile && npm run check:sandbox-pair && npm run skills:check && npm run check:deps-drift && npm run licenses:check && npm run docs:check && npm run pr-title:check && npm run specs:check && npm run decisions:check && npm run hygiene:check && npm run vocabulary:check && npm run user-message:check && npm run check:project-facts && npm run check:registry-drift && npm run agents:check && npm run clock:check && npm run screenshots:check",
43
43
  "ci:gate": "node scripts/ci-gate.mjs",
44
- "fix": "npm run docs:gen && npm run pr-title:gen && npm run agents:gen && npm run clock:gen && npm run hygiene:gen && npm run vocabulary:gen && npm run deploy:gen && npm run skills:sync && npm run lint:fix && npm run format",
44
+ "fix": "npm run docs:gen && npm run pr-title:gen && npm run agents:gen && npm run clock:gen && npm run hygiene:gen && npm run vocabulary:gen && npm run user-message:check -- --write && npm run deploy:gen && npm run skills:sync && npm run lint:fix && npm run format",
45
45
  "deploy:gen": "npm run --silent cli -- deploy init",
46
46
  "deploy:check": "npm run --silent cli -- deploy init --check",
47
47
  "check:lockfile": "node scripts/check-lockfile.mjs",
@@ -75,6 +75,7 @@
75
75
  "hygiene:gen": "node scripts/public-hygiene.mjs --write",
76
76
  "vocabulary:check": "node scripts/vocabulary-check.mjs",
77
77
  "vocabulary:gen": "node scripts/vocabulary-check.mjs --write",
78
+ "user-message:check": "node scripts/user-message-check.mjs",
78
79
  "docs:changed": "node scripts/docs-changed.mjs",
79
80
  "deploy:targets": "node scripts/deploy-targets.mjs",
80
81
  "docs:dev": "npm run dev -w docs --",
@@ -183,6 +183,10 @@
183
183
  "does": "Records the remaining internal words; refuses growth unless `-- --force`.",
184
184
  "when": "Part of `fix`."
185
185
  },
186
+ "user-message:check": {
187
+ "does": "No user-facing statement delegates recovery.",
188
+ "when": "Part of `check:consistency`; the baseline only shrinks."
189
+ },
186
190
  "agents:gen": {
187
191
  "does": "Writes the Commands table in AGENTS.md from `package.json` and this file.",
188
192
  "when": "After adding or changing a script; part of `fix`."
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.260.3",
3
- "commit": "9f64917c9c2520b2797f3d5e6a73494879408e26",
4
- "builtAt": "2026-09-22T05:04:08.797Z"
2
+ "version": "1.260.4",
3
+ "commit": "714dbd34ad03841fe421bfdc78648854b4c8dbc1",
4
+ "builtAt": "2026-09-22T07:09:50.930Z"
5
5
  }
@@ -806,7 +806,7 @@ async function tellStepThrew(
806
806
  : `in round ${at.round.index} (\`${at.step}\`)`;
807
807
  const report =
808
808
  `⚠️ The runner failed ${where}: ${line}\n\n` +
809
- "Re-issue `agent:ship` in this thread to continue — a pull request already approved with green checks resumes at the checks step, never at a fresh coding round.";
809
+ "The unit remains bound to this thread; the next reply continues it, and a pull request already approved with green checks resumes at the checks step, never at a fresh coding round.";
810
810
  const body = {
811
811
  ...tag,
812
812
  ending: {
@@ -1041,14 +1041,14 @@ async function runUnit(
1041
1041
 
1042
1042
  /** The report of a unit the hard stop ended before it ran (record 0060; issue 1924). */
1043
1043
  function stoppedReport(unit: string): string {
1044
- return `⏹ Stopped: the pipeline's hosted parent was hard-stopped, so ${unit} was ended without running. Re-issue the plan naming the remaining units to run them.`;
1044
+ return `⏹ Stopped: the pipeline's hosted parent was hard-stopped, so ${unit} was ended without running. The original plan remains the durable task for any later pipeline.`;
1045
1045
  }
1046
1046
 
1047
1047
  function blockedReport(unit: string, dep: string, depEnding: string): string {
1048
1048
  if (depEnding === "blocked")
1049
- return `⛔ Blocked: ${unit} waits on ${dep}, which is blocked itself. Re-issue the plan naming the remaining units once it is resolved.`;
1049
+ return `⛔ Blocked: ${unit} waits on ${dep}, which is blocked itself. A later pipeline recognizes both units once the dependency is resolved.`;
1050
1050
  const person = depEnding === "merge_ready";
1051
- return `⛔ Blocked: ${unit} waits on ${dep}, which ended ${depEnding}${person ? " — a person's merge" : ""}. Re-issue the plan naming the remaining units once it is ${person ? "merged" : "resolved"}.`;
1051
+ return `⛔ Blocked: ${unit} waits on ${dep}, which ended ${depEnding}${person ? " — a person's merge" : ""}. A later pipeline recognizes the dependency once it is ${person ? "merged" : "resolved"}.`;
1052
1052
  }
1053
1053
 
1054
1054
  /** End a unit that never entered `runUnit` under the same failure net. */
@@ -621,7 +621,7 @@ export function analyzeRunFriction(events: readonly RunEvent[], opts: FrictionOp
621
621
  findings.push({
622
622
  category: "infra_failure",
623
623
  severity: "high",
624
- summary: `no result for tool call (run ended mid-tool): ${event.summary}`,
624
+ summary: `no result for tool call (the run ended mid-tool): ${event.summary}`,
625
625
  tool: event.tool,
626
626
  eventIndex: index,
627
627
  ...(durationMs !== undefined ? { durationMs } : {}),
@@ -482,7 +482,7 @@ function renderFirstInstruction(rebase: ChildContract["rebase"]): string {
482
482
  `Rebase ${branch} onto ${onto} before any other work — the parent unit has merged and the base has moved; ` +
483
483
  `the only writes are your own on that branch. ` +
484
484
  `Push the branch as soon as the change exists and the fast gates pass — the project's full verification ` +
485
- `is CI's gate, run there after the push with any fix as a further commit; an unpushed tree does not ` +
485
+ `is CI's gate and runs there after the push, with any fix as a further commit; an unpushed tree does not ` +
486
486
  `survive the run's end. ${gates} ${REBASE_POINTER} At the wind-down note, ` +
487
487
  `commit and push what compiles, say what does not, then answer. ${TIMEOUT_ON_LONG_COMMANDS}`
488
488
  );
@@ -137,11 +137,11 @@ export function shipInterruptedNote(prUrl?: string, cause?: InterruptionCause, i
137
137
  const stands = prUrl
138
138
  ? `Its work stands on GitHub: ${prUrl}.`
139
139
  : "Whatever it pushed stands on its pipeline branch; no PR was opened yet.";
140
- const reissue = idle
141
- ? "To continue, reply in this thread to continue."
140
+ const continuation = idle
141
+ ? "The next reply in this thread continues the unit."
142
142
  : prUrl
143
- ? `To continue the review loop, re-issue \`agent:ship\` in this thread with only the PR URL (${prUrl}).`
144
- : "To continue, re-issue `agent:ship` in this thread with the task — round 0 runs again on the same branch.";
143
+ ? `The pipeline kept its task, branch and pull request; the next reply in this thread continues the review loop at ${prUrl}.`
144
+ : "The pipeline kept its task and branch; the next reply in this thread starts round 0 again on that branch.";
145
145
  const opening =
146
146
  cause === "container_replaced"
147
147
  ? "⚠️ The resident container running this pipeline's child was replaced (a deploy's image swap) and the child could not resume, so the pipeline stopped."
@@ -150,7 +150,7 @@ export function shipInterruptedNote(prUrl?: string, cause?: InterruptionCause, i
150
150
  : cause === "bot_restart"
151
151
  ? "⚠️ The bot restarted while this ship pipeline was running, so the pipeline stopped."
152
152
  : "⚠️ This ship pipeline's child was interrupted and could not resume, so the pipeline stopped.";
153
- return `${opening} ${stands} ${reissue}`;
153
+ return `${opening} ${stands} ${continuation}`;
154
154
  }
155
155
 
156
156
  // ---- the plan graph --------------------------------------------------------------------------------
@@ -1755,7 +1755,7 @@ function settleCoding(
1755
1755
  next,
1756
1756
  {
1757
1757
  kind: "aborted",
1758
- reason: `⚠️ The coding child ended because ${cause}; the branch carries the interrupted work at \`${checkpoint.sha.slice(0, 7)}\` on \`${checkpoint.branch}\`. Re-issue the request to resume from it instead of starting over.`,
1758
+ reason: `⚠️ The coding child ended because ${cause}; the branch carries the interrupted work at \`${checkpoint.sha.slice(0, 7)}\` on \`${checkpoint.branch}\`. The next reply in this thread resumes from that checkpoint instead of starting over.`,
1759
1759
  round,
1760
1760
  reviewRounds: next.reviewRounds,
1761
1761
  },
@@ -2922,7 +2922,7 @@ function dispositionFor(s: UnitPipelineState, finding: Finding, round: number):
2922
2922
  function writeUpPointer(s: UnitPipelineState, kind: RoundKind, runId: string | undefined): string | undefined {
2923
2923
  if (runId === undefined) return undefined;
2924
2924
  const base = s.input.runPageBase;
2925
- const at = base !== undefined ? `${base.replace(/\/+$/, "")}/${encodeURIComponent(runId)}` : `run ${runId}`;
2925
+ const at = base !== undefined ? `${base.replace(/\/+$/, "")}/${encodeURIComponent(runId)}` : `the run ${runId}`;
2926
2926
  return `The ${presetOf(kind)} child's write-up is its own message above in this thread — the single copy of the detail (${at}).`;
2927
2927
  }
2928
2928
 
@@ -3036,9 +3036,9 @@ export function renderUnitReport(
3036
3036
  // The re-issue line keys on the instance's mark, never on who merges: a
3037
3037
  // generated plan is re-issued with the request's own text, a seeded one by
3038
3038
  // its plan path — and a seeded plan can be a person's merge too.
3039
- const reissue = s.input.generated
3040
- ? `To continue, re-issue \`agent:ship\` in this thread with the same text${prUrl ? ` and include the PR URL (${prUrl})` : " — include the PR URL if a PR exists"}.`
3041
- : `The unit's dependents in this plan stay blocked; the unit runs again when the plan is re-issued.`;
3039
+ const continuation = s.input.generated
3040
+ ? `The pipeline kept this unit's task and branch; the next reply in this thread continues it${prUrl ? ` from the open pull request (${prUrl})` : " from the branch head"}.`
3041
+ : `The unit's dependents in this plan stay blocked; the next run of this plan recognizes the unit's branch and pull request.`;
3042
3042
  const declined = [...(s.dispositionsByRound[e.reviewRounds - 1] ?? [])].filter((d) => d.disposition === "declined");
3043
3043
  const declinedLine = `Declined findings: ${declined.length > 0 ? declined.map((d) => `${d.findingId}${d.note ? ` — ${d.note}` : ""}`).join("; ") : "none"}`;
3044
3044
  const verdictLine = `Verdict: LGTM${s.lastVerdictSummary ? ` — ${s.lastVerdictSummary}` : ""}`;
@@ -3063,7 +3063,7 @@ export function renderUnitReport(
3063
3063
  const checkpointLine = (checkpoint: { branch: string; sha: string } | undefined) =>
3064
3064
  checkpoint === undefined
3065
3065
  ? undefined
3066
- : `The branch carries the interrupted work at \`${checkpoint.sha.slice(0, 7)}\` on \`${checkpoint.branch}\`; re-issue to resume from it instead of starting over.`;
3066
+ : `The branch carries the interrupted work at \`${checkpoint.sha.slice(0, 7)}\` on \`${checkpoint.branch}\`; the next reply in this thread resumes from it instead of starting over.`;
3067
3067
  switch (e.kind) {
3068
3068
  case "merged":
3069
3069
  if (e.by === "other")
@@ -3119,13 +3119,13 @@ export function renderUnitReport(
3119
3119
  // the cause and the person's exact next step.
3120
3120
  if (e.cause === "draft") {
3121
3121
  const link = e.pr !== undefined ? ` — ${e.pr.url}` : "";
3122
- if (!shows(verbosity, "verbose")) return `⏸️ Held: draft — mark it ready to continue${link}`;
3123
- const draftReissue = s.input.generated
3124
- ? `To continue, mark it ready and re-issue \`agent:ship\` in this thread with only the PR URL${e.pr !== undefined ? ` (${e.pr.url})` : ""} — no new task text; the re-issued pipeline resumes at the review round.`
3125
- : `To continue, mark it ready; ${reissue.charAt(0).toLowerCase()}${reissue.slice(1)}`;
3122
+ if (!shows(verbosity, "verbose")) return `⏸️ Held: draft — GitHub still marks the pull request as draft${link}`;
3123
+ const draftContinuation = s.input.generated
3124
+ ? `The pipeline kept the task, branch and pull request${e.pr !== undefined ? ` (${e.pr.url})` : ""}; after GitHub marks it ready, the next reply in this thread resumes at the review round.`
3125
+ : `The unit stays held while GitHub marks the pull request as a draft; ${continuation.charAt(0).toLowerCase()}${continuation.slice(1)}`;
3126
3126
  return join([
3127
3127
  `⏸️ Held after ${rounds}${e.pr !== undefined ? `: ${e.pr.url}` : ""} — the pull request is a draft, so nothing can merge and no fix round would change anything.`,
3128
- draftReissue,
3128
+ draftContinuation,
3129
3129
  ]);
3130
3130
  }
3131
3131
  // The blocked hold (issue 2086): the coding child concluded its round
@@ -3138,7 +3138,7 @@ export function renderUnitReport(
3138
3138
  return join([
3139
3139
  `⏸️ Held after ${rounds}: the coding child of round ${e.round.index} concluded its round blocked instead of opening a pull request — ${e.reason}.${prLine}`,
3140
3140
  writeUpPointer(s, e.round.kind, e.runId),
3141
- `No renewal was spent and no second coding child ran — a concluded round is not renewed (a renewal continues a budget that ran out mid-work). Next step: your word in this thread — answer what the child raised, and the unit continues from there. ${reissue}`,
3141
+ `No renewal was spent and no second coding child ran — a concluded round is not renewed (a renewal continues a budget that ran out mid-work). The child's answer remains the thread's open question; the next reply is treated as its answer. ${continuation}`,
3142
3142
  ]);
3143
3143
  }
3144
3144
  // A human-gated row is a question to a person. The enclosing idle keeps
@@ -3150,7 +3150,7 @@ export function renderUnitReport(
3150
3150
  return join([
3151
3151
  `⏸️ Waiting for a person after ${rounds}${e.pr !== undefined ? `: ${e.pr.url}` : ""} — every finding of review round ${e.round.index} is human-gated, a receipt only a person can produce: ${rows}. The unit stays live; no unanswered fix round was opened.`,
3152
3152
  levelLine,
3153
- `Reply in this unit thread or comment on the pull request with the receipt. The answer and finding become the fix round's brief, then review runs again.`,
3153
+ `The next reply in this unit thread or authorized pull request comment becomes the fix round's answer; review then runs again.`,
3154
3154
  ]);
3155
3155
  }
3156
3156
  case "merge_refused":
@@ -3166,8 +3166,8 @@ export function renderUnitReport(
3166
3166
  return join([
3167
3167
  `⚠️ The review approved ${e.pr.url} but the pipeline did not merge it: ${e.reason}. A person decides what becomes of the pull request.`,
3168
3168
  s.input.generated
3169
- ? reissue
3170
- : `The approved work is on the branch: rebase or fix it, push, and merge it by hand. Then re-issue the plan naming the remaining units — a unit whose pull request has merged is recognized and not run again, and its dependents start from there.`,
3169
+ ? continuation
3170
+ : "The approved work remains on the branch. This is a bug: the pipeline has no automatic recovery for this refused merge, so the unit's dependents remain blocked.",
3171
3171
  ]);
3172
3172
  case "round_cap": {
3173
3173
  // The quiet copy counts the last review's open findings, only the
@@ -3186,7 +3186,7 @@ export function renderUnitReport(
3186
3186
  failedChecks.length > 0
3187
3187
  ? `🧢 Ship stopped at a cap: the ${e.maxRounds}-round cap — the review of round ${e.reviewRounds} approved, but ${failedChecks.map((f) => `\`${f.id}\``).join(", ")} failed at the approved head and no fix round remains.${prLine}`
3188
3188
  : `🧢 Ship stopped at a cap: the ${e.maxRounds}-round cap — no approval after ${rounds}.${prLine}`;
3189
- return join([headline, splitReport(s), reissue]);
3189
+ return join([headline, splitReport(s), continuation]);
3190
3190
  }
3191
3191
  case "wall_clock_cap":
3192
3192
  if (!shows(verbosity, "verbose")) return `🧢 Out of budget — no approval after ${rounds}.${prLine}`;
@@ -3194,13 +3194,13 @@ export function renderUnitReport(
3194
3194
  `🧢 Ship stopped at a cap: the remaining pipeline time (~${Math.max(0, Math.round(e.remainingMs / MIN))} min of the ${s.input.caps.maxMinutes}-minute budget) cannot hold another round${e.refused ? ` (the ${e.refused.round} round would get ${e.refused.minutes} min, under its floor of ${e.refused.floor})` : ""} — no approval after ${rounds}.${prLine}`,
3195
3195
  budgetSplitLine(e.spent, s.input.caps.maxMinutes),
3196
3196
  splitReport(s),
3197
- reissue,
3197
+ continuation,
3198
3198
  ]);
3199
3199
  case "review_pending":
3200
3200
  return join([
3201
3201
  `⏳ Review pending: the coding child shipped ${e.pr.url}${e.headSha !== undefined ? ` (head \`${e.headSha.slice(0, 7)}\`)` : ""} but the remaining pipeline time cannot hold the review round — the work stands, only the review is missing. The next pipeline starts at the review round while the pull request still heads at the child's own last push.`,
3202
3202
  aside(budgetSplitLine(e.spent, s.input.caps.maxMinutes)),
3203
- aside(reissue),
3203
+ aside(continuation),
3204
3204
  ]);
3205
3205
  case "stopped":
3206
3206
  if (!shows(verbosity, "verbose"))
@@ -3216,7 +3216,7 @@ export function renderUnitReport(
3216
3216
  e.postedReview
3217
3217
  ? "ℹ️ A changes-requested review was posted this round before the stop — its findings stand on the PR."
3218
3218
  : undefined,
3219
- s.input.idleDays && s.input.idleDays > 0 ? "Next step: reply in this thread to continue." : reissue,
3219
+ continuation,
3220
3220
  ]);
3221
3221
  case "aborted":
3222
3222
  if (!shows(verbosity, "verbose")) return `⚠️ Aborted after ${rounds}: ${e.reason}${prLine}`;
@@ -3229,7 +3229,7 @@ export function renderUnitReport(
3229
3229
  ),
3230
3230
  e.renewal !== undefined ? `🔁 Not renewed: ${e.renewal.line}.` : undefined,
3231
3231
  `⚠️ Ship aborted after ${rounds}.`,
3232
- reissue,
3232
+ continuation,
3233
3233
  ]);
3234
3234
  case "continued":
3235
3235
  // A segment boundary is the runner continuing — an acknowledgement, verbose
@@ -3239,18 +3239,18 @@ export function renderUnitReport(
3239
3239
  return join([
3240
3240
  writeUpPointer(s, e.round.kind, e.runId),
3241
3241
  s.input.idleDays && s.input.idleDays > 0
3242
- ? `🔁 The unit's budget ran out with the unit unfinished — ${e.line}. Next step: reply in this thread to continue; ${e.renewalsLeft} renewal${e.renewalsLeft === 1 ? "" : "s"} remain${e.spendUsd !== null ? `, $${e.spendUsd.toFixed(2)} spent so far` : ""}.`
3242
+ ? `🔁 The unit's budget ran out with the unit unfinished — ${e.line}. The next reply in this thread continues it; ${e.renewalsLeft} renewal${e.renewalsLeft === 1 ? "" : "s"} remain${e.spendUsd !== null ? `, $${e.spendUsd.toFixed(2)} spent so far` : ""}.`
3243
3243
  : `🔁 The unit's budget ran out with the unit unfinished — ${e.line}. A fresh ${s.input.caps.maxMinutes}-minute budget opens in this thread${e.from !== undefined ? ` from \`${e.from.slice(0, 7)}\`` : ""}, with the last run's write-up as its request; ${e.renewalsLeft} renewal${e.renewalsLeft === 1 ? "" : "s"} remain${e.spendUsd !== null ? `, $${e.spendUsd.toFixed(2)} spent so far` : ""}.`,
3244
3244
  aside(budgetSplitLine(e.spent, s.input.caps.maxMinutes)),
3245
3245
  ]);
3246
3246
  case "transient":
3247
3247
  if (!shows(verbosity, "verbose"))
3248
- return `⚠️ Aborted after ${rounds}: the model provider failed twice; re-issue once it settles.${prLine}`;
3248
+ return `⚠️ Aborted after ${rounds}: this is a bug — the model provider failed twice and no automatic retry remains.${prLine}`;
3249
3249
  return join([
3250
3250
  `⚠️ The coding child of round ${e.round.index} (run ${e.runId}) died on a provider transient — a model-gateway 5xx, a cut stream or a gateway timeout past the harness's retry ladder — with nothing pushed, after the round was already re-run once for the same reason. The task itself was never the problem.`,
3251
3251
  writeUpPointer(s, e.round.kind, s.lastCodingRunId),
3252
- `⚠️ Ship ended after ${rounds}; re-issue once the provider settles.`,
3253
- reissue,
3252
+ `⚠️ This is a bug: ship ended after ${rounds} because its automatic provider retry was spent.`,
3253
+ continuation,
3254
3254
  ]);
3255
3255
  case "no_verdict":
3256
3256
  if (!shows(verbosity, "verbose"))
@@ -3259,7 +3259,7 @@ export function renderUnitReport(
3259
3259
  `⚠️ Review round ${e.round.index} ended without a submitted verdict (budget, refusal, or stop) — ship never converts that into a request for changes, so no findings step ran.`,
3260
3260
  writeUpPointer(s, e.round.kind, s.reviewRunByRound[e.round.index]),
3261
3261
  `⚠️ Ship aborted after ${rounds}.`,
3262
- reissue,
3262
+ continuation,
3263
3263
  ]);
3264
3264
  case "interrupted":
3265
3265
  return join([shipInterruptedNote(prUrl, e.cause, (s.input.idleDays ?? 0) > 0), checkpointLine(e.checkpoint)]);
@@ -3268,7 +3268,7 @@ export function renderUnitReport(
3268
3268
  case "refused":
3269
3269
  return join([
3270
3270
  `🚫 The ${presetOf(e.round.kind)} child of round ${e.round.index} was refused by the authorize stage (${e.refusal})${e.message ? `: ${e.message}` : ""} — every child is authorized as the requesting user, so the pipeline ends here.`,
3271
- aside(reissue),
3271
+ aside(continuation),
3272
3272
  ]);
3273
3273
  case "idle":
3274
3274
  // The old kind's sentence at this copy's level — the report is unchanged
@@ -160,7 +160,9 @@ export function renderRenewal(
160
160
  grant: Grant,
161
161
  options: { idle?: boolean; senders?: readonly string[] } = {},
162
162
  ): string {
163
- const recourse = options.idle ? "reply in this thread to continue" : "re-issue the request to try again";
163
+ const recourse = options.idle
164
+ ? "the next reply in this thread continues the unit"
165
+ : "the next reply in this thread continues the original task";
164
166
  const senders = options.senders?.length
165
167
  ? `, with ${options.senders.length} message${options.senders.length === 1 ? "" : "s"} from ${options.senders.join(", ")}`
166
168
  : "";
@@ -59,7 +59,7 @@ export function bashBudgetWithinRun(wantedMs: number, remainingMs: number): RunB
59
59
  return {
60
60
  kind: "exhausted",
61
61
  note:
62
- `run budget exhausted — ${secs(remainingMs)}s of wall clock left, inside the ` +
62
+ `the run budget is exhausted — ${secs(remainingMs)}s of wall clock left, inside the ` +
63
63
  `${secs(RUN_DEADLINE_RESERVE_MS)}s write-up reserve, so the command was not run; write up what you have now`,
64
64
  };
65
65
  }
@@ -194,11 +194,11 @@ export function fleetBusyRunEndedLine(run: string | undefined, thread: string, e
194
194
  }
195
195
 
196
196
  /** The message `ExecCapacityError` carries once the wait is spent: names the
197
- * wait and the knob, and tells the reader this is a retry-later condition. */
197
+ * wait and the missing automatic queue as a bug, never delegates a retry. */
198
198
  export function fleetBusyExhaustedMessage(waitedMs: number): string {
199
199
  return (
200
- `sandbox fleet busy — no free per-thread sandbox after waiting ${Math.round(waitedMs / 1000)}s ` +
201
- "(the fleet's max_instances is reached); try again in a few minutes"
200
+ `this is a bug: the sandbox fleet had no free per-thread sandbox after waiting ${Math.round(waitedMs / 1000)}s ` +
201
+ "(the fleet's max_instances is reached), and no automatic queue remained"
202
202
  );
203
203
  }
204
204
 
@@ -273,11 +273,11 @@ export function sandboxStartingExecAnswer(cause: string): {
273
273
  }
274
274
 
275
275
  /** The message `ExecCapacityError` carries when a container did not start
276
- * inside the start budget: the wait, and what to do. */
276
+ * inside the start budget: the wait and the missing automatic recovery. */
277
277
  export function startWaitExhaustedMessage(waitedMs: number): string {
278
278
  return (
279
- `sandbox not ready — the thread's container did not finish starting within ${Math.round(waitedMs / 1000)}s; ` +
280
- "try again in a few minutes"
279
+ `this is a bug: the thread's sandbox did not finish starting within ${Math.round(waitedMs / 1000)}s, ` +
280
+ "and no automatic start wait remained"
281
281
  );
282
282
  }
283
283