wicked-crew 0.16.4 → 0.16.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 (158) hide show
  1. package/dist/api/ask-paths.d.ts +5 -2
  2. package/dist/api/ask-paths.d.ts.map +1 -1
  3. package/dist/api/ask-paths.js +6 -3
  4. package/dist/api/ask-paths.js.map +1 -1
  5. package/dist/api/chat-scope.d.ts +8 -3
  6. package/dist/api/chat-scope.d.ts.map +1 -1
  7. package/dist/api/chat-scope.js +17 -5
  8. package/dist/api/chat-scope.js.map +1 -1
  9. package/dist/api/chat-workflow.d.ts +60 -0
  10. package/dist/api/chat-workflow.d.ts.map +1 -0
  11. package/dist/api/chat-workflow.js +80 -0
  12. package/dist/api/chat-workflow.js.map +1 -0
  13. package/dist/api/endpoint-manifest-live.d.ts.map +1 -1
  14. package/dist/api/endpoint-manifest-live.js +2 -0
  15. package/dist/api/endpoint-manifest-live.js.map +1 -1
  16. package/dist/api/needs.d.ts +116 -0
  17. package/dist/api/needs.d.ts.map +1 -0
  18. package/dist/api/needs.js +618 -0
  19. package/dist/api/needs.js.map +1 -0
  20. package/dist/api/routes.d.ts +23 -1
  21. package/dist/api/routes.d.ts.map +1 -1
  22. package/dist/api/routes.js +297 -25
  23. package/dist/api/routes.js.map +1 -1
  24. package/dist/api/run-liveness.d.ts +8 -0
  25. package/dist/api/run-liveness.d.ts.map +1 -1
  26. package/dist/api/run-liveness.js +39 -7
  27. package/dist/api/run-liveness.js.map +1 -1
  28. package/dist/api/run-timing-index.d.ts +6 -0
  29. package/dist/api/run-timing-index.d.ts.map +1 -1
  30. package/dist/api/run-timing-index.js +18 -0
  31. package/dist/api/run-timing-index.js.map +1 -1
  32. package/dist/api/server.d.ts +8 -0
  33. package/dist/api/server.d.ts.map +1 -1
  34. package/dist/api/server.js +149 -3
  35. package/dist/api/server.js.map +1 -1
  36. package/dist/api/system-snapshot.d.ts +7 -15
  37. package/dist/api/system-snapshot.d.ts.map +1 -1
  38. package/dist/api/system-snapshot.js +7 -115
  39. package/dist/api/system-snapshot.js.map +1 -1
  40. package/dist/core/run-identity.d.ts.map +1 -1
  41. package/dist/core/run-identity.js +3 -0
  42. package/dist/core/run-identity.js.map +1 -1
  43. package/dist/evals/capture.d.ts +93 -0
  44. package/dist/evals/capture.d.ts.map +1 -0
  45. package/dist/evals/capture.js +419 -0
  46. package/dist/evals/capture.js.map +1 -0
  47. package/dist/evals/replay.d.ts +33 -0
  48. package/dist/evals/replay.d.ts.map +1 -0
  49. package/dist/evals/replay.js +87 -0
  50. package/dist/evals/replay.js.map +1 -0
  51. package/dist/evals/routes.d.ts +48 -0
  52. package/dist/evals/routes.d.ts.map +1 -0
  53. package/dist/evals/routes.js +121 -0
  54. package/dist/evals/routes.js.map +1 -0
  55. package/dist/evals/run-cli.d.ts +31 -0
  56. package/dist/evals/run-cli.d.ts.map +1 -0
  57. package/dist/evals/run-cli.js +128 -0
  58. package/dist/evals/run-cli.js.map +1 -0
  59. package/dist/evals/runner.d.ts +34 -0
  60. package/dist/evals/runner.d.ts.map +1 -0
  61. package/dist/evals/runner.js +181 -0
  62. package/dist/evals/runner.js.map +1 -0
  63. package/dist/evals/score.d.ts +31 -0
  64. package/dist/evals/score.d.ts.map +1 -0
  65. package/dist/evals/score.js +191 -0
  66. package/dist/evals/score.js.map +1 -0
  67. package/dist/evals/seed-needs-you.d.ts +17 -0
  68. package/dist/evals/seed-needs-you.d.ts.map +1 -0
  69. package/dist/evals/seed-needs-you.js +57 -0
  70. package/dist/evals/seed-needs-you.js.map +1 -0
  71. package/dist/evals/seed-rig.d.ts +9 -0
  72. package/dist/evals/seed-rig.d.ts.map +1 -0
  73. package/dist/evals/seed-rig.js +156 -0
  74. package/dist/evals/seed-rig.js.map +1 -0
  75. package/dist/evals/stats.d.ts +31 -0
  76. package/dist/evals/stats.d.ts.map +1 -0
  77. package/dist/evals/stats.js +59 -0
  78. package/dist/evals/stats.js.map +1 -0
  79. package/dist/evals/steering.d.ts +9 -0
  80. package/dist/evals/steering.d.ts.map +1 -0
  81. package/dist/evals/steering.js +25 -0
  82. package/dist/evals/steering.js.map +1 -0
  83. package/dist/evals/store.d.ts +26 -0
  84. package/dist/evals/store.d.ts.map +1 -0
  85. package/dist/evals/store.js +96 -0
  86. package/dist/evals/store.js.map +1 -0
  87. package/dist/evals/synthetic.d.ts +17 -0
  88. package/dist/evals/synthetic.d.ts.map +1 -0
  89. package/dist/evals/synthetic.js +71 -0
  90. package/dist/evals/synthetic.js.map +1 -0
  91. package/dist/evals/types.d.ts +29 -0
  92. package/dist/evals/types.d.ts.map +1 -0
  93. package/dist/evals/types.js +25 -0
  94. package/dist/evals/types.js.map +1 -0
  95. package/dist/evals/workflow.d.ts +33 -0
  96. package/dist/evals/workflow.d.ts.map +1 -0
  97. package/dist/evals/workflow.js +66 -0
  98. package/dist/evals/workflow.js.map +1 -0
  99. package/dist/insights/analyze-cli.d.ts +18 -0
  100. package/dist/insights/analyze-cli.d.ts.map +1 -0
  101. package/dist/insights/analyze-cli.js +71 -0
  102. package/dist/insights/analyze-cli.js.map +1 -0
  103. package/dist/insights/analyze.d.ts +136 -0
  104. package/dist/insights/analyze.d.ts.map +1 -0
  105. package/dist/insights/analyze.js +358 -0
  106. package/dist/insights/analyze.js.map +1 -0
  107. package/dist/insights/review-service.d.ts +127 -0
  108. package/dist/insights/review-service.d.ts.map +1 -0
  109. package/dist/insights/review-service.js +392 -0
  110. package/dist/insights/review-service.js.map +1 -0
  111. package/dist/insights/routes.d.ts +15 -0
  112. package/dist/insights/routes.d.ts.map +1 -0
  113. package/dist/insights/routes.js +66 -0
  114. package/dist/insights/routes.js.map +1 -0
  115. package/dist/insights/run-facts.d.ts +64 -0
  116. package/dist/insights/run-facts.d.ts.map +1 -0
  117. package/dist/insights/run-facts.js +152 -0
  118. package/dist/insights/run-facts.js.map +1 -0
  119. package/dist/insights/stats.d.ts +38 -0
  120. package/dist/insights/stats.d.ts.map +1 -0
  121. package/dist/insights/stats.js +102 -0
  122. package/dist/insights/stats.js.map +1 -0
  123. package/dist/insights/store.d.ts +89 -0
  124. package/dist/insights/store.d.ts.map +1 -0
  125. package/dist/insights/store.js +87 -0
  126. package/dist/insights/store.js.map +1 -0
  127. package/dist/insights/workflow.d.ts +22 -0
  128. package/dist/insights/workflow.d.ts.map +1 -0
  129. package/dist/insights/workflow.js +39 -0
  130. package/dist/insights/workflow.js.map +1 -0
  131. package/dist/qe/ledger.d.ts +15 -0
  132. package/dist/qe/ledger.d.ts.map +1 -1
  133. package/dist/qe/ledger.js +24 -0
  134. package/dist/qe/ledger.js.map +1 -1
  135. package/dist/schedules/routes.d.ts +48 -0
  136. package/dist/schedules/routes.d.ts.map +1 -0
  137. package/dist/schedules/routes.js +72 -0
  138. package/dist/schedules/routes.js.map +1 -0
  139. package/dist/schedules/scheduler.d.ts +58 -0
  140. package/dist/schedules/scheduler.d.ts.map +1 -0
  141. package/dist/schedules/scheduler.js +194 -0
  142. package/dist/schedules/scheduler.js.map +1 -0
  143. package/dist/schedules/store.d.ts +67 -0
  144. package/dist/schedules/store.d.ts.map +1 -0
  145. package/dist/schedules/store.js +148 -0
  146. package/dist/schedules/store.js.map +1 -0
  147. package/dist/skills/runtime.d.ts +12 -2
  148. package/dist/skills/runtime.d.ts.map +1 -1
  149. package/dist/skills/runtime.js +27 -1
  150. package/dist/skills/runtime.js.map +1 -1
  151. package/dist/team/routes.d.ts +10 -0
  152. package/dist/team/routes.d.ts.map +1 -1
  153. package/dist/team/routes.js +3 -0
  154. package/dist/team/routes.js.map +1 -1
  155. package/endpoint-manifest.json +132 -1
  156. package/package.json +4 -3
  157. package/workflows/review-recent-work.json +54 -0
  158. package/workflows/run-evals.json +29 -0
@@ -0,0 +1,127 @@
1
+ /**
2
+ * The run review around its workflow (DES-RUNS-REVIEW-001 §3, §5, §6):
3
+ *
4
+ * - {@link ReviewService.prepareLaunch} — every launch of `review-recent-work` (a schedule, Run it now, or
5
+ * `POST /runs`) goes through it: it reads the run store, writes `<reviews dir>/<run>/snapshot.json` and the
6
+ * handoff, and makes the launch repo-less, delivering nothing, with that directory as its ONLY write root and
7
+ * `report.json` as its deliverable, on ONE seat.
8
+ * - {@link ReviewService.reconcile} — once a review run is terminal, crew reads `facts.json` (the script's) and
9
+ * `report.json` (the seat's), keeps at most five recommendations ranked by crew's impact score, files the
10
+ * steering-rule and memory ones into the approve queue, and records the review.
11
+ * - {@link ReviewService.accept} / {@link ReviewService.dismiss} — a person's decision on one recommendation.
12
+ *
13
+ * The seat never files anything and never writes a number crew stores.
14
+ */
15
+ import type { SessionStatus, SessionView } from 'wicked-crew-api-types';
16
+ import type { LaunchRunInput } from '../core/types.js';
17
+ import type { Facts, Snapshot, Window } from './analyze.js';
18
+ import { type LooseEvent } from './run-facts.js';
19
+ import type { InsightsStore, Recommendation, Review } from './store.js';
20
+ export declare const HANDOFF_FILE = "handoff.json";
21
+ /** The seat's ONLY write root inside its review directory: the snapshot and the numbers beside it stay out of reach. */
22
+ export declare const OUT_DIR = "out";
23
+ /** The first review looks back this far; later ones start where the last completed review's window ended. */
24
+ export declare const FIRST_WINDOW_MS: number;
25
+ /** A window never spans more than this (a review after a long pause looks at the last two weeks). */
26
+ export declare const MAX_WINDOW_MS: number;
27
+ /** The baseline: this many previous windows of the same length. */
28
+ export declare const BASELINE_WINDOWS = 4;
29
+ export interface ReviewServiceDeps {
30
+ store: InsightsStore;
31
+ adapter: {
32
+ sessionsDetail(): Promise<SessionView[]>;
33
+ runEvents(runId: string): Promise<LooseEvent[] | null>;
34
+ seatsForWorkflow(workflowId: string): Promise<unknown[]>;
35
+ listRepos?(): Promise<Array<{
36
+ id: string;
37
+ git_url?: string;
38
+ }>>;
39
+ };
40
+ reviewsDir: string;
41
+ /** The approve queue (estate `proposal.*`), the same seam `/proposals` uses. */
42
+ estateTool: (tool: string, args: Record<string, unknown>) => Promise<unknown>;
43
+ log: (msg: string) => void;
44
+ now?: () => Date;
45
+ }
46
+ export interface PrepareOptions {
47
+ trigger: 'schedule' | 'manual';
48
+ scheduleId?: string | null;
49
+ /** Limit the review to these projects; empty = every run. */
50
+ projectIds?: readonly string[];
51
+ }
52
+ /** What a person's Accept opens in studio. */
53
+ export type AcceptTarget = {
54
+ kind: 'proposal';
55
+ proposalId: string;
56
+ } | {
57
+ kind: 'settings';
58
+ section: string;
59
+ } | {
60
+ kind: 'issue';
61
+ repoRef: string | null;
62
+ title: string;
63
+ body: string;
64
+ url: string | null;
65
+ } | {
66
+ kind: 'runs';
67
+ runIds: string[];
68
+ } | {
69
+ kind: 'none';
70
+ };
71
+ export declare class RecommendationError extends Error {
72
+ readonly status: 400 | 404 | 409 | 502;
73
+ constructor(status: 400 | 404 | 409 | 502, message: string);
74
+ }
75
+ export declare class ReviewService {
76
+ private readonly deps;
77
+ constructor(deps: ReviewServiceDeps);
78
+ private now;
79
+ runDir(runId: string): string;
80
+ /** The review window: from the end of the last completed review OF THE SAME SCOPE (else 48 h back), capped at
81
+ * 14 days. A review of other projects never moves this scope's cursor. */
82
+ windowFor(nowMs: number, projectIds?: readonly string[]): {
83
+ window: Window;
84
+ baselineWindows: Window[];
85
+ };
86
+ /** Fold the run store into a snapshot (the analyze script's only input). */
87
+ snapshot(projectIds: readonly string[], nowMs: number): Promise<Snapshot>;
88
+ /**
89
+ * Make a launch a review launch. Mutates and returns `input`: repo-less, no deliver, one seat, the run directory
90
+ * as the only write root, `report.json` as the only deliverable. Records the review as running.
91
+ */
92
+ prepareLaunch(input: LaunchRunInput, opts: PrepareOptions): Promise<LaunchRunInput>;
93
+ /** A launch the engine refused: the review never ran. */
94
+ launchRefused(runId: string, reason: string): void;
95
+ /** Fold every running review whose run is terminal. Never throws. */
96
+ reconcile(status: ReadonlyMap<string, SessionStatus | string>): Promise<void>;
97
+ private readJson;
98
+ ingest(review: Review, status: 'completed' | 'failed' | 'cancelled'): Promise<Review | undefined>;
99
+ /** Accept: record it, and say what to open. A proposal-backed one opens its proposal (approval stays there). */
100
+ accept(id: string): Promise<{
101
+ recommendation: Recommendation;
102
+ open: AcceptTarget;
103
+ }>;
104
+ /** Dismiss with a reason. A proposal-backed one rejects its proposal in the approve queue too. */
105
+ dismiss(id: string, reason: string): Promise<{
106
+ recommendation: Recommendation;
107
+ }>;
108
+ private targetOf;
109
+ }
110
+ /** The scope a review covers, as one comparable key (its projects, sorted; `''` = every run). */
111
+ export declare function scopeKey(projectIds: readonly string[]): string;
112
+ /** The one seat a review runs on: the first the roster says is eligible and not failing. `null` when none is. */
113
+ export declare function pickSeat(roster: readonly unknown[]): unknown | null;
114
+ /** `https://github.com/<owner>/<repo>/issues/new?title=&body=` for a GitHub remote, else `null`. */
115
+ export declare function githubNewIssueUrl(gitUrl: string | undefined, title: string, body: string): string | null;
116
+ /**
117
+ * Crew's candidates, worded by the seat where its report says so. Crew decides WHICH recommendations (its top five
118
+ * by impact) and their evidence; the seat may only retitle, describe the effect, say what would change its mind,
119
+ * and LOWER the confidence. A report entry naming no recommended candidate is dropped.
120
+ */
121
+ export declare function mergeReport(runId: string, facts: Facts, report: Record<string, unknown> | null): {
122
+ summary: string | null;
123
+ interpreted: boolean;
124
+ recommendations: Recommendation[];
125
+ watching: Review['watching'];
126
+ };
127
+ //# sourceMappingURL=review-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-service.d.ts","sourceRoot":"","sources":["../../src/insights/review-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,392 @@
1
+ /**
2
+ * The run review around its workflow (DES-RUNS-REVIEW-001 §3, §5, §6):
3
+ *
4
+ * - {@link ReviewService.prepareLaunch} — every launch of `review-recent-work` (a schedule, Run it now, or
5
+ * `POST /runs`) goes through it: it reads the run store, writes `<reviews dir>/<run>/snapshot.json` and the
6
+ * handoff, and makes the launch repo-less, delivering nothing, with that directory as its ONLY write root and
7
+ * `report.json` as its deliverable, on ONE seat.
8
+ * - {@link ReviewService.reconcile} — once a review run is terminal, crew reads `facts.json` (the script's) and
9
+ * `report.json` (the seat's), keeps at most five recommendations ranked by crew's impact score, files the
10
+ * steering-rule and memory ones into the approve queue, and records the review.
11
+ * - {@link ReviewService.accept} / {@link ReviewService.dismiss} — a person's decision on one recommendation.
12
+ *
13
+ * The seat never files anything and never writes a number crew stores.
14
+ */
15
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
16
+ import { join } from 'node:path';
17
+ import { MAX_RECOMMENDATIONS, analyze } from './analyze.js';
18
+ import { FACTS_FILE, REPORT_FILE, SNAPSHOT_FILE } from './analyze-cli.js';
19
+ import { runFactOf } from './run-facts.js';
20
+ import { REVIEW_WORKFLOW } from './workflow.js';
21
+ export const HANDOFF_FILE = 'handoff.json';
22
+ /** The seat's ONLY write root inside its review directory: the snapshot and the numbers beside it stay out of reach. */
23
+ export const OUT_DIR = 'out';
24
+ const HOUR = 3_600_000;
25
+ /** The first review looks back this far; later ones start where the last completed review's window ended. */
26
+ export const FIRST_WINDOW_MS = 48 * HOUR;
27
+ /** A window never spans more than this (a review after a long pause looks at the last two weeks). */
28
+ export const MAX_WINDOW_MS = 14 * 24 * HOUR;
29
+ /** The baseline: this many previous windows of the same length. */
30
+ export const BASELINE_WINDOWS = 4;
31
+ const TERMINAL = new Set(['completed', 'failed', 'cancelled']);
32
+ /** A review run the daemon no longer lists is given up on after this long. */
33
+ const LOST_AFTER_MS = 48 * HOUR;
34
+ export class RecommendationError extends Error {
35
+ status;
36
+ constructor(status, message) {
37
+ super(message);
38
+ this.status = status;
39
+ }
40
+ }
41
+ const CONFIDENCE_RANK = { low: 0, medium: 1, high: 2 };
42
+ const line = (v, max) => {
43
+ if (typeof v !== 'string')
44
+ return null;
45
+ const t = v.replace(/\s+/gu, ' ').trim();
46
+ return t === '' ? null : t.slice(0, max);
47
+ };
48
+ export class ReviewService {
49
+ deps;
50
+ constructor(deps) {
51
+ this.deps = deps;
52
+ }
53
+ now() {
54
+ return this.deps.now?.() ?? new Date();
55
+ }
56
+ runDir(runId) {
57
+ return join(this.deps.reviewsDir, runId);
58
+ }
59
+ /** The review window: from the end of the last completed review OF THE SAME SCOPE (else 48 h back), capped at
60
+ * 14 days. A review of other projects never moves this scope's cursor. */
61
+ windowFor(nowMs, projectIds = []) {
62
+ const last = this.deps.store.lastCompleted(scopeKey(projectIds));
63
+ const start = Math.max(last?.window.endMs ?? nowMs - FIRST_WINDOW_MS, nowMs - MAX_WINDOW_MS);
64
+ // A review run seconds after the last one still compares a meaningful span.
65
+ const len = Math.max(nowMs - start, HOUR);
66
+ const window = { startMs: nowMs - len, endMs: nowMs };
67
+ const baselineWindows = [];
68
+ for (let i = 1; i <= BASELINE_WINDOWS; i += 1)
69
+ baselineWindows.push({ startMs: window.startMs - i * len, endMs: window.startMs - (i - 1) * len });
70
+ return { window, baselineWindows };
71
+ }
72
+ /** Fold the run store into a snapshot (the analyze script's only input). */
73
+ async snapshot(projectIds, nowMs) {
74
+ const { window, baselineWindows } = this.windowFor(nowMs, projectIds);
75
+ const earliest = Math.min(...baselineWindows.map((w) => w.startMs), window.startMs);
76
+ const views = await this.deps.adapter.sessionsDetail();
77
+ const runs = [];
78
+ for (const view of views) {
79
+ const s = view.session;
80
+ if (s['workflow_id'] === REVIEW_WORKFLOW)
81
+ continue;
82
+ if (projectIds.length > 0 && !projectIds.includes(String(s['project_id'] ?? '')))
83
+ continue;
84
+ const dated = typeof s['created_at'] === 'number' ? (s['created_at'] < 1e12 ? s['created_at'] * 1000 : s['created_at']) : null;
85
+ // A run dated by the daemon's launch record outside every window is skipped without reading its events.
86
+ if (dated !== null && (dated < earliest || dated >= window.endMs))
87
+ continue;
88
+ let events = [];
89
+ try {
90
+ events = (await this.deps.adapter.runEvents(String(s['id']))) ?? [];
91
+ }
92
+ catch (err) {
93
+ this.deps.log(`[review] events of ${String(s['id'])} unreadable, counted from its record only: ${err instanceof Error ? err.message : String(err)}`);
94
+ }
95
+ // An undated run (no launch record: onboarding, campaign nodes, older runs) is dated by its first event.
96
+ const created = dated ?? Math.min(...events.map((e) => (typeof e.ts === 'number' ? e.ts : Number.POSITIVE_INFINITY)));
97
+ if (!Number.isFinite(created) || created < earliest || created >= window.endMs)
98
+ continue;
99
+ runs.push(runFactOf(view, events, nowMs));
100
+ }
101
+ return { version: 1, generatedAtMs: nowMs, window, baselineWindows, projectIds: [...projectIds], runs };
102
+ }
103
+ /**
104
+ * Make a launch a review launch. Mutates and returns `input`: repo-less, no deliver, one seat, the run directory
105
+ * as the only write root, `report.json` as the only deliverable. Records the review as running.
106
+ */
107
+ async prepareLaunch(input, opts) {
108
+ const now = this.now();
109
+ const runId = input.sessionId;
110
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u.test(runId) || runId.includes('..'))
111
+ throw new Error(`a review run id must be one plain path segment: ${runId}`);
112
+ // One review per run id: a repeated id never touches the review already recorded under it.
113
+ if (this.deps.store.get(runId) !== undefined)
114
+ throw new Error(`a review is already recorded for run ${runId}`);
115
+ const seat = pickSeat(await this.deps.adapter.seatsForWorkflow(REVIEW_WORKFLOW));
116
+ if (seat === null)
117
+ throw new Error('no signed-in, eligible agent seat to interpret the review: sign one in (Agents) and run it again');
118
+ const projectIds = [...(opts.projectIds ?? [])];
119
+ const snap = await this.snapshot(projectIds, now.getTime());
120
+ const dir = this.runDir(runId);
121
+ mkdirSync(this.deps.reviewsDir, { recursive: true });
122
+ try {
123
+ mkdirSync(dir); // exclusive: the filesystem decides who owns this id
124
+ }
125
+ catch (err) {
126
+ if (err.code === 'EEXIST')
127
+ throw new Error(`a review directory already exists for run ${runId}`);
128
+ throw err;
129
+ }
130
+ const out = join(dir, OUT_DIR);
131
+ mkdirSync(out);
132
+ writeFileSync(join(dir, SNAPSHOT_FILE), JSON.stringify(snap), 'utf8');
133
+ const factsPath = join(dir, FACTS_FILE);
134
+ const reportPath = join(out, REPORT_FILE);
135
+ const handoffPath = join(dir, HANDOFF_FILE);
136
+ writeFileSync(handoffPath, JSON.stringify({ facts_path: factsPath, report_path: reportPath, max_recommendations: MAX_RECOMMENDATIONS }, null, 2), 'utf8');
137
+ const asked = input.problem.trim();
138
+ input.problem =
139
+ `Review recent work: interpret the run review computed for ${snap.runs.length} runs ` +
140
+ `(${new Date(snap.window.startMs).toISOString()} to ${new Date(snap.window.endMs).toISOString()}). ` +
141
+ `Read the handoff file at ${handoffPath}. It names facts_path (the numbers, computed by a script) and ` +
142
+ `report_path (the one file you write).` +
143
+ (asked !== '' && asked !== REVIEW_WORKFLOW ? `\n\nThe person also asked: ${asked.slice(0, 2000)}` : '');
144
+ input.workflow = REVIEW_WORKFLOW;
145
+ input.clisJson = JSON.stringify([seat]);
146
+ // The seat reads the review directory and writes ONLY `out/`: the snapshot and facts.json are not its to change.
147
+ input.extraWriteRoots = [out];
148
+ input.requireDeliverables = [reportPath];
149
+ delete input.repoRef;
150
+ delete input.deliver;
151
+ delete input.baseRef;
152
+ delete input.revisesPr;
153
+ delete input.plan;
154
+ delete input.projectGraph;
155
+ input.extraReadRoots = [dir];
156
+ this.deps.store.add({
157
+ runId,
158
+ trigger: opts.trigger,
159
+ scheduleId: opts.scheduleId ?? null,
160
+ launchedAt: now.toISOString(),
161
+ endedAt: null,
162
+ status: 'running',
163
+ window: snap.window,
164
+ baselineWindows: snap.baselineWindows,
165
+ projectIds,
166
+ summary: null,
167
+ interpreted: false,
168
+ totals: null,
169
+ series: [],
170
+ improvements: [],
171
+ regressions: [],
172
+ recommendations: [],
173
+ watching: [],
174
+ error: null,
175
+ });
176
+ return input;
177
+ }
178
+ /** A launch the engine refused: the review never ran. */
179
+ launchRefused(runId, reason) {
180
+ this.deps.store.update(runId, { status: 'failed', endedAt: this.now().toISOString(), error: `the launch was refused: ${reason.slice(0, 500)}` });
181
+ }
182
+ /** Fold every running review whose run is terminal. Never throws. */
183
+ async reconcile(status) {
184
+ for (const review of this.deps.store.running()) {
185
+ try {
186
+ const s = status.get(review.runId);
187
+ if (s === undefined) {
188
+ if (this.now().getTime() - Date.parse(review.launchedAt) > LOST_AFTER_MS) {
189
+ this.deps.store.update(review.runId, { status: 'failed', endedAt: this.now().toISOString(), error: 'the review run is no longer on the daemon' });
190
+ }
191
+ continue;
192
+ }
193
+ if (!TERMINAL.has(s))
194
+ continue;
195
+ await this.ingest(review, s);
196
+ }
197
+ catch (err) {
198
+ this.deps.log(`[review] ingest of ${review.runId} failed: ${err instanceof Error ? err.message : String(err)}`);
199
+ }
200
+ }
201
+ }
202
+ readJson(path) {
203
+ if (!existsSync(path))
204
+ return null;
205
+ try {
206
+ return JSON.parse(readFileSync(path, 'utf8'));
207
+ }
208
+ catch {
209
+ return null;
210
+ }
211
+ }
212
+ async ingest(review, status) {
213
+ const dir = this.runDir(review.runId);
214
+ const endedAt = this.now().toISOString();
215
+ // The Tool step must have run (its facts.json is the seat's input), but the numbers crew STORES are
216
+ // re-derived here from the snapshot crew wrote, by the same deterministic code: nothing the run wrote can
217
+ // change a metric, a candidate, a ranking or a proposal.
218
+ const ran = existsSync(join(dir, FACTS_FILE));
219
+ const snapshot = this.readJson(join(dir, SNAPSHOT_FILE));
220
+ const facts = ran && snapshot !== null && snapshot.version === 1 && Array.isArray(snapshot.runs) ? analyze(snapshot) : null;
221
+ if (facts === null) {
222
+ return this.deps.store.update(review.runId, {
223
+ status: status === 'completed' ? 'failed' : status,
224
+ endedAt,
225
+ error: status === 'cancelled' ? 'the review was cancelled before its numbers were computed' : 'the analyze step wrote no facts.json',
226
+ });
227
+ }
228
+ const report = this.readJson(join(dir, OUT_DIR, REPORT_FILE));
229
+ const merged = mergeReport(review.runId, facts, report);
230
+ for (const rec of merged.recommendations) {
231
+ const p = rec.action.proposal;
232
+ if (p === undefined || (rec.kind !== 'steering-rule' && rec.kind !== 'memory'))
233
+ continue;
234
+ try {
235
+ const payload = rec.kind === 'memory'
236
+ ? { content: p.content, tier: 'semantic', capture: 'review', source: REVIEW_WORKFLOW }
237
+ : { rule: p.content, severity: p.severity ?? 'warn', source: REVIEW_WORKFLOW };
238
+ const res = (await this.deps.estateTool('proposal.submit', { kind_type: p.kindType, payload, facets: {} }));
239
+ if (typeof res?.id === 'string' && res.id !== '')
240
+ rec.proposalId = res.id;
241
+ else
242
+ rec.proposalError = 'proposal.submit returned no id';
243
+ }
244
+ catch (err) {
245
+ rec.proposalError = (err instanceof Error ? err.message : String(err)).slice(0, 300);
246
+ }
247
+ }
248
+ return this.deps.store.update(review.runId, {
249
+ // A run that computed its numbers but was cancelled or failed afterwards still shows them, said plainly.
250
+ status: status === 'completed' ? 'completed' : status,
251
+ endedAt,
252
+ summary: merged.summary,
253
+ interpreted: merged.interpreted,
254
+ totals: facts.totals,
255
+ series: facts.metrics.filter((m) => m.segment === 'all').map((m) => ({ metric: m.metric, window: m.window, baseline: m.baseline, verdict: m.verdict })),
256
+ improvements: facts.improvements.map((f) => ({ metric: f.metric, segment: f.segment, line: f.line })),
257
+ regressions: facts.regressions.map((f) => ({ metric: f.metric, segment: f.segment, line: f.line })),
258
+ recommendations: merged.recommendations,
259
+ watching: merged.watching,
260
+ error: status === 'completed' ? null : `the review run ended ${status}${merged.interpreted ? '' : ' before its report was written'}`,
261
+ });
262
+ }
263
+ /** Accept: record it, and say what to open. A proposal-backed one opens its proposal (approval stays there). */
264
+ async accept(id) {
265
+ const found = this.deps.store.findRecommendation(id);
266
+ if (found === undefined)
267
+ throw new RecommendationError(404, `no recommendation ${id}`);
268
+ if (found.rec.state === 'dismissed')
269
+ throw new RecommendationError(409, 'this recommendation was dismissed');
270
+ const rec = this.deps.store.updateRecommendation(id, { state: 'accepted', decidedAt: this.now().toISOString(), reason: null });
271
+ return { recommendation: rec, open: await this.targetOf(rec) };
272
+ }
273
+ /** Dismiss with a reason. A proposal-backed one rejects its proposal in the approve queue too. */
274
+ async dismiss(id, reason) {
275
+ const text = line(reason, 500);
276
+ if (text === null)
277
+ throw new RecommendationError(400, 'a dismissal needs a reason');
278
+ const found = this.deps.store.findRecommendation(id);
279
+ if (found === undefined)
280
+ throw new RecommendationError(404, `no recommendation ${id}`);
281
+ if (found.rec.state === 'accepted')
282
+ throw new RecommendationError(409, 'this recommendation was accepted');
283
+ if (found.rec.proposalId !== null && found.rec.state === 'new') {
284
+ try {
285
+ await this.deps.estateTool('proposal.reject', { id: found.rec.proposalId });
286
+ }
287
+ catch (err) {
288
+ throw new RecommendationError(502, `its proposal could not be rejected: ${err instanceof Error ? err.message : String(err)}`);
289
+ }
290
+ }
291
+ const rec = this.deps.store.updateRecommendation(id, { state: 'dismissed', reason: text, decidedAt: this.now().toISOString() });
292
+ return { recommendation: rec };
293
+ }
294
+ async targetOf(rec) {
295
+ if (rec.proposalId !== null)
296
+ return { kind: 'proposal', proposalId: rec.proposalId };
297
+ if (rec.action.setting !== undefined)
298
+ return { kind: 'settings', section: rec.action.setting };
299
+ if (rec.action.runs !== undefined)
300
+ return { kind: 'runs', runIds: rec.action.runs };
301
+ const issue = rec.action.issue;
302
+ if (issue !== undefined) {
303
+ let url = null;
304
+ if (issue.repoRef !== null && this.deps.adapter.listRepos !== undefined) {
305
+ const repo = (await this.deps.adapter.listRepos().catch(() => [])).find((r) => r.id === issue.repoRef);
306
+ url = githubNewIssueUrl(repo?.git_url, issue.title, issue.body);
307
+ }
308
+ return { kind: 'issue', repoRef: issue.repoRef, title: issue.title, body: issue.body, url };
309
+ }
310
+ return { kind: 'none' };
311
+ }
312
+ }
313
+ /** The scope a review covers, as one comparable key (its projects, sorted; `''` = every run). */
314
+ export function scopeKey(projectIds) {
315
+ return [...projectIds].sort().join(',');
316
+ }
317
+ /** The one seat a review runs on: the first the roster says is eligible and not failing. `null` when none is. */
318
+ export function pickSeat(roster) {
319
+ for (const seat of roster) {
320
+ if (typeof seat !== 'object' || seat === null)
321
+ continue;
322
+ const s = seat;
323
+ if (s['council_eligible'] === false)
324
+ continue;
325
+ const health = s['health'];
326
+ if (health !== null && health !== undefined && typeof health.status === 'string' && health.status !== 'active')
327
+ continue;
328
+ return seat;
329
+ }
330
+ return null;
331
+ }
332
+ /** `https://github.com/<owner>/<repo>/issues/new?title=&body=` for a GitHub remote, else `null`. */
333
+ export function githubNewIssueUrl(gitUrl, title, body) {
334
+ if (gitUrl === undefined)
335
+ return null;
336
+ const m = /github\.com[:/]([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+?)(?:\.git)?\/?$/u.exec(gitUrl.trim());
337
+ if (m === null)
338
+ return null;
339
+ const q = new URLSearchParams({ title: title.slice(0, 200), body: body.slice(0, 4000) });
340
+ return `https://github.com/${m[1]}/${m[2]}/issues/new?${q.toString()}`;
341
+ }
342
+ /**
343
+ * Crew's candidates, worded by the seat where its report says so. Crew decides WHICH recommendations (its top five
344
+ * by impact) and their evidence; the seat may only retitle, describe the effect, say what would change its mind,
345
+ * and LOWER the confidence. A report entry naming no recommended candidate is dropped.
346
+ */
347
+ export function mergeReport(runId, facts, report) {
348
+ const byId = new Map(facts.candidates.map((c) => [c.id, c]));
349
+ const words = new Map();
350
+ const entries = Array.isArray(report?.['recommendations']) ? report['recommendations'] : [];
351
+ for (const e of entries) {
352
+ if (typeof e !== 'object' || e === null)
353
+ continue;
354
+ const cand = e['candidate'];
355
+ if (typeof cand === 'string' && facts.recommended.includes(cand) && !words.has(cand))
356
+ words.set(cand, e);
357
+ }
358
+ const summary = line(report?.['summary'], 1200);
359
+ const interpreted = report !== null && summary !== null;
360
+ const recommendations = facts.recommended.flatMap((cid) => {
361
+ const c = byId.get(cid);
362
+ if (c === undefined)
363
+ return [];
364
+ const w = words.get(cid);
365
+ const asked = w?.['confidence'];
366
+ const confidence = (asked === 'high' || asked === 'medium' || asked === 'low') && CONFIDENCE_RANK[asked] < CONFIDENCE_RANK[c.confidence] ? asked : c.confidence;
367
+ return [{
368
+ id: `${runId}:${c.id}`,
369
+ candidate: c.id,
370
+ kind: c.kind,
371
+ title: line(w?.['title'], 90) ?? c.title,
372
+ evidence: c.evidence,
373
+ expectedEffect: line(w?.['expectedEffect'], 300) ?? c.expectedEffect,
374
+ confidence,
375
+ changeMind: line(w?.['changeMind'], 300) ?? c.changeMind,
376
+ impact: c.impact,
377
+ basis: c.basis,
378
+ action: c.action,
379
+ state: 'new',
380
+ reason: null,
381
+ decidedAt: null,
382
+ proposalId: null,
383
+ proposalError: null,
384
+ }];
385
+ });
386
+ const watching = facts.watching.flatMap((cid) => {
387
+ const c = byId.get(cid);
388
+ return c === undefined ? [] : [{ id: c.id, title: c.title, line: c.evidence.line }];
389
+ });
390
+ return { summary, interpreted, recommendations, watching };
391
+ }
392
+ //# sourceMappingURL=review-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-service.js","sourceRoot":"","sources":["../../src/insights/review-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * `/api/v1/insights` (DES-RUNS-REVIEW-001 §6): the review history the Run insights plugin draws, and a person's
3
+ * decision on one recommendation. Reads are open to any actor; Accept and Dismiss are a person's only.
4
+ */
5
+ import type { FastifyInstance } from 'fastify';
6
+ import type { AuditLog } from '../api/audit.js';
7
+ import { type ReviewService } from './review-service.js';
8
+ import type { InsightsStore } from './store.js';
9
+ export interface InsightsRouteDeps {
10
+ store: InsightsStore;
11
+ service: Pick<ReviewService, 'accept' | 'dismiss'>;
12
+ audit: AuditLog;
13
+ }
14
+ export declare function registerInsightsRoutes(app: FastifyInstance, deps: InsightsRouteDeps): void;
15
+ //# sourceMappingURL=routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/insights/routes.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAgB,MAAM,SAAS,CAAC;AAK7D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAIhD,OAAO,EAAuB,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAC9E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAKhD,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,aAAa,CAAC;IACrB,OAAO,EAAE,IAAI,CAAC,aAAa,EAAE,QAAQ,GAAG,SAAS,CAAC,CAAC;IACnD,KAAK,EAAE,QAAQ,CAAC;CACjB;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,iBAAiB,GAAG,IAAI,CA6D1F"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * `/api/v1/insights` (DES-RUNS-REVIEW-001 §6): the review history the Run insights plugin draws, and a person's
3
+ * decision on one recommendation. Reads are open to any actor; Accept and Dismiss are a person's only.
4
+ */
5
+ import { z } from 'zod';
6
+ import { API_PREFIX } from '../api/api-prefix.js';
7
+ import { LOCAL_ACTOR } from '../api/auth.js';
8
+ import { isHumanOperator } from '../api/watch-routes.js';
9
+ import { METRICS } from './analyze.js';
10
+ import { MIN_SAMPLE } from './stats.js';
11
+ import { RecommendationError } from './review-service.js';
12
+ const V = API_PREFIX;
13
+ const DismissSchema = z.object({ reason: z.string().trim().min(1).max(500) }).strict();
14
+ export function registerInsightsRoutes(app, deps) {
15
+ const actorOf = (req) => req.actor ?? LOCAL_ACTOR;
16
+ const refuse = (reply) => reply.code(403).send({ error: 'Only a person can accept or dismiss a recommendation' });
17
+ const fail = (reply, err) => {
18
+ if (err instanceof RecommendationError)
19
+ return reply.code(err.status).send({ error: err.message });
20
+ throw err;
21
+ };
22
+ app.get(`${V}/insights`, { config: { manifest: { responseType: 'InsightsResponse', statusCodes: [200] } } }, async () => {
23
+ const reviews = deps.store.list();
24
+ const latest = reviews.find((r) => r.status !== 'running') ?? null;
25
+ return {
26
+ reviews,
27
+ newRecommendations: deps.store.newCount(),
28
+ running: reviews.some((r) => r.status === 'running'),
29
+ lastReview: latest === null ? null : { runId: latest.runId, launchedAt: latest.launchedAt, endedAt: latest.endedAt, status: latest.status },
30
+ metrics: METRICS,
31
+ minSample: MIN_SAMPLE,
32
+ };
33
+ });
34
+ app.post(`${V}/insights/recommendations/:id/accept`, { config: { manifest: { responseType: 'AcceptRecommendationResponse', statusCodes: [200, 403, 404, 409] } } }, async (req, reply) => {
35
+ const actor = actorOf(req);
36
+ if (!isHumanOperator(actor))
37
+ return refuse(reply);
38
+ const { id } = req.params;
39
+ try {
40
+ const res = await deps.service.accept(id);
41
+ deps.audit.record('insights.recommendation.accepted', actor, { detail: { id, kind: res.recommendation.kind } });
42
+ return res;
43
+ }
44
+ catch (err) {
45
+ return fail(reply, err);
46
+ }
47
+ });
48
+ app.post(`${V}/insights/recommendations/:id/dismiss`, { config: { manifest: { requestType: 'DismissRecommendationBody', responseType: 'DismissRecommendationResponse', statusCodes: [200, 400, 403, 404, 409, 502] } } }, async (req, reply) => {
49
+ const actor = actorOf(req);
50
+ if (!isHumanOperator(actor))
51
+ return refuse(reply);
52
+ const parsed = DismissSchema.safeParse(req.body);
53
+ if (!parsed.success)
54
+ return reply.code(400).send({ error: 'body must be { reason: <why, 1 to 500 characters> }' });
55
+ const { id } = req.params;
56
+ try {
57
+ const res = await deps.service.dismiss(id, parsed.data.reason);
58
+ deps.audit.record('insights.recommendation.dismissed', actor, { detail: { id, reason: parsed.data.reason } });
59
+ return res;
60
+ }
61
+ catch (err) {
62
+ return fail(reply, err);
63
+ }
64
+ });
65
+ }
66
+ //# sourceMappingURL=routes.js.map