fix: extend code queue pr preflight diagnostics

This commit is contained in:
Codex
2026-05-21 01:14:24 +00:00
parent a43c24dd88
commit 9e4561e102
7 changed files with 87 additions and 8 deletions
+1 -1
View File
@@ -41,7 +41,7 @@ CLI 可以从 `master` 快速演进,但必须兼容 `deploy.json` 固定的 CI
- `schedule list|get|runs|run|retry-run|delete|upsert-pgdata-backup` 管理 backend-core 定时任务和运行历史。`schedule list``schedule get``schedule runs --limit N``schedule runs <scheduleId> --limit N` 是只读观察入口;`schedule run``schedule retry-run``schedule delete``schedule upsert-pgdata-backup` 会触发运行或写入配置,生产恢复时必须有明确授权。`schedule runs --limit N` 是全局历史视图,返回 `scope=global``scheduleId=null``schedule runs <scheduleId> --limit N` 是指定 schedule 历史视图,返回 `scope=schedule` 和对应 `scheduleId`。CLI 必须拒绝 `schedule runs 50` 这类纯数字位置参数,并提示使用 `schedule runs --limit 50`,避免把空数组误判成“没有历史 run”。`schedule run <id> --wait-ms N` 触发同一 schedule,并且即使 wait 超时也必须返回 `newRunId``observeCommand``schedule retry-run <failedRunId>` 只接受 failed run,从原 run 反查 `scheduleId` 后重触发同一 schedule,并输出 `originalRunId``scheduleId``newRunId``observeCommand`。当 backend-core 目标容器缺失或只观察到 verify-only 容器时,schedule/microservice 命令必须以非零退出并返回 `failureKind=target-stack-not-running``runnerDisposition=infra-blocked``readOnlyCommands``authorizationRequiredForRecovery`,不得把 Docker 的 `No such container` 当成成功的空历史。
- `codex deploy <commitId>` 是旧 Code Queue 兼容部署入口,已禁用以防止维护通道直连 D601 部署 Code Queue;当前 dev 自动化只做 `ci run-dev-e2e` smoke,不提供 Code Queue CD,详细规则见 `docs/reference/codex-deploy.md`
- `codex submit [prompt] [--prompt-file path|--prompt-stdin] [--queue queueId] [--provider-id id] [--cwd path] [--model model] [--reasoning-effort effort] [--execution-mode mode] [--max-attempts N] [--reference-task-id id] [--dry-run]` 通过 backend-core 私有代理向稳定 `code-queue` 用户服务路径提交任务;prompt 必须且只能来自位置参数、文件或 stdin 之一,`--dry-run` 只返回结构化请求且不实际入队。dry-run 会额外输出 `routingRecommendation`,包含推荐 route、runner、model、风险信号、prompt 自包含/issue 非唯一来源/prod-secret-DB 禁止/证据要求等 guard 状态;该建议只用于指挥官 preflight,不会改写 payload,也不假设生产 MiniMax 可用。提交确认和 dry-run 必须返回完整 prompt、字符数和 `truncated=false`,不能套用任务详情的预览截断策略,否则长任务 prompt 无法被人工验收。真实提交会经过本机本地串行化保护和短节流,避免同一指挥端并发 submit 把低内存主机或 `code-queue-mgr` 控制面打抖;返回值会附带 `submitConcurrencyGuard` 说明本次提交的锁与等待信息。backend-core 默认把提交、队列 CRUD、已读状态、历史摘要和轻量 Trace 读取分流到主 server `code-queue-mgr`,由它写入主 PostgreSQLD601 scheduler 只轮询并执行已入库任务。
- `codex pr-preflight [--remote] [--push-dry-run --push-dry-run-ref refs/heads/probe/<name>] [--issue N] [--full]` 通过稳定 `code-queue` proxy 请求 D601 scheduler `/api/runtime-preflight`,用于 PR 型派单 admission。输出会压缩展示 scheduler/runner 的 token 覆盖、工具、agent port、Git worktree、GitHub egress、repo/issue/PR 只读探测可选 push dry-run;只报告 `GH_TOKEN`/`GITHUB_TOKEN` 是否存在和来源 key,不打印值。缺少 env token 时顶层 `ok=false``runnerDisposition=infra-blocked``tokenCoverage.missing` 同时列出 `GH_TOKEN``GITHUB_TOKEN`
- `codex pr-preflight [--remote] [--push-dry-run --push-dry-run-ref refs/heads/probe/<name>] [--pr-create-dry-run --pr-create-dry-run-head <head>] [--issue N] [--full]` 通过稳定 `code-queue` proxy 请求 D601 scheduler `/api/runtime-preflight`,用于 PR 型派单 admission。输出会压缩展示 scheduler/runner 的 token 覆盖、工具、agent port、Git worktree、GitHub egress、repo/issue/PR 只读探测可选 push dry-run,以及可选 PR body/create dry-run guard;只报告 `GH_TOKEN`/`GITHUB_TOKEN` 是否存在和来源 key,不打印值。缺少 env token 时顶层 `ok=false``runnerDisposition=infra-blocked``tokenCoverage.missing` 同时列出 `GH_TOKEN``GITHUB_TOKEN``--pr-create-dry-run` 不 POST GitHub,只证明 runner 内 PR body 生成、`scripts/cli.ts gh pr create --dry-run` 和 branch 参数形态可用;服务端创建权限仍以 token auth、repo/issue/PR read、push dry-run 和最终授权后的真实 PR 创建结果为准。
- `codex task <taskId>` 通过 Code Queue 私有代理按任务 ID 查询结构化审阅摘要;默认只返回任务身份、执行 Provider、工作目录、attempt 计数、原始 prompt、最终 response、最后错误和渐进披露命令,适合指挥官审阅完成未读任务且避免上下文爆炸。需要旧式详细摘要时显式加 `--detail`;需要完整 prompt/response 文本时加 `--full`;需要工具调用、judge、attempt 全量摘要时使用 `--detail --full --tool-limit N`。该摘要读取默认由主 server `code-queue-mgr` 从 PostgreSQL 返回,不依赖 D601 `code-queue-read` Service 可用。
- `codex tasks [--view supervisor|full] [--queue id] [--status succeeded|running|queued|failed|canceled|judging|retry_wait[,..]] [--unread|--unread-only] [--limit N] [--before-id id]` 通过同一私有代理输出渐进式披露视图。默认 `supervisor` 只返回 `running``completedUnread``recentCompleted``queued``executionDiagnostics` 摘要,不嵌入完整 Trace、final response 或全量 overview;每个条目都带 `commands.show``commands.trace``commands.output``commands.read``commands.full``--unread``--unread-only` 的别名,必须只保留未读终态;`--status` 必须真实过滤支持的状态,未知参数或未知状态必须结构化失败,不能静默忽略。需要完整当前页任务简表时显式使用 `--view full``--full`,仍受 `--limit``--before-id` 分页约束。
- `codex task <taskId> --trace --tail|--from-start|--after-seq N|--before-seq N --limit N` 按页拉取 Code Queue 的逻辑 trace;响应会返回 `nextAfterSeq``previousBeforeSeq``hasMore``hasBefore` 和下一页/上一页命令,默认 `--trace` 取最新一页,且仍以分页 trace 为主;需要完整 prompt/最终 response 时加 `--full`,需要详细 task 摘要时加 `--detail`
+2 -2
View File
@@ -61,7 +61,7 @@ D601 原生 k3s 的人工诊断必须显式使用 host kubeconfig`KUBECONFIG=
Code Queue worker 可以在任务明确要求审查型交付时创建 Pull Request。PR 交付不是默认出口;默认集成仍遵循项目当前 master-only 规则,直到具体任务或指挥官要求改为 PR。PR 型任务必须从最新目标线创建短生命周期分支,报告源分支、目标分支、PR URL、关联 issue、验证证据和未完成风险;分支命名应使用 `probe/``code-queue/` 或其他明确任务前缀,禁止把隐藏分支当成长期交付状态。
Code Queue runtime 提供 `/api/runtime-preflight` 作为 PR 能力探测入口;CLI 稳定入口是 `bun scripts/cli.ts codex pr-preflight [--remote] [--push-dry-run --push-dry-run-ref refs/heads/probe/<name>] [--issue N]`。默认请求只检查本地工具、凭证可见性、Git worktree、HOME、known_hosts、agent port 和 proxy DNS`--remote` 会增加 GitHub 网络、issue API、SSH/HTTPS `git ls-remote`、GitHub SSH、`gh auth status``gh repo view``gh issue view` 和只读 `gh pr list` 探测;`--push-dry-run` 会额外执行 `git push --dry-run`,验证远端写权限但不创建分支。探测输出只报告 `GH_TOKEN`/`GITHUB_TOKEN` 是否存在,不得输出 token 内容。backend-core 的稳定 `code-queue` proxy 必须把 `/api/runtime-preflight` 路由到 D601 scheduler,而不是主 server `code-queue-mgr`,因为 token 和 PR runner 能力属于执行面环境。
Code Queue runtime 提供 `/api/runtime-preflight` 作为 PR 能力探测入口;CLI 稳定入口是 `bun scripts/cli.ts codex pr-preflight [--remote] [--push-dry-run --push-dry-run-ref refs/heads/probe/<name>] [--pr-create-dry-run --pr-create-dry-run-head <head>] [--issue N]`。默认请求只检查本地工具、凭证可见性、Git worktree、HOME、known_hosts、agent port 和 proxy DNS`--remote` 会增加 GitHub 网络、issue API、SSH/HTTPS `git ls-remote`、GitHub SSH、`gh auth status``gh repo view``gh issue view` 和只读 `gh pr list` 探测;`--push-dry-run` 会额外执行 `git push --dry-run`,验证远端写权限但不创建分支`--pr-create-dry-run` 会在 runner 内生成受控 PR body 并执行 `scripts/cli.ts gh pr create --dry-run`,只证明 PR body guard 和命令形态可用,不 POST GitHub。探测输出只报告 `GH_TOKEN`/`GITHUB_TOKEN` 是否存在,不得输出 token 内容。backend-core 的稳定 `code-queue` proxy 必须把 `/api/runtime-preflight` 路由到 D601 scheduler,而不是主 server `code-queue-mgr`,因为 token 和 PR runner 能力属于执行面环境。
PR 创建依赖以下最小运行时能力:
@@ -70,7 +70,7 @@ PR 创建依赖以下最小运行时能力:
- `CODE_QUEUE_REMOTE_CODEX_ENV_KEYS` 默认允许把 `GH_TOKEN`/`GITHUB_TOKEN` 以及 `GH_HOST``GITHUB_API_URL``GH_REPO` 从 scheduler 传给 provider dev container,供隔离执行环境内的 `gh repo view``gh issue view``gh pr list``gh pr create` 使用。
- DEV 的持久注入路径复用 `unidesk-dev-runtime-secrets`:把 `GH_TOKEN``GITHUB_TOKEN` 写入该 Secret 后,通过受控 `deploy apply --env dev --service code-queue` 或等价 dev-only rollout 重启 Code Queue scheduler/read/write。default/prod runner 使用生产 `unidesk` namespace 的 `code-queue-env` Secret;仅修复 dev Secret 不会覆盖 default queue scheduler。不要在 PROD Code Queue 上直接 patch Secret 或 rollout,生产 token 覆盖需要显式 release/runtime 授权。
安全验证顺序固定为先只读、再 dry-run、最后才创建真实 PR。优先执行 `/api/runtime-preflight``/api/runtime-preflight?remote=1``pushDryRun=1` 的 probe ref;只有工具、token、网络push dry-run 都满足且任务明确允许时,才创建 draft PR 或普通 PR。若创建真实 probe PR,最终报告必须记录 URL 并说明保留或关闭状态。
安全验证顺序固定为先只读、再 dry-run、最后才创建真实 PR。优先执行 `/api/runtime-preflight``/api/runtime-preflight?remote=1``pushDryRun=1` 的 probe ref,以及带 `prCreateDryRun=1` 的 PR body/create dry-run guard;只有工具、token、网络push dry-run 和 PR body guard 都满足且任务明确允许时,才创建 draft PR 或普通 PR。若创建真实 probe PR,最终报告必须记录 URL 并说明保留或关闭状态。
preflight failure 必须定位到具体缺失项。已知必须区分的状态包括:`gh`/`hub` 缺失、`GH_TOKEN`/`GITHUB_TOKEN`/`GH_HOST`/`GITHUB_API_URL`/`GH_REPO` 未注入、`/root/.config/gh/hosts.yml``/root/.git-credentials` 缺失、SSH Git 可读但 GitHub issue/API 匿名访问返回 404、匿名 HTTPS Git 不可用、以及 `HTTP_PROXY`/`HTTPS_PROXY` 指向的 egress proxy 在 runner 内不可解析。发现这类缺口时,worker 应报告 preflight error 和阻塞项,不得强行创建 PR。
@@ -66,6 +66,7 @@ function fixtureRuntimePreflight(tokenPresent: boolean): JsonRecord {
knownHostsPresent: true,
privateKeyPresent: true,
},
prCreateDryRun: tokenPresent ? { command: "sh", args: ["-lc", "bun scripts/cli.ts gh pr create --dry-run"], ok: true, exitCode: 0, signal: null, error: null, stdout: "{\"ok\":true,\"data\":{\"dryRun\":true}}", stderr: "" } : undefined,
limitations: tokenPresent ? [] : ["GH_TOKEN/GITHUB_TOKEN is not present; gh cannot create PRs unless another gh credential store is mounted"],
risks: [],
},
@@ -114,6 +115,14 @@ export function runCodeQueuePrPreflightContract(): JsonRecord {
const readyTokenCoverage = asRecord(readyPreflight.tokenCoverage);
assertCondition(readyTokenCoverage.source === "GH_TOKEN", "ready token source should be redacted to key name only", readyTokenCoverage);
const prCreateDryRun = codexPrPreflightQueryForTest(["--remote", "--pr-create-dry-run", "--pr-create-dry-run-head", "codequeue/pr-probe"], (path) => {
assertCondition(path === "/api/microservices/code-queue/proxy/api/runtime-preflight?remote=1&prCreateDryRun=1&prCreateDryRunHead=codequeue%2Fpr-probe", "PR create dry-run options should map to query string", { path });
return fixtureResponse(true);
});
const prCreateDryRunPreflight = asRecord(asRecord(prCreateDryRun).preflight);
const prCreateDryRunProbe = asRecord(prCreateDryRunPreflight.prCreateDryRun);
assertCondition(prCreateDryRunProbe.ok === true, "PR create dry-run probe should be compacted", prCreateDryRunProbe);
return {
ok: true,
checks: [
@@ -121,6 +130,7 @@ export function runCodeQueuePrPreflightContract(): JsonRecord {
"missing GitHub token is infra-blocked",
"token key names are reported without values",
"push dry-run options are forwarded",
"PR create dry-run options are forwarded",
],
};
}
+10 -2
View File
@@ -190,6 +190,8 @@ interface CodexPrPreflightOptions {
remote: boolean;
pushDryRun: boolean;
pushDryRunRef: string | undefined;
prCreateDryRun: boolean;
prCreateDryRunHead: string | undefined;
issueNumber: number | null;
full: boolean;
}
@@ -1259,13 +1261,15 @@ function parseQueuesOptions(args: string[]): CodexQueuesOptions {
function parsePrPreflightOptions(args: string[]): CodexPrPreflightOptions {
assertKnownOptions(args, {
flags: ["--remote", "--push-dry-run", "--pushDryRun", "--full", "--raw"],
valueOptions: ["--push-dry-run-ref", "--pushDryRunRef", "--issue", "--issue-number", "--issueNumber"],
flags: ["--remote", "--push-dry-run", "--pushDryRun", "--pr-create-dry-run", "--prCreateDryRun", "--full", "--raw"],
valueOptions: ["--push-dry-run-ref", "--pushDryRunRef", "--pr-create-dry-run-head", "--prCreateDryRunHead", "--issue", "--issue-number", "--issueNumber"],
}, "codex pr-preflight");
return {
remote: hasFlag(args, "--remote"),
pushDryRun: hasFlag(args, "--push-dry-run") || hasFlag(args, "--pushDryRun"),
pushDryRunRef: optionValue(args, ["--push-dry-run-ref", "--pushDryRunRef"]),
prCreateDryRun: hasFlag(args, "--pr-create-dry-run") || hasFlag(args, "--prCreateDryRun"),
prCreateDryRunHead: optionValue(args, ["--pr-create-dry-run-head", "--prCreateDryRunHead"]),
issueNumber: nullablePositiveNumberOption(args, ["--issue", "--issue-number", "--issueNumber"]),
full: hasFlag(args, "--full") || hasFlag(args, "--raw"),
};
@@ -2364,6 +2368,7 @@ function compactPrRuntimePreflight(preflight: Record<string, unknown>, options:
ghPrReadOnly: compactCommandProbe(remote.ghPrReadOnly),
},
pushDryRun: compactCommandProbe(pull.pushDryRun),
prCreateDryRun: compactCommandProbe(pull.prCreateDryRun),
limitations,
risks,
runnerDisposition: ok ? "ready" : "infra-blocked",
@@ -2374,6 +2379,7 @@ function compactPrRuntimePreflight(preflight: Record<string, unknown>, options:
local: "bun scripts/cli.ts gh auth status --repo pikasTech/unidesk",
runner: "bun scripts/cli.ts codex pr-preflight --remote",
runnerPushDryRun: "bun scripts/cli.ts codex pr-preflight --remote --push-dry-run --push-dry-run-ref refs/heads/probe/code-queue-pr-capability",
runnerPrCreateDryRun: "bun scripts/cli.ts codex pr-preflight --remote --pr-create-dry-run --pr-create-dry-run-head <head-branch>",
rawProxy: "bun scripts/cli.ts microservice proxy code-queue /api/runtime-preflight?remote=1 --raw",
},
};
@@ -2387,6 +2393,8 @@ function codeQueuePrPreflight(optionArgs: string[] = [], fetcher: CodexResponseF
remote: options.remote ? 1 : undefined,
pushDryRun: options.pushDryRun ? 1 : undefined,
pushDryRunRef: options.pushDryRunRef,
prCreateDryRun: options.prCreateDryRun ? 1 : undefined,
prCreateDryRunHead: options.prCreateDryRunHead,
issue: options.issueNumber,
})}`);
const response = unwrapCodexResponse(fetcher(path));
+2 -2
View File
@@ -49,7 +49,7 @@ export function rootHelp(): unknown {
{ command: "schedule upsert-pgdata-backup [--time HH:MM] [--remote-base /SERVER_DATA/UNIDESK_PG_DATA]", description: "Create or update the daily PGDATA physical backup task that uploads monthly rotated archives to Baidu Netdisk." },
{ command: "codex deploy <commitId> [--provider-id D601] [--timeout-ms N]", description: "Disabled legacy Code Queue deploy path; use the dev-only artifact consumer instead." },
{ command: "codex submit [prompt] [--prompt-file path|--prompt-stdin] [--queue queueId] [--provider-id id] [--cwd path] [--model model] [--execution-mode mode] [--max-attempts N] [--reference-task-id id] [--dry-run]", description: "Submit a Code Queue task through backend-core -> code-queue proxy; --dry-run shows the structured request without enqueueing." },
{ command: "codex pr-preflight [--remote] [--push-dry-run --push-dry-run-ref refs/heads/probe/<name>] [--issue N]", description: "Read-only PR admission check against the D601 scheduler/runner token, GitHub egress, repo visibility, and optional push dry-run." },
{ command: "codex pr-preflight [--remote] [--push-dry-run --push-dry-run-ref refs/heads/probe/<name>] [--pr-create-dry-run --pr-create-dry-run-head <head>] [--issue N]", description: "Read-only PR admission check against the D601 scheduler/runner token, GitHub egress, repo visibility, optional push dry-run, and PR body/create dry-run guard." },
{ command: "codex task <taskId> [--detail] [--trace --tail|--from-start|--after-seq N|--before-seq N --limit N] [--full]", description: "Fetch the bounded review view by default: original prompt, final response, and drill-down commands; detail and trace are opt-in." },
{ command: "codex tasks [--view supervisor|full] [--queue id] [--status status[,status]] [--unread|--unread-only] [--limit N] [--before-id id]", description: "Show the bounded supervisor view by default: running, unread terminal, recent completed, queued, diagnostics, and drill-down commands." },
{ command: "codex output <taskId> [--tail|--from-start|--after-seq N|--before-seq N --limit N] [--full-text]", description: "Fetch paged raw Code Queue output records by seq when a trace row has omitted command/output text." },
@@ -215,7 +215,7 @@ function codexHelp(): unknown {
"bun scripts/cli.ts codex output <taskId> [--tail|--from-start|--after-seq N|--before-seq N --limit N] [--full-text]",
"bun scripts/cli.ts codex read <taskId>",
"bun scripts/cli.ts codex dev-ready",
"bun scripts/cli.ts codex pr-preflight [--remote] [--push-dry-run --push-dry-run-ref refs/heads/probe/<name>] [--issue N]",
"bun scripts/cli.ts codex pr-preflight [--remote] [--push-dry-run --push-dry-run-ref refs/heads/probe/<name>] [--pr-create-dry-run --pr-create-dry-run-head <head>] [--issue N]",
"bun scripts/cli.ts codex judge <taskId> --attempt N [--dry-run] [--include-prompt]",
"bun scripts/cli.ts codex steer <taskId> [prompt|--prompt-file path|--prompt-stdin] [--dry-run]",
"bun scripts/cli.ts codex interrupt|cancel <taskId>",
@@ -5284,6 +5284,8 @@ async function route(req: Request): Promise<Response> {
includeRemote: url.searchParams.get("remote") === "1",
includePushDryRun: url.searchParams.get("pushDryRun") === "1",
pushDryRunRef: url.searchParams.get("pushDryRunRef") ?? undefined,
includePrCreateDryRun: url.searchParams.get("prCreateDryRun") === "1",
prCreateDryRunHead: url.searchParams.get("prCreateDryRunHead") ?? undefined,
issueNumber: Number(url.searchParams.get("issue") ?? url.searchParams.get("issueNumber") ?? NaN),
})),
});
@@ -9,6 +9,8 @@ export interface RuntimePreflightOptions {
includeRemote?: boolean;
includePushDryRun?: boolean;
pushDryRunRef?: string;
includePrCreateDryRun?: boolean;
prCreateDryRunHead?: string;
issueNumber?: number;
}
@@ -115,6 +117,7 @@ export interface PullRequestDeliveryPreflight {
ghPrReadOnly: RuntimeCommandProbe | null;
};
pushDryRun?: RuntimeCommandProbe;
prCreateDryRun?: RuntimeCommandProbe;
limitations: string[];
risks: string[];
}
@@ -309,6 +312,55 @@ function httpsGitUrl(repo: string | null, host: string): string | null {
return repo === null ? null : `https://${host}/${repo}.git`;
}
function prCreateDryRunProbe(repo: string | null, issueNumber: number, head: string | null): RuntimeCommandProbe {
if (repo === null) {
return shellProbe("echo 'cannot run PR create dry-run: GitHub repo is unknown' >&2; exit 2", 2000);
}
const headRef = head?.trim() || "probe/code-queue-pr-capability";
const body = [
"# Code Queue PR preflight",
"",
`Related issue: #${issueNumber}`,
"",
"Validation:",
"- runtime PR create dry-run only",
"",
"Risk:",
"- no remote PR is created by this probe",
"",
].join("\n");
const script = [
"set -eu",
"root=$(git rev-parse --show-toplevel 2>/dev/null || pwd)",
"body_file=$(mktemp /tmp/unidesk-pr-create-dry-run.XXXXXX.md)",
"trap 'rm -f \"$body_file\"' EXIT",
`cat > "$body_file" <<'EOF_PR_PREFLIGHT_BODY'`,
body,
"EOF_PR_PREFLIGHT_BODY",
"cd \"$root\"",
"test -f scripts/cli.ts",
[
"bun",
"scripts/cli.ts",
"gh",
"pr",
"create",
"--repo",
shellQuote(repo),
"--title",
shellQuote("Code Queue PR preflight dry run"),
"--body-file",
"\"$body_file\"",
"--base",
"master",
"--head",
shellQuote(headRef),
"--dry-run",
].join(" "),
].join("\n");
return shellProbe(script, 20_000);
}
function collectPullRequestDeliveryPreflight(options: RuntimePreflightOptions, checkedAt: string): PullRequestDeliveryPreflight {
const tools = {
git: toolStatus("git", ["--version"]),
@@ -392,6 +444,10 @@ function collectPullRequestDeliveryPreflight(options: RuntimePreflightOptions, c
const pushDryRun = options.includePushDryRun === true
? commandProbe("git", ["push", "--dry-run", "origin", `HEAD:${pushDryRunRef}`], { timeoutMs: 30_000 })
: undefined;
const prCreateDryRunHead = options.prCreateDryRunHead?.trim() || gitInfo.branch || "probe/code-queue-pr-capability";
const prCreateDryRun = options.includePrCreateDryRun === true
? prCreateDryRunProbe(githubRepo, issueProbeNumber, prCreateDryRunHead)
: undefined;
const limitations: string[] = [];
const risks: string[] = [];
if (!tools.git.ok) limitations.push("missing git CLI");
@@ -419,12 +475,14 @@ function collectPullRequestDeliveryPreflight(options: RuntimePreflightOptions, c
if (tools.gh.ok && remote.ghPrReadOnly !== null && !remote.ghPrReadOnly.ok) limitations.push("gh pr read-only probe failed");
}
if (pushDryRun !== undefined && !pushDryRun.ok) limitations.push("git push --dry-run failed for probe branch");
if (prCreateDryRun !== undefined && !prCreateDryRun.ok) limitations.push("PR create dry-run body/command guard failed");
const prCliReady = tools.gh.ok || tools.hub.ok;
const tokenOrGhStore = credentials.ghTokenPresent || credentials.githubTokenPresent || credentials.ghHostsConfigPresent || (remote?.ghAuthStatus?.ok === true);
const remoteReady = remote === undefined || (egress.githubDefault.ok && egress.apiDefault.ok && remote.gitLsRemote.ok && remote.githubSshAuthenticated);
const pushReady = pushDryRun === undefined || pushDryRun.ok;
const prCreateDryRunReady = prCreateDryRun === undefined || prCreateDryRun.ok;
return {
ok: tools.git.ok && tools.jq.ok && prCliReady && tokenOrGhStore && gitInfo.insideWorktree && gitInfo.remoteOrigin !== null && gitInfo.homeWritable && remoteReady && pushReady,
ok: tools.git.ok && tools.jq.ok && prCliReady && tokenOrGhStore && gitInfo.insideWorktree && gitInfo.remoteOrigin !== null && gitInfo.homeWritable && remoteReady && pushReady && prCreateDryRunReady,
checkedAt,
tools,
credentials,
@@ -438,6 +496,7 @@ function collectPullRequestDeliveryPreflight(options: RuntimePreflightOptions, c
git: gitInfo,
remote,
pushDryRun,
prCreateDryRun,
limitations,
risks,
};