diff --git a/.github/workflows/weekly-update.yml b/.github/workflows/weekly-update.yml
index 9dcc87a5c..d9ef38b4e 100644
--- a/.github/workflows/weekly-update.yml
+++ b/.github/workflows/weekly-update.yml
@@ -51,8 +51,6 @@ jobs:
steps:
- uses: SocketDev/socket-registry/.github/actions/setup-and-install@6096b06b1790f411714c89c40f72aade2eeaab7c # main
- # claude-code is now a devDependency, installed via setup-and-install
-
- name: Create update branch
id: branch
env:
@@ -67,15 +65,22 @@ jobs:
with:
gpg-private-key: ${{ secrets.BOT_GPG_PRIVATE_KEY }}
- - name: Run updating skill with Claude Code
- id: claude
- timeout-minutes: 30
+ - name: Update dependencies (haiku — fast, cheap)
+ id: update
+ timeout-minutes: 10
shell: bash
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GITHUB_ACTIONS: 'true'
run: |
- alias pnpm="$SFW_BIN pnpm"
+ # Wrap pnpm through Socket firewall for all subprocesses (not just this shell).
+ if [ -n "$SFW_BIN" ]; then
+ mkdir -p /tmp/sfw-bin
+ printf '#!/bin/bash\nexec "%s" pnpm "$@"\n' "$SFW_BIN" > /tmp/sfw-bin/pnpm
+ chmod +x /tmp/sfw-bin/pnpm
+ export PATH="/tmp/sfw-bin:$PATH"
+ fi
+
if [ -z "$ANTHROPIC_API_KEY" ]; then
echo "ANTHROPIC_API_KEY not set - skipping automated update"
echo "success=false" >> $GITHUB_OUTPUT
@@ -83,9 +88,35 @@ jobs:
fi
set +e
- claude --print --dangerously-skip-permissions \
- --model sonnet \
- "/updating" \
+ claude --print \
+ --model haiku \
+ --max-turns 15 \
+ --allowedTools \
+ "Bash(pnpm:*)" \
+ "Bash(git add:*)" "Bash(git commit:*)" "Bash(git status:*)" "Bash(git diff:*)" "Bash(git log:*)" "Bash(git rev-parse:*)" \
+ "Read" "Write" "Edit" "Glob" "Grep" \
+ "$(cat <<'PROMPT'
+ /updating
+
+
+ You are an automated CI agent in a weekly dependency update workflow.
+ Git is configured with GPG signing. A branch has been created for you.
+
+
+
+ Update all dependencies to their latest versions.
+ Create one atomic commit per dependency update with a conventional commit message.
+ Leave all changes local — the workflow handles pushing and PR creation.
+ Do not run builds or tests — the next step handles that.
+
+
+
+ Each updated dependency has its own commit.
+ The lockfile is consistent with package.json changes.
+ No uncommitted changes remain in the working tree.
+
+ PROMPT
+ )" \
2>&1 | tee claude-output.log
CLAUDE_EXIT=${PIPESTATUS[0]}
set -e
@@ -96,6 +127,130 @@ jobs:
echo "success=false" >> $GITHUB_OUTPUT
fi
+ - name: Run tests
+ id: tests
+ if: steps.update.outputs.success == 'true'
+ shell: bash
+ run: |
+ if [ -n "$SFW_BIN" ]; then
+ mkdir -p /tmp/sfw-bin
+ printf '#!/bin/bash\nexec "%s" pnpm "$@"\n' "$SFW_BIN" > /tmp/sfw-bin/pnpm
+ chmod +x /tmp/sfw-bin/pnpm
+ export PATH="/tmp/sfw-bin:$PATH"
+ fi
+
+ set +e
+ pnpm run build 2>&1 | tee build-output.log
+ BUILD_EXIT=$?
+ if [ "$BUILD_EXIT" -ne 0 ]; then
+ echo "tests-passed=false" >> $GITHUB_OUTPUT
+ exit 0
+ fi
+
+ pnpm test 2>&1 | tee test-output.log
+ TEST_EXIT=$?
+ set -e
+
+ if [ "$TEST_EXIT" -eq 0 ]; then
+ echo "tests-passed=true" >> $GITHUB_OUTPUT
+ else
+ echo "tests-passed=false" >> $GITHUB_OUTPUT
+ fi
+
+ - name: Fix test failures (sonnet — smarter, escalated)
+ id: claude
+ if: steps.update.outputs.success == 'true' && steps.tests.outputs.tests-passed == 'false'
+ timeout-minutes: 15
+ shell: bash
+ env:
+ ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
+ GITHUB_ACTIONS: 'true'
+ run: |
+ if [ -n "$SFW_BIN" ]; then
+ mkdir -p /tmp/sfw-bin
+ printf '#!/bin/bash\nexec "%s" pnpm "$@"\n' "$SFW_BIN" > /tmp/sfw-bin/pnpm
+ chmod +x /tmp/sfw-bin/pnpm
+ export PATH="/tmp/sfw-bin:$PATH"
+ fi
+
+ # Collect failure context for the agent.
+ FAILURE_LOG=""
+ if [ -f build-output.log ]; then
+ FAILURE_LOG+="Build output (last 50 lines):"$'\n'
+ FAILURE_LOG+="$(tail -50 build-output.log)"$'\n\n'
+ fi
+ if [ -f test-output.log ]; then
+ FAILURE_LOG+="Test output (last 100 lines):"$'\n'
+ FAILURE_LOG+="$(tail -100 test-output.log)"$'\n'
+ fi
+
+ set +e
+ claude --print \
+ --model sonnet \
+ --max-turns 25 \
+ --allowedTools \
+ "Bash(pnpm:*)" \
+ "Bash(git add:*)" "Bash(git commit:*)" "Bash(git status:*)" "Bash(git diff:*)" "Bash(git log:*)" "Bash(git rev-parse:*)" \
+ "Read" "Write" "Edit" "Glob" "Grep" \
+ "$(cat <
+ $FAILURE_LOG
+
+
+
+ Diagnose the failures from the output above.
+ Fix the code — do not revert the dependency updates.
+ Run the tests again to verify your fix works.
+ Create a commit for each fix with a conventional commit message.
+
+ PROMPT
+ )" \
+ 2>&1 | tee -a claude-output.log
+ CLAUDE_EXIT=${PIPESTATUS[0]}
+ set -e
+
+ if [ "$CLAUDE_EXIT" -eq 0 ]; then
+ echo "success=true" >> $GITHUB_OUTPUT
+ else
+ echo "success=false" >> $GITHUB_OUTPUT
+ fi
+
+ - name: Set final status
+ id: final
+ if: always()
+ run: |
+ # Success if: update succeeded AND (tests passed OR fix succeeded)
+ if [ "${{ steps.update.outputs.success }}" = "true" ]; then
+ if [ "${{ steps.tests.outputs.tests-passed }}" = "true" ] || [ "${{ steps.claude.outputs.success }}" = "true" ]; then
+ echo "success=true" >> $GITHUB_OUTPUT
+ exit 0
+ fi
+ fi
+ echo "success=false" >> $GITHUB_OUTPUT
+
+ - name: Validate changes
+ id: validate
+ if: steps.final.outputs.success == 'true'
+ run: |
+ # Allow dependency files + source/test fixes from the fix step.
+ UNEXPECTED=""
+ for file in $(git diff --name-only origin/main..HEAD); do
+ case "$file" in
+ package.json|*/package.json|pnpm-lock.yaml|*/pnpm-lock.yaml) ;;
+ .npmrc|pnpm-workspace.yaml) ;;
+ src/*|test/*|*.ts|*.mts|*.js|*.mjs) ;;
+ *) UNEXPECTED="$UNEXPECTED $file" ;;
+ esac
+ done
+ if [ -n "$UNEXPECTED" ]; then
+ echo "::error::Unexpected files modified:$UNEXPECTED"
+ echo "valid=false" >> $GITHUB_OUTPUT
+ else
+ echo "valid=true" >> $GITHUB_OUTPUT
+ fi
+
- name: Check for changes
id: changes
run: |
@@ -106,13 +261,13 @@ jobs:
fi
- name: Push branch
- if: steps.claude.outputs.success == 'true' && steps.changes.outputs.has-changes == 'true'
+ if: steps.final.outputs.success == 'true' && steps.validate.outputs.valid == 'true' && steps.changes.outputs.has-changes == 'true'
env:
BRANCH_NAME: ${{ steps.branch.outputs.branch }}
run: git push origin "$BRANCH_NAME"
- name: Create Pull Request
- if: steps.claude.outputs.success == 'true' && steps.changes.outputs.has-changes == 'true'
+ if: steps.final.outputs.success == 'true' && steps.validate.outputs.valid == 'true' && steps.changes.outputs.has-changes == 'true'
env:
GH_TOKEN: ${{ github.token }}
BRANCH_NAME: ${{ steps.branch.outputs.branch }}
@@ -141,7 +296,7 @@ jobs:
--base main
- name: Add job summary
- if: steps.claude.outputs.success == 'true' && steps.changes.outputs.has-changes == 'true'
+ if: steps.final.outputs.success == 'true' && steps.validate.outputs.valid == 'true' && steps.changes.outputs.has-changes == 'true'
env:
BRANCH_NAME: ${{ steps.branch.outputs.branch }}
run: |
@@ -151,12 +306,15 @@ jobs:
echo "**Branch:** \`${BRANCH_NAME}\`" >> $GITHUB_STEP_SUMMARY
echo "**Commits:** ${COMMIT_COUNT}" >> $GITHUB_STEP_SUMMARY
- - name: Upload Claude output
+ - name: Upload logs
if: always()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
- name: claude-output-${{ github.run_id }}
- path: claude-output.log
+ name: weekly-update-logs-${{ github.run_id }}
+ path: |
+ claude-output.log
+ build-output.log
+ test-output.log
retention-days: 7
- uses: SocketDev/socket-registry/.github/actions/cleanup-git-signing@6096b06b1790f411714c89c40f72aade2eeaab7c # main