Skip to content

fix(init): push member registration via MR when main is protected - #677

Closed
damenjs wants to merge 8 commits into
Tencent:mainfrom
damenjs:fix/init-push-via-mr-not-protected-main
Closed

damenjs wants to merge 8 commits into
Tencent:mainfrom
damenjs:fix/init-push-via-mr-not-protected-main

Conversation

@damenjs

@damenjs damenjs commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

teamai init hard-pushed the reviewer config and the empty-repo skeleton directly to the default branch via pushRepoDirectly. When the team repo's main branch is protected (push: No one — common for team repos), the direct push is rejected by the server. Worse, simple-git's push had no subprocess timeout and no GIT_TERMINAL_PROMPT=0 guard, so when credentials were missing the push hung indefinitely instead of throwing, stalling the entire init before the local config was ever written. Every new member running init would hit this and be stuck with no skills/config.

Member registration already lands on the teamai-reports orphan branch upstream — it never touches the protected default branch — so this PR does not move member registration to an MR. That is by design: an orphan branch is not the protected default branch, and the reports-branch flow is the upstream-chosen mechanism for member roster updates. This PR adds a spawn-level timeout guard to that path (and to the reviewer-config and skeleton pushes), so even if the reports branch were also protected or unreachable, init cannot hang.

What this PR changes:

  1. Reviewer-config push via MR (autoPushViaMR), so a protected default branch no longer rejects it. The provider's pr create CLI runs via spawnSync/crossSpawn.sync — which blocks the event loop so withTimeout's timer can never fire — so PrCreateOptions.spawnTimeoutMs is threaded from autoPushViaMR (using initPushBlockTimeoutMs()) through createPrWithFallback → github ghExec / tgit gfExec, which pass it to spawnSync's timeout option. That kills the stalled pr create at the OS level, bounding init for real.
  2. Spawn-level timeout, scoped to init pushes only. A new createGitForInitPush factory passes simple-git's timeout.block (default 30s, configurable via TEAMAI_INIT_PUSH_TIMEOUT_MS), which kills a hung git subprocess at the process level — unlike withTimeout, a Promise.race that only stops awaiting while the child keeps running and holds the Node event loop open. This factory is used only by init's push path; the global createGit has no spawn timeout, so legitimate slow clones/fetches/rebases in unrelated commands are unaffected. The initPush flag is threaded through the entire reports-worktree chain (updateReports → updateImpl → ensureWorktree/syncWorktree/commitAndPushAt, including cold-start ls-remote/fetch/first-push), so a first-time init's worktree setup is also guarded.
  3. Init push retries bounded to 1. The reports push/fetch/rebase retry loop (5 retries elsewhere) is capped at 1 during init, so a stuck remote can no longer hold init for 5× the timeout; a failed init push is non-blocking and retried on the next init.
  4. withTimeout honors the same configured timeout. The withTimeout call sites in init.ts read initPushBlockTimeoutMs() (the same value createGitForInitPush and the MR-creation spawnTimeoutMs use), so the await guard, the git-subprocess kill, and the pr create subprocess kill all share one configurable ceiling.
  5. GIT_TERMINAL_PROMPT=0 process-wide. Set once at CLI startup via disableGitTerminalPrompt() (src/index.ts); every git subprocess inherits it, so a push with missing credentials fails fast with "could not read Username" instead of hanging on an invisible prompt. (Done process-wide rather than via simple-git's .env(): both its overloads replace the child's whole environment — dropping PATH/HOME — so neither is usable for this.)
  6. TEAMAI_INIT_PUSH_TIMEOUT_MS validated as a finite positive integer; negative, non-numeric, or non-finite values fall back to 30s rather than reaching simple-git.

All push/MR failures stay non-blocking (warn only): init always completes and writes local config + skills even if a push could not be created. A protected teamai-reports branch surfaces as a non-blocking warning (the member file is already written to the local worktree and retried on the next run), not a hang.

Summary

Fixes the "teamai init hangs forever after registering as team member" failure mode. Member registration stays on the teamai-reports orphan branch (upstream design, unchanged destination) but gets a spawn-level 30s block timeout (scoped to init pushes only, configurable via TEAMAI_INIT_PUSH_TIMEOUT_MS, threaded through worktree setup, retries capped at 1, and shared by the outer withTimeout await guard) plus GIT_TERMINAL_PROMPT=0 process-wide, so a hung or credential-less push is killed fast instead of stalling init. The reviewer-config push goes via MR (protected default branch no longer rejects it) with the provider's pr create subprocess bounded by the same init-push timeout so a stalled MR creation cannot block the event loop. Push failures remain non-blocking: init always completes and writes local config + skills.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature causing existing behavior to change)
  • Documentation only
  • Refactor / internal cleanup

Test Plan

  • npx tsc --noEmit passes — clean, no errors.
  • npm run build passes — ESM build success.
  • npx vitest run passes — 255 test files / 3529 tests, all green, including:
    • src/__tests__/init-hang-regression.test.ts (new, real-git regression for this PR):
      • Protected default branch: seeds a bare origin with an update hook rejecting pushes to main; pushRepoDirectly({initPush:true}) fails fast (~0.7s) instead of hanging, and the happy path (push to an unprotected branch) still completes normally — both exercise the guarded createGitForInitPush factory (per round-5 feedback), so removing it would fail the tests.
      • Credential prompt (the original bug): a push to a local HTTP remote that always returns 401, with all credential helpers stripped and GIT_TERMINAL_PROMPT=0 set, is killed by the spawn-level block timeout (~3.5s with TEAMAI_INIT_PUSH_TIMEOUT_MS=3000) instead of blocking forever — proving the subprocess kill (not just the await guard) is what actually ends the hang.
    • src/__tests__/git.test.ts (updated): asserts createGit does not add a global spawn timeout (so unrelated slow git ops survive), createGitForInitPush adds the 30s timeout.block (honoring TEAMAI_INIT_PUSH_TIMEOUT_MS), initPushBlockTimeoutMs() validates negative/non-numeric values and is shared by the await guard, and disableGitTerminalPrompt sets GIT_TERMINAL_PROMPT=0 process-wide while preserving an explicit user override.
    • src/__tests__/git-kind-reports.test.ts / git-kind-learnings.test.ts (existing real-git suites, all green): protected-default-branch repos with real git push / worktree / rebase flows — confirms the scoped timeout, prompt guard, and capped init retries do not break any legitimate git operation, including cold-start worktree creation.
  • Added/updated tests for the change — see above.
  • Real-CLI spot check (npm run build then dist/index.js init . against a real GitHub repo in a clean HOME): with no credentials the CLI fails fast at the auth step in ~1s (previously this hung indefinitely on an invisible credential prompt).
  • Note on agent/provider matrix: the change lives in src/utils/git.ts (the shared simple-git factory), src/init.ts, src/utils/branch-worktree.ts, and the github/tgit provider createPullRequest paths, and is provider-agnostic by construction (same code path for git/gitlab/github). The full real-git integration suites above exercise it per-path. If maintainers want the 4-agent × 3-provider end-to-end matrix re-run for this specific change, happy to do it — but the guarded factory is exercised identically regardless of agent/provider choice.

Related Issues

Notes for Reviewers

  • Reviewer config via MR, with the pr create subprocess bounded (per round-5/6 feedback): an earlier revision routed reviewer config through autoPushViaMR, but round-5 flagged that the provider's pr create runs via spawnSync (blocks the event loop, so withTimeout can't interrupt it). This is now fixed at the root: PrCreateOptions.spawnTimeoutMs is threaded from autoPushViaMR (using initPushBlockTimeoutMs()) through createPrWithFallback → github ghExec / tgit gfExec, which pass it to spawnSync/crossSpawn.sync's timeout option — the stalled pr create is killed at the OS level. (The generic git provider's createPullRequest still throws — no PR API — and autoPushViaMR's catch surfaces it as a non-blocking warning; the branch is already pushed, so the user can open the MR manually.)
  • Init push retries capped at 1 (per round-6 feedback): commitAndPushAt's 5-retry push/fetch/rebase loop is bounded to 1 during init (INIT_PUSH_MAX_RETRIES), so a stuck remote can no longer hold init for 5× the timeout; a failed init push is non-blocking and retried on the next init.
  • TEAMAI_INIT_PUSH_TIMEOUT_MS validated (per round-6 feedback): negative, non-numeric, or non-finite values now fall back to 30s instead of reaching simple-git.
  • Member registration is intentionally NOT an MR (per round-2/4 feedback): it already targets the teamai-reports orphan branch upstream — never the protected default branch. This PR adds a spawn-level timeout guard (threaded through worktree setup, retries capped at 1) so it cannot hang.
  • withTimeout honors the configured timeout (per round-4 feedback): the await-guard call sites in init.ts read the exported initPushBlockTimeoutMs(), the same value createGitForInitPush and the MR-creation spawnTimeoutMs use.
  • Spawn-level timeout covers worktree setup (per round-3 feedback): initPush is threaded through ensureWorktree (cold-start ls-remote/fetch/first-push), syncWorktree, remoteBranchExists, and createOrphanWorktree.
  • Scoped, not global, timeout (per round-2 feedback): the 30s spawn-level timeout.block lives in a dedicated createGitForInitPush factory used only by init's push path. The shared createGit has no spawn timeout, so unrelated commands' slow clones/fetches/rebases are not at risk.
  • GIT_TERMINAL_PROMPT=0 actually applied (per round-2 feedback): set process-wide at CLI startup; every git subprocess inherits it. A real-git regression test (401 HTTP remote, helpers stripped) pins the fast-failure behavior.
  • TEAMAI_INIT_PUSH_TIMEOUT_MS documented (per round-4 feedback): added an Environment variables subsection to the Configuration Reference in both docs/usage-guide.md and docs/usage-guide.zh-CN.md.
  • No README changes (per round-3 feedback): the troubleshooting section added in an earlier revision overstated the fix's scope and was dropped entirely — this PR now touches no README.
  • The patches/init-push-via-mr-and-timeout.patch file from the first revision is dropped.

teamai init still has two direct pushes to the default branch that hang
or are rejected when main is protected (push: No one):

1. The reviewer-config push (teamai.yaml) uses pushRepoDirectly —
   rejected on protected main, and simple-git's push has no timeout /
   no GIT_TERMINAL_PROMPT=0 guard, so a missing-credential push hangs
   indefinitely instead of throwing, stalling init before local config
   is written. Switch this to autoPushViaMR (branch + MR, already used
   by other flows) wrapped in withTimeout(30s), non-blocking.

2. The empty-repo skeleton push also uses pushRepoDirectly with no
   timeout — wrap it in withTimeout(30s) so a hung push can never
   block init. (Member registration already moved to the teamai-reports
   orphan branch upstream, so it no longer touches main.)

All failures remain non-blocking (warn only): init always completes and
writes local config + skills even if a push/MR could not be created.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@damenjs
damenjs force-pushed the fix/init-push-via-mr-not-protected-main branch from 67455be to cae3759 Compare September 20, 2026 08:41
Add a Troubleshooting section to README explaining why `teamai init`
hangs after "Registered as team member" (protected default branch +
push with no timeout), with a no-code MR-based quick fix. Ship the
fix as patches/init-push-via-mr-and-timeout.patch so teams can apply
it locally before the PR lands.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@jeff-r2026 jeff-r2026 self-assigned this Sep 20, 2026
@github-actions

Copy link
Copy Markdown
  • [P1 blocking] Missing required test evidence. The PR body leaves the entire Test Plan unchecked and provides neither an end-to-end/real-CLI verification record nor results for the required agent/provider combinations. This must be documented before merge.
  • [P1 blocking] The timeout does not terminate the hanging Git operation. withTimeout(pushRepoDirectly(...)) in src/init.ts:1423 only stops awaiting the promise; it cannot cancel the already-running Git subprocess. A credential prompt can therefore continue holding the Node process open after 30 seconds, so the claimed “init can never stall” guarantee is not achieved.
  • [P1 blocking] The stated member-registration path is not fixed. The PR changes the skeleton push and reviewer-config push, but the member registration still awaits the unchanged updateReports(...) path without a timeout or MR conversion. Thus the PR does not implement its title/summary claim or demonstrate that the reported member-registration hang is resolved.
  • [P1 blocking] README translations are out of sync. README.md:319 adds substantial troubleshooting behavior documentation, but README.ja.md, README.ko.md, README.th.md, and README.zh-CN.md are unchanged, violating the explicit requirement to update every README language version consistently.
  • [P2 non-blocking] Remove the committed patch artifact. patches/init-push-via-mr-and-timeout.patch:1 duplicates the PR’s own source and README changes and tells users to apply a patch already present in the release. It is unnecessary packaging baggage and violates the surgical-change rule.

Address review feedback on PR Tencent#677:

1. createGit now passes simple-git's timeout.block (30s) to every git
   instance, so a hung git subprocess is killed at the spawn level — a
   Promise.race only stopped awaiting while the child kept running and
   held the Node process open. initRepo is also switched to createGit
   (it used a bare simpleGit() and bypassed the guards).

2. createGit also sets GIT_TERMINAL_PROMPT=0 on every git subprocess:
   a push with missing credentials now fails fast with a clear error
   instead of hanging on an invisible prompt (teamai runs git with no
   tty, so the prompt could never be answered).

3. Member-registration updateReports calls (both team-repo and
   single-repo paths) are wrapped in withTimeout(30s) as a second-layer
   await guard on top of the spawn-level kill.

4. All five README language versions now carry the same Troubleshooting
   section (en/zh-CN/ja/ko/th), with the patch-file reference removed
   and the internal hostname taken out.

5. Dropped patches/init-push-via-mr-and-timeout.patch (duplicated the
   PR's own diff — packaging baggage).

New tests: createGit factory-argument assertions in git.test.ts and a
real-git regression suite (init-hang-regression.test.ts) that pins
fast-failure on a protected default branch and the intact happy path.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
  • [P1 blocking] GIT_TERMINAL_PROMPT=0 is not actually applied. env is not a supported SimpleGitOptions constructor field, so simpleGit(options) ignores NO_PROMPT_ENV at src/utils/git.ts:42. The tests only assert that the mocked factory received the property, while the real-git test uses a local remote and never exercises missing credentials. Configure the environment through simple-git’s .env(...) API and add a credential-prompt regression test.

  • [P1 blocking] The 30-second timeout affects every Git operation in the CLI. Adding timeout.block globally in createGit at src/utils/git.ts:41 can terminate legitimate slow clones, fetches, pushes, and worktree operations after 30 seconds without output. This is substantially broader than fixing init pushes and risks regressions across unrelated commands; scope the timeout to the affected init network operations or make it appropriately configurable.

  • [P1 blocking] The PR description contains no testing record. The Test Plan is entirely unchecked and provides neither executed commands nor the required real-CLI end-to-end verification across the relevant agents/providers.

  • [P2 non-blocking] The description does not match the diff. It claims member registration was switched to autoPushViaMR, but registration still uses the existing teamai-reports flow; only reviewer configuration was changed to use an MR.

Comment thread README.zh-CN.md Outdated
| `teamai doctor` | 诊断配置问题(`--json` 输出 JSON,供 CI、hook 与 agent 消费)|
| `teamai uninstall` | 移除所有 teamai 资源和 hooks |

## 故障排查

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This PR should not modify README.

Address second-round review on PR Tencent#677:

1. GIT_TERMINAL_PROMPT=0 was never applied: 'env' is not a
   SimpleGitOptions constructor field, so simpleGit(options) silently
   dropped it. simple-git's .env() overloads both REPLACE the whole
   child environment (dropping PATH/HOME), so neither works for this.
   Set it process-wide via disableGitTerminalPrompt(), called once at
   CLI startup (src/index.ts); every git subprocess inherits it.

2. The 30s block timeout was global in createGit, so it could kill
   legitimate slow clones/fetches/rebases in unrelated commands.
   Removed from createGit; added createGitForInitPush (timeout.block,
   configurable via TEAMAI_INIT_PUSH_TIMEOUT_MS, default 30s) used ONLY
   by init's pushes. pushRepoDirectly / pushRepoBranch / autoPushViaMR /
   updateReports gain an opts.initPush flag threaded through the
   branch-worktree chain; init's 4 push call sites pass it.

3. New real-git regression test: a credential-less push to a 401 HTTP
   remote (with helpers stripped) is killed by the spawn-level timeout
   instead of hanging. Factory tests assert the timeout is NOT global.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
  • [P1 blocking] Missing required test plan and end-to-end record. The PR description leaves the Test Plan unchecked and provides no command results or real-CLI verification. Repository policy requires documented E2E testing, including the relevant agents/providers, before merge.
  • [P1 blocking] The timeout does not cover reports-worktree initialization. updateImpl calls ensureWorktree before reaching the timed Git instance at src/utils/branch-worktree.ts:311. Cold-start operations inside ensureWorktree—including ls-remote, fetch, and the initial teamai-reports push—still use ordinary createGit without a subprocess timeout (src/utils/branch-worktree.ts:421). The outer withTimeout only stops awaiting and cannot terminate a hung Git child, so first-time teamai init can still remain alive indefinitely. Thread initPush through worktree setup and test the actual updateReports init path.
  • [P2 non-blocking] The troubleshooting documentation overstates the fix. README.md:327 says every Git subprocess receives a 30-second timeout, but only selected init push instances use createGitForInitPush; worktree setup and other Git operations remain untimed. Update this statement in every translated README to describe the actual scope.

…leshooting

Address third-round review on PR Tencent#677:

1. The spawn-level timeout did not cover reports-worktree
   initialization. updateImpl called ensureWorktree before reaching
   the timed git instance, so cold-start ls-remote/fetch/first-push
   still used ordinary createGit with no subprocess timeout — a
   first-time init could hang indefinitely. Thread initPush through
   ensureWorktree (and syncWorktree, remoteBranchExists,
   createOrphanWorktree) so every git op during a guarded init push
   uses createGitForInitPush.

2. The README troubleshooting section overstated the fix (claimed
   every git subprocess gets a 30s timeout; only selected init-push
   instances do). Rather than reword five translations, drop the
   section entirely — it was added by this PR and is not essential
   documentation, which also satisfies the surgical-change rule.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
  • [P1 blocking] The PR description has an empty Test Plan and no real-CLI/e2e verification record. Repository policy explicitly requires documented build and end-to-end testing before merge.
  • [P1 blocking] Member registration was not switched to an MR flow. src/init.ts:1452 still calls updateReports, which ultimately performs a direct git push to teamai-reports at src/utils/branch-worktree.ts:340. Repositories protecting that branch still cannot register members, contrary to the PR title/body.
  • [P1 blocking] The 30-second withTimeout calls do not cancel the underlying operation (src/init.ts:1452, src/init.ts:1530). A progress-producing Git process or hung provider API request can continue holding locks, mutating branches, and keeping Node alive after init proceeds. The outer hard-coded 30 seconds also makes TEAMAI_INIT_PUSH_TIMEOUT_MS ineffective for operations intended to exceed 30 seconds.
  • [P2 non-blocking] The new user-configurable TEAMAI_INIT_PUSH_TIMEOUT_MS behavior at src/utils/git.ts:48 is undocumented. The repo rules require behavior changes to update all affected English and Chinese documentation.

…T_MS

Address round-4 review on PR Tencent#677:

1. withTimeout's 30s was hard-coded and did not honor
   TEAMAI_INIT_PUSH_TIMEOUT_MS, so the configurable ceiling was
   ineffective for operations meant to exceed 30s. Export
   initPushBlockTimeoutMs() from git.ts and use it in all four
   withTimeout call sites in init.ts, so the await guard and the
   spawn-level timeout share one configured value.

2. TEAMAI_INIT_PUSH_TIMEOUT_MS (and GIT_TERMINAL_PROMPT) were
   undocumented. Add an Environment variables subsection to the
   Configuration Reference in both usage-guide.md and
   usage-guide.zh-CN.md.

Member registration stays on the teamai-reports orphan branch
(upstream design — an orphan branch is not the protected default
branch). The push is non-blocking and guarded by the spawn-level
timeout threaded through worktree setup; a protected reports branch
surfaces as a non-blocking warning, not a hang.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
  • [P1 blocking] src/init.ts:1530 — withTimeout() cannot interrupt synchronous provider calls inside autoPushViaMR. GitHub’s gh pr create and TGit’s CLI use spawnSync/crossSpawn.sync without a timeout, blocking the event loop so the timer never fires. A stalled MR creation can still prevent init from reaching local-config persistence. Add subprocess-level timeouts or use cancellable asynchronous provider calls.
  • [P1 blocking] The PR description contains no completed Test Plan and no real-CLI end-to-end verification record. Repository policy explicitly requires npm run build followed by real CLI validation across the affected agents/providers before merge.
  • [P2 non-blocking] src/init.ts:1531 — routing reviewer configuration unconditionally through autoPushViaMR regresses the supported generic git provider: GenericGitProvider.createPullRequest() always fails, so settings that previously reached an unprotected default branch now only remain on a feature branch requiring undocumented manual MR creation.
  • [P2 non-blocking] src/__tests__/init-hang-regression.test.ts:95 — the protected-branch regression test calls pushRepoDirectly without { initPush: true }, so it exercises the ordinary unguarded Git factory and cannot detect removal of the new init-specific subprocess timeout.

…n tests

Address round-5 review on PR Tencent#677:

1. autoPushViaMR for reviewer config shelled out to a provider's
   pr-create CLI via spawnSync (gh pr create / TGit), which blocks
   the event loop — withTimeout's timer could never fire, so a
   stalled MR creation still blocked init. Reviewer config is part
   of teamai.yaml and belongs on the default branch, not behind an
   MR, so push it directly with pushRepoDirectly({initPush:true})
   instead. This also fixes the round-5 P2: the generic git
   provider has no PR API, so MR-only routing regressed it —
   direct push works for unprotected default branches (the common
   case), and the spawn-level timeout guards the push itself.

2. The protected-branch and happy-path regression tests called
   pushRepoDirectly without {initPush:true}, so they exercised the
   ordinary unguarded factory and could not detect removal of the
   init-specific subprocess timeout. Both now pass {initPush:true}.

autoPushViaMR is no longer called from init; it remains for the
import flow in push.ts.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Findings

  • [P1 blocking] src/init.ts:1524 still calls pushRepoDirectly for reviewer configuration. A protected default branch therefore rejects the update exactly as before; no MR is created. This contradicts the PR’s primary claimed fix and the documentation at docs/usage-guide.md:1793.
  • [P1 blocking] src/init.ts:1452 applies an await-only timeout to updateReports, but timing out does not cancel it. The background operation continues through the five push/fetch/rebase retries at src/utils/branch-worktree.ts:334, potentially spawning Git children for several additional timeout periods and keeping Node alive. Thus member registration can still make init hang for minutes.
  • [P1 blocking] The PR description does not document the required complete real-CLI verification across Claude, Codex, CodeBuddy, and OpenCode with git, gitlab, and github. It explicitly defers that matrix, while the recorded spot check only covers a credential-failure path and does not demonstrate a completed end-to-end init.
  • [P2 non-blocking] src/utils/git.ts:48 accepts negative and partially numeric timeout values such as -1 or 100abc. Since this is now a documented user-facing environment variable, validate it as a finite positive integer before passing it to simple-git.

…init retries

Address round-6 review on PR Tencent#677:

1. Reviewer config goes via autoPushViaMR again (a protected default
   branch rejects a direct push). The stalled-MR-creation risk from
   round-5 is fixed at the root: PrCreateOptions gains spawnTimeoutMs,
   threaded through createPrWithFallback -> autoPushViaMR (uses
   initPushBlockTimeoutMs) -> github ghExec / tgit gfExec, which pass
   it to spawnSync/crossSpawn.sync's timeout option. spawnSync blocks
   the event loop so withTimeout cannot interrupt it; the spawnSync
   timeout kills the process at the OS level, bounding init for real.

2. updateReports' push/fetch/rebase retry loop could run for several
   timeout periods (5 retries) even after init's withTimeout fired.
   Init pushes now bound retries to 1 (INIT_PUSH_MAX_RETRIES): init is
   non-blocking and retries on the next run, so a stuck remote no
   longer holds init for 5x the timeout.

3. TEAMAI_INIT_PUSH_TIMEOUT_MS accepted -1 / 100abc. Validate as a
   finite positive integer, falling back to 30s otherwise.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Findings

  • [P1 blocking] src/init.ts:1536 still routes reviewer configuration through autoPushViaMR, contrary to the PR description’s claim that this was reverted. GenericGitProvider.createPullRequest() always throws (src/providers/git/index.ts:106), so provider: git users get only a stranded feature branch and their reviewer configuration never reaches the default branch—even when direct push is allowed.
  • [P1 blocking] src/utils/git.ts:87 uses simple-git’s timeout.block, which is an inactivity timeout, not a hard subprocess deadline. A push producing periodic output can survive indefinitely after withTimeout rejects at src/init.ts:1452; the uncancelled child still keeps Node alive and can continue mutating the repository. This does not guarantee the reported hang is eliminated.
  • [P2 non-blocking] The timeout regression test at src/__tests__/init-hang-regression.test.ts:198 sets GIT_TERMINAL_PROMPT=0, then only asserts completion within 10 seconds. That push fails immediately without any spawn timeout, so removing createGitForInitPush would not fail the test as claimed.
  • [P1 blocking] The PR description explicitly says the required 4-agent × 3-provider real-CLI matrix was not run. The repository instructions require end-to-end verification for Claude, Codex, CodeBuddy, and OpenCode across git, gitlab, and github; the single GitHub credential spot-check is insufficient, especially since this diff changes provider-specific PR creation.

@jeff-r2026

Copy link
Copy Markdown
Collaborator

This PR currently has merge conflicts with main. Please rebase and resolve them so review can continue.

@jeff-r2026 jeff-r2026 closed this Sep 23, 2026
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