diff --git a/.github/actions/prepare-model-env/pick.sh b/.github/actions/prepare-model-env/pick.sh index abab28f20961c2..85d0114e0e03c7 100755 --- a/.github/actions/prepare-model-env/pick.sh +++ b/.github/actions/prepare-model-env/pick.sh @@ -50,6 +50,19 @@ normalize_token() { printf '%s' "$v" } +# Classify an arbitrary CLI log for a caller. The verify canary hits the +# same failure modes this probe does, but later in the job — after this +# step already passed — so its reporter cannot lean on model-env-reason. +# One classifier, exposed here, keeps the two verdicts from drifting. +if [ "${1:-}" = --classify ]; then + if [ -z "${2:-}" ] || [ ! -f "$2" ]; then + echo other + exit 0 + fi + classify_failure "$2" + exit 0 +fi + if [ "${1:-}" = --self-test ]; then tmp=$(mktemp) printf '%s\n' 'HTTP 429 rate_limit_error: rate limit exceeded' >"$tmp" @@ -62,6 +75,10 @@ if [ "${1:-}" = --self-test ]; then [ "$(classify_failure "$tmp")" = auth ] printf '%s\n' 'ECONNRESET connection reset by peer' >"$tmp" [ "$(classify_failure "$tmp")" = other ] + printf '%s\n' 'API Error: 529 Overloaded. This is a server-side issue, usually temporary.' >"$tmp" + [ "$(classify_failure "$tmp")" = other ] + [ "$(bash "$0" --classify "$tmp")" = other ] + [ "$(bash "$0" --classify /nonexistent-classify-input)" = other ] rm -f "$tmp" [ "$(token_shape "sk-ant-oat01-aaaa")" = oat ] [ "$(token_shape "sk-ant-api03-aaaa")" = api ] diff --git a/.github/workflows/agent-commands.yml b/.github/workflows/agent-commands.yml index 042a7e396d8b66..2f27b9f9800a1b 100644 --- a/.github/workflows/agent-commands.yml +++ b/.github/workflows/agent-commands.yml @@ -1089,17 +1089,35 @@ jobs: else canary_prompt="Write the single word ok to .verify-out/.permcheck. Say nothing else." fi + set +e claude -p "$canary_prompt" \ --model "$VERIFY_MODEL" \ --disallowedTools "$DENIED" --strict-mcp-config \ - --allowedTools "$ALLOWED" > "$RUNNER_TEMP/permcheck.log" 2>&1 || true + --allowedTools "$ALLOWED" > "$RUNNER_TEMP/permcheck.log" 2>&1 + canary_rc=$? + set -e if [ ! -f .verify-out/.permcheck ]; then + # Also record WHICH failure this was, for the thread comment. + # The reporter's cause line comes from model-env-reason, and the + # credential probe PASSED by the time this runs — so without a + # verdict of its own, a canary death posts a bare "failed" with + # no cause (2026-08-18, PR 46816: a 529 between the probe and + # the canary). The verdict is grounded, not guessed: exit 0 + # with no file means the CLI ran and the rule did not match; + # non-zero means the CLI itself failed, and the probe's own + # classifier names the flavor from the kept output. + if [ "$canary_rc" -eq 0 ]; then + echo permission > "$RUNNER_TEMP/canary-reason" + else + bash .github/actions/prepare-model-env/pick.sh --classify "$RUNNER_TEMP/permcheck.log" > "$RUNNER_TEMP/canary-reason" || true + fi echo "::error::Preflight canary did not write .verify-out/.permcheck, so the run stopped before spending a sandbox. This is EITHER a file-permission rule that does not match OR the CLI failing to run at all (credential, model access, outage). The CLI output below says which:" sed -n '1,40p' "$RUNNER_TEMP/permcheck.log" || true echo "::error::--allowedTools was: $ALLOWED" exit 1 fi if [ "$FIX_MODE" = "true" ] && [ ! -f .permcheck-checkout ]; then + echo permission > "$RUNNER_TEMP/canary-reason" echo "::error::Preflight canary wrote .verify-out but not the checkout root, so the checkout-wide Edit rule is not matching — the agent would do its work and then fail to mirror it. Stopped before spending a sandbox. The CLI output:" sed -n '1,40p' "$RUNNER_TEMP/permcheck.log" || true echo "::error::--allowedTools was: $ALLOWED" @@ -1736,12 +1754,27 @@ jobs: agent=$(git rev-parse HEAD) echo "origin/main moved under the run (${base:0:12} -> ${tip:0:12}); replaying ${agent:0:12} onto it" git checkout --detach origin/main - if ! git cherry-pick "$agent"; then + # cherry-pick CREATES a commit, so it needs the same committer + # identity the publish commit got — without it, git dies with + # "empty ident name" and the first moved-main replay in the + # wild was misreported as "conflict: unknown" (run 32167073927). + if ! pick_out=$(git -c user.name='expo-bot' -c user.email='expo-bot@users.noreply.github.com' \ + cherry-pick "$agent" 2>&1); then conflicted=$(git diff --name-only --diff-filter=U | tr '\n' ' ') git cherry-pick --abort >/dev/null 2>&1 || true - echo "::error::could not replay the publish commit onto origin/main (conflict: ${conflicted:-unknown})." + echo "ALIGN_FAIL=replay" >> "$RUNNER_TEMP/align.env" + # Only a failure with unmerged paths is a conflict. Anything + # else is git refusing for its own reasons — show its words + # instead of diagnosing a conflict that did not happen. + if [ -n "$conflicted" ]; then + echo "::error::could not replay the publish commit onto origin/main (conflict: $conflicted)." + else + echo "::error::could not replay the publish commit onto origin/main (not a content conflict — git said):" + printf '%s\n' "$pick_out" + fi return 1 fi + printf '%s\n' "$pick_out" return 0 } local_workflows_tree() { @@ -1808,6 +1841,12 @@ jobs: if [ "$fail_kind" = "stale_fork" ]; then gh issue comment "$ISSUE_NUMBER" --body "⛔ \`$command_name\` prepared the $change_name but the bot fork's workflow files do not match current \`main\`, and the dedicated sync job is not running. Land on \`main\` (or re-run \`sync-expo-bot-fork\`) and re-trigger. The outcome comment still stands. [Run log]($RUN_URL)" echo "::error::stale fork with no in-flight sync; refusing to push." + elif [ "$fail_kind" = "replay" ]; then + # Without its own branch this fell through to the workflow-tree + # message below, which told the thread a fork-sync story about + # a replay failure (run 32167073927). + gh issue comment "$ISSUE_NUMBER" --body "⛔ \`$command_name\` prepared the $change_name, but \`main\` moved under the run and the change could not be replayed onto the new tip. Re-trigger \`$command_name\` to regenerate it against current \`main\`. The outcome comment still stands and describes the change. [Run log]($RUN_URL)" + echo "::error::replay onto moved origin/main failed; refusing to push." else gh issue comment "$ISSUE_NUMBER" --body "⛔ \`$command_name\` prepared the $change_name but could not line its workflow files up with the bot fork in time (the dedicated sync job on \`expo/expo\` main is what fast-forwards the fork). Re-run once that job has finished. The outcome comment still stands. [Run log]($RUN_URL)" echo "::error::could not match $fork .github/workflows to this commit; refusing to push." @@ -2101,16 +2140,26 @@ jobs: cancelled) outcome="was cancelled" ;; *) outcome="failed" ;; esac - cause="" + # Two sources for the cause, one vocabulary. model-env-reason + # covers a credential probe that failed; canary-reason covers a + # probe that PASSED followed by a preflight canary that did not — + # a window a 529 fell into on 2026-08-18 (PR 46816), which posted + # a bare "failed" with no cause and no hint that a plain retry + # would work. + reason="" if [ "$MODEL_ENV" = "failure" ]; then reason=$(cat "$RUNNER_TEMP/model-env-reason" 2>/dev/null || echo unprobed) - case "$reason" in - auth) cause=" The cause is its model credential, which was rejected — a maintainer needs to take a look." ;; - usage) cause=" The cause is a temporary model usage limit; nothing is misconfigured, so trying again later should work." ;; - other) cause=" The cause is a failure reaching the model — a network fault, a timeout, or a provider outage." ;; - *) cause=" It failed before reaching the model, so this is a workflow problem rather than a credential one." ;; - esac + elif [ -s "$RUNNER_TEMP/canary-reason" ]; then + reason=$(cat "$RUNNER_TEMP/canary-reason") fi + case "$reason" in + "") cause="" ;; + auth) cause=" The cause is its model credential, which was rejected — a maintainer needs to take a look." ;; + usage) cause=" The cause is a temporary model usage limit; nothing is misconfigured, so trying again later should work." ;; + other) cause=" The cause is a failure reaching the model — a network fault, a timeout, or a provider outage; nothing is misconfigured, so trying again later should work." ;; + permission) cause=" The cause is the workflow's file-permission rules: the preflight write check was refused, so a maintainer needs to fix the \`--allowedTools\` rules first — a plain retry will fail the same way." ;; + *) cause=" It failed before reaching the model, so this is a workflow problem rather than a credential one." ;; + esac # Point at the salvage when there is one. An interrupted run often # established real things before it died, and a maintainer should be # told where they are rather than left to assume nothing survived. diff --git a/apps/observe-tester/app/(tabs)/examples/_layout.tsx b/apps/observe-tester/app/(tabs)/examples/_layout.tsx index 1db064a2590d98..8e253edab7061f 100644 --- a/apps/observe-tester/app/(tabs)/examples/_layout.tsx +++ b/apps/observe-tester/app/(tabs)/examples/_layout.tsx @@ -15,6 +15,7 @@ export default function ExamplesLayout() { + ); } diff --git a/apps/observe-tester/app/(tabs)/examples/event-flood.tsx b/apps/observe-tester/app/(tabs)/examples/event-flood.tsx new file mode 100644 index 00000000000000..12f21e4046d318 --- /dev/null +++ b/apps/observe-tester/app/(tabs)/examples/event-flood.tsx @@ -0,0 +1,85 @@ +import AppMetrics from 'expo-app-metrics'; +import { Observe } from 'expo-observe'; +import { useState } from 'react'; +import { Platform, ScrollView, StyleSheet, Text } from 'react-native'; + +import { Button } from '@/components/Button'; +import { useTheme } from '@/utils/theme'; + +export default function EventFlood() { + const theme = useTheme(); + const [status, setStatus] = useState(null); + + function logFlood(count: number) { + const startedAt = performance.now(); + + for (let index = 0; index < count; index++) { + Observe.logEvent('stress.driver_event', { + severity: 'info', + body: 'Driver telemetry update received', + attributes: { + driverId: 'driver_48291', + vehicleId: 'vehicle_731', + tripId: 'trip_2026_08_14_1842', + mode: 'delivery', + isOnline: true, + speedKph: 42.7, + location: { + latitude: 37.7751, + longitude: -122.4193, + accuracyMeters: 8.4, + }, + route: ['warehouse', 'pickup', 'dropoff'], + index, + }, + }); + } + + setStatus( + `Logged ${count.toLocaleString()} events in ${Math.round(performance.now() - startedAt).toLocaleString()} ms` + ); + } + + async function clearStoredEntries() { + try { + await AppMetrics.clearStoredEntries(); + setStatus('Cleared stored entries'); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + setStatus(`Failed to clear stored entries: ${message}`); + } + } + + return ( + + + Log a burst of driver telemetry events synchronously to reproduce high-volume integrations. + +