@oss-autopilot/core 3.12.0 → 3.13.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.
@@ -1,6 +1,9 @@
1
1
  /**
2
2
  * list-mark-done command (#1299).
3
3
  *
4
+ * A URL that is not in the list at all is an error (#1406) — marking done
5
+ * requires an existing entry; only the already-marked re-run is a quiet no-op.
6
+ *
4
7
  * Mark an issue line in a curated list as done by wrapping it in
5
8
  * `~~strikethrough~~` and appending a `**Done** — PR [#N](url) ...` sub-bullet.
6
9
  * If every issue under the same `### repo/name` heading is now struck through,
@@ -19,7 +22,8 @@ export interface MarkDoneOptions {
19
22
  listPath: string;
20
23
  }
21
24
  export interface MarkDoneOutput {
22
- /** True if the line was found and updated. False on already-marked or not-found. */
25
+ /** True if the line was found and updated. False only when already marked
26
+ * done — a URL missing from the list entirely throws instead (#1406). */
23
27
  marked: boolean;
24
28
  /** Fully-resolved file path that was inspected. */
25
29
  filePath: string;
@@ -32,6 +36,8 @@ export interface MarkDoneOutput {
32
36
  /** Human-readable explanation when `marked` is false. */
33
37
  reason?: string;
34
38
  }
39
+ /** Discriminates the two `marked: false` outcomes of {@link markIssueAsDone}. */
40
+ export type MarkDoneNoOpReason = 'not-found' | 'already-marked';
35
41
  /** Pure transform — exposed for unit testing. */
36
42
  export declare function markIssueAsDone(content: string, opts: {
37
43
  issueUrl: string;
@@ -43,6 +49,8 @@ export declare function markIssueAsDone(content: string, opts: {
43
49
  repoHeadingStruck: boolean;
44
50
  remainingUnderRepo: number;
45
51
  reason?: string;
52
+ /** Set only when `marked` is false — why nothing changed. */
53
+ reasonCode?: MarkDoneNoOpReason;
46
54
  };
47
55
  /** Read → transform → write atomically (tmp + rename) so a crash mid-write can't corrupt the file. */
48
56
  export declare function runMarkIssueListItemDone(options: MarkDoneOptions): Promise<MarkDoneOutput>;
@@ -1,6 +1,9 @@
1
1
  /**
2
2
  * list-mark-done command (#1299).
3
3
  *
4
+ * A URL that is not in the list at all is an error (#1406) — marking done
5
+ * requires an existing entry; only the already-marked re-run is a quiet no-op.
6
+ *
4
7
  * Mark an issue line in a curated list as done by wrapping it in
5
8
  * `~~strikethrough~~` and appending a `**Done** — PR [#N](url) ...` sub-bullet.
6
9
  * If every issue under the same `### repo/name` heading is now struck through,
@@ -13,7 +16,7 @@
13
16
  */
14
17
  import * as fs from 'node:fs';
15
18
  import * as path from 'node:path';
16
- import { errorMessage } from '../core/errors.js';
19
+ import { errorMessage, ValidationError } from '../core/errors.js';
17
20
  const STRIKE = '~~';
18
21
  const DONE_PREFIX = ' - **Done**';
19
22
  const ISSUE_LINE_RE = /^[*+-]\s/;
@@ -113,6 +116,7 @@ export function markIssueAsDone(content, opts) {
113
116
  repoHeadingStruck: false,
114
117
  remainingUnderRepo: 0,
115
118
  reason: 'issue URL not found in the list',
119
+ reasonCode: 'not-found',
116
120
  };
117
121
  }
118
122
  const issueLine = lines[block.start];
@@ -126,6 +130,7 @@ export function markIssueAsDone(content, opts) {
126
130
  repoHeadingStruck: false,
127
131
  remainingUnderRepo: section ? countOpenIssues(lines, section) : 0,
128
132
  reason: 'already marked done',
133
+ reasonCode: 'already-marked',
129
134
  };
130
135
  }
131
136
  // 1. Strike the issue line.
@@ -186,6 +191,14 @@ export async function runMarkIssueListItemDone(options) {
186
191
  prUrl: options.prUrl,
187
192
  prStatus: options.prStatus,
188
193
  });
194
+ // #1406: a missing entry is a caller error, not a quiet success — the
195
+ // consumer branch in workflows/draft-first-workflow.md already documents
196
+ // this contract (STOP on success:false mentioning "Issue URL not found").
197
+ // Idempotent re-runs (already marked done) still resolve normally.
198
+ if (result.reasonCode === 'not-found') {
199
+ throw new ValidationError(`Issue URL not found in the list: ${options.issueUrl} (${filePath}). ` +
200
+ 'Check the URL and --list-path; the entry must exist before it can be marked done.');
201
+ }
189
202
  if (result.marked) {
190
203
  const tmp = `${filePath}.tmp-${process.pid}-${Date.now()}`;
191
204
  try {
@@ -10,6 +10,14 @@ export { type SearchOutput } from '../formatters/json.js';
10
10
  * lands in one place instead of three (#1002).
11
11
  */
12
12
  export declare const MAX_SEARCH_RESULTS = 100;
13
+ /**
14
+ * Fraction of search slots reserved for candidates that matched neither
15
+ * strategy-preferred languages nor repos (#1244). Counterweight against
16
+ * echo-chamber bias: without it, strategy-boosted searches return more of
17
+ * what already merged, which merges more of the same, and the profile
18
+ * narrows over time. Scout clamps to [0, 1]; 0.2 is the issue's proposal.
19
+ */
20
+ export declare const SEARCH_DIVERSITY_RATIO = 0.2;
13
21
  interface SearchOptions {
14
22
  maxResults: number;
15
23
  }
@@ -14,6 +14,14 @@ const MODULE = 'search';
14
14
  * lands in one place instead of three (#1002).
15
15
  */
16
16
  export const MAX_SEARCH_RESULTS = 100;
17
+ /**
18
+ * Fraction of search slots reserved for candidates that matched neither
19
+ * strategy-preferred languages nor repos (#1244). Counterweight against
20
+ * echo-chamber bias: without it, strategy-boosted searches return more of
21
+ * what already merged, which merges more of the same, and the profile
22
+ * narrows over time. Scout clamps to [0, 1]; 0.2 is the issue's proposal.
23
+ */
24
+ export const SEARCH_DIVERSITY_RATIO = 0.2;
17
25
  /**
18
26
  * Search GitHub for contributable issues using multi-phase discovery.
19
27
  *
@@ -63,6 +71,7 @@ export async function runSearch(options) {
63
71
  maxResults: options.maxResults,
64
72
  preferLanguages,
65
73
  preferRepos,
74
+ diversityRatio: SEARCH_DIVERSITY_RATIO,
66
75
  });
67
76
  // #1354: never surface issues the user already has an open PR for. Uses
68
77
  // scout's structured linked-PR metadata when present; candidates without it
@@ -128,6 +137,7 @@ export async function runSearch(options) {
128
137
  ...(linkedPR ? { linkedPR } : {}),
129
138
  ...(typeof c.boostScore === 'number' ? { boostScore: c.boostScore } : {}),
130
139
  ...(c.boostReasons && c.boostReasons.length > 0 ? { boostReasons: c.boostReasons } : {}),
140
+ ...(c.diversitySlot === true ? { diversitySlot: true } : {}),
131
141
  };
132
142
  }),
133
143
  excludedRepos: result.excludedRepos,
@@ -648,6 +648,7 @@ export declare const SearchOutputSchema: z.ZodObject<{
648
648
  }, z.core.$strip>>;
649
649
  boostScore: z.ZodOptional<z.ZodNumber>;
650
650
  boostReasons: z.ZodOptional<z.ZodArray<z.ZodString>>;
651
+ diversitySlot: z.ZodOptional<z.ZodBoolean>;
651
652
  }, z.core.$strip>>;
652
653
  excludedRepos: z.ZodArray<z.ZodString>;
653
654
  aiPolicyBlocklist: z.ZodArray<z.ZodString>;
@@ -707,6 +708,7 @@ export declare const FeaturesOutputSchema: z.ZodObject<{
707
708
  }, z.core.$strip>>;
708
709
  boostScore: z.ZodOptional<z.ZodNumber>;
709
710
  boostReasons: z.ZodOptional<z.ZodArray<z.ZodString>>;
711
+ diversitySlot: z.ZodOptional<z.ZodBoolean>;
710
712
  horizon: z.ZodEnum<{
711
713
  "quick-win": "quick-win";
712
714
  "bigger-bet": "bigger-bet";
@@ -764,6 +766,7 @@ export declare const FeaturesOutputSchema: z.ZodObject<{
764
766
  }, z.core.$strip>>;
765
767
  boostScore: z.ZodOptional<z.ZodNumber>;
766
768
  boostReasons: z.ZodOptional<z.ZodArray<z.ZodString>>;
769
+ diversitySlot: z.ZodOptional<z.ZodBoolean>;
767
770
  horizon: z.ZodEnum<{
768
771
  "quick-win": "quick-win";
769
772
  "bigger-bet": "bigger-bet";
@@ -816,7 +819,7 @@ export declare const ListMarkDoneOutputSchema: z.ZodObject<{
816
819
  url: z.ZodString;
817
820
  repoHeadingStruck: z.ZodBoolean;
818
821
  remainingUnderRepo: z.ZodNumber;
819
- reason: z.ZodOptional<z.ZodString>;
822
+ reason: z.ZodOptional<z.ZodLiteral<"already marked done">>;
820
823
  }, z.core.$strip>;
821
824
  export declare const VerifyIssueOutputSchema: z.ZodObject<{
822
825
  url: z.ZodString;
@@ -1216,6 +1219,12 @@ export interface SearchCandidate {
1216
1219
  * opportunities (open PR + no updates for 30+ days, scout 0.9.0 #97).
1217
1220
  */
1218
1221
  linkedPR?: CandidateLinkedPR;
1222
+ /** Strategy-bias sort boost from scout (#1244); absent when unboosted. */
1223
+ boostScore?: number;
1224
+ /** Human-readable boost explanations, e.g. "repo affinity: vercel/next.js". */
1225
+ boostReasons?: string[];
1226
+ /** True when this candidate filled a diversity slot (#1244). */
1227
+ diversitySlot?: boolean;
1219
1228
  }
1220
1229
  export interface SearchOutput {
1221
1230
  candidates: SearchCandidate[];
@@ -353,6 +353,9 @@ const SearchCandidateSchema = z.object({
353
353
  */
354
354
  boostScore: z.number().optional(),
355
355
  boostReasons: z.array(z.string()).optional(),
356
+ /** True when this candidate filled a diversity slot (#1244) — surfaced so
357
+ * the user can see the counterweight working, not just its absence. */
358
+ diversitySlot: z.boolean().optional(),
356
359
  });
357
360
  export const SearchOutputSchema = z.object({
358
361
  candidates: z.array(SearchCandidateSchema),
@@ -421,7 +424,10 @@ export const ListMarkDoneOutputSchema = z.object({
421
424
  url: z.string(),
422
425
  repoHeadingStruck: z.boolean(),
423
426
  remainingUnderRepo: z.number().int().nonnegative(),
424
- reason: z.string().optional(),
427
+ // #1406: not-found now throws before the success envelope, so the only
428
+ // reachable no-op reason is the idempotent one. Pinned so a new quiet
429
+ // no-op path trips the #1105 runtime validator instead of shipping silently.
430
+ reason: z.literal('already marked done').optional(),
425
431
  });
426
432
  // verify-issue (#1353, #1354): mirrors {@link IssueVerification} from
427
433
  // core/issue-verification.ts. Strict shape — additional keys must be added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oss-autopilot/core",
3
- "version": "3.12.0",
3
+ "version": "3.13.0",
4
4
  "description": "CLI and core library for managing open source contributions",
5
5
  "type": "module",
6
6
  "bin": {