@cat-factory/contracts 0.166.0 → 0.168.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.
- package/dist/execution.d.ts +78 -0
- package/dist/execution.d.ts.map +1 -1
- package/dist/execution.js +13 -0
- package/dist/execution.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/merge.d.ts +289 -1
- package/dist/merge.d.ts.map +1 -1
- package/dist/merge.js +60 -0
- package/dist/merge.js.map +1 -1
- package/dist/mergeTrackRecord.d.ts +183 -0
- package/dist/mergeTrackRecord.d.ts.map +1 -0
- package/dist/mergeTrackRecord.js +251 -0
- package/dist/mergeTrackRecord.js.map +1 -0
- package/dist/notification-webhooks.d.ts +5 -3
- package/dist/notification-webhooks.d.ts.map +1 -1
- package/dist/notifications.d.ts +42 -3
- package/dist/notifications.d.ts.map +1 -1
- package/dist/notifications.js +33 -1
- package/dist/notifications.js.map +1 -1
- package/dist/public-api.d.ts +3 -1
- package/dist/public-api.d.ts.map +1 -1
- package/dist/reproduction.d.ts +163 -0
- package/dist/reproduction.d.ts.map +1 -0
- package/dist/reproduction.js +166 -0
- package/dist/reproduction.js.map +1 -0
- package/dist/routes/agent-runs.d.ts +56 -0
- package/dist/routes/agent-runs.d.ts.map +1 -1
- package/dist/routes/execution.d.ts +227 -1
- package/dist/routes/execution.d.ts.map +1 -1
- package/dist/routes/execution.js +5 -1
- package/dist/routes/execution.js.map +1 -1
- package/dist/routes/human-review.d.ts +28 -0
- package/dist/routes/human-review.d.ts.map +1 -1
- package/dist/routes/human-test.d.ts +140 -0
- package/dist/routes/human-test.d.ts.map +1 -1
- package/dist/routes/index.d.ts +1 -0
- package/dist/routes/index.d.ts.map +1 -1
- package/dist/routes/index.js +1 -0
- package/dist/routes/index.js.map +1 -1
- package/dist/routes/merge.d.ts +354 -0
- package/dist/routes/merge.d.ts.map +1 -1
- package/dist/routes/mergeTrackRecord.d.ts +113 -0
- package/dist/routes/mergeTrackRecord.d.ts.map +1 -0
- package/dist/routes/mergeTrackRecord.js +33 -0
- package/dist/routes/mergeTrackRecord.js.map +1 -0
- package/dist/routes/notification-webhooks.d.ts +3 -3
- package/dist/routes/notifications.d.ts +12 -4
- package/dist/routes/notifications.d.ts.map +1 -1
- package/dist/routes/notifications.js +4 -2
- package/dist/routes/notifications.js.map +1 -1
- package/dist/routes/public-api.d.ts +9 -3
- package/dist/routes/public-api.d.ts.map +1 -1
- package/dist/routes/slack.d.ts +3 -3
- package/dist/routes/visual-confirm.d.ts +84 -0
- package/dist/routes/visual-confirm.d.ts.map +1 -1
- package/dist/routes/workspaces.d.ts +180 -2
- package/dist/routes/workspaces.d.ts.map +1 -1
- package/dist/slack.d.ts +2 -2
- package/dist/snapshot.d.ts +90 -1
- package/dist/snapshot.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import * as v from 'valibot';
|
|
2
|
+
/**
|
|
3
|
+
* What kind of change a run's pull request made, derived DETERMINISTICALLY on the backend
|
|
4
|
+
* from the PR's changed-file list (never from an agent's opinion). A small closed set so it
|
|
5
|
+
* can key a per-class merge rule, a rollup `GROUP BY`, and an i18n label.
|
|
6
|
+
*
|
|
7
|
+
* - `docs` — documentation + user-facing copy (Markdown, `docs/`, i18n catalogs).
|
|
8
|
+
* - `test` — test/spec/fixture files only.
|
|
9
|
+
* - `dependency` — dependency manifests + lockfiles (a bump).
|
|
10
|
+
* - `config` — CI workflows, tooling config, container/IaC manifests.
|
|
11
|
+
* - `source` — application/library source code (the catch-all for real code).
|
|
12
|
+
* - `schema` — database migrations + schema definitions (the riskiest: irreversible).
|
|
13
|
+
* - `unknown` — no changed-file list was available (no VCS client wired, or an empty diff).
|
|
14
|
+
*
|
|
15
|
+
* A MIXED diff resolves to the HIGHEST-RANKED class present (see `CHANGE_CLASS_RANK` /
|
|
16
|
+
* `classifyChangedFiles` in `@cat-factory/kernel`), so a per-class rule can only ever fire on a
|
|
17
|
+
* diff containing nothing riskier than that class.
|
|
18
|
+
*/
|
|
19
|
+
export declare const changeClassSchema: v.PicklistSchema<["docs", "test", "dependency", "config", "source", "schema", "unknown"], undefined>;
|
|
20
|
+
export type ChangeClass = v.InferOutput<typeof changeClassSchema>;
|
|
21
|
+
/**
|
|
22
|
+
* Every {@link ChangeClass} in rank order (lowest → highest risk), `unknown` last. The
|
|
23
|
+
* ordering the preset editor renders and the rollup table iterates, so a new class is added
|
|
24
|
+
* in ONE place. `unknown` is deliberately last: it is a fallback outcome, not a policy target.
|
|
25
|
+
*/
|
|
26
|
+
export declare const CHANGE_CLASSES: readonly ["docs", "test", "dependency", "config", "source", "schema", "unknown"];
|
|
27
|
+
/**
|
|
28
|
+
* The classes a per-class merge RULE may be authored for — every class except `unknown`.
|
|
29
|
+
* `unknown` must stay inert: an unclassifiable diff (a transient VCS outage) has to fall
|
|
30
|
+
* back to the score thresholds rather than silently adopt a widened policy.
|
|
31
|
+
*/
|
|
32
|
+
export declare const RULEABLE_CHANGE_CLASSES: readonly ["docs", "test", "dependency", "config", "source", "schema"];
|
|
33
|
+
export type RuleableChangeClass = (typeof RULEABLE_CHANGE_CLASSES)[number];
|
|
34
|
+
/**
|
|
35
|
+
* How much review effort a human actually spent on a pull request before merging it — the
|
|
36
|
+
* ground truth the auto-merge score thresholds are trying to approximate.
|
|
37
|
+
*
|
|
38
|
+
* - `none` — merged with zero blocking comments.
|
|
39
|
+
* - `minor` — small comments / a nit pass, no substantive rework.
|
|
40
|
+
* - `major` — real rework was required.
|
|
41
|
+
*
|
|
42
|
+
* ALWAYS optional: an untagged merge records a null tag and every consumer treats null as
|
|
43
|
+
* "not tagged", never as `none`.
|
|
44
|
+
*/
|
|
45
|
+
export declare const reviewEffortSchema: v.PicklistSchema<["none", "minor", "major"], undefined>;
|
|
46
|
+
export type ReviewEffort = v.InferOutput<typeof reviewEffortSchema>;
|
|
47
|
+
/** Every {@link ReviewEffort} in ascending order — the chip order the SPA renders. */
|
|
48
|
+
export declare const REVIEW_EFFORTS: readonly ["none", "minor", "major"];
|
|
49
|
+
/**
|
|
50
|
+
* What ultimately happened to the pull request a track record covers.
|
|
51
|
+
*
|
|
52
|
+
* - `pending_review` — the merger step routed it to a human; not yet resolved. Recorded at
|
|
53
|
+
* the decision so the class + scores are captured even if the human never comes back.
|
|
54
|
+
* - `auto_merged` — the engine merged it on the policy (thresholds or a class rule).
|
|
55
|
+
* - `human_merged` — a human confirmed the merge THROUGH cat-factory (a notification act
|
|
56
|
+
* or the inspector's merge control).
|
|
57
|
+
* - `external_merged` — a human merged it directly on the VCS provider, bypassing
|
|
58
|
+
* cat-factory; detected from the webhook ingest.
|
|
59
|
+
* - `rejected` — a human dismissed the review card instead of merging.
|
|
60
|
+
*/
|
|
61
|
+
export declare const mergeTrackDecisionSchema: v.PicklistSchema<["pending_review", "auto_merged", "human_merged", "external_merged", "rejected"], undefined>;
|
|
62
|
+
export type MergeTrackDecision = v.InferOutput<typeof mergeTrackDecisionSchema>;
|
|
63
|
+
/**
|
|
64
|
+
* The decisions that represent a pull request that actually LANDED. The auto-merge share a
|
|
65
|
+
* rollup reports is `autoMerged / (every landed decision)` — a `pending_review` row is not in
|
|
66
|
+
* the denominator (nothing was decided yet) and neither is a `rejected` one.
|
|
67
|
+
*/
|
|
68
|
+
export declare const MERGED_TRACK_DECISIONS: readonly ["auto_merged", "human_merged", "external_merged"];
|
|
69
|
+
/**
|
|
70
|
+
* One persisted merge decision: what kind of change it was, what the merger scored it, what
|
|
71
|
+
* the engine/human did, and how much review effort it needed. Written best-effort off the
|
|
72
|
+
* merge path — a failure to write one NEVER blocks or fails a merge.
|
|
73
|
+
*
|
|
74
|
+
* Provider-neutral by construction: the repo identity is `repoId` + `provider` (the kernel
|
|
75
|
+
* `VcsRepoRef`/`VcsProvider` vocabulary), never a GitHub-shaped `githubId`/`installationId`.
|
|
76
|
+
*/
|
|
77
|
+
export declare const mergeTrackRecordSchema: v.ObjectSchema<{
|
|
78
|
+
/**
|
|
79
|
+
* Deterministic id, derived from the run: `mtr_<executionId>`. Deterministic so a durable-driver
|
|
80
|
+
* replay of a settled merger step collides with the row it already wrote (first write wins)
|
|
81
|
+
* instead of duplicating it.
|
|
82
|
+
*/
|
|
83
|
+
readonly id: v.StringSchema<undefined>;
|
|
84
|
+
/** The task/block whose pull request this covers. */
|
|
85
|
+
readonly blockId: v.StringSchema<undefined>;
|
|
86
|
+
/**
|
|
87
|
+
* The run that produced the PR. Nullable at the wire/storage level because the column is, but
|
|
88
|
+
* every record written today is born at a merge DECISION POINT inside a run, so it is set. An
|
|
89
|
+
* external merge does not MINT a record — it settles the one the run already wrote (which is
|
|
90
|
+
* also why attribution keys on `(repoId, prNumber)`); a PR cat-factory never opened has no
|
|
91
|
+
* record and is correctly invisible to the track record.
|
|
92
|
+
*/
|
|
93
|
+
readonly executionId: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
94
|
+
/** The deterministic, path-derived change class (see {@link changeClassSchema}). */
|
|
95
|
+
readonly changeClass: v.PicklistSchema<["docs", "test", "dependency", "config", "source", "schema", "unknown"], undefined>;
|
|
96
|
+
/** How many files the PR changed, when the changed-file list was readable. */
|
|
97
|
+
readonly changedFileCount: v.NullableSchema<v.NumberSchema<undefined>, undefined>;
|
|
98
|
+
/** The merger's complexity score at the decision, when it produced a parseable assessment. */
|
|
99
|
+
readonly complexity: v.NullableSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.MinValueAction<number, 0, undefined>, v.MaxValueAction<number, 1, undefined>]>, undefined>;
|
|
100
|
+
/** The merger's risk score at the decision, when one was produced. */
|
|
101
|
+
readonly risk: v.NullableSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.MinValueAction<number, 0, undefined>, v.MaxValueAction<number, 1, undefined>]>, undefined>;
|
|
102
|
+
/** The merger's impact score at the decision, when one was produced. */
|
|
103
|
+
readonly impact: v.NullableSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.MinValueAction<number, 0, undefined>, v.MaxValueAction<number, 1, undefined>]>, undefined>;
|
|
104
|
+
/** The merge preset the decision was made against, for reading a record back in context. */
|
|
105
|
+
readonly riskPolicyId: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
106
|
+
readonly riskPolicyName: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
107
|
+
/** What happened (see {@link mergeTrackDecisionSchema}). */
|
|
108
|
+
readonly decision: v.PicklistSchema<["pending_review", "auto_merged", "human_merged", "external_merged", "rejected"], undefined>;
|
|
109
|
+
/** The human's effort tag — null until somebody tags it. Tagging is never mandatory. */
|
|
110
|
+
readonly reviewEffort: v.NullableSchema<v.PicklistSchema<["none", "minor", "major"], undefined>, undefined>;
|
|
111
|
+
/** The PR number within its repo, when known. */
|
|
112
|
+
readonly prNumber: v.NullableSchema<v.NumberSchema<undefined>, undefined>;
|
|
113
|
+
/** The PR's web URL, for the card's deep link. */
|
|
114
|
+
readonly prUrl: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
115
|
+
/** Provider-neutral repo id the PR belongs to (`VcsRepoRef.repoId`). */
|
|
116
|
+
readonly repoId: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
117
|
+
/** Which VCS provider hosts it — so a mixed-provider workspace reads back correctly. */
|
|
118
|
+
readonly provider: v.NullableSchema<v.PicklistSchema<["github", "gitlab"], undefined>, undefined>;
|
|
119
|
+
/** Epoch ms the decision was recorded (the merger step's resolution). */
|
|
120
|
+
readonly createdAt: v.NumberSchema<undefined>;
|
|
121
|
+
/** Epoch ms the decision reached a terminal value; null while `pending_review`. */
|
|
122
|
+
readonly resolvedAt: v.NullableSchema<v.NumberSchema<undefined>, undefined>;
|
|
123
|
+
/** Epoch ms the effort tag was recorded; null while untagged. */
|
|
124
|
+
readonly taggedAt: v.NullableSchema<v.NumberSchema<undefined>, undefined>;
|
|
125
|
+
}, undefined>;
|
|
126
|
+
export type MergeTrackRecord = v.InferOutput<typeof mergeTrackRecordSchema>;
|
|
127
|
+
/** The per-effort-level counts within a class's rollup. `untagged` counts null tags. */
|
|
128
|
+
export declare const reviewEffortDistributionSchema: v.ObjectSchema<{
|
|
129
|
+
readonly none: v.NumberSchema<undefined>;
|
|
130
|
+
readonly minor: v.NumberSchema<undefined>;
|
|
131
|
+
readonly major: v.NumberSchema<undefined>;
|
|
132
|
+
readonly untagged: v.NumberSchema<undefined>;
|
|
133
|
+
}, undefined>;
|
|
134
|
+
export type ReviewEffortDistribution = v.InferOutput<typeof reviewEffortDistributionSchema>;
|
|
135
|
+
/** One change class's accumulated track record — the number that justifies widening a rule. */
|
|
136
|
+
export declare const mergeClassRollupSchema: v.ObjectSchema<{
|
|
137
|
+
readonly changeClass: v.PicklistSchema<["docs", "test", "dependency", "config", "source", "schema", "unknown"], undefined>;
|
|
138
|
+
/** Every record in the class, including still-`pending_review` ones. */
|
|
139
|
+
readonly total: v.NumberSchema<undefined>;
|
|
140
|
+
/** Records whose PR landed (auto + human + external) — the auto-merge-share denominator. */
|
|
141
|
+
readonly merged: v.NumberSchema<undefined>;
|
|
142
|
+
readonly autoMerged: v.NumberSchema<undefined>;
|
|
143
|
+
readonly humanMerged: v.NumberSchema<undefined>;
|
|
144
|
+
readonly externalMerged: v.NumberSchema<undefined>;
|
|
145
|
+
readonly rejected: v.NumberSchema<undefined>;
|
|
146
|
+
readonly pendingReview: v.NumberSchema<undefined>;
|
|
147
|
+
readonly effort: v.ObjectSchema<{
|
|
148
|
+
readonly none: v.NumberSchema<undefined>;
|
|
149
|
+
readonly minor: v.NumberSchema<undefined>;
|
|
150
|
+
readonly major: v.NumberSchema<undefined>;
|
|
151
|
+
readonly untagged: v.NumberSchema<undefined>;
|
|
152
|
+
}, undefined>;
|
|
153
|
+
}, undefined>;
|
|
154
|
+
export type MergeClassRollup = v.InferOutput<typeof mergeClassRollupSchema>;
|
|
155
|
+
/**
|
|
156
|
+
* The auto-merge share of a class's LANDED records, 0..1, or null when nothing has landed
|
|
157
|
+
* yet (so the UI shows "no data" rather than a misleading 0%). Shared by the preset editor
|
|
158
|
+
* and any backend consumer so the two can never disagree.
|
|
159
|
+
*/
|
|
160
|
+
export declare function autoMergeShare(rollup: MergeClassRollup): number | null;
|
|
161
|
+
/**
|
|
162
|
+
* The share of a class's TAGGED records that needed no blocking comments, 0..1, or null when
|
|
163
|
+
* nothing in the class has been tagged. This — not the auto-merge share — is the evidence that
|
|
164
|
+
* justifies flipping a class to `always`: it says humans found nothing to say.
|
|
165
|
+
*/
|
|
166
|
+
export declare function frictionlessShare(rollup: MergeClassRollup): number | null;
|
|
167
|
+
/** An empty rollup for a class with no records yet (the UI renders every class, always). */
|
|
168
|
+
export declare function emptyMergeClassRollup(changeClass: ChangeClass): MergeClassRollup;
|
|
169
|
+
/**
|
|
170
|
+
* Body of `POST /blocks/:blockId/merge` — the inspector's merge control. Every field is optional,
|
|
171
|
+
* so `{}` is the historical no-body merge; `reviewEffort` records the effort tag onto the block's
|
|
172
|
+
* merge track record in the same request (the counterpart of the notification card's `act` body).
|
|
173
|
+
*/
|
|
174
|
+
export declare const mergeBlockSchema: v.ObjectSchema<{
|
|
175
|
+
readonly reviewEffort: v.OptionalSchema<v.NullableSchema<v.PicklistSchema<["none", "minor", "major"], undefined>, undefined>, undefined>;
|
|
176
|
+
}, undefined>;
|
|
177
|
+
export type MergeBlockInput = v.InferOutput<typeof mergeBlockSchema>;
|
|
178
|
+
/** Tag a record with the review effort a human actually spent (`null` clears the tag). */
|
|
179
|
+
export declare const tagReviewEffortSchema: v.ObjectSchema<{
|
|
180
|
+
readonly reviewEffort: v.NullableSchema<v.PicklistSchema<["none", "minor", "major"], undefined>, undefined>;
|
|
181
|
+
}, undefined>;
|
|
182
|
+
export type TagReviewEffortInput = v.InferOutput<typeof tagReviewEffortSchema>;
|
|
183
|
+
//# sourceMappingURL=mergeTrackRecord.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mergeTrackRecord.d.ts","sourceRoot":"","sources":["../src/mergeTrackRecord.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAwB5B;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,iBAAiB,sGAQ5B,CAAA;AACF,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,iBAAiB,CAAC,CAAA;AAEjE;;;;GAIG;AACH,eAAO,MAAM,cAAc,kFAQgB,CAAA;AAE3C;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,uEAOO,CAAA;AAE3C,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,CAAC,CAAA;AAE1E;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,yDAAyC,CAAA;AACxE,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,kBAAkB,CAAC,CAAA;AAEnE,sFAAsF;AACtF,eAAO,MAAM,cAAc,qCAAwE,CAAA;AAEnG;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,wBAAwB,+GAMnC,CAAA;AACF,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,wBAAwB,CAAC,CAAA;AAE/E;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,6DAIe,CAAA;AAIlD;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB;IACjC;;;;OAIG;;IAEH,qDAAqD;;IAErD;;;;;;OAMG;;IAEH,oFAAoF;;IAEpF,8EAA8E;;IAE9E,8FAA8F;;IAE9F,sEAAsE;;IAEtE,wEAAwE;;IAExE,4FAA4F;;;IAG5F,4DAA4D;;IAE5D,wFAAwF;;IAExF,iDAAiD;;IAEjD,kDAAkD;;IAElD,wEAAwE;;IAExE,wFAAwF;;IAExF,yEAAyE;;IAEzE,mFAAmF;;IAEnF,iEAAiE;;aAEjE,CAAA;AACF,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,sBAAsB,CAAC,CAAA;AAQ3E,wFAAwF;AACxF,eAAO,MAAM,8BAA8B;;;;;aAKzC,CAAA;AACF,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,8BAA8B,CAAC,CAAA;AAE3F,+FAA+F;AAC/F,eAAO,MAAM,sBAAsB;;IAEjC,wEAAwE;;IAExE,4FAA4F;;;;;;;;;;;;;aAQ5F,CAAA;AACF,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,sBAAsB,CAAC,CAAA;AAE3E;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,GAAG,IAAI,CAGtE;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,GAAG,IAAI,CAIzE;AAED,4FAA4F;AAC5F,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,WAAW,GAAG,gBAAgB,CAYhF;AAID;;;;GAIG;AACH,eAAO,MAAM,gBAAgB;;aAE3B,CAAA;AACF,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,gBAAgB,CAAC,CAAA;AAEpE,0FAA0F;AAC1F,eAAO,MAAM,qBAAqB;;aAEhC,CAAA;AACF,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,qBAAqB,CAAC,CAAA"}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import * as v from 'valibot';
|
|
2
|
+
import { vcsProviderSchema } from './routes/auth.js';
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// Merge TRACK RECORD wire contracts — the accumulated human evidence behind the
|
|
5
|
+
// auto-merge policy.
|
|
6
|
+
//
|
|
7
|
+
// The merge decision itself runs on the `merger` agent's self-assessment (see
|
|
8
|
+
// `merge.ts`): scores vs the task's preset ceilings. This module models what the
|
|
9
|
+
// decision LEAVES BEHIND so the policy can eventually stand on evidence instead:
|
|
10
|
+
//
|
|
11
|
+
// - WHAT KIND of change it was (a deterministic, path-derived {@link ChangeClass}),
|
|
12
|
+
// - the merger's scores at the moment of the decision,
|
|
13
|
+
// - how much review effort a human actually spent ({@link ReviewEffort}, nullable
|
|
14
|
+
// until somebody tags it — tagging is never mandatory),
|
|
15
|
+
// - and what happened ({@link MergeTrackDecision}).
|
|
16
|
+
//
|
|
17
|
+
// Per-class rollups of those records are what justify WIDENING a policy, which is
|
|
18
|
+
// why merge presets carry per-class rules ({@link MergeClassRules} in `merge.ts`).
|
|
19
|
+
//
|
|
20
|
+
// Full design — the class precedence rules, the external-merge detection, and the
|
|
21
|
+
// preset reshape — is in `docs/initiatives/merge-track-record.md`.
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
/**
|
|
24
|
+
* What kind of change a run's pull request made, derived DETERMINISTICALLY on the backend
|
|
25
|
+
* from the PR's changed-file list (never from an agent's opinion). A small closed set so it
|
|
26
|
+
* can key a per-class merge rule, a rollup `GROUP BY`, and an i18n label.
|
|
27
|
+
*
|
|
28
|
+
* - `docs` — documentation + user-facing copy (Markdown, `docs/`, i18n catalogs).
|
|
29
|
+
* - `test` — test/spec/fixture files only.
|
|
30
|
+
* - `dependency` — dependency manifests + lockfiles (a bump).
|
|
31
|
+
* - `config` — CI workflows, tooling config, container/IaC manifests.
|
|
32
|
+
* - `source` — application/library source code (the catch-all for real code).
|
|
33
|
+
* - `schema` — database migrations + schema definitions (the riskiest: irreversible).
|
|
34
|
+
* - `unknown` — no changed-file list was available (no VCS client wired, or an empty diff).
|
|
35
|
+
*
|
|
36
|
+
* A MIXED diff resolves to the HIGHEST-RANKED class present (see `CHANGE_CLASS_RANK` /
|
|
37
|
+
* `classifyChangedFiles` in `@cat-factory/kernel`), so a per-class rule can only ever fire on a
|
|
38
|
+
* diff containing nothing riskier than that class.
|
|
39
|
+
*/
|
|
40
|
+
export const changeClassSchema = v.picklist([
|
|
41
|
+
'docs',
|
|
42
|
+
'test',
|
|
43
|
+
'dependency',
|
|
44
|
+
'config',
|
|
45
|
+
'source',
|
|
46
|
+
'schema',
|
|
47
|
+
'unknown',
|
|
48
|
+
]);
|
|
49
|
+
/**
|
|
50
|
+
* Every {@link ChangeClass} in rank order (lowest → highest risk), `unknown` last. The
|
|
51
|
+
* ordering the preset editor renders and the rollup table iterates, so a new class is added
|
|
52
|
+
* in ONE place. `unknown` is deliberately last: it is a fallback outcome, not a policy target.
|
|
53
|
+
*/
|
|
54
|
+
export const CHANGE_CLASSES = [
|
|
55
|
+
'docs',
|
|
56
|
+
'test',
|
|
57
|
+
'dependency',
|
|
58
|
+
'config',
|
|
59
|
+
'source',
|
|
60
|
+
'schema',
|
|
61
|
+
'unknown',
|
|
62
|
+
];
|
|
63
|
+
/**
|
|
64
|
+
* The classes a per-class merge RULE may be authored for — every class except `unknown`.
|
|
65
|
+
* `unknown` must stay inert: an unclassifiable diff (a transient VCS outage) has to fall
|
|
66
|
+
* back to the score thresholds rather than silently adopt a widened policy.
|
|
67
|
+
*/
|
|
68
|
+
export const RULEABLE_CHANGE_CLASSES = [
|
|
69
|
+
'docs',
|
|
70
|
+
'test',
|
|
71
|
+
'dependency',
|
|
72
|
+
'config',
|
|
73
|
+
'source',
|
|
74
|
+
'schema',
|
|
75
|
+
];
|
|
76
|
+
/**
|
|
77
|
+
* How much review effort a human actually spent on a pull request before merging it — the
|
|
78
|
+
* ground truth the auto-merge score thresholds are trying to approximate.
|
|
79
|
+
*
|
|
80
|
+
* - `none` — merged with zero blocking comments.
|
|
81
|
+
* - `minor` — small comments / a nit pass, no substantive rework.
|
|
82
|
+
* - `major` — real rework was required.
|
|
83
|
+
*
|
|
84
|
+
* ALWAYS optional: an untagged merge records a null tag and every consumer treats null as
|
|
85
|
+
* "not tagged", never as `none`.
|
|
86
|
+
*/
|
|
87
|
+
export const reviewEffortSchema = v.picklist(['none', 'minor', 'major']);
|
|
88
|
+
/** Every {@link ReviewEffort} in ascending order — the chip order the SPA renders. */
|
|
89
|
+
export const REVIEW_EFFORTS = ['none', 'minor', 'major'];
|
|
90
|
+
/**
|
|
91
|
+
* What ultimately happened to the pull request a track record covers.
|
|
92
|
+
*
|
|
93
|
+
* - `pending_review` — the merger step routed it to a human; not yet resolved. Recorded at
|
|
94
|
+
* the decision so the class + scores are captured even if the human never comes back.
|
|
95
|
+
* - `auto_merged` — the engine merged it on the policy (thresholds or a class rule).
|
|
96
|
+
* - `human_merged` — a human confirmed the merge THROUGH cat-factory (a notification act
|
|
97
|
+
* or the inspector's merge control).
|
|
98
|
+
* - `external_merged` — a human merged it directly on the VCS provider, bypassing
|
|
99
|
+
* cat-factory; detected from the webhook ingest.
|
|
100
|
+
* - `rejected` — a human dismissed the review card instead of merging.
|
|
101
|
+
*/
|
|
102
|
+
export const mergeTrackDecisionSchema = v.picklist([
|
|
103
|
+
'pending_review',
|
|
104
|
+
'auto_merged',
|
|
105
|
+
'human_merged',
|
|
106
|
+
'external_merged',
|
|
107
|
+
'rejected',
|
|
108
|
+
]);
|
|
109
|
+
/**
|
|
110
|
+
* The decisions that represent a pull request that actually LANDED. The auto-merge share a
|
|
111
|
+
* rollup reports is `autoMerged / (every landed decision)` — a `pending_review` row is not in
|
|
112
|
+
* the denominator (nothing was decided yet) and neither is a `rejected` one.
|
|
113
|
+
*/
|
|
114
|
+
export const MERGED_TRACK_DECISIONS = [
|
|
115
|
+
'auto_merged',
|
|
116
|
+
'human_merged',
|
|
117
|
+
'external_merged',
|
|
118
|
+
];
|
|
119
|
+
const scoreSchema = v.pipe(v.number(), v.minValue(0), v.maxValue(1));
|
|
120
|
+
/**
|
|
121
|
+
* One persisted merge decision: what kind of change it was, what the merger scored it, what
|
|
122
|
+
* the engine/human did, and how much review effort it needed. Written best-effort off the
|
|
123
|
+
* merge path — a failure to write one NEVER blocks or fails a merge.
|
|
124
|
+
*
|
|
125
|
+
* Provider-neutral by construction: the repo identity is `repoId` + `provider` (the kernel
|
|
126
|
+
* `VcsRepoRef`/`VcsProvider` vocabulary), never a GitHub-shaped `githubId`/`installationId`.
|
|
127
|
+
*/
|
|
128
|
+
export const mergeTrackRecordSchema = v.object({
|
|
129
|
+
/**
|
|
130
|
+
* Deterministic id, derived from the run: `mtr_<executionId>`. Deterministic so a durable-driver
|
|
131
|
+
* replay of a settled merger step collides with the row it already wrote (first write wins)
|
|
132
|
+
* instead of duplicating it.
|
|
133
|
+
*/
|
|
134
|
+
id: v.string(),
|
|
135
|
+
/** The task/block whose pull request this covers. */
|
|
136
|
+
blockId: v.string(),
|
|
137
|
+
/**
|
|
138
|
+
* The run that produced the PR. Nullable at the wire/storage level because the column is, but
|
|
139
|
+
* every record written today is born at a merge DECISION POINT inside a run, so it is set. An
|
|
140
|
+
* external merge does not MINT a record — it settles the one the run already wrote (which is
|
|
141
|
+
* also why attribution keys on `(repoId, prNumber)`); a PR cat-factory never opened has no
|
|
142
|
+
* record and is correctly invisible to the track record.
|
|
143
|
+
*/
|
|
144
|
+
executionId: v.nullable(v.string()),
|
|
145
|
+
/** The deterministic, path-derived change class (see {@link changeClassSchema}). */
|
|
146
|
+
changeClass: changeClassSchema,
|
|
147
|
+
/** How many files the PR changed, when the changed-file list was readable. */
|
|
148
|
+
changedFileCount: v.nullable(v.number()),
|
|
149
|
+
/** The merger's complexity score at the decision, when it produced a parseable assessment. */
|
|
150
|
+
complexity: v.nullable(scoreSchema),
|
|
151
|
+
/** The merger's risk score at the decision, when one was produced. */
|
|
152
|
+
risk: v.nullable(scoreSchema),
|
|
153
|
+
/** The merger's impact score at the decision, when one was produced. */
|
|
154
|
+
impact: v.nullable(scoreSchema),
|
|
155
|
+
/** The merge preset the decision was made against, for reading a record back in context. */
|
|
156
|
+
riskPolicyId: v.nullable(v.string()),
|
|
157
|
+
riskPolicyName: v.nullable(v.string()),
|
|
158
|
+
/** What happened (see {@link mergeTrackDecisionSchema}). */
|
|
159
|
+
decision: mergeTrackDecisionSchema,
|
|
160
|
+
/** The human's effort tag — null until somebody tags it. Tagging is never mandatory. */
|
|
161
|
+
reviewEffort: v.nullable(reviewEffortSchema),
|
|
162
|
+
/** The PR number within its repo, when known. */
|
|
163
|
+
prNumber: v.nullable(v.number()),
|
|
164
|
+
/** The PR's web URL, for the card's deep link. */
|
|
165
|
+
prUrl: v.nullable(v.string()),
|
|
166
|
+
/** Provider-neutral repo id the PR belongs to (`VcsRepoRef.repoId`). */
|
|
167
|
+
repoId: v.nullable(v.string()),
|
|
168
|
+
/** Which VCS provider hosts it — so a mixed-provider workspace reads back correctly. */
|
|
169
|
+
provider: v.nullable(vcsProviderSchema),
|
|
170
|
+
/** Epoch ms the decision was recorded (the merger step's resolution). */
|
|
171
|
+
createdAt: v.number(),
|
|
172
|
+
/** Epoch ms the decision reached a terminal value; null while `pending_review`. */
|
|
173
|
+
resolvedAt: v.nullable(v.number()),
|
|
174
|
+
/** Epoch ms the effort tag was recorded; null while untagged. */
|
|
175
|
+
taggedAt: v.nullable(v.number()),
|
|
176
|
+
});
|
|
177
|
+
// ---------------------------------------------------------------------------
|
|
178
|
+
// Per-class rollups — SQL aggregates (COUNT / GROUP BY / conditional SUM), never
|
|
179
|
+
// rows loaded and reduced in JS. ONE query per workspace returns every class, so
|
|
180
|
+
// the preset editor's stats load in a single round-trip.
|
|
181
|
+
// ---------------------------------------------------------------------------
|
|
182
|
+
/** The per-effort-level counts within a class's rollup. `untagged` counts null tags. */
|
|
183
|
+
export const reviewEffortDistributionSchema = v.object({
|
|
184
|
+
none: v.number(),
|
|
185
|
+
minor: v.number(),
|
|
186
|
+
major: v.number(),
|
|
187
|
+
untagged: v.number(),
|
|
188
|
+
});
|
|
189
|
+
/** One change class's accumulated track record — the number that justifies widening a rule. */
|
|
190
|
+
export const mergeClassRollupSchema = v.object({
|
|
191
|
+
changeClass: changeClassSchema,
|
|
192
|
+
/** Every record in the class, including still-`pending_review` ones. */
|
|
193
|
+
total: v.number(),
|
|
194
|
+
/** Records whose PR landed (auto + human + external) — the auto-merge-share denominator. */
|
|
195
|
+
merged: v.number(),
|
|
196
|
+
autoMerged: v.number(),
|
|
197
|
+
humanMerged: v.number(),
|
|
198
|
+
externalMerged: v.number(),
|
|
199
|
+
rejected: v.number(),
|
|
200
|
+
pendingReview: v.number(),
|
|
201
|
+
effort: reviewEffortDistributionSchema,
|
|
202
|
+
});
|
|
203
|
+
/**
|
|
204
|
+
* The auto-merge share of a class's LANDED records, 0..1, or null when nothing has landed
|
|
205
|
+
* yet (so the UI shows "no data" rather than a misleading 0%). Shared by the preset editor
|
|
206
|
+
* and any backend consumer so the two can never disagree.
|
|
207
|
+
*/
|
|
208
|
+
export function autoMergeShare(rollup) {
|
|
209
|
+
if (rollup.merged === 0)
|
|
210
|
+
return null;
|
|
211
|
+
return rollup.autoMerged / rollup.merged;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* The share of a class's TAGGED records that needed no blocking comments, 0..1, or null when
|
|
215
|
+
* nothing in the class has been tagged. This — not the auto-merge share — is the evidence that
|
|
216
|
+
* justifies flipping a class to `always`: it says humans found nothing to say.
|
|
217
|
+
*/
|
|
218
|
+
export function frictionlessShare(rollup) {
|
|
219
|
+
const tagged = rollup.effort.none + rollup.effort.minor + rollup.effort.major;
|
|
220
|
+
if (tagged === 0)
|
|
221
|
+
return null;
|
|
222
|
+
return rollup.effort.none / tagged;
|
|
223
|
+
}
|
|
224
|
+
/** An empty rollup for a class with no records yet (the UI renders every class, always). */
|
|
225
|
+
export function emptyMergeClassRollup(changeClass) {
|
|
226
|
+
return {
|
|
227
|
+
changeClass,
|
|
228
|
+
total: 0,
|
|
229
|
+
merged: 0,
|
|
230
|
+
autoMerged: 0,
|
|
231
|
+
humanMerged: 0,
|
|
232
|
+
externalMerged: 0,
|
|
233
|
+
rejected: 0,
|
|
234
|
+
pendingReview: 0,
|
|
235
|
+
effort: { none: 0, minor: 0, major: 0, untagged: 0 },
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
// ---- Request bodies -------------------------------------------------------
|
|
239
|
+
/**
|
|
240
|
+
* Body of `POST /blocks/:blockId/merge` — the inspector's merge control. Every field is optional,
|
|
241
|
+
* so `{}` is the historical no-body merge; `reviewEffort` records the effort tag onto the block's
|
|
242
|
+
* merge track record in the same request (the counterpart of the notification card's `act` body).
|
|
243
|
+
*/
|
|
244
|
+
export const mergeBlockSchema = v.object({
|
|
245
|
+
reviewEffort: v.optional(v.nullable(reviewEffortSchema)),
|
|
246
|
+
});
|
|
247
|
+
/** Tag a record with the review effort a human actually spent (`null` clears the tag). */
|
|
248
|
+
export const tagReviewEffortSchema = v.object({
|
|
249
|
+
reviewEffort: v.nullable(reviewEffortSchema),
|
|
250
|
+
});
|
|
251
|
+
//# sourceMappingURL=mergeTrackRecord.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mergeTrackRecord.js","sourceRoot":"","sources":["../src/mergeTrackRecord.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,7 +8,7 @@ export declare const notificationWebhookSchema: v.ObjectSchema<{
|
|
|
8
8
|
* headless overseer cares about — rather than "everything", so registering an endpoint can't
|
|
9
9
|
* accidentally fire-hose every card at an integration that only wanted to hear about parks.
|
|
10
10
|
*/
|
|
11
|
-
readonly types: v.ArraySchema<v.PicklistSchema<["merge_review", "pipeline_complete", "ci_failed", "test_failed", "requirement_review", "clarity_review", "release_regression", "decision_required", "human_test_ready", "visual_confirmation_ready", "human_review", "followup_pending", "fork_decision_pending", "pr_review_ready", "initiative", "platform_health", "budget_paused", "key_drift"], undefined>, undefined>;
|
|
11
|
+
readonly types: v.ArraySchema<v.PicklistSchema<["merge_review", "pipeline_complete", "ci_failed", "test_failed", "requirement_review", "clarity_review", "release_regression", "decision_required", "human_test_ready", "visual_confirmation_ready", "human_review", "followup_pending", "fork_decision_pending", "pr_review_ready", "initiative", "platform_health", "budget_paused", "key_drift", "merge_tag_request"], undefined>, undefined>;
|
|
12
12
|
/** Whether deliveries are currently attempted. Registering an endpoint enables it. */
|
|
13
13
|
readonly enabled: v.BooleanSchema<undefined>;
|
|
14
14
|
/** Whether a signing secret is set (the secret itself is never returned). */
|
|
@@ -24,7 +24,7 @@ export type NotificationWebhook = v.InferOutput<typeof notificationWebhookSchema
|
|
|
24
24
|
export declare const putNotificationWebhookSchema: v.ObjectSchema<{
|
|
25
25
|
readonly url: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TrimAction, v.UrlAction<string, undefined>, v.StartsWithAction<string, "https://", "The webhook endpoint must be an https:// URL">, v.MaxLengthAction<string, 2000, undefined>]>;
|
|
26
26
|
/** Omit ⇒ deliver the default parking types. */
|
|
27
|
-
readonly types: v.OptionalSchema<v.ArraySchema<v.PicklistSchema<["merge_review", "pipeline_complete", "ci_failed", "test_failed", "requirement_review", "clarity_review", "release_regression", "decision_required", "human_test_ready", "visual_confirmation_ready", "human_review", "followup_pending", "fork_decision_pending", "pr_review_ready", "initiative", "platform_health", "budget_paused", "key_drift"], undefined>, undefined>, undefined>;
|
|
27
|
+
readonly types: v.OptionalSchema<v.ArraySchema<v.PicklistSchema<["merge_review", "pipeline_complete", "ci_failed", "test_failed", "requirement_review", "clarity_review", "release_regression", "decision_required", "human_test_ready", "visual_confirmation_ready", "human_review", "followup_pending", "fork_decision_pending", "pr_review_ready", "initiative", "platform_health", "budget_paused", "key_drift", "merge_tag_request"], undefined>, undefined>, undefined>;
|
|
28
28
|
readonly enabled: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
|
|
29
29
|
/** New signing secret; omit to keep the current one. */
|
|
30
30
|
readonly secret: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TrimAction, v.MinLengthAction<string, 16, undefined>, v.MaxLengthAction<string, 200, undefined>]>, undefined>;
|
|
@@ -52,7 +52,7 @@ export declare const notificationWebhookDeliverySchema: v.ObjectSchema<{
|
|
|
52
52
|
readonly taskId: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
53
53
|
readonly notification: v.ObjectSchema<{
|
|
54
54
|
readonly id: v.StringSchema<undefined>;
|
|
55
|
-
readonly type: v.PicklistSchema<["merge_review", "pipeline_complete", "ci_failed", "test_failed", "requirement_review", "clarity_review", "release_regression", "decision_required", "human_test_ready", "visual_confirmation_ready", "human_review", "followup_pending", "fork_decision_pending", "pr_review_ready", "initiative", "platform_health", "budget_paused", "key_drift"], undefined>;
|
|
55
|
+
readonly type: v.PicklistSchema<["merge_review", "pipeline_complete", "ci_failed", "test_failed", "requirement_review", "clarity_review", "release_regression", "decision_required", "human_test_ready", "visual_confirmation_ready", "human_review", "followup_pending", "fork_decision_pending", "pr_review_ready", "initiative", "platform_health", "budget_paused", "key_drift", "merge_tag_request"], undefined>;
|
|
56
56
|
readonly status: v.PicklistSchema<["open", "acted", "dismissed"], undefined>;
|
|
57
57
|
readonly severity: v.OptionalSchema<v.PicklistSchema<["normal", "urgent"], undefined>, undefined>;
|
|
58
58
|
readonly blockId: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
@@ -98,6 +98,8 @@ export declare const notificationWebhookDeliverySchema: v.ObjectSchema<{
|
|
|
98
98
|
readonly reason: v.PicklistSchema<["key-mismatch", "corrupt"], undefined>;
|
|
99
99
|
readonly sealedAt: v.NullableSchema<v.NumberSchema<undefined>, undefined>;
|
|
100
100
|
}, undefined>, undefined>, undefined>;
|
|
101
|
+
readonly changeClass: v.OptionalSchema<v.PicklistSchema<["docs", "test", "dependency", "config", "source", "schema", "unknown"], undefined>, undefined>;
|
|
102
|
+
readonly mergeTrackRecordId: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
101
103
|
}, undefined>, undefined>, undefined>;
|
|
102
104
|
readonly createdAt: v.NumberSchema<undefined>;
|
|
103
105
|
readonly resolvedAt: v.NullableSchema<v.NumberSchema<undefined>, undefined>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notification-webhooks.d.ts","sourceRoot":"","sources":["../src/notification-webhooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAmB5B,+FAA+F;AAC/F,eAAO,MAAM,yBAAyB;IACpC,mDAAmD;;IAEnD;;;;OAIG;;IAEH,sFAAsF;;IAEtF,6EAA6E;;;aAG7E,CAAA;AACF,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,yBAAyB,CAAC,CAAA;AAEjF;;;;GAIG;AACH,eAAO,MAAM,4BAA4B;;IAQvC,gDAAgD;;;IAGhD,wDAAwD;;aAExD,CAAA;AACF,MAAM,MAAM,2BAA2B,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,4BAA4B,CAAC,CAAA;AAE5F;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iCAAiC;;IAE5C,kGAAkG;;;IAGlG,4DAA4D;;IAE5D,mEAAmE
|
|
1
|
+
{"version":3,"file":"notification-webhooks.d.ts","sourceRoot":"","sources":["../src/notification-webhooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAmB5B,+FAA+F;AAC/F,eAAO,MAAM,yBAAyB;IACpC,mDAAmD;;IAEnD;;;;OAIG;;IAEH,sFAAsF;;IAEtF,6EAA6E;;;aAG7E,CAAA;AACF,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,yBAAyB,CAAC,CAAA;AAEjF;;;;GAIG;AACH,eAAO,MAAM,4BAA4B;;IAQvC,gDAAgD;;;IAGhD,wDAAwD;;aAExD,CAAA;AACF,MAAM,MAAM,2BAA2B,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,4BAA4B,CAAC,CAAA;AAE5F;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iCAAiC;;IAE5C,kGAAkG;;;IAGlG,4DAA4D;;IAE5D,mEAAmE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAGnE,CAAA;AACF,MAAM,MAAM,2BAA2B,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,iCAAiC,CAAC,CAAA;AAEjG;;;;;;GAMG;AACH,eAAO,MAAM,kCAAkC,kKAS6B,CAAA"}
|
package/dist/notifications.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import * as v from 'valibot';
|
|
|
4
4
|
* the frontend can switch on it to render the right action; extending it is a
|
|
5
5
|
* one-line change here plus a handler in the worker's `act` route.
|
|
6
6
|
*/
|
|
7
|
-
export declare const notificationTypeSchema: v.PicklistSchema<["merge_review", "pipeline_complete", "ci_failed", "test_failed", "requirement_review", "clarity_review", "release_regression", "decision_required", "human_test_ready", "visual_confirmation_ready", "human_review", "followup_pending", "fork_decision_pending", "pr_review_ready", "initiative", "platform_health", "budget_paused", "key_drift"], undefined>;
|
|
7
|
+
export declare const notificationTypeSchema: v.PicklistSchema<["merge_review", "pipeline_complete", "ci_failed", "test_failed", "requirement_review", "clarity_review", "release_regression", "decision_required", "human_test_ready", "visual_confirmation_ready", "human_review", "followup_pending", "fork_decision_pending", "pr_review_ready", "initiative", "platform_health", "budget_paused", "key_drift", "merge_tag_request"], undefined>;
|
|
8
8
|
export type NotificationType = v.InferOutput<typeof notificationTypeSchema>;
|
|
9
9
|
/**
|
|
10
10
|
* The closed set of notification types that count as "a task is waiting on a human review"
|
|
@@ -18,7 +18,9 @@ export type NotificationType = v.InferOutput<typeof notificationTypeSchema>;
|
|
|
18
18
|
* Deliberately EXCLUDED: failure-remediation cards (`ci_failed`, `test_failed`,
|
|
19
19
|
* `release_regression`) — "the machine needs help", not "a human owes a review" — and
|
|
20
20
|
* block-less/system cards (`platform_health`, `budget_paused`, `key_drift`, `initiative`) that
|
|
21
|
-
* aren't tied to a reviewable task.
|
|
21
|
+
* aren't tied to a reviewable task. `merge_tag_request` is excluded too: the PR it concerns has
|
|
22
|
+
* ALREADY merged, so it is a post-hoc nudge for one tap — counting it as review debt would
|
|
23
|
+
* friction task authoring over work that is finished.
|
|
22
24
|
*/
|
|
23
25
|
export declare const REVIEW_WAIT_NOTIFICATION_TYPES: readonly ["merge_review", "pipeline_complete", "requirement_review", "clarity_review", "decision_required", "human_test_ready", "visual_confirmation_ready", "human_review", "followup_pending", "fork_decision_pending", "pr_review_ready"];
|
|
24
26
|
export type ReviewWaitNotificationType = (typeof REVIEW_WAIT_NOTIFICATION_TYPES)[number];
|
|
@@ -154,12 +156,24 @@ export declare const notificationPayloadSchema: v.ObjectSchema<{
|
|
|
154
156
|
/** Epoch ms the secret was sealed, when known — helps an operator correlate a key change. */
|
|
155
157
|
readonly sealedAt: v.NullableSchema<v.NumberSchema<undefined>, undefined>;
|
|
156
158
|
}, undefined>, undefined>, undefined>;
|
|
159
|
+
/**
|
|
160
|
+
* The run's deterministic change class, on a `merge_review` / `pipeline_complete` /
|
|
161
|
+
* `merge_tag_request` card — so the human sees WHAT KIND of change they are being asked to
|
|
162
|
+
* review or tag, and the SPA can show that class's accumulated track record alongside.
|
|
163
|
+
*/
|
|
164
|
+
readonly changeClass: v.OptionalSchema<v.PicklistSchema<["docs", "test", "dependency", "config", "source", "schema", "unknown"], undefined>, undefined>;
|
|
165
|
+
/**
|
|
166
|
+
* The merge track record the card's effort tag applies to. Carried on the merge-decision
|
|
167
|
+
* cards (so acting on one can tag in the same tap) and on a `merge_tag_request` (whose whole
|
|
168
|
+
* purpose is to tag it). Absent when no record was written (a best-effort side channel).
|
|
169
|
+
*/
|
|
170
|
+
readonly mergeTrackRecordId: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
157
171
|
}, undefined>;
|
|
158
172
|
export type NotificationPayload = v.InferOutput<typeof notificationPayloadSchema>;
|
|
159
173
|
/** A human-actionable item surfaced on the board. */
|
|
160
174
|
export declare const notificationSchema: v.ObjectSchema<{
|
|
161
175
|
readonly id: v.StringSchema<undefined>;
|
|
162
|
-
readonly type: v.PicklistSchema<["merge_review", "pipeline_complete", "ci_failed", "test_failed", "requirement_review", "clarity_review", "release_regression", "decision_required", "human_test_ready", "visual_confirmation_ready", "human_review", "followup_pending", "fork_decision_pending", "pr_review_ready", "initiative", "platform_health", "budget_paused", "key_drift"], undefined>;
|
|
176
|
+
readonly type: v.PicklistSchema<["merge_review", "pipeline_complete", "ci_failed", "test_failed", "requirement_review", "clarity_review", "release_regression", "decision_required", "human_test_ready", "visual_confirmation_ready", "human_review", "followup_pending", "fork_decision_pending", "pr_review_ready", "initiative", "platform_health", "budget_paused", "key_drift", "merge_tag_request"], undefined>;
|
|
163
177
|
readonly status: v.PicklistSchema<["open", "acted", "dismissed"], undefined>;
|
|
164
178
|
/**
|
|
165
179
|
* Render urgency (see {@link notificationSeveritySchema}). Absent ⇒ `normal`. Flipped
|
|
@@ -259,6 +273,18 @@ export declare const notificationSchema: v.ObjectSchema<{
|
|
|
259
273
|
/** Epoch ms the secret was sealed, when known — helps an operator correlate a key change. */
|
|
260
274
|
readonly sealedAt: v.NullableSchema<v.NumberSchema<undefined>, undefined>;
|
|
261
275
|
}, undefined>, undefined>, undefined>;
|
|
276
|
+
/**
|
|
277
|
+
* The run's deterministic change class, on a `merge_review` / `pipeline_complete` /
|
|
278
|
+
* `merge_tag_request` card — so the human sees WHAT KIND of change they are being asked to
|
|
279
|
+
* review or tag, and the SPA can show that class's accumulated track record alongside.
|
|
280
|
+
*/
|
|
281
|
+
readonly changeClass: v.OptionalSchema<v.PicklistSchema<["docs", "test", "dependency", "config", "source", "schema", "unknown"], undefined>, undefined>;
|
|
282
|
+
/**
|
|
283
|
+
* The merge track record the card's effort tag applies to. Carried on the merge-decision
|
|
284
|
+
* cards (so acting on one can tag in the same tap) and on a `merge_tag_request` (whose whole
|
|
285
|
+
* purpose is to tag it). Absent when no record was written (a best-effort side channel).
|
|
286
|
+
*/
|
|
287
|
+
readonly mergeTrackRecordId: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
262
288
|
}, undefined>, undefined>, undefined>;
|
|
263
289
|
readonly createdAt: v.NumberSchema<undefined>;
|
|
264
290
|
/** When it left `open` (acted/dismissed); null while open. */
|
|
@@ -268,4 +294,17 @@ export type Notification = v.InferOutput<typeof notificationSchema>;
|
|
|
268
294
|
/** How a human resolved a notification from its card. */
|
|
269
295
|
export declare const resolveNotificationActionSchema: v.PicklistSchema<["act", "dismiss"], undefined>;
|
|
270
296
|
export type ResolveNotificationAction = v.InferOutput<typeof resolveNotificationActionSchema>;
|
|
297
|
+
/**
|
|
298
|
+
* Body of `POST /notifications/:id/act`. Every field is optional, so `{}` is the historical
|
|
299
|
+
* no-body behaviour.
|
|
300
|
+
*
|
|
301
|
+
* On a `merge_review` / `pipeline_complete` card, `reviewEffort` records — in the SAME tap that
|
|
302
|
+
* confirms the merge — how much review the PR actually needed, onto the run's merge track
|
|
303
|
+
* record. Omitting it is always fine: the record keeps a null tag and nothing downstream breaks
|
|
304
|
+
* (tagging is a nudge, never a gate).
|
|
305
|
+
*/
|
|
306
|
+
export declare const actNotificationSchema: v.ObjectSchema<{
|
|
307
|
+
readonly reviewEffort: v.OptionalSchema<v.NullableSchema<v.PicklistSchema<["none", "minor", "major"], undefined>, undefined>, undefined>;
|
|
308
|
+
}, undefined>;
|
|
309
|
+
export type ActNotificationInput = v.InferOutput<typeof actNotificationSchema>;
|
|
271
310
|
//# sourceMappingURL=notifications.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notifications.d.ts","sourceRoot":"","sources":["../src/notifications.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"notifications.d.ts","sourceRoot":"","sources":["../src/notifications.ts"],"names":[],"mappings":"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"}
|