From efe32b19f7cafc59ee3736d861d9fdb41c53ca10 Mon Sep 17 00:00:00 2001 From: Codex Date: Sat, 23 May 2026 03:03:11 +0000 Subject: [PATCH] fix: disclose codex task limit caps --- docs/reference/cli.md | 2 +- docs/reference/code-queue-supervision.md | 2 +- ...eue-supervisor-disclosure-contract-test.ts | 23 ++++++- scripts/src/code-queue.ts | 64 ++++++++++++------- 4 files changed, 62 insertions(+), 29 deletions(-) diff --git a/docs/reference/cli.md b/docs/reference/cli.md index a3758f2b..94023a35 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -47,7 +47,7 @@ CLI 可以从 `master` 快速演进,但必须兼容 `deploy.json` 固定的 CI - `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` 只返回结构化请求且不实际入队。长 prompt、多行 prompt、含引号/反引号/Markdown 表格/JSON/反斜杠的 prompt 必须优先用 `--prompt-stdin` 或 `--prompt-file`,不要拼进 shell 单个参数;位置参数只适合短单行 smoke prompt。stdin 推荐用 quoted heredoc:`cat <<'PROMPT' | bun scripts/cli.ts codex submit --prompt-stdin --queue --dry-run`,文件路径推荐 `bun scripts/cli.ts codex submit --prompt-file /tmp/code-queue-prompt.md --queue --dry-run`,确认 dry-run 后移除 `--dry-run` 提交同一 payload。dry-run 会额外输出 `routingRecommendation`,包含推荐 route、runner、model、风险信号、prompt 自包含/issue 非唯一来源/prod-secret-DB 禁止/运行态或 release 禁止/证据要求/中等复杂度候选等 guard 状态;同时输出 `policyContract`,固定暴露 GPT-5.5、DeepSeek、MiniMax 的风险分层、并发上限和外部 provider 429 退避处置。该建议只用于指挥官 preflight,不会改写 payload,不改变 runtime admission,也不假设生产 MiniMax 或 DeepSeek 可用。`--dry-run` 必须返回完整 prompt、字符数和 `truncated=false` 用于人工验收;真实提交是写入操作,默认只返回 `accepted=true`、task id、队列、写入保护摘要和后续查看命令,必须标记 `promptOmitted=true` 且不得回显 prompt 或 promptPreview。真实提交会经过本机本地串行化保护和短节流,避免同一指挥端并发 submit 把低内存主机或 `code-queue-mgr` 控制面打抖;返回值会附带低噪声 `submitConcurrencyGuard` 说明本次提交的锁与等待信息。backend-core 默认把提交、队列 CRUD、已读状态、历史摘要和轻量 Trace 读取分流到主 server `code-queue-mgr`,由它写入主 PostgreSQL;D601 scheduler 只轮询并执行已入库任务。 - `codex pr-preflight [--remote] [--push-dry-run --push-dry-run-ref refs/heads/probe/] [--pr-create-dry-run --pr-create-dry-run-head ] [--issue N] [--full]` 通过稳定 `code-queue` proxy 请求 D601 scheduler `/api/runtime-preflight`,用于 PR 型派单 admission。输出会压缩展示 scheduler/runner 的 token 覆盖、Auth Broker source/capability/nextAction、工具、agent port、Git worktree、GitHub egress、repo/issue/PR 只读探测、可选 push dry-run,以及可选 PR body/create dry-run guard;只报告 `GH_TOKEN`/`GITHUB_TOKEN` 是否存在和来源 key,不打印值。当 auth-broker 配置存在时,`tokenCoverage.source="auth-broker"`、`credentialSource="broker-issued-token"` 且 runner env token 不是成功前提;当仅 env token 存在时,`credentialSource="env-token"` 且 `preflight.authBroker.nextAction="use-env-token-until-auth-broker-live"`;两者都缺失时顶层 `ok=false`、`runnerDisposition=infra-blocked`、`degradedReason=auth-broker-needed`,`tokenCoverage.missing` 同时列出 `GH_TOKEN` 与 `GITHUB_TOKEN`,并输出 `preflight.authBroker.source="broker/auth-broker-needed"`、`capability.source="missing-token"`。该 `auth-missing` 的 scope 是 `scheduler-runner-env`,不能简化成“当前 active runner/dev container 不能创建 PR”;输出必须带 `scopeBoundary` 和 `activeRunnerDevContainer`,要求调用方另跑 `bun scripts/cli.ts gh auth status --repo pikasTech/unidesk` 与 PR dry-run 来确认当前 dev container 能力。`preflight.prCapabilityContract` 是 runner-facing 合同摘要,必须包含目标分支、token/auth 来源、`systemGhBinaryRequiredForWrites=false`、UniDesk REST `bun scripts/cli.ts gh` 可用性、push dry-run/PR create dry-run 的 `writesRemote=false`、expected PR handoff、真实 PR 创建需要 commander 授权和 `gh pr merge` 的 `unsupported-command` 边界;系统 `gh` binary 缺失只进入 `tools.systemGhBinary`,不得误判为 UniDesk REST `gh` CLI 不可用。`--remote` 在 runner-like 环境里不再依赖本地 `unidesk-backend-core`、`unidesk-database`、`baidu-netdisk-backend` 容器存在;这些缺失只作为本地观测证据。若远程控制面可达,则继续走远程控制面结果;若远程控制面不可达,则结构化返回 `failureKind=control-plane-missing` / `degradedReason=remote-control-plane-unreachable`,而不是把本地 `backend-core-container-missing` 当作最终阻塞。`--pr-create-dry-run` 不 POST GitHub,只证明 runner 内 PR body 生成、`scripts/cli.ts gh pr create --dry-run` 和 branch 参数形态可用;服务端创建权限仍以 token/auth broker、repo/issue/PR read、push dry-run 和最终授权后的真实 PR 创建结果为准。 - `codex task ` 通过 Code Queue 私有代理按任务 ID 查询结构化审阅摘要;默认只返回任务身份、执行 Provider、工作目录、attempt 计数、原始 prompt、最终 response、最后错误和渐进披露命令,适合指挥官审阅完成未读任务且避免上下文爆炸。`--detail` 仍是有界详细摘要:默认只返回少量 attempt/tool 行、短 prompt/response/stderr/feedback 预览和 omitted/truncated 元数据;需要完整 prompt/response 文本或更多 tool/attempt 细节时再显式加 `--full`、`--tool-limit N`、`--trace` 或 `codex output`。该摘要读取默认由主 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` 是低噪声指挥官视图,只返回 `activeRunning`、`running`、`completedUnread`、`recentCompleted`、`queued`、`activity`、`commanderConcurrency` 和 `executionDiagnostics` 的紧凑行;`activeRunning.count` 是 running+judging 的状态计数,`exact=true` 时来自 queue summary counts,`running.returned` 和 `activeRunning.rowPage.returned` 只是本次返回的紧凑行数。`commanderConcurrency.activeRunnerCount` 是并发策略应使用的 active/running 计数,等于 `activity.effectiveActiveTaskCount`;15 并发策略按 `15 - activeRunnerCount` 计算剩余窗口。`commanderConcurrency.splitBrainDisposition=live-count-as-active` 表示 split-brain 有 fresh heartbeat 证据,应继续监督并计入 active;`interventionRequired=true` 才提示介入。prompt/body 只给短预览和原始字符数,`running`/`completedUnread`/`queued` 默认只返回一个有界小页并通过 section `commands.next` 继续分页,`recentCompleted` 默认限量且不重复 `completedUnread` 未读终态,不嵌入完整 Trace、final response 或全量 overview。`--limit` 在 supervisor 中主要是扫描/分页预算,不是返回几十条肥行的开关;`filters.requestedLimit` 保留用户输入,`filters.limit`/`effectiveLimit` 显示实际 cap。`--unread` 是 `--unread-only` 的别名,必须只保留未读终态;`--status` 必须真实过滤支持的状态,未知参数或未知状态必须结构化失败。需要更详细当前页任务行时显式使用 `--view full` 或 `--full`,仍受 `--limit` 和 `--before-id` 分页约束。 +- `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` 是低噪声指挥官视图,只返回 `activeRunning`、`running`、`completedUnread`、`recentCompleted`、`queued`、`activity`、`commanderConcurrency` 和 `executionDiagnostics` 的紧凑行;`activeRunning.count` 是 running+judging 的状态计数,`exact=true` 时来自 queue summary counts,`running.returned` 和 `activeRunning.rowPage.returned` 只是本次返回的紧凑行数。`commanderConcurrency.activeRunnerCount` 是并发策略应使用的 active/running 计数,等于 `activity.effectiveActiveTaskCount`;15 并发策略按 `15 - activeRunnerCount` 计算剩余窗口。`commanderConcurrency.splitBrainDisposition=live-count-as-active` 表示 split-brain 有 fresh heartbeat 证据,应继续监督并计入 active;`interventionRequired=true` 才提示介入。prompt/body 只给短预览和原始字符数,`running`/`completedUnread`/`queued` 默认只返回一个有界小页并通过 section `commands.next` 继续分页,`recentCompleted` 默认限量且不重复 `completedUnread` 未读终态,不嵌入完整 Trace、final response 或全量 overview。`--limit` 在 supervisor 中主要是扫描/分页预算,不是返回几十条肥行的开关;CLI 安全上限是 100,输出会在 `filters.requestedLimit`、`filters.effectiveLimit`、`filters.limitCapped` 和 `disclosure.limitPolicy` 说明显式请求是否被 capped;底层 overview 拉取预算独立显示在 `source.requestedLimit` / `source.effectiveLimit`,所以 `--limit 260` 应显示 requested=260、effective=100、source requested/effective=200,而不是只露出一个含糊的 `limit`。`--unread` 是 `--unread-only` 的别名,必须只保留未读终态;`--status` 必须真实过滤支持的状态,未知参数或未知状态必须结构化失败。需要更详细当前页任务行时显式使用 `--view full` 或 `--full`,仍受 `--limit` 和 `--before-id` 分页约束。 - `codex task --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`。 - `codex output --tail|--from-start|--after-seq N|--before-seq N --limit N [--full-text]` 按原始 output seq 分页读取底层记录;当 trace 行提示 `commandOmittedLines`、`bodyOmittedLines` 或 `rawSeqs` 时,用该命令按 seq 补取信息。默认是低噪声 raw-output 摘要:即使传入很大的 `--limit`,非 `--full-text` 也会限制返回行数和单条文本预览,并在 `disclosure.limitCapped`、`requestedLimit`、`effectiveLimit` 和 `commands.fullText` 中说明如何继续展开;显式 `--full-text` 才返回该页全文。 - `codex read ` 在人工审阅后标记单个终态任务已读;列表、overview 和 supervisor 视图只返回这个命令字段,不得自动执行,也不得批量清空未读状态。 diff --git a/docs/reference/code-queue-supervision.md b/docs/reference/code-queue-supervision.md index dfdf59b6..e5b7a97c 100644 --- a/docs/reference/code-queue-supervision.md +++ b/docs/reference/code-queue-supervision.md @@ -220,7 +220,7 @@ bun scripts/cli.ts codex pr-preflight --remote --issue 常用入口: -- `bun scripts/cli.ts codex tasks --view supervisor --limit N`:查看默认低噪声监督视图,包括 `activeRunning`、running、完成未读、少量最近完成、queued/runnable、activity、commanderConcurrency、execution diagnostics、任务分类和下一步 drill-down 命令。默认行只保留 task id、队列、短 prompt/body 预览和原始字符数;`--limit` 是扫描/分页预算,不是返回几十条肥行的开关,`filters.requestedLimit` 保留用户输入,`filters.limit`/`effectiveLimit` 是实际上限,`running.returned` 只是低噪声返回行数;`show/detail/trace/output/full/read` 放在 section template 中,避免每条任务重复刷屏,需要更多内容再按 taskId 展开。 +- `bun scripts/cli.ts codex tasks --view supervisor --limit N`:查看默认低噪声监督视图,包括 `activeRunning`、running、完成未读、少量最近完成、queued/runnable、activity、commanderConcurrency、execution diagnostics、任务分类和下一步 drill-down 命令。默认行只保留 task id、队列、短 prompt/body 预览和原始字符数;`--limit` 是扫描/分页预算,不是返回几十条肥行的开关,CLI effective limit 安全上限为 100,输出必须用 `filters.requestedLimit`、`filters.effectiveLimit`、`filters.limitCapped`、`source.requestedLimit` 和 `source.effectiveLimit` 区分用户请求、CLI cap 和 overview 源拉取预算;例如 `--limit 260` 应明确显示 requested=260、effective=100、source=200,`running.returned` 只是低噪声返回行数。`show/detail/trace/output/full/read` 放在 section template 中,避免每条任务重复刷屏,需要更多内容再按 taskId 展开。 - `bun scripts/cli.ts codex queues`:查看低噪声队列计数、activity、commanderConcurrency、active task id、完成未读队列、runnable 队列和控制面诊断;需要完整队列行视图时加 `--full`,但 `--full` 仍默认分页,继续用 `--limit N`、`--page N` 或 `--offset N` 渐进展开。summary 和 full 都使用稳定 JSON path `.data.queues.items[]` 读取队列行,并从 `.data.queues.commanderConcurrency`、`.data.queues.activity`、`.data.queues.counts` 与 `.data.queues.executionDiagnostics` 读取全局活跃计数和执行诊断;完整 upstream 只通过输出中的 raw command 显式获取。 - `bun scripts/cli.ts codex tasks --unread --limit N`:查看完成未读审阅积压;`--unread` 与 `--unread-only` 等价,不能被静默忽略。 - `bun scripts/cli.ts codex tasks --status succeeded --unread --limit N`:按具体终态过滤监督结果;不支持的 status filter 必须显式失败,不能扩大为未过滤结果。 diff --git a/scripts/code-queue-supervisor-disclosure-contract-test.ts b/scripts/code-queue-supervisor-disclosure-contract-test.ts index 90259fb7..865e414d 100644 --- a/scripts/code-queue-supervisor-disclosure-contract-test.ts +++ b/scripts/code-queue-supervisor-disclosure-contract-test.ts @@ -242,8 +242,10 @@ function splitBrainLiveSupervisorFixtureResponse(path: string): JsonRecord { } export function runCodeQueueSupervisorDisclosureContract(): JsonRecord { - const supervisor = codexTasksQueryForTest(["--view", "supervisor", "--limit", "260"], fixtureResponse); + const supervisor = codexTasksQueryForTest(["--view", "supervisor", "--limit", "20"], fixtureResponse); + const cappedLimit = codexTasksQueryForTest(["--view", "supervisor", "--limit", "260"], fixtureResponse); const full = codexTasksQueryForTest(["--view", "full", "--limit", "20"], fixtureResponse); + const cappedFull = codexTasksQueryForTest(["--view", "full", "--limit", "260"], fixtureResponse); const runningFiltered = codexTasksQueryForTest(["--status", "running", "--limit", "40"], manyRunningFixtureResponse); const unreadFiltered = codexTasksQueryForTest(["--unread", "--limit", "20"], fixtureResponse); const splitBrainLive = codexTasksQueryForTest(["--view", "supervisor", "--limit", "20"], splitBrainLiveSupervisorFixtureResponse); @@ -254,6 +256,7 @@ export function runCodeQueueSupervisorDisclosureContract(): JsonRecord { const unreadFilteredBody = JSON.stringify(unreadFiltered); const supervisorData = asRecord(supervisor); const supervisorView = asRecord(supervisorData.supervisor); + const cappedSupervisorView = asRecord(asRecord(cappedLimit).supervisor); const runningFilteredView = asRecord(asRecord(runningFiltered).supervisor); const runningFilteredSection = asRecord(runningFilteredView.running); const unreadFilteredView = asRecord(asRecord(unreadFiltered).supervisor); @@ -265,6 +268,7 @@ export function runCodeQueueSupervisorDisclosureContract(): JsonRecord { const fullItem = asRecord(asArray(asRecord(asRecord(full).tasks).items)[0]); const completedUnread = asRecord(supervisorView.completedUnread); const fullTasks = asRecord(asRecord(full).tasks); + const cappedFullTasks = asRecord(asRecord(cappedFull).tasks); const diagnostics = asRecord(supervisorView.executionDiagnostics); const filters = asRecord(supervisorView.filters); const activeRunning = asRecord(supervisorView.activeRunning); @@ -279,9 +283,21 @@ export function runCodeQueueSupervisorDisclosureContract(): JsonRecord { const splitBrainLiveActivity = asRecord(splitBrainLiveView.activity); const splitBrainLiveConcurrency = asRecord(splitBrainLiveView.commanderConcurrency); const splitBrainLiveCounts = asRecord(splitBrainLiveView.counts); + const cappedFilters = asRecord(cappedSupervisorView.filters); + const cappedSource = asRecord(cappedSupervisorView.source); + const cappedLimitPolicy = asRecord(asRecord(cappedSupervisorView.disclosure).limitPolicy); + const cappedCommands = asRecord(cappedSupervisorView.commands); + const cappedFullFilters = asRecord(cappedFullTasks.filters); + const cappedFullSource = asRecord(cappedFullTasks.source); assertCondition(supervisorBody.length < fullBody.length * 0.55, "supervisor output should be materially smaller than full output", { supervisorChars: supervisorBody.length, fullChars: fullBody.length }); assertCondition(supervisorBody.length < 45_000, "supervisor output should remain bounded even with large diagnostics", { supervisorChars: supervisorBody.length }); + assertCondition(cappedFilters.requestedLimit === 260 && cappedFilters.effectiveLimit === 100 && cappedFilters.limit === 100 && cappedFilters.limitCapped === true, "supervisor filters should disclose requested and capped effective limit", cappedFilters); + assertCondition(cappedSource.requestedLimit === 200 && cappedSource.effectiveLimit === 200 && cappedSource.limit === 200 && cappedSource.returned === 15, "supervisor source should disclose independent overview fetch limit", cappedSource); + assertCondition(cappedLimitPolicy.requestedLimit === 260 && cappedLimitPolicy.effectiveLimit === 100 && cappedLimitPolicy.sourceFetchLimit === 200 && cappedLimitPolicy.sourceEffectiveLimit === 200, "supervisor disclosure should summarize requested/effective/source limits", cappedLimitPolicy); + assertCondition(String(cappedCommands.refresh ?? "").includes("--limit 260") && String(cappedCommands.byStatus ?? "").includes("--limit 260"), "supervisor follow-up commands should preserve requested limit", cappedCommands); + assertCondition(cappedFullFilters.requestedLimit === 260 && cappedFullFilters.effectiveLimit === 100 && cappedFullFilters.limitCapped === true, "full view filters should disclose capped requested limit", cappedFullFilters); + assertCondition(cappedFullSource.requestedLimit === 200 && cappedFullSource.effectiveLimit === 200, "full view source should disclose independent overview fetch limit", cappedFullSource); assertCondition(recentItems.length === 3, "recentCompleted should be capped below --limit by default", { returned: recentItems.length }); assertCondition(asArray(completedUnread.items).length === 3, "completedUnread should be locally paged and kept separate from recentCompleted", completedUnread); assertCondition(recentItems.every((item) => asRecord(item).unreadTerminal === false), "recentCompleted should not duplicate unread terminal tasks", { recentItems }); @@ -308,8 +324,8 @@ export function runCodeQueueSupervisorDisclosureContract(): JsonRecord { assertCondition(fullItem.status === "running" && String(fullItem.statusLabel ?? "").includes("awaiting terminal/judge"), "full view should keep raw status while exposing derived closeout label", fullItem); assertCondition(fullItem.awaitingTerminalJudge === true && fullItem.closeoutState === "awaiting-terminal-or-judge", "full view should expose awaiting terminal/judge state", fullItem); assertCondition(fullTasks.returned === 15, "full view must not inherit supervisor recentCompleted cap", fullTasks); - assertCondition(filters.requestedLimit === 260 && filters.limit === 100 && filters.limitCapped === true, "supervisor filters should disclose requested vs effective limit", filters); - assertCondition(outputBudget.requestedLimit === 260 && outputBudget.effectiveLimit === 100 && outputBudget.sectionReturnedLimit === 3, "supervisor must expose output budget metadata", outputBudget); + assertCondition(filters.requestedLimit === 20 && filters.limit === 20 && filters.limitCapped === false, "supervisor filters should disclose requested vs effective limit", filters); + assertCondition(outputBudget.requestedLimit === 20 && outputBudget.effectiveLimit === 20 && outputBudget.sectionReturnedLimit === 3, "supervisor must expose output budget metadata", outputBudget); assertCondition(activeRunning.count === 15 && activeRunning.exact === true && activeRunning.source === "queue-summary-counts", "activeRunning should expose exact running+judging count from queue summary", activeRunning); assertCondition(activeRunningRowPage.returned === 1 && activeRunningRowPage.returnedLimit === 3 && activeRunningRowPage.distinction.includes("row page"), "activeRunning row page should distinguish returned rows from active count", activeRunningRowPage); assertCondition(activeRunningRedline.countField === "supervisor.activeRunning.count" && activeRunningRedline.hardRedline === 15 && activeRunningRedline.state === "at-or-over-hard-redline", "activeRunning redline should name count field and interpretation", activeRunningRedline); @@ -349,6 +365,7 @@ export function runCodeQueueSupervisorDisclosureContract(): JsonRecord { checks: [ "supervisor output materially smaller than full", "recentCompleted capped", + "explicit --limit cap disclosed", "running/unread locally paged", "split-brain diagnostics capped", "active running exact count exposed", diff --git a/scripts/src/code-queue.ts b/scripts/src/code-queue.ts index 19e8d5c4..ecb62d87 100644 --- a/scripts/src/code-queue.ts +++ b/scripts/src/code-queue.ts @@ -14,6 +14,7 @@ const maxOutputPreviewChars = 1200; const defaultTasksLimit = 20; const defaultQueuesLimit = 8; const maxTasksLimit = 100; +const tasksOverviewSourceFetchLimit = 200; const supervisorSectionReturnedLimit = 3; const supervisorRecentCompletedLimit = 3; const supervisorPromptPreviewChars = 70; @@ -2411,6 +2412,32 @@ function taskListCommand(options: CodexTasksOptions, extra: string[] = []): stri return `bun scripts/cli.ts ${args.join(" ")}`; } +function codexTasksLimitDisclosure(options: CodexTasksOptions): Record { + return { + requestedLimit: options.requestedLimit, + effectiveLimit: options.limit, + maxLimit: maxTasksLimit, + limitCapped: options.requestedLimit > options.limit, + }; +} + +function codexTasksSourceDisclosure(pagination: Record): Record { + const effectiveLimit = asNumber(pagination.limit, 0) || null; + return { + endpoint: "/api/tasks/overview", + requestedLimit: tasksOverviewSourceFetchLimit, + fetchLimit: tasksOverviewSourceFetchLimit, + limit: effectiveLimit, + effectiveLimit, + limitCapped: effectiveLimit !== null && effectiveLimit < tasksOverviewSourceFetchLimit, + returned: asNumber(pagination.returned, 0) || null, + total: asNumber(pagination.total, 0) || null, + hasMore: asBoolean(pagination.hasMore), + nextBeforeId: asString(pagination.nextBeforeId) || null, + includeActive: asBoolean(pagination.includeActive), + }; +} + function taskSectionLimit(options: CodexTasksOptions, maxReturned = supervisorSectionReturnedLimit): number { return Math.min(options.limit, maxReturned); } @@ -2618,8 +2645,8 @@ type CodexTasksTaskPage = { function tasksListQueryString(options: CodexTasksOptions): string { return queryString({ - limit: 200, - priorityLimit: 200, + limit: tasksOverviewSourceFetchLimit, + priorityLimit: tasksOverviewSourceFetchLimit, queueId: options.queueId, beforeId: options.beforeId, includeActive: 1, @@ -2692,28 +2719,24 @@ function codexTasksOverviewResult( queueId: options.queueId ?? null, requestedLimit: options.requestedLimit, limit: options.limit, - effectiveLimit: options.limit, - limitCapped: options.limit < options.requestedLimit, + ...codexTasksLimitDisclosure(options), unreadOnly: options.unreadOnly, status: options.statusFilter, beforeId: options.beforeId ?? null, }, - source: { - endpoint: "/api/tasks/overview", - queueId: options.queueId ?? null, - limit: asNumber(pagination.limit, 0) || null, - limitSemantics: "upstream overview fetch limit; separate from filters.requestedLimit/effectiveLimit and compact section row counts", - returned: asNumber(pagination.returned, 0) || null, - total: asNumber(pagination.total, 0) || null, - hasMore: asBoolean(pagination.hasMore), - nextBeforeId: asString(pagination.nextBeforeId) || null, - includeActive: asBoolean(pagination.includeActive), - }, + source: { queueId: options.queueId ?? null, ...codexTasksSourceDisclosure(pagination) }, bounded: true, disclosure: { defaultView: "supervisor", policy: "bounded summary rows only; prompt/body are short previews and raw detail requires explicit task/full/trace/output commands", limitSemantics: "--limit is request/scanning/page budget; supervisor sections still return compact bounded row pages and expose counts separately", + limitPolicy: { + ...codexTasksLimitDisclosure(options), + sourceFetchLimit: tasksOverviewSourceFetchLimit, + sourceEffectiveLimit: asNumber(pagination.limit, 0) || null, + sourceReturned: asNumber(pagination.returned, 0) || null, + note: "codex tasks --limit is capped for CLI output safety; supervisor sections also keep small row caps and expose commands.next for pagination", + }, outputBudget: { promptPreviewChars: supervisorPromptPreviewChars, bodyPreviewChars: supervisorBodyPreviewChars, @@ -2786,19 +2809,12 @@ function codexTasksFullResult( queueId: options.queueId ?? null, requestedLimit: options.requestedLimit, limit: options.limit, - effectiveLimit: options.limit, - limitCapped: options.limit < options.requestedLimit, + ...codexTasksLimitDisclosure(options), unreadOnly: options.unreadOnly, status: options.statusFilter, beforeId: options.beforeId ?? null, }, - source: { - endpoint: "/api/tasks/overview", - returned: asNumber(pagination.returned, 0) || null, - total: asNumber(pagination.total, 0) || null, - hasMore: asBoolean(pagination.hasMore), - nextBeforeId, - }, + source: codexTasksSourceDisclosure(pagination), count: filtered.length, returned: visible.length, hasMore: filtered.length > visible.length || sourceHasMore,