@cat-factory/orchestration 0.190.0 → 0.192.0

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 (88) hide show
  1. package/dist/container/dependencies.d.ts +15 -2
  2. package/dist/container/dependencies.d.ts.map +1 -1
  3. package/dist/container/engine-dependent-modules.d.ts.map +1 -1
  4. package/dist/container/engine-dependent-modules.js +1 -1
  5. package/dist/container/engine-dependent-modules.js.map +1 -1
  6. package/dist/container/modules.d.ts +1 -1
  7. package/dist/container/modules.d.ts.map +1 -1
  8. package/dist/container/modules.js +26 -1
  9. package/dist/container/modules.js.map +1 -1
  10. package/dist/container/platform-modules.d.ts.map +1 -1
  11. package/dist/container/platform-modules.js +1 -0
  12. package/dist/container/platform-modules.js.map +1 -1
  13. package/dist/container.d.ts +1 -1
  14. package/dist/index.d.ts +4 -3
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +4 -3
  17. package/dist/index.js.map +1 -1
  18. package/dist/modules/execution/ExecutionServiceDependencies.d.ts +8 -1
  19. package/dist/modules/execution/ExecutionServiceDependencies.d.ts.map +1 -1
  20. package/dist/modules/execution/GateHelperDispatcher.js +1 -1
  21. package/dist/modules/execution/GateHelperDispatcher.js.map +1 -1
  22. package/dist/modules/execution/GateStepController.d.ts +15 -0
  23. package/dist/modules/execution/GateStepController.d.ts.map +1 -1
  24. package/dist/modules/execution/GateStepController.js +19 -0
  25. package/dist/modules/execution/GateStepController.js.map +1 -1
  26. package/dist/modules/execution/HumanTestController.js +1 -1
  27. package/dist/modules/execution/HumanTestController.js.map +1 -1
  28. package/dist/modules/execution/PollRunningController.d.ts +8 -0
  29. package/dist/modules/execution/PollRunningController.d.ts.map +1 -1
  30. package/dist/modules/execution/PollRunningController.js +14 -0
  31. package/dist/modules/execution/PollRunningController.js.map +1 -1
  32. package/dist/modules/execution/RalphController.js +1 -1
  33. package/dist/modules/execution/RalphController.js.map +1 -1
  34. package/dist/modules/execution/RunDispatcher.d.ts +6 -20
  35. package/dist/modules/execution/RunDispatcher.d.ts.map +1 -1
  36. package/dist/modules/execution/RunDispatcher.js +30 -7
  37. package/dist/modules/execution/RunDispatcher.js.map +1 -1
  38. package/dist/modules/execution/RunStateMachine.d.ts.map +1 -1
  39. package/dist/modules/execution/RunStateMachine.js +12 -0
  40. package/dist/modules/execution/RunStateMachine.js.map +1 -1
  41. package/dist/modules/execution/StepGraph.d.ts.map +1 -1
  42. package/dist/modules/execution/StepGraph.js +13 -1
  43. package/dist/modules/execution/StepGraph.js.map +1 -1
  44. package/dist/modules/execution/TesterController.js +2 -2
  45. package/dist/modules/execution/TesterController.js.map +1 -1
  46. package/dist/modules/execution/VisualConfirmationController.js +1 -1
  47. package/dist/modules/execution/VisualConfirmationController.js.map +1 -1
  48. package/dist/modules/execution/step-fold.logic.d.ts +11 -2
  49. package/dist/modules/execution/step-fold.logic.d.ts.map +1 -1
  50. package/dist/modules/execution/step-fold.logic.js +18 -2
  51. package/dist/modules/execution/step-fold.logic.js.map +1 -1
  52. package/dist/modules/observability/GateOutcomeRecorder.d.ts +33 -0
  53. package/dist/modules/observability/GateOutcomeRecorder.d.ts.map +1 -0
  54. package/dist/modules/observability/GateOutcomeRecorder.js +45 -0
  55. package/dist/modules/observability/GateOutcomeRecorder.js.map +1 -0
  56. package/dist/modules/observability/LlmObservabilityService.d.ts +22 -1
  57. package/dist/modules/observability/LlmObservabilityService.d.ts.map +1 -1
  58. package/dist/modules/observability/LlmObservabilityService.js +30 -0
  59. package/dist/modules/observability/LlmObservabilityService.js.map +1 -1
  60. package/dist/modules/observability/PlatformObservabilityService.d.ts +32 -1
  61. package/dist/modules/observability/PlatformObservabilityService.d.ts.map +1 -1
  62. package/dist/modules/observability/PlatformObservabilityService.js +71 -10
  63. package/dist/modules/observability/PlatformObservabilityService.js.map +1 -1
  64. package/dist/modules/observability/platform-health.logic.d.ts +35 -4
  65. package/dist/modules/observability/platform-health.logic.d.ts.map +1 -1
  66. package/dist/modules/observability/platform-health.logic.js +45 -0
  67. package/dist/modules/observability/platform-health.logic.js.map +1 -1
  68. package/dist/modules/observability/platform-observability.logic.d.ts +41 -4
  69. package/dist/modules/observability/platform-observability.logic.d.ts.map +1 -1
  70. package/dist/modules/observability/platform-observability.logic.js +88 -6
  71. package/dist/modules/observability/platform-observability.logic.js.map +1 -1
  72. package/dist/modules/observability/retentionPass.d.ts +22 -0
  73. package/dist/modules/observability/retentionPass.d.ts.map +1 -1
  74. package/dist/modules/observability/retentionPass.js +12 -0
  75. package/dist/modules/observability/retentionPass.js.map +1 -1
  76. package/dist/modules/observability/runTraceSpans.logic.d.ts +20 -0
  77. package/dist/modules/observability/runTraceSpans.logic.d.ts.map +1 -0
  78. package/dist/modules/observability/runTraceSpans.logic.js +127 -0
  79. package/dist/modules/observability/runTraceSpans.logic.js.map +1 -0
  80. package/dist/modules/recurring/RecurringPipelineService.d.ts +79 -14
  81. package/dist/modules/recurring/RecurringPipelineService.d.ts.map +1 -1
  82. package/dist/modules/recurring/RecurringPipelineService.js +157 -24
  83. package/dist/modules/recurring/RecurringPipelineService.js.map +1 -1
  84. package/dist/modules/recurring/issueIntake.logic.d.ts +43 -0
  85. package/dist/modules/recurring/issueIntake.logic.d.ts.map +1 -0
  86. package/dist/modules/recurring/issueIntake.logic.js +69 -0
  87. package/dist/modules/recurring/issueIntake.logic.js.map +1 -0
  88. package/package.json +11 -11
@@ -1,16 +1,53 @@
1
- import type { PlatformObservabilityWindow, PlatformOutcomeTotals, PlatformTrendPoint } from '@cat-factory/contracts';
2
- import type { PlatformRunOutcome, PlatformRunTrendPoint } from '@cat-factory/kernel';
3
- /** Per-window sizing: how far back to aggregate and how wide each trend bucket is. */
1
+ import type { PlatformFailureSlice, PlatformGateStat, PlatformObservabilityWindow, PlatformOutcomeTotals, PlatformTrendPoint, PlatformTrendSource } from '@cat-factory/contracts';
2
+ import type { PlatformDailyRunCount, PlatformGateOutcomeCount, PlatformRunTrendPoint } from '@cat-factory/kernel';
3
+ /** A day, in ms: the daily rollup's bucket width and the long windows' trend resolution. */
4
+ export declare const DAY_MS: number;
5
+ /**
6
+ * Per-window sizing: how far back to aggregate, how wide each trend bucket is, and which
7
+ * store answers it.
8
+ *
9
+ * The short windows scan `agent_runs` live. The long ones read the daily rollup: a `90d`
10
+ * window at any finer resolution would scan a quarter of every run the deployment has ever
11
+ * made on each dashboard load, which is the cost the rollup exists to remove, and it would
12
+ * do it while producing a series nobody can read at that density anyway.
13
+ */
4
14
  export declare const PLATFORM_WINDOWS: Record<PlatformObservabilityWindow, {
5
15
  windowMs: number;
6
16
  bucketMs: number;
17
+ source: PlatformTrendSource;
7
18
  }>;
8
19
  /** Reduce the `(kind, status)` outcome rows into per-status totals + the success rate. */
9
- export declare function summarizeOutcomes(rows: PlatformRunOutcome[]): PlatformOutcomeTotals;
20
+ export declare function summarizeOutcomes(rows: readonly {
21
+ status: string;
22
+ count: number;
23
+ }[]): PlatformOutcomeTotals;
10
24
  /**
11
25
  * Fold the sparse `(bucketStart, status)` trend rows into a contiguous, zero-filled,
12
26
  * oldest-first series spanning `[since, now]` at `bucketMs` resolution — so the sparkline
13
27
  * shows empty buckets as zeros rather than collapsing gaps.
14
28
  */
15
29
  export declare function buildTrend(points: PlatformRunTrendPoint[], since: number, now: number, bucketMs: number): PlatformTrendPoint[];
30
+ /**
31
+ * The daily-rollup rows as trend input. The rollup splits a `failed` day into one row per
32
+ * failure kind, so the same day appears several times; `buildTrend` sums them, which is why
33
+ * this is a projection rather than a regrouping.
34
+ */
35
+ export declare function dailyTrendRows(rows: readonly PlatformDailyRunCount[]): PlatformRunTrendPoint[];
36
+ /**
37
+ * The failure taxonomy folded out of the daily-rollup rows, most frequent first: the
38
+ * long-window counterpart of `failureKindBreakdown`. Only `failed` rows carry a kind; a row
39
+ * that somehow lacks one is counted as `unknown` rather than dropped, so the slice totals
40
+ * still add up to the window's failure count.
41
+ */
42
+ export declare function dailyFailureSlices(rows: readonly PlatformDailyRunCount[]): PlatformFailureSlice[];
43
+ /**
44
+ * Fold the `(gateKind, helperKind, outcome)` buckets into one statistic per gate kind, busiest
45
+ * first. The port groups by outcome because that is one `GROUP BY`; the dashboard reads a gate
46
+ * kind as a single row, so passed/exhausted become columns here rather than rows.
47
+ *
48
+ * `helperKind` is taken from whichever bucket names one: a gate's helper is a property of the
49
+ * gate definition, so the buckets agree, but a gate that passed cleanly every time in the
50
+ * window legitimately recorded no helper at all and must not blank out the label.
51
+ */
52
+ export declare function summarizeGateOutcomes(rows: readonly PlatformGateOutcomeCount[]): PlatformGateStat[];
16
53
  //# sourceMappingURL=platform-observability.logic.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"platform-observability.logic.d.ts","sourceRoot":"","sources":["../../../src/modules/observability/platform-observability.logic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,2BAA2B,EAC3B,qBAAqB,EACrB,kBAAkB,EACnB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,KAAK,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AAMpF,sFAAsF;AACtF,eAAO,MAAM,gBAAgB,EAAE,MAAM,CACnC,2BAA2B,EAC3B;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAKvC,CAAA;AAED,0FAA0F;AAC1F,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,kBAAkB,EAAE,GAAG,qBAAqB,CAoCnF;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,qBAAqB,EAAE,EAC/B,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,GACf,kBAAkB,EAAE,CAkBtB"}
1
+ {"version":3,"file":"platform-observability.logic.d.ts","sourceRoot":"","sources":["../../../src/modules/observability/platform-observability.logic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,oBAAoB,EACpB,gBAAgB,EAChB,2BAA2B,EAC3B,qBAAqB,EACrB,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,KAAK,EACV,qBAAqB,EACrB,wBAAwB,EACxB,qBAAqB,EACtB,MAAM,qBAAqB,CAAA;AAM5B,4FAA4F;AAC5F,eAAO,MAAM,MAAM,QAAmB,CAAA;AAEtC;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CACnC,2BAA2B,EAC3B;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,mBAAmB,CAAA;CAAE,CAOpE,CAAA;AAED,0FAA0F;AAC1F,wBAAgB,iBAAiB,CAI/B,IAAI,EAAE,SAAS;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,GACjD,qBAAqB,CAoCvB;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,qBAAqB,EAAE,EAC/B,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,GACf,kBAAkB,EAAE,CAkBtB;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,qBAAqB,EAAE,GAAG,qBAAqB,EAAE,CAE9F;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,SAAS,qBAAqB,EAAE,GAAG,oBAAoB,EAAE,CAUjG;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,SAAS,wBAAwB,EAAE,GACxC,gBAAgB,EAAE,CA+BpB"}
@@ -1,14 +1,30 @@
1
1
  // Pure reshaping behind the platform-observability read: the port returns raw grouped
2
2
  // rows; these functions fold them into the wire projection. Kept pure (no clock, no I/O)
3
- // so they're unit-tested directly and reused by the future alert sweep.
4
- /** Per-window sizing: how far back to aggregate and how wide each trend bucket is. */
3
+ // so they're unit-tested directly and reused by the alert sweep.
4
+ /** A day, in ms: the daily rollup's bucket width and the long windows' trend resolution. */
5
+ export const DAY_MS = 24 * 60 * 60_000;
6
+ /**
7
+ * Per-window sizing: how far back to aggregate, how wide each trend bucket is, and which
8
+ * store answers it.
9
+ *
10
+ * The short windows scan `agent_runs` live. The long ones read the daily rollup: a `90d`
11
+ * window at any finer resolution would scan a quarter of every run the deployment has ever
12
+ * made on each dashboard load, which is the cost the rollup exists to remove, and it would
13
+ * do it while producing a series nobody can read at that density anyway.
14
+ */
5
15
  export const PLATFORM_WINDOWS = {
6
- '1h': { windowMs: 60 * 60_000, bucketMs: 5 * 60_000 }, // 12 × 5min buckets
7
- '24h': { windowMs: 24 * 60 * 60_000, bucketMs: 60 * 60_000 }, // 24 × 1h buckets
8
- '7d': { windowMs: 7 * 24 * 60 * 60_000, bucketMs: 6 * 60 * 60_000 }, // 28 × 6h buckets
16
+ '1h': { windowMs: 60 * 60_000, bucketMs: 5 * 60_000, source: 'runs' }, // 12 × 5min buckets
17
+ '24h': { windowMs: 24 * 60 * 60_000, bucketMs: 60 * 60_000, source: 'runs' }, // 24 × 1h
18
+ '7d': { windowMs: 7 * DAY_MS, bucketMs: 6 * 60 * 60_000, source: 'runs' }, // 28 × 6h
19
+ '30d': { windowMs: 30 * DAY_MS, bucketMs: DAY_MS, source: 'daily-rollup' },
20
+ '90d': { windowMs: 90 * DAY_MS, bucketMs: DAY_MS, source: 'daily-rollup' },
9
21
  };
10
22
  /** Reduce the `(kind, status)` outcome rows into per-status totals + the success rate. */
11
- export function summarizeOutcomes(rows) {
23
+ export function summarizeOutcomes(
24
+ // Only `status` + `count` are read, so this also folds the daily-rollup rows, which carry
25
+ // no run kind. Typed structurally rather than against `PlatformRunOutcome` so the two
26
+ // sources share one reduction instead of drifting into two that must agree.
27
+ rows) {
12
28
  const totals = {
13
29
  total: 0,
14
30
  done: 0,
@@ -72,4 +88,70 @@ export function buildTrend(points, since, now, bucketMs) {
72
88
  }
73
89
  return [...byStart.values()].sort((a, b) => a.start - b.start);
74
90
  }
91
+ /**
92
+ * The daily-rollup rows as trend input. The rollup splits a `failed` day into one row per
93
+ * failure kind, so the same day appears several times; `buildTrend` sums them, which is why
94
+ * this is a projection rather than a regrouping.
95
+ */
96
+ export function dailyTrendRows(rows) {
97
+ return rows.map((r) => ({ bucketStart: r.dayStart, status: r.status, count: r.count }));
98
+ }
99
+ /**
100
+ * The failure taxonomy folded out of the daily-rollup rows, most frequent first: the
101
+ * long-window counterpart of `failureKindBreakdown`. Only `failed` rows carry a kind; a row
102
+ * that somehow lacks one is counted as `unknown` rather than dropped, so the slice totals
103
+ * still add up to the window's failure count.
104
+ */
105
+ export function dailyFailureSlices(rows) {
106
+ const byKind = new Map();
107
+ for (const r of rows) {
108
+ if (r.status !== 'failed')
109
+ continue;
110
+ const kind = r.failureKind ?? 'unknown';
111
+ byKind.set(kind, (byKind.get(kind) ?? 0) + r.count);
112
+ }
113
+ return [...byKind.entries()]
114
+ .map(([kind, count]) => ({ kind, count }))
115
+ .sort((a, b) => b.count - a.count || a.kind.localeCompare(b.kind));
116
+ }
117
+ /**
118
+ * Fold the `(gateKind, helperKind, outcome)` buckets into one statistic per gate kind, busiest
119
+ * first. The port groups by outcome because that is one `GROUP BY`; the dashboard reads a gate
120
+ * kind as a single row, so passed/exhausted become columns here rather than rows.
121
+ *
122
+ * `helperKind` is taken from whichever bucket names one: a gate's helper is a property of the
123
+ * gate definition, so the buckets agree, but a gate that passed cleanly every time in the
124
+ * window legitimately recorded no helper at all and must not blank out the label.
125
+ */
126
+ export function summarizeGateOutcomes(rows) {
127
+ const byKind = new Map();
128
+ for (const r of rows) {
129
+ let stat = byKind.get(r.gateKind);
130
+ if (!stat) {
131
+ stat = {
132
+ gateKind: r.gateKind,
133
+ helperKind: null,
134
+ gates: 0,
135
+ passed: 0,
136
+ exhausted: 0,
137
+ cleanPasses: 0,
138
+ attempts: 0,
139
+ helperFailures: 0,
140
+ };
141
+ byKind.set(r.gateKind, stat);
142
+ }
143
+ stat.helperKind ??= r.helperKind;
144
+ stat.gates += r.gates;
145
+ stat.attempts += r.attempts;
146
+ stat.helperFailures += r.helperFailures;
147
+ if (r.outcome === 'passed') {
148
+ stat.passed += r.gates;
149
+ stat.cleanPasses += r.cleanGates;
150
+ }
151
+ else {
152
+ stat.exhausted += r.gates;
153
+ }
154
+ }
155
+ return [...byKind.values()].sort((a, b) => b.gates - a.gates || a.gateKind.localeCompare(b.gateKind));
156
+ }
75
157
  //# sourceMappingURL=platform-observability.logic.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"platform-observability.logic.js","sourceRoot":"","sources":["../../../src/modules/observability/platform-observability.logic.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"platform-observability.logic.js","sourceRoot":"","sources":["../../../src/modules/observability/platform-observability.logic.ts"],"names":[],"mappings":"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"}
@@ -12,6 +12,17 @@ export interface RetentionPass {
12
12
  * tokens, subscription activations), where there is no window to disable.
13
13
  */
14
14
  expire(table: string, del: () => Promise<number>): Promise<number>;
15
+ /**
16
+ * The same isolation for a table this pass WRITES rather than prunes: today the daily run
17
+ * rollup, which is materialised on the same schedule that bounds its neighbours.
18
+ *
19
+ * It shares the isolation for the reason the prunes do: a failing rollup must not abort the
20
+ * prunes that follow it, and it must be NAMED when it fails rather than reported as "0
21
+ * rows", which is also what a fully caught-up rollup returns. Kept a distinct verb from
22
+ * {@link prune} so a reader of the sweep can see at a glance which tables it is bounding and
23
+ * which it is filling.
24
+ */
25
+ materialize(table: string, write: () => Promise<number>): Promise<number>;
15
26
  /**
16
27
  * The tables whose prune threw this pass, in the order they were attempted. EMPTY on a
17
28
  * clean pass — a caller logs it only when non-empty, so a healthy deployment stays quiet.
@@ -23,4 +34,15 @@ export interface RetentionPass {
23
34
  * list is per-pass state), then read `failed` after the last table.
24
35
  */
25
36
  export declare function createRetentionPass(logger?: Logger): RetentionPass;
37
+ /**
38
+ * How far back each rollup pass recomputes the daily run buckets.
39
+ *
40
+ * Not just "today": the sweep that owns it runs daily on the Worker and hourly on Node, so a
41
+ * missed pass (a deploy, an isolate that never fired, a restart) would otherwise leave a day
42
+ * permanently half-counted, and a half-counted day is indistinguishable from a quiet one once
43
+ * it is written. Recomputing a short trailing window makes every pass self-healing, at the
44
+ * cost of re-aggregating a few days of runs, which is one indexed GROUP BY, not a scan of
45
+ * history. Shared by both facades so the two cannot drift on how much they heal.
46
+ */
47
+ export declare const RUN_DAY_ROLLUP_LOOKBACK_MS: number;
26
48
  //# sourceMappingURL=retentionPass.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"retentionPass.d.ts","sourceRoot":"","sources":["../../../src/modules/observability/retentionPass.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAiB,MAAM,qBAAqB,CAAA;AAiBhE,0FAA0F;AAC1F,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,KAAK,CACH,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GACvC,OAAO,CAAC,MAAM,CAAC,CAAA;IAClB;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAClE;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAC1B;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,aAAa,CAwBlE"}
1
+ {"version":3,"file":"retentionPass.d.ts","sourceRoot":"","sources":["../../../src/modules/observability/retentionPass.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAiB,MAAM,qBAAqB,CAAA;AAiBhE,0FAA0F;AAC1F,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,KAAK,CACH,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GACvC,OAAO,CAAC,MAAM,CAAC,CAAA;IAClB;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAClE;;;;;;;;;OASG;IACH,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IACzE;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAC1B;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,aAAa,CAyBlE;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,0BAA0B,QAAuB,CAAA"}
@@ -25,7 +25,19 @@ export function createRetentionPass(logger) {
25
25
  return {
26
26
  prune: (table, windowMs, now, del) => windowMs <= 0 ? Promise.resolve(0) : isolate(table, () => del(now - windowMs)),
27
27
  expire: (table, del) => isolate(table, del),
28
+ materialize: (table, write) => isolate(table, write),
28
29
  failed,
29
30
  };
30
31
  }
32
+ /**
33
+ * How far back each rollup pass recomputes the daily run buckets.
34
+ *
35
+ * Not just "today": the sweep that owns it runs daily on the Worker and hourly on Node, so a
36
+ * missed pass (a deploy, an isolate that never fired, a restart) would otherwise leave a day
37
+ * permanently half-counted, and a half-counted day is indistinguishable from a quiet one once
38
+ * it is written. Recomputing a short trailing window makes every pass self-healing, at the
39
+ * cost of re-aggregating a few days of runs, which is one indexed GROUP BY, not a scan of
40
+ * history. Shared by both facades so the two cannot drift on how much they heal.
41
+ */
42
+ export const RUN_DAY_ROLLUP_LOOKBACK_MS = 3 * 24 * 60 * 60_000;
31
43
  //# sourceMappingURL=retentionPass.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"retentionPass.js","sourceRoot":"","sources":["../../../src/modules/observability/retentionPass.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,aAAa,EAAE,MAAM,qBAAqB,CAAA;AA0ChE;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAe;IACjD,MAAM,MAAM,GAAa,EAAE,CAAA;IAC3B,MAAM,OAAO,GAAG,KAAK,EAAE,KAAa,EAAE,GAA0B,EAAmB,EAAE;QACnF,IAAI,CAAC;YACH,OAAO,MAAM,GAAG,EAAE,CAAA;QACpB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YAClB,yFAAyF;YACzF,uFAAuF;YACvF,6DAA6D;YAC7D,MAAM,EAAE,IAAI,CAAC,0DAA0D,EAAE;gBACvE,KAAK,EAAE,WAAW;gBAClB,KAAK;gBACL,GAAG,aAAa,CAAC,KAAK,CAAC;aACxB,CAAC,CAAA;YACF,OAAO,CAAC,CAAA;QACV,CAAC;IACH,CAAC,CAAA;IACD,OAAO;QACL,KAAK,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,CACnC,QAAQ,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,GAAG,QAAQ,CAAC,CAAC;QAChF,MAAM,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;QAC3C,MAAM;KACP,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"retentionPass.js","sourceRoot":"","sources":["../../../src/modules/observability/retentionPass.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAqDhE;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAe;IACjD,MAAM,MAAM,GAAa,EAAE,CAAA;IAC3B,MAAM,OAAO,GAAG,KAAK,EAAE,KAAa,EAAE,GAA0B,EAAmB,EAAE;QACnF,IAAI,CAAC;YACH,OAAO,MAAM,GAAG,EAAE,CAAA;QACpB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YAClB,yFAAyF;YACzF,uFAAuF;YACvF,6DAA6D;YAC7D,MAAM,EAAE,IAAI,CAAC,0DAA0D,EAAE;gBACvE,KAAK,EAAE,WAAW;gBAClB,KAAK;gBACL,GAAG,aAAa,CAAC,KAAK,CAAC;aACxB,CAAC,CAAA;YACF,OAAO,CAAC,CAAA;QACV,CAAC;IACH,CAAC,CAAA;IACD,OAAO;QACL,KAAK,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,CACnC,QAAQ,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,GAAG,QAAQ,CAAC,CAAC;QAChF,MAAM,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;QAC3C,WAAW,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;QACpD,MAAM;KACP,CAAA;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,MAAM,CAAA"}
@@ -0,0 +1,20 @@
1
+ import type { ExecutionInstance } from '@cat-factory/contracts';
2
+ import type { LlmRunSpan, LlmStepSpan } from '@cat-factory/kernel';
3
+ /** A settled run's root span plus the per-agent-kind step spans that hang under it. */
4
+ export interface RunTraceSpans {
5
+ run: LlmRunSpan;
6
+ steps: LlmStepSpan[];
7
+ }
8
+ /**
9
+ * Build the trace parents for a settled run, or null when the run has no observable extent at
10
+ * all (never stamped with a creation time and never started a step). Null rather than a
11
+ * zero-width span: a run that ran nothing emitted no children, so there is nothing to be the
12
+ * parent of, and inventing a root would put an empty trace in front of an operator.
13
+ *
14
+ * The steps fold by agent KIND rather than by index, because that is the only grain a
15
+ * generation can name (its event carries no step ordinal) and the grain the rest of the LLM
16
+ * telemetry already buckets by. Two `coder` steps in one pipeline therefore become one span
17
+ * covering both, reported as `stepCount: 2` rather than passed off as a single long step.
18
+ */
19
+ export declare function buildRunTraceSpans(workspaceId: string | null, instance: ExecutionInstance): RunTraceSpans | null;
20
+ //# sourceMappingURL=runTraceSpans.logic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runTraceSpans.logic.d.ts","sourceRoot":"","sources":["../../../src/modules/observability/runTraceSpans.logic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAgB,MAAM,wBAAwB,CAAA;AAC7E,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAmClE,uFAAuF;AACvF,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,UAAU,CAAA;IACf,KAAK,EAAE,WAAW,EAAE,CAAA;CACrB;AAwDD;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,QAAQ,EAAE,iBAAiB,GAC1B,aAAa,GAAG,IAAI,CAyEtB"}
@@ -0,0 +1,127 @@
1
+ /** When a step actually began, across every attempt rather than only the one in flight. */
2
+ function stepStartedAt(step) {
3
+ const at = step.firstStartedAt ?? step.startedAt;
4
+ return typeof at === 'number' ? at : null;
5
+ }
6
+ /** Every epoch-ms stamp the run itself recorded, in no particular order. */
7
+ function observedStamps(instance) {
8
+ const stamps = [];
9
+ for (const step of instance.steps) {
10
+ // A step in flight when the run settled carries no `finishedAt` (it is stamped only on the
11
+ // transition to `done`), so its start and its last observed sign of life are what bound it.
12
+ for (const stamp of [stepStartedAt(step), step.finishedAt, step.lastActivityAt]) {
13
+ if (typeof stamp === 'number')
14
+ stamps.push(stamp);
15
+ }
16
+ }
17
+ // The exact instant a FAILED run settled. A `done` run's counterpart is its last step's
18
+ // `finishedAt`, already collected above, so both terminal states are bounded by a fact the
19
+ // run recorded rather than by how long the platform took to notice.
20
+ if (typeof instance.failure?.occurredAt === 'number')
21
+ stamps.push(instance.failure.occurredAt);
22
+ return stamps;
23
+ }
24
+ /**
25
+ * How many times a step dispatched a given kind. Falls back to the step's own start count for
26
+ * its OWN kind, because an inline step (a judge, a consensus panel, a reviewer served without a
27
+ * container) never reaches the dispatch funnel and would otherwise report zero rounds for work
28
+ * that plainly ran.
29
+ */
30
+ function dispatchCount(step, agentKind) {
31
+ const recorded = step.dispatches?.find((d) => d.agentKind === agentKind)?.count;
32
+ if (recorded)
33
+ return recorded;
34
+ return agentKind === step.agentKind ? (step.attempts ?? 1) : 1;
35
+ }
36
+ /** The kinds one step contributed a span for: its own, then any helper it dispatched. */
37
+ function kindsOf(step) {
38
+ const helpers = (step.dispatches ?? [])
39
+ .map((d) => d.agentKind)
40
+ .filter((kind) => kind !== step.agentKind)
41
+ .map((agentKind) => ({ agentKind, parentAgentKind: step.agentKind }));
42
+ return [{ agentKind: step.agentKind }, ...helpers];
43
+ }
44
+ /**
45
+ * Build the trace parents for a settled run, or null when the run has no observable extent at
46
+ * all (never stamped with a creation time and never started a step). Null rather than a
47
+ * zero-width span: a run that ran nothing emitted no children, so there is nothing to be the
48
+ * parent of, and inventing a root would put an empty trace in front of an operator.
49
+ *
50
+ * The steps fold by agent KIND rather than by index, because that is the only grain a
51
+ * generation can name (its event carries no step ordinal) and the grain the rest of the LLM
52
+ * telemetry already buckets by. Two `coder` steps in one pipeline therefore become one span
53
+ * covering both, reported as `stepCount: 2` rather than passed off as a single long step.
54
+ */
55
+ export function buildRunTraceSpans(workspaceId, instance) {
56
+ const started = instance.steps
57
+ .map(stepStartedAt)
58
+ .filter((at) => typeof at === 'number');
59
+ const runStartedAt = instance.createdAt ?? (started.length > 0 ? Math.min(...started) : null);
60
+ if (runStartedAt === null)
61
+ return null;
62
+ // The run's end is the latest thing it was OBSERVED to do. Taking the max against the start
63
+ // also covers a creation stamp that postdates every step, which would otherwise render as a
64
+ // negative-width span.
65
+ const runEndedAt = Math.max(runStartedAt, ...observedStamps(instance));
66
+ const failure = instance.failure ?? null;
67
+ const ok = instance.status === 'done';
68
+ // A failed run whose failure was never attributed to a step (`stepIndex` is optional, and a
69
+ // bootstrap failure has no step to point at) leaves every step span UNSET rather than
70
+ // guessing at one. The root still carries the error, so the failure is not lost; what is
71
+ // withheld is the single thing the run does not know, which is where it happened.
72
+ const failedStepIndex = ok ? null : (failure?.stepIndex ?? null);
73
+ const folds = new Map();
74
+ for (const [index, step] of instance.steps.entries()) {
75
+ // A step that never started contributed no telemetry, so it gets no span. A SKIPPED step is
76
+ // the case that matters: an estimate-gated step is absent from the run's work, and giving
77
+ // it a span would show an operator a step that was deliberately not run.
78
+ const startedAt = stepStartedAt(step);
79
+ if (startedAt === null)
80
+ continue;
81
+ // A helper shares its host step's window. That is not an approximation to apologise for: a
82
+ // parent span is REQUIRED to contain its children, and the helper ran inside the step, so
83
+ // the host's window is the tightest bound the run actually recorded.
84
+ const endedAt = Math.min(runEndedAt, step.finishedAt ?? runEndedAt);
85
+ for (const { agentKind, parentAgentKind } of kindsOf(step)) {
86
+ const existing = folds.get(agentKind);
87
+ folds.set(agentKind, {
88
+ startedAt: Math.min(existing?.startedAt ?? startedAt, startedAt),
89
+ endedAt: Math.max(existing?.endedAt ?? endedAt, endedAt),
90
+ stepCount: (existing?.stepCount ?? 0) + 1,
91
+ attemptCount: (existing?.attemptCount ?? 0) + dispatchCount(step, agentKind),
92
+ // Only the step the run actually failed ON is marked ERROR. Marking every step of a
93
+ // failed run would say the reviewer that passed had failed too.
94
+ failed: (existing?.failed ?? false) || failedStepIndex === index,
95
+ // A kind that is some step's OWN kind keeps its run-level parent even if another step
96
+ // also dispatched it as a helper: one span cannot hang in two places, and the spans
97
+ // underneath it cannot be told apart to split it.
98
+ parentAgentKind: existing ? existing.parentAgentKind : parentAgentKind,
99
+ });
100
+ }
101
+ }
102
+ const steps = [...folds].map(([agentKind, fold]) => ({
103
+ workspaceId,
104
+ executionId: instance.id,
105
+ agentKind,
106
+ ...(fold.parentAgentKind ? { parentAgentKind: fold.parentAgentKind } : {}),
107
+ startedAt: fold.startedAt,
108
+ endedAt: Math.max(fold.startedAt, fold.endedAt),
109
+ stepCount: fold.stepCount,
110
+ attemptCount: fold.attemptCount,
111
+ ok: !fold.failed,
112
+ errorMessage: fold.failed ? (failure?.message ?? null) : null,
113
+ }));
114
+ return {
115
+ run: {
116
+ workspaceId,
117
+ executionId: instance.id,
118
+ pipelineName: instance.pipelineName,
119
+ startedAt: runStartedAt,
120
+ endedAt: runEndedAt,
121
+ ok,
122
+ errorMessage: ok ? null : (failure?.message ?? null),
123
+ },
124
+ steps,
125
+ };
126
+ }
127
+ //# sourceMappingURL=runTraceSpans.logic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runTraceSpans.logic.js","sourceRoot":"","sources":["../../../src/modules/observability/runTraceSpans.logic.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { Clock, CreateScheduleInput, ExecutionEventPublisher, ExecutionRepository, IdGenerator, Logger, PipelineRepository, PipelineSchedule, PipelineScheduleRepository, ScheduleRun, ServiceRepository, TrackerIssueEvent, UpdateScheduleInput, WorkspaceMountRepository, WorkspaceRepository } from '@cat-factory/kernel';
1
+ import type { Clock, CreateScheduleInput, ExecutionEventPublisher, ExecutionRepository, IdGenerator, Logger, PipelineRepository, PipelineSchedule, PipelineScheduleRepository, ScheduleRun, ServiceRepository, TaskSourceKind, TrackerIssueEvent, UpdateScheduleInput, WorkspaceMountRepository, WorkspaceRepository } from '@cat-factory/kernel';
2
2
  import type { BlockRepository } from '@cat-factory/kernel';
3
3
  import { type TaskConnectionService } from '@cat-factory/integrations';
4
4
  import type { ExecutionService } from '../execution/ExecutionService.js';
@@ -40,6 +40,28 @@ export interface RecurringPipelineServiceDependencies {
40
40
  * only "push intake never happens". Absent ⇒ those paths stay silent, as they were.
41
41
  */
42
42
  logger?: Logger;
43
+ /**
44
+ * PER-TICKET dispatch: import a pushed tracker issue and materialise it as its own task inside
45
+ * `containerId`, resolving to the created block — or to `null` when the issue is ALREADY linked
46
+ * to a block, which is how a redelivery is recognised.
47
+ *
48
+ * Declared structurally and bound at the composition root (to `TaskImportService` +
49
+ * `TaskLinkService`) rather than imported, for the layering reason `TrackerWebhookService`
50
+ * states about its own gateway: it keeps the import/link behaviour in the ONE place that owns
51
+ * it, so a per-ticket dispatch and a human's "create task from issue" cannot drift.
52
+ *
53
+ * Absent ⇒ a per-ticket config cannot dispatch and says so in the log; the queue mode is
54
+ * unaffected, so a deployment with no task sources is byte-for-byte unchanged.
55
+ */
56
+ adoptIssueAsTask?: (input: {
57
+ workspaceId: string;
58
+ source: TaskSourceKind;
59
+ externalId: string;
60
+ containerId: string;
61
+ pipelineId: string;
62
+ }) => Promise<{
63
+ blockId: string;
64
+ } | null>;
43
65
  }
44
66
  /**
45
67
  * Manages a workspace's recurring pipelines. Each schedule owns one reused
@@ -62,6 +84,7 @@ export declare class RecurringPipelineService {
62
84
  private readonly taskConnectionService?;
63
85
  private readonly events?;
64
86
  private readonly log;
87
+ private readonly adoptIssueAsTask?;
65
88
  constructor(deps: RecurringPipelineServiceDependencies);
66
89
  private requireWorkspace;
67
90
  /**
@@ -109,24 +132,66 @@ export declare class RecurringPipelineService {
109
132
  * A tracker pushed an issue event: fire every ENABLED schedule in the workspace whose
110
133
  * issue-intake configuration that event plausibly qualifies for. Returns how many started.
111
134
  *
112
- * This is the whole of push-driven intake (D3 of `docs/initiatives/tracker-webhook-intake.md`).
113
- * It imports nothing and links nothing the fired run's `bug-intake` step does all of that
114
- * through the unchanged `BugIntakeService`, so there is exactly ONE intake implementation and a
115
- * pushed pickup is byte-for-byte a cadence pickup that simply happened sooner. Consequently the
116
- * run may legitimately pick a DIFFERENT, older issue than the one that triggered it: intake is
117
- * oldest-first fair queueing, and the webhook's job is to drain the queue promptly, not reorder
118
- * it.
135
+ * This is the whole of push-driven intake (D3 + D8 of
136
+ * `backend/docs/adr/0032-tracker-webhook-intake.md`), and what a qualifying event DOES depends on the
137
+ * schedule's dispatch mode:
138
+ *
139
+ * - **`queue`** (the default, and every pre-existing schedule) imports nothing and links
140
+ * nothing: {@link fire} runs the schedule's reused block and the `bug-intake` step does all of
141
+ * it through the unchanged `BugIntakeService`, so there is exactly ONE intake implementation
142
+ * and a pushed pickup is byte-for-byte a cadence pickup that happened sooner. The run may
143
+ * legitimately pick a DIFFERENT, older issue than the one that triggered it: intake is
144
+ * oldest-first fair queueing, and the webhook's job is to drain the queue promptly, not
145
+ * reorder it.
146
+ * - **`per-ticket`** dispatches the pushed ticket itself ({@link firePerTicket}), which DOES
147
+ * import and link.
148
+ *
149
+ * Neither path is FORCED. `force` is the human run-now lever (it throws on overlap and bypasses
150
+ * the on-demand guard) and a webhook has no human present to unlock a personal credential, which
151
+ * is exactly the situation the unattended-fire guards exist for. So a burst of deliveries cannot
152
+ * start a second run over one already running or parked (`fire` returns false and leaves
153
+ * `nextRunAt` for the sweeper), and an individual-usage model still refuses on BOTH paths.
119
154
  *
120
- * Deliberately NOT forced. `force` is the human run-now lever it throws on overlap and bypasses
121
- * the on-demand guard and a webhook has no human present to unlock a personal credential, which
122
- * is exactly the situation the unattended-fire guards exist for. So an on-demand schedule is never
123
- * webhook-fired, an individual-usage model still refuses, and a burst of deliveries cannot start a
124
- * second run over one that is already running or parked (`fire` returns false and leaves
125
- * `nextRunAt` for the sweeper).
155
+ * The on-demand guard reads differently per mode, and deliberately so. `fire` refuses an
156
+ * on-demand schedule outright, so a queue schedule is never webhook-fired unattended. A
157
+ * per-ticket schedule is REQUIRED to be on-demand (it has no cadence to be driven by), so
158
+ * `firePerTicket` does not consult that guard and applies the individual-usage check itself,
159
+ * against the block it just created rather than a reused one.
126
160
  *
127
161
  * ONE schedule read for the workspace, filtered in memory — never a point-read per schedule.
128
162
  */
129
163
  triggerForIssueEvent(workspaceId: string, event: TrackerIssueEvent): Promise<number>;
164
+ /**
165
+ * PER-TICKET dispatch: import the pushed ticket, materialise it as its own task under the
166
+ * schedule's frame, and start the schedule's pipeline on THAT task. Returns whether a run
167
+ * started.
168
+ *
169
+ * The contrast with {@link fire} is the whole feature. `fire` re-runs one reused block and the
170
+ * run's `bug-intake` step decides what to work; this creates a block per ticket and the ticket
171
+ * IS the work, so a feature request enters the platform from the tracker it was filed in
172
+ * instead of through an API call.
173
+ *
174
+ * Three properties are load-bearing:
175
+ *
176
+ * - **Idempotency is the issue's existing single `linkedBlockId`**, not a new marker. A
177
+ * redelivery (or the `updated` event that follows a `created` one) finds the issue already
178
+ * linked, and `adoptIssueAsTask` reports that as `null`. The link already guarantees what a
179
+ * claim table would have bought. It is read off the row the import returns rather than by
180
+ * catching `createTaskFromIssue`'s conflict, because that refusal is PROSE: matching it would
181
+ * start double-dispatching the day someone rewords the message. A genuine RACE (two deliveries
182
+ * interleaving between that read and the create) still lands on the conflict and propagates,
183
+ * which is correct: the caller's per-schedule isolation logs it and the winner has already
184
+ * dispatched the ticket.
185
+ * - **The unattended-fire guard is the SAME one `fire` applies.** A webhook has no human present
186
+ * to unlock a personal credential, so an individual-usage model must refuse here too — checked
187
+ * against the CREATED block, which is the block that will actually run, rather than the
188
+ * schedule's reused one.
189
+ * - **`origin: 'manual'`**, because this run IS a one-off task run: a dedicated block, started
190
+ * once, never reused. That also makes `assertPipelineLaunchable` refuse a `recurring`-only
191
+ * pipeline, which is exactly right — a `bug-intake` pipeline would go and pick a DIFFERENT
192
+ * ticket — and it is the run-time half of the create-time `assertValidIssueIntake` rule.
193
+ */
194
+ private firePerTicket;
130
195
  /**
131
196
  * Start a schedule's pipeline against its reused block. Finalises the prior run's
132
197
  * history row (its execution is about to be replaced), records a new running row,
@@ -1 +1 @@
1
- {"version":3,"file":"RecurringPipelineService.d.ts","sourceRoot":"","sources":["../../../src/modules/recurring/RecurringPipelineService.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"RecurringPipelineService.d.ts","sourceRoot":"","sources":["../../../src/modules/recurring/RecurringPipelineService.ts"],"names":[],"mappings":"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"}