Skip to content

Commit 8a4fb5f

Browse files
authored
chore: sync alpha from main (20260330-123045) (#1103)
## Summary Automated merge of `main` into `alpha`. - Commits from `main` not yet in `alpha`: **10** - merge-base: `f1f5bb1669ad1e1b2cc3cfd90fd439f7ee8609dc` - Generated: 20260330-123045 ## Review Instructions 1. Check for conflict markers (`<<<<<<<`) in changed files and resolve them. 2. Cherry-pick any alpha-specific fix commits onto this branch. 3. Verify CI passes. 4. Merge into `alpha` using **Create a merge commit** (not rebase). Generated by `scripts/rebase-main-to-alpha.sh`.
2 parents 7c7ea1b + 0baa51c commit 8a4fb5f

27 files changed

Lines changed: 771 additions & 376 deletions

.claude/skills/ambient-pr-test/SKILL.md

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ with .claude/skills/ambient-pr-test https://github.com/ambient-code/platform/pu
1616
```
1717

1818
Optional modifiers the user may specify:
19-
- **`--force-build`** — rebuild and push images even if CI already pushed them
2019
- **`--keep-alive`** — do not tear down after the workflow; leave the instance online for human access
2120
- **`provision-only`** / **`deploy-only`** / **`teardown-only`** — run a single phase instead of the full workflow
2221

@@ -56,7 +55,7 @@ This cluster's tenant operator does not emit `Ready` conditions on `TenantNamesp
5655
## Full Workflow
5756

5857
```
59-
0. Build: skip if CI pushed images (or --force-build to always rebuild)
58+
0. Build: always run build.sh to build and push images tagged pr-<PR_NUMBER>
6059
1. Derive instance-id from PR number
6160
2. Provision: bash components/pr-test/provision.sh create <instance-id>
6261
3. Deploy: bash components/pr-test/install.sh <namespace> <image-tag>
@@ -70,21 +69,15 @@ Phases can be run individually — see **Individual Phases** below.
7069

7170
## Step 0: Build and Push Images
7271

73-
Check CI first:
74-
```bash
75-
gh run list --repo ambient-code/platform \
76-
--workflow "Build and Push Component Docker Images" \
77-
--branch <head-branch> --limit 1
78-
```
79-
80-
**Skip** if the latest run shows `completed / success`. Otherwise build:
72+
Always run `build.sh` — CI may skip builds when no component source files changed (e.g. sync/merge branches), so never rely on CI to have pushed images:
8173
```bash
8274
bash components/pr-test/build.sh https://github.com/ambient-code/platform/pull/1005
8375
```
8476

85-
**`--force-build`**: skip the CI check and always run `build.sh` regardless. Use when:
86-
- Images exist but were built from a different commit (e.g. after a force-push)
87-
- CI built images but from a stale SHA
77+
This builds and pushes 3 images tagged `pr-<PR_NUMBER>`:
78+
- `quay.io/ambient_code/vteam_api_server:pr-<PR_NUMBER>`
79+
- `quay.io/ambient_code/vteam_control_plane:pr-<PR_NUMBER>`
80+
- `quay.io/ambient_code/vteam_claude_runner:pr-<PR_NUMBER>`
8881

8982
Builds 3 images: `vteam_api_server`, `vteam_control_plane`, `vteam_claude_runner`.
9083

.github/workflows/ci-failure-resolver-with-agent.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ jobs:
8989
9090
- name: Checkout & label
9191
if: steps.analyze.outputs.action == 'fix'
92-
uses: actions/checkout@v4
92+
uses: actions/checkout@v6
9393
with:
9494
ref: refs/pull/${{ matrix.pr }}/head
9595
fetch-depth: 0

.github/workflows/components-build-deploy.yml

Lines changed: 26 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: Build and Push Component Docker Images
22

33
on:
44
push:
5-
branches: [main]
5+
branches: [main, alpha]
66
paths:
77
- '.github/workflows/components-build-deploy.yml'
88
- 'components/manifests/**'
@@ -15,7 +15,7 @@ on:
1515
- 'components/ambient-control-plane/**'
1616
- 'components/ambient-mcp/**'
1717
pull_request:
18-
branches: [main]
18+
branches: [main, alpha]
1919
paths:
2020
- '.github/workflows/components-build-deploy.yml'
2121
- 'components/manifests/**'
@@ -29,11 +29,6 @@ on:
2929
- 'components/ambient-mcp/**'
3030
workflow_dispatch:
3131
inputs:
32-
force_build_all:
33-
description: 'Force rebuild all components'
34-
required: false
35-
type: boolean
36-
default: false
3732
components:
3833
description: 'Components to build (comma-separated: frontend,backend,operator,ambient-runner,state-sync,public-api,ambient-api-server,ambient-control-plane,ambient-mcp) - leave empty for all'
3934
required: false
@@ -107,29 +102,9 @@ jobs:
107102
{"name":"ambient-mcp","context":"./components/ambient-mcp","image":"quay.io/ambient_code/vteam_mcp","dockerfile":"./components/ambient-mcp/Dockerfile"}
108103
]'
109104
110-
FORCE_ALL="${{ github.event.inputs.force_build_all }}"
111105
SELECTED="${{ github.event.inputs.components }}"
112-
EVENT="${{ github.event_name }}"
113-
114-
# Map component names to paths-filter output names
115-
# (ambient-runner uses claude-runner filter)
116-
declare -A FILTER_MAP=(
117-
[frontend]="${{ steps.filter.outputs.frontend }}"
118-
[backend]="${{ steps.filter.outputs.backend }}"
119-
[operator]="${{ steps.filter.outputs.operator }}"
120-
[ambient-runner]="${{ steps.filter.outputs.claude-runner }}"
121-
[state-sync]="${{ steps.filter.outputs.state-sync }}"
122-
[public-api]="${{ steps.filter.outputs.public-api }}"
123-
[ambient-api-server]="${{ steps.filter.outputs.ambient-api-server }}"
124-
)
125-
126-
if [ "$FORCE_ALL" == "true" ]; then
127-
# Force build all
128-
FILTERED="$ALL_COMPONENTS"
129-
elif [ "$EVENT" == "workflow_dispatch" ] && [ -z "$SELECTED" ] && [ "$FORCE_ALL" != "true" ]; then
130-
# Dispatch with no selection and no force — build all
131-
FILTERED="$ALL_COMPONENTS"
132-
elif [ -n "$SELECTED" ]; then
106+
107+
if [ -n "$SELECTED" ]; then
133108
# Dispatch with specific components
134109
FILTERED=$(echo "$ALL_COMPONENTS" | jq -c --arg sel "$SELECTED" '[.[] | select(.name as $n | $sel | split(",") | map(gsub("^\\s+|\\s+$";"")) | index($n))]')
135110
elif [ "$EVENT" == "pull_request" ]; then
@@ -215,7 +190,7 @@ jobs:
215190
cache-from: type=gha,scope=${{ matrix.component.name }}-${{ matrix.arch.suffix }}
216191
cache-to: type=gha,mode=max,scope=${{ matrix.component.name }}-${{ matrix.arch.suffix }}
217192

218-
- name: Build ${{ matrix.component.name }} (${{ matrix.arch.suffix }}) for pull request
193+
- name: Build and push ${{ matrix.component.name }} (${{ matrix.arch.suffix }}) for pull request
219194
if: github.event_name == 'pull_request'
220195
uses: docker/build-push-action@v7
221196
with:
@@ -228,10 +203,11 @@ jobs:
228203
${{ matrix.component.image }}:pr-${{ github.event.pull_request.number }}-${{ github.sha }}-${{ matrix.arch.suffix }}
229204
build-args: AMBIENT_VERSION=${{ github.sha }}
230205
cache-from: type=gha,scope=${{ matrix.component.name }}-${{ matrix.arch.suffix }}
206+
cache-to: type=gha,mode=max,scope=${{ matrix.component.name }}-${{ matrix.arch.suffix }}
231207

232208
merge-manifests:
233209
needs: [detect-changes, build]
234-
if: github.event_name != 'pull_request' && needs.detect-changes.outputs.has-builds == 'true'
210+
if: needs.detect-changes.outputs.has-builds == 'true'
235211
runs-on: ubuntu-latest
236212
permissions:
237213
contents: read
@@ -251,7 +227,8 @@ jobs:
251227
username: ${{ secrets.QUAY_USERNAME }}
252228
password: ${{ secrets.QUAY_PASSWORD }}
253229

254-
- name: Create multi-arch manifest for ${{ matrix.component.name }}
230+
- name: Create multi-arch manifest for ${{ matrix.component.name }} (main)
231+
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
255232
# Suffixes (-amd64, -arm64) must match the arch matrix in the build job above.
256233
# Arch-suffixed tags remain in the registry after merging. Clean these up
257234
# via Quay tag expiration policies or a periodic job.
@@ -263,6 +240,23 @@ jobs:
263240
${{ matrix.component.image }}:${{ github.sha }}-amd64 \
264241
${{ matrix.component.image }}:${{ github.sha }}-arm64
265242
243+
- name: Create multi-arch manifest for ${{ matrix.component.name }} (alpha)
244+
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/alpha'
245+
run: |
246+
docker buildx imagetools create \
247+
-t ${{ matrix.component.image }}:alpha \
248+
-t ${{ matrix.component.image }}:${{ github.sha }} \
249+
${{ matrix.component.image }}:${{ github.sha }}-amd64 \
250+
${{ matrix.component.image }}:${{ github.sha }}-arm64
251+
252+
- name: Create multi-arch manifest for ${{ matrix.component.name }} (pull request)
253+
if: github.event_name == 'pull_request'
254+
run: |
255+
docker buildx imagetools create \
256+
-t ${{ matrix.component.image }}:pr-${{ github.event.pull_request.number }} \
257+
${{ matrix.component.image }}:pr-${{ github.event.pull_request.number }}-amd64 \
258+
${{ matrix.component.image }}:pr-${{ github.event.pull_request.number }}-arm64
259+
266260
update-rbac-and-crd:
267261
runs-on: ubuntu-latest
268262
needs: [detect-changes, merge-manifests]

.github/workflows/feedback-loop.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ jobs:
3636

3737
steps:
3838
- name: Checkout repository
39-
uses: actions/checkout@v4
39+
uses: actions/checkout@v6
4040

4141
- name: Set up Python
4242
uses: actions/setup-python@v6
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
name: Sync Alpha from Main
2+
3+
on:
4+
push:
5+
branches: [main]
6+
workflow_dispatch:
7+
8+
permissions:
9+
contents: write
10+
pull-requests: write
11+
12+
concurrency:
13+
group: sync-alpha-from-main
14+
cancel-in-progress: false
15+
16+
jobs:
17+
sync:
18+
name: Rebase main into alpha
19+
runs-on: ubuntu-latest
20+
timeout-minutes: 15
21+
22+
steps:
23+
- name: Checkout repository
24+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
25+
with:
26+
fetch-depth: 0
27+
token: ${{ secrets.GITHUB_TOKEN }}
28+
29+
- name: Configure git
30+
run: |
31+
git config user.name "github-actions[bot]"
32+
git config user.email "github-actions[bot]@users.noreply.github.com"
33+
34+
- name: Check if alpha is already up to date
35+
id: check
36+
run: |
37+
MAIN_SHA="$(git rev-parse origin/main)"
38+
ALPHA_SHA="$(git rev-parse origin/alpha)"
39+
COMMIT_COUNT="$(git rev-list --count "${ALPHA_SHA}..${MAIN_SHA}")"
40+
41+
echo "main_sha=${MAIN_SHA}" >> "$GITHUB_OUTPUT"
42+
echo "alpha_sha=${ALPHA_SHA}" >> "$GITHUB_OUTPUT"
43+
echo "commit_count=${COMMIT_COUNT}" >> "$GITHUB_OUTPUT"
44+
45+
if [ "${COMMIT_COUNT}" -eq 0 ]; then
46+
echo "needs_sync=false" >> "$GITHUB_OUTPUT"
47+
echo "alpha is already up to date with main"
48+
else
49+
echo "needs_sync=true" >> "$GITHUB_OUTPUT"
50+
echo "Commits in main not in alpha: ${COMMIT_COUNT}"
51+
fi
52+
53+
- name: Check for existing open sync PR
54+
if: steps.check.outputs.needs_sync == 'true'
55+
id: existing_pr
56+
env:
57+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
58+
run: |
59+
EXISTING=$(gh pr list \
60+
--base alpha \
61+
--state open \
62+
--json headRefName \
63+
--jq '[.[] | select(.headRefName | startswith("chore/sync-alpha-from-main-"))] | length')
64+
65+
if [ "${EXISTING}" -gt 0 ]; then
66+
echo "Open sync PR already exists — skipping"
67+
echo "pr_exists=true" >> "$GITHUB_OUTPUT"
68+
else
69+
echo "pr_exists=false" >> "$GITHUB_OUTPUT"
70+
fi
71+
72+
- name: Create work branch off alpha
73+
if: steps.check.outputs.needs_sync == 'true' && steps.existing_pr.outputs.pr_exists == 'false'
74+
id: branch
75+
run: |
76+
TIMESTAMP="$(date +%Y%m%d-%H%M%S)"
77+
WORK_BRANCH="chore/sync-alpha-from-main-${TIMESTAMP}"
78+
echo "work_branch=${WORK_BRANCH}" >> "$GITHUB_OUTPUT"
79+
80+
git checkout -b "${WORK_BRANCH}" origin/alpha
81+
echo "Created ${WORK_BRANCH} from origin/alpha"
82+
83+
- name: Attempt rebase of main onto work branch
84+
if: steps.check.outputs.needs_sync == 'true' && steps.existing_pr.outputs.pr_exists == 'false'
85+
id: rebase
86+
env:
87+
WORK_BRANCH: ${{ steps.branch.outputs.work_branch }}
88+
MAIN_SHA: ${{ steps.check.outputs.main_sha }}
89+
ALPHA_SHA: ${{ steps.check.outputs.alpha_sha }}
90+
run: |
91+
MERGE_BASE="$(git merge-base "${ALPHA_SHA}" "${MAIN_SHA}")"
92+
93+
git rebase --onto "${WORK_BRANCH}" "${MERGE_BASE}" origin/main && {
94+
echo "rebase_clean=true" >> "$GITHUB_OUTPUT"
95+
git checkout -B "${WORK_BRANCH}"
96+
echo "Rebase completed cleanly"
97+
} || {
98+
echo "rebase_clean=false" >> "$GITHUB_OUTPUT"
99+
git rebase --abort 2>/dev/null || true
100+
101+
echo "Rebase had conflicts — falling back to merge"
102+
MERGE_MSG=$(cat <<'MSG'
103+
chore: merge main into alpha (conflict resolution required)
104+
105+
Automated merge of origin/main into origin/alpha.
106+
Rebase encountered conflicts; falling back to merge.
107+
A human must resolve conflict markers before merging this PR.
108+
MSG
109+
)
110+
git merge --no-ff --allow-unrelated-histories origin/main -m "${MERGE_MSG}" || {
111+
git add -A
112+
CONFLICT_MSG=$(cat <<'MSG'
113+
chore: best-effort merge main into alpha (conflicts present)
114+
115+
Automated merge of origin/main into origin/alpha.
116+
Both rebase and merge encountered conflicts. Conflict markers
117+
are present and must be resolved before this PR can be merged.
118+
MSG
119+
)
120+
git commit --no-verify -m "${CONFLICT_MSG}"
121+
}
122+
}
123+
124+
- name: Push work branch
125+
if: steps.check.outputs.needs_sync == 'true' && steps.existing_pr.outputs.pr_exists == 'false'
126+
env:
127+
WORK_BRANCH: ${{ steps.branch.outputs.work_branch }}
128+
run: |
129+
git push origin "${WORK_BRANCH}"
130+
131+
- name: Open PR against alpha
132+
if: steps.check.outputs.needs_sync == 'true' && steps.existing_pr.outputs.pr_exists == 'false'
133+
env:
134+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
135+
WORK_BRANCH: ${{ steps.branch.outputs.work_branch }}
136+
COMMIT_COUNT: ${{ steps.check.outputs.commit_count }}
137+
REBASE_CLEAN: ${{ steps.rebase.outputs.rebase_clean }}
138+
MAIN_SHA: ${{ steps.check.outputs.main_sha }}
139+
ALPHA_SHA: ${{ steps.check.outputs.alpha_sha }}
140+
run: |
141+
if [ "${REBASE_CLEAN}" = "true" ]; then
142+
CONFLICT_NOTE="Rebase completed cleanly — no conflicts detected. This PR can be merged directly."
143+
else
144+
CONFLICT_NOTE="⚠️ **Conflicts detected.** Rebase fell back to merge. Search for \`<<<<<<<\` conflict markers and resolve before merging."
145+
fi
146+
147+
gh pr create \
148+
--base alpha \
149+
--head "${WORK_BRANCH}" \
150+
--title "chore: sync alpha from main ($(date +%Y-%m-%d))" \
151+
--body "## Summary
152+
153+
Automated sync of \`main\` into \`alpha\` triggered by push to \`main\`.
154+
155+
| | |
156+
|---|---|
157+
| Commits synced | ${COMMIT_COUNT} |
158+
| origin/main | \`${MAIN_SHA:0:8}\` |
159+
| origin/alpha | \`${ALPHA_SHA:0:8}\` |
160+
161+
## Status
162+
163+
${CONFLICT_NOTE}
164+
165+
## Review Instructions
166+
167+
1. Check for conflict markers (\`<<<<<<<\`) in changed files.
168+
2. Resolve any conflicts and push to this branch.
169+
3. Verify the build passes.
170+
4. Merge into \`alpha\`.
171+
172+
---
173+
*Auto-generated by \`.github/workflows/sync-alpha-from-main.yml\`*"
174+
175+
- name: Summary
176+
if: always()
177+
env:
178+
NEEDS_SYNC: ${{ steps.check.outputs.needs_sync }}
179+
PR_EXISTS: ${{ steps.existing_pr.outputs.pr_exists || 'false' }}
180+
COMMIT_COUNT: ${{ steps.check.outputs.commit_count || '0' }}
181+
REBASE_CLEAN: ${{ steps.rebase.outputs.rebase_clean || 'n/a' }}
182+
JOB_STATUS: ${{ job.status }}
183+
run: |
184+
if [ "${NEEDS_SYNC}" = "false" ]; then
185+
echo "## ✅ Already in sync" >> "$GITHUB_STEP_SUMMARY"
186+
echo "alpha is up to date with main — nothing to do." >> "$GITHUB_STEP_SUMMARY"
187+
elif [ "${PR_EXISTS}" = "true" ]; then
188+
echo "## ℹ️ Sync PR already open" >> "$GITHUB_STEP_SUMMARY"
189+
echo "An open sync PR already exists against alpha — skipped." >> "$GITHUB_STEP_SUMMARY"
190+
elif [ "${JOB_STATUS}" = "failure" ]; then
191+
echo "## ❌ Sync failed" >> "$GITHUB_STEP_SUMMARY"
192+
echo "Check the logs above for details." >> "$GITHUB_STEP_SUMMARY"
193+
elif [ "${REBASE_CLEAN}" = "true" ]; then
194+
echo "## ✅ PR opened — clean rebase" >> "$GITHUB_STEP_SUMMARY"
195+
echo "${COMMIT_COUNT} commits synced from main to alpha with no conflicts." >> "$GITHUB_STEP_SUMMARY"
196+
else
197+
echo "## ⚠️ PR opened — conflicts require resolution" >> "$GITHUB_STEP_SUMMARY"
198+
echo "${COMMIT_COUNT} commits from main; rebase had conflicts. PR opened for human resolution." >> "$GITHUB_STEP_SUMMARY"
199+
fi

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ Thumbs.db
7979
# IDE / AI assistant configuration
8080
.cursor/
8181
.tessl/
82+
.idea/
83+
8284

8385
# mypy
8486
.mypy_cache/

0 commit comments

Comments
 (0)