@oss-autopilot/core 3.13.4 → 3.14.1
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/README.md +3 -3
- package/dist/cli-registry.js +59 -84
- package/dist/cli.bundle.cjs +112 -109
- package/dist/cli.js +5 -4
- package/dist/commands/comments.js +44 -10
- package/dist/commands/config.d.ts +2 -0
- package/dist/commands/config.js +50 -2
- package/dist/commands/curated-list.d.ts +17 -0
- package/dist/commands/curated-list.js +25 -0
- package/dist/commands/daily.d.ts +7 -1
- package/dist/commands/daily.js +136 -57
- package/dist/commands/dashboard-cache.d.ts +69 -0
- package/dist/commands/dashboard-cache.js +219 -0
- package/dist/commands/dashboard-data.d.ts +18 -10
- package/dist/commands/dashboard-data.js +58 -8
- package/dist/commands/dashboard-gist-sync.d.ts +93 -0
- package/dist/commands/dashboard-gist-sync.js +237 -0
- package/dist/commands/dashboard-server.d.ts +6 -10
- package/dist/commands/dashboard-server.js +181 -347
- package/dist/commands/features.js +6 -0
- package/dist/commands/guidelines.d.ts +6 -0
- package/dist/commands/guidelines.js +7 -0
- package/dist/commands/index.d.ts +2 -5
- package/dist/commands/index.js +2 -4
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.js +7 -1
- package/dist/commands/list-mark-done.js +6 -21
- package/dist/commands/list-move-tier.js +3 -5
- package/dist/commands/locate-issue-list.d.ts +25 -0
- package/dist/commands/locate-issue-list.js +67 -0
- package/dist/commands/merge-loop.d.ts +63 -0
- package/dist/commands/merge-loop.js +157 -0
- package/dist/commands/repo-vet.js +40 -1
- package/dist/commands/scout-bridge.d.ts +35 -2
- package/dist/commands/scout-bridge.js +65 -13
- package/dist/commands/search.d.ts +4 -6
- package/dist/commands/search.js +58 -11
- package/dist/commands/setup.d.ts +2 -0
- package/dist/commands/setup.js +56 -2
- package/dist/commands/skip-file-parser.d.ts +23 -0
- package/dist/commands/skip-file-parser.js +23 -10
- package/dist/commands/startup.d.ts +1 -6
- package/dist/commands/startup.js +25 -59
- package/dist/commands/track.d.ts +2 -2
- package/dist/commands/track.js +2 -2
- package/dist/commands/vet-list.d.ts +6 -6
- package/dist/commands/vet-list.js +194 -65
- package/dist/core/config-registry.js +36 -0
- package/dist/core/daily-logic.d.ts +25 -2
- package/dist/core/daily-logic.js +58 -3
- package/dist/core/gist-health.d.ts +81 -0
- package/dist/core/gist-health.js +39 -0
- package/dist/core/gist-state-store.d.ts +3 -1
- package/dist/core/gist-state-store.js +7 -2
- package/dist/core/github-stats.d.ts +2 -2
- package/dist/core/github-stats.js +20 -4
- package/dist/core/index.d.ts +5 -4
- package/dist/core/index.js +5 -4
- package/dist/core/issue-conversation.js +8 -2
- package/dist/core/issue-grading.d.ts +9 -0
- package/dist/core/issue-grading.js +9 -0
- package/dist/core/issue-verification.d.ts +39 -0
- package/dist/core/issue-verification.js +48 -0
- package/dist/core/pagination.d.ts +27 -0
- package/dist/core/pagination.js +23 -5
- package/dist/core/pr-comments-fetcher.d.ts +7 -0
- package/dist/core/pr-comments-fetcher.js +19 -8
- package/dist/core/pr-monitor.d.ts +2 -0
- package/dist/core/pr-monitor.js +26 -9
- package/dist/core/repo-score-manager.d.ts +2 -2
- package/dist/core/repo-score-manager.js +3 -3
- package/dist/core/repo-vet.d.ts +2 -2
- package/dist/core/repo-vet.js +1 -1
- package/dist/core/review-analysis.d.ts +19 -0
- package/dist/core/review-analysis.js +28 -0
- package/dist/core/state-schema.d.ts +43 -6
- package/dist/core/state-schema.js +81 -4
- package/dist/core/state.d.ts +36 -5
- package/dist/core/state.js +177 -28
- package/dist/core/strategy.js +6 -5
- package/dist/core/types.d.ts +8 -0
- package/dist/core/untrusted-content.d.ts +45 -0
- package/dist/core/untrusted-content.js +54 -0
- package/dist/formatters/json.d.ts +120 -12
- package/dist/formatters/json.js +55 -2
- package/package.json +2 -2
- package/dist/commands/shelve.d.ts +0 -45
- package/dist/commands/shelve.js +0 -54
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* and computing aggregate statistics. Mutation functions modify
|
|
5
5
|
* the passed state object in place; query functions are pure.
|
|
6
6
|
*
|
|
7
|
-
* **User-facing reference:** `docs/repo-
|
|
7
|
+
* **User-facing reference:** `docs/repo-scores.md` — plain-language
|
|
8
8
|
* explanation of the formula and what a given score means.
|
|
9
9
|
*/
|
|
10
10
|
import { isBelowMinStars } from './types.js';
|
|
@@ -14,7 +14,7 @@ const MODULE = 'scoring';
|
|
|
14
14
|
// ── Scoring constants (#1054) ─────────────────────────────────────────
|
|
15
15
|
// Previously inlined as magic numbers in `calculateScore`. Extracted with
|
|
16
16
|
// rationale comments so the formula is auditable without source spelunking.
|
|
17
|
-
// Changing any of these is a behavior change — update docs/repo-
|
|
17
|
+
// Changing any of these is a behavior change — update docs/repo-scores.md
|
|
18
18
|
// and the tests below in lockstep.
|
|
19
19
|
/** Starting point before any signals are applied. Deliberately optimistic so first-time repos aren't punished. */
|
|
20
20
|
const BASE_SCORE = 5;
|
|
@@ -66,7 +66,7 @@ function createDefaultRepoScore(repo) {
|
|
|
66
66
|
* − (hasHostileComments ? HOSTILITY_PENALTY : 0)
|
|
67
67
|
* clamped to [SCORE_MIN, SCORE_MAX].
|
|
68
68
|
*
|
|
69
|
-
* See `docs/repo-
|
|
69
|
+
* See `docs/repo-scores.md` for user-facing intent and what a given
|
|
70
70
|
* score means in practice.
|
|
71
71
|
*/
|
|
72
72
|
export function calculateScore(repoScore) {
|
package/dist/core/repo-vet.d.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* supply pre-fetched repo signals so the score is reproducible
|
|
12
12
|
* against fixture data and offline replays.
|
|
13
13
|
*
|
|
14
|
-
* Rubric reference: docs/repo-
|
|
14
|
+
* Rubric reference: docs/repo-scores.md.
|
|
15
15
|
*/
|
|
16
16
|
export interface RepoVetInput {
|
|
17
17
|
/** Star count from the GitHub repo metadata. */
|
|
@@ -78,7 +78,7 @@ export interface RepoVetResult {
|
|
|
78
78
|
prTemplate: boolean;
|
|
79
79
|
codeOfConduct: boolean;
|
|
80
80
|
};
|
|
81
|
-
/** Weighted 1-10 score per docs/repo-
|
|
81
|
+
/** Weighted 1-10 score per docs/repo-scores.md. */
|
|
82
82
|
rubricScore: number;
|
|
83
83
|
/** Top-line verdict derived from the score and red-flag overrides. */
|
|
84
84
|
rubricVerdict: RepoVetVerdict;
|
package/dist/core/repo-vet.js
CHANGED
|
@@ -53,6 +53,25 @@ export declare function getInlineCommentBody(reviewId: number, reviewComments: R
|
|
|
53
53
|
* that also posted inline comments (#829).
|
|
54
54
|
*/
|
|
55
55
|
export declare function reviewHasInlineComments(reviewId: number, reviewComments: ReviewComment[]): boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Earliest maintainer response timestamp across issue comments and submitted
|
|
58
|
+
* reviews (#1461). A "response" is any comment or review by a non-bot
|
|
59
|
+
* account other than the contributor — acknowledgments and approvals count,
|
|
60
|
+
* since for latency purposes any human maintainer reply is a response.
|
|
61
|
+
* Computed from data fetchPRDetails already holds in memory (zero extra API
|
|
62
|
+
* calls). Returns undefined when no maintainer has responded yet.
|
|
63
|
+
*/
|
|
64
|
+
export declare function getFirstMaintainerResponseAt(comments: Array<{
|
|
65
|
+
user?: {
|
|
66
|
+
login?: string;
|
|
67
|
+
} | null;
|
|
68
|
+
created_at: string;
|
|
69
|
+
}>, reviews: Array<{
|
|
70
|
+
user?: {
|
|
71
|
+
login?: string;
|
|
72
|
+
} | null;
|
|
73
|
+
submitted_at?: string | null;
|
|
74
|
+
}>, username: string): string | undefined;
|
|
56
75
|
/**
|
|
57
76
|
* Check if there are unresponded comments from maintainers.
|
|
58
77
|
* Combines issue comments and review comments into a timeline,
|
|
@@ -97,6 +97,34 @@ export function getInlineCommentBody(reviewId, reviewComments) {
|
|
|
97
97
|
export function reviewHasInlineComments(reviewId, reviewComments) {
|
|
98
98
|
return reviewComments.some((c) => c.pull_request_review_id === reviewId);
|
|
99
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* Earliest maintainer response timestamp across issue comments and submitted
|
|
102
|
+
* reviews (#1461). A "response" is any comment or review by a non-bot
|
|
103
|
+
* account other than the contributor — acknowledgments and approvals count,
|
|
104
|
+
* since for latency purposes any human maintainer reply is a response.
|
|
105
|
+
* Computed from data fetchPRDetails already holds in memory (zero extra API
|
|
106
|
+
* calls). Returns undefined when no maintainer has responded yet.
|
|
107
|
+
*/
|
|
108
|
+
export function getFirstMaintainerResponseAt(comments, reviews, username) {
|
|
109
|
+
const usernameLower = username.toLowerCase();
|
|
110
|
+
let earliest;
|
|
111
|
+
const consider = (author, createdAt) => {
|
|
112
|
+
if (!author || !createdAt)
|
|
113
|
+
return;
|
|
114
|
+
if (author.toLowerCase() === usernameLower)
|
|
115
|
+
return;
|
|
116
|
+
if (isBotAuthor(author))
|
|
117
|
+
return;
|
|
118
|
+
if (!earliest || new Date(createdAt).getTime() < new Date(earliest).getTime()) {
|
|
119
|
+
earliest = createdAt;
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
for (const comment of comments)
|
|
123
|
+
consider(comment.user?.login, comment.created_at);
|
|
124
|
+
for (const review of reviews)
|
|
125
|
+
consider(review.user?.login, review.submitted_at);
|
|
126
|
+
return earliest;
|
|
127
|
+
}
|
|
100
128
|
/**
|
|
101
129
|
* Check if there are unresponded comments from maintainers.
|
|
102
130
|
* Combines issue comments and review comments into a timeline,
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* Unknown keys are stripped by default (Zod 4 behavior).
|
|
10
10
|
*/
|
|
11
11
|
import { z } from 'zod';
|
|
12
|
+
import type { FetchedPR } from './types.js';
|
|
12
13
|
export declare const IssueStatusSchema: z.ZodEnum<{
|
|
13
14
|
candidate: "candidate";
|
|
14
15
|
claimed: "claimed";
|
|
@@ -63,6 +64,8 @@ export declare const StoredMergedPRSchema: z.ZodObject<{
|
|
|
63
64
|
url: z.ZodString;
|
|
64
65
|
title: z.ZodString;
|
|
65
66
|
mergedAt: z.ZodString;
|
|
67
|
+
openedAt: z.ZodOptional<z.ZodString>;
|
|
68
|
+
firstMaintainerResponseAt: z.ZodOptional<z.ZodString>;
|
|
66
69
|
commentsFetchedAt: z.ZodOptional<z.ZodString>;
|
|
67
70
|
learningsExtractedAt: z.ZodOptional<z.ZodString>;
|
|
68
71
|
}, z.core.$strip>;
|
|
@@ -70,6 +73,8 @@ export declare const StoredClosedPRSchema: z.ZodObject<{
|
|
|
70
73
|
url: z.ZodString;
|
|
71
74
|
title: z.ZodString;
|
|
72
75
|
closedAt: z.ZodString;
|
|
76
|
+
openedAt: z.ZodOptional<z.ZodString>;
|
|
77
|
+
firstMaintainerResponseAt: z.ZodOptional<z.ZodString>;
|
|
73
78
|
commentsFetchedAt: z.ZodOptional<z.ZodString>;
|
|
74
79
|
learningsExtractedAt: z.ZodOptional<z.ZodString>;
|
|
75
80
|
}, z.core.$strip>;
|
|
@@ -251,6 +256,8 @@ export declare const AgentConfigSchema: z.ZodObject<{
|
|
|
251
256
|
}>>>;
|
|
252
257
|
excludeRepos: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
253
258
|
excludeOrgs: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
259
|
+
avoidRepos: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
260
|
+
boostIssueTypes: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
254
261
|
trustedProjects: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
255
262
|
githubUsername: z.ZodDefault<z.ZodString>;
|
|
256
263
|
minRepoScoreThreshold: z.ZodDefault<z.ZodNumber>;
|
|
@@ -311,6 +318,7 @@ export declare const ClosedPRSchema: z.ZodObject<{
|
|
|
311
318
|
title: z.ZodString;
|
|
312
319
|
closedAt: z.ZodString;
|
|
313
320
|
closedBy: z.ZodOptional<z.ZodString>;
|
|
321
|
+
openedAt: z.ZodOptional<z.ZodString>;
|
|
314
322
|
}, z.core.$strip>;
|
|
315
323
|
export declare const MergedPRSchema: z.ZodObject<{
|
|
316
324
|
url: z.ZodString;
|
|
@@ -318,6 +326,7 @@ export declare const MergedPRSchema: z.ZodObject<{
|
|
|
318
326
|
number: z.ZodNumber;
|
|
319
327
|
title: z.ZodString;
|
|
320
328
|
mergedAt: z.ZodString;
|
|
329
|
+
openedAt: z.ZodOptional<z.ZodString>;
|
|
321
330
|
}, z.core.$strip>;
|
|
322
331
|
export declare const DailyDigestSummarySchema: z.ZodObject<{
|
|
323
332
|
totalActivePRs: z.ZodNumber;
|
|
@@ -325,11 +334,29 @@ export declare const DailyDigestSummarySchema: z.ZodObject<{
|
|
|
325
334
|
totalMergedAllTime: z.ZodNumber;
|
|
326
335
|
mergeRate: z.ZodNumber;
|
|
327
336
|
}, z.core.$strip>;
|
|
337
|
+
/**
|
|
338
|
+
* Minimal FetchedPR validation for persisted digest arrays (#1456). The
|
|
339
|
+
* persisted digest is rendered by the dashboard from cold start (before any
|
|
340
|
+
* refresh), so the fields it dereferences must be guaranteed present. Only
|
|
341
|
+
* the identity/status core is pinned — required on every real `FetchedPR`
|
|
342
|
+
* since the type's introduction — and `.passthrough()` keeps the dozens of
|
|
343
|
+
* volatile enrichment fields (CI, review, staleness) from rejecting digests
|
|
344
|
+
* persisted by older or newer producers.
|
|
345
|
+
*/
|
|
346
|
+
export declare const FetchedPRSchema: z.ZodObject<{
|
|
347
|
+
url: z.ZodString;
|
|
348
|
+
repo: z.ZodString;
|
|
349
|
+
number: z.ZodNumber;
|
|
350
|
+
status: z.ZodEnum<{
|
|
351
|
+
needs_addressing: "needs_addressing";
|
|
352
|
+
waiting_on_maintainer: "waiting_on_maintainer";
|
|
353
|
+
}>;
|
|
354
|
+
}, z.core.$loose>;
|
|
328
355
|
export declare const DailyDigestSchema: z.ZodObject<{
|
|
329
356
|
generatedAt: z.ZodString;
|
|
330
|
-
openPRs: z.
|
|
331
|
-
needsAddressingPRs: z.
|
|
332
|
-
waitingOnMaintainerPRs: z.
|
|
357
|
+
openPRs: z.ZodType<FetchedPR[], unknown, z.core.$ZodTypeInternals<FetchedPR[], unknown>>;
|
|
358
|
+
needsAddressingPRs: z.ZodType<FetchedPR[], unknown, z.core.$ZodTypeInternals<FetchedPR[], unknown>>;
|
|
359
|
+
waitingOnMaintainerPRs: z.ZodType<FetchedPR[], unknown, z.core.$ZodTypeInternals<FetchedPR[], unknown>>;
|
|
333
360
|
recentlyClosedPRs: z.ZodArray<z.ZodObject<{
|
|
334
361
|
url: z.ZodString;
|
|
335
362
|
repo: z.ZodString;
|
|
@@ -337,6 +364,7 @@ export declare const DailyDigestSchema: z.ZodObject<{
|
|
|
337
364
|
title: z.ZodString;
|
|
338
365
|
closedAt: z.ZodString;
|
|
339
366
|
closedBy: z.ZodOptional<z.ZodString>;
|
|
367
|
+
openedAt: z.ZodOptional<z.ZodString>;
|
|
340
368
|
}, z.core.$strip>>;
|
|
341
369
|
recentlyMergedPRs: z.ZodArray<z.ZodObject<{
|
|
342
370
|
url: z.ZodString;
|
|
@@ -344,6 +372,7 @@ export declare const DailyDigestSchema: z.ZodObject<{
|
|
|
344
372
|
number: z.ZodNumber;
|
|
345
373
|
title: z.ZodString;
|
|
346
374
|
mergedAt: z.ZodString;
|
|
375
|
+
openedAt: z.ZodOptional<z.ZodString>;
|
|
347
376
|
}, z.core.$strip>>;
|
|
348
377
|
shelvedPRs: z.ZodArray<z.ZodObject<{
|
|
349
378
|
number: z.ZodNumber;
|
|
@@ -413,6 +442,8 @@ export declare const AgentStateSchema: z.ZodObject<{
|
|
|
413
442
|
}>>>;
|
|
414
443
|
excludeRepos: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
415
444
|
excludeOrgs: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
445
|
+
avoidRepos: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
446
|
+
boostIssueTypes: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
416
447
|
trustedProjects: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
417
448
|
githubUsername: z.ZodDefault<z.ZodString>;
|
|
418
449
|
minRepoScoreThreshold: z.ZodDefault<z.ZodNumber>;
|
|
@@ -462,9 +493,9 @@ export declare const AgentStateSchema: z.ZodObject<{
|
|
|
462
493
|
lastStrategyAt: z.ZodOptional<z.ZodString>;
|
|
463
494
|
lastDigest: z.ZodOptional<z.ZodObject<{
|
|
464
495
|
generatedAt: z.ZodString;
|
|
465
|
-
openPRs: z.
|
|
466
|
-
needsAddressingPRs: z.
|
|
467
|
-
waitingOnMaintainerPRs: z.
|
|
496
|
+
openPRs: z.ZodType<FetchedPR[], unknown, z.core.$ZodTypeInternals<FetchedPR[], unknown>>;
|
|
497
|
+
needsAddressingPRs: z.ZodType<FetchedPR[], unknown, z.core.$ZodTypeInternals<FetchedPR[], unknown>>;
|
|
498
|
+
waitingOnMaintainerPRs: z.ZodType<FetchedPR[], unknown, z.core.$ZodTypeInternals<FetchedPR[], unknown>>;
|
|
468
499
|
recentlyClosedPRs: z.ZodArray<z.ZodObject<{
|
|
469
500
|
url: z.ZodString;
|
|
470
501
|
repo: z.ZodString;
|
|
@@ -472,6 +503,7 @@ export declare const AgentStateSchema: z.ZodObject<{
|
|
|
472
503
|
title: z.ZodString;
|
|
473
504
|
closedAt: z.ZodString;
|
|
474
505
|
closedBy: z.ZodOptional<z.ZodString>;
|
|
506
|
+
openedAt: z.ZodOptional<z.ZodString>;
|
|
475
507
|
}, z.core.$strip>>;
|
|
476
508
|
recentlyMergedPRs: z.ZodArray<z.ZodObject<{
|
|
477
509
|
url: z.ZodString;
|
|
@@ -479,6 +511,7 @@ export declare const AgentStateSchema: z.ZodObject<{
|
|
|
479
511
|
number: z.ZodNumber;
|
|
480
512
|
title: z.ZodString;
|
|
481
513
|
mergedAt: z.ZodString;
|
|
514
|
+
openedAt: z.ZodOptional<z.ZodString>;
|
|
482
515
|
}, z.core.$strip>>;
|
|
483
516
|
shelvedPRs: z.ZodArray<z.ZodObject<{
|
|
484
517
|
number: z.ZodNumber;
|
|
@@ -525,6 +558,8 @@ export declare const AgentStateSchema: z.ZodObject<{
|
|
|
525
558
|
url: z.ZodString;
|
|
526
559
|
title: z.ZodString;
|
|
527
560
|
mergedAt: z.ZodString;
|
|
561
|
+
openedAt: z.ZodOptional<z.ZodString>;
|
|
562
|
+
firstMaintainerResponseAt: z.ZodOptional<z.ZodString>;
|
|
528
563
|
commentsFetchedAt: z.ZodOptional<z.ZodString>;
|
|
529
564
|
learningsExtractedAt: z.ZodOptional<z.ZodString>;
|
|
530
565
|
}, z.core.$strip>>>;
|
|
@@ -532,6 +567,8 @@ export declare const AgentStateSchema: z.ZodObject<{
|
|
|
532
567
|
url: z.ZodString;
|
|
533
568
|
title: z.ZodString;
|
|
534
569
|
closedAt: z.ZodString;
|
|
570
|
+
openedAt: z.ZodOptional<z.ZodString>;
|
|
571
|
+
firstMaintainerResponseAt: z.ZodOptional<z.ZodString>;
|
|
535
572
|
commentsFetchedAt: z.ZodOptional<z.ZodString>;
|
|
536
573
|
learningsExtractedAt: z.ZodOptional<z.ZodString>;
|
|
537
574
|
}, z.core.$strip>>>;
|
|
@@ -44,6 +44,20 @@ export const StoredMergedPRSchema = z.object({
|
|
|
44
44
|
url: z.string(),
|
|
45
45
|
title: z.string(),
|
|
46
46
|
mergedAt: z.string(),
|
|
47
|
+
/**
|
|
48
|
+
* When the PR was opened (#1461). Optional: entries persisted before the
|
|
49
|
+
* outcome ledger existed don't carry it. Sourced from the Search API's
|
|
50
|
+
* `created_at` at merge-detection time — zero extra API calls.
|
|
51
|
+
*/
|
|
52
|
+
openedAt: z.string().optional(),
|
|
53
|
+
/**
|
|
54
|
+
* Earliest maintainer (non-bot, non-author) comment or review on the
|
|
55
|
+
* PR (#1461). Best-effort: only derivable when the PR was enriched while
|
|
56
|
+
* open (looked up from the previous run's persisted digest at detection
|
|
57
|
+
* time), so it is absent for PRs that merged before ever appearing in an
|
|
58
|
+
* enriched run.
|
|
59
|
+
*/
|
|
60
|
+
firstMaintainerResponseAt: z.string().optional(),
|
|
47
61
|
/** When the raw review-comment bundle for this PR was last fetched (#867). */
|
|
48
62
|
// ISO-8601 datetime guards against `markPRCommentsFetched(url, "garbage")`
|
|
49
63
|
// poisoning state through the stamping API (#1209 L4).
|
|
@@ -55,6 +69,13 @@ export const StoredClosedPRSchema = z.object({
|
|
|
55
69
|
url: z.string(),
|
|
56
70
|
title: z.string(),
|
|
57
71
|
closedAt: z.string(),
|
|
72
|
+
/** When the PR was opened (#1461). See StoredMergedPRSchema.openedAt. */
|
|
73
|
+
openedAt: z.string().optional(),
|
|
74
|
+
/**
|
|
75
|
+
* Earliest maintainer response (#1461). See
|
|
76
|
+
* StoredMergedPRSchema.firstMaintainerResponseAt.
|
|
77
|
+
*/
|
|
78
|
+
firstMaintainerResponseAt: z.string().optional(),
|
|
58
79
|
/** When the raw review-comment bundle for this PR was last fetched (#867). */
|
|
59
80
|
// ISO-8601 datetime guards against `markPRCommentsFetched(url, "garbage")`
|
|
60
81
|
// poisoning state through the stamping API (#1209 L4).
|
|
@@ -176,6 +197,21 @@ export const AgentConfigSchema = z.object({
|
|
|
176
197
|
scope: z.array(IssueScopeSchema).optional(),
|
|
177
198
|
excludeRepos: z.array(z.string()).default([]),
|
|
178
199
|
excludeOrgs: z.array(z.string()).optional(),
|
|
200
|
+
/**
|
|
201
|
+
* Repos (owner/repo) to softly downrank in discovery results (#1464).
|
|
202
|
+
* Milder than `excludeRepos`' hard filter: scout pushes matches below
|
|
203
|
+
* equally-recommended candidates but does not remove them, and a strong
|
|
204
|
+
* affinity boost can still outweigh the penalty (scout #168).
|
|
205
|
+
* Threaded to scout via `scout-bridge.ts`.
|
|
206
|
+
*/
|
|
207
|
+
avoidRepos: z.array(z.string()).default([]),
|
|
208
|
+
/**
|
|
209
|
+
* Issue label types (e.g. "bug", "good first issue") to softly boost in
|
|
210
|
+
* discovery ranking, matched case-insensitively against issue labels
|
|
211
|
+
* (scout #168 / #1464). Does not filter results or change viability
|
|
212
|
+
* scores. Threaded to scout via `scout-bridge.ts`.
|
|
213
|
+
*/
|
|
214
|
+
boostIssueTypes: z.array(z.string()).default([]),
|
|
179
215
|
trustedProjects: z.array(z.string()).default([]),
|
|
180
216
|
githubUsername: z.string().default(''),
|
|
181
217
|
minRepoScoreThreshold: z.number().default(4),
|
|
@@ -251,6 +287,12 @@ export const ClosedPRSchema = z.object({
|
|
|
251
287
|
title: z.string(),
|
|
252
288
|
closedAt: z.string(),
|
|
253
289
|
closedBy: z.string().optional(),
|
|
290
|
+
/**
|
|
291
|
+
* When the PR was opened (#1461). Carried from the Search API's
|
|
292
|
+
* `created_at` so close detection can persist it into the outcome ledger.
|
|
293
|
+
* Optional: digests persisted by older producers don't have it.
|
|
294
|
+
*/
|
|
295
|
+
openedAt: z.string().optional(),
|
|
254
296
|
});
|
|
255
297
|
export const MergedPRSchema = z.object({
|
|
256
298
|
url: z.string(),
|
|
@@ -258,6 +300,12 @@ export const MergedPRSchema = z.object({
|
|
|
258
300
|
number: z.number(),
|
|
259
301
|
title: z.string(),
|
|
260
302
|
mergedAt: z.string(),
|
|
303
|
+
/**
|
|
304
|
+
* When the PR was opened (#1461). Carried from the Search API's
|
|
305
|
+
* `created_at` so merge detection can persist it into the outcome ledger.
|
|
306
|
+
* Optional: digests persisted by older producers don't have it.
|
|
307
|
+
*/
|
|
308
|
+
openedAt: z.string().optional(),
|
|
261
309
|
});
|
|
262
310
|
export const DailyDigestSummarySchema = z.object({
|
|
263
311
|
totalActivePRs: z.number(),
|
|
@@ -265,12 +313,41 @@ export const DailyDigestSummarySchema = z.object({
|
|
|
265
313
|
totalMergedAllTime: z.number(),
|
|
266
314
|
mergeRate: z.number(),
|
|
267
315
|
});
|
|
316
|
+
/**
|
|
317
|
+
* Minimal FetchedPR validation for persisted digest arrays (#1456). The
|
|
318
|
+
* persisted digest is rendered by the dashboard from cold start (before any
|
|
319
|
+
* refresh), so the fields it dereferences must be guaranteed present. Only
|
|
320
|
+
* the identity/status core is pinned — required on every real `FetchedPR`
|
|
321
|
+
* since the type's introduction — and `.passthrough()` keeps the dozens of
|
|
322
|
+
* volatile enrichment fields (CI, review, staleness) from rejecting digests
|
|
323
|
+
* persisted by older or newer producers.
|
|
324
|
+
*/
|
|
325
|
+
export const FetchedPRSchema = z
|
|
326
|
+
.object({
|
|
327
|
+
url: z.string(),
|
|
328
|
+
repo: z.string(),
|
|
329
|
+
number: z.number(),
|
|
330
|
+
status: FetchedPRStatusSchema,
|
|
331
|
+
})
|
|
332
|
+
.passthrough();
|
|
333
|
+
/**
|
|
334
|
+
* The digest arrays hold full `FetchedPR` objects at runtime; validation is
|
|
335
|
+
* intentionally limited to the identity/status core above (everything else
|
|
336
|
+
* is ephemeral enrichment that older/newer producers legitimately differ
|
|
337
|
+
* on), so the inferred type is widened back to `FetchedPR[]` for consumers.
|
|
338
|
+
* The cast is sound in both directions: every real `FetchedPR` satisfies the
|
|
339
|
+
* core schema, and `.passthrough()` preserves the enrichment fields on parse
|
|
340
|
+
* output. This mirrors the pre-#1456 `z.array(z.any())` static contract
|
|
341
|
+
* while actually validating the fields the dashboard dereferences.
|
|
342
|
+
*/
|
|
343
|
+
const FetchedPRArraySchema = z.array(FetchedPRSchema);
|
|
268
344
|
export const DailyDigestSchema = z.object({
|
|
269
345
|
generatedAt: z.string(),
|
|
270
|
-
// FetchedPR arrays — ephemeral, regenerated each run. Validated
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
346
|
+
// FetchedPR arrays — ephemeral, regenerated each run. Validated for the
|
|
347
|
+
// identity/status core the dashboard dereferences; loose on the rest.
|
|
348
|
+
openPRs: FetchedPRArraySchema,
|
|
349
|
+
needsAddressingPRs: FetchedPRArraySchema,
|
|
350
|
+
waitingOnMaintainerPRs: FetchedPRArraySchema,
|
|
274
351
|
recentlyClosedPRs: z.array(ClosedPRSchema),
|
|
275
352
|
recentlyMergedPRs: z.array(MergedPRSchema),
|
|
276
353
|
shelvedPRs: z.array(ShelvedPRRefSchema),
|
package/dist/core/state.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { AgentState, TrackedIssue, RepoScore, RepoScoreUpdate, DailyDigest, Loca
|
|
|
7
7
|
import { type LoadRecoveryInfo } from './state-persistence.js';
|
|
8
8
|
import type { Stats } from './repo-score-manager.js';
|
|
9
9
|
import { GistStateStore } from './gist-state-store.js';
|
|
10
|
+
import { type GistHealth } from './gist-health.js';
|
|
10
11
|
export { acquireLock, releaseLock, atomicWriteFileSync } from './state-persistence.js';
|
|
11
12
|
export type { LoadRecoveryInfo } from './state-persistence.js';
|
|
12
13
|
export type { Stats } from './repo-score-manager.js';
|
|
@@ -135,6 +136,20 @@ export declare class StateManager {
|
|
|
135
136
|
isGistMode(): boolean;
|
|
136
137
|
/** Whether the Gist is in degraded mode (using local cache fallback). */
|
|
137
138
|
isGistDegraded(): boolean;
|
|
139
|
+
/**
|
|
140
|
+
* Single source of truth for gist persistence health (#1444). Surfaces
|
|
141
|
+
* that need "is this process reliably syncing to the Gist?" (daily
|
|
142
|
+
* warnings, dashboard banners/recovery gates) derive it from here instead
|
|
143
|
+
* of re-combining `config.persistence` / `isGistMode()` /
|
|
144
|
+
* `isGistDegraded()` at each call site.
|
|
145
|
+
*
|
|
146
|
+
* Composes the same primitive fields those accessors expose:
|
|
147
|
+
* - no gist store + config asks for gist → `configured-but-local`
|
|
148
|
+
* - gist store attached but disarmed (#1443) → `bootstrap-degraded`
|
|
149
|
+
* (`since` comes from the staleness marker the degraded bootstrap seeds)
|
|
150
|
+
* - otherwise healthy (`degraded: null`)
|
|
151
|
+
*/
|
|
152
|
+
getGistHealth(): GistHealth;
|
|
138
153
|
/**
|
|
139
154
|
* Whether per-repo guidelines (#867) are available. True iff the Gist store
|
|
140
155
|
* is initialized — in local-only mode, guidelines are unavailable and
|
|
@@ -225,7 +240,15 @@ export declare class StateManager {
|
|
|
225
240
|
* Entries with URLs that fail {@link parseGitHubUrl} are dropped before
|
|
226
241
|
* persistence (read-side filters in dashboard-data already skip them, but
|
|
227
242
|
* this prevents the bad data from reaching disk in the first place).
|
|
228
|
-
*
|
|
243
|
+
*
|
|
244
|
+
* Re-seen URLs are not re-added, but upgrade the stored entry in place
|
|
245
|
+
* (#1461): outcome-ledger fields (`openedAt`, `firstMaintainerResponseAt`)
|
|
246
|
+
* missing on the stored entry are filled from the richer incoming one.
|
|
247
|
+
* Existing values are never overwritten, so stamps that live only on the
|
|
248
|
+
* stored entry (commentsFetchedAt, learningsExtractedAt) and earlier
|
|
249
|
+
* enriched writes stay intact.
|
|
250
|
+
*
|
|
251
|
+
* @param prs - Merged PRs to add (duplicates by URL upgrade in place)
|
|
229
252
|
* @returns count of entries added vs. dropped (invalid URL)
|
|
230
253
|
*/
|
|
231
254
|
addMergedPRs(prs: StoredMergedPR[]): {
|
|
@@ -241,7 +264,11 @@ export declare class StateManager {
|
|
|
241
264
|
* Entries with URLs that fail {@link parseGitHubUrl} are dropped before
|
|
242
265
|
* persistence (read-side filters in dashboard-data already skip them, but
|
|
243
266
|
* this prevents the bad data from reaching disk in the first place).
|
|
244
|
-
*
|
|
267
|
+
*
|
|
268
|
+
* Re-seen URLs upgrade the stored entry's missing ledger fields in place
|
|
269
|
+
* (#1461) — see {@link addMergedPRs} for the full semantics.
|
|
270
|
+
*
|
|
271
|
+
* @param prs - Closed PRs to add (duplicates by URL upgrade in place)
|
|
245
272
|
* @returns count of entries added vs. dropped (invalid URL)
|
|
246
273
|
*/
|
|
247
274
|
addClosedPRs(prs: StoredClosedPR[]): {
|
|
@@ -450,10 +477,14 @@ export type GistPersistenceStatus =
|
|
|
450
477
|
| 'state-unreadable'
|
|
451
478
|
/** Gist mode is configured but no token was available for this attempt. */
|
|
452
479
|
| 'no-token'
|
|
453
|
-
/** Gist mode active: the singleton is gist-backed
|
|
480
|
+
/** Gist mode active: the singleton is gist-backed AND the store is armed
|
|
481
|
+
* (the bootstrap actually verified its Gist — not a degraded fallback). */
|
|
454
482
|
| 'gist'
|
|
455
|
-
/** Gist mode is configured and a token was available, but
|
|
456
|
-
* to
|
|
483
|
+
/** Gist mode is configured and a token was available, but this process is
|
|
484
|
+
* not writing to the Gist: init either fell back to a local-only manager
|
|
485
|
+
* (transient network failure) or bootstrapped DEGRADED off the local
|
|
486
|
+
* cache (#1443 — gist-backed but disarmed, reads stale and pushes fail).
|
|
487
|
+
* A later call may recover. */
|
|
457
488
|
| 'degraded';
|
|
458
489
|
export declare function ensureGistPersistence(token: string | null): Promise<GistPersistenceStatus>;
|
|
459
490
|
/**
|