Skip to content

Commit edf7b37

Browse files
authored
fix(contract): restate MAX_PRIORITY_ELIGIBILITY_WINDOW_MINUTES so the generated schemas compile (#9936)
main is currently failing `contract:api-schemas:check` for every PR. #9738 added `.max(MAX_PRIORITY_ELIGIBILITY_WINDOW_MINUTES)` to the settings schema in src/openapi/schemas.ts. gen-contract-api-schemas.ts copies that schema verbatim into @loopover/contract, which cannot import the Worker's src/ -- so a referenced constant has to be restated in the contract's own limits.ts. That step was missed, leaving both paths broken: regenerating emits a file referencing a name it never imports (TS2304, contract build fails), and NOT regenerating leaves the drift check red. The generator's doc calls this "the loud failure this wants", and it worked -- it just needs the constant it was asking for. Also pins the three restated Worker bounds against their originals. limits.ts says they are "pinned against their originals like every other entry here", but PREFLIGHT_LIMITS was the only group with a meta-test doing that; the single constants were restated on trust. Nothing at compile time relates the two copies (that is the whole reason the contract is a zod-only leaf), so a one-sided edit would surface as a client-side validation error rather than a build failure. The compile-time failure only catches a MISSING constant, never a drifted VALUE.
1 parent b21216b commit edf7b37

3 files changed

Lines changed: 51 additions & 1 deletion

File tree

packages/loopover-contract/src/api-schemas.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { z } from "zod";
1010

1111
import { checkBeforeStartSchema, slopRiskSchema, validateFocusManifestSchema, validateLinkedIssueSchema } from "./api-requests.js";
1212
import { AGENT_ACTION_CLASSES, AUTONOMY_LEVELS } from "./enums.js";
13-
import { MAX_CONTRIBUTOR_OPEN_ITEM_CAP, MAX_REVIEW_NAG_COOLDOWN_DAYS } from "./limits.js";
13+
import { MAX_CONTRIBUTOR_OPEN_ITEM_CAP, MAX_PRIORITY_ELIGIBILITY_WINDOW_MINUTES, MAX_REVIEW_NAG_COOLDOWN_DAYS } from "./limits.js";
1414

1515
export const FindingSchema = z
1616
.object({
@@ -465,6 +465,8 @@ export const RepositorySettingsSchema = z
465465
gateDryRun: z.boolean().optional(),
466466
premergeContentRecheck: z.boolean().optional(),
467467
requireFreshRebaseWindowMinutes: z.number().int().positive().nullable().optional(),
468+
// #9738: non-negative, not positive -- 0 is the documented way to turn the window off.
469+
priorityEligibilityWindowMinutes: z.number().int().min(0).max(MAX_PRIORITY_ELIGIBILITY_WINDOW_MINUTES).nullable().optional(),
468470
staleBaseAheadByThreshold: z.number().int().positive().nullable().optional(),
469471
mergeReadinessGateMode: z.enum(["off", "advisory", "block"]),
470472
manifestPolicyGateMode: z.enum(["off", "advisory", "block"]),

packages/loopover-contract/src/limits.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,3 +121,5 @@ export const PUBLIC_SURFACE_SKIP_REASONS = [
121121
export const MAX_CONTRIBUTOR_OPEN_ITEM_CAP = 100;
122122
/** src/settings/agent-actions.ts -- keeps the review-nag lookback from overflowing Date arithmetic. */
123123
export const MAX_REVIEW_NAG_COOLDOWN_DAYS = 365;
124+
/** src/review/priority-eligibility-window.ts (#9738) -- one day, the longest an eligibility hold may last. */
125+
export const MAX_PRIORITY_ELIGIBILITY_WINDOW_MINUTES = 24 * 60;
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// The Worker-side bounds that @loopover/contract restates must equal their originals (#9773 follow-up).
2+
//
3+
// WHY THIS EXISTS. limits.ts says these entries are "pinned against their originals like every other entry
4+
// here" -- but PREFLIGHT_LIMITS was the only group with a meta-test actually doing the pinning. The three
5+
// single constants were restated on trust, which is the same thing as not being pinned.
6+
//
7+
// The failure this catches is quiet and one-sided: the contract package cannot import the Worker's `src/`
8+
// (it is a zod-only leaf, which is the property every other surface depends on), so nothing at compile time
9+
// relates the two copies. Raise a bound on the Worker side alone and the published schema keeps rejecting
10+
// input the server would now accept; lower it alone and the schema accepts input the server then rejects or
11+
// truncates. Either way the mismatch surfaces as a confusing client-side validation error rather than as a
12+
// build failure.
13+
//
14+
// This is not a hypothetical. gen-contract-api-schemas.ts copies these schemas verbatim, so a copied schema
15+
// referencing a constant that has NOT been restated here emits a file that does not compile -- the
16+
// generator's own doc calls that "the loud failure this wants". #9738 added
17+
// `.max(MAX_PRIORITY_ELIGIBILITY_WINDOW_MINUTES)` to the settings schema without adding the constant, which
18+
// left `main` failing `contract:api-schemas:check` for every PR: regenerating produced an uncompilable file,
19+
// and not regenerating left the check red. The constant is now restated; this test is what keeps the VALUES
20+
// together from here on, which the compile-time failure alone never did.
21+
import { describe, expect, it } from "vitest";
22+
23+
import {
24+
MAX_CONTRIBUTOR_OPEN_ITEM_CAP as CONTRACT_MAX_CONTRIBUTOR_OPEN_ITEM_CAP,
25+
MAX_PRIORITY_ELIGIBILITY_WINDOW_MINUTES as CONTRACT_MAX_PRIORITY_ELIGIBILITY_WINDOW_MINUTES,
26+
MAX_REVIEW_NAG_COOLDOWN_DAYS as CONTRACT_MAX_REVIEW_NAG_COOLDOWN_DAYS,
27+
} from "../../packages/loopover-contract/src/limits";
28+
import { MAX_REVIEW_NAG_COOLDOWN_DAYS } from "../../src/settings/agent-actions";
29+
import { MAX_PRIORITY_ELIGIBILITY_WINDOW_MINUTES } from "../../src/review/priority-eligibility-window";
30+
import { MAX_CONTRIBUTOR_OPEN_ITEM_CAP } from "../../src/types";
31+
32+
describe("@loopover/contract restated Worker bounds stay pinned to their originals", () => {
33+
// One case per constant rather than a table, so a failure names the specific bound that drifted and the
34+
// file it has to be reconciled with.
35+
it("MAX_CONTRIBUTOR_OPEN_ITEM_CAP matches src/types.ts", () => {
36+
expect(CONTRACT_MAX_CONTRIBUTOR_OPEN_ITEM_CAP).toBe(MAX_CONTRIBUTOR_OPEN_ITEM_CAP);
37+
});
38+
39+
it("MAX_REVIEW_NAG_COOLDOWN_DAYS matches src/settings/agent-actions.ts", () => {
40+
expect(CONTRACT_MAX_REVIEW_NAG_COOLDOWN_DAYS).toBe(MAX_REVIEW_NAG_COOLDOWN_DAYS);
41+
});
42+
43+
it("MAX_PRIORITY_ELIGIBILITY_WINDOW_MINUTES matches src/review/priority-eligibility-window.ts", () => {
44+
expect(CONTRACT_MAX_PRIORITY_ELIGIBILITY_WINDOW_MINUTES).toBe(MAX_PRIORITY_ELIGIBILITY_WINDOW_MINUTES);
45+
});
46+
});

0 commit comments

Comments
 (0)