Skip to content

fix(ci sync): probe a state commit that bundles source edits instead of trusting convergence - #10598

Open
luvkapur wants to merge 4 commits into
masterfrom
fix/ci-sync-bundled-state-commit
Open

fix(ci sync): probe a state commit that bundles source edits instead of trusting convergence#10598
luvkapur wants to merge 4 commits into
masterfrom
fix/ci-sync-bundled-state-commit

Conversation

@luvkapur

@luvkapur luvkapur commented Aug 11, 2026

Copy link
Copy Markdown
Member

Proposed Changes

  • Fix bit ci sync wrongly reporting "converged" when one git commit contains both a source edit and a .bitmap change. The edit never reached the lane, and the next lane update would overwrite it on the branch.

The bug

Sync decides "does the branch have new work?" by counting commits AFTER the last commit that touched .bitmap. When a single commit changes .bitmap AND source files, that count is zero — so sync reports noop (converged) even though the source edit was never snapped. The lane never gets the edit, and a later import-lane overwrites it.

This is exactly the commit shape sync's own conflict-resolution instructions produce (bit lane import, fix the files, commit once). Found live while testing the halt → resolve → resume flow.

The fix

Git alone can't tell whether the bundled files are already inside the recorded snap (a dev who snapped, exported, and committed everything at once) or were never snapped. So instead of guessing, sync checks with bit:

  • Nothing to snap → truly converged. Sync writes nothing, same as before.
  • Something to snap → real work. Sync exports it to the lane, like any other dev commit.

Where a wrong "no work" answer could lose something (branch deletion, divergence, first contact), the bundled commit counts as work — the safe direction. Sync's own ledger commits are exempt (they legitimately bundle merged sources).

Tests

  • New e2e reproducing the bug: red on master (noop (converged)), green with the fix (the lane gets the edit; the next run converges).
  • The three ci-sync-state.e2e.ts cells that had locked the old behavior are updated: the converged-dev case still ends with zero writes, the invisible-edit case exports immediately.
  • 257 unit tests, all 59 ci-sync e2e cells (both suites, post-merge with fix(ci sync): adopt a branch whose lane exists but whose committed .bitmap has no lane state #10593), lint, prettier — green.

Note

Master (with #10593 merged) is merged in. At first contact, a bundled commit routes to adopt-branch — adoption already checks with bit before writing — and the different-lane guard now also fires for bundled commits.

…of trusting convergence

a commit changing .bitmap AND sources is its own state commit, so the
dev-commit count started after it and the edits read as converged —
the exact shape the conflict-halt comment's resolve-by-hand recipe
produces, stranding the resolution (and a later lane move would
import-lane over it). git names alone cannot tell whether those
sources are already inside the recorded snap, so the planner treats
them as suspected work: a probe-only export-branch lets the snap
decide — nothing pending settles as converged with zero writes, real
work exports to the lane. suspected work counts as work on every path
where a wrong 'no work' answer could lose something (deletion,
divergence, first contact). the state-model cells that locked the old
Stage-1 delta are rewritten to the new contract
@luvkapur
luvkapur marked this pull request as draft August 11, 2026 18:59
@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Aug 11, 2026

Copy link
Copy Markdown

PR Summary by Qodo

Fix bit ci sync false convergence on bundled .bitmap+source state commits

🐞 Bug fix 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Detect when the branch state commit also touches sources, not just .bitmap.
• Treat bundled state commits as “suspected work” and probe via Bit before declaring convergence.
• Add/adjust e2e + unit tests to cover bundled-commit and probe-only behaviors.
Diagram

graph TD
  A(["LaneSyncExecutor"]) --> B["readBranchSyncState()"] --> C["planLaneSync()"] --> D{"probeOnly?"} --> E["executeExportBranch()"] --> F(["bit snap+export"]) --> G[("Remote lane")]
  E --> H[("Remote branch")]

  subgraph Legend
    direction LR
    _exec(["Executor"]) ~~~ _mod["Module"] ~~~ _dec{"Decision"} ~~~ _rem[("Remote")]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Probe with `bit status` before snap/export
  • ➕ Cheaper than running a snap/export when the bundled sources were already snapped.
  • ➕ Aligns with adopt-branch’s existing “status-first” probing approach.
  • ➖ Still requires a second step (snap+export) when changes exist, so adds branching/complexity.
  • ➖ May differ from snap’s notion of “nothing to snap” if status output/filters diverge, creating edge cases.
2. Always treat bundled state commits as dev commits (force export)
  • ➕ Simplest rule; guarantees no false convergence that could lose work.
  • ➖ Can create unnecessary exports/ledger commits when a developer legitimately snapped+exported+committed together.
  • ➖ More lane churn and noisier history for a case that can be legitimately converged.

Recommendation: The PR’s approach (flag bundled state commits as suspected work, then run a probe-only export that can settle with zero writes) is the best balance of safety and correctness. It avoids the data-loss failure mode while preventing unnecessary ledger commits when the bundled sources were already snapped; the added planner/action surface area is justified by the risk profile of declaring a false noop.

Files changed (7) +216 / -55

Bug fix (3) +112 / -34
lane-sync-executor.tsThread bundled-state detection into planning and add probe-only export behavior +23/-25

Thread bundled-state detection into planning and add probe-only export behavior

• Extends the executor’s branch state defaults and logging to include 'stateCommitBundlesSources'. Treats bundled commits as potential work for the “different-lane” first-contact guard, and passes 'probeOnly' through to 'executeExportBranch' so a no-op export can settle as converged without writing a ledger commit.

scopes/git/ci/sync/lane-sync-executor.ts

sync-planner.tsPlan probe-only export for bundled source edits in the state commit +23/-8

Plan probe-only export for bundled source edits in the state commit

• Adds 'stateCommitBundlesSources' to the planner input and treats it as “may carry work” wherever a false ‘no work’ decision could be destructive (deletion, divergence, first contact). When otherwise converged, plans 'export-branch' with 'probeOnly' so the executor can confirm via Bit without committing unnecessary ledger updates.

scopes/git/ci/sync/sync-planner.ts

sync-state.tsDetect when the state commit bundles sources and expose it to the planner +66/-1

Detect when the state commit bundles sources and expose it to the planner

• Extends 'BranchSyncState' with 'stateCommitBundlesSources' and computes it when the state commit is also the tip, not sync-authored, and 'git diff-tree' shows files beyond '.bitmap'. Adds a defensive 'diff-tree' invocation (first-parent, root-aware, merge-aware) and treats unreadable output as ‘true’ to avoid unsafe convergence decisions.

scopes/git/ci/sync/sync-state.ts

Tests (4) +104 / -21
ci-sync-state.e2e.tsUpdate state-model e2e expectations for bundled state commits +20/-21

Update state-model e2e expectations for bundled state commits

• Rewrites the previously-locked “invisible edit then self-heal” contract: bundled '.bitmap'+source commits now trigger an immediate export (or a probe that settles as converged with zero writes when nothing is pending). Adjusts assertions around outputs and adds a clearer lane-deletion guard scenario using a '.bitmap'-only commit.

e2e/harmony/ci-sync-state.e2e.ts

ci-sync.e2e.tsAdd e2e reproducer for bundled '.bitmap'+source commit false convergence +45/-0

Add e2e reproducer for bundled '.bitmap'+source commit false convergence

• Introduces a new scenario where a single commit edits sources and touches '.bitmap' without changing the parsed state. Verifies the first sync exports the edit to the lane (no false noop), and a second run correctly reports convergence.

e2e/harmony/ci-sync.e2e.ts

sync-planner.spec.tsExpand planner decision-table tests for bundled-state probing +25/-0

Expand planner decision-table tests for bundled-state probing

• Adds table rows asserting that bundled sources plan 'export-branch' with 'probeOnly', count as work for merge-diverged and branch-keep paths, and route to 'adopt-branch' on first contact.

scopes/git/ci/sync/sync-planner.spec.ts

sync-state.spec.tsAdd unit tests for detecting non-'.bitmap' files in a state commit +14/-0

Add unit tests for detecting non-'.bitmap' files in a state commit

• Introduces tests for 'touchesBeyondBitmap()', ensuring '.bitmap'-only and empty diffs are false while any additional file marks the commit as bundling sources.

scopes/git/ci/sync/sync-state.spec.ts

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Aug 11, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (1) 📜 Skill insights (0)

Grey Divider


Action required

1. Empty diff treated clean ✓ Resolved 🐞 Bug ☼ Reliability
Description
commitTouchesBeyondBitmap() is documented to fail-safe (unreadable ⇒ true), but if git.raw()
resolves with an empty string it returns false via touchesBeyondBitmap(''). Given this module
already notes that simple-git can resolve empty output on non-zero exits, this can incorrectly
clear stateCommitBundlesSources and let a bundled state commit be declared converged without
probing/exporting.
Code

scopes/git/ci/sync/sync-state.ts[R218-221]

+    ]);
+    return touchesBeyondBitmap(names);
+  } catch {
+    return true;
Evidence
The code explicitly states unreadable outputs must keep the export path open, but the implementation
only handles the thrown-error case; an empty resolved string will be treated as “no files besides
.bitmap” because touchesBeyondBitmap('') is false. The same file already documents that
simple-git can resolve empty output on non-zero exits, making this a realistic failure mode.

scopes/git/ci/sync/sync-state.ts[145-153]
scopes/git/ci/sync/sync-state.ts[176-199]
scopes/git/ci/sync/sync-state.ts[201-223]
scopes/git/ci/sync/sync-state.ts[225-232]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`commitTouchesBeyondBitmap()` promises “Unreadable answers `true`”, but currently only treats thrown errors as unreadable. If `git.raw([...diff-tree...])` returns an empty string (which this module already documents as possible for `git.raw` on non-zero exits), the function will incorrectly return `false`.
### Issue Context
This boolean feeds `stateCommitBundlesSources`, which is used to decide whether to probe/export rather than declare convergence.
### Fix Focus Areas
- scopes/git/ci/sync/sync-state.ts[201-223]
### Suggested change
In `commitTouchesBeyondBitmap()`:
- After `git.raw(...)`, add a guard:
- if `!names.trim()` return `true` (unknown/unreadable)
- optionally also if the output doesn’t include `.bitmap` (unexpected for a “state commit”), return `true`
- Consider adding/adjusting a unit test by factoring the guard into a small exported helper (or otherwise making it testable) to cover the empty-string case without relying on real git execution.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. CiMain uses chalk.yellow 📘 Rule violation ⚙ Maintainability
Description
This PR modifies CLI output code but still formats the message with direct chalk.yellow(...)
instead of using the shared @teambit/cli output formatting toolkit/style guide. This can lead to
inconsistent CLI output styling and harder-to-maintain formatting across commands.
Code

scopes/git/ci/ci.main.runtime.ts[R1146-1147]

+        this.logger.console(chalk.yellow(NO_CHANGES_TO_SNAP));
+        return NO_CHANGES_TO_SNAP;
Evidence
PR Compliance ID 1 requires using the shared CLI output formatting toolkit/style guide when
modifying CLI output. The changed lines still apply direct chalk.yellow(...) formatting for the
user-facing message, bypassing the shared formatter utilities.

CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide
scopes/git/ci/ci.main.runtime.ts[1146-1147]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The `bit` CLI output was modified, but the code still uses ad-hoc `chalk` formatting instead of the shared CLI output formatting toolkit required by the style guide.
## Issue Context
The compliance checklist requires using the shared formatting utilities (per `scopes/harmony/cli/cli-output-style-guide.md` and `@teambit/cli` output formatter utilities) when changing CLI output, to keep output consistent and maintainable.
## Fix Focus Areas
- scopes/git/ci/ci.main.runtime.ts[1146-1147]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

3. Misleading probe noop text 🐞 Bug ◔ Observability ⭐ New
Description
When probeOnly is set and snapAndExportOntoLane() returns noop, executeExportBranch()
reports that the “sources bundled into the state commit were already snapped”, but the noop result
only proves Bit found nothing pending to snap/export (it can also happen when the bundled
non-.bitmap files are non-component files like docs). This makes the CLI output assert a cause the
implementation cannot actually know from the probe result.
Code

scopes/git/ci/sync/lane-sync-executor.ts[R709-711]

+      if (probeOnly && exported.status === 'noop') {
+        return `${laneName} -> noop (converged; the sources bundled into the state commit were already snapped — nothing to export)`;
+      }
Evidence
stateCommitBundlesSources is raised whenever the state commit touched any path besides .bitmap,
not specifically Bit component sources. The probe result exported.status === 'noop' is derived
from Bit returning NO_CHANGES_TO_SNAP, which only means there were no pending Bit changes to
snap/export; it does not prove non-.bitmap files were “already snapped”.

scopes/git/ci/sync/sync-state.ts[210-223]
scopes/git/ci/sync/sync-state.ts[235-269]
scopes/git/ci/sync/sync-state.ts[71-76]
scopes/git/ci/sync/lane-sync-executor.ts[698-711]
scopes/git/ci/sync/lane-sync-executor.ts[941-958]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The probe-only `export-branch` path returns a `noop` summary that claims the bundled sources were “already snapped”, but the probe’s only signal is `NO_CHANGES_TO_SNAP` (i.e., no *Bit component* changes to snap/export). This message can be wrong/misleading when the bundled non-`.bitmap` files are not Bit-tracked component changes (e.g., docs).

### Issue Context
- `stateCommitBundlesSources` is triggered by *any* file change beyond `.bitmap`.
- `snapAndExportOntoLane()` maps Bit’s `NO_CHANGES_TO_SNAP` to `exported.status === 'noop'`.
- Therefore, the executor should only report what it actually knows: the probe found nothing to snap/export.

### Fix Focus Areas
- scopes/git/ci/sync/lane-sync-executor.ts[709-711]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Stale hasDevCommits comment 🐞 Bug ⚙ Maintainability
Description
The JSDoc for commitTouchesBeyondBitmap() says unreadable results “feed hasDevCommits”, but the
function is only used to compute stateCommitBundlesSources (which then drives the probe-only
export path). This is a low-severity maintainability issue that can mislead future changes around
the probe behavior.
Code

scopes/git/ci/sync/sync-state.ts[R236-239]

+ * Whether `commit` changed any file besides `.bitmap`, against its first parent (`--root` covers an
+ * initial commit; `-m --first-parent` makes a merge commit report the files it brought in, instead of
+ * the silent empty diff plain `diff-tree` gives merges). Unreadable answers `true`: this feeds
+ * `hasDevCommits`, where not knowing must keep the export path open, never declare convergence.
Evidence
The comment explicitly mentions hasDevCommits, but the code uses the function only to compute
stateCommitBundlesSources, which is then consumed by planLaneSync() to produce a probe-only
export-branch action when otherwise converged.

scopes/git/ci/sync/sync-state.ts[210-232]
scopes/git/ci/sync/sync-state.ts[235-260]
scopes/git/ci/sync/sync-planner.ts[75-133]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`commitTouchesBeyondBitmap()`'s JSDoc currently states that unreadable answers feed `hasDevCommits`, but the implementation uses this function only to compute `stateCommitBundlesSources` (suspected work) which then drives probe-only `export-branch` planning.
This mismatch is confusing for maintainers and risks incorrect future refactors around the probe/convergence behavior.
### Issue Context
- `commitTouchesBeyondBitmap()` is invoked only for `stateCommitBundlesSources` computation.
- `hasDevCommits` continues to come exclusively from `parseDevCommitCount(count)`.
### Fix Focus Areas
- scopes/git/ci/sync/sync-state.ts[235-240]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can enable the Remediation agent and Qodo fixes findings in a dedicated fix PR

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Previous review results

Review updated until commit 1348de3 ⚖️ Balanced

Results up to commit e4cf976


🐞 Bugs (1) 📘 Rule violations (1) 📜 Skill insights (0)


Action required
1. Empty diff treated clean ✓ Resolved 🐞 Bug ☼ Reliability
Description
commitTouchesBeyondBitmap() is documented to fail-safe (unreadable ⇒ true), but if git.raw()
resolves with an empty string it returns false via touchesBeyondBitmap(''). Given this module
already notes that simple-git can resolve empty output on non-zero exits, this can incorrectly
clear stateCommitBundlesSources and let a bundled state commit be declared converged without
probing/exporting.
Code

scopes/git/ci/sync/sync-state.ts[R218-221]

+    ]);
+    return touchesBeyondBitmap(names);
+  } catch {
+    return true;
Evidence
The code explicitly states unreadable outputs must keep the export path open, but the implementation
only handles the thrown-error case; an empty resolved string will be treated as “no files besides
.bitmap” because touchesBeyondBitmap('') is false. The same file already documents that
simple-git can resolve empty output on non-zero exits, making this a realistic failure mode.

scopes/git/ci/sync/sync-state.ts[145-153]
scopes/git/ci/sync/sync-state.ts[176-199]
scopes/git/ci/sync/sync-state.ts[201-223]
scopes/git/ci/sync/sync-state.ts[225-232]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`commitTouchesBeyondBitmap()` promises “Unreadable answers `true`”, but currently only treats thrown errors as unreadable. If `git.raw([...diff-tree...])` returns an empty string (which this module already documents as possible for `git.raw` on non-zero exits), the function will incorrectly return `false`.
### Issue Context
This boolean feeds `stateCommitBundlesSources`, which is used to decide whether to probe/export rather than declare convergence.
### Fix Focus Areas
- scopes/git/ci/sync/sync-state.ts[201-223]
### Suggested change
In `commitTouchesBeyondBitmap()`:
- After `git.raw(...)`, add a guard:
- if `!names.trim()` return `true` (unknown/unreadable)
- optionally also if the output doesn’t include `.bitmap` (unexpected for a “state commit”), return `true`
- Consider adding/adjusting a unit test by factoring the guard into a small exported helper (or otherwise making it testable) to cover the empty-string case without relying on real git execution.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended
2. CiMain uses chalk.yellow 📘 Rule violation ⚙ Maintainability
Description
This PR modifies CLI output code but still formats the message with direct chalk.yellow(...)
instead of using the shared @teambit/cli output formatting toolkit/style guide. This can lead to
inconsistent CLI output styling and harder-to-maintain formatting across commands.
Code

scopes/git/ci/ci.main.runtime.ts[R1146-1147]

+        this.logger.console(chalk.yellow(NO_CHANGES_TO_SNAP));
+        return NO_CHANGES_TO_SNAP;
Evidence
PR Compliance ID 1 requires using the shared CLI output formatting toolkit/style guide when
modifying CLI output. The changed lines still apply direct chalk.yellow(...) formatting for the
user-facing message, bypassing the shared formatter utilities.

CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide
scopes/git/ci/ci.main.runtime.ts[1146-1147]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The `bit` CLI output was modified, but the code still uses ad-hoc `chalk` formatting instead of the shared CLI output formatting toolkit required by the style guide.
## Issue Context
The compliance checklist requires using the shared formatting utilities (per `scopes/harmony/cli/cli-output-style-guide.md` and `@teambit/cli` output formatter utilities) when changing CLI output, to keep output consistent and maintainable.
## Fix Focus Areas
- scopes/git/ci/ci.main.runtime.ts[1146-1147]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational
3. Stale hasDevCommits comment 🐞 Bug ⚙ Maintainability ⭐ New
Description
The JSDoc for commitTouchesBeyondBitmap() says unreadable results “feed hasDevCommits”, but the
function is only used to compute stateCommitBundlesSources (which then drives the probe-only
export path). This is a low-severity maintainability issue that can mislead future changes around
the probe behavior.
Code

scopes/git/ci/sync/sync-state.ts[R236-239]

+ * Whether `commit` changed any file besides `.bitmap`, against its first parent (`--root` covers an
+ * initial commit; `-m --first-parent` makes a merge commit report the files it brought in, instead of
+ * the silent empty diff plain `diff-tree` gives merges). Unreadable answers `true`: this feeds
+ * `hasDevCommits`, where not knowing must keep the export path open, never declare convergence.
Evidence
The comment explicitly mentions hasDevCommits, but the code uses the function only to compute
stateCommitBundlesSources, which is then consumed by planLaneSync() to produce a probe-only
export-branch action when otherwise converged.

scopes/git/ci/sync/sync-state.ts[210-232]
scopes/git/ci/sync/sync-state.ts[235-260]
scopes/git/ci/sync/sync-planner.ts[75-133]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`commitTouchesBeyondBitmap()`'s JSDoc currently states that unreadable answers feed `hasDevCommits`, but the implementation uses this function only to compute `stateCommitBundlesSources` (suspected work) which then drives probe-only `export-branch` planning.

This mismatch is confusing for maintainers and risks incorrect future refactors around the probe/convergence behavior.

### Issue Context
- `commitTouchesBeyondBitmap()` is invoked only for `stateCommitBundlesSources` computation.
- `hasDevCommits` continues to come exclusively from `parseDevCommitCount(count)`.

### Fix Focus Areas
- scopes/git/ci/sync/sync-state.ts[235-240]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Results up to commit N/A


🐞 Bugs (0) 📘 Rule violations (1) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)


Action required
1. Empty diff treated clean ✓ Resolved 🐞 Bug ☼ Reliability
Description
commitTouchesBeyondBitmap() is documented to fail-safe (unreadable ⇒ true), but if git.raw()
resolves with an empty string it returns false via touchesBeyondBitmap(''). Given this module
already notes that simple-git can resolve empty output on non-zero exits, this can incorrectly
clear stateCommitBundlesSources and let a bundled state commit be declared converged without
probing/exporting.
Code

scopes/git/ci/sync/sync-state.ts[R218-221]

+    ]);
+    return touchesBeyondBitmap(names);
+  } catch {
+    return true;
Evidence
The code explicitly states unreadable outputs must keep the export path open, but the implementation
only handles the thrown-error case; an empty resolved string will be treated as “no files besides
.bitmap” because touchesBeyondBitmap('') is false. The same file already documents that
simple-git can resolve empty output on non-zero exits, making this a realistic failure mode.

scopes/git/ci/sync/sync-state.ts[145-153]
scopes/git/ci/sync/sync-state.ts[176-199]
scopes/git/ci/sync/sync-state.ts[201-223]
scopes/git/ci/sync/sync-state.ts[225-232]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`commitTouchesBeyondBitmap()` promises “Unreadable answers `true`”, but currently only treats thrown errors as unreadable. If `git.raw([...diff-tree...])` returns an empty string (which this module already documents as possible for `git.raw` on non-zero exits), the function will incorrectly return `false`.
### Issue Context
This boolean feeds `stateCommitBundlesSources`, which is used to decide whether to probe/export rather than declare convergence.
### Fix Focus Areas
- scopes/git/ci/sync/sync-state.ts[201-223]
### Suggested change
In `commitTouchesBeyondBitmap()`:
- After `git.raw(...)`, add a guard:
- if `!names.trim()` return `true` (unknown/unreadable)
- optionally also if the output doesn’t include `.bitmap` (unexpected for a “state commit”), return `true`
- Consider adding/adjusting a unit test by factoring the guard into a small exported helper (or otherwise making it testable) to cover the empty-string case without relying on real git execution.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended
2. CiMain uses chalk.yellow 📘 Rule violation ⚙ Maintainability
Description
This PR modifies CLI output code but still formats the message with direct chalk.yellow(...)
instead of using the shared @teambit/cli output formatting toolkit/style guide. This can lead to
inconsistent CLI output styling and harder-to-maintain formatting across commands.
Code

scopes/git/ci/ci.main.runtime.ts[R1146-1147]

+        this.logger.console(chalk.yellow(NO_CHANGES_TO_SNAP));
+        return NO_CHANGES_TO_SNAP;
Evidence
PR Compliance ID 1 requires using the shared CLI output formatting toolkit/style guide when
modifying CLI output. The changed lines still apply direct chalk.yellow(...) formatting for the
user-facing message, bypassing the shared formatter utilities.

CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide: CLAUDE.md: Use shared CLI output formatting toolkit and follow CLI output style guide
scopes/git/ci/ci.main.runtime.ts[1146-1147]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The `bit` CLI output was modified, but the code still uses ad-hoc `chalk` formatting instead of the shared CLI output formatting toolkit required by the style guide.
## Issue Context
The compliance checklist requires using the shared formatting utilities (per `scopes/harmony/cli/cli-output-style-guide.md` and `@teambit/cli` output formatter utilities) when changing CLI output, to keep output consistent and maintainable.
## Fix Focus Areas
- scopes/git/ci/ci.main.runtime.ts[1146-1147]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Qodo Logo

Comment thread scopes/git/ci/ci.main.runtime.ts
Comment thread scopes/git/ci/sync/sync-state.ts
…n diff

a state commit changed .bitmap by definition, so its first-parent
diff is never legitimately empty — empty output is simple-git
resolving on a non-zero exit, and must fail toward probing
…robe

first contact with bundled sources routes to adopt-branch (adoption
already probes via bit status); the different-lane guard's gate widens
to suspected work so a bundles-only branch cannot be adopted over
another lane's live claim; deletion cascade keeps master's shape with
mayCarryWork feeding the unmerged-work check
@luvkapur
luvkapur marked this pull request as ready for review August 12, 2026 17:55
Comment thread scopes/git/ci/sync/sync-state.ts Outdated
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit e4cf976

…ds stateCommitBundlesSources, not hasDevCommits
Comment on lines +709 to +711
if (probeOnly && exported.status === 'noop') {
return `${laneName} -> noop (converged; the sources bundled into the state commit were already snapped — nothing to export)`;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Informational

1. Misleading probe noop text 🐞 Bug ◔ Observability

When probeOnly is set and snapAndExportOntoLane() returns noop, executeExportBranch()
reports that the “sources bundled into the state commit were already snapped”, but the noop result
only proves Bit found nothing pending to snap/export (it can also happen when the bundled
non-.bitmap files are non-component files like docs). This makes the CLI output assert a cause the
implementation cannot actually know from the probe result.
Agent Prompt
### Issue description
The probe-only `export-branch` path returns a `noop` summary that claims the bundled sources were “already snapped”, but the probe’s only signal is `NO_CHANGES_TO_SNAP` (i.e., no *Bit component* changes to snap/export). This message can be wrong/misleading when the bundled non-`.bitmap` files are not Bit-tracked component changes (e.g., docs).

### Issue Context
- `stateCommitBundlesSources` is triggered by *any* file change beyond `.bitmap`.
- `snapAndExportOntoLane()` maps Bit’s `NO_CHANGES_TO_SNAP` to `exported.status === 'noop'`.
- Therefore, the executor should only report what it actually knows: the probe found nothing to snap/export.

### Fix Focus Areas
- scopes/git/ci/sync/lane-sync-executor.ts[709-711]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 1348de3

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.

1 participant