[feat] Build and ship the mobile gh image (4/12) - #5683
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughSummary by CodeRabbit
WalkthroughAdded mobile CI checks, multi-architecture image builds, smoke tests, optional Compose deployment at ChangesMobile CI and deployment
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant PullRequest
participant GitHubActions
participant Buildx
participant GHCR
participant MobileContainer
PullRequest->>GitHubActions: trigger mobile checks
GitHubActions->>GitHubActions: typecheck mobile packages and validate image metadata
GitHubActions->>Buildx: build amd64 and arm64 images
Buildx->>GHCR: publish images and architecture caches when enabled
GitHubActions->>MobileContainer: run mobile route and user smoke tests
GitHubActions->>GHCR: merge architecture images when enabled
sequenceDiagram
participant RunScript as run.sh
participant Compose
participant WebMobile as web-mobile
participant Traefik
RunScript->>Compose: enable with-web-mobile profile
Compose->>WebMobile: start mobile server on port 3000
Traefik->>WebMobile: route /m traffic without path stripping
RunScript->>Compose: include with-web-mobile during shutdown
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (1)
.github/workflows/17-check-mobile.yml (1)
44-44: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winSet
persist-credentials: falseon each checkout.None of the three
actions/checkout@v6steps (lines 44, 73, 119) disable credential persistence. Static analysis (zizmor) flags this asartipacked: the job's git credentials remain on disk and can leak if a later step or a compromised action touches the working tree. Since no step in this workflow needsgit push, disable persistence on all three checkouts.🔒 Proposed fix
- uses: actions/checkout@v6 + with: + persist-credentials: false(apply the same
with:block at all three occurrences)Also applies to: 73-73, 119-119
Source: Linters/SAST tools
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: ecd8f93a-d0d9-49cc-a5a3-a16793702491
📒 Files selected for processing (11)
.github/workflows/17-check-mobile.yml.gitignoredocs/design/agenta-mobile/README.mdhosting/docker-compose/ee/docker-compose.gh.local.ymlhosting/docker-compose/ee/docker-compose.gh.ymlhosting/docker-compose/ee/env.ee.gh.examplehosting/docker-compose/oss/docker-compose.gh.local.ymlhosting/docker-compose/oss/docker-compose.gh.ssl.ymlhosting/docker-compose/oss/docker-compose.gh.ymlhosting/docker-compose/oss/env.oss.gh.examplehosting/docker-compose/run.sh
| | [plans/2026-07-26-wp5-device-gate.md](./plans/2026-07-26-wp5-device-gate.md) | WP5 device gate (flag-gated middleware, both directions) — **READY TO EXECUTE**; default-off, T8 banner-retirement deferred to flag-flip | | ||
|
|
||
| Wave-2 plans (WP2 auth + project drawer, WP3b mobile chat skin, WP4 product pages, WP5 device | ||
| gate) are **deliberately unwritten** — they must be planned against the real wave-1 code and the | ||
| finalized sessions surface. |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Make the WP5 planning status consistent.
Line 39 marks the WP5 plan as READY TO EXECUTE. Lines 41-43 state that the WP5 plan is deliberately unwritten. Keep one status.
| ```bash | ||
| cd web && pnpm install | ||
| pnpm --filter @agenta/mobile lint # bans + tokens:check | ||
| pnpm --filter @agenta/mobile types:check | ||
| pnpm build-mobile && test -f web/../web/mobile/.next/standalone/mobile/server.js | ||
| pnpm dev-mobile # → http://localhost:3000/m, check light+dark |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Fix the standalone-file check path.
After cd web on Line 163, Line 166 resolves web/../web/mobile/... to web/web/mobile/.... The check fails after a valid build.
Proposed fix
-pnpm build-mobile && test -f web/../web/mobile/.next/standalone/mobile/server.js
+pnpm build-mobile && test -f mobile/.next/standalone/mobile/server.js📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ```bash | |
| cd web && pnpm install | |
| pnpm --filter @agenta/mobile lint # bans + tokens:check | |
| pnpm --filter @agenta/mobile types:check | |
| pnpm build-mobile && test -f web/../web/mobile/.next/standalone/mobile/server.js | |
| pnpm dev-mobile # → http://localhost:3000/m, check light+dark |
| - **Chores pending:** `.gitignore` allowlist entries for the `mobile-*` skills (they're tracked | ||
| via `git add -f`, matching repo precedent — new files inside them would be invisible to | ||
| `git status` until allowlisted); Fern regen so the FE `include_ended` param stops being a | ||
| runtime cast. |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Remove the completed .gitignore chore.
These lines mark the mobile skill allowlists as pending, but this change adds those allowlists in .gitignore Lines 103-105 and 130-132. Mark the chore complete or remove it.
2bdbdca to
1976012
Compare
85f8fd4 to
7edd737
Compare
1976012 to
a10ef27
Compare
7edd737 to
578f9fb
Compare
a10ef27 to
af8398c
Compare
578f9fb to
602ba19
Compare
602ba19 to
9629004
Compare
|
Both workflow findings were real and are fixed at the commit that introduced the file. Least privilege. Script injection. The four README findings are docs accuracy. I have applied the one that mattered outside this PR (a machine-specific absolute path in the WP3a plan, which leaked one developer's checkout into the repo); the remaining status/wording reconciliations are tracked but not yet applied. |
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (4)
.github/workflows/17-check-mobile.yml (1)
94-106: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick winValidate
TAGbefore publishing it as a job output.
INPUT_TAGis safe in this shell step, butTAGlater re-enters shell source through expressions such as Line 164. A dispatch value containing a quote and shell syntax can execute in thebuild-imagejob, which haspackages: write.Reject invalid Docker tags before writing
image_tagto$GITHUB_OUTPUT. This also rejects newline-based output injection.Proposed fix
else TAG="${INPUT_TAG:-manual-${SHA}}" PUSH="$INPUT_PUSH" + if ! [[ "$TAG" =~ ^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$ ]]; then + echo "::error::image_tag is not a valid Docker tag" + exit 1 + fi REF="$(printf "%s" "${GITHUB_REF_NAME}" | tr '[:upper:]' '[:lower:]' | tr -cs 'a-z0-9._-' '-')"Source: Linters/SAST tools
docs/design/agenta-mobile/README.md (3)
39-43: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winUse one WP5 planning status.
Line 39 states that the WP5 plan is ready to execute. Lines 41-43 state that WP5 is deliberately unwritten. Update one statement so the runbook has one status.
162-167: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winCorrect the standalone-file path.
After
cd web, the current path resolves toweb/web/mobile/.next/.... The verification command fails after a valid build.-pnpm build-mobile && test -f web/../web/mobile/.next/standalone/mobile/server.js +pnpm build-mobile && test -f mobile/.next/standalone/mobile/server.js
186-189: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winRemove the completed mobile-skill allowlist chore.
This PR adds the
.gitignoreallowlist entries formobile-*skills. Do not list that work as pending.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: bb9a0966-ca0f-47ff-87ee-e001112dc9c8
📒 Files selected for processing (11)
.github/workflows/17-check-mobile.yml.gitignoredocs/design/agenta-mobile/README.mdhosting/docker-compose/ee/docker-compose.gh.local.ymlhosting/docker-compose/ee/docker-compose.gh.ymlhosting/docker-compose/ee/env.ee.gh.examplehosting/docker-compose/oss/docker-compose.gh.local.ymlhosting/docker-compose/oss/docker-compose.gh.ssl.ymlhosting/docker-compose/oss/docker-compose.gh.ymlhosting/docker-compose/oss/env.oss.gh.examplehosting/docker-compose/run.sh
🚧 Files skipped from review as they are similar to previous changes (9)
- hosting/docker-compose/ee/env.ee.gh.example
- hosting/docker-compose/oss/docker-compose.gh.yml
- hosting/docker-compose/oss/env.oss.gh.example
- hosting/docker-compose/ee/docker-compose.gh.yml
- hosting/docker-compose/oss/docker-compose.gh.local.yml
- hosting/docker-compose/run.sh
- hosting/docker-compose/oss/docker-compose.gh.ssl.yml
- .gitignore
- hosting/docker-compose/ee/docker-compose.gh.local.yml
af8398c to
abe74aa
Compare
9629004 to
5fb3cb8
Compare
5fb3cb8 to
2f47ef7
Compare
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
Assert the exact root status — real, fixed. |
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: e8b8da7f-2348-431b-b4e1-f84dfe6d55b8
📒 Files selected for processing (11)
.github/workflows/17-check-mobile.yml.gitignoredocs/design/agenta-mobile/README.mdhosting/docker-compose/ee/docker-compose.gh.local.ymlhosting/docker-compose/ee/docker-compose.gh.ymlhosting/docker-compose/ee/env.ee.gh.examplehosting/docker-compose/oss/docker-compose.gh.local.ymlhosting/docker-compose/oss/docker-compose.gh.ssl.ymlhosting/docker-compose/oss/docker-compose.gh.ymlhosting/docker-compose/oss/env.oss.gh.examplehosting/docker-compose/run.sh
🚧 Files skipped from review as they are similar to previous changes (9)
- hosting/docker-compose/ee/env.ee.gh.example
- hosting/docker-compose/oss/docker-compose.gh.ssl.yml
- hosting/docker-compose/oss/env.oss.gh.example
- hosting/docker-compose/run.sh
- hosting/docker-compose/oss/docker-compose.gh.yml
- hosting/docker-compose/ee/docker-compose.gh.local.yml
- hosting/docker-compose/ee/docker-compose.gh.yml
- hosting/docker-compose/oss/docker-compose.gh.local.yml
- .gitignore
6e2e450 to
854acef
Compare
2f47ef7 to
d395582
Compare
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 2
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: bf364e75-36cd-475b-891b-76fcdaf0e343
📒 Files selected for processing (11)
.github/workflows/17-check-mobile.yml.gitignoredocs/design/agenta-mobile/README.mdhosting/docker-compose/ee/docker-compose.gh.local.ymlhosting/docker-compose/ee/docker-compose.gh.ymlhosting/docker-compose/ee/env.ee.gh.examplehosting/docker-compose/oss/docker-compose.gh.local.ymlhosting/docker-compose/oss/docker-compose.gh.ssl.ymlhosting/docker-compose/oss/docker-compose.gh.ymlhosting/docker-compose/oss/env.oss.gh.examplehosting/docker-compose/run.sh
🚧 Files skipped from review as they are similar to previous changes (9)
- hosting/docker-compose/ee/env.ee.gh.example
- .gitignore
- hosting/docker-compose/oss/env.oss.gh.example
- hosting/docker-compose/oss/docker-compose.gh.ssl.yml
- hosting/docker-compose/ee/docker-compose.gh.yml
- hosting/docker-compose/run.sh
- hosting/docker-compose/oss/docker-compose.gh.local.yml
- hosting/docker-compose/oss/docker-compose.gh.yml
- hosting/docker-compose/ee/docker-compose.gh.local.yml
| - name: Smoke-test the image serves /m | ||
| run: | | ||
| IMAGE="ghcr.io/agenta-ai/agenta-web-mobile:${{ needs.prepare.outputs.image_tag }}-${{ matrix.arch }}" | ||
| if [ "${{ needs.prepare.outputs.push }}" = "true" ]; then | ||
| docker pull "$IMAGE" | ||
| fi | ||
| docker run -d --name mobile-smoke -p 3000:3000 "$IMAGE" | ||
| for i in $(seq 1 30); do | ||
| if curl -sf http://127.0.0.1:3000/m >/dev/null; then break; fi | ||
| sleep 1 | ||
| done | ||
| # Assert the status itself rather than curl's exit code: `-sf` succeeds on a 3xx | ||
| # (the page never rendered) and fails identically on 404 and 500. | ||
| expect_status() { | ||
| local path="$1" want="$2" got | ||
| got="$(curl -s -o /dev/null -w '%{http_code}' "http://127.0.0.1:3000${path}")" | ||
| if [ "$got" != "$want" ]; then | ||
| echo "::error::expected ${path} to return ${want}, got ${got}" | ||
| docker logs mobile-smoke | ||
| exit 1 | ||
| fi | ||
| } | ||
| expect_status /m 200 | ||
| expect_status /m/__env.js 200 | ||
| # basePath /m owns the prefix, so the bare root belongs to nothing. | ||
| expect_status / 404 | ||
| docker rm -f mobile-smoke |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Bound and handle smoke-test HTTP requests.
The curl calls at Line 184 and Line 191 have no connection or total timeout. If the container accepts a connection but does not respond, one request can block the job indefinitely.
At Line 191, a connection failure can terminate the script before docker logs mobile-smoke runs. Convert transport failures to a status value so the existing diagnostic path runs.
Proposed fix
docker run -d --name mobile-smoke -p 3000:3000 "$IMAGE"
for i in $(seq 1 30); do
- if curl -sf http://127.0.0.1:3000/m >/dev/null; then break; fi
+ if curl --connect-timeout 2 --max-time 5 -sf http://127.0.0.1:3000/m >/dev/null; then break; fi
sleep 1
done
# Assert the status itself rather than curl's exit code: `-sf` succeeds on a 3xx
# (the page never rendered) and fails identically on 404 and 500.
expect_status() {
local path="$1" want="$2" got
- got="$(curl -s -o /dev/null -w '%{http_code}' "http://127.0.0.1:3000${path}")"
+ if ! got="$(curl --connect-timeout 2 --max-time 5 -s -o /dev/null -w '%{http_code}' "http://127.0.0.1:3000${path}")"; then
+ got="000"
+ fi
if [ "$got" != "$want" ]; then📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - name: Smoke-test the image serves /m | |
| run: | | |
| IMAGE="ghcr.io/agenta-ai/agenta-web-mobile:${{ needs.prepare.outputs.image_tag }}-${{ matrix.arch }}" | |
| if [ "${{ needs.prepare.outputs.push }}" = "true" ]; then | |
| docker pull "$IMAGE" | |
| fi | |
| docker run -d --name mobile-smoke -p 3000:3000 "$IMAGE" | |
| for i in $(seq 1 30); do | |
| if curl -sf http://127.0.0.1:3000/m >/dev/null; then break; fi | |
| sleep 1 | |
| done | |
| # Assert the status itself rather than curl's exit code: `-sf` succeeds on a 3xx | |
| # (the page never rendered) and fails identically on 404 and 500. | |
| expect_status() { | |
| local path="$1" want="$2" got | |
| got="$(curl -s -o /dev/null -w '%{http_code}' "http://127.0.0.1:3000${path}")" | |
| if [ "$got" != "$want" ]; then | |
| echo "::error::expected ${path} to return ${want}, got ${got}" | |
| docker logs mobile-smoke | |
| exit 1 | |
| fi | |
| } | |
| expect_status /m 200 | |
| expect_status /m/__env.js 200 | |
| # basePath /m owns the prefix, so the bare root belongs to nothing. | |
| expect_status / 404 | |
| docker rm -f mobile-smoke | |
| - name: Smoke-test the image serves /m | |
| run: | | |
| IMAGE="ghcr.io/agenta-ai/agenta-web-mobile:${{ needs.prepare.outputs.image_tag }}-${{ matrix.arch }}" | |
| if [ "${{ needs.prepare.outputs.push }}" = "true" ]; then | |
| docker pull "$IMAGE" | |
| fi | |
| docker run -d --name mobile-smoke -p 3000:3000 "$IMAGE" | |
| for i in $(seq 1 30); do | |
| if curl --connect-timeout 2 --max-time 5 -sf http://127.0.0.1:3000/m >/dev/null; then break; fi | |
| sleep 1 | |
| done | |
| # Assert the status itself rather than curl's exit code: `-sf` succeeds on a 3xx | |
| # (the page never rendered) and fails identically on 404 and 500. | |
| expect_status() { | |
| local path="$1" want="$2" got | |
| if ! got="$(curl --connect-timeout 2 --max-time 5 -s -o /dev/null -w '%{http_code}' "http://127.0.0.1:3000${path}")"; then | |
| got="000" | |
| fi | |
| if [ "$got" != "$want" ]; then | |
| echo "::error::expected ${path} to return ${want}, got ${got}" | |
| docker logs mobile-smoke | |
| exit 1 | |
| fi | |
| } | |
| expect_status /m 200 | |
| expect_status /m/__env.js 200 | |
| # basePath /m owns the prefix, so the bare root belongs to nothing. | |
| expect_status / 404 | |
| docker rm -f mobile-smoke |
🧰 Tools
🪛 zizmor (1.28.0)
[info] 178-178: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
[info] 179-179: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
| Branch **`feat/agenta-mobile-wave-1`** (off `feat/sessions-continuity-fixes`, worktree | ||
| `.claude/worktrees/big-agents-sessions`). Not pushed. Executed subagent-driven: every task got a | ||
| fresh implementer + spec-compliance review + code-quality review; fix rounds were re-reviewed. |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Remove the stale local branch state from the committed handoff.
This file is committed in the current pull request, but Lines 47-49 still state that feat/agenta-mobile-wave-1 is “Not pushed” and reference a local worktree. Replace this with the current repository state or mark it as historical.
854acef to
7b120cf
Compare
d395582 to
4c31b88
Compare
7b120cf to
e007054
Compare
4c31b88 to
beca6ec
Compare
mmabrouk
left a comment
There was a problem hiding this comment.
🤖 The AI agent says:
@ardaerzin, this review found four issues in the mobile image and hosting path:
- P1: A manual push can publish an image even when the dedicated typecheck fails.
- P1: A draft PR head can remain unvalidated after the PR becomes ready for review.
- P2:
--nginx --with-mobilestarts the mobile service but does not route/mto it. - P2: A stalled smoke-test HTTP response can hang the job and bypass container logs on transport failure.
I added the details and suggested fix direction inline.
| contents: read | ||
| packages: write | ||
| name: build-image | ||
| needs: prepare |
There was a problem hiding this comment.
🤖 The AI agent says:
[P1] Gate image publication on the typecheck job
During workflow_dispatch with push=true, this job only needs prepare, so it can build and push independently of typecheck. Because web/mobile/next.config.ts sets typescript.ignoreBuildErrors: true, the build and smoke test can succeed while the dedicated typecheck fails, and merge-manifests can still publish the invalid tag or overwrite latest. Please add typecheck to the image publication dependency path, for example by making build-image need both prepare and typecheck.
| jobs: | ||
| typecheck: | ||
| name: Mobile types | ||
| if: github.event_name == 'workflow_dispatch' || !github.event.pull_request.draft |
There was a problem hiding this comment.
🤖 The AI agent says:
[P1] Run checks when a draft becomes ready
If a commit is pushed while the PR is a draft, this condition skips both entry jobs. The default pull_request activity types do not include ready_for_review, so marking that PR ready does not validate the existing head. Please include ready_for_review in the trigger activity types or allow the checks to run for draft events.
| fi | ||
|
|
||
| if $WITH_MOBILE; then | ||
| COMPOSE_CMD+=" --profile with-web-mobile" |
There was a problem hiding this comment.
🤖 The AI agent says:
[P2] Route mobile traffic when nginx is selected
run.sh --oss --gh --nginx --with-mobile activates web-mobile, but that service only has Traefik labels. The OSS nginx config sends /m through its catch-all to web, so the advertised endpoint never reaches the mobile container. Please add an nginx /m route or reject the nginx and mobile flag combination.
| # (the page never rendered) and fails identically on 404 and 500. | ||
| expect_status() { | ||
| local path="$1" want="$2" got | ||
| got="$(curl -s -o /dev/null -w '%{http_code}' "http://127.0.0.1:3000${path}")" |
There was a problem hiding this comment.
🤖 The AI agent says:
[P2] Bound every smoke-test HTTP request
These status requests and the readiness request above have no connection or total timeout. A container that accepts a connection but never responds can therefore hang the job. Also, a transport failure in this command substitution exits the Actions bash script under -e before docker logs mobile-smoke runs. Please add bounded connect and total timeouts, and convert transport failures into a diagnostic status that reaches the log path.
mobile-app-structure, mobile-shadcn-conventions, and mobile-motion-patterns are already tracked via force-add; allowlist them so future files inside (e.g. references/) stay visible to git status.
Also runs types:check for @agenta/chat alongside @agenta/mobile in the typecheck job: verified locally (pnpm turbo run types:check --filter=@agenta/chat exits 0, pulling in @agenta/shared/entities/playground via turbo dependsOn).
Add an opt-in web-mobile service (with-web-mobile profile) to the five prod/gh compose files (oss gh/gh.ssl/gh.local, ee gh/gh.local), plus the AGENTA_WEB_MOBILE_IMAGE_NAME/TAG env-example entries. run.sh gains --with-mobile to activate the profile and reap it on shutdown.
The image smoke test asserts that the bare root 404s, because basePath /m owns the prefix. It did so by treating a successful `curl -sf` as the failure, but `curl -sf` fails on every 4xx and 5xx alike. A root that returned 500 read as a pass. Compare the status code itself, and dump the container log when it is anything other than 404.
…he /m segment Three findings from the same review, all in how this workflow and its compose siblings treat untrusted or ambiguous input. `image_tag` comes from workflow_dispatch and was written to GITHUB_OUTPUT unchecked, then interpolated into later run: blocks as an image name. It is now rejected unless it is a Docker tag. The check is a case statement rather than a grep because grep matches per line: a value containing a newline passes a line-based check and forges a second step output. The smoke test asserted curl's exit code, which cannot tell 404 from 500 and succeeds on a 3xx that never rendered a page. It now asserts each status directly: 200 for /m and /m/__env.js, 404 for the bare root. The five gh/ssl compose files routed the mobile app with PathPrefix(`/m`), which also matches /mobile, /metrics and anything else whose first two characters are /m, taking those paths from the web catch-all. They now use the segment-exact form the dev compose files already had.
…quests Three defects in the image workflow, all on the path that publishes a tag. `build-image` needed only `prepare`. `web/mobile/next.config.ts` sets `typescript.ignoreBuildErrors: true`, so the build and the smoke test can both pass while the dedicated typecheck fails — a dispatch with `push=true` would publish that tag and `merge-manifests` could move `latest` onto it. It now needs `typecheck` as well. `ready_for_review` is not a default `pull_request` activity type, and both entry jobs skip while a PR is a draft. Commits pushed during draft were therefore never checked, and marking the PR ready validated nothing. It is now in the trigger list. The smoke requests had no timeouts, so a container that accepts a connection and never answers hung the job until the 30-minute limit. They are bounded now. A transport failure also used to exit the step under `set -e` before `docker logs` ran, taking the only diagnostic with it; it becomes a sentinel status that reaches the mismatch branch instead.
beca6ec to
bad9ffd
Compare
`run.sh --oss --gh --nginx --with-mobile` starts `web-mobile`, but that service carries only
Traefik labels. Under nginx the OSS config sent `/m` through its catch-all to `web`, so the
endpoint the run advertised never reached the mobile container.
`location = /m` plus `location /m/` is nginx's spelling of the Traefik rule the same service
already has: segment-exact, so `/models` keeps falling through to the catch-all.
The upstream goes through a variable rather than a literal host. nginx resolves literal
upstreams at startup, so naming `web-mobile` directly would stop the whole proxy from booting
on any stack started WITHOUT the with-web-mobile profile. Through a variable the lookup is per
request against the resolver the file already declares.
Verified against a real nginx container on a stub network:
/m, /m/, /m/sessions -> the mobile upstream
/models, /mobile, / -> web, unchanged
and with no `web-mobile` container present at all, nginx still starts, `/` still serves, and
only `/m` answers 502.
Context
Lane 1 added the mobile app's production Dockerfile but deliberately left it unwired ("build wiring follows"). This lane connects it, so
/mcan actually ship in a gh image.Changes
A CI workflow runs the mobile typecheck and builds the gh image on changes under
web/mobile. Theweb-mobileservice joins the gh compose stacks for both editions. The mobile skills are allowlisted in.gitignore, which otherwise ignores everything under.claude/skills.Tests / notes
docs/design/agenta-mobile/plans/2026-07-25-wp1-infra-tail.md: the image builds before anything routes to it, so a half-deployed stack never serves a 502 at/m.