wicked-crew 0.8.4 → 0.8.6

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 (135) hide show
  1. package/dist/api/ask-paths.d.ts +17 -0
  2. package/dist/api/ask-paths.d.ts.map +1 -1
  3. package/dist/api/ask-paths.js +13 -0
  4. package/dist/api/ask-paths.js.map +1 -1
  5. package/dist/api/ask-relay.d.ts +2 -0
  6. package/dist/api/ask-relay.d.ts.map +1 -1
  7. package/dist/api/ask-relay.js +35 -0
  8. package/dist/api/ask-relay.js.map +1 -1
  9. package/dist/api/chat-scope.d.ts +4 -4
  10. package/dist/api/chat-scope.d.ts.map +1 -1
  11. package/dist/api/chat-scope.js +1 -1
  12. package/dist/api/chat-turns.d.ts +12 -4
  13. package/dist/api/chat-turns.d.ts.map +1 -1
  14. package/dist/api/chat-turns.js +9 -4
  15. package/dist/api/chat-turns.js.map +1 -1
  16. package/dist/api/delivery-index.d.ts +53 -0
  17. package/dist/api/delivery-index.d.ts.map +1 -1
  18. package/dist/api/delivery-index.js +117 -0
  19. package/dist/api/delivery-index.js.map +1 -1
  20. package/dist/api/mcp.d.ts +4 -4
  21. package/dist/api/onboarding-capture.d.ts +58 -0
  22. package/dist/api/onboarding-capture.d.ts.map +1 -0
  23. package/dist/api/onboarding-capture.js +125 -0
  24. package/dist/api/onboarding-capture.js.map +1 -0
  25. package/dist/api/routes.d.ts +11 -3
  26. package/dist/api/routes.d.ts.map +1 -1
  27. package/dist/api/routes.js +84 -9
  28. package/dist/api/routes.js.map +1 -1
  29. package/dist/api/run-timing-index.d.ts +20 -0
  30. package/dist/api/run-timing-index.d.ts.map +1 -1
  31. package/dist/api/run-timing-index.js +46 -0
  32. package/dist/api/run-timing-index.js.map +1 -1
  33. package/dist/api/server.d.ts.map +1 -1
  34. package/dist/api/server.js +52 -37
  35. package/dist/api/server.js.map +1 -1
  36. package/dist/api/skills.d.ts.map +1 -1
  37. package/dist/api/skills.js +3 -1
  38. package/dist/api/skills.js.map +1 -1
  39. package/dist/api/watch-routes.d.ts +3 -1
  40. package/dist/api/watch-routes.d.ts.map +1 -1
  41. package/dist/api/watch-routes.js +3 -0
  42. package/dist/api/watch-routes.js.map +1 -1
  43. package/dist/core/adapter.d.ts +10 -36
  44. package/dist/core/adapter.d.ts.map +1 -1
  45. package/dist/core/adapter.js +81 -57
  46. package/dist/core/adapter.js.map +1 -1
  47. package/dist/core/deliver-exclusions.d.ts +15 -1
  48. package/dist/core/deliver-exclusions.d.ts.map +1 -1
  49. package/dist/core/deliver-exclusions.js +25 -3
  50. package/dist/core/deliver-exclusions.js.map +1 -1
  51. package/dist/core/deliver-text.d.ts +84 -10
  52. package/dist/core/deliver-text.d.ts.map +1 -1
  53. package/dist/core/deliver-text.js +279 -10
  54. package/dist/core/deliver-text.js.map +1 -1
  55. package/dist/core/deliver.d.ts +45 -0
  56. package/dist/core/deliver.d.ts.map +1 -1
  57. package/dist/core/deliver.js +136 -27
  58. package/dist/core/deliver.js.map +1 -1
  59. package/dist/core/types.d.ts.map +1 -1
  60. package/dist/core/types.js +3 -0
  61. package/dist/core/types.js.map +1 -1
  62. package/dist/interactive/bridge-pool.d.ts +17 -0
  63. package/dist/interactive/bridge-pool.d.ts.map +1 -1
  64. package/dist/interactive/bridge-pool.js +69 -3
  65. package/dist/interactive/bridge-pool.js.map +1 -1
  66. package/dist/interactive/bridge-root.d.ts +7 -0
  67. package/dist/interactive/bridge-root.d.ts.map +1 -1
  68. package/dist/interactive/bridge-root.js +11 -0
  69. package/dist/interactive/bridge-root.js.map +1 -1
  70. package/dist/projects/routes.d.ts +6 -2
  71. package/dist/projects/routes.d.ts.map +1 -1
  72. package/dist/projects/routes.js +6 -0
  73. package/dist/projects/routes.js.map +1 -1
  74. package/dist/projects/settings.d.ts +2 -0
  75. package/dist/projects/settings.d.ts.map +1 -1
  76. package/dist/projects/settings.js +4 -0
  77. package/dist/projects/settings.js.map +1 -1
  78. package/dist/skills/refs.d.ts +6 -0
  79. package/dist/skills/refs.d.ts.map +1 -1
  80. package/dist/skills/refs.js +8 -0
  81. package/dist/skills/refs.js.map +1 -1
  82. package/dist/skills/runtime.d.ts +17 -2
  83. package/dist/skills/runtime.d.ts.map +1 -1
  84. package/dist/skills/runtime.js +30 -1
  85. package/dist/skills/runtime.js.map +1 -1
  86. package/dist/skills/store.d.ts.map +1 -1
  87. package/dist/skills/store.js +9 -1
  88. package/dist/skills/store.js.map +1 -1
  89. package/dist/standing-orders/parse.d.ts +9 -5
  90. package/dist/standing-orders/parse.d.ts.map +1 -1
  91. package/dist/standing-orders/parse.js +85 -37
  92. package/dist/standing-orders/parse.js.map +1 -1
  93. package/dist/standing-orders/routes.d.ts +21 -21
  94. package/dist/standing-orders/store.d.ts +9 -9
  95. package/dist/studio/assets/{index-B-ce66JW.js → index-CUNMURZ4.js} +122 -122
  96. package/dist/studio/assets/index-DPqNRzrT.css +32 -0
  97. package/dist/studio/editors/wicked-page/editor.json +1 -1
  98. package/dist/studio/editors/wicked-page/index.html +1 -1
  99. package/dist/studio/index.html +2 -2
  100. package/dist/studio/testid-inventory.json +141 -3
  101. package/dist/watch/checks/claim-vs-evidence.js +2 -2
  102. package/dist/watch/checks/claim-vs-evidence.js.map +1 -1
  103. package/dist/watch/checks/deliver-audit.js +2 -2
  104. package/dist/watch/checks/deliver-audit.js.map +1 -1
  105. package/dist/watch/checks/discovery.d.ts.map +1 -1
  106. package/dist/watch/checks/discovery.js +5 -3
  107. package/dist/watch/checks/discovery.js.map +1 -1
  108. package/dist/watch/checks/index.d.ts.map +1 -1
  109. package/dist/watch/checks/index.js +5 -0
  110. package/dist/watch/checks/index.js.map +1 -1
  111. package/dist/watch/checks/quiet-after-claim.js +2 -2
  112. package/dist/watch/checks/quiet-after-claim.js.map +1 -1
  113. package/dist/watch/checks/scope-drift.d.ts.map +1 -1
  114. package/dist/watch/checks/scope-drift.js +4 -3
  115. package/dist/watch/checks/scope-drift.js.map +1 -1
  116. package/dist/watch/checks/team-path.d.ts +38 -0
  117. package/dist/watch/checks/team-path.d.ts.map +1 -0
  118. package/dist/watch/checks/team-path.js +220 -0
  119. package/dist/watch/checks/team-path.js.map +1 -0
  120. package/dist/watch/checks/ungated.js +2 -2
  121. package/dist/watch/checks/ungated.js.map +1 -1
  122. package/dist/watch/checks/warned-rule.js +2 -2
  123. package/dist/watch/checks/warned-rule.js.map +1 -1
  124. package/dist/watch/registry.d.ts +18 -2
  125. package/dist/watch/registry.d.ts.map +1 -1
  126. package/dist/watch/registry.js +23 -4
  127. package/dist/watch/registry.js.map +1 -1
  128. package/dist/watch/types.d.ts +8 -2
  129. package/dist/watch/types.d.ts.map +1 -1
  130. package/endpoint-manifest.json +1 -1
  131. package/package.json +3 -3
  132. package/watch/entries/help-unanswered.json +16 -0
  133. package/watch/entries/path-repicked.json +13 -0
  134. package/watch/entries/reviewer-absent.json +13 -0
  135. package/dist/studio/assets/index-TvS52qj5.css +0 -32
@@ -27,13 +27,17 @@
27
27
  * that committed incrementally keeps its own commits untouched — the PR title is composed from the
28
28
  * run either way; only the deliver phase's own commit is guaranteed to match it.
29
29
  */
30
- import type { GateSpec, PhaseDef, SessionView } from './types.js';
30
+ import type { GateSpec, PhaseDef, SessionView, WorkUnit } from './types.js';
31
31
  /**
32
- * The PR title cap — GitHub's own limit (crew#550 P-1: a 72-character cap on the PR title still
33
- * severed the intent inside a quoted phrase). The COMMIT subject keeps git's conventional width,
32
+ * The PR title cap (crew#860, S17b F15 / S17a F20). GitHub ACCEPTS 256 characters, and under that
33
+ * cap wicked-studio#586 opened as the intent's first 244 — a whole paragraph, cut mid-sentence,
34
+ * wider than the PR list renders. A title is a headline: the intent's FIRST SENTENCE, cut at a word
35
+ * boundary inside 100 characters; the full intent is the body's first section and the run id is
36
+ * named there. (crew#550 P-1 — a 72-character cap severed a quoted phrase — is still honoured: the
37
+ * cut is depth-aware, see {@link boundedTitle}.) The COMMIT subject keeps git's conventional width,
34
38
  * {@link COMMIT_SUBJECT_MAX}; a longer title then opens the commit body in full.
35
39
  */
36
- export declare const DELIVER_TITLE_MAX = 256;
40
+ export declare const DELIVER_TITLE_MAX = 100;
37
41
  /** The commit subject cap — git's conventional subject width (crew#550). */
38
42
  export declare const COMMIT_SUBJECT_MAX = 72;
39
43
  /** One phase of the run, with as much as is known about it at composition time. */
@@ -69,6 +73,27 @@ export interface DeliverVerdictFact {
69
73
  verdict: string;
70
74
  reason: string | null;
71
75
  }
76
+ /**
77
+ * What a unit's seat REPORTED in its final output (crew#860): the governed-worker output contract's
78
+ * "What you did / Commands run (with exit codes) / Counts / Findings" block — for a creator, what
79
+ * changed and the recipe it ran; for an evaluator, its findings by severity. Read from the unit's
80
+ * captured output by {@link extractUnitReport}; `report` is bounded markdown, never empty.
81
+ */
82
+ export interface DeliverReportFact {
83
+ phase: string;
84
+ seat: string | null;
85
+ role: 'creator' | 'evaluator';
86
+ report: string;
87
+ /** The commands the seat says it ran, with the exit code it pasted beside each
88
+ * ({@link extractRecipeExits}) — a creator's recipe, read for the done-when evidence table.
89
+ * Absent/empty = the output named no command with an exit code. */
90
+ exits?: DeliverRecipeExit[];
91
+ }
92
+ /** One command a seat reported running, with the exit code it pasted (crew#860). */
93
+ export interface DeliverRecipeExit {
94
+ command: string;
95
+ exit: number;
96
+ }
72
97
  /** Everything the composer needs. Built by {@link factsFromRun} or {@link factsFromWorkflow}. */
73
98
  export interface DeliverTextFacts {
74
99
  runId: string;
@@ -102,6 +127,10 @@ export interface DeliverTextFacts {
102
127
  * evaluator units' output by {@link extractFollowUps}). `[]` = read, none flagged; absent = not
103
128
  * read (a definition-time composition), and the section is left out. */
104
129
  followUps?: string[];
130
+ /** crew#860: the creator and evaluator seats' final reports, read from the unit outputs
131
+ * ({@link reportsFromOutputs}). `[]` = read, nothing reportable; absent = the outputs could not
132
+ * be read (a definition-time composition, or an adapter without transcripts) — said in the body. */
133
+ reports?: DeliverReportFact[];
105
134
  }
106
135
  export interface DeliverText {
107
136
  /** The PR title: ≤ {@link DELIVER_TITLE_MAX} characters, one line, never cut mid-word. */
@@ -122,12 +151,21 @@ export interface IssueRefs {
122
151
  */
123
152
  export declare function conventionalPrefix(workflowId: string | null | undefined): string;
124
153
  /**
125
- * The PR title / commit subject: the intent's first line, whole when it fits, otherwise cut at the
126
- * last word boundary that leaves room for a single `…` — so the result is ≤ 256 characters and never
127
- * ends mid-word (the F-3R2-014 headline `…scenario CLN-2) aga`). Dangling punctuation before the
128
- * ellipsis is dropped. A blank intent names the run instead — through the SAME bounded cut, so a
129
- * long caller-supplied session id (the CLI passes `--session` through) is never cut mid-id either
130
- * (Copilot on #525): the body names the run id in full.
154
+ * The first SENTENCE of a headline line (crew#860): the text up to the first `.`, `!` or `?` that
155
+ * is followed by whitespace or the end, at quote/bracket depth 0 (a period inside `(…)` or `"…"`
156
+ * does not end the sentence), and not the dot of an abbreviation. A closing `.` is dropped (a
157
+ * headline carries no full stop); `!` and `?` stay. A dot glued to the next word (`0.8.4`,
158
+ * `deliver.ts`, `wicked-studio#586.`) never splits. No terminator ⇒ the whole line.
159
+ */
160
+ export declare function firstSentence(line: string): string;
161
+ /**
162
+ * The PR title / commit subject: the FIRST SENTENCE of the intent's first prose line (crew#860),
163
+ * whole when it fits, otherwise cut at the last word boundary that leaves room for a single `…` —
164
+ * so the result is ≤ {@link DELIVER_TITLE_MAX} characters and never ends mid-word (the F-3R2-014
165
+ * headline `…scenario CLN-2) aga`). Dangling punctuation before the ellipsis is dropped. A blank
166
+ * intent names the run instead — through the SAME bounded cut, so a long caller-supplied session id
167
+ * (the CLI passes `--session` through) is never cut mid-id either (Copilot on #525): the body names
168
+ * the run id in full.
131
169
  */
132
170
  export declare function deliverTitle(intent: string, runId: string, workflowId?: string | null): string;
133
171
  /**
@@ -182,6 +220,8 @@ export declare function composeEmbeddedDeliverText(f: DeliverTextFacts): Deliver
182
220
  * references of the FULL intent while `f.intent` is the bounded copy ({@link composeEmbeddedDeliverText}).
183
221
  */
184
222
  export declare function composeDeliverText(f: DeliverTextFacts, links?: IssueRefs): DeliverText;
223
+ /** At most this many pasted recipe commands per phase in the done-when table. */
224
+ export declare const RECIPE_EXITS_MAX = 12;
185
225
  /** The one shape both carriers speak: title, blank line, body, trailing newline. */
186
226
  export declare function framedDeliverText(text: DeliverText): string;
187
227
  /**
@@ -215,6 +255,8 @@ export declare function factsFromRun(view: SessionView, runUrl: string | null, r
215
255
  } | null;
216
256
  /** crew#550: the evaluator follow-ups, when the caller read the evaluator units' output. */
217
257
  followUps?: string[];
258
+ /** crew#860: the seats' final reports, when the caller read the unit outputs. */
259
+ reports?: DeliverReportFact[];
218
260
  }): DeliverTextFacts;
219
261
  /** What is known at LAUNCH, from the workflow definition — the script's embedded fallback. */
220
262
  export declare function factsFromWorkflow(input: {
@@ -268,4 +310,36 @@ export declare const FOLLOW_UP_MAX_CHARS = 300;
268
310
  * de-duplicated, capped at {@link FOLLOW_UPS_MAX}.
269
311
  */
270
312
  export declare function extractFollowUps(text: string): string[];
313
+ /** How much of one seat's report rides a PR body (characters); the run record has the rest. */
314
+ export declare const REPORT_MAX_CHARS = 4000;
315
+ /** At most this many reports ride a PR body (one per creator / evaluator unit, by ord). */
316
+ export declare const REPORTS_MAX = 6;
317
+ /**
318
+ * A seat's final REPORT from its captured output (crew#860): the text from the first report heading
319
+ * ({@link REPORT_HEADING}) to the end — the output contract puts the report LAST, after the work —
320
+ * or, when no heading is found, the tail of the output (its last 40 lines), said so. `VERDICT:`
321
+ * lines are removed (the composer states the recorded verdict beside the phase). Headings are
322
+ * demoted below the body's own `##` / `###` so the report cannot restyle the PR; an unclosed code
323
+ * fence is closed so it cannot swallow the footer; control characters are dropped; the result is
324
+ * cut at a line boundary inside {@link REPORT_MAX_CHARS} and the cut is disclosed. `null` when the
325
+ * output carries nothing reportable (empty, or whitespace only).
326
+ */
327
+ export declare function extractUnitReport(text: string | null | undefined): string | null;
328
+ /**
329
+ * The seats' reports for a run (crew#860), one per creator / evaluator unit in ord order, read
330
+ * through `readOutput` (the adapter's `workOutput`, injected so the composer stays pure). A unit
331
+ * whose output cannot be read or carries nothing reportable contributes nothing; the result is
332
+ * `[]` when nothing was reportable, never `undefined` — the caller says "could not read" only when
333
+ * it has no reader at all.
334
+ */
335
+ export declare function reportsFromOutputs(view: SessionView, readOutput: (unit: WorkUnit) => Promise<string | null>): Promise<DeliverReportFact[]>;
336
+ /**
337
+ * The commands a seat's output says it ran, with the exit code it pasted beside each (crew#860) —
338
+ * the "Commands run (with exit codes)" half of the governed-worker output contract, in the shapes
339
+ * seats write it: `` - `npm run lint` → exit 0 ``, `` | `npx vitest run` | 0 | ``, `typecheck: exit 0`.
340
+ * A line counts only when it names BOTH a command (a code span, else the list item's text before
341
+ * the exit word) and an exit code; deduplicated by command (the LAST report wins — a re-run
342
+ * supersedes), at most {@link RECIPE_EXITS_MAX}. Never throws; `[]` when nothing parses.
343
+ */
344
+ export declare function extractRecipeExits(text: string | null | undefined): DeliverRecipeExit[];
271
345
  //# sourceMappingURL=deliver-text.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"deliver-text.d.ts","sourceRoot":"","sources":["../../src/core/deliver-text.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"deliver-text.d.ts","sourceRoot":"","sources":["../../src/core/deliver-text.ts"],"names":[],"mappings":"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"}
@@ -29,11 +29,15 @@
29
29
  */
30
30
  import { stripLinkedIssues } from './linked-issues.js';
31
31
  /**
32
- * The PR title cap — GitHub's own limit (crew#550 P-1: a 72-character cap on the PR title still
33
- * severed the intent inside a quoted phrase). The COMMIT subject keeps git's conventional width,
32
+ * The PR title cap (crew#860, S17b F15 / S17a F20). GitHub ACCEPTS 256 characters, and under that
33
+ * cap wicked-studio#586 opened as the intent's first 244 — a whole paragraph, cut mid-sentence,
34
+ * wider than the PR list renders. A title is a headline: the intent's FIRST SENTENCE, cut at a word
35
+ * boundary inside 100 characters; the full intent is the body's first section and the run id is
36
+ * named there. (crew#550 P-1 — a 72-character cap severed a quoted phrase — is still honoured: the
37
+ * cut is depth-aware, see {@link boundedTitle}.) The COMMIT subject keeps git's conventional width,
34
38
  * {@link COMMIT_SUBJECT_MAX}; a longer title then opens the commit body in full.
35
39
  */
36
- export const DELIVER_TITLE_MAX = 256;
40
+ export const DELIVER_TITLE_MAX = 100;
37
41
  /** The commit subject cap — git's conventional subject width (crew#550). */
38
42
  export const COMMIT_SUBJECT_MAX = 72;
39
43
  // ── the title ──────────────────────────────────────────────────────────────────────────────────
@@ -104,16 +108,64 @@ const HAS_CONVENTIONAL_PREFIX = /^(?:feat|fix|refactor|chore|docs|test|build|ci|
104
108
  export function conventionalPrefix(workflowId) {
105
109
  return CONVENTIONAL_TYPE_BY_WORKFLOW[workflowId ?? ''] ?? 'chore';
106
110
  }
111
+ /** A `.` that ends one of these is an abbreviation, not a sentence (`e.g. the gate`, `vs. main`). */
112
+ const ABBREVIATION_BEFORE_DOT = /(?:^|[\s(])(?:e\.g|i\.e|etc|vs|cf|approx|incl|resp|fig|no|vol|ca)$/i;
107
113
  /**
108
- * The PR title / commit subject: the intent's first line, whole when it fits, otherwise cut at the
109
- * last word boundary that leaves room for a single `…` — so the result is ≤ 256 characters and never
110
- * ends mid-word (the F-3R2-014 headline `…scenario CLN-2) aga`). Dangling punctuation before the
111
- * ellipsis is dropped. A blank intent names the run instead — through the SAME bounded cut, so a
112
- * long caller-supplied session id (the CLI passes `--session` through) is never cut mid-id either
113
- * (Copilot on #525): the body names the run id in full.
114
+ * The first SENTENCE of a headline line (crew#860): the text up to the first `.`, `!` or `?` that
115
+ * is followed by whitespace or the end, at quote/bracket depth 0 (a period inside `(…)` or `"…"`
116
+ * does not end the sentence), and not the dot of an abbreviation. A closing `.` is dropped (a
117
+ * headline carries no full stop); `!` and `?` stay. A dot glued to the next word (`0.8.4`,
118
+ * `deliver.ts`, `wicked-studio#586.`) never splits. No terminator ⇒ the whole line.
119
+ */
120
+ export function firstSentence(line) {
121
+ const stack = [];
122
+ for (let i = 0; i < line.length; i += 1) {
123
+ const c = line[i];
124
+ const top = stack[stack.length - 1];
125
+ if (top !== undefined && c === top) {
126
+ stack.pop();
127
+ continue;
128
+ }
129
+ if (c === "'") {
130
+ if (i === 0 || line[i - 1] === ' ')
131
+ stack.push("'");
132
+ continue;
133
+ }
134
+ const closer = OPENERS[c];
135
+ if (closer !== undefined) {
136
+ stack.push(closer);
137
+ continue;
138
+ }
139
+ if (stack.length > 0 || (c !== '.' && c !== '!' && c !== '?'))
140
+ continue;
141
+ // A run of terminators (`?!`, `...`) ends together; the sentence ends only when whitespace or
142
+ // the end follows the run.
143
+ let j = i;
144
+ while (j + 1 < line.length && /[.!?]/.test(line[j + 1]))
145
+ j += 1;
146
+ if (j + 1 < line.length && !/\s/.test(line[j + 1]))
147
+ continue;
148
+ const head = line.slice(0, i);
149
+ if (c === '.' && j === i && ABBREVIATION_BEFORE_DOT.test(head))
150
+ continue;
151
+ const sentence = (c === '.' && j === i ? head : line.slice(0, j + 1)).trim();
152
+ if (sentence === '')
153
+ continue;
154
+ return sentence;
155
+ }
156
+ return line;
157
+ }
158
+ /**
159
+ * The PR title / commit subject: the FIRST SENTENCE of the intent's first prose line (crew#860),
160
+ * whole when it fits, otherwise cut at the last word boundary that leaves room for a single `…` —
161
+ * so the result is ≤ {@link DELIVER_TITLE_MAX} characters and never ends mid-word (the F-3R2-014
162
+ * headline `…scenario CLN-2) aga`). Dangling punctuation before the ellipsis is dropped. A blank
163
+ * intent names the run instead — through the SAME bounded cut, so a long caller-supplied session id
164
+ * (the CLI passes `--session` through) is never cut mid-id either (Copilot on #525): the body names
165
+ * the run id in full.
114
166
  */
115
167
  export function deliverTitle(intent, runId, workflowId) {
116
- const line = firstLine(intent);
168
+ const line = firstSentence(firstLine(intent));
117
169
  // The run id is caller-supplied too (`LaunchSchema` only requires it non-empty): a newline in it
118
170
  // must not turn the title into two lines and break the framing (Copilot on #525).
119
171
  if (line === '')
@@ -402,6 +454,40 @@ export function composeDeliverText(f, links = issueRefs(f.intent, f.repoRef)) {
402
454
  }
403
455
  }
404
456
  out.push('');
457
+ // crew#860 (S17b F15): the DONE-WHEN EVIDENCE, one row per phase — the deterministic floor's
458
+ // results for that phase (`typecheck 0 · lint 0`), the evaluator verdict recorded for it, and the
459
+ // exit codes the creator PASTED for its own recipe — so a reviewer reads "what proved this" in one
460
+ // table instead of reconstructing it from three sections and the run record.
461
+ if (f.source === 'run') {
462
+ out.push('## Done-when evidence', '');
463
+ out.push(...doneWhenTable(f), '');
464
+ }
465
+ // crew#860 (S17b F15): the FACTS the intent's own done-when asks the PR to state — the creator's
466
+ // "what I did / commands run with exit codes / done-when table" and each evaluator's findings —
467
+ // already exist in the unit outputs; the PR used to carry only "passed its gate" and the operator
468
+ // appended the table by hand. They ride here, bounded, under the seat that wrote them.
469
+ out.push('## Creator report', '');
470
+ const creators = (f.reports ?? []).filter((r) => r.role === 'creator');
471
+ if (f.source === 'workflow') {
472
+ out.push('_Not available at composition time — the run record has it._');
473
+ }
474
+ else if (f.reports === undefined) {
475
+ out.push("_The creator's output could not be read when this PR opened — the run record has it._");
476
+ }
477
+ else if (creators.length === 0) {
478
+ out.push('_No creator report was recorded (no creator phase, or its output carried no report)._');
479
+ }
480
+ else {
481
+ for (const r of creators)
482
+ out.push(`### ${code(r.phase)} (${cell(r.seat ?? 'seat unknown')})`, '', r.report, '');
483
+ }
484
+ out.push('');
485
+ const evaluations = (f.reports ?? []).filter((r) => r.role === 'evaluator');
486
+ if (evaluations.length > 0) {
487
+ out.push('## Evaluator findings', '');
488
+ for (const r of evaluations)
489
+ out.push(`### ${code(r.phase)} (${cell(r.seat ?? 'seat unknown')})`, '', r.report, '');
490
+ }
405
491
  // crew#550 P-7: what the evaluator said should happen NEXT lands on the PR, not only in its log.
406
492
  if (f.followUps !== undefined) {
407
493
  out.push('## Follow-ups', '');
@@ -418,6 +504,41 @@ export function composeDeliverText(f, links = issueRefs(f.intent, f.repoRef)) {
418
504
  `Delivered-By: wicked-crew run ${oneLine(f.runId)}`, ...(f.trailers ?? []));
419
505
  return { title, body: out.join('\n') };
420
506
  }
507
+ /** At most this many pasted recipe commands per phase in the done-when table. */
508
+ export const RECIPE_EXITS_MAX = 12;
509
+ /**
510
+ * The done-when evidence table (crew#860): one row per phase that has ANY evidence — a floor
511
+ * result, a recorded verdict, or pasted recipe exit codes. Every cell says what was recorded, or
512
+ * `—` when nothing was; a phase with no evidence at all is left out (the Phases table lists it).
513
+ */
514
+ function doneWhenTable(f) {
515
+ const rows = [];
516
+ const order = [];
517
+ for (const p of f.phases)
518
+ if (!order.includes(p.id))
519
+ order.push(p.id);
520
+ for (const x of [...(f.checks ?? []).map((c) => c.phase), ...f.verdicts.map((v) => v.phase), ...(f.reports ?? []).map((r) => r.phase)]) {
521
+ if (!order.includes(x))
522
+ order.push(x);
523
+ }
524
+ for (const id of order) {
525
+ const phase = f.phases.find((p) => p.id === id);
526
+ const checks = (f.checks ?? []).filter((c) => c.phase === id);
527
+ const floor = checks.map((c) => `${c.name} ${exitLabel(c)}${c.exitCode !== null && c.exitCode !== 0 && c.classification !== null ? ` (${c.classification})` : ''}`).join(' · ');
528
+ const verdict = f.verdicts.find((v) => v.phase === id);
529
+ const verdictCell = verdict === undefined ? '' : `${verdict.verdict}${verdict.reason !== null && verdict.reason.trim() !== '' ? ` — ${verdict.reason}` : ''}`;
530
+ const exits = (f.reports ?? []).filter((r) => r.phase === id).flatMap((r) => r.exits ?? []).slice(0, RECIPE_EXITS_MAX);
531
+ const recipe = exits.map((e) => `${code(e.command)} → ${e.exit}`).join('<br>');
532
+ if (floor === '' && verdictCell === '' && recipe === '')
533
+ continue;
534
+ const seat = phase?.seat ?? f.reports?.find((r) => r.phase === id)?.seat ?? verdict?.seat ?? null;
535
+ rows.push(`| ${code(id)} | ${cell(seat)} | ${floor === '' ? '—' : cell(floor, 400)} | ${verdictCell === '' ? '—' : cell(verdictCell, 300)} | ${recipe === '' ? '—' : recipe} |`);
536
+ }
537
+ if (rows.length === 0) {
538
+ return ['_No floor results, evaluator verdicts or pasted recipe exit codes were recorded for this run._'];
539
+ }
540
+ return ['| phase | seat | floor (exit codes) | evaluator verdict | recipe the seat ran (pasted exit codes) |', '|---|---|---|---|---|', ...rows];
541
+ }
421
542
  /** The classification cell: the engine's word for a non-zero exit, `—` for a pass or none. */
422
543
  function classificationLabel(c) {
423
544
  if (c.classification !== null && c.classification.trim() !== '')
@@ -582,6 +703,7 @@ export function factsFromRun(view, runUrl, resolved = {}) {
582
703
  revisesPr: resolved.revisesPr ?? null,
583
704
  trailers: configuredTrailers(),
584
705
  ...(resolved.followUps !== undefined ? { followUps: resolved.followUps } : {}),
706
+ ...(resolved.reports !== undefined ? { reports: resolved.reports } : {}),
585
707
  };
586
708
  }
587
709
  /** What is known at LAUNCH, from the workflow definition — the script's embedded fallback. */
@@ -728,4 +850,151 @@ export function extractFollowUps(text) {
728
850
  }
729
851
  return out;
730
852
  }
853
+ // ── seat reports (crew#860) ────────────────────────────────────────────────────────────────────
854
+ /** How much of one seat's report rides a PR body (characters); the run record has the rest. */
855
+ export const REPORT_MAX_CHARS = 4_000;
856
+ /** At most this many reports ride a PR body (one per creator / evaluator unit, by ord). */
857
+ export const REPORTS_MAX = 6;
858
+ /**
859
+ * The heading that OPENS a seat's final report — the governed-worker output contract's sections
860
+ * ("What you did", "Commands run", "Counts", "Findings", "Open questions") and the shapes seats
861
+ * actually write them in (`## What I did`, `**Commands run:**`, `Done-when`, `Evidence`, `Summary`).
862
+ */
863
+ const REPORT_HEADING = /^\s*(?:#{1,6}\s*|\*\*|\d+[.)]\s*|[-*]\s*)*\s*(?:what (?:you|i|we) did|what (?:was|has been) done|what changed|commands? run|commands? executed|counts?|findings?|done[- ]when|evidence|open questions?|summary|report|verification|results?)\b/i;
864
+ /** The evaluator's contract line — stated separately by the composer, never repeated in the report. */
865
+ const VERDICT_LINE = /^\s*verdict:\s*(pass|fail)\s*$/i;
866
+ /**
867
+ * A seat's final REPORT from its captured output (crew#860): the text from the first report heading
868
+ * ({@link REPORT_HEADING}) to the end — the output contract puts the report LAST, after the work —
869
+ * or, when no heading is found, the tail of the output (its last 40 lines), said so. `VERDICT:`
870
+ * lines are removed (the composer states the recorded verdict beside the phase). Headings are
871
+ * demoted below the body's own `##` / `###` so the report cannot restyle the PR; an unclosed code
872
+ * fence is closed so it cannot swallow the footer; control characters are dropped; the result is
873
+ * cut at a line boundary inside {@link REPORT_MAX_CHARS} and the cut is disclosed. `null` when the
874
+ * output carries nothing reportable (empty, or whitespace only).
875
+ */
876
+ export function extractUnitReport(text) {
877
+ if (text === null || text === undefined)
878
+ return null;
879
+ const lines = text
880
+ .replace(/\r\n?/g, '\n')
881
+ .replace(CONTROL_CHARS, '')
882
+ .split('\n')
883
+ .filter((l) => !VERDICT_LINE.test(l));
884
+ let start = lines.findIndex((l) => REPORT_HEADING.test(l));
885
+ let note = null;
886
+ if (start < 0) {
887
+ start = Math.max(0, lines.length - 40);
888
+ note = '_(no report headings found in the output — its last lines follow)_';
889
+ }
890
+ let block = lines.slice(start).join('\n').trim();
891
+ if (block === '')
892
+ return null;
893
+ // Below the body's `### <phase>` heading: `#`/`##`/`###` → `####`.
894
+ block = block.replace(/^#{1,3}(?=\s)/gm, '####');
895
+ let cut = false;
896
+ if (block.length > REPORT_MAX_CHARS) {
897
+ const head = block.slice(0, REPORT_MAX_CHARS);
898
+ const nl = head.lastIndexOf('\n');
899
+ block = (nl > REPORT_MAX_CHARS / 2 ? head.slice(0, nl) : head).trimEnd();
900
+ cut = true;
901
+ }
902
+ // An odd number of fences would leave the rest of the PR body inside a code block.
903
+ if ((block.match(/^\s*```/gm) ?? []).length % 2 === 1)
904
+ block += '\n```';
905
+ const parts = [block];
906
+ if (cut)
907
+ parts.push('', '_(cut here — the full report is on the run record)_');
908
+ if (note !== null)
909
+ parts.unshift(note, '');
910
+ return parts.join('\n');
911
+ }
912
+ /**
913
+ * The seats' reports for a run (crew#860), one per creator / evaluator unit in ord order, read
914
+ * through `readOutput` (the adapter's `workOutput`, injected so the composer stays pure). A unit
915
+ * whose output cannot be read or carries nothing reportable contributes nothing; the result is
916
+ * `[]` when nothing was reportable, never `undefined` — the caller says "could not read" only when
917
+ * it has no reader at all.
918
+ */
919
+ export async function reportsFromOutputs(view, readOutput) {
920
+ const units = [...view.units]
921
+ .filter((u) => u.role === 'creator' || u.role === 'evaluator')
922
+ .sort((a, b) => a.ord - b.ord);
923
+ const out = [];
924
+ for (const u of units) {
925
+ if (out.length >= REPORTS_MAX)
926
+ break;
927
+ let text = null;
928
+ try {
929
+ text = await readOutput(u);
930
+ }
931
+ catch {
932
+ text = null;
933
+ }
934
+ const report = extractUnitReport(text);
935
+ if (report === null)
936
+ continue;
937
+ const exits = extractRecipeExits(text);
938
+ out.push({
939
+ phase: phaseIdOf(u),
940
+ seat: seatOf(u),
941
+ role: u.role,
942
+ report,
943
+ ...(exits.length > 0 ? { exits } : {}),
944
+ });
945
+ }
946
+ return out;
947
+ }
948
+ /** `exit 0`, `exit code: 1`, `exit=2`, `exited 0`, `(exit 0)` — the exit code a seat pasted. */
949
+ const EXIT_WORD = /\bexit(?:ed)?(?:\s*code)?\s*[:=]?\s*(-?\d{1,3})\b/i;
950
+ /** `` `cmd` → 0 `` / `` `cmd` -> 0 `` / `` `cmd`: 0 `` — the arrow shapes after a code span. */
951
+ const ARROW_EXIT = /^\s*(?:→|->|=>|:|—|–)\s*(-?\d{1,3})\b/;
952
+ /** `` | `cmd` | 0 | `` — a table row whose next cell is a bare exit code. */
953
+ const TABLE_EXIT = /^\s*\|\s*(-?\d{1,3})\s*\|/;
954
+ const CODE_SPAN = /`([^`\n]{2,200})`/;
955
+ /**
956
+ * The commands a seat's output says it ran, with the exit code it pasted beside each (crew#860) —
957
+ * the "Commands run (with exit codes)" half of the governed-worker output contract, in the shapes
958
+ * seats write it: `` - `npm run lint` → exit 0 ``, `` | `npx vitest run` | 0 | ``, `typecheck: exit 0`.
959
+ * A line counts only when it names BOTH a command (a code span, else the list item's text before
960
+ * the exit word) and an exit code; deduplicated by command (the LAST report wins — a re-run
961
+ * supersedes), at most {@link RECIPE_EXITS_MAX}. Never throws; `[]` when nothing parses.
962
+ */
963
+ export function extractRecipeExits(text) {
964
+ if (text === null || text === undefined)
965
+ return [];
966
+ const found = new Map();
967
+ for (const raw of text.replace(/\r\n?/g, '\n').replace(CONTROL_CHARS, '').split('\n')) {
968
+ const span = CODE_SPAN.exec(raw);
969
+ let command = null;
970
+ let exit = null;
971
+ if (span !== null) {
972
+ command = span[1].trim();
973
+ const after = raw.slice(span.index + span[0].length);
974
+ const m = EXIT_WORD.exec(after) ?? ARROW_EXIT.exec(after) ?? TABLE_EXIT.exec(after);
975
+ if (m !== null)
976
+ exit = Number(m[1]);
977
+ }
978
+ else {
979
+ const m = EXIT_WORD.exec(raw);
980
+ if (m !== null) {
981
+ const head = raw
982
+ .slice(0, m.index)
983
+ .replace(/^\s*(?:[-*•]|\d+[.)])\s+/, '')
984
+ .replace(/[\s(:—–,-]+$/, '')
985
+ .trim();
986
+ if (head !== '' && head.length <= 120 && !/^(?:all|every|each)\b/i.test(head)) {
987
+ command = head;
988
+ exit = Number(m[1]);
989
+ }
990
+ }
991
+ }
992
+ if (command === null || exit === null || command === '' || !Number.isFinite(exit))
993
+ continue;
994
+ const key = oneLine(command).slice(0, 120);
995
+ found.delete(key);
996
+ found.set(key, exit);
997
+ }
998
+ return [...found.entries()].slice(-RECIPE_EXITS_MAX).map(([command, exit]) => ({ command, exit }));
999
+ }
731
1000
  //# sourceMappingURL=deliver-text.js.map