Skip to content

feat(action): ⚡️ Add parallel batch and dimension execution#8

Merged
jorben merged 3 commits into
masterfrom
feat/parallel-support
Apr 19, 2026
Merged

feat(action): ⚡️ Add parallel batch and dimension execution#8
jorben merged 3 commits into
masterfrom
feat/parallel-support

Conversation

@HayWolf

@HayWolf HayWolf commented Apr 19, 2026

Copy link
Copy Markdown

Summary

  • Add two-level parallel execution within each review round: batch-level (via Promise.allSettled) and dimension-level (general-first, then remaining dimensions in parallel)
  • Introduce lightweight createSemaphore concurrency limiter to cap total in-flight API calls
  • New max_concurrency action input (default 4) for user-tunable parallelism
  • Budget pre-allocation prevents over-scheduling batches when model call budget is low

Changes

Core (src/index.js)

  • createSemaphore(n): inline Promise-based semaphore (no new dependencies)
  • runDimension(): extracted function for single dimension review call
  • executeBatch(): extracted function orchestrating general-first → parallel dimensions
  • Round loop refactored from sequential for to Promise.allSettled over semaphore-guarded batches
  • Results merged into global state after all batches settle
  • Log format updated with [B{n}/{total}] prefix for parallel batch identification

Config (action.yml, src/config.js)

  • New max_concurrency input with parsePositiveIntInput validation

Tests

  • 3 new config tests: default value, custom value, invalid value rejection
  • 4 new semaphore tests: concurrency limit, sequential mode, error propagation, return values

Test Plan

  • npm run check — syntax OK
  • npm test — 118/118 pass (111 existing + 7 new)
  • Smoke test on a real PR with multiple batches to verify wall-clock speedup
  • Verify max_concurrency=1 produces identical results to previous serial behavior

Notes

  • max_concurrency=1 is a safe fallback that restores serial execution
  • API rate limits may be the practical bottleneck at high concurrency; default 4 is conservative
  • Round-level execution remains serial (hard dependency on coverageState updates)

🤖 Generated with TiyCode

@github-actions

github-actions Bot commented Apr 19, 2026

Copy link
Copy Markdown

AI 代码审查汇总

PR: #8 (feat(action): ⚡️ Add parallel batch and dimension execution)
指定语言: 简体中文

总体评价

共发现 7 条可执行问题,建议优先处理 CRITICAL/HIGH。

主要问题(按严重级别)

  • HIGH (1)
    • src/index.js:166 - createSemaphore 信号量缺少单元测试覆盖
  • MEDIUM (3)
    • src/index.js:499 - executeBatch 并行执行逻辑缺少集成测试
    • src/index.js:911 - 预算分配时未考虑当前轮次已消耗的调用数
    • src/index.js:908 - 预算预分配逻辑缺少边界测试
  • LOW (3)
    • src/index.js:166 - 信号量缺少对并发参数的边界校验
    • src/index.js:629 - 并行执行时 recommendedExtraDimensions 的动态追加可能影响其他批次
    • src/index.js:657 - Promise.allSettled 错误处理路径缺少测试

可执行建议

  • 在 createSemaphore 函数开头添加对 concurrency 参数的校验,确保其为正整数
  • 考虑在 executeBatch 内部检查 modelCalls 是否已超限,并在超限时跳过后续维度执行
  • 在预算计算时考虑为正在执行的批次预留调用数,或使用原子计数器模式
  • 添加单元测试覆盖信号量的并发控制逻辑,验证队列和 active 计数的正确性
  • 在 test/index.test.js 中新增 createSemaphore 信号量测试套件,覆盖: concurrency=0/1/N、连续 run 调用顺序、异步函数抛错时错误传播、高并发下队列正确处理
  • 添加 executeBatch 函数的单元测试,模拟 reviewerAgents 返回成功/失败/混合结果,验证结果聚合、degradedReasons 收集、coveredPaths 更新等逻辑
  • 添加预算预分配边界测试: maxModelCalls=0、预算刚好够一个批次、预算足够部分批次、roundDimensions 为空数组等场景
  • 添加并行执行异常处理测试: 模拟 runDimension 抛出异常、验证 Promise.allSettled rejected 分支处理正确
  • 考虑在 test/config.test.js 中补充 max_concurrency 为负数(如 '-1')和非数字(如 'abc')输入的测试用例,以增强边界值覆盖。

潜在风险

  • 并行执行时预算控制可能失效,导致模型调用超限
  • 如果 config.maxConcurrency 配置为无效值(0 或负数),可能导致系统挂起或无限队列增长
  • 未经充分测试的并发控制逻辑可能导致生产环境中的竞态条件或死锁
  • 预算预分配的 2x 乘数因子缺乏验证,可能在特定配置下导致预算超支或利用率不足
  • 并行执行重构改变了原有的执行语义,可能引入新的边界条件问题

测试建议

  • 测试 createSemaphore 在 concurrency=0 时的行为(应抛错或合理处理)
  • 测试 createSemaphore 在 concurrency=1 时的串行执行正确性
  • 测试多个并发任务时信号量的队列处理顺序
  • 测试预算分配在多批次并行执行时的准确性
  • 测试 modelCalls 超过 maxModelCalls 时的提前终止行为
  • createSemaphore 单元测试: 并发限制正确性、队列 FIFO 顺序、错误传播、边界值(concurrency=0,1)
  • executeBatch 集成测试: general 维度先行、并行维度并发执行、结果聚合正确性
  • 预算预分配边界测试: 零预算、部分预算、充足预算等场景
  • Promise.allSettled 异常处理测试: 部分维度失败时正确处理并继续执行
  • 测试 max_concurrency 输入为负数(如 '-1')时应正确拒绝
  • 测试 max_concurrency 输入为非数字(如 'abc')时应正确拒绝或报错
  • 验证 src/config.js 和 src/provider.js 中是否已实现文档中描述的 HTTPS-only 强制和 hostname allowlist 逻辑

文件级覆盖说明

  • src/index.js: needs_tests (重构将串行批处理改为并行执行,引入了新的并发控制原语和执行流程,需要补充对应的测试覆盖以确保功能正确性和稳定性。)
  • src/config.js: safe (新增 maxConcurrency 配置项,使用 parsePositiveIntInput 确保输入为正整数,默认值 4 合理,符合项目架构中关于并发控制的描述。)
  • test/config.test.js: ok (测试覆盖完整:默认值、自定义值、边界值(0)校验,与现有测试风格一致。可考虑补充负数和非数字输入的测试用例以增强边界覆盖。)
  • AGENTS.md: 文档结构完整,新增模块描述(model-runtime.js、provider.js、inline-key.js、public-error.js、repo-guidance.js)与并行执行架构说明准确反映了代码库演进。安全提示部分更新为 api_key/api_base 和 HTTPS 校验,建议与实际实现核对。 (新增的 Architecture Notes 段落(R40-R44)清晰描述了并行执行和信号量控制机制,与 README 中的 max_concurrency 参数说明一致。)
  • README.md: README 变更准确描述了多提供商支持和并行执行特性。新增 max_concurrency 参数文档、移除已弃用的 llm_compatibility_mode 参数,与 AGENTS.md 保持同步。 (移除了关于 OPENAI_API_BASE 的旧说明,更新为 api_base 和 HTTPS-only 校验描述,与 AGENTS.md 安全提示一致。)

无法 inline 的已处理项

覆盖状态

  • Target files: 6
  • Covered files: 6
  • Uncovered files: 0
  • No-patch/binary covered as file-level: 0
  • 置信度未知(N/A)的问题数: 0

未覆盖文件清单:

无 patch 文件覆盖清单:

轮次与预算

  • 轮次: 1/2
  • 计划批次: 4
  • 执行批次: 4
  • SubAgent 执行次数: 6
  • Planner 调用: 1
  • SubAgent 调用: 6
  • 模型调用: 7/64
  • 结构化输出降级为仅汇总评论: 否

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

自动化 PR 审查已完成。

  • Findings kept: 0
  • Findings with unknown confidence: 0
  • Inline comments attempted: 1
  • Target files: 4
  • Covered files: 4
  • Uncovered files: 0
    详细结论请查看汇总评论。

Comment thread src/config.js
@@ -171,6 +171,7 @@ function loadConfig() {
coverageFirstRoundPrimaryOnly: parseBooleanInput('coverage_first_round_primary_only', true),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

自动审查已完成;聚合后没有可稳定定位的具体 inline 问题。

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

自动化 PR 审查已完成。

  • Findings kept: 3
  • Findings with unknown confidence: 0
  • Inline comments attempted: 3
  • Target files: 4
  • Covered files: 4
  • Uncovered files: 0
    详细结论请查看汇总评论。

Comment thread src/index.js Outdated
.map((path) => pendingFileByPath.get(path))
.filter(Boolean);
// Pre-allocate budget: each batch gets at most roundDimensions.length calls
const estimatedCallsPerBatch = roundDimensions.length;

This comment was marked as outdated.

Comment thread src/index.js

// Phase 2: run remaining dimensions in parallel via semaphore
if (parallelDimensions.length > 0) {
const dimPromises = parallelDimensions.map((dimension) =>

This comment was marked as outdated.

Comment thread src/index.js
*/
async function executeBatch({
batch, batchIndex, batchCount, round, roundDimensions,
pendingFileByPath, reviewerAgents, config, projectGuidance, semaphore

This comment was marked as outdated.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

自动化 PR 审查已完成。

  • Findings kept: 1
  • Findings with unknown confidence: 0
  • Inline comments attempted: 1
  • Target files: 4
  • Covered files: 4
  • Uncovered files: 0
    详细结论请查看汇总评论。

Comment thread src/index.js
const semaphore = createSemaphore(config.maxConcurrency);
const batchCount = plannedBatches.length;

// Pre-allocate budget: each batch may use up to roundDimensions.length calls,

This comment was marked as outdated.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

自动化 PR 审查已完成。

  • Findings kept: 0
  • Findings with unknown confidence: 0
  • Inline comments attempted: 1
  • Target files: 4
  • Covered files: 4
  • Uncovered files: 0
    详细结论请查看汇总评论。

Comment thread src/config.js
@@ -171,6 +171,7 @@ function loadConfig() {
coverageFirstRoundPrimaryOnly: parseBooleanInput('coverage_first_round_primary_only', true),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

自动审查已完成;聚合后没有可稳定定位的具体 inline 问题。

Base automatically changed from feat/add-ai-provider-factory to master April 19, 2026 06:34
jorben added 2 commits April 19, 2026 14:34
Add configurable concurrency limiting for sub-agent and batch processing via
a new `max_concurrency` input.

- Introduce `createSemaphore` to cap concurrent async review runs
- Refactor batch execution to run dimension reviews in parallel (with
  general dimension executed first)
- Add `max_concurrency` parsing and validation to config
- Update tests for default, custom, invalid `max_concurrency`, and semaphore
  behavior
@jorben jorben force-pushed the feat/parallel-support branch from c0395f6 to 163b140 Compare April 19, 2026 06:35

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

自动化 PR 审查已完成。

  • Findings kept: 6
  • Findings with unknown confidence: 0
  • Inline comments attempted: 6
  • Target files: 4
  • Covered files: 4
  • Uncovered files: 0
    详细结论请查看汇总评论。

Comment thread src/index.js
* Returns { run(asyncFn) → Promise } that ensures at most `concurrency`
* async functions execute simultaneously.
*/
function createSemaphore(concurrency) {

This comment was marked as outdated.

Comment thread src/index.js
* general dimension runs first (to collect recommendedExtraDimensions),
* then remaining dimensions run in parallel via the shared semaphore.
*/
async function executeBatch({

This comment was marked as outdated.

Comment thread src/index.js

// Pre-allocate budget: each batch may use up to roundDimensions.length calls,
// with a 2x multiplier to account for potential structured-output repair retries.
const estimatedCallsPerBatch = roundDimensions.length * 2;

This comment was marked as outdated.

Comment thread src/index.js Outdated
executionDimensions.push(roundDimensions[0]);
}
const batchPromises = batchesToRun.map((batch, batchIndex) =>
semaphore.run(() => executeBatch({

This comment was marked as outdated.

Comment thread src/index.js
// --- Merge batch results into global state ---
for (let i = 0; i < batchSettled.length; i += 1) {
const outcome = batchSettled[i];
if (outcome.status === 'rejected') {

This comment was marked as outdated.

Comment thread src/config.js
coverageFirstRoundPrimaryOnly: parseBooleanInput('coverage_first_round_primary_only', true),
autoMinimizeOutdatedComments: parseBooleanInput('auto_minimize_outdated_comments', true),
maxRounds: parsePositiveIntInput('max_rounds', 8),
maxConcurrency: parsePositiveIntInput('max_concurrency', 4),

This comment was marked as outdated.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

自动化 PR 审查已完成。

  • Findings kept: 7
  • Findings with unknown confidence: 0
  • Inline comments attempted: 7
  • Target files: 6
  • Covered files: 6
  • Uncovered files: 0
    详细结论请查看汇总评论。

Comment thread src/index.js
* Returns { run(asyncFn) → Promise } that ensures at most `concurrency`
* async functions execute simultaneously.
*/
function createSemaphore(concurrency) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[HIGH] createSemaphore 信号量缺少单元测试覆盖

新增的 createSemaphore 函数是一个关键的并发控制原语,已导出供测试使用,但未发现对应的单元测试文件变更。信号量逻辑包含队列管理、异步执行顺序、错误传播等复杂场景,需要充分测试。

建议: 在 test/index.test.js 中添加 createSemaphore 的专项测试:并发限制生效、队列顺序保持、错误正确传播、边界值(concurrency=0,1)、连续调用稳定性。

风险: 未经测试的并发控制可能导致竞态条件、死锁或资源泄漏,在高并发生产环境中难以排查。

置信度: 0.95

[来自 SubAgent:testing]

Comment thread src/index.js
* general dimension runs first (to collect recommendedExtraDimensions),
* then remaining dimensions run in parallel via the shared semaphore.
*/
async function executeBatch({

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[MEDIUM] executeBatch 并行执行逻辑缺少集成测试

executeBatch 重构了批量执行逻辑,包含 general 维度先行、其余维度并行、信号量控制等新特性,但未发现相应的测试覆盖。

建议: 添加 executeBatch 的单元测试:模拟 reviewerAgents 返回不同结果、验证 general 维度优先执行、验证并行维度正确并发、验证结果正确聚合。

风险: 复杂的并行执行逻辑未经测试可能导致维度执行顺序错误、结果丢失或状态不一致。

置信度: 0.85

[来自 SubAgent:testing]

Comment thread src/index.js
const estimatedCallsPerBatch = roundDimensions.length * 2;
let budgetAllowedBatches = batchCount;
if (estimatedCallsPerBatch > 0) {
const budgetRemaining = config.maxModelCalls - modelCalls;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[MEDIUM] 预算分配时未考虑当前轮次已消耗的调用数

在并行执行批次前计算剩余预算时,modelCalls 尚未包含当前正在执行的批次的调用数,因为 Promise.allSettled 是在计算预算之后才等待结果。这意味着在并行场景下,如果多个批次同时执行,它们各自的调用数不会被计入预算检查,可能导致总调用数超过 maxModelCalls 限制。

建议: 考虑在并行执行前为每个批次预留预算,或在批次执行完成后动态检查总调用数是否超限。可以在 executeBatch 内部检查 modelCalls 并在超限时提前返回。

风险: 可能导致模型调用总次数超出配置的 maxModelCalls 限制,增加成本或触发 API 限流。

置信度: 0.85

[来自 SubAgent:general]

Comment thread src/index.js

// Pre-allocate budget: each batch may use up to roundDimensions.length calls,
// with a 2x multiplier to account for potential structured-output repair retries.
const estimatedCallsPerBatch = roundDimensions.length * 2;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[MEDIUM] 预算预分配逻辑缺少边界测试

预算预分配使用 2x 乘数因子估算每批次调用次数,该逻辑影响并行批次数量的计算,但缺少边界条件测试。

建议: 添加测试用例:roundDimensions 为空、预算刚好不够一个批次、预算足够部分批次、预算充足全部批次等场景。

风险: 边界条件处理不当可能导致预算耗尽时仍启动批次,或过于保守导致利用率不足。

置信度: 0.80

[来自 SubAgent:testing]

Comment thread src/index.js
* Returns { run(asyncFn) → Promise } that ensures at most `concurrency`
* async functions execute simultaneously.
*/
function createSemaphore(concurrency) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[LOW] 信号量缺少对并发参数的边界校验

createSemaphore 函数没有对 concurrency 参数进行有效性校验。如果传入 0 或负数,active < concurrency 条件将始终为真(0)或永不满足(负数),导致队列无限增长或永远阻塞。

建议: 在函数开头添加校验:if (typeof concurrency !== 'number' || concurrency < 1) throw new Error('concurrency must be a positive integer'); 或至少设置默认值。

风险: 如果 config.maxConcurrency 配置错误(如 0 或负数),可能导致死锁或资源耗尽。

置信度: 0.80

[来自 SubAgent:general]

Comment thread src/index.js
const recommendationReason = String(generalResult.output.recommendationReason || '').trim();

if (appendable.length > 0) {
executionDimensions.push(...appendable);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[LOW] 并行执行时 recommendedExtraDimensions 的动态追加可能影响其他批次

在并行执行场景下,每个批次的 executeBatch 会独立执行 general 维度并可能修改 executionDimensions 数组(追加推荐的额外维度)。虽然 executionDimensions 是每个批次的局部变量,但如果 reviewerAgents 对象是共享的,且 recommendedExtraDimensions 推荐的维度在并行批次间存在竞争,可能导致日志混乱或重复创建 reviewer agent。不过由于 reviewerAgents 的写入使用了 || 短路逻辑,实际风险较低。

建议: 此问题风险较低,但建议在文档中说明并行批次间维度推荐的独立性,或考虑在日志中标注批次 ID 以便于追踪。

风险: 可能导致日志中维度推荐信息混淆,但不影响功能正确性。

置信度: 0.75

[来自 SubAgent:general]

Comment thread src/index.js
}))
);

const dimSettled = await Promise.allSettled(dimPromises);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[LOW] Promise.allSettled 错误处理路径缺少测试

维度并行执行使用 Promise.allSettled,其中 rejected 分支增加 subAgentRuns 但未收集其他结果,该路径需要测试验证。

建议: 添加测试模拟维度执行抛出异常,验证 rejected 状态被正确处理、subAgentRuns 计数正确、其他成功维度结果被正常收集。

置信度: 0.75

[来自 SubAgent:testing]

@jorben jorben merged commit bd7fabd into master Apr 19, 2026
2 checks passed
@jorben jorben deleted the feat/parallel-support branch April 19, 2026 06:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants