@overdeck/core 0.62.0 → 0.62.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/dist/dashboard/{merge-train-2WUgME6h.js → merge-train-I2N9TlqX.js} +1 -1
- package/dist/dashboard/{merge-train-DwYhNytU.js → merge-train-pMKZ_hti.js} +6 -6
- package/dist/dashboard/{merge-train-DwYhNytU.js.map → merge-train-pMKZ_hti.js.map} +1 -1
- package/dist/dashboard/server.js +4 -4
- package/dist/dashboard/{uat-train-Bik_I85J.js → uat-train-BORW2R0A.js} +3 -3
- package/dist/dashboard/{uat-train-Bik_I85J.js.map → uat-train-BORW2R0A.js.map} +1 -1
- package/dist/dashboard/{uat-train-CuZLaq2g.js → uat-train-tiJ_fEPf.js} +1 -1
- package/package.json +1 -2
- package/scripts/lint-dist-externals.mjs +18 -0
- package/sync-sources/skills/pan-release/SKILL.md +26 -3
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as getMergeTrainConfigPayload, c as getPendingAutoMergePayload, d as postMergeTrainAssemblePayload, f as postMergeTrainConfigPayload, i as getAutoMergeProblemPayload, l as mergeTrainRouteLayer, m as postMergeTrainMergeNextPayload, n as deleteAutoMergePayload, o as getMergeTrainGenerationsPayload, p as postMergeTrainGenerationShipPayload, r as getAutoMergePolicyPayload, s as getMergeTrainQueuesPayload, t as autoMergePolicyRefusal, u as postAutoMergeSchedulePayload } from "./merge-train-
|
|
1
|
+
import { a as getMergeTrainConfigPayload, c as getPendingAutoMergePayload, d as postMergeTrainAssemblePayload, f as postMergeTrainConfigPayload, i as getAutoMergeProblemPayload, l as mergeTrainRouteLayer, m as postMergeTrainMergeNextPayload, n as deleteAutoMergePayload, o as getMergeTrainGenerationsPayload, p as postMergeTrainGenerationShipPayload, r as getAutoMergePolicyPayload, s as getMergeTrainQueuesPayload, t as autoMergePolicyRefusal, u as postAutoMergeSchedulePayload } from "./merge-train-pMKZ_hti.js";
|
|
2
2
|
export { autoMergePolicyRefusal, mergeTrainRouteLayer as default, mergeTrainRouteLayer, deleteAutoMergePayload, getAutoMergePolicyPayload, getAutoMergeProblemPayload, getMergeTrainConfigPayload, getMergeTrainGenerationsPayload, getMergeTrainQueuesPayload, getPendingAutoMergePayload, postAutoMergeSchedulePayload, postMergeTrainAssemblePayload, postMergeTrainConfigPayload, postMergeTrainGenerationShipPayload, postMergeTrainMergeNextPayload };
|
|
@@ -219,7 +219,7 @@ async function getMergeTrainQueuesPayload() {
|
|
|
219
219
|
/** PAN-1696 AC2: per-project generation chains across every tracked project. */
|
|
220
220
|
async function getMergeTrainGenerationsPayload() {
|
|
221
221
|
const { isMergeTrainEnabledForProject } = await import("./merge-sync-GeddtnBd.js");
|
|
222
|
-
const { getUatGenerationsPayload } = await import("./uat-train-
|
|
222
|
+
const { getUatGenerationsPayload } = await import("./uat-train-tiJ_fEPf.js");
|
|
223
223
|
return (await Promise.allSettled(listProjectsSync().map(async ({ key, config }) => ({
|
|
224
224
|
projectKey: key,
|
|
225
225
|
projectName: config.name,
|
|
@@ -306,7 +306,7 @@ async function postMergeTrainAssemblePayload(payload) {
|
|
|
306
306
|
status: 404,
|
|
307
307
|
body: { error: `Unknown project key: ${projectKey}` }
|
|
308
308
|
};
|
|
309
|
-
const { runUatTrainReconcile } = await import("./uat-train-
|
|
309
|
+
const { runUatTrainReconcile } = await import("./uat-train-tiJ_fEPf.js");
|
|
310
310
|
return {
|
|
311
311
|
status: 200,
|
|
312
312
|
body: { projects: [{
|
|
@@ -318,7 +318,7 @@ async function postMergeTrainAssemblePayload(payload) {
|
|
|
318
318
|
}] }
|
|
319
319
|
};
|
|
320
320
|
}
|
|
321
|
-
const { runUatTrainReconcileAllProjects } = await import("./uat-train-
|
|
321
|
+
const { runUatTrainReconcileAllProjects } = await import("./uat-train-tiJ_fEPf.js");
|
|
322
322
|
return {
|
|
323
323
|
status: 200,
|
|
324
324
|
body: { projects: await runUatTrainReconcileAllProjects({ force: true }) }
|
|
@@ -368,7 +368,7 @@ const postMergeTrainGenerationStackRoute = HttpRouter.add("POST", "/api/merge-tr
|
|
|
368
368
|
const authError = rejectUnsafeDashboardMutationRequest(yield* HttpServerRequest.HttpServerRequest);
|
|
369
369
|
if (authError) return authError;
|
|
370
370
|
const name = uatGenerationNameFromParam((yield* HttpRouter.params)["name"] ?? "");
|
|
371
|
-
const { postUatGenerationStackPayload } = yield* Effect.promise(() => import("./uat-train-
|
|
371
|
+
const { postUatGenerationStackPayload } = yield* Effect.promise(() => import("./uat-train-tiJ_fEPf.js"));
|
|
372
372
|
const result = yield* Effect.promise(() => postUatGenerationStackPayload(name));
|
|
373
373
|
if (!result.ok) return jsonResponse({ error: result.error }, { status: result.status });
|
|
374
374
|
return jsonResponse({
|
|
@@ -385,7 +385,7 @@ const postMergeTrainGenerationPromoteRoute = HttpRouter.add("POST", "/api/merge-
|
|
|
385
385
|
const rawShipVersion = parsed.body["shipVersion"];
|
|
386
386
|
if (rawShipVersion !== void 0 && (typeof rawShipVersion !== "string" || !/^\d+\.\d+\.\d+$/.test(rawShipVersion))) return jsonResponse({ error: "shipVersion must look like 48.8.0" }, { status: 400 });
|
|
387
387
|
const name = uatGenerationNameFromParam((yield* HttpRouter.params)["name"] ?? "");
|
|
388
|
-
const { postUatGenerationPromotePayload } = yield* Effect.promise(() => import("./uat-train-
|
|
388
|
+
const { postUatGenerationPromotePayload } = yield* Effect.promise(() => import("./uat-train-tiJ_fEPf.js"));
|
|
389
389
|
const { firePostMergeLifecycle } = yield* Effect.promise(() => import("./specialists-C7M9CdDA.js"));
|
|
390
390
|
const result = yield* Effect.promise(() => postUatGenerationPromotePayload(name, firePostMergeLifecycle, rawShipVersion));
|
|
391
391
|
if (!result.success) return jsonResponse(result, { status: result.reason === "not-found" ? 404 : result.reason === "merge-failed" ? 500 : 409 });
|
|
@@ -726,4 +726,4 @@ const mergeTrainRouteLayer = Layer.mergeAll(getMergeTrainQueuesRoute, getMergeTr
|
|
|
726
726
|
//#endregion
|
|
727
727
|
export { getMergeTrainConfigPayload as a, getPendingAutoMergePayload as c, postMergeTrainAssemblePayload as d, postMergeTrainConfigPayload as f, getAutoMergeProblemPayload as i, mergeTrainRouteLayer as l, postMergeTrainMergeNextPayload as m, deleteAutoMergePayload as n, getMergeTrainGenerationsPayload as o, postMergeTrainGenerationShipPayload as p, getAutoMergePolicyPayload as r, getMergeTrainQueuesPayload as s, autoMergePolicyRefusal as t, postAutoMergeSchedulePayload as u };
|
|
728
728
|
|
|
729
|
-
//# sourceMappingURL=merge-train-
|
|
729
|
+
//# sourceMappingURL=merge-train-pMKZ_hti.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"merge-train-DwYhNytU.js","names":["nodeServicesLayer"],"sources":["../../src/lib/cloister/merge-eligibility.ts","../../src/lib/cloister/auto-merge-config.ts","../../src/lib/cloister/merge-blockers.ts","../../src/dashboard/server/routes/merge-train.ts"],"sourcesContent":["import { gatherProjectLensSignals } from '../pipeline-membership-gather.js';\nimport {\n resolvePipelineMembership,\n type PipelineMembership,\n} from '../pipeline-membership.js';\nimport {\n getProjectSync,\n resolveProjectFromIssueSync,\n type ProjectConfig,\n} from '../projects.js';\n\nexport interface MergeEligibilityDeps {\n resolveProject: typeof resolveProjectFromIssueSync;\n getProject: typeof getProjectSync;\n gather: typeof gatherProjectLensSignals;\n}\n\nconst defaultDeps: MergeEligibilityDeps = {\n resolveProject: resolveProjectFromIssueSync,\n getProject: getProjectSync,\n gather: gatherProjectLensSignals,\n};\n\nexport function isMergeEligible(membership: PipelineMembership): boolean {\n return membership.bucket === 'in_flight';\n}\n\n/** Classify candidates through one durable-lens gather per represented project. */\nexport async function gatherMergeEligibility(\n issueIds: string[],\n deps: MergeEligibilityDeps = defaultDeps,\n): Promise<Map<string, PipelineMembership>> {\n const candidatesByProject = new Map<string, { project: ProjectConfig; issueIds: Set<string> }>();\n\n for (const issueId of issueIds) {\n const resolved = deps.resolveProject(issueId);\n if (!resolved) continue;\n const configured = deps.getProject(resolved.projectKey);\n if (!configured) continue;\n const existing = candidatesByProject.get(resolved.projectKey);\n if (existing) {\n existing.issueIds.add(issueId.toUpperCase());\n } else {\n candidatesByProject.set(resolved.projectKey, {\n project: { ...configured, path: resolved.projectPath },\n issueIds: new Set([issueId.toUpperCase()]),\n });\n }\n }\n\n const memberships = new Map<string, PipelineMembership>();\n await Promise.all([...candidatesByProject.values()].map(async ({ project, issueIds: candidates }) => {\n const signals = await deps.gather(project);\n for (const signal of signals) {\n const issueId = signal.issueId.toUpperCase();\n if (candidates.has(issueId)) memberships.set(issueId, resolvePipelineMembership(signal));\n }\n }));\n return memberships;\n}\n","export const AUTO_MERGE_COOLDOWN_MS = 300_000;\n","/**\n * PRs that are done being reviewed but cannot merge for a forge-native reason\n * (PAN-1620, re-pointed by PAN-3917): merge conflicts, failing checks, or a\n * not-mergeable state.\n *\n * Before the cut this read `blockerReasons` off the `review_status` row — a\n * stored copy a patrol had to keep in step with GitHub. Now every reason comes\n * straight from the forge's account of the PR, so there is nothing to drift.\n * The in-flight candidate set comes from the durable pipeline lenses (tracker +\n * git + PR), never from a record.\n */\nimport { listInFlightIssuesWithPr, type MergeCandidateDeps } from './merge-ready-set.js';\nimport { getPrFacts, type PrFacts } from './pr-facts.js';\n\nexport type MergeBlockerType = 'merge_conflict' | 'failing_checks' | 'not_mergeable';\n\nexport interface MergeBlocker {\n issueId: string;\n prUrl?: string;\n reasons: Array<{ type: MergeBlockerType; summary: string }>;\n}\n\nexport type MergeBlockersDeps = MergeCandidateDeps;\n\n/**\n * Classify one PR's forge state into blocker reasons. Pure: the caller supplies\n * the facts. An approved PR with green checks and `mergeable` true has none.\n */\nexport function blockerReasonsFor(facts: PrFacts): MergeBlocker['reasons'] {\n if (!facts.exists || !facts.open || facts.merged) return [];\n const reasons: MergeBlocker['reasons'] = [];\n if (facts.mergeable === false) {\n const conflicting = (facts.mergeableState ?? '').includes('conflict')\n || facts.mergeableState === 'conflicting';\n reasons.push(conflicting\n ? { type: 'merge_conflict', summary: 'the branch conflicts with the base branch' }\n : { type: 'not_mergeable', summary: `the forge reports the PR is not mergeable${facts.mergeableState ? ` (${facts.mergeableState})` : ''}` });\n }\n if (facts.checks === 'red') {\n reasons.push({ type: 'failing_checks', summary: `CI checks are failing on ${facts.headSha?.slice(0, 8) ?? 'HEAD'}` });\n }\n return reasons;\n}\n\n/**\n * Every in-flight issue whose PR is approved but blocked by the forge.\n *\n * Async because the forge is the source: callers that used to read SQLite\n * synchronously (the dashboard route and `pan flywheel merge-blockers`) await it.\n */\nexport async function getMergeBlockersPayload(deps: MergeBlockersDeps = {}): Promise<MergeBlocker[]> {\n const candidates = await listInFlightIssuesWithPr(deps);\n\n const getFacts = deps.getFacts ?? getPrFacts;\n const out: MergeBlocker[] = [];\n for (const issueId of candidates) {\n const facts = await getFacts(issueId);\n if (!facts.approved) continue;\n const reasons = blockerReasonsFor(facts);\n if (reasons.length === 0) continue;\n out.push({ issueId, prUrl: facts.url ?? undefined, reasons });\n }\n return out;\n}\n","/**\n * PAN-1696 merge-train-routes: the aggregate `/api/merge-train/*` namespace.\n *\n * The merge-train (conflict-aware merge order + UAT batch trains) is a\n * per-project pipeline concern, but its only HTTP surface used to live under\n * `/api/flywheel/*` and answered for the Overdeck repo alone. These routes\n * answer for EVERY tracked project and require no active flywheel run: the\n * ready set is derived from the forge — approvals, checks, and mergeability.\n *\n * PAN-3917 W6: the flywheel routes are gone, and the merge-train config and\n * auto-merge surface that lived under `/api/flywheel/*` moved here, under\n * `/api/merge-train/config` and `/api/merge-train/auto-merge/*`. Scheduling an\n * auto-merge no longer consults a flywheel run or a review-status record: it\n * gates on the derived issue state, which is approvals plus green checks plus\n * forge mergeability (FR-9, D3).\n */\n\nimport { Effect, Layer } from 'effect';\nimport { HttpRouter, HttpServerRequest } from 'effect/unstable/http';\nimport { layer as nodeServicesLayer } from '@effect/platform-node/NodeServices';\nimport { resolve } from 'node:path';\nimport { httpHandler } from './http-handler.js';\nimport { jsonResponse } from '../http-helpers.js';\nimport { hasDashboardInternalToken, rejectUnsafeDashboardMutationRequest } from './dashboard-auth.js';\nimport { getProjectSync, listProjectsSync, resolveProjectFromIssueSync, type ProjectConfig, type ResolvedProject } from '../../../lib/projects.js';\nimport type { MergeQueueItem } from '../../../lib/flywheel-merge-order.js';\nimport { gatherMergeEligibility, isMergeEligible } from '../../../lib/cloister/merge-eligibility.js';\nimport { emitActivityTts } from '../../../lib/activity-logger.js';\nimport { parseArtifactRef } from '../../../lib/forge.js';\nimport { validateOrigin } from './origin-validation.js';\nimport { AUTO_MERGE_COOLDOWN_MS } from '../../../lib/cloister/auto-merge-config.js';\nimport { autoMergeFromLabels, isAutoMergeEligible, issueHoldsForUat, type AutoMergeEligibility } from '../../../lib/cloister/auto-merge-eligibility.js';\nimport {\n getProjectAutoMergeDefault,\n projectHoldsForUat,\n shouldHoldForUat,\n type ProjectAutoMergeDefault,\n} from '../../../lib/cloister/auto-merge-policy.js';\nimport { getMergeBlockersPayload } from '../../../lib/cloister/merge-blockers.js';\nimport {\n isFlywheelAutoPickupBacklog,\n isFlywheelRequireUatBeforeMerge,\n isMergeTrainEnabled,\n setFlywheelAutoPickupBacklog,\n setFlywheelRequireUatBeforeMerge,\n setMergeTrainEnabled,\n} from '../../../lib/overdeck/control-settings.js';\nimport {\n cancelPending,\n countActionableAutoMerges,\n getActionableAutoMerge,\n listActiveAutoMerges,\n listProblemAutoMerges,\n scheduleAutoMergeWithResult,\n type PendingAutoMerge,\n type ScheduleAutoMergeInput,\n type ScheduleAutoMergeResult,\n} from '../../../lib/overdeck/merge-sync.js';\nimport { removeQueuedMerge } from '../../../lib/overdeck/merge.js';\nimport { listReadyIssuesForProject } from '../services/derived-issue-state.js';\nimport { evaluateIssueMergeGate, type MergeGateResult } from '../../../lib/cloister/merge-gate.js';\nimport { getSharedIssueService } from '../services/issue-service-singleton.js';\nimport type { PipelineMembership } from '../../../lib/pipeline-membership.js';\n\nconst readUnknownJsonBody = Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const text = yield* request.text;\n try {\n return { ok: true as const, body: text ? (JSON.parse(text) as unknown) : {} };\n } catch {\n return { ok: false as const, error: 'Request body must be valid JSON' };\n }\n});\n\n/** A plain JSON object — not null, not an array, not a primitive. */\nfunction isJsonObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/** One project's merge-train queue as served by GET /api/merge-train/queues. */\nexport interface MergeTrainQueueEntry {\n projectKey: string;\n projectName: string;\n /** Effective per-project flag: the project override, else the global setting. */\n enabled: boolean;\n /**\n * PAN-3965: one ready feature still gets a batch (its UAT stack). With\n * exactly one feature queued this is that feature's own hold (its label,\n * then the project, then global); otherwise the project-level hold.\n * False = one ready feature merges directly.\n */\n holdsForUat: boolean;\n queue: MergeQueueItem[];\n}\n\n/** One project's UAT generation chain as served by GET /api/merge-train/generations. */\nexport interface MergeTrainGenerationsEntry {\n projectKey: string;\n projectName: string;\n enabled: boolean;\n generations: unknown[];\n}\n\n/**\n * A disabled project reports an empty queue rather than being omitted, so the\n * multi-project view can render it as an explicitly-off row instead of making\n * \"off\" and \"no ready work\" look identical.\n */\nasync function queueEntryForProject(\n key: string,\n config: ProjectConfig,\n enabled: boolean,\n): Promise<MergeTrainQueueEntry> {\n const globalRequireUat = isFlywheelRequireUatBeforeMerge();\n const holdsForUat = projectHoldsForUat(config, globalRequireUat);\n const base = { projectKey: key, projectName: config.name, enabled, holdsForUat };\n if (!enabled) return { ...base, queue: [] };\n\n const projectPath = resolve(config.path);\n // PAN-3917 FR-9: the ready set is the forge's — approvals, green checks, and\n // mergeability — not a scan of review-status records.\n const candidates = await listReadyIssuesForProject(projectPath);\n if (candidates.length === 0) return { ...base, queue: [] };\n\n // Imported here so the conflict-ordering module's git work is only loaded\n // when a queue is actually computed.\n const { computeMergeQueueFromCandidates } = await import('../../../lib/flywheel-merge-order.js');\n const queue = await Effect.runPromise(\n computeMergeQueueFromCandidates(candidates, projectPath).pipe(Effect.provide(nodeServicesLayer)),\n );\n // Review of #4017: a lone ready feature's hold is its own (label, then\n // project, then global), the same one the reconciler applies — the page must\n // not say \"merges directly\" for a feature that gets a UAT batch.\n if (queue.length === 1) {\n return { ...base, holdsForUat: await issueHoldsForUat(queue[0]!.issueId, config, globalRequireUat), queue };\n }\n return { ...base, queue };\n}\n\n/**\n * PAN-1696 AC1: one entry per tracked project, with that project's effective\n * flag and queue, with no flywheel run active. One project's git failure must\n * not blank the whole response, so a failed project reports an empty queue.\n */\nexport async function getMergeTrainQueuesPayload(): Promise<MergeTrainQueueEntry[]> {\n const { isMergeTrainEnabledForProject } = await import('../../../lib/overdeck/merge-sync.js');\n const projects = listProjectsSync();\n const settled = await Promise.allSettled(\n projects.map(({ key, config }) =>\n queueEntryForProject(key, config, isMergeTrainEnabledForProject(config)),\n ),\n );\n return settled.flatMap((outcome, i) => {\n if (outcome.status === 'fulfilled') return [outcome.value];\n const entry = projects[i];\n if (!entry) return [];\n const reason = outcome.reason instanceof Error ? outcome.reason.message : String(outcome.reason);\n console.warn(`[merge-train] queue for project ${entry.key} failed: ${reason}`);\n return [{ projectKey: entry.key, projectName: entry.config.name, enabled: true, holdsForUat: projectHoldsForUat(entry.config, isFlywheelRequireUatBeforeMerge()), queue: [] }];\n });\n}\n\n/** PAN-1696 AC2: per-project generation chains across every tracked project. */\nexport async function getMergeTrainGenerationsPayload(): Promise<MergeTrainGenerationsEntry[]> {\n const { isMergeTrainEnabledForProject } = await import('../../../lib/overdeck/merge-sync.js');\n const { getUatGenerationsPayload } = await import('../services/uat-train.js');\n\n const settled = await Promise.allSettled(\n listProjectsSync().map(async ({ key, config }) => ({\n projectKey: key,\n projectName: config.name,\n enabled: isMergeTrainEnabledForProject(config),\n generations: await getUatGenerationsPayload(resolve(config.path)),\n })),\n );\n return settled.flatMap((outcome) => (outcome.status === 'fulfilled' ? [outcome.value] : []));\n}\n\n/**\n * PAN-1696 AC3: merge the first N issues of the named project's ready set,\n * one at a time, stopping at the first failure (the rest would need\n * re-rebasing). An unknown project key is a 4xx, never a silent empty merge.\n */\nexport interface MergeTrainMergeNextDeps {\n getOrderedIssueIds?: (projectPath: string) => Promise<string[]>;\n merge?: (issueId: string) => Promise<{ ok: true } | { ok: false; reason: string }>;\n gatherEligibility?: (issueIds: string[]) => Promise<Map<string, PipelineMembership>>;\n}\n\nasync function defaultOrderedIssueIdsForProject(projectPath: string): Promise<string[]> {\n const candidates = await listReadyIssuesForProject(projectPath);\n if (candidates.length === 0) return [];\n const { computeMergeQueueFromCandidates } = await import('../../../lib/flywheel-merge-order.js');\n const queue = await Effect.runPromise(\n computeMergeQueueFromCandidates(candidates, projectPath).pipe(Effect.provide(nodeServicesLayer)),\n );\n return queue.map((item) => item.issueId);\n}\n\nasync function defaultMergeOne(issueId: string): Promise<{ ok: true } | { ok: false; reason: string }> {\n const { triggerMerge } = await import('./workspaces/merge-ops.js');\n const r = await triggerMerge(issueId);\n return r.success ? { ok: true } : { ok: false, reason: r.error ?? r.message ?? 'merge failed' };\n}\n\nexport async function postMergeTrainMergeNextPayload(\n payload: unknown,\n deps: MergeTrainMergeNextDeps = {},\n): Promise<{ status: number; body: unknown }> {\n if (!isJsonObject(payload)) {\n return { status: 400, body: { error: 'body must be a JSON object: { n, project }' } };\n }\n const body = payload as { n?: unknown; project?: unknown };\n const n = typeof body.n === 'number' && Number.isFinite(body.n) ? Math.floor(body.n) : 0;\n if (n <= 0) return { status: 400, body: { error: 'n must be a positive integer' } };\n\n if (typeof body.project !== 'string' || body.project.trim() === '') {\n return { status: 400, body: { error: 'project must be a non-empty string' } };\n }\n const projectKey = body.project.trim();\n const config = getProjectSync(projectKey);\n if (!config) return { status: 404, body: { error: `Unknown project key: ${projectKey}` } };\n\n const { isMergeTrainEnabledForProject } = await import('../../../lib/overdeck/merge-sync.js');\n if (!isMergeTrainEnabledForProject(config)) {\n return { status: 409, body: { error: `merge-train is disabled for project ${projectKey}` } };\n }\n\n const ordered = await (deps.getOrderedIssueIds ?? defaultOrderedIssueIdsForProject)(resolve(config.path));\n const issueIds = ordered.slice(0, n);\n const memberships = await (deps.gatherEligibility ?? gatherMergeEligibility)(issueIds);\n for (const issueId of issueIds) {\n const membership = memberships.get(issueId.toUpperCase());\n if (!membership || !isMergeEligible(membership)) {\n const reason = membership?.reasons.join('; ') || 'pipeline membership unavailable';\n return { status: 409, body: { error: `${issueId} is not merge-eligible: ${reason}` } };\n }\n }\n const { shipMergeBatch } = await import('../../../lib/cloister/merge-batch.js');\n const outcomes = await shipMergeBatch(issueIds, { merge: deps.merge ?? defaultMergeOne });\n return { status: 200, body: { projectKey, outcomes } };\n}\n\n/**\n * PAN-1696: forced reconcile. With `{ project }` it rebuilds that one project's\n * generation; with no body it reconciles every merge-train-enabled project.\n */\nexport async function postMergeTrainAssemblePayload(payload: unknown): Promise<{ status: number; body: unknown }> {\n // Only a JSON OBJECT may reach the all-projects path. `\"x\"`, `123`, `null` and\n // `[…]` are all valid JSON that would otherwise read as \"no project named\" and\n // force a git fetch/worktree sweep across every tracked repo. An absent body\n // arrives here as {} from readUnknownJsonBody, which is the deliberate form.\n if (!isJsonObject(payload)) {\n return { status: 400, body: { error: 'body must be a JSON object: {} for all projects, or { project }' } };\n }\n // Only an ABSENT project field means \"every project\". A PRESENT but unusable one\n // (42, null, \"\", {}) is a malformed SCOPED request, and letting it fall through\n // would silently widen it into the broadest git/UAT write we have.\n if ('project' in payload) {\n const raw = payload['project'];\n if (typeof raw !== 'string' || raw.trim() === '') {\n return {\n status: 400,\n body: { error: 'project must be a non-empty string; omit the field entirely to reconcile every project' },\n };\n }\n const projectKey = raw.trim();\n const config = getProjectSync(projectKey);\n if (!config) return { status: 404, body: { error: `Unknown project key: ${projectKey}` } };\n const { runUatTrainReconcile } = await import('../services/uat-train.js');\n const result = await runUatTrainReconcile({ force: true, projectRoot: resolve(config.path) });\n return { status: 200, body: { projects: [{ projectKey, result }] } };\n }\n\n const { runUatTrainReconcileAllProjects } = await import('../services/uat-train.js');\n const results = await runUatTrainReconcileAllProjects({ force: true });\n return { status: 200, body: { projects: results } };\n}\n\n/**\n * PAN-1737 generation names contain a slash (`uat/pan-otter-0610`); URL params\n * carry the name WITHOUT the `uat/` prefix and handlers reconstitute it. Same\n * reconstruction as the legacy flywheel routes so links stay interchangeable.\n */\nfunction uatGenerationNameFromParam(param: string): string {\n const decoded = decodeURIComponent(param);\n return decoded.startsWith('uat/') ? decoded : `uat/${decoded}`;\n}\n\nexport async function postMergeTrainGenerationShipPayload(\n name: string,\n version: string,\n): Promise<{ status: number; body: unknown }> {\n const { getUatGeneration } = await import('../../../lib/overdeck/merge-sync.js');\n const generation = getUatGeneration(name);\n if (!generation) return { status: 404, body: { error: `No UAT generation named ${name}` } };\n\n const { shipPromotedBatch, ShipPromotedBatchError } = await import('../services/generation-ship.js');\n try {\n return {\n status: 200,\n body: await shipPromotedBatch({\n generationName: name,\n projectRoot: generation.projectRoot,\n version,\n }),\n };\n } catch (error) {\n if (error instanceof ShipPromotedBatchError) {\n const status = error.reason === 'not-found' ? 404 : error.reason === 'wrong-status' ? 409 : 422;\n return { status, body: { error: error.message } };\n }\n throw error;\n }\n}\n\n// ─── Routes ───────────────────────────────────────────────────────────────────\n\nconst getMergeTrainQueuesRoute = HttpRouter.add(\n 'GET',\n '/api/merge-train/queues',\n httpHandler(Effect.gen(function* () {\n return jsonResponse(yield* Effect.promise(() => getMergeTrainQueuesPayload()));\n })),\n);\n\nconst getMergeTrainGenerationsRoute = HttpRouter.add(\n 'GET',\n '/api/merge-train/generations',\n httpHandler(Effect.gen(function* () {\n return jsonResponse(yield* Effect.promise(() => getMergeTrainGenerationsPayload()));\n })),\n);\n\nconst postMergeTrainGenerationStackRoute = HttpRouter.add(\n 'POST',\n '/api/merge-train/generations/:name/stack',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const authError = rejectUnsafeDashboardMutationRequest(request);\n if (authError) return authError;\n const params = yield* HttpRouter.params;\n const name = uatGenerationNameFromParam(params['name'] ?? '');\n const { postUatGenerationStackPayload } = yield* Effect.promise(() => import('../services/uat-train.js'));\n const result = yield* Effect.promise(() => postUatGenerationStackPayload(name));\n if (!result.ok) return jsonResponse({ error: result.error }, { status: result.status });\n return jsonResponse({ frontendUrl: result.frontendUrl, evicted: result.evicted });\n })),\n);\n\nconst postMergeTrainGenerationPromoteRoute = HttpRouter.add(\n 'POST',\n '/api/merge-train/generations/:name/promote',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const authError = rejectUnsafeDashboardMutationRequest(request);\n if (authError) return authError;\n const parsed = yield* readUnknownJsonBody;\n if (!parsed.ok) return jsonResponse({ error: parsed.error }, { status: 400 });\n if (!isJsonObject(parsed.body)) {\n return jsonResponse({ error: 'body must be a JSON object: { shipVersion? }' }, { status: 400 });\n }\n const rawShipVersion = parsed.body['shipVersion'];\n if (rawShipVersion !== undefined && (typeof rawShipVersion !== 'string' || !/^\\d+\\.\\d+\\.\\d+$/.test(rawShipVersion))) {\n return jsonResponse({ error: 'shipVersion must look like 48.8.0' }, { status: 400 });\n }\n const params = yield* HttpRouter.params;\n const name = uatGenerationNameFromParam(params['name'] ?? '');\n const { postUatGenerationPromotePayload } = yield* Effect.promise(() => import('../services/uat-train.js'));\n const { firePostMergeLifecycle } = yield* Effect.promise(() => import('./specialists.js'));\n const result = yield* Effect.promise(() => postUatGenerationPromotePayload(\n name,\n firePostMergeLifecycle,\n rawShipVersion,\n ));\n if (!result.success) {\n const status = result.reason === 'not-found' ? 404 : result.reason === 'merge-failed' ? 500 : 409;\n return jsonResponse(result, { status });\n }\n return jsonResponse(result);\n })),\n);\n\nconst postMergeTrainGenerationShipRoute = HttpRouter.add(\n 'POST',\n '/api/merge-train/generations/:name/ship',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const authError = rejectUnsafeDashboardMutationRequest(request);\n if (authError) return authError;\n const parsed = yield* readUnknownJsonBody;\n if (!parsed.ok) return jsonResponse({ error: parsed.error }, { status: 400 });\n if (!isJsonObject(parsed.body)) {\n return jsonResponse({ error: 'version must look like 48.8.0' }, { status: 400 });\n }\n const version = parsed.body['version'];\n if (typeof version !== 'string' || !/^\\d+\\.\\d+\\.\\d+$/.test(version)) {\n return jsonResponse({ error: 'version must look like 48.8.0' }, { status: 400 });\n }\n\n const params = yield* HttpRouter.params;\n const name = uatGenerationNameFromParam(params['name'] ?? '');\n const result = yield* Effect.promise(() => postMergeTrainGenerationShipPayload(name, version));\n return jsonResponse(result.body, { status: result.status });\n })),\n);\n\nconst postMergeTrainAssembleRoute = HttpRouter.add(\n 'POST',\n '/api/merge-train/assemble',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const authError = rejectUnsafeDashboardMutationRequest(request);\n if (authError) return authError;\n const parsed = yield* readUnknownJsonBody;\n // Malformed JSON must 400. Coercing it to {} would silently mean \"no project\n // named\", i.e. a forced reconcile of EVERY tracked project — a git-heavy\n // fetch/worktree sweep across every repo triggered by a typo'd body. A\n // genuinely empty body still parses to {} in readUnknownJsonBody, so the\n // all-projects form keeps working.\n if (!parsed.ok) return jsonResponse({ error: parsed.error }, { status: 400 });\n const result = yield* Effect.promise(() => postMergeTrainAssemblePayload(parsed.body));\n return jsonResponse(result.body, { status: result.status });\n })),\n);\n\nconst postMergeTrainMergeNextRoute = HttpRouter.add(\n 'POST',\n '/api/merge-train/merge-next',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const authError = rejectUnsafeDashboardMutationRequest(request);\n if (authError) return authError;\n const parsed = yield* readUnknownJsonBody;\n if (!parsed.ok) return jsonResponse({ error: parsed.error }, { status: 400 });\n const result = yield* Effect.promise(() => postMergeTrainMergeNextPayload(parsed.body));\n return jsonResponse(result.body, { status: result.status });\n })),\n);\n\n\n// ─── Moved from /api/flywheel/* (PAN-3917 W6, D3) ────────────────────────────\n//\n// `config` and the auto-merge surface belong to the merge train, not to a\n// flywheel run. The flywheel is a loop skill now (D12), so these gate on the\n// derived issue state instead of a run id or a review-status record.\n\nfunction requireTrustedOrigin(request: HttpServerRequest.HttpServerRequest) {\n if (hasDashboardInternalToken(request)) return null;\n const originCheck = validateOrigin(request);\n if (!originCheck.ok) return jsonResponse({ error: originCheck.error }, { status: 403 });\n return rejectUnsafeDashboardMutationRequest(request);\n}\n\nexport interface MergeTrainConfigBody {\n auto_pickup_backlog: boolean;\n require_uat_before_merge: boolean;\n merge_train_enabled: boolean;\n}\n\nexport function getMergeTrainConfigPayload(): MergeTrainConfigBody {\n return {\n auto_pickup_backlog: isFlywheelAutoPickupBacklog(),\n require_uat_before_merge: isFlywheelRequireUatBeforeMerge(),\n merge_train_enabled: isMergeTrainEnabled(),\n };\n}\n\nexport async function postMergeTrainConfigPayload(payload: unknown): Promise<{ status: number; body: unknown }> {\n if (!isJsonObject(payload)) {\n return { status: 400, body: { error: 'Request body must be a JSON object' } };\n }\n for (const key of ['auto_pickup_backlog', 'require_uat_before_merge', 'merge_train_enabled'] as const) {\n if (payload[key] !== undefined && typeof payload[key] !== 'boolean') {\n return { status: 400, body: { error: `${key} must be a boolean` } };\n }\n }\n if (payload['auto_pickup_backlog'] !== undefined) setFlywheelAutoPickupBacklog(payload['auto_pickup_backlog'] as boolean);\n if (payload['require_uat_before_merge'] !== undefined) setFlywheelRequireUatBeforeMerge(payload['require_uat_before_merge'] as boolean);\n if (payload['merge_train_enabled'] !== undefined) setMergeTrainEnabled(payload['merge_train_enabled'] as boolean);\n return { status: 200, body: getMergeTrainConfigPayload() };\n}\n\nexport interface AutoMergeScheduleDeps {\n now?: () => Date;\n isRequireUatBeforeMerge?: () => boolean;\n isMergeTrainEnabled?: () => boolean;\n isEligible?: (issueId: string) => Promise<AutoMergeEligibility>;\n /** The one merge gate (#4040); `evaluateIssueMergeGate` by default. */\n mergeGate?: (issueId: string) => Promise<MergeGateResult>;\n resolveProject?: (issueId: string) => ResolvedProject | null;\n schedule?: (input: ScheduleAutoMergeInput) => ScheduleAutoMergeResult;\n announce?: (issueId: string, entry: PendingAutoMerge) => void;\n getProjectAutoMergeDefault?: (issueId: string) => ProjectAutoMergeDefault;\n /** The issue's tracker labels; defaults to the dashboard's cached tracker row. */\n getIssueLabels?: (issueId: string) => readonly string[];\n /** #3983: true when the cached tracker row says the issue is closed. */\n isIssueClosed?: (issueId: string) => boolean;\n}\n\n/**\n * PAN-3932: the issue's `auto-merge` / `hold-for-uat` labels from the cached\n * tracker row. The auto-merge policy map is polled across every in-flight\n * issue, so it reads the cache rather than asking the forge per issue.\n */\nfunction cachedIssueLabels(issueId: string): readonly string[] {\n try {\n return getSharedIssueService().getTrackerIssue(issueId)?.labels ?? [];\n } catch {\n return [];\n }\n}\n\n/**\n * #3983: whether the cached tracker row says the issue is closed. An issue the\n * cache does not know reads as open here; the executor reads the tracker live\n * before it merges.\n */\nfunction cachedIssueClosed(issueId: string): boolean {\n try {\n return getSharedIssueService().getTrackerIssue(issueId)?.open === false;\n } catch {\n return false;\n }\n}\n\nexport interface AutoMergeCancelDeps {\n now?: () => Date;\n getPending?: (issueId: string) => PendingAutoMerge | null;\n cancel?: (id: number, cancelledBy: string) => boolean;\n countRemaining?: (issueId: string) => number;\n announce?: (issueId: string) => void;\n /** Drop the issue's waiting merge-queue entry; `removeQueuedMerge` by default. */\n removeQueued?: (issueId: string) => number;\n}\n\nfunction announceAutoMergeScheduled(issueId: string, _entry: PendingAutoMerge): void {\n emitActivityTts({\n utterance: `${issueId} auto-merging in 5 minutes; pan merge cancel ${issueId} to abort`,\n priority: 1,\n issueId,\n source: 'dashboard',\n eventType: 'auto-merge-scheduled',\n });\n}\n\nfunction announceAutoMergeCancelled(issueId: string): void {\n emitActivityTts({\n utterance: `auto-merge cancelled for ${issueId}`,\n priority: 1,\n issueId,\n source: 'dashboard',\n eventType: 'auto-merge-cancelled',\n });\n}\n\ntype AutoMergePolicyDeps = Pick<\n AutoMergeScheduleDeps,\n 'getIssueLabels' | 'getProjectAutoMergeDefault' | 'isRequireUatBeforeMerge' | 'isMergeTrainEnabled' | 'isIssueClosed'\n>;\n\n/**\n * The schedule door's policy check, which reads no forge: the issue's UAT hold\n * (its `auto-merge` / `hold-for-uat` label (PAN-3932), then the project\n * default, then global), the merge-train switch, and a closed tracker issue\n * (from the cached tracker row). The scheduler (#3983) asks it first, so a held\n * PR costs no forge read per tick.\n */\nexport function autoMergePolicyRefusal(\n issueId: string,\n deps: AutoMergePolicyDeps = {},\n): { status: 412 | 422; body: { error: string } } | null {\n if ((deps.isIssueClosed ?? cachedIssueClosed)(issueId)) {\n return { status: 422, body: { error: `${issueId} is closed in the tracker` } };\n }\n const labels = (deps.getIssueLabels ?? cachedIssueLabels)(issueId);\n const projectDefault = (deps.getProjectAutoMergeDefault ?? getProjectAutoMergeDefault)(issueId);\n const globalRequireUat = (deps.isRequireUatBeforeMerge ?? isFlywheelRequireUatBeforeMerge)();\n if (shouldHoldForUat(autoMergeFromLabels(labels), projectDefault, globalRequireUat)) {\n return { status: 412, body: { error: 'UAT is still required before merge' } };\n }\n if (!(deps.isMergeTrainEnabled ?? isMergeTrainEnabled)()) {\n return { status: 412, body: { error: 'Merge train is disabled' } };\n }\n return null;\n}\n\n/**\n * Schedule an auto-merge. The gate is the one merge gate, `evaluateIssueMergeGate`\n * (#4040): approved (a forge review or a trusted verdict marker), green checks,\n * and `mergeable` on the PR the gate links to the issue (FR-9).\n */\nexport async function postAutoMergeSchedulePayload(payload: unknown, deps: AutoMergeScheduleDeps = {}) {\n if (!isJsonObject(payload)) {\n return { status: 400, body: { error: 'Request body must be a JSON object' } };\n }\n const rawIssueId = payload['issueId'];\n if (typeof rawIssueId !== 'string' || rawIssueId.trim().length === 0) {\n return { status: 400, body: { error: 'issueId must be a non-empty string' } };\n }\n const issueId = rawIssueId.trim().toUpperCase();\n\n const refusal = autoMergePolicyRefusal(issueId, deps);\n if (refusal) return refusal;\n\n // #3983: the automatic path needs an approval bound to the PR head.\n const gate = await (deps.mergeGate ?? ((id: string) => evaluateIssueMergeGate(id)))(issueId);\n if (!gate.ready) {\n return { status: 422, body: { error: `${issueId} is not ready to merge: ${gate.reason ?? 'the merge gate refused it'}` } };\n }\n\n const eligibility = await (deps.isEligible ?? isAutoMergeEligible)(issueId);\n if (!eligibility.eligible) {\n return { status: 422, body: { error: eligibility.reason } };\n }\n\n const prUrl = gate.facts.url;\n if (!prUrl) {\n return { status: 422, body: { error: `No pull request for ${issueId}` } };\n }\n const artifactRef = parseArtifactRef(prUrl);\n if (artifactRef === null) {\n return { status: 422, body: { error: `Pull request URL for ${issueId} is not a recognized forge artifact` } };\n }\n\n const project = (deps.resolveProject ?? resolveProjectFromIssueSync)(issueId);\n if (!project) return { status: 422, body: { error: `Unknown project for issue ${issueId}` } };\n\n const scheduledAt = (deps.now ?? (() => new Date()))();\n const scheduledMergeAt = new Date(scheduledAt.getTime() + AUTO_MERGE_COOLDOWN_MS);\n const result = (deps.schedule ?? scheduleAutoMergeWithResult)({\n issueId,\n prUrl,\n prNumber: artifactRef.number,\n projectKey: project.projectKey,\n forge: artifactRef.forge,\n scheduledMergeAt: scheduledMergeAt.toISOString(),\n scheduledAt: scheduledAt.toISOString(),\n ...(gate.facts.headSha ? { headSha: gate.facts.headSha } : {}),\n });\n if (result.created) (deps.announce ?? announceAutoMergeScheduled)(issueId, result.entry);\n return { status: 200, body: result.entry };\n}\n\nconst AUTO_MERGE_POLL_LIMIT = 100;\n\nexport function getPendingAutoMergePayload(): PendingAutoMerge[] {\n return listActiveAutoMerges(AUTO_MERGE_POLL_LIMIT);\n}\n\nexport function getAutoMergeProblemPayload(): PendingAutoMerge[] {\n return listProblemAutoMerges(AUTO_MERGE_POLL_LIMIT);\n}\n\nexport function deleteAutoMergePayload(issueIdParam: string, deps: AutoMergeCancelDeps = {}) {\n const issueId = issueIdParam.trim().toUpperCase();\n if (!issueId) return { status: 400, body: { error: 'issueId must be a non-empty string' } };\n\n const entry = (deps.getPending ?? getActionableAutoMerge)(issueId);\n if (!entry) return { status: 404, body: { error: `No pending auto-merge for ${issueId}` } };\n if (entry.status === 'merging') {\n return { status: 409, body: { error: `Auto-merge cooldown has expired for ${issueId}; merge is in progress` } };\n }\n\n const cancelledAt = (deps.now ?? (() => new Date()))().toISOString();\n const cancelled = (deps.cancel ?? cancelPending)(entry.id, 'operator');\n if (!cancelled) {\n const raced = (deps.getPending ?? getActionableAutoMerge)(issueId);\n if (raced?.status === 'merging') {\n return { status: 409, body: { error: `Auto-merge cooldown has expired for ${issueId}; merge is in progress` } };\n }\n return { status: 404, body: { error: `No pending auto-merge for ${issueId}` } };\n }\n\n // #4066 review: a cancel stops the merge wherever it waits, the project\n // merge queue included.\n try {\n (deps.removeQueued ?? removeQueuedMerge)(issueId);\n } catch (error) {\n console.warn(`[auto-merge] could not drop ${issueId} from the merge queue: ${error instanceof Error ? error.message : String(error)}`);\n }\n (deps.announce ?? announceAutoMergeCancelled)(issueId);\n return {\n status: 200,\n body: {\n ...entry,\n status: 'cancelled' as const,\n cancelledAt,\n cancelledBy: 'operator',\n remainingActionable: (deps.countRemaining ?? countActionableAutoMerges)(issueId),\n },\n };\n}\n\n/**\n * The effective auto-merge routing key per in-flight issue (PAN-3917, D3).\n *\n * There is no per-issue routing key record any more. The answer is derived:\n * the issue's own `auto-merge` / `hold-for-uat` tracker label (PAN-3932),\n * then its project default, then the global `require_uat_before_merge`.\n * `autoMerge: true` means the train may ship it when green; `false` means\n * hold for UAT.\n */\nexport async function getAutoMergePolicyPayload(): Promise<{\n issues: Array<{ issueId: string; autoMerge: boolean }>;\n}> {\n const globalRequireUat = isFlywheelRequireUatBeforeMerge();\n const { loadIssueStatesForProject } = await import('../services/derived-issue-state.js');\n const IN_FLIGHT = new Set(['working', 'in-review', 'changes-requested', 'ready']);\n\n // PAN-3925: the projects derive in parallel, each over its PR listing\n // served stale-while-revalidate, so one slow `gh pr list` no longer\n // serializes behind every other project's.\n const perProject = await Promise.all(listProjectsSync().map(async ({ config }) => {\n const projectPath = resolve(config.path);\n try {\n return await loadIssueStatesForProject(projectPath, await listCandidateIssueIds(projectPath));\n } catch (error) {\n console.warn(`[merge-train] auto-merge policy for ${config.name} failed: ${error instanceof Error ? error.message : String(error)}`);\n return null;\n }\n }));\n\n const issues: Array<{ issueId: string; autoMerge: boolean }> = [];\n for (const states of perProject) {\n if (!states) continue;\n for (const [issueId, derived] of states) {\n if (!IN_FLIGHT.has(derived.state)) continue;\n const held = shouldHoldForUat(\n autoMergeFromLabels(cachedIssueLabels(issueId)),\n getProjectAutoMergeDefault(issueId),\n globalRequireUat,\n );\n issues.push({ issueId, autoMerge: !held });\n }\n }\n return { issues };\n}\n\n/** Issue ids with an open PR in the project — the only ones the train can route. */\nasync function listCandidateIssueIds(projectPath: string): Promise<string[]> {\n const { listRepoPullRequestsStaleOk, issueIdFromBranch } = await import('../services/derived-issue-state.js');\n const rows = await listRepoPullRequestsStaleOk(projectPath);\n return rows.flatMap((row) => {\n const issueId = issueIdFromBranch(row.headRefName);\n return issueId ? [issueId] : [];\n });\n}\n\nconst getAutoMergePolicyRoute = HttpRouter.add(\n 'GET',\n '/api/merge-train/auto-merge',\n httpHandler(Effect.gen(function* () {\n return jsonResponse(yield* Effect.promise(() => getAutoMergePolicyPayload()));\n })),\n);\n\n/** Whether an autonomous merge can actually be performed (GitHub App or gh CLI). */\nconst getMergeBackendRoute = HttpRouter.add(\n 'GET',\n '/api/merge-train/merge-backend',\n httpHandler(Effect.gen(function* () {\n const { getMergeBackendStatus } = yield* Effect.promise(() => import('../../../lib/github-app.js'));\n return jsonResponse(yield* Effect.promise(() => getMergeBackendStatus()));\n })),\n);\n\nconst getMergeTrainConfigRoute = HttpRouter.add(\n 'GET',\n '/api/merge-train/config',\n httpHandler(Effect.gen(function* () {\n return jsonResponse(getMergeTrainConfigPayload());\n })),\n);\n\nconst postMergeTrainConfigRoute = HttpRouter.add(\n 'POST',\n '/api/merge-train/config',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const originError = requireTrustedOrigin(request);\n if (originError) return originError;\n const parsed = yield* readUnknownJsonBody;\n if (!parsed.ok) return jsonResponse({ error: parsed.error }, { status: 400 });\n const result = yield* Effect.promise(() => postMergeTrainConfigPayload(parsed.body));\n return jsonResponse(result.body, { status: result.status });\n })),\n);\n\nconst getPendingAutoMergeRoute = HttpRouter.add(\n 'GET',\n '/api/merge-train/auto-merge/pending',\n httpHandler(Effect.gen(function* () {\n return jsonResponse(getPendingAutoMergePayload());\n })),\n);\n\nconst getAutoMergeProblemsRoute = HttpRouter.add(\n 'GET',\n '/api/merge-train/auto-merge/problems',\n httpHandler(Effect.gen(function* () {\n return jsonResponse(getAutoMergeProblemPayload());\n })),\n);\n\nconst postAutoMergeScheduleRoute = HttpRouter.add(\n 'POST',\n '/api/merge-train/auto-merge/schedule',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const originError = requireTrustedOrigin(request);\n if (originError) return originError;\n const parsed = yield* readUnknownJsonBody;\n if (!parsed.ok) return jsonResponse({ error: parsed.error }, { status: 400 });\n const result = yield* Effect.promise(() => postAutoMergeSchedulePayload(parsed.body));\n return jsonResponse(result.body, { status: result.status });\n })),\n);\n\nconst deleteAutoMergeRoute = HttpRouter.add(\n 'DELETE',\n '/api/merge-train/auto-merge/:id',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const originError = requireTrustedOrigin(request);\n if (originError) return originError;\n const params = yield* HttpRouter.params;\n const result = deleteAutoMergePayload(params['id'] ?? '');\n return jsonResponse(result.body, { status: result.status });\n })),\n);\n\nconst getMergeBlockersRoute = HttpRouter.add(\n 'GET',\n '/api/merge-train/merge-blockers',\n httpHandler(Effect.gen(function* () {\n return jsonResponse(getMergeBlockersPayload());\n })),\n);\n\nexport const mergeTrainRouteLayer = Layer.mergeAll(\n getMergeTrainQueuesRoute,\n getMergeTrainGenerationsRoute,\n postMergeTrainGenerationStackRoute,\n postMergeTrainGenerationPromoteRoute,\n postMergeTrainGenerationShipRoute,\n postMergeTrainAssembleRoute,\n postMergeTrainMergeNextRoute,\n getMergeTrainConfigRoute,\n postMergeTrainConfigRoute,\n getAutoMergePolicyRoute,\n getMergeBackendRoute,\n getPendingAutoMergeRoute,\n getAutoMergeProblemsRoute,\n postAutoMergeScheduleRoute,\n deleteAutoMergeRoute,\n getMergeBlockersRoute,\n);\n\nexport default mergeTrainRouteLayer;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"merge-train-pMKZ_hti.js","names":["nodeServicesLayer"],"sources":["../../src/lib/cloister/merge-eligibility.ts","../../src/lib/cloister/auto-merge-config.ts","../../src/lib/cloister/merge-blockers.ts","../../src/dashboard/server/routes/merge-train.ts"],"sourcesContent":["import { gatherProjectLensSignals } from '../pipeline-membership-gather.js';\nimport {\n resolvePipelineMembership,\n type PipelineMembership,\n} from '../pipeline-membership.js';\nimport {\n getProjectSync,\n resolveProjectFromIssueSync,\n type ProjectConfig,\n} from '../projects.js';\n\nexport interface MergeEligibilityDeps {\n resolveProject: typeof resolveProjectFromIssueSync;\n getProject: typeof getProjectSync;\n gather: typeof gatherProjectLensSignals;\n}\n\nconst defaultDeps: MergeEligibilityDeps = {\n resolveProject: resolveProjectFromIssueSync,\n getProject: getProjectSync,\n gather: gatherProjectLensSignals,\n};\n\nexport function isMergeEligible(membership: PipelineMembership): boolean {\n return membership.bucket === 'in_flight';\n}\n\n/** Classify candidates through one durable-lens gather per represented project. */\nexport async function gatherMergeEligibility(\n issueIds: string[],\n deps: MergeEligibilityDeps = defaultDeps,\n): Promise<Map<string, PipelineMembership>> {\n const candidatesByProject = new Map<string, { project: ProjectConfig; issueIds: Set<string> }>();\n\n for (const issueId of issueIds) {\n const resolved = deps.resolveProject(issueId);\n if (!resolved) continue;\n const configured = deps.getProject(resolved.projectKey);\n if (!configured) continue;\n const existing = candidatesByProject.get(resolved.projectKey);\n if (existing) {\n existing.issueIds.add(issueId.toUpperCase());\n } else {\n candidatesByProject.set(resolved.projectKey, {\n project: { ...configured, path: resolved.projectPath },\n issueIds: new Set([issueId.toUpperCase()]),\n });\n }\n }\n\n const memberships = new Map<string, PipelineMembership>();\n await Promise.all([...candidatesByProject.values()].map(async ({ project, issueIds: candidates }) => {\n const signals = await deps.gather(project);\n for (const signal of signals) {\n const issueId = signal.issueId.toUpperCase();\n if (candidates.has(issueId)) memberships.set(issueId, resolvePipelineMembership(signal));\n }\n }));\n return memberships;\n}\n","export const AUTO_MERGE_COOLDOWN_MS = 300_000;\n","/**\n * PRs that are done being reviewed but cannot merge for a forge-native reason\n * (PAN-1620, re-pointed by PAN-3917): merge conflicts, failing checks, or a\n * not-mergeable state.\n *\n * Before the cut this read `blockerReasons` off the `review_status` row — a\n * stored copy a patrol had to keep in step with GitHub. Now every reason comes\n * straight from the forge's account of the PR, so there is nothing to drift.\n * The in-flight candidate set comes from the durable pipeline lenses (tracker +\n * git + PR), never from a record.\n */\nimport { listInFlightIssuesWithPr, type MergeCandidateDeps } from './merge-ready-set.js';\nimport { getPrFacts, type PrFacts } from './pr-facts.js';\n\nexport type MergeBlockerType = 'merge_conflict' | 'failing_checks' | 'not_mergeable';\n\nexport interface MergeBlocker {\n issueId: string;\n prUrl?: string;\n reasons: Array<{ type: MergeBlockerType; summary: string }>;\n}\n\nexport type MergeBlockersDeps = MergeCandidateDeps;\n\n/**\n * Classify one PR's forge state into blocker reasons. Pure: the caller supplies\n * the facts. An approved PR with green checks and `mergeable` true has none.\n */\nexport function blockerReasonsFor(facts: PrFacts): MergeBlocker['reasons'] {\n if (!facts.exists || !facts.open || facts.merged) return [];\n const reasons: MergeBlocker['reasons'] = [];\n if (facts.mergeable === false) {\n const conflicting = (facts.mergeableState ?? '').includes('conflict')\n || facts.mergeableState === 'conflicting';\n reasons.push(conflicting\n ? { type: 'merge_conflict', summary: 'the branch conflicts with the base branch' }\n : { type: 'not_mergeable', summary: `the forge reports the PR is not mergeable${facts.mergeableState ? ` (${facts.mergeableState})` : ''}` });\n }\n if (facts.checks === 'red') {\n reasons.push({ type: 'failing_checks', summary: `CI checks are failing on ${facts.headSha?.slice(0, 8) ?? 'HEAD'}` });\n }\n return reasons;\n}\n\n/**\n * Every in-flight issue whose PR is approved but blocked by the forge.\n *\n * Async because the forge is the source: callers that used to read SQLite\n * synchronously (the dashboard route and `pan flywheel merge-blockers`) await it.\n */\nexport async function getMergeBlockersPayload(deps: MergeBlockersDeps = {}): Promise<MergeBlocker[]> {\n const candidates = await listInFlightIssuesWithPr(deps);\n\n const getFacts = deps.getFacts ?? getPrFacts;\n const out: MergeBlocker[] = [];\n for (const issueId of candidates) {\n const facts = await getFacts(issueId);\n if (!facts.approved) continue;\n const reasons = blockerReasonsFor(facts);\n if (reasons.length === 0) continue;\n out.push({ issueId, prUrl: facts.url ?? undefined, reasons });\n }\n return out;\n}\n","/**\n * PAN-1696 merge-train-routes: the aggregate `/api/merge-train/*` namespace.\n *\n * The merge-train (conflict-aware merge order + UAT batch trains) is a\n * per-project pipeline concern, but its only HTTP surface used to live under\n * `/api/flywheel/*` and answered for the Overdeck repo alone. These routes\n * answer for EVERY tracked project and require no active flywheel run: the\n * ready set is derived from the forge — approvals, checks, and mergeability.\n *\n * PAN-3917 W6: the flywheel routes are gone, and the merge-train config and\n * auto-merge surface that lived under `/api/flywheel/*` moved here, under\n * `/api/merge-train/config` and `/api/merge-train/auto-merge/*`. Scheduling an\n * auto-merge no longer consults a flywheel run or a review-status record: it\n * gates on the derived issue state, which is approvals plus green checks plus\n * forge mergeability (FR-9, D3).\n */\n\nimport { Effect, Layer } from 'effect';\nimport { HttpRouter, HttpServerRequest } from 'effect/unstable/http';\nimport { layer as nodeServicesLayer } from '@effect/platform-node/NodeServices';\nimport { resolve } from 'node:path';\nimport { httpHandler } from './http-handler.js';\nimport { jsonResponse } from '../http-helpers.js';\nimport { hasDashboardInternalToken, rejectUnsafeDashboardMutationRequest } from './dashboard-auth.js';\nimport { getProjectSync, listProjectsSync, resolveProjectFromIssueSync, type ProjectConfig, type ResolvedProject } from '../../../lib/projects.js';\nimport type { MergeQueueItem } from '../../../lib/flywheel-merge-order.js';\nimport { gatherMergeEligibility, isMergeEligible } from '../../../lib/cloister/merge-eligibility.js';\nimport { emitActivityTts } from '../../../lib/activity-logger.js';\nimport { parseArtifactRef } from '../../../lib/forge.js';\nimport { validateOrigin } from './origin-validation.js';\nimport { AUTO_MERGE_COOLDOWN_MS } from '../../../lib/cloister/auto-merge-config.js';\nimport { autoMergeFromLabels, isAutoMergeEligible, issueHoldsForUat, type AutoMergeEligibility } from '../../../lib/cloister/auto-merge-eligibility.js';\nimport {\n getProjectAutoMergeDefault,\n projectHoldsForUat,\n shouldHoldForUat,\n type ProjectAutoMergeDefault,\n} from '../../../lib/cloister/auto-merge-policy.js';\nimport { getMergeBlockersPayload } from '../../../lib/cloister/merge-blockers.js';\nimport {\n isFlywheelAutoPickupBacklog,\n isFlywheelRequireUatBeforeMerge,\n isMergeTrainEnabled,\n setFlywheelAutoPickupBacklog,\n setFlywheelRequireUatBeforeMerge,\n setMergeTrainEnabled,\n} from '../../../lib/overdeck/control-settings.js';\nimport {\n cancelPending,\n countActionableAutoMerges,\n getActionableAutoMerge,\n listActiveAutoMerges,\n listProblemAutoMerges,\n scheduleAutoMergeWithResult,\n type PendingAutoMerge,\n type ScheduleAutoMergeInput,\n type ScheduleAutoMergeResult,\n} from '../../../lib/overdeck/merge-sync.js';\nimport { removeQueuedMerge } from '../../../lib/overdeck/merge.js';\nimport { listReadyIssuesForProject } from '../services/derived-issue-state.js';\nimport { evaluateIssueMergeGate, type MergeGateResult } from '../../../lib/cloister/merge-gate.js';\nimport { getSharedIssueService } from '../services/issue-service-singleton.js';\nimport type { PipelineMembership } from '../../../lib/pipeline-membership.js';\n\nconst readUnknownJsonBody = Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const text = yield* request.text;\n try {\n return { ok: true as const, body: text ? (JSON.parse(text) as unknown) : {} };\n } catch {\n return { ok: false as const, error: 'Request body must be valid JSON' };\n }\n});\n\n/** A plain JSON object — not null, not an array, not a primitive. */\nfunction isJsonObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/** One project's merge-train queue as served by GET /api/merge-train/queues. */\nexport interface MergeTrainQueueEntry {\n projectKey: string;\n projectName: string;\n /** Effective per-project flag: the project override, else the global setting. */\n enabled: boolean;\n /**\n * PAN-3965: one ready feature still gets a batch (its UAT stack). With\n * exactly one feature queued this is that feature's own hold (its label,\n * then the project, then global); otherwise the project-level hold.\n * False = one ready feature merges directly.\n */\n holdsForUat: boolean;\n queue: MergeQueueItem[];\n}\n\n/** One project's UAT generation chain as served by GET /api/merge-train/generations. */\nexport interface MergeTrainGenerationsEntry {\n projectKey: string;\n projectName: string;\n enabled: boolean;\n generations: unknown[];\n}\n\n/**\n * A disabled project reports an empty queue rather than being omitted, so the\n * multi-project view can render it as an explicitly-off row instead of making\n * \"off\" and \"no ready work\" look identical.\n */\nasync function queueEntryForProject(\n key: string,\n config: ProjectConfig,\n enabled: boolean,\n): Promise<MergeTrainQueueEntry> {\n const globalRequireUat = isFlywheelRequireUatBeforeMerge();\n const holdsForUat = projectHoldsForUat(config, globalRequireUat);\n const base = { projectKey: key, projectName: config.name, enabled, holdsForUat };\n if (!enabled) return { ...base, queue: [] };\n\n const projectPath = resolve(config.path);\n // PAN-3917 FR-9: the ready set is the forge's — approvals, green checks, and\n // mergeability — not a scan of review-status records.\n const candidates = await listReadyIssuesForProject(projectPath);\n if (candidates.length === 0) return { ...base, queue: [] };\n\n // Imported here so the conflict-ordering module's git work is only loaded\n // when a queue is actually computed.\n const { computeMergeQueueFromCandidates } = await import('../../../lib/flywheel-merge-order.js');\n const queue = await Effect.runPromise(\n computeMergeQueueFromCandidates(candidates, projectPath).pipe(Effect.provide(nodeServicesLayer)),\n );\n // Review of #4017: a lone ready feature's hold is its own (label, then\n // project, then global), the same one the reconciler applies — the page must\n // not say \"merges directly\" for a feature that gets a UAT batch.\n if (queue.length === 1) {\n return { ...base, holdsForUat: await issueHoldsForUat(queue[0]!.issueId, config, globalRequireUat), queue };\n }\n return { ...base, queue };\n}\n\n/**\n * PAN-1696 AC1: one entry per tracked project, with that project's effective\n * flag and queue, with no flywheel run active. One project's git failure must\n * not blank the whole response, so a failed project reports an empty queue.\n */\nexport async function getMergeTrainQueuesPayload(): Promise<MergeTrainQueueEntry[]> {\n const { isMergeTrainEnabledForProject } = await import('../../../lib/overdeck/merge-sync.js');\n const projects = listProjectsSync();\n const settled = await Promise.allSettled(\n projects.map(({ key, config }) =>\n queueEntryForProject(key, config, isMergeTrainEnabledForProject(config)),\n ),\n );\n return settled.flatMap((outcome, i) => {\n if (outcome.status === 'fulfilled') return [outcome.value];\n const entry = projects[i];\n if (!entry) return [];\n const reason = outcome.reason instanceof Error ? outcome.reason.message : String(outcome.reason);\n console.warn(`[merge-train] queue for project ${entry.key} failed: ${reason}`);\n return [{ projectKey: entry.key, projectName: entry.config.name, enabled: true, holdsForUat: projectHoldsForUat(entry.config, isFlywheelRequireUatBeforeMerge()), queue: [] }];\n });\n}\n\n/** PAN-1696 AC2: per-project generation chains across every tracked project. */\nexport async function getMergeTrainGenerationsPayload(): Promise<MergeTrainGenerationsEntry[]> {\n const { isMergeTrainEnabledForProject } = await import('../../../lib/overdeck/merge-sync.js');\n const { getUatGenerationsPayload } = await import('../services/uat-train.js');\n\n const settled = await Promise.allSettled(\n listProjectsSync().map(async ({ key, config }) => ({\n projectKey: key,\n projectName: config.name,\n enabled: isMergeTrainEnabledForProject(config),\n generations: await getUatGenerationsPayload(resolve(config.path)),\n })),\n );\n return settled.flatMap((outcome) => (outcome.status === 'fulfilled' ? [outcome.value] : []));\n}\n\n/**\n * PAN-1696 AC3: merge the first N issues of the named project's ready set,\n * one at a time, stopping at the first failure (the rest would need\n * re-rebasing). An unknown project key is a 4xx, never a silent empty merge.\n */\nexport interface MergeTrainMergeNextDeps {\n getOrderedIssueIds?: (projectPath: string) => Promise<string[]>;\n merge?: (issueId: string) => Promise<{ ok: true } | { ok: false; reason: string }>;\n gatherEligibility?: (issueIds: string[]) => Promise<Map<string, PipelineMembership>>;\n}\n\nasync function defaultOrderedIssueIdsForProject(projectPath: string): Promise<string[]> {\n const candidates = await listReadyIssuesForProject(projectPath);\n if (candidates.length === 0) return [];\n const { computeMergeQueueFromCandidates } = await import('../../../lib/flywheel-merge-order.js');\n const queue = await Effect.runPromise(\n computeMergeQueueFromCandidates(candidates, projectPath).pipe(Effect.provide(nodeServicesLayer)),\n );\n return queue.map((item) => item.issueId);\n}\n\nasync function defaultMergeOne(issueId: string): Promise<{ ok: true } | { ok: false; reason: string }> {\n const { triggerMerge } = await import('./workspaces/merge-ops.js');\n const r = await triggerMerge(issueId);\n return r.success ? { ok: true } : { ok: false, reason: r.error ?? r.message ?? 'merge failed' };\n}\n\nexport async function postMergeTrainMergeNextPayload(\n payload: unknown,\n deps: MergeTrainMergeNextDeps = {},\n): Promise<{ status: number; body: unknown }> {\n if (!isJsonObject(payload)) {\n return { status: 400, body: { error: 'body must be a JSON object: { n, project }' } };\n }\n const body = payload as { n?: unknown; project?: unknown };\n const n = typeof body.n === 'number' && Number.isFinite(body.n) ? Math.floor(body.n) : 0;\n if (n <= 0) return { status: 400, body: { error: 'n must be a positive integer' } };\n\n if (typeof body.project !== 'string' || body.project.trim() === '') {\n return { status: 400, body: { error: 'project must be a non-empty string' } };\n }\n const projectKey = body.project.trim();\n const config = getProjectSync(projectKey);\n if (!config) return { status: 404, body: { error: `Unknown project key: ${projectKey}` } };\n\n const { isMergeTrainEnabledForProject } = await import('../../../lib/overdeck/merge-sync.js');\n if (!isMergeTrainEnabledForProject(config)) {\n return { status: 409, body: { error: `merge-train is disabled for project ${projectKey}` } };\n }\n\n const ordered = await (deps.getOrderedIssueIds ?? defaultOrderedIssueIdsForProject)(resolve(config.path));\n const issueIds = ordered.slice(0, n);\n const memberships = await (deps.gatherEligibility ?? gatherMergeEligibility)(issueIds);\n for (const issueId of issueIds) {\n const membership = memberships.get(issueId.toUpperCase());\n if (!membership || !isMergeEligible(membership)) {\n const reason = membership?.reasons.join('; ') || 'pipeline membership unavailable';\n return { status: 409, body: { error: `${issueId} is not merge-eligible: ${reason}` } };\n }\n }\n const { shipMergeBatch } = await import('../../../lib/cloister/merge-batch.js');\n const outcomes = await shipMergeBatch(issueIds, { merge: deps.merge ?? defaultMergeOne });\n return { status: 200, body: { projectKey, outcomes } };\n}\n\n/**\n * PAN-1696: forced reconcile. With `{ project }` it rebuilds that one project's\n * generation; with no body it reconciles every merge-train-enabled project.\n */\nexport async function postMergeTrainAssemblePayload(payload: unknown): Promise<{ status: number; body: unknown }> {\n // Only a JSON OBJECT may reach the all-projects path. `\"x\"`, `123`, `null` and\n // `[…]` are all valid JSON that would otherwise read as \"no project named\" and\n // force a git fetch/worktree sweep across every tracked repo. An absent body\n // arrives here as {} from readUnknownJsonBody, which is the deliberate form.\n if (!isJsonObject(payload)) {\n return { status: 400, body: { error: 'body must be a JSON object: {} for all projects, or { project }' } };\n }\n // Only an ABSENT project field means \"every project\". A PRESENT but unusable one\n // (42, null, \"\", {}) is a malformed SCOPED request, and letting it fall through\n // would silently widen it into the broadest git/UAT write we have.\n if ('project' in payload) {\n const raw = payload['project'];\n if (typeof raw !== 'string' || raw.trim() === '') {\n return {\n status: 400,\n body: { error: 'project must be a non-empty string; omit the field entirely to reconcile every project' },\n };\n }\n const projectKey = raw.trim();\n const config = getProjectSync(projectKey);\n if (!config) return { status: 404, body: { error: `Unknown project key: ${projectKey}` } };\n const { runUatTrainReconcile } = await import('../services/uat-train.js');\n const result = await runUatTrainReconcile({ force: true, projectRoot: resolve(config.path) });\n return { status: 200, body: { projects: [{ projectKey, result }] } };\n }\n\n const { runUatTrainReconcileAllProjects } = await import('../services/uat-train.js');\n const results = await runUatTrainReconcileAllProjects({ force: true });\n return { status: 200, body: { projects: results } };\n}\n\n/**\n * PAN-1737 generation names contain a slash (`uat/pan-otter-0610`); URL params\n * carry the name WITHOUT the `uat/` prefix and handlers reconstitute it. Same\n * reconstruction as the legacy flywheel routes so links stay interchangeable.\n */\nfunction uatGenerationNameFromParam(param: string): string {\n const decoded = decodeURIComponent(param);\n return decoded.startsWith('uat/') ? decoded : `uat/${decoded}`;\n}\n\nexport async function postMergeTrainGenerationShipPayload(\n name: string,\n version: string,\n): Promise<{ status: number; body: unknown }> {\n const { getUatGeneration } = await import('../../../lib/overdeck/merge-sync.js');\n const generation = getUatGeneration(name);\n if (!generation) return { status: 404, body: { error: `No UAT generation named ${name}` } };\n\n const { shipPromotedBatch, ShipPromotedBatchError } = await import('../services/generation-ship.js');\n try {\n return {\n status: 200,\n body: await shipPromotedBatch({\n generationName: name,\n projectRoot: generation.projectRoot,\n version,\n }),\n };\n } catch (error) {\n if (error instanceof ShipPromotedBatchError) {\n const status = error.reason === 'not-found' ? 404 : error.reason === 'wrong-status' ? 409 : 422;\n return { status, body: { error: error.message } };\n }\n throw error;\n }\n}\n\n// ─── Routes ───────────────────────────────────────────────────────────────────\n\nconst getMergeTrainQueuesRoute = HttpRouter.add(\n 'GET',\n '/api/merge-train/queues',\n httpHandler(Effect.gen(function* () {\n return jsonResponse(yield* Effect.promise(() => getMergeTrainQueuesPayload()));\n })),\n);\n\nconst getMergeTrainGenerationsRoute = HttpRouter.add(\n 'GET',\n '/api/merge-train/generations',\n httpHandler(Effect.gen(function* () {\n return jsonResponse(yield* Effect.promise(() => getMergeTrainGenerationsPayload()));\n })),\n);\n\nconst postMergeTrainGenerationStackRoute = HttpRouter.add(\n 'POST',\n '/api/merge-train/generations/:name/stack',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const authError = rejectUnsafeDashboardMutationRequest(request);\n if (authError) return authError;\n const params = yield* HttpRouter.params;\n const name = uatGenerationNameFromParam(params['name'] ?? '');\n const { postUatGenerationStackPayload } = yield* Effect.promise(() => import('../services/uat-train.js'));\n const result = yield* Effect.promise(() => postUatGenerationStackPayload(name));\n if (!result.ok) return jsonResponse({ error: result.error }, { status: result.status });\n return jsonResponse({ frontendUrl: result.frontendUrl, evicted: result.evicted });\n })),\n);\n\nconst postMergeTrainGenerationPromoteRoute = HttpRouter.add(\n 'POST',\n '/api/merge-train/generations/:name/promote',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const authError = rejectUnsafeDashboardMutationRequest(request);\n if (authError) return authError;\n const parsed = yield* readUnknownJsonBody;\n if (!parsed.ok) return jsonResponse({ error: parsed.error }, { status: 400 });\n if (!isJsonObject(parsed.body)) {\n return jsonResponse({ error: 'body must be a JSON object: { shipVersion? }' }, { status: 400 });\n }\n const rawShipVersion = parsed.body['shipVersion'];\n if (rawShipVersion !== undefined && (typeof rawShipVersion !== 'string' || !/^\\d+\\.\\d+\\.\\d+$/.test(rawShipVersion))) {\n return jsonResponse({ error: 'shipVersion must look like 48.8.0' }, { status: 400 });\n }\n const params = yield* HttpRouter.params;\n const name = uatGenerationNameFromParam(params['name'] ?? '');\n const { postUatGenerationPromotePayload } = yield* Effect.promise(() => import('../services/uat-train.js'));\n const { firePostMergeLifecycle } = yield* Effect.promise(() => import('./specialists.js'));\n const result = yield* Effect.promise(() => postUatGenerationPromotePayload(\n name,\n firePostMergeLifecycle,\n rawShipVersion,\n ));\n if (!result.success) {\n const status = result.reason === 'not-found' ? 404 : result.reason === 'merge-failed' ? 500 : 409;\n return jsonResponse(result, { status });\n }\n return jsonResponse(result);\n })),\n);\n\nconst postMergeTrainGenerationShipRoute = HttpRouter.add(\n 'POST',\n '/api/merge-train/generations/:name/ship',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const authError = rejectUnsafeDashboardMutationRequest(request);\n if (authError) return authError;\n const parsed = yield* readUnknownJsonBody;\n if (!parsed.ok) return jsonResponse({ error: parsed.error }, { status: 400 });\n if (!isJsonObject(parsed.body)) {\n return jsonResponse({ error: 'version must look like 48.8.0' }, { status: 400 });\n }\n const version = parsed.body['version'];\n if (typeof version !== 'string' || !/^\\d+\\.\\d+\\.\\d+$/.test(version)) {\n return jsonResponse({ error: 'version must look like 48.8.0' }, { status: 400 });\n }\n\n const params = yield* HttpRouter.params;\n const name = uatGenerationNameFromParam(params['name'] ?? '');\n const result = yield* Effect.promise(() => postMergeTrainGenerationShipPayload(name, version));\n return jsonResponse(result.body, { status: result.status });\n })),\n);\n\nconst postMergeTrainAssembleRoute = HttpRouter.add(\n 'POST',\n '/api/merge-train/assemble',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const authError = rejectUnsafeDashboardMutationRequest(request);\n if (authError) return authError;\n const parsed = yield* readUnknownJsonBody;\n // Malformed JSON must 400. Coercing it to {} would silently mean \"no project\n // named\", i.e. a forced reconcile of EVERY tracked project — a git-heavy\n // fetch/worktree sweep across every repo triggered by a typo'd body. A\n // genuinely empty body still parses to {} in readUnknownJsonBody, so the\n // all-projects form keeps working.\n if (!parsed.ok) return jsonResponse({ error: parsed.error }, { status: 400 });\n const result = yield* Effect.promise(() => postMergeTrainAssemblePayload(parsed.body));\n return jsonResponse(result.body, { status: result.status });\n })),\n);\n\nconst postMergeTrainMergeNextRoute = HttpRouter.add(\n 'POST',\n '/api/merge-train/merge-next',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const authError = rejectUnsafeDashboardMutationRequest(request);\n if (authError) return authError;\n const parsed = yield* readUnknownJsonBody;\n if (!parsed.ok) return jsonResponse({ error: parsed.error }, { status: 400 });\n const result = yield* Effect.promise(() => postMergeTrainMergeNextPayload(parsed.body));\n return jsonResponse(result.body, { status: result.status });\n })),\n);\n\n\n// ─── Moved from /api/flywheel/* (PAN-3917 W6, D3) ────────────────────────────\n//\n// `config` and the auto-merge surface belong to the merge train, not to a\n// flywheel run. The flywheel is a loop skill now (D12), so these gate on the\n// derived issue state instead of a run id or a review-status record.\n\nfunction requireTrustedOrigin(request: HttpServerRequest.HttpServerRequest) {\n if (hasDashboardInternalToken(request)) return null;\n const originCheck = validateOrigin(request);\n if (!originCheck.ok) return jsonResponse({ error: originCheck.error }, { status: 403 });\n return rejectUnsafeDashboardMutationRequest(request);\n}\n\nexport interface MergeTrainConfigBody {\n auto_pickup_backlog: boolean;\n require_uat_before_merge: boolean;\n merge_train_enabled: boolean;\n}\n\nexport function getMergeTrainConfigPayload(): MergeTrainConfigBody {\n return {\n auto_pickup_backlog: isFlywheelAutoPickupBacklog(),\n require_uat_before_merge: isFlywheelRequireUatBeforeMerge(),\n merge_train_enabled: isMergeTrainEnabled(),\n };\n}\n\nexport async function postMergeTrainConfigPayload(payload: unknown): Promise<{ status: number; body: unknown }> {\n if (!isJsonObject(payload)) {\n return { status: 400, body: { error: 'Request body must be a JSON object' } };\n }\n for (const key of ['auto_pickup_backlog', 'require_uat_before_merge', 'merge_train_enabled'] as const) {\n if (payload[key] !== undefined && typeof payload[key] !== 'boolean') {\n return { status: 400, body: { error: `${key} must be a boolean` } };\n }\n }\n if (payload['auto_pickup_backlog'] !== undefined) setFlywheelAutoPickupBacklog(payload['auto_pickup_backlog'] as boolean);\n if (payload['require_uat_before_merge'] !== undefined) setFlywheelRequireUatBeforeMerge(payload['require_uat_before_merge'] as boolean);\n if (payload['merge_train_enabled'] !== undefined) setMergeTrainEnabled(payload['merge_train_enabled'] as boolean);\n return { status: 200, body: getMergeTrainConfigPayload() };\n}\n\nexport interface AutoMergeScheduleDeps {\n now?: () => Date;\n isRequireUatBeforeMerge?: () => boolean;\n isMergeTrainEnabled?: () => boolean;\n isEligible?: (issueId: string) => Promise<AutoMergeEligibility>;\n /** The one merge gate (#4040); `evaluateIssueMergeGate` by default. */\n mergeGate?: (issueId: string) => Promise<MergeGateResult>;\n resolveProject?: (issueId: string) => ResolvedProject | null;\n schedule?: (input: ScheduleAutoMergeInput) => ScheduleAutoMergeResult;\n announce?: (issueId: string, entry: PendingAutoMerge) => void;\n getProjectAutoMergeDefault?: (issueId: string) => ProjectAutoMergeDefault;\n /** The issue's tracker labels; defaults to the dashboard's cached tracker row. */\n getIssueLabels?: (issueId: string) => readonly string[];\n /** #3983: true when the cached tracker row says the issue is closed. */\n isIssueClosed?: (issueId: string) => boolean;\n}\n\n/**\n * PAN-3932: the issue's `auto-merge` / `hold-for-uat` labels from the cached\n * tracker row. The auto-merge policy map is polled across every in-flight\n * issue, so it reads the cache rather than asking the forge per issue.\n */\nfunction cachedIssueLabels(issueId: string): readonly string[] {\n try {\n return getSharedIssueService().getTrackerIssue(issueId)?.labels ?? [];\n } catch {\n return [];\n }\n}\n\n/**\n * #3983: whether the cached tracker row says the issue is closed. An issue the\n * cache does not know reads as open here; the executor reads the tracker live\n * before it merges.\n */\nfunction cachedIssueClosed(issueId: string): boolean {\n try {\n return getSharedIssueService().getTrackerIssue(issueId)?.open === false;\n } catch {\n return false;\n }\n}\n\nexport interface AutoMergeCancelDeps {\n now?: () => Date;\n getPending?: (issueId: string) => PendingAutoMerge | null;\n cancel?: (id: number, cancelledBy: string) => boolean;\n countRemaining?: (issueId: string) => number;\n announce?: (issueId: string) => void;\n /** Drop the issue's waiting merge-queue entry; `removeQueuedMerge` by default. */\n removeQueued?: (issueId: string) => number;\n}\n\nfunction announceAutoMergeScheduled(issueId: string, _entry: PendingAutoMerge): void {\n emitActivityTts({\n utterance: `${issueId} auto-merging in 5 minutes; pan merge cancel ${issueId} to abort`,\n priority: 1,\n issueId,\n source: 'dashboard',\n eventType: 'auto-merge-scheduled',\n });\n}\n\nfunction announceAutoMergeCancelled(issueId: string): void {\n emitActivityTts({\n utterance: `auto-merge cancelled for ${issueId}`,\n priority: 1,\n issueId,\n source: 'dashboard',\n eventType: 'auto-merge-cancelled',\n });\n}\n\ntype AutoMergePolicyDeps = Pick<\n AutoMergeScheduleDeps,\n 'getIssueLabels' | 'getProjectAutoMergeDefault' | 'isRequireUatBeforeMerge' | 'isMergeTrainEnabled' | 'isIssueClosed'\n>;\n\n/**\n * The schedule door's policy check, which reads no forge: the issue's UAT hold\n * (its `auto-merge` / `hold-for-uat` label (PAN-3932), then the project\n * default, then global), the merge-train switch, and a closed tracker issue\n * (from the cached tracker row). The scheduler (#3983) asks it first, so a held\n * PR costs no forge read per tick.\n */\nexport function autoMergePolicyRefusal(\n issueId: string,\n deps: AutoMergePolicyDeps = {},\n): { status: 412 | 422; body: { error: string } } | null {\n if ((deps.isIssueClosed ?? cachedIssueClosed)(issueId)) {\n return { status: 422, body: { error: `${issueId} is closed in the tracker` } };\n }\n const labels = (deps.getIssueLabels ?? cachedIssueLabels)(issueId);\n const projectDefault = (deps.getProjectAutoMergeDefault ?? getProjectAutoMergeDefault)(issueId);\n const globalRequireUat = (deps.isRequireUatBeforeMerge ?? isFlywheelRequireUatBeforeMerge)();\n if (shouldHoldForUat(autoMergeFromLabels(labels), projectDefault, globalRequireUat)) {\n return { status: 412, body: { error: 'UAT is still required before merge' } };\n }\n if (!(deps.isMergeTrainEnabled ?? isMergeTrainEnabled)()) {\n return { status: 412, body: { error: 'Merge train is disabled' } };\n }\n return null;\n}\n\n/**\n * Schedule an auto-merge. The gate is the one merge gate, `evaluateIssueMergeGate`\n * (#4040): approved (a forge review or a trusted verdict marker), green checks,\n * and `mergeable` on the PR the gate links to the issue (FR-9).\n */\nexport async function postAutoMergeSchedulePayload(payload: unknown, deps: AutoMergeScheduleDeps = {}) {\n if (!isJsonObject(payload)) {\n return { status: 400, body: { error: 'Request body must be a JSON object' } };\n }\n const rawIssueId = payload['issueId'];\n if (typeof rawIssueId !== 'string' || rawIssueId.trim().length === 0) {\n return { status: 400, body: { error: 'issueId must be a non-empty string' } };\n }\n const issueId = rawIssueId.trim().toUpperCase();\n\n const refusal = autoMergePolicyRefusal(issueId, deps);\n if (refusal) return refusal;\n\n // #3983: the automatic path needs an approval bound to the PR head.\n const gate = await (deps.mergeGate ?? ((id: string) => evaluateIssueMergeGate(id)))(issueId);\n if (!gate.ready) {\n return { status: 422, body: { error: `${issueId} is not ready to merge: ${gate.reason ?? 'the merge gate refused it'}` } };\n }\n\n const eligibility = await (deps.isEligible ?? isAutoMergeEligible)(issueId);\n if (!eligibility.eligible) {\n return { status: 422, body: { error: eligibility.reason } };\n }\n\n const prUrl = gate.facts.url;\n if (!prUrl) {\n return { status: 422, body: { error: `No pull request for ${issueId}` } };\n }\n const artifactRef = parseArtifactRef(prUrl);\n if (artifactRef === null) {\n return { status: 422, body: { error: `Pull request URL for ${issueId} is not a recognized forge artifact` } };\n }\n\n const project = (deps.resolveProject ?? resolveProjectFromIssueSync)(issueId);\n if (!project) return { status: 422, body: { error: `Unknown project for issue ${issueId}` } };\n\n const scheduledAt = (deps.now ?? (() => new Date()))();\n const scheduledMergeAt = new Date(scheduledAt.getTime() + AUTO_MERGE_COOLDOWN_MS);\n const result = (deps.schedule ?? scheduleAutoMergeWithResult)({\n issueId,\n prUrl,\n prNumber: artifactRef.number,\n projectKey: project.projectKey,\n forge: artifactRef.forge,\n scheduledMergeAt: scheduledMergeAt.toISOString(),\n scheduledAt: scheduledAt.toISOString(),\n ...(gate.facts.headSha ? { headSha: gate.facts.headSha } : {}),\n });\n if (result.created) (deps.announce ?? announceAutoMergeScheduled)(issueId, result.entry);\n return { status: 200, body: result.entry };\n}\n\nconst AUTO_MERGE_POLL_LIMIT = 100;\n\nexport function getPendingAutoMergePayload(): PendingAutoMerge[] {\n return listActiveAutoMerges(AUTO_MERGE_POLL_LIMIT);\n}\n\nexport function getAutoMergeProblemPayload(): PendingAutoMerge[] {\n return listProblemAutoMerges(AUTO_MERGE_POLL_LIMIT);\n}\n\nexport function deleteAutoMergePayload(issueIdParam: string, deps: AutoMergeCancelDeps = {}) {\n const issueId = issueIdParam.trim().toUpperCase();\n if (!issueId) return { status: 400, body: { error: 'issueId must be a non-empty string' } };\n\n const entry = (deps.getPending ?? getActionableAutoMerge)(issueId);\n if (!entry) return { status: 404, body: { error: `No pending auto-merge for ${issueId}` } };\n if (entry.status === 'merging') {\n return { status: 409, body: { error: `Auto-merge cooldown has expired for ${issueId}; merge is in progress` } };\n }\n\n const cancelledAt = (deps.now ?? (() => new Date()))().toISOString();\n const cancelled = (deps.cancel ?? cancelPending)(entry.id, 'operator');\n if (!cancelled) {\n const raced = (deps.getPending ?? getActionableAutoMerge)(issueId);\n if (raced?.status === 'merging') {\n return { status: 409, body: { error: `Auto-merge cooldown has expired for ${issueId}; merge is in progress` } };\n }\n return { status: 404, body: { error: `No pending auto-merge for ${issueId}` } };\n }\n\n // #4066 review: a cancel stops the merge wherever it waits, the project\n // merge queue included.\n try {\n (deps.removeQueued ?? removeQueuedMerge)(issueId);\n } catch (error) {\n console.warn(`[auto-merge] could not drop ${issueId} from the merge queue: ${error instanceof Error ? error.message : String(error)}`);\n }\n (deps.announce ?? announceAutoMergeCancelled)(issueId);\n return {\n status: 200,\n body: {\n ...entry,\n status: 'cancelled' as const,\n cancelledAt,\n cancelledBy: 'operator',\n remainingActionable: (deps.countRemaining ?? countActionableAutoMerges)(issueId),\n },\n };\n}\n\n/**\n * The effective auto-merge routing key per in-flight issue (PAN-3917, D3).\n *\n * There is no per-issue routing key record any more. The answer is derived:\n * the issue's own `auto-merge` / `hold-for-uat` tracker label (PAN-3932),\n * then its project default, then the global `require_uat_before_merge`.\n * `autoMerge: true` means the train may ship it when green; `false` means\n * hold for UAT.\n */\nexport async function getAutoMergePolicyPayload(): Promise<{\n issues: Array<{ issueId: string; autoMerge: boolean }>;\n}> {\n const globalRequireUat = isFlywheelRequireUatBeforeMerge();\n const { loadIssueStatesForProject } = await import('../services/derived-issue-state.js');\n const IN_FLIGHT = new Set(['working', 'in-review', 'changes-requested', 'ready']);\n\n // PAN-3925: the projects derive in parallel, each over its PR listing\n // served stale-while-revalidate, so one slow `gh pr list` no longer\n // serializes behind every other project's.\n const perProject = await Promise.all(listProjectsSync().map(async ({ config }) => {\n const projectPath = resolve(config.path);\n try {\n return await loadIssueStatesForProject(projectPath, await listCandidateIssueIds(projectPath));\n } catch (error) {\n console.warn(`[merge-train] auto-merge policy for ${config.name} failed: ${error instanceof Error ? error.message : String(error)}`);\n return null;\n }\n }));\n\n const issues: Array<{ issueId: string; autoMerge: boolean }> = [];\n for (const states of perProject) {\n if (!states) continue;\n for (const [issueId, derived] of states) {\n if (!IN_FLIGHT.has(derived.state)) continue;\n const held = shouldHoldForUat(\n autoMergeFromLabels(cachedIssueLabels(issueId)),\n getProjectAutoMergeDefault(issueId),\n globalRequireUat,\n );\n issues.push({ issueId, autoMerge: !held });\n }\n }\n return { issues };\n}\n\n/** Issue ids with an open PR in the project — the only ones the train can route. */\nasync function listCandidateIssueIds(projectPath: string): Promise<string[]> {\n const { listRepoPullRequestsStaleOk, issueIdFromBranch } = await import('../services/derived-issue-state.js');\n const rows = await listRepoPullRequestsStaleOk(projectPath);\n return rows.flatMap((row) => {\n const issueId = issueIdFromBranch(row.headRefName);\n return issueId ? [issueId] : [];\n });\n}\n\nconst getAutoMergePolicyRoute = HttpRouter.add(\n 'GET',\n '/api/merge-train/auto-merge',\n httpHandler(Effect.gen(function* () {\n return jsonResponse(yield* Effect.promise(() => getAutoMergePolicyPayload()));\n })),\n);\n\n/** Whether an autonomous merge can actually be performed (GitHub App or gh CLI). */\nconst getMergeBackendRoute = HttpRouter.add(\n 'GET',\n '/api/merge-train/merge-backend',\n httpHandler(Effect.gen(function* () {\n const { getMergeBackendStatus } = yield* Effect.promise(() => import('../../../lib/github-app.js'));\n return jsonResponse(yield* Effect.promise(() => getMergeBackendStatus()));\n })),\n);\n\nconst getMergeTrainConfigRoute = HttpRouter.add(\n 'GET',\n '/api/merge-train/config',\n httpHandler(Effect.gen(function* () {\n return jsonResponse(getMergeTrainConfigPayload());\n })),\n);\n\nconst postMergeTrainConfigRoute = HttpRouter.add(\n 'POST',\n '/api/merge-train/config',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const originError = requireTrustedOrigin(request);\n if (originError) return originError;\n const parsed = yield* readUnknownJsonBody;\n if (!parsed.ok) return jsonResponse({ error: parsed.error }, { status: 400 });\n const result = yield* Effect.promise(() => postMergeTrainConfigPayload(parsed.body));\n return jsonResponse(result.body, { status: result.status });\n })),\n);\n\nconst getPendingAutoMergeRoute = HttpRouter.add(\n 'GET',\n '/api/merge-train/auto-merge/pending',\n httpHandler(Effect.gen(function* () {\n return jsonResponse(getPendingAutoMergePayload());\n })),\n);\n\nconst getAutoMergeProblemsRoute = HttpRouter.add(\n 'GET',\n '/api/merge-train/auto-merge/problems',\n httpHandler(Effect.gen(function* () {\n return jsonResponse(getAutoMergeProblemPayload());\n })),\n);\n\nconst postAutoMergeScheduleRoute = HttpRouter.add(\n 'POST',\n '/api/merge-train/auto-merge/schedule',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const originError = requireTrustedOrigin(request);\n if (originError) return originError;\n const parsed = yield* readUnknownJsonBody;\n if (!parsed.ok) return jsonResponse({ error: parsed.error }, { status: 400 });\n const result = yield* Effect.promise(() => postAutoMergeSchedulePayload(parsed.body));\n return jsonResponse(result.body, { status: result.status });\n })),\n);\n\nconst deleteAutoMergeRoute = HttpRouter.add(\n 'DELETE',\n '/api/merge-train/auto-merge/:id',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const originError = requireTrustedOrigin(request);\n if (originError) return originError;\n const params = yield* HttpRouter.params;\n const result = deleteAutoMergePayload(params['id'] ?? '');\n return jsonResponse(result.body, { status: result.status });\n })),\n);\n\nconst getMergeBlockersRoute = HttpRouter.add(\n 'GET',\n '/api/merge-train/merge-blockers',\n httpHandler(Effect.gen(function* () {\n return jsonResponse(getMergeBlockersPayload());\n })),\n);\n\nexport const mergeTrainRouteLayer = Layer.mergeAll(\n getMergeTrainQueuesRoute,\n getMergeTrainGenerationsRoute,\n postMergeTrainGenerationStackRoute,\n postMergeTrainGenerationPromoteRoute,\n postMergeTrainGenerationShipRoute,\n postMergeTrainAssembleRoute,\n postMergeTrainMergeNextRoute,\n getMergeTrainConfigRoute,\n postMergeTrainConfigRoute,\n getAutoMergePolicyRoute,\n getMergeBackendRoute,\n getPendingAutoMergeRoute,\n getAutoMergeProblemsRoute,\n postAutoMergeScheduleRoute,\n deleteAutoMergeRoute,\n getMergeBlockersRoute,\n);\n\nexport default mergeTrainRouteLayer;\n"],"mappings":"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"}
|
package/dist/dashboard/server.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { a as __toCommonJS, n as __esmMin, o as __toESM, r as __exportAll, t as __commonJSMin } from "./rolldown-runtime-DNCzzKnI.js";
|
|
2
2
|
import { $ as isAgentSessionName, An as getModelEffortLevels, B as parsePullRequestRef, Cr as init_paths, Ct as PIPELINE_MEMBERSHIP_LOADING_CODE, Dt as getDefaultTtsDaemonConfig, En as resolveModel, Er as packageRoot, Et as getDefaultDocsConfig, F as init_js_yaml, G as isAgentRole, H as COMPANION_TERMINAL_CLOSE_BODY_KEYS, Ht as TieredExecutionConfigError, I as yaml, Jt as canUseHarness, K as COMPOSER_COMMAND_MANIFEST, L as mergeConfigs, Lt as mergeTtsDaemonConfigs, Mn as init_model_capabilities, N as init_errors, Nn as modelSupportsImages, On as MODEL_CAPABILITIES, P as require_dist$1, Pn as resolveModelId, R as init_src, Rt as DEFAULT_CONFIG$1, S as FsError, Sn as computeModelOrigin, Sr as getOverdeckHome, St as AgentSnapshot, Tr as isDevMode, U as COMPANION_TERMINAL_OPEN_BODY_KEYS, Ut as init_tier_table, V as repoFromRemoteUrl, Vn as MODEL_DEPRECATIONS, Wt as resolveTieredExecutionBlock, Xn as PARENT_MODEL_REF, Xt as init_harness_policy, Yn as COMPLIANCE_MODES, Z as getHarnessBehavior, Zn as ROLE_EFFORTS, _ as updateConversationsConfig, _r as getDocsBudgetStatePath, a as getGlobalConfigPath, an as getProviderForModel, ar as LOGS_DIR, bn as RETIRED_SUB_ROLES, br as getDocsTelemetryPath, bt as resolveEffectivePullRequest, dt as PanRpcError, en as UnknownModelError, et as ContextLayerSaveRequest, ft as PanRpcGroup, gn as init_registry, gt as SystemHealthSnapshot, h as loadConfigSync, hn as defaultBackgroundAiFeatures, hr as activeDeploymentRepoRoot, ht as DomainEvent, i as getConversationsConfig, jn as hasModelCapability, k as ProcessSpawnError, m as loadConfigNoMigration, mn as init_openai_compatible_proxy, mt as EDITORS, n as clearConfigCache, nn as getBuiltInDefaultHarness, o as getOpenInEditorCommand, on as init_providers, or as OVERDECK_HOME, p as isTldrEnabled, pn as getDashScopeUpstreamBaseUrl, pr as SYNC_SOURCES, pt as WS_METHODS, q as compareIssueIds, qt as validateTieredExecutionConfig, r as getConversationSearchConfig, rn as getKimiAnthropicBaseUrl, t as init_config_yaml, tt as ContextPreviewRequest, u as init_load, vn as DEFAULT_ROLES, vr as getDocsDisableStatePath, vt as MemoryObservation, wr as isDeploymentGenerationRoot, xt as AgentRuntimeSnapshot, yn as DEFAULT_WORKHORSES, yr as getDocsIndexPath, z as isConfiguredPullRequestRepo } from "./config-yaml-eISZeFuh.js";
|
|
3
3
|
import { A as init_boot_gates, D as getOverdeckDatabasePath, E as OVERDECK_MIGRATION_PATH, M as resolveBootGates, O as init_paths$1, d as dropPipelineStateMirrorTables, i as DbLive, j as isPeerDashboardProcess, k as formatBootGateState, l as TmuxLive, p as init_infra, u as dropDeadIssuesForeignKeys } from "./infra-DEg0Pwsm.js";
|
|
4
|
-
import { d as getDashboardIdentity, f as isNonPrimaryCheckoutRoot, l as startUatTrainReconciler, m as shouldRefuseHostDashboardPort, p as readHostDashboardApiPort, u as stopUatTrainReconciler } from "./uat-train-
|
|
4
|
+
import { d as getDashboardIdentity, f as isNonPrimaryCheckoutRoot, l as startUatTrainReconciler, m as shouldRefuseHostDashboardPort, p as readHostDashboardApiPort, u as stopUatTrainReconciler } from "./uat-train-BORW2R0A.js";
|
|
5
5
|
import { a as validateOrigin$1, c as jsonStringResponse, o as validateOriginHeaders, r as isTrustedOriginForHost, s as jsonResponse, t as getHeaderFromMap } from "./origin-validation-DzzAfBTF.js";
|
|
6
6
|
import { c as loadConfigSync$1, n as getDashboardApiUrl, o as init_config } from "./config-Q5xnOeq2.js";
|
|
7
7
|
import { a as emitActivityTts, o as emitDashboardLifecycle, r as emitActivityEntry, s as init_activity_logger, t as appendDomainEventAsync } from "./activity-logger-89A9ZC2h.js";
|
|
@@ -122,7 +122,7 @@ import { n as isRemoteAvailable, t as createFlyProviderFromConfig } from "./remo
|
|
|
122
122
|
import { t as detectProviderEnvConflicts } from "./claude-settings-overlay-Cl8Xq_sf.js";
|
|
123
123
|
import { a as projectIdFromPath, i as parentSessionIdFromPath, n as dimensionsForModel, o as sessionIdFromPath, r as openEmbeddingsDb } from "./conversations-search-EDX0ltxJ.js";
|
|
124
124
|
import { r as getShipLog } from "./ship-log-DdnHsR67.js";
|
|
125
|
-
import { l as mergeTrainRouteLayer } from "./merge-train-
|
|
125
|
+
import { l as mergeTrainRouteLayer } from "./merge-train-pMKZ_hti.js";
|
|
126
126
|
import { a as resumeFlywheel, c as readFlywheelReportFile, d as FlywheelNotRunning, f as FlywheelOrphanSession, g as resolveFlywheelProjectRoot, h as readFlywheelRun, i as requestFlywheelReport, l as readFlywheelStateFile, m as deriveFlywheelStatus, n as abortFlywheel, o as startFlywheel, p as FlywheelPausedExists, r as pauseFlywheel, s as stopFlywheel, t as computeSubstrateStats, u as FlywheelAlreadyRunning } from "./substrate-stats-beoo0bT5.js";
|
|
127
127
|
import { i as appendLinearMcpAuthHealthyEvent, r as appendLinearMcpAuthCallbackRelayedEvent, s as resolveLinearMcpAuthIntervention } from "./linear-mcp-auth-Bt0bIPww.js";
|
|
128
128
|
import { n as getSequencerRunStatus, r as spawnSequencerAgent, t as SEQUENCER_AGENT_ID } from "./sequencer-agent-Bf_xHy-j.js";
|
|
@@ -65570,11 +65570,11 @@ async function defaultMergeGate(issueId) {
|
|
|
65570
65570
|
return evaluateIssueMergeGate(issueId);
|
|
65571
65571
|
}
|
|
65572
65572
|
async function defaultPolicyRefusal() {
|
|
65573
|
-
const { autoMergePolicyRefusal } = await import("./merge-train-
|
|
65573
|
+
const { autoMergePolicyRefusal } = await import("./merge-train-I2N9TlqX.js");
|
|
65574
65574
|
return (issueId) => autoMergePolicyRefusal(issueId);
|
|
65575
65575
|
}
|
|
65576
65576
|
async function defaultSchedule(issueId, canSchedule) {
|
|
65577
|
-
const [{ postAutoMergeSchedulePayload }, { scheduleAutoMergeWithResult }] = await Promise.all([import("./merge-train-
|
|
65577
|
+
const [{ postAutoMergeSchedulePayload }, { scheduleAutoMergeWithResult }] = await Promise.all([import("./merge-train-I2N9TlqX.js"), import("./merge-sync-GeddtnBd.js")]);
|
|
65578
65578
|
return postAutoMergeSchedulePayload({ issueId }, { schedule: (input) => scheduleAutoMergeWithResult({
|
|
65579
65579
|
...input,
|
|
65580
65580
|
canSchedule
|
|
@@ -29,8 +29,8 @@ var import_toml = require_toml();
|
|
|
29
29
|
init_boot_gates();
|
|
30
30
|
function getBuildInfo() {
|
|
31
31
|
return {
|
|
32
|
-
buildCommit: "
|
|
33
|
-
builtAt: "2026-09-
|
|
32
|
+
buildCommit: "514884c9dd939158a164c88d0694fe94ec0ff99c",
|
|
33
|
+
builtAt: "2026-09-27T04:42:06.924Z",
|
|
34
34
|
buildDirty: false,
|
|
35
35
|
buildBranch: null
|
|
36
36
|
};
|
|
@@ -3835,4 +3835,4 @@ async function postUatGenerationPromotePayload(name, firePostMerge, shipVersion)
|
|
|
3835
3835
|
//#endregion
|
|
3836
3836
|
export { postUatGenerationStackPayload as a, runUatTrainReconcileAllProjects as c, getDashboardIdentity as d, isNonPrimaryCheckoutRoot as f, postUatGenerationPromotePayload as i, startUatTrainReconciler as l, shouldRefuseHostDashboardPort as m, getUatCandidatePayload as n, resolveUatProjectRoot as o, readHostDashboardApiPort as p, getUatGenerationsPayload as r, runUatTrainReconcile as s, canStartUatTrainReconciler as t, stopUatTrainReconciler as u };
|
|
3837
3837
|
|
|
3838
|
-
//# sourceMappingURL=uat-train-
|
|
3838
|
+
//# sourceMappingURL=uat-train-BORW2R0A.js.map
|