Skip to content

feat(shell): org + agent navigation in the toolbar breadcrumb; threads-only sidebar #17115

feat(shell): org + agent navigation in the toolbar breadcrumb; threads-only sidebar

feat(shell): org + agent navigation in the toolbar breadcrumb; threads-only sidebar #17115

Workflow file for this run

name: Checks & Unit Tests
on:
push:
branches:
- main
# Skip pushes to main that only touch the version bump, CI/workflow config,
# or docs — none need the suite re-run (PRs already validated them, and the
# [release] bump is owned by release-mesh).
paths-ignore:
- "apps/mesh/package.json"
- ".github/**"
- "apps/docs/**"
- "**/*.md"
pull_request:
branches:
- main
jobs:
# Gate: skip the (required) check jobs when a PR only touches workflow YAML,
# docs, or markdown. Required checks can't be skipped via workflow-level
# `paths` filters — a never-scheduled required check blocks the merge forever.
# A job skipped via `if`, by contrast, reports as passed. So we keep the
# trigger broad and skip at the job level. Pushes to main always run.
changes:
runs-on: ubuntu-latest
outputs:
relevant: ${{ steps.filter.outputs.relevant }}
steps:
- name: Detect relevant changes
id: filter
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
HEAD_MSG: ${{ github.event.head_commit.message }}
run: |
if [ "${{ github.event_name }}" != "pull_request" ]; then
# Push to main runs everything — except the [release]: version-bump
# commit, which only touches the version string. release-mesh picks
# that commit up on its own push trigger and cuts the release.
case "$HEAD_MSG" in
'[release]:'*) echo "relevant=false" >> "$GITHUB_OUTPUT" ;;
*) echo "relevant=true" >> "$GITHUB_OUTPUT" ;;
esac
exit 0
fi
set +e
FILES=$(gh api --paginate \
"repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files" \
--jq '.[].filename')
if [ $? -ne 0 ]; then
echo "Could not list PR files — running to be safe."
echo "relevant=true" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "Changed files:"; echo "$FILES"
# Run unless every changed file is workflow/docs/markdown.
relevant=false
while IFS= read -r f; do
[ -z "$f" ] && continue
case "$f" in
.github/*|apps/docs/*|deploy/*|*.md) ;;
*) relevant=true; break ;;
esac
done <<< "$FILES"
echo "relevant=$relevant" >> "$GITHUB_OUTPUT"
format:
needs: changes
if: needs.changes.outputs.relevant == 'true'
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: "1.3.14"
- name: Install dependencies
run: bun install
- name: Run format check
run: bun run fmt:check
lint:
needs: changes
if: needs.changes.outputs.relevant == 'true'
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: "1.3.14"
- name: Install dependencies
run: bun install
- name: Run lint
run: bun run lint
typecheck:
needs: changes
if: needs.changes.outputs.relevant == 'true'
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "22"
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: "1.3.14"
- name: Install dependencies
run: bun install
- name: Run typecheck
run: bun run check:ci
test:
needs: changes
if: needs.changes.outputs.relevant == 'true'
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: "1.3.14"
- name: Install dependencies
run: bun install
- name: Run tests
run: |
# Tests are split by filename convention:
# *.test.ts → unit (here)
# *.integration.test.ts → .github/workflows/storage-integration.yml
# *.e2e.test.ts → reserved for the sandbox-daemon workflow
# See TESTING.md for the decision tree.
#
# One bun process per file: cleanest isolation, doesn't share
# ports/sockets/DB pools between files. Bun 1.3.14's teardown
# fixes mean we no longer need the signal-tolerance branches
# we had on 1.3.5 — a real test failure is now the only way
# bun exits non-zero.
set -e
while IFS= read -r f; do
echo "::group::$f"
bun test "$f"
echo "::endgroup::"
done < <(find apps/mesh/src apps/mesh/migrations packages \
\( -name '*.test.ts' -o -name '*.test.tsx' \) \
! -name '*.integration.test.ts' \
! -name '*.e2e.test.ts')
build:
needs: changes
if: needs.changes.outputs.relevant == 'true'
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: "1.3.14"
- name: Install dependencies
run: bun install
- name: Build server bundle
run: bun run --cwd=apps/mesh build:server
- name: Verify bundle outputs
run: |
test -f apps/mesh/dist/server/server.js
test -f apps/mesh/dist/server/migrate.js
test -f apps/mesh/dist/server/cli.js
- name: Run knip
run: bun run knip
docker-smoke:
needs: changes
if: needs.changes.outputs.relevant == 'true'
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: "1.3.14"
- name: Install dependencies
run: bun install
- name: Build daemon bundle
run: bun run --cwd=packages/sandbox build
- name: Build sandbox image
run: |
docker build \
-t studio-sandbox:ci \
-f packages/sandbox/image/Dockerfile \
packages/sandbox
- name: Smoke test
run: |
docker run -d --name sandbox-smoke -p 19999:9000 \
-e DAEMON_TOKEN="$(printf 't%.0s' {1..32})" \
-e DAEMON_BOOT_ID="ci-smoke" \
-e APP_ROOT=/app \
-e PROXY_PORT=9000 \
-e DAEMON_NO_AUTOSTART=1 \
studio-sandbox:ci
for i in $(seq 1 30); do
if curl -fsS http://localhost:19999/health | grep -q '"bootId":"ci-smoke"'; then
echo "ok"
exit 0
fi
sleep 1
done
echo "smoke test failed — daemon did not return /health with ci-smoke bootId"
docker logs sandbox-smoke
exit 1
- name: Tear down
if: always()
run: docker rm -f sandbox-smoke || true