Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
252 changes: 230 additions & 22 deletions build/cli/index.js

Large diffs are not rendered by default.

229 changes: 207 additions & 22 deletions build/github_action/index.js

Large diffs are not rendered by default.

14 changes: 12 additions & 2 deletions docs/development/architecture.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -347,19 +347,29 @@ unit tests.

Branch synchronization has two independent entry points behind the same semantic
ports. The all-branch push workflow invokes `check_branch_sync_action`, which can
reach only dependency discovery, branch comparison, and issue notification adapters.
reach only dependency discovery, branch comparison, and the generic semantic
issue-publication adapter.
It has no agent role or agent environment. The comment command resolves one target,
authorizes the actor, and delegates Git merge ownership to a specialized workspace
adapter. The fixer capability becomes reachable only after Git reports eligible
conflicts.

The application use case owns orchestration and publishable results; pure policies
own command parsing, dependency selection, and notification rendering; repositories
own command parsing, dependency selection, stable publication identity, source-head
fingerprints, and localized safe rendering; repositories
translate GitHub GraphQL/REST responses; and the Git adapter owns transient merge
state. Verification runs without credentials. The adapter rejects changes to Git
heads, merge metadata, the non-conflicted index, or the prepared path set, then
re-fetches both remote heads immediately before the trusted commit/push boundary.

The observer creates only the initial stale status card, skips an identical
render, updates changed stale/aligned projections in place, and delegates only an
aligned-to-stale human-action transition to the shared immutable notification
coordinator. That coordinator reuses the generic issue-comment publication port
for exact bot ownership, concurrent-create reconciliation, duplicate removal or
compaction, and content-free Job Summary evidence. Branch-sync defines no parallel
comment authority.

## Deployment orchestration boundary

Release and hotfix deployment is a durable state machine rather than one
Expand Down
35 changes: 31 additions & 4 deletions docs/issues/branch-synchronization.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: Detect parent-branch drift on every push and safely align issue or

# Branch synchronization

Copilot can watch parent-to-child branch relationships and notify the related open issue when its working branch falls behind. Detection is intentionally separate from the normal commit workflow: `copilot_branch_sync.yml` listens to pushes on **all branches**, performs GitHub metadata and comparison requests, and never loads Bugbot or a code-changing agent. English and Spanish notices use bundled catalogs. Another configured BCP-47 locale may make one schema-constrained language request for the complete branch-notice catalog; invalid or unavailable localization falls back atomically to English before any comment is written.
Copilot can watch parent-to-child branch relationships and keep the related open issue's branch status current when its working branch falls behind. Detection is intentionally separate from the normal commit workflow: `copilot_branch_sync.yml` listens to pushes on **all branches**, performs GitHub metadata and comparison requests, and never loads Bugbot or a code-changing agent. English is the default. English and Spanish notices use bundled catalogs; another configured BCP-47 locale may make one schema-constrained language request for the complete branch-notice catalog. Invalid or unavailable localization falls back atomically to English before any comment is written.

This separation keeps pushes to `main`, `master`, `develop`, release branches, and intermediate feature branches observable without paying the cost or accepting the noise of the full commit-analysis pipeline.

Expand All @@ -17,12 +17,39 @@ On each non-deletion push, the observer:
2. resolves branch relationships from Copilot's durable issue configuration first, then from GitHub linked branches and PR base/head data;
3. selects relationships where the pushed branch is either the parent or the working branch;
4. compares each working branch with its parent; and
5. creates or updates a single bot-authored synchronization notice on the issue.
5. reconciles one bot-authored synchronization status card on the issue.

The notice is stateful. Further parent pushes update it instead of creating comment spam. When the working branch catches up—including after an automated synchronization—the same notice is marked resolved. A PAT-authored sync push is observed too, so a chain such as `develop → feature/A → feature/B` can propagate recommendations to the next level.
The card is stateful. The first stale observation creates only that card. Further parent pushes update it only when the rendered comparison changes. When the working branch catches up—including after an automated synchronization—the same card is marked resolved. If a later push changes that resolved card back to stale, Copilot also creates one short action notification linked to the card so the newly required action is not hidden by an edit. Replays of the same source head create nothing, and concurrent duplicates are removed or compacted to a link to the oldest notification.

A PAT-authored sync push is observed too, so a chain such as `develop → feature/A → feature/B` can propagate recommendations to the next level.

Notice prose uses the issue locale inherited from the repository locale unless `issues-locale` overrides it. Commands, branch names, comparison URLs, markers, and machine-readable state remain unchanged in every language.

## Status and notification lifecycle

| Previous state | New comparison | Conversation effect | Why |
| --- | --- | --- | --- |
| No card | Stale | Create one status card | The card itself is the initial notification. |
| Stale | Same rendered comparison | No mutation | Equivalent pushes should be silent. |
| Stale | Changed comparison | Update the card | The current fact changes without creating timeline noise. |
| Stale | Aligned | Update the card to resolved | The action is complete; GitHub does not need another notification. |
| Aligned | Stale | Update the card and create one short linked notification | Human action has become newly necessary. |
| Aligned | Stale replay at the same source head | No additional notification | The transition fingerprint is immutable and idempotent. |

With the default locale, the transition notification is intentionally compact:

```markdown
Branch synchronization needs attention: `feature/42` is behind `develop`. [Open the current status](https://github.com/example/project/issues/42#issuecomment-8).
```

With `issues-locale: es-ES`, the same semantic transition is rendered in Spanish:

```markdown
La sincronización de la rama necesita atención: `feature/42` está por detrás de `develop`. [Abrir el estado actual](https://github.com/example/project/issues/42#issuecomment-8).
```

The visible wording never participates in notification identity. Changing a translation therefore does not recreate or rewrite an already issued notification. The workflow Job Summary records whether the notification was created or reused and reports bounded duplicate-cleanup evidence without copying the message body.

<Info>
A manually created linked branch needs an open PR whose head is that branch to establish its parent. Branches created by Copilot already persist `parentBranch` and `workingBranch`, so they do not require this fallback.
</Info>
Expand Down Expand Up @@ -101,7 +128,7 @@ Every command finishes with a comment in the conversation. The report distinguis
- merged after fixer-agent conflict resolution; or
- safely aborted, with a publishable reason and no provider-secret details.

Successful reports include the parent and working branches, verification count, and merge commit SHA in the structured run result. The subsequent push observer resolves the prior stale-branch notice.
Successful reports include the parent and working branches, verification count, and merge commit SHA in the structured run result. The subsequent push observer resolves the prior stale-branch card.

## Why the normal commit workflow remains limited

Expand Down
21 changes: 21 additions & 0 deletions docs/issues/notifications-and-auto-close.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,26 @@ Push processing is part of the **Commit** workflow, not the Issue workflow.
Ensure `copilot_commit.yml` is installed and has its token and configured agent
inputs. See [How to use](/how-to-use) and [Bugbot](/bugbot).

## Cross-capability notification budget

Copilot treats GitHub comments as product UI and potential notifications, not as
an execution log. Routine metadata, no-op, replay, stale, superseded, merge, and
close-success events create no new comment. A durable capability creates at
most one initial status card and updates that card only when its semantic
projection changes.

A short transition notification is reserved for the moment an existing state
newly requires human action. It is limited to 400 visible characters and two
links, points back to the current status, and is created once for an immutable
semantic fingerprint. For example, branch synchronization creates no second
comment for its first stale observation; it notifies only when a previously
aligned card becomes stale again. Equivalent retries are silent. See
[Branch synchronization](/issues/branch-synchronization) for the complete flow.

Created or reused notifications and any duplicate-cleanup debt are reported in
the repository-locale Job Summary. Message bodies, internal steps, and provider
diagnostics are not copied into that evidence.

## Reopen issue on push

If an issue was **closed** but someone pushes again to its branch, you may want the issue to **reopen** so it’s not forgotten.
Expand Down Expand Up @@ -114,6 +134,7 @@ the missing context matters.
| Behavior | Controlled by | Where it runs |
|----------|---------------|---------------|
| Progress card update | Changed progress projection | Push (Commit) workflow |
| Branch-sync action notification | Aligned card becomes stale at a new source head | Branch Sync workflow |
| Reopen closed issue on push | `reopen-issue-on-push` (default: true) | Push (Commit) workflow |
| Auto-close issue when branch merged | Built-in | Push / PR workflow when merge is detected |
| Auto-close waiting issue after inactivity | `inactiveIssueClosure` + `inactivity-threshold-hours` | Scheduled workflow every 6 hours |
Expand Down
24 changes: 24 additions & 0 deletions scripts/coverage-budgets.json
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@
"src/domain/locale.ts",
"src/domain/message_catalog.ts",
"src/application/policies/action_summary_message_catalog.ts",
"src/application/policies/branch_sync_message_catalog.ts",
"src/application/policies/comment_translation_policy.ts",
"src/application/policies/inactivity_message_catalog.ts",
"src/application/policies/inactivity_notification_policy.ts",
Expand Down Expand Up @@ -356,6 +357,29 @@
}
],
"successMessage": "repository localization coverage: PASS (pure policies 100%; changed path 95% lines/statements, 90% branches/functions)"
},
{
"name": "Branch synchronization presentation",
"missingEntryLabel": "branch synchronization presentation",
"rules": [
{
"files": [
"src/application/policies/branch_sync_message_catalog.ts",
"src/application/policies/branch_sync_notification_policy.ts"
],
"mode": "each",
"thresholdProfile": "exhaustive"
},
{
"files": [
"src/application/usecases/actions/observe_branch_sync_use_case.ts",
"src/application/usecases/steps/common/transition_notification_workflow.ts"
],
"mode": "aggregate",
"thresholdProfile": "default"
}
],
"successMessage": "branch synchronization presentation coverage: PASS (catalog/policy 100%; observer/publication path 95% lines/statements, 90% branches/functions)"
}
]
}
10 changes: 5 additions & 5 deletions specs/CATALOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ debt or convert unknown historic intent into a design decision.
| `managed-issue-lifecycle` | As-built baseline | Convert typed issues into traceable work branches, project state, and lifecycle state | [Managed issue and branch lifecycle](./managed-issue-and-branch-lifecycle.md) | 21 paths · 2026-09-13 |
| `comment-automation` | Implemented | Admit only explicit commands or exact mentions, then route them while protecting repository mutations | [Comment automation and authorization](./comment-automation-and-authorization.md) | 52 paths · 2026-09-15 |
| `bugbot-analysis-and-autofix` | Implemented | Select one canonical PR, analyze bounded evidence, publish stable findings, and apply authorized verified fixes | [Bugbot analysis, finding publication, and autofix](./bugbot-analysis-publication-and-autofix.md) + 1 companion | 63 paths · 2026-09-13 |
| `branch-synchronization` | As-built baseline | Observe parent drift and safely merge a parent branch into a linked working branch | [Branch synchronization and conflict recovery](./branch-synchronization-and-conflict-recovery.md) | 15 paths · 2026-09-11 |
| `branch-synchronization` | Implemented | Observe parent drift with one localized status card and transition-only notifications, then safely merge a parent branch into a linked working branch | [Branch synchronization and conflict recovery](./branch-synchronization-and-conflict-recovery.md) | 30 paths · 2026-09-15 |
| `pull-request-lifecycle` | Implemented | Enrich linked and unlinked pull requests with safe issue linkage, projects, metadata, reviewers, concise descriptions, and distinct workflow evidence | [Pull request lifecycle and enrichment](./pull-request-lifecycle-and-enrichment.md) | 38 paths · 2026-09-15 |
| `agent-runtime` | Implemented | Resolve, provision, authenticate, authorize, and execute only the agent roles reachable by a run | [Agent runtime, provider, model, and role routing](./agent-runtime-provider-and-model-routing.md) + 1 companion | 51 paths · 2026-09-12 |
| `cli-and-single-actions` | As-built baseline | Expose bounded local commands and workflow-dispatched operations through the shared application core | [CLI and single-action execution](./cli-and-single-action-execution.md) | 33 paths · 2026-09-15 |
Expand Down Expand Up @@ -140,13 +140,13 @@ debt or convert unknown historic intent into a design decision.
### `branch-synchronization` — Branch synchronization and conflict recovery

- Owner: Copilot maintainers
- Last verified: 2026-09-11
- Last verified: 2026-09-15
- Specifications: [`specs/branch-synchronization-and-conflict-recovery.md`](./branch-synchronization-and-conflict-recovery.md)
- Workflows: [`.github/workflows/copilot_branch_sync.yml`](../.github/workflows/copilot_branch_sync.yml) · [`setup/workflows/copilot_branch_sync.yml`](../setup/workflows/copilot_branch_sync.yml)
- Entrypoints: [`src/application/usecases/branch_sync/sync_branch_use_case.ts`](../src/application/usecases/branch_sync/sync_branch_use_case.ts) · [`src/application/usecases/actions/observe_branch_sync_use_case.ts`](../src/application/usecases/actions/observe_branch_sync_use_case.ts)
- Core code: [`src/domain/branch_sync_command.ts`](../src/domain/branch_sync_command.ts) · [`src/application/usecases/branch_sync/branch_sync_execution_policy.ts`](../src/application/usecases/branch_sync/branch_sync_execution_policy.ts) · [`src/data/repository/branch_sync/branch_dependency_policy.ts`](../src/data/repository/branch_sync/branch_dependency_policy.ts) · [`src/data/repository/branch_sync/branch_dependency_repository.ts`](../src/data/repository/branch_sync/branch_dependency_repository.ts) · [`src/infrastructure/branch_sync_workspace_adapter.ts`](../src/infrastructure/branch_sync_workspace_adapter.ts)
- Tests: [`src/application/usecases/branch_sync/__tests__/sync_branch_use_case.test.ts`](../src/application/usecases/branch_sync/__tests__/sync_branch_use_case.test.ts) · [`src/data/repository/branch_sync/__tests__/branch_dependency_policy.test.ts`](../src/data/repository/branch_sync/__tests__/branch_dependency_policy.test.ts) · [`src/data/repository/branch_sync/__tests__/branch_dependency_repository.test.ts`](../src/data/repository/branch_sync/__tests__/branch_dependency_repository.test.ts) · [`src/infrastructure/__tests__/branch_sync_workspace_adapter.test.ts`](../src/infrastructure/__tests__/branch_sync_workspace_adapter.test.ts)
- User documentation: [`docs/issues/branch-synchronization.mdx`](../docs/issues/branch-synchronization.mdx) · [`docs/issues/comment-commands.mdx`](../docs/issues/comment-commands.mdx)
- Core code: [`src/domain/branch_sync_command.ts`](../src/domain/branch_sync_command.ts) · [`src/application/ports/branch_sync_ports.ts`](../src/application/ports/branch_sync_ports.ts) · [`src/application/policies/branch_sync_message_catalog.ts`](../src/application/policies/branch_sync_message_catalog.ts) · [`src/application/policies/branch_sync_notification_policy.ts`](../src/application/policies/branch_sync_notification_policy.ts) · [`src/application/usecases/branch_sync/branch_sync_execution_policy.ts`](../src/application/usecases/branch_sync/branch_sync_execution_policy.ts) · [`src/application/usecases/actions/observe_branch_sync_use_case.ts`](../src/application/usecases/actions/observe_branch_sync_use_case.ts) · [`src/application/usecases/push_single_action_contexts.ts`](../src/application/usecases/push_single_action_contexts.ts) · [`src/application/usecases/steps/common/transition_notification_workflow.ts`](../src/application/usecases/steps/common/transition_notification_workflow.ts) · [`src/data/repository/branch_sync/branch_dependency_policy.ts`](../src/data/repository/branch_sync/branch_dependency_policy.ts) · [`src/data/repository/branch_sync/branch_dependency_repository.ts`](../src/data/repository/branch_sync/branch_dependency_repository.ts) · [`src/infrastructure/composition/main_run_route_composition_root.ts`](../src/infrastructure/composition/main_run_route_composition_root.ts) · [`src/infrastructure/composition/push_single_action_capability_port_binding.ts`](../src/infrastructure/composition/push_single_action_capability_port_binding.ts) · [`src/infrastructure/branch_sync_workspace_adapter.ts`](../src/infrastructure/branch_sync_workspace_adapter.ts)
- Tests: [`src/application/policies/__tests__/branch_sync_notification_policy.test.ts`](../src/application/policies/__tests__/branch_sync_notification_policy.test.ts) · [`src/application/usecases/actions/__tests__/observe_branch_sync_use_case.test.ts`](../src/application/usecases/actions/__tests__/observe_branch_sync_use_case.test.ts) · [`src/application/usecases/branch_sync/__tests__/sync_branch_use_case.test.ts`](../src/application/usecases/branch_sync/__tests__/sync_branch_use_case.test.ts) · [`src/application/usecases/__tests__/push_single_action_contexts.test.ts`](../src/application/usecases/__tests__/push_single_action_contexts.test.ts) · [`src/application/usecases/steps/common/__tests__/transition_notification_workflow.test.ts`](../src/application/usecases/steps/common/__tests__/transition_notification_workflow.test.ts) · [`src/data/repository/branch_sync/__tests__/branch_dependency_policy.test.ts`](../src/data/repository/branch_sync/__tests__/branch_dependency_policy.test.ts) · [`src/data/repository/branch_sync/__tests__/branch_dependency_repository.test.ts`](../src/data/repository/branch_sync/__tests__/branch_dependency_repository.test.ts) · [`src/infrastructure/__tests__/branch_sync_workspace_adapter.test.ts`](../src/infrastructure/__tests__/branch_sync_workspace_adapter.test.ts) · [`src/infrastructure/composition/__tests__/push_single_action_capability_port_binding.test.ts`](../src/infrastructure/composition/__tests__/push_single_action_capability_port_binding.test.ts)
- User documentation: [`docs/issues/branch-synchronization.mdx`](../docs/issues/branch-synchronization.mdx) · [`docs/issues/comment-commands.mdx`](../docs/issues/comment-commands.mdx) · [`docs/issues/notifications-and-auto-close.mdx`](../docs/issues/notifications-and-auto-close.mdx) · [`docs/development/architecture.mdx`](../docs/development/architecture.mdx)

### `pull-request-lifecycle` — Pull request lifecycle and enrichment

Expand Down
Loading