@teambit/ci 1.0.523 → 1.0.524

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/ci.docs.mdx CHANGED
@@ -377,29 +377,42 @@ Set a `branchPrefix`, or an explicit `lanes` list, to leave developer branches o
377
377
  ### Cross-scope lanes
378
378
 
379
379
  The hosting scope of a lane holds the lane object. The scopes of the components of the lane show which
380
- repositories the change touches. The two are different. This version supports one shape only: the content of
381
- the lane must live in one scope, the `defaultScope` of this workspace. The hosting scope can be another
382
- scope, and a scope-qualified id addresses such a lane.
380
+ repositories the change touches. The two are different. The hosting scope can be any scope — a
381
+ scope-qualified id addresses a lane hosted elsewhere:
383
382
 
384
383
  ```bash
385
384
  bit ci sync my-lane # hosted on this workspace's defaultScope
386
- bit ci sync other-org.other-scope/my-lane # hosted elsewhere, content still in this repo's scope
385
+ bit ci sync other-org.other-scope/my-lane # hosted elsewhere
387
386
  ```
388
387
 
389
388
  The branch mapping always uses the lane **name**, so a lane that another scope hosts maps to the same branch
390
389
  as before. Every request to Bit uses the full `hostScope/name` id.
391
390
 
392
- **The command never mirrors a lane that has components outside `defaultScope`.** Such a mirror writes the
393
- components of another repository into this repository, and exports them from here. A lane like this is still
394
- legitimate, so only the report depends on how the command reached the lane:
391
+ **The command mirrors a lane's `defaultScope` slice.** A lane may carry components from several scopes; this
392
+ repository sources only one of them. The mirror materializes, fingerprints, snaps and exports the components
393
+ of `defaultScope` alone. The lane's other components are never written into this repository — the branch
394
+ consumes them as package dependencies at their lane versions, and their sources live in their own scopes'
395
+ repositories (each of which can mirror the same lane's own slice). The pull request lists the mirrored slice
396
+ and, separately, the foreign components the lane also carries.
395
397
 
396
- | How the command reached the lane | Outcome | Exit | Pull request touched |
397
- | -------------------------------------------------------------------- | --------- | ---- | ---------------------------- |
398
- | **Enumerated** — an `--all` run, or a push or webhook `--branch` run | `skipped` | 0 | no |
399
- | **Named** — `bit ci sync my-lane` | refusal | ≠ 0 | no |
400
- | **The lane became cross-scope during a live mirror** | `HALTED` | ≠ 0 | yes — labelled and commented |
398
+ Two consequences of slice-based reconciliation are worth knowing:
401
399
 
402
- The skip keeps the exit code at 0 on purpose: a permanent cross-scope lane must not fail every scheduled run.
400
+ - A change that touches **only** foreign components does not move this repository's mirror: the branch keeps
401
+ building against the foreign versions recorded when its own components last changed, and catches up on the
402
+ next change to an own-scope component.
403
+ - An exported change from this repository updates only the own-scope components on the lane; the foreign
404
+ entries keep their heads.
405
+
406
+ A lane with **no** `defaultScope` components at all has nothing to mirror here. Only the report depends on
407
+ how the command reached such a lane:
408
+
409
+ | How the command reached the lane | Outcome | Exit | Pull request touched |
410
+ | --------------------------------------------------------------------- | --------- | ---- | ---------------------------- |
411
+ | **Enumerated** — an `--all` run, or a push or webhook `--branch` run | `skipped` | 0 | no |
412
+ | **Named** — `bit ci sync my-lane` | refusal | ≠ 0 | no |
413
+ | **Every `defaultScope` component left the lane during a live mirror** | `HALTED` | ≠ 0 | yes — labelled and commented |
414
+
415
+ The skip keeps the exit code at 0 on purpose: a standing foreign lane must not fail every scheduled run.
403
416
  A named lane exits non-zero, so the user learns why nothing happened. That exit is a plain refusal, because
404
417
  the command writes nothing and labels no pull request. The command refuses two lanes that map to one branch
405
418
  in the same way, and neither lane takes the branch. The command never lane-maps the default branch or
@@ -11,11 +11,11 @@ export class CiSyncCmd implements Command {
11
11
  {
12
12
  name: 'lane',
13
13
  description:
14
- 'the lane to reconcile: either a lane name (hosted on the workspace\'s defaultScope) or a scope-qualified lane id, "other-org.other-scope/my-lane", for a lane hosted on another scope. Its components must still all live in this workspace\'s defaultScope',
14
+ 'the lane to reconcile: either a lane name (hosted on the workspace\'s defaultScope) or a scope-qualified lane id, "other-org.other-scope/my-lane", for a lane hosted on another scope. Only the lane\'s defaultScope components are mirrored; components from other scopes stay on the lane as package dependencies',
15
15
  },
16
16
  ];
17
17
  description = 'Reconciles Bit lanes and the main scope with git branches and pull requests.';
18
- extendedDescription = `Stateless reconciler: compares each mapped lane's remote head against the state the branch itself records, and converges — importing lane changes onto the branch, exporting dev commits to the lane, or opening/closing PRs. That state comes from bit's own data: the .bitmap committed on the branch, which records the lane the branch mirrors and the exact version of every component on it. The "chore(bit-sync)" subject, "Bit-Lane-Head" trailer and "[bit-sync]" marker on sync commits are annotations for humans and triggers; the only decision that consults one is branch deletion, which additionally requires the marker to prove the reconciler wrote the branch tip. The main scope is reconciled by checking the workspace out to its latest exported versions and proposing the result as a sync PR. Triggers (webhook, push, cron) only decide when it runs, never what it does. Safe to re-run at any time; converged state is a no-op. A lane whose components are not all in this workspace's defaultScope is a cross-scope lane: no branch is created for it (it would leak another repository's components into this one) — enumerated runs skip it and stay green, an explicitly named one is refused, and a lane that became cross-scope after its branch existed is halted for a human. Configure mapping under \`"teambit.git/ci": { "sync": { ... } }\` in workspace.jsonc.`;
18
+ extendedDescription = `Stateless reconciler: compares each mapped lane's remote head against the state the branch itself records, and converges — importing lane changes onto the branch, exporting dev commits to the lane, or opening/closing PRs. That state comes from bit's own data: the .bitmap committed on the branch, which records the lane the branch mirrors and the exact version of every component on it. The "chore(bit-sync)" subject, "Bit-Lane-Head" trailer and "[bit-sync]" marker on sync commits are annotations for humans and triggers; the only decision that consults one is branch deletion, which additionally requires the marker to prove the reconciler wrote the branch tip. The main scope is reconciled by checking the workspace out to its latest exported versions and proposing the result as a sync PR. Triggers (webhook, push, cron) only decide when it runs, never what it does. Safe to re-run at any time; converged state is a no-op. A lane carrying components from other scopes (a cross-scope lane) is reconciled over its defaultScope slice only: foreign components are never written into this repository — the branch consumes them as package dependencies at their lane versions, and only their own scopes' repositories can mirror their sources. A lane with no defaultScope components has nothing to mirror here: enumerated runs skip it and stay green, an explicitly named one is refused, and a mirrored lane whose defaultScope components all left it is halted for a human. Configure mapping under \`"teambit.git/ci": { "sync": { ... } }\` in workspace.jsonc.`;
19
19
  group = 'collaborate';
20
20
 
21
21
  /**
package/dist/ci.docs.mdx CHANGED
@@ -377,29 +377,42 @@ Set a `branchPrefix`, or an explicit `lanes` list, to leave developer branches o
377
377
  ### Cross-scope lanes
378
378
 
379
379
  The hosting scope of a lane holds the lane object. The scopes of the components of the lane show which
380
- repositories the change touches. The two are different. This version supports one shape only: the content of
381
- the lane must live in one scope, the `defaultScope` of this workspace. The hosting scope can be another
382
- scope, and a scope-qualified id addresses such a lane.
380
+ repositories the change touches. The two are different. The hosting scope can be any scope — a
381
+ scope-qualified id addresses a lane hosted elsewhere:
383
382
 
384
383
  ```bash
385
384
  bit ci sync my-lane # hosted on this workspace's defaultScope
386
- bit ci sync other-org.other-scope/my-lane # hosted elsewhere, content still in this repo's scope
385
+ bit ci sync other-org.other-scope/my-lane # hosted elsewhere
387
386
  ```
388
387
 
389
388
  The branch mapping always uses the lane **name**, so a lane that another scope hosts maps to the same branch
390
389
  as before. Every request to Bit uses the full `hostScope/name` id.
391
390
 
392
- **The command never mirrors a lane that has components outside `defaultScope`.** Such a mirror writes the
393
- components of another repository into this repository, and exports them from here. A lane like this is still
394
- legitimate, so only the report depends on how the command reached the lane:
391
+ **The command mirrors a lane's `defaultScope` slice.** A lane may carry components from several scopes; this
392
+ repository sources only one of them. The mirror materializes, fingerprints, snaps and exports the components
393
+ of `defaultScope` alone. The lane's other components are never written into this repository — the branch
394
+ consumes them as package dependencies at their lane versions, and their sources live in their own scopes'
395
+ repositories (each of which can mirror the same lane's own slice). The pull request lists the mirrored slice
396
+ and, separately, the foreign components the lane also carries.
395
397
 
396
- | How the command reached the lane | Outcome | Exit | Pull request touched |
397
- | -------------------------------------------------------------------- | --------- | ---- | ---------------------------- |
398
- | **Enumerated** — an `--all` run, or a push or webhook `--branch` run | `skipped` | 0 | no |
399
- | **Named** — `bit ci sync my-lane` | refusal | ≠ 0 | no |
400
- | **The lane became cross-scope during a live mirror** | `HALTED` | ≠ 0 | yes — labelled and commented |
398
+ Two consequences of slice-based reconciliation are worth knowing:
401
399
 
402
- The skip keeps the exit code at 0 on purpose: a permanent cross-scope lane must not fail every scheduled run.
400
+ - A change that touches **only** foreign components does not move this repository's mirror: the branch keeps
401
+ building against the foreign versions recorded when its own components last changed, and catches up on the
402
+ next change to an own-scope component.
403
+ - An exported change from this repository updates only the own-scope components on the lane; the foreign
404
+ entries keep their heads.
405
+
406
+ A lane with **no** `defaultScope` components at all has nothing to mirror here. Only the report depends on
407
+ how the command reached such a lane:
408
+
409
+ | How the command reached the lane | Outcome | Exit | Pull request touched |
410
+ | --------------------------------------------------------------------- | --------- | ---- | ---------------------------- |
411
+ | **Enumerated** — an `--all` run, or a push or webhook `--branch` run | `skipped` | 0 | no |
412
+ | **Named** — `bit ci sync my-lane` | refusal | ≠ 0 | no |
413
+ | **Every `defaultScope` component left the lane during a live mirror** | `HALTED` | ≠ 0 | yes — labelled and commented |
414
+
415
+ The skip keeps the exit code at 0 on purpose: a standing foreign lane must not fail every scheduled run.
403
416
  A named lane exits non-zero, so the user learns why nothing happened. That exit is a plain refusal, because
404
417
  the command writes nothing and labels no pull request. The command refuses two lanes that map to one branch
405
418
  in the same way, and neither lane takes the branch. The command never lane-maps the default branch or
@@ -22,10 +22,10 @@ class CiSyncCmd {
22
22
  _defineProperty(this, "name", 'sync [lane]');
23
23
  _defineProperty(this, "arguments", [{
24
24
  name: 'lane',
25
- description: 'the lane to reconcile: either a lane name (hosted on the workspace\'s defaultScope) or a scope-qualified lane id, "other-org.other-scope/my-lane", for a lane hosted on another scope. Its components must still all live in this workspace\'s defaultScope'
25
+ description: 'the lane to reconcile: either a lane name (hosted on the workspace\'s defaultScope) or a scope-qualified lane id, "other-org.other-scope/my-lane", for a lane hosted on another scope. Only the lane\'s defaultScope components are mirrored; components from other scopes stay on the lane as package dependencies'
26
26
  }]);
27
27
  _defineProperty(this, "description", 'Reconciles Bit lanes and the main scope with git branches and pull requests.');
28
- _defineProperty(this, "extendedDescription", `Stateless reconciler: compares each mapped lane's remote head against the state the branch itself records, and converges — importing lane changes onto the branch, exporting dev commits to the lane, or opening/closing PRs. That state comes from bit's own data: the .bitmap committed on the branch, which records the lane the branch mirrors and the exact version of every component on it. The "chore(bit-sync)" subject, "Bit-Lane-Head" trailer and "[bit-sync]" marker on sync commits are annotations for humans and triggers; the only decision that consults one is branch deletion, which additionally requires the marker to prove the reconciler wrote the branch tip. The main scope is reconciled by checking the workspace out to its latest exported versions and proposing the result as a sync PR. Triggers (webhook, push, cron) only decide when it runs, never what it does. Safe to re-run at any time; converged state is a no-op. A lane whose components are not all in this workspace's defaultScope is a cross-scope lane: no branch is created for it (it would leak another repository's components into this one) — enumerated runs skip it and stay green, an explicitly named one is refused, and a lane that became cross-scope after its branch existed is halted for a human. Configure mapping under \`"teambit.git/ci": { "sync": { ... } }\` in workspace.jsonc.`);
28
+ _defineProperty(this, "extendedDescription", `Stateless reconciler: compares each mapped lane's remote head against the state the branch itself records, and converges — importing lane changes onto the branch, exporting dev commits to the lane, or opening/closing PRs. That state comes from bit's own data: the .bitmap committed on the branch, which records the lane the branch mirrors and the exact version of every component on it. The "chore(bit-sync)" subject, "Bit-Lane-Head" trailer and "[bit-sync]" marker on sync commits are annotations for humans and triggers; the only decision that consults one is branch deletion, which additionally requires the marker to prove the reconciler wrote the branch tip. The main scope is reconciled by checking the workspace out to its latest exported versions and proposing the result as a sync PR. Triggers (webhook, push, cron) only decide when it runs, never what it does. Safe to re-run at any time; converged state is a no-op. A lane carrying components from other scopes (a cross-scope lane) is reconciled over its defaultScope slice only: foreign components are never written into this repository — the branch consumes them as package dependencies at their lane versions, and only their own scopes' repositories can mirror their sources. A lane with no defaultScope components has nothing to mirror here: enumerated runs skip it and stay green, an explicitly named one is refused, and a mirrored lane whose defaultScope components all left it is halted for a human. Configure mapping under \`"teambit.git/ci": { "sync": { ... } }\` in workspace.jsonc.`);
29
29
  _defineProperty(this, "group", 'collaborate');
30
30
  /**
31
31
  * Changing any user-facing text on this command requires regenerating the CLI reference (the
@@ -1 +1 @@
1
- {"version":3,"names":["_workspace","data","require","_defineProperty","e","r","t","_toPropertyKey","Object","defineProperty","value","enumerable","configurable","writable","i","_toPrimitive","Symbol","toPrimitive","call","TypeError","String","Number","CiSyncCmd","constructor","workspace","logger","ci","name","description","report","lane","options","console","OutsideWorkspaceError","sync","undefined","branch","all","main","dryRun","init","exports"],"sources":["sync.cmd.ts"],"sourcesContent":["import type { Command, CommandOptions } from '@teambit/cli';\nimport type { Logger } from '@teambit/logger';\nimport { OutsideWorkspaceError, type Workspace } from '@teambit/workspace';\nimport type { CiMain } from '../ci.main.runtime';\n\ntype Options = { branch?: string; all?: boolean; main?: boolean; dryRun?: boolean; init?: boolean };\n\nexport class CiSyncCmd implements Command {\n name = 'sync [lane]';\n arguments = [\n {\n name: 'lane',\n description:\n 'the lane to reconcile: either a lane name (hosted on the workspace\\'s defaultScope) or a scope-qualified lane id, \"other-org.other-scope/my-lane\", for a lane hosted on another scope. Its components must still all live in this workspace\\'s defaultScope',\n },\n ];\n description = 'Reconciles Bit lanes and the main scope with git branches and pull requests.';\n extendedDescription = `Stateless reconciler: compares each mapped lane's remote head against the state the branch itself records, and converges — importing lane changes onto the branch, exporting dev commits to the lane, or opening/closing PRs. That state comes from bit's own data: the .bitmap committed on the branch, which records the lane the branch mirrors and the exact version of every component on it. The \"chore(bit-sync)\" subject, \"Bit-Lane-Head\" trailer and \"[bit-sync]\" marker on sync commits are annotations for humans and triggers; the only decision that consults one is branch deletion, which additionally requires the marker to prove the reconciler wrote the branch tip. The main scope is reconciled by checking the workspace out to its latest exported versions and proposing the result as a sync PR. Triggers (webhook, push, cron) only decide when it runs, never what it does. Safe to re-run at any time; converged state is a no-op. A lane whose components are not all in this workspace's defaultScope is a cross-scope lane: no branch is created for it (it would leak another repository's components into this one) — enumerated runs skip it and stay green, an explicitly named one is refused, and a lane that became cross-scope after its branch existed is halted for a human. Configure mapping under \\`\"teambit.git/ci\": { \"sync\": { ... } }\\` in workspace.jsonc.`;\n group = 'collaborate';\n\n /**\n * Changing any user-facing text on this command requires regenerating the CLI reference (the\n * `generate-cli-reference*` npm scripts), or the `check_generated_reference` CI job fails. The\n * scripts invoke whatever `bit` is on PATH — put `<repo>/bin/bit.js` first — and a local workspace\n * may register extra commands, so inspect the generated diff and keep only the text that changed.\n */\n options: CommandOptions = [\n [\n '',\n 'branch <branch>',\n 'Resolve the lane from this git branch name using the sync mapping config (cannot be combined with --all)',\n ],\n [\n '',\n 'all',\n 'Reconcile every mapped lane plus the main scope (the default when no target is given; cannot be combined with a lane argument, --branch or --main)',\n ],\n [\n '',\n 'main',\n 'Reconcile only the main scope against the default branch, opening a sync PR on drift (conflicts resolve in favour of the scope)',\n ],\n [\n '',\n 'dry-run',\n 'Print the planned action per target. Nothing is pushed and no pull request is created or modified; the working tree is still written and then restored, so a dirty tree is refused rather than discarded',\n ],\n [\n '',\n 'init',\n 'One-command onboarding: scaffold .github/workflows/bit-sync.yml + bit-release.yml (with this repository\\'s actual default branch substituted), add the `\"teambit.git/ci\": { \"sync\": {} }` config block to workspace.jsonc if absent, and print the remaining manual-steps checklist (secrets + bit.cloud webhook). Writes nothing else and never overwrites an existing workflow file. Cannot be combined with a lane argument or any other flag.',\n ],\n ];\n\n constructor(\n private workspace: Workspace,\n private logger: Logger,\n private ci: CiMain\n ) {}\n\n // `[lane]: [string]` rather than `[string?]`: `CLIArgs` does not admit an undefined-carrying tuple.\n // The argument is genuinely optional at runtime, hence the `|| undefined` below.\n async report([lane]: [string], options: Options) {\n this.logger.console('\\n\\n');\n this.logger.console('Initializing sync command');\n if (!this.workspace) throw new OutsideWorkspaceError();\n\n return this.ci.sync({\n lane: lane || undefined,\n branch: options.branch,\n all: options.all,\n main: options.main,\n dryRun: options.dryRun,\n init: options.init,\n });\n }\n}\n"],"mappings":";;;;;;AAEA,SAAAA,WAAA;EAAA,MAAAC,IAAA,GAAAC,OAAA;EAAAF,UAAA,YAAAA,CAAA;IAAA,OAAAC,IAAA;EAAA;EAAA,OAAAA,IAAA;AAAA;AAA2E,SAAAE,gBAAAC,CAAA,EAAAC,CAAA,EAAAC,CAAA,YAAAD,CAAA,GAAAE,cAAA,CAAAF,CAAA,MAAAD,CAAA,GAAAI,MAAA,CAAAC,cAAA,CAAAL,CAAA,EAAAC,CAAA,IAAAK,KAAA,EAAAJ,CAAA,EAAAK,UAAA,MAAAC,YAAA,MAAAC,QAAA,UAAAT,CAAA,CAAAC,CAAA,IAAAC,CAAA,EAAAF,CAAA;AAAA,SAAAG,eAAAD,CAAA,QAAAQ,CAAA,GAAAC,YAAA,CAAAT,CAAA,uCAAAQ,CAAA,GAAAA,CAAA,GAAAA,CAAA;AAAA,SAAAC,aAAAT,CAAA,EAAAD,CAAA,2BAAAC,CAAA,KAAAA,CAAA,SAAAA,CAAA,MAAAF,CAAA,GAAAE,CAAA,CAAAU,MAAA,CAAAC,WAAA,kBAAAb,CAAA,QAAAU,CAAA,GAAAV,CAAA,CAAAc,IAAA,CAAAZ,CAAA,EAAAD,CAAA,uCAAAS,CAAA,SAAAA,CAAA,YAAAK,SAAA,yEAAAd,CAAA,GAAAe,MAAA,GAAAC,MAAA,EAAAf,CAAA;AAKpE,MAAMgB,SAAS,CAAoB;EA+CxCC,WAAWA,CACDC,SAAoB,EACpBC,MAAc,EACdC,EAAU,EAClB;IAAA,KAHQF,SAAoB,GAApBA,SAAoB;IAAA,KACpBC,MAAc,GAAdA,MAAc;IAAA,KACdC,EAAU,GAAVA,EAAU;IAAAvB,eAAA,eAjDb,aAAa;IAAAA,eAAA,oBACR,CACV;MACEwB,IAAI,EAAE,MAAM;MACZC,WAAW,EACT;IACJ,CAAC,CACF;IAAAzB,eAAA,sBACa,8EAA8E;IAAAA,eAAA,8BACtE,60CAA60C;IAAAA,eAAA,gBAC31C,aAAa;IAErB;AACF;AACA;AACA;AACA;AACA;IALEA,eAAA,kBAM0B,CACxB,CACE,EAAE,EACF,iBAAiB,EACjB,0GAA0G,CAC3G,EACD,CACE,EAAE,EACF,KAAK,EACL,oJAAoJ,CACrJ,EACD,CACE,EAAE,EACF,MAAM,EACN,iIAAiI,CAClI,EACD,CACE,EAAE,EACF,SAAS,EACT,0MAA0M,CAC3M,EACD,CACE,EAAE,EACF,MAAM,EACN,mbAAmb,CACpb,CACF;EAME;;EAEH;EACA;EACA,MAAM0B,MAAMA,CAAC,CAACC,IAAI,CAAW,EAAEC,OAAgB,EAAE;IAC/C,IAAI,CAACN,MAAM,CAACO,OAAO,CAAC,MAAM,CAAC;IAC3B,IAAI,CAACP,MAAM,CAACO,OAAO,CAAC,2BAA2B,CAAC;IAChD,IAAI,CAAC,IAAI,CAACR,SAAS,EAAE,MAAM,KAAIS,kCAAqB,EAAC,CAAC;IAEtD,OAAO,IAAI,CAACP,EAAE,CAACQ,IAAI,CAAC;MAClBJ,IAAI,EAAEA,IAAI,IAAIK,SAAS;MACvBC,MAAM,EAAEL,OAAO,CAACK,MAAM;MACtBC,GAAG,EAAEN,OAAO,CAACM,GAAG;MAChBC,IAAI,EAAEP,OAAO,CAACO,IAAI;MAClBC,MAAM,EAAER,OAAO,CAACQ,MAAM;MACtBC,IAAI,EAAET,OAAO,CAACS;IAChB,CAAC,CAAC;EACJ;AACF;AAACC,OAAA,CAAAnB,SAAA,GAAAA,SAAA","ignoreList":[]}
1
+ {"version":3,"names":["_workspace","data","require","_defineProperty","e","r","t","_toPropertyKey","Object","defineProperty","value","enumerable","configurable","writable","i","_toPrimitive","Symbol","toPrimitive","call","TypeError","String","Number","CiSyncCmd","constructor","workspace","logger","ci","name","description","report","lane","options","console","OutsideWorkspaceError","sync","undefined","branch","all","main","dryRun","init","exports"],"sources":["sync.cmd.ts"],"sourcesContent":["import type { Command, CommandOptions } from '@teambit/cli';\nimport type { Logger } from '@teambit/logger';\nimport { OutsideWorkspaceError, type Workspace } from '@teambit/workspace';\nimport type { CiMain } from '../ci.main.runtime';\n\ntype Options = { branch?: string; all?: boolean; main?: boolean; dryRun?: boolean; init?: boolean };\n\nexport class CiSyncCmd implements Command {\n name = 'sync [lane]';\n arguments = [\n {\n name: 'lane',\n description:\n 'the lane to reconcile: either a lane name (hosted on the workspace\\'s defaultScope) or a scope-qualified lane id, \"other-org.other-scope/my-lane\", for a lane hosted on another scope. Only the lane\\'s defaultScope components are mirrored; components from other scopes stay on the lane as package dependencies',\n },\n ];\n description = 'Reconciles Bit lanes and the main scope with git branches and pull requests.';\n extendedDescription = `Stateless reconciler: compares each mapped lane's remote head against the state the branch itself records, and converges — importing lane changes onto the branch, exporting dev commits to the lane, or opening/closing PRs. That state comes from bit's own data: the .bitmap committed on the branch, which records the lane the branch mirrors and the exact version of every component on it. The \"chore(bit-sync)\" subject, \"Bit-Lane-Head\" trailer and \"[bit-sync]\" marker on sync commits are annotations for humans and triggers; the only decision that consults one is branch deletion, which additionally requires the marker to prove the reconciler wrote the branch tip. The main scope is reconciled by checking the workspace out to its latest exported versions and proposing the result as a sync PR. Triggers (webhook, push, cron) only decide when it runs, never what it does. Safe to re-run at any time; converged state is a no-op. A lane carrying components from other scopes (a cross-scope lane) is reconciled over its defaultScope slice only: foreign components are never written into this repository — the branch consumes them as package dependencies at their lane versions, and only their own scopes' repositories can mirror their sources. A lane with no defaultScope components has nothing to mirror here: enumerated runs skip it and stay green, an explicitly named one is refused, and a mirrored lane whose defaultScope components all left it is halted for a human. Configure mapping under \\`\"teambit.git/ci\": { \"sync\": { ... } }\\` in workspace.jsonc.`;\n group = 'collaborate';\n\n /**\n * Changing any user-facing text on this command requires regenerating the CLI reference (the\n * `generate-cli-reference*` npm scripts), or the `check_generated_reference` CI job fails. The\n * scripts invoke whatever `bit` is on PATH — put `<repo>/bin/bit.js` first — and a local workspace\n * may register extra commands, so inspect the generated diff and keep only the text that changed.\n */\n options: CommandOptions = [\n [\n '',\n 'branch <branch>',\n 'Resolve the lane from this git branch name using the sync mapping config (cannot be combined with --all)',\n ],\n [\n '',\n 'all',\n 'Reconcile every mapped lane plus the main scope (the default when no target is given; cannot be combined with a lane argument, --branch or --main)',\n ],\n [\n '',\n 'main',\n 'Reconcile only the main scope against the default branch, opening a sync PR on drift (conflicts resolve in favour of the scope)',\n ],\n [\n '',\n 'dry-run',\n 'Print the planned action per target. Nothing is pushed and no pull request is created or modified; the working tree is still written and then restored, so a dirty tree is refused rather than discarded',\n ],\n [\n '',\n 'init',\n 'One-command onboarding: scaffold .github/workflows/bit-sync.yml + bit-release.yml (with this repository\\'s actual default branch substituted), add the `\"teambit.git/ci\": { \"sync\": {} }` config block to workspace.jsonc if absent, and print the remaining manual-steps checklist (secrets + bit.cloud webhook). Writes nothing else and never overwrites an existing workflow file. Cannot be combined with a lane argument or any other flag.',\n ],\n ];\n\n constructor(\n private workspace: Workspace,\n private logger: Logger,\n private ci: CiMain\n ) {}\n\n // `[lane]: [string]` rather than `[string?]`: `CLIArgs` does not admit an undefined-carrying tuple.\n // The argument is genuinely optional at runtime, hence the `|| undefined` below.\n async report([lane]: [string], options: Options) {\n this.logger.console('\\n\\n');\n this.logger.console('Initializing sync command');\n if (!this.workspace) throw new OutsideWorkspaceError();\n\n return this.ci.sync({\n lane: lane || undefined,\n branch: options.branch,\n all: options.all,\n main: options.main,\n dryRun: options.dryRun,\n init: options.init,\n });\n }\n}\n"],"mappings":";;;;;;AAEA,SAAAA,WAAA;EAAA,MAAAC,IAAA,GAAAC,OAAA;EAAAF,UAAA,YAAAA,CAAA;IAAA,OAAAC,IAAA;EAAA;EAAA,OAAAA,IAAA;AAAA;AAA2E,SAAAE,gBAAAC,CAAA,EAAAC,CAAA,EAAAC,CAAA,YAAAD,CAAA,GAAAE,cAAA,CAAAF,CAAA,MAAAD,CAAA,GAAAI,MAAA,CAAAC,cAAA,CAAAL,CAAA,EAAAC,CAAA,IAAAK,KAAA,EAAAJ,CAAA,EAAAK,UAAA,MAAAC,YAAA,MAAAC,QAAA,UAAAT,CAAA,CAAAC,CAAA,IAAAC,CAAA,EAAAF,CAAA;AAAA,SAAAG,eAAAD,CAAA,QAAAQ,CAAA,GAAAC,YAAA,CAAAT,CAAA,uCAAAQ,CAAA,GAAAA,CAAA,GAAAA,CAAA;AAAA,SAAAC,aAAAT,CAAA,EAAAD,CAAA,2BAAAC,CAAA,KAAAA,CAAA,SAAAA,CAAA,MAAAF,CAAA,GAAAE,CAAA,CAAAU,MAAA,CAAAC,WAAA,kBAAAb,CAAA,QAAAU,CAAA,GAAAV,CAAA,CAAAc,IAAA,CAAAZ,CAAA,EAAAD,CAAA,uCAAAS,CAAA,SAAAA,CAAA,YAAAK,SAAA,yEAAAd,CAAA,GAAAe,MAAA,GAAAC,MAAA,EAAAf,CAAA;AAKpE,MAAMgB,SAAS,CAAoB;EA+CxCC,WAAWA,CACDC,SAAoB,EACpBC,MAAc,EACdC,EAAU,EAClB;IAAA,KAHQF,SAAoB,GAApBA,SAAoB;IAAA,KACpBC,MAAc,GAAdA,MAAc;IAAA,KACdC,EAAU,GAAVA,EAAU;IAAAvB,eAAA,eAjDb,aAAa;IAAAA,eAAA,oBACR,CACV;MACEwB,IAAI,EAAE,MAAM;MACZC,WAAW,EACT;IACJ,CAAC,CACF;IAAAzB,eAAA,sBACa,8EAA8E;IAAAA,eAAA,8BACtE,mhDAAmhD;IAAAA,eAAA,gBACjiD,aAAa;IAErB;AACF;AACA;AACA;AACA;AACA;IALEA,eAAA,kBAM0B,CACxB,CACE,EAAE,EACF,iBAAiB,EACjB,0GAA0G,CAC3G,EACD,CACE,EAAE,EACF,KAAK,EACL,oJAAoJ,CACrJ,EACD,CACE,EAAE,EACF,MAAM,EACN,iIAAiI,CAClI,EACD,CACE,EAAE,EACF,SAAS,EACT,0MAA0M,CAC3M,EACD,CACE,EAAE,EACF,MAAM,EACN,mbAAmb,CACpb,CACF;EAME;;EAEH;EACA;EACA,MAAM0B,MAAMA,CAAC,CAACC,IAAI,CAAW,EAAEC,OAAgB,EAAE;IAC/C,IAAI,CAACN,MAAM,CAACO,OAAO,CAAC,MAAM,CAAC;IAC3B,IAAI,CAACP,MAAM,CAACO,OAAO,CAAC,2BAA2B,CAAC;IAChD,IAAI,CAAC,IAAI,CAACR,SAAS,EAAE,MAAM,KAAIS,kCAAqB,EAAC,CAAC;IAEtD,OAAO,IAAI,CAACP,EAAE,CAACQ,IAAI,CAAC;MAClBJ,IAAI,EAAEA,IAAI,IAAIK,SAAS;MACvBC,MAAM,EAAEL,OAAO,CAACK,MAAM;MACtBC,GAAG,EAAEN,OAAO,CAACM,GAAG;MAChBC,IAAI,EAAEP,OAAO,CAACO,IAAI;MAClBC,MAAM,EAAER,OAAO,CAACQ,MAAM;MACtBC,IAAI,EAAET,OAAO,CAACS;IAChB,CAAC,CAAC;EACJ;AACF;AAACC,OAAA,CAAAnB,SAAA,GAAAA,SAAA","ignoreList":[]}
@@ -1,5 +1,5 @@
1
1
  ;
2
- import * as overview_0 from '/home/circleci/Library/Caches/Bit/capsules/8891be5ad/teambit.git_ci@1.0.523/dist/ci.docs.mdx';
2
+ import * as overview_0 from '/home/circleci/Library/Caches/Bit/capsules/8891be5ad/teambit.git_ci@1.0.524/dist/ci.docs.mdx';
3
3
 
4
4
  export const compositions = [];
5
5
  export const overview = [overview_0];
@@ -0,0 +1,36 @@
1
+ import type { Logger } from '@teambit/logger';
2
+ import type { Workspace } from '@teambit/workspace';
3
+ /**
4
+ * A `.bitmap` entry whose recorded `mainFile` is no longer on disk cannot be loaded at all — the
5
+ * component loader throws `MainFileRemoved`, and that one entry fails the whole `bit checkout head`
6
+ * the main-scope reconciler runs, so every scheduled run stays red until a human intervenes.
7
+ *
8
+ * The shape is legitimate: a new version moves the component's main file (e.g. an env stops emitting
9
+ * `dist/`, so main goes from `dist/index.d.ts` back to source), the old path is deleted in git, and
10
+ * this repository's `.bitmap` still names it.
11
+ *
12
+ * Two heals, cheapest first:
13
+ * 1. RETARGET — the scope's current version names a main file that IS in this repository, so only
14
+ * the `.bitmap` pointer is stale. Rewrite it and the component loads; nothing moves on disk.
15
+ * 2. UNTRACK — the scope's current version was read, and the main file it names is not here
16
+ * either. Drop the entry; main sync's `includeNewFromScope` re-imports the component from the
17
+ * scope's main. This one writes the component to a fresh directory, so it shows up as a move in
18
+ * the sync PR.
19
+ *
20
+ * An entry is only ever untracked on a POSITIVE read of the scope's head. A component the scope does
21
+ * not have, or whose objects could not be read (a failed import, a corrupt object), is left exactly
22
+ * as it is and reported: a transient remote failure must not delete a `.bitmap` entry, and the run
23
+ * failing loudly on it is the honest outcome.
24
+ *
25
+ * Deliberately ci-local: making the checkout itself tolerate an unloadable component would change
26
+ * `bit checkout` for every caller passing a "theirs" resolution.
27
+ */
28
+ export type MainFileHeal = {
29
+ id: string;
30
+ retargetedTo?: string;
31
+ };
32
+ /**
33
+ * Heal every `.bitmap` entry whose main file is missing, and persist. Returns what was healed so the
34
+ * caller can report it; the `.bitmap` change rides the sync commit like any other drift.
35
+ */
36
+ export declare function healMissingMainFiles(workspace: Workspace, logger: Logger): Promise<MainFileHeal[]>;
@@ -0,0 +1,180 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.healMissingMainFiles = healMissingMainFiles;
7
+ function _fsExtra() {
8
+ const data = _interopRequireDefault(require("fs-extra"));
9
+ _fsExtra = function () {
10
+ return data;
11
+ };
12
+ return data;
13
+ }
14
+ function _path() {
15
+ const data = _interopRequireDefault(require("path"));
16
+ _path = function () {
17
+ return data;
18
+ };
19
+ return data;
20
+ }
21
+ function _cli() {
22
+ const data = require("@teambit/cli");
23
+ _cli = function () {
24
+ return data;
25
+ };
26
+ return data;
27
+ }
28
+ function _componentId() {
29
+ const data = require("@teambit/component-id");
30
+ _componentId = function () {
31
+ return data;
32
+ };
33
+ return data;
34
+ }
35
+ function _laneSyncExecutor() {
36
+ const data = require("./lane-sync-executor");
37
+ _laneSyncExecutor = function () {
38
+ return data;
39
+ };
40
+ return data;
41
+ }
42
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
43
+ /**
44
+ * A `.bitmap` entry whose recorded `mainFile` is no longer on disk cannot be loaded at all — the
45
+ * component loader throws `MainFileRemoved`, and that one entry fails the whole `bit checkout head`
46
+ * the main-scope reconciler runs, so every scheduled run stays red until a human intervenes.
47
+ *
48
+ * The shape is legitimate: a new version moves the component's main file (e.g. an env stops emitting
49
+ * `dist/`, so main goes from `dist/index.d.ts` back to source), the old path is deleted in git, and
50
+ * this repository's `.bitmap` still names it.
51
+ *
52
+ * Two heals, cheapest first:
53
+ * 1. RETARGET — the scope's current version names a main file that IS in this repository, so only
54
+ * the `.bitmap` pointer is stale. Rewrite it and the component loads; nothing moves on disk.
55
+ * 2. UNTRACK — the scope's current version was read, and the main file it names is not here
56
+ * either. Drop the entry; main sync's `includeNewFromScope` re-imports the component from the
57
+ * scope's main. This one writes the component to a fresh directory, so it shows up as a move in
58
+ * the sync PR.
59
+ *
60
+ * An entry is only ever untracked on a POSITIVE read of the scope's head. A component the scope does
61
+ * not have, or whose objects could not be read (a failed import, a corrupt object), is left exactly
62
+ * as it is and reported: a transient remote failure must not delete a `.bitmap` entry, and the run
63
+ * failing loudly on it is the honest outcome.
64
+ *
65
+ * Deliberately ci-local: making the checkout itself tolerate an unloadable component would change
66
+ * `bit checkout` for every caller passing a "theirs" resolution.
67
+ */
68
+
69
+ /**
70
+ * Fetch the remote head of the stale components. Without it the local scope still holds the version
71
+ * this repository already had — whose main file is the missing one — and every entry would look
72
+ * unrepairable. A failure is not fatal on its own (local objects may still answer), but it must
73
+ * never be mistaken for "the scope says this is gone", and its cause is the first thing an operator
74
+ * needs when a scheduled run starts leaving entries untouched — so it is carried, not swallowed.
75
+ */
76
+ async function importHeadsOf(workspace, ids) {
77
+ try {
78
+ await workspace.scope.legacyScope.scopeImporter.importWithoutDeps(_componentId().ComponentIdList.fromArray(ids).toVersionLatest(), {
79
+ cache: false,
80
+ ignoreMissingHead: true
81
+ });
82
+ return {
83
+ ok: true
84
+ };
85
+ } catch (e) {
86
+ return {
87
+ ok: false,
88
+ error: e?.message || String(e)
89
+ };
90
+ }
91
+ }
92
+
93
+ /**
94
+ * What the scope's current (head) version says the component's main file is. The three outcomes are
95
+ * deliberately distinct: only `read` licenses a destructive heal.
96
+ */
97
+
98
+ async function mainFileOnScopeHead(workspace, id) {
99
+ const legacyScope = workspace.scope.legacyScope;
100
+ try {
101
+ const modelComponent = await legacyScope.getModelComponentIfExist(id);
102
+ const head = modelComponent?.getHeadRegardlessOfLaneAsTagOrHash();
103
+ if (!modelComponent || !head) return {
104
+ status: 'absent'
105
+ };
106
+ const version = await modelComponent.loadVersion(head, legacyScope.objects);
107
+ if (!version?.mainFile) return {
108
+ status: 'absent'
109
+ };
110
+ return {
111
+ status: 'read',
112
+ mainFile: version.mainFile
113
+ };
114
+ } catch (e) {
115
+ return {
116
+ status: 'unreadable',
117
+ reason: e?.message || String(e)
118
+ };
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Heal every `.bitmap` entry whose main file is missing, and persist. Returns what was healed so the
124
+ * caller can report it; the `.bitmap` change rides the sync commit like any other drift.
125
+ */
126
+ async function healMissingMainFiles(workspace, logger) {
127
+ const workspacePath = workspace.path;
128
+ const {
129
+ bitMap
130
+ } = workspace.consumer;
131
+ const onDisk = (rootDir, file) => _fsExtra().default.existsSync(_path().default.join(workspacePath, rootDir, file));
132
+ const stale = bitMap.components.filter(componentMap => {
133
+ const {
134
+ rootDir,
135
+ mainFile
136
+ } = componentMap;
137
+ return Boolean(rootDir) && Boolean(mainFile) && !onDisk(rootDir, mainFile);
138
+ });
139
+ if (!stale.length) return [];
140
+ const headFetch = await importHeadsOf(workspace, stale.map(componentMap => componentMap.id));
141
+ const healed = [];
142
+ const skipped = [];
143
+ for (const componentMap of stale) {
144
+ const id = componentMap.id.toStringWithoutVersion();
145
+ const onScope = await mainFileOnScopeHead(workspace, componentMap.id);
146
+ if (onScope.status === 'read' && onDisk(componentMap.rootDir, onScope.mainFile)) {
147
+ componentMap.mainFile = onScope.mainFile;
148
+ bitMap.markAsChanged();
149
+ healed.push({
150
+ id,
151
+ retargetedTo: onScope.mainFile
152
+ });
153
+ } else if (onScope.status === 'read') {
154
+ // Positively read, and the file it names is not here either: the whole component is gone from
155
+ // this tree, so the entry cannot be repaired in place.
156
+ bitMap.removeComponent(componentMap.id);
157
+ healed.push({
158
+ id
159
+ });
160
+ } else {
161
+ // Unknown, never assumed: leave the entry and let the checkout fail with its own message.
162
+ skipped.push(`${id} (${onScope.status === 'absent' ? 'not on the scope' : `unreadable: ${onScope.reason}`})`);
163
+ }
164
+ }
165
+ if (healed.length) await bitMap.write();
166
+ if (skipped.length) {
167
+ logger.consoleWarning(`Left ${skipped.length} .bitmap entr(ies) with a missing main file untouched — the scope could not ` + `confirm what to repair them to: ${(0, _laneSyncExecutor().capEntries)(skipped).join(', ')}` + (headFetch.ok ? '' : `. Fetching their heads failed first: ${headFetch.error}`));
168
+ }
169
+ const retargeted = healed.filter(heal => heal.retargetedTo);
170
+ const untracked = healed.filter(heal => !heal.retargetedTo);
171
+ if (retargeted.length) {
172
+ logger.console((0, _cli().formatWarningSummary)(`Repointed ${retargeted.length} .bitmap entr(ies) at the main file the scope's current version ` + `records: ${(0, _laneSyncExecutor().capEntries)(retargeted.map(heal => `${heal.id} -> ${heal.retargetedTo}`)).join(', ')}`));
173
+ }
174
+ if (untracked.length) {
175
+ logger.console((0, _cli().formatWarningSummary)(`Untracked ${untracked.length} component(s) whose recorded main file is gone from this repository, ` + `so the scope's current version can be written in their place: ` + `${(0, _laneSyncExecutor().capEntries)(untracked.map(heal => heal.id)).join(', ')}`));
176
+ }
177
+ return healed;
178
+ }
179
+
180
+ //# sourceMappingURL=heal-missing-main-files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_fsExtra","data","_interopRequireDefault","require","_path","_cli","_componentId","_laneSyncExecutor","e","__esModule","default","importHeadsOf","workspace","ids","scope","legacyScope","scopeImporter","importWithoutDeps","ComponentIdList","fromArray","toVersionLatest","cache","ignoreMissingHead","ok","error","message","String","mainFileOnScopeHead","id","modelComponent","getModelComponentIfExist","head","getHeadRegardlessOfLaneAsTagOrHash","status","version","loadVersion","objects","mainFile","reason","healMissingMainFiles","logger","workspacePath","path","bitMap","consumer","onDisk","rootDir","file","fs","existsSync","join","stale","components","filter","componentMap","Boolean","length","headFetch","map","healed","skipped","toStringWithoutVersion","onScope","markAsChanged","push","retargetedTo","removeComponent","write","consoleWarning","capEntries","retargeted","heal","untracked","console","formatWarningSummary"],"sources":["heal-missing-main-files.ts"],"sourcesContent":["import fs from 'fs-extra';\nimport path from 'path';\nimport { formatWarningSummary } from '@teambit/cli';\nimport type { Logger } from '@teambit/logger';\nimport type { Workspace } from '@teambit/workspace';\nimport type { ComponentID } from '@teambit/component-id';\nimport { ComponentIdList } from '@teambit/component-id';\nimport { capEntries } from './lane-sync-executor';\n\n/**\n * A `.bitmap` entry whose recorded `mainFile` is no longer on disk cannot be loaded at all — the\n * component loader throws `MainFileRemoved`, and that one entry fails the whole `bit checkout head`\n * the main-scope reconciler runs, so every scheduled run stays red until a human intervenes.\n *\n * The shape is legitimate: a new version moves the component's main file (e.g. an env stops emitting\n * `dist/`, so main goes from `dist/index.d.ts` back to source), the old path is deleted in git, and\n * this repository's `.bitmap` still names it.\n *\n * Two heals, cheapest first:\n * 1. RETARGET — the scope's current version names a main file that IS in this repository, so only\n * the `.bitmap` pointer is stale. Rewrite it and the component loads; nothing moves on disk.\n * 2. UNTRACK — the scope's current version was read, and the main file it names is not here\n * either. Drop the entry; main sync's `includeNewFromScope` re-imports the component from the\n * scope's main. This one writes the component to a fresh directory, so it shows up as a move in\n * the sync PR.\n *\n * An entry is only ever untracked on a POSITIVE read of the scope's head. A component the scope does\n * not have, or whose objects could not be read (a failed import, a corrupt object), is left exactly\n * as it is and reported: a transient remote failure must not delete a `.bitmap` entry, and the run\n * failing loudly on it is the honest outcome.\n *\n * Deliberately ci-local: making the checkout itself tolerate an unloadable component would change\n * `bit checkout` for every caller passing a \"theirs\" resolution.\n */\nexport type MainFileHeal = { id: string; retargetedTo?: string };\n\n/**\n * Fetch the remote head of the stale components. Without it the local scope still holds the version\n * this repository already had — whose main file is the missing one — and every entry would look\n * unrepairable. A failure is not fatal on its own (local objects may still answer), but it must\n * never be mistaken for \"the scope says this is gone\", and its cause is the first thing an operator\n * needs when a scheduled run starts leaving entries untouched — so it is carried, not swallowed.\n */\nasync function importHeadsOf(\n workspace: Workspace,\n ids: ComponentID[]\n): Promise<{ ok: true } | { ok: false; error: string }> {\n try {\n await workspace.scope.legacyScope.scopeImporter.importWithoutDeps(\n ComponentIdList.fromArray(ids).toVersionLatest(),\n { cache: false, ignoreMissingHead: true }\n );\n return { ok: true };\n } catch (e: any) {\n return { ok: false, error: e?.message || String(e) };\n }\n}\n\n/**\n * What the scope's current (head) version says the component's main file is. The three outcomes are\n * deliberately distinct: only `read` licenses a destructive heal.\n */\ntype ScopeMainFile =\n | { status: 'read'; mainFile: string }\n /** the scope has no such component, or no head for it — nothing to compare against */\n | { status: 'absent' }\n /** objects could not be read (failed import, corrupt object) — the answer is unknown */\n | { status: 'unreadable'; reason: string };\n\nasync function mainFileOnScopeHead(workspace: Workspace, id: ComponentID): Promise<ScopeMainFile> {\n const legacyScope = workspace.scope.legacyScope;\n try {\n const modelComponent = await legacyScope.getModelComponentIfExist(id);\n const head = modelComponent?.getHeadRegardlessOfLaneAsTagOrHash();\n if (!modelComponent || !head) return { status: 'absent' };\n const version = await modelComponent.loadVersion(head, legacyScope.objects);\n if (!version?.mainFile) return { status: 'absent' };\n return { status: 'read', mainFile: version.mainFile };\n } catch (e: any) {\n return { status: 'unreadable', reason: e?.message || String(e) };\n }\n}\n\n/**\n * Heal every `.bitmap` entry whose main file is missing, and persist. Returns what was healed so the\n * caller can report it; the `.bitmap` change rides the sync commit like any other drift.\n */\nexport async function healMissingMainFiles(workspace: Workspace, logger: Logger): Promise<MainFileHeal[]> {\n const workspacePath = workspace.path;\n const { bitMap } = workspace.consumer;\n const onDisk = (rootDir: string, file: string) => fs.existsSync(path.join(workspacePath, rootDir, file));\n\n const stale = bitMap.components.filter((componentMap) => {\n const { rootDir, mainFile } = componentMap;\n return Boolean(rootDir) && Boolean(mainFile) && !onDisk(rootDir as string, mainFile);\n });\n if (!stale.length) return [];\n\n const headFetch = await importHeadsOf(\n workspace,\n stale.map((componentMap) => componentMap.id)\n );\n\n const healed: MainFileHeal[] = [];\n const skipped: string[] = [];\n for (const componentMap of stale) {\n const id = componentMap.id.toStringWithoutVersion();\n const onScope = await mainFileOnScopeHead(workspace, componentMap.id);\n if (onScope.status === 'read' && onDisk(componentMap.rootDir as string, onScope.mainFile)) {\n componentMap.mainFile = onScope.mainFile;\n bitMap.markAsChanged();\n healed.push({ id, retargetedTo: onScope.mainFile });\n } else if (onScope.status === 'read') {\n // Positively read, and the file it names is not here either: the whole component is gone from\n // this tree, so the entry cannot be repaired in place.\n bitMap.removeComponent(componentMap.id);\n healed.push({ id });\n } else {\n // Unknown, never assumed: leave the entry and let the checkout fail with its own message.\n skipped.push(`${id} (${onScope.status === 'absent' ? 'not on the scope' : `unreadable: ${onScope.reason}`})`);\n }\n }\n if (healed.length) await bitMap.write();\n\n if (skipped.length) {\n logger.consoleWarning(\n `Left ${skipped.length} .bitmap entr(ies) with a missing main file untouched — the scope could not ` +\n `confirm what to repair them to: ${capEntries(skipped).join(', ')}` +\n (headFetch.ok ? '' : `. Fetching their heads failed first: ${headFetch.error}`)\n );\n }\n\n const retargeted = healed.filter((heal) => heal.retargetedTo);\n const untracked = healed.filter((heal) => !heal.retargetedTo);\n if (retargeted.length) {\n logger.console(\n formatWarningSummary(\n `Repointed ${retargeted.length} .bitmap entr(ies) at the main file the scope's current version ` +\n `records: ${capEntries(retargeted.map((heal) => `${heal.id} -> ${heal.retargetedTo}`)).join(', ')}`\n )\n );\n }\n if (untracked.length) {\n logger.console(\n formatWarningSummary(\n `Untracked ${untracked.length} component(s) whose recorded main file is gone from this repository, ` +\n `so the scope's current version can be written in their place: ` +\n `${capEntries(untracked.map((heal) => heal.id)).join(', ')}`\n )\n );\n }\n return healed;\n}\n"],"mappings":"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","ignoreList":[]}
@@ -56,29 +56,40 @@ export type LaneSyncDeps = {
56
56
  */
57
57
  export declare function laneHeadFingerprint(components: LaneData['components']): string;
58
58
  /**
59
- * The lane's components that do not belong to `defaultScope`. A lane may span scopes, and the
60
- * reconciler acts on a lane whole (fingerprint, materialize, snap, export), so foreign content is
61
- * refused rather than written into this repository. Soft-deleted components and `updateDependents`
62
- * entries are deliberately not read — the reconciler never materializes or exports them either.
59
+ * The lane's components that do not belong to `defaultScope`. A lane may span scopes; this
60
+ * repository reconciles only its own-scope slice (see `ownLaneComponents`), and the foreign entries
61
+ * ride along on the lane untouched — sourced in their own scopes' repositories and reachable here
62
+ * only as package dependencies. Soft-deleted components and `updateDependents` entries are
63
+ * deliberately not read — the reconciler never materializes or exports them either.
63
64
  */
64
65
  export declare function foreignLaneComponents(components: LaneData['components'], defaultScope: string): string[];
66
+ /**
67
+ * The slice of the lane this repository mirrors: the components that belong to `defaultScope`.
68
+ * Every reconciliation act — fingerprint, materialize, snap, export — operates on this slice, so a
69
+ * foreign component's head moving neither wakes the mirror nor reads as divergence.
70
+ */
71
+ export declare function ownLaneComponents(components: LaneData['components'], defaultScope: string): LaneData['components'];
65
72
  /**
66
73
  * `entries`, capped at `max`, with a final "…and K more" entry. The bound is on entries, not rendered
67
74
  * length, so a markdown list is never cut mid-line. `overflowPrefix` carries the caller's list marker
68
75
  * onto the summary line.
69
76
  */
70
77
  export declare function capEntries(entries: string[], overflowPrefix?: string, max?: number): string[];
71
- /** The shared clause describing why a lane is cross-scope, used by the skip/refusal/halt outcomes. */
78
+ /**
79
+ * The shared clause describing a lane with nothing of this repository's scope on it, used by the
80
+ * skip/refusal/halt outcomes. Only such a lane is refused: a lane that merely INCLUDES foreign
81
+ * components is reconciled over its own-scope slice.
82
+ */
72
83
  export declare function crossScopeDescription(foreignIds: string[], defaultScope: string): string;
73
84
  /**
74
- * A cross-scope lane that was merely enumerated: a skip, and the run stays green — a standing, valid
75
- * lane must not turn every scheduled run permanently red.
85
+ * A lane with no own-scope components that was merely enumerated: a skip, and the run stays green — a
86
+ * standing, valid lane must not turn every scheduled run permanently red.
76
87
  */
77
88
  export declare function crossScopeSkipSummary(laneName: string, foreignIds: string[], defaultScope: string): string;
78
89
  /**
79
- * A cross-scope lane the user named explicitly: exits non-zero with the explanation. A refusal, not a
80
- * halt — no PR is labelled, no branch is written. `existingBranch` keeps the closing sentence truthful
81
- * when a branch of that name already exists.
90
+ * A lane with no own-scope components that the user named explicitly: exits non-zero with the
91
+ * explanation. A refusal, not a halt — no PR is labelled, no branch is written. `existingBranch`
92
+ * keeps the closing sentence truthful when a branch of that name already exists.
82
93
  */
83
94
  export declare function crossScopeRefusal(foreignIds: string[], defaultScope: string, existingBranch?: string): string;
84
95
  /**
@@ -92,8 +103,9 @@ export declare function branchMirrorsOtherLaneReason(branch: string, mirroredLan
92
103
  */
93
104
  export declare function branchMirrorsOtherLaneNote(mirroredLaneIdStr: string, laneIdStr: string): string;
94
105
  /**
95
- * A lane that became cross-scope after this repository had already mirrored it onto a branch — the one
96
- * cross-scope shape that is a genuine halt: the pair is mid-flight and can no longer converge.
106
+ * A lane whose own-scope slice emptied out after this repository had already mirrored it onto a
107
+ * branch — the one cross-scope shape that is a genuine halt: the pair is mid-flight, and an empty
108
+ * slice fingerprints as a constant, which would read as converged forever over a stale mirror.
97
109
  */
98
110
  export declare function crossScopeMidFlightHaltReason(branch: string, foreignIds: string[], defaultScope: string): string;
99
111
  /**
@@ -126,10 +138,12 @@ export declare class LaneSyncExecutor {
126
138
  }): Promise<string>;
127
139
  private reconcileLane;
128
140
  /**
129
- * What a cross-scope lane means for this repository: a mid-flight halt when the branch is this
130
- * lane's live mirror (checked first — the problem is the state of the pair, not the phrasing of the
131
- * request); a refusal when the user named the lane explicitly; otherwise an enumerated skip that
132
- * stays green — one standing cross-scope lane must not fail every scheduled run forever.
141
+ * What a lane with NO own-scope components means for this repository (a lane that merely includes
142
+ * foreign components never reaches here — it is reconciled over its own-scope slice): a mid-flight
143
+ * halt when the branch is this lane's live mirror (checked first — the problem is the state of the
144
+ * pair, not the phrasing of the request); a refusal when the user named the lane explicitly;
145
+ * otherwise an enumerated skip that stays green — one standing foreign lane must not fail every
146
+ * scheduled run forever.
133
147
  */
134
148
  private crossScopeOutcome;
135
149
  /**
@@ -319,15 +333,19 @@ export declare class LaneSyncExecutor {
319
333
  private restoreWorkspace;
320
334
  }
321
335
  /**
322
- * The body of the pull request that mirrors a lane onto a branch. The component list is capped so the
323
- * body stays inside the git host's size limit however large the lane is; the total is not.
336
+ * The body of the pull request that mirrors a lane onto a branch. On a cross-scope lane only the
337
+ * own-scope slice is mirrored, and the foreign components get their own section — the branch's diff
338
+ * cannot show them, and a reviewer must know the lane carries more than this repository sees. Every
339
+ * component list is capped so the body stays inside the git host's size limit however large the lane
340
+ * is; the totals are not.
324
341
  */
325
- export declare function laneSyncPrBody({ laneIdStr, laneUrl, branch, laneHead, components, }: {
342
+ export declare function laneSyncPrBody({ laneIdStr, laneUrl, branch, laneHead, components, defaultScope, }: {
326
343
  laneIdStr: string;
327
344
  laneUrl: string;
328
345
  branch: string;
329
346
  laneHead: string;
330
347
  components: LaneData['components'];
348
+ defaultScope: string;
331
349
  }): string;
332
350
  /**
333
351
  * The lane components this export actually changed: new on the lane, or moved to a new head. Diffed