From 5b6435dbf4a4cc97bbb42b0d0f1047b48874aa2f Mon Sep 17 00:00:00 2001 From: jdalton Date: Fri, 3 Apr 2026 12:53:41 -0400 Subject: [PATCH 1/9] Add format and format:check scripts for consistency Aligns with socket-btm and ultrathink convention: - format: oxfmt --write . (auto-fix formatting) - format:check: oxfmt . (check-only, CI) --- package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package.json b/package.json index ee42f9d69..774dd11ab 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,8 @@ "fix:all": "node scripts/fix.mjs --all", "lint": "node scripts/lint.mjs", "lint:all": "node scripts/lint.mjs --all", + "format": "oxfmt --write .", + "format:check": "oxfmt .", "// Claude": "", "claude": "pnpm --filter @socketsecurity/cli run claude --", "// Type Checking": "", From 477e5acb1b70dbd043aab2144732957160c75c1d Mon Sep 17 00:00:00 2001 From: jdalton Date: Fri, 3 Apr 2026 14:50:33 -0400 Subject: [PATCH 2/9] chore(ci): add GPG commit signing to weekly-update workflow --- .github/workflows/weekly-update.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/weekly-update.yml b/.github/workflows/weekly-update.yml index eb4a65e21..09389a545 100644 --- a/.github/workflows/weekly-update.yml +++ b/.github/workflows/weekly-update.yml @@ -86,12 +86,14 @@ jobs: GH_TOKEN: ${{ github.token }} run: | BRANCH_NAME="weekly-update-$(date +%Y%m%d)" - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" git remote set-url origin "https://x-access-token:${GH_TOKEN}@github.com/${{ github.repository }}.git" git checkout -b "$BRANCH_NAME" echo "branch=$BRANCH_NAME" >> $GITHUB_OUTPUT + - uses: SocketDev/socket-registry/.github/actions/setup-git-signing@6096b06b1790f411714c89c40f72aade2eeaab7c # main + with: + gpg-private-key: ${{ secrets.BOT_GPG_PRIVATE_KEY }} + - name: Run updating skill with Claude Code id: claude timeout-minutes: 30 @@ -182,6 +184,9 @@ jobs: path: claude-output.log retention-days: 7 + - uses: SocketDev/socket-registry/.github/actions/cleanup-git-signing@6096b06b1790f411714c89c40f72aade2eeaab7c # main + if: always() + notify: name: Notify results needs: [check-updates, apply-updates] From c0d41cad9a9e9e48f65be5199f7a9d96df6392ad Mon Sep 17 00:00:00 2001 From: jdalton Date: Fri, 3 Apr 2026 15:16:16 -0400 Subject: [PATCH 3/9] chore(ci): improve Claude prompt structure in weekly-update --- .github/workflows/weekly-update.yml | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/.github/workflows/weekly-update.yml b/.github/workflows/weekly-update.yml index 09389a545..9f9dc4f8c 100644 --- a/.github/workflows/weekly-update.yml +++ b/.github/workflows/weekly-update.yml @@ -110,7 +110,28 @@ jobs: set +e pnpm exec claude --print --dangerously-skip-permissions \ --model sonnet \ - "/updating - Run the updating skill to update all dependencies. Create atomic commits for each update. You are running in CI mode - skip builds and tests. Do not push or create a PR." \ + "$(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. + Skip running builds, tests, and type checks — CI runs those separately. + + + + 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 From 745d34758ac0ed2dd7a8f03a9b2a132dbb4c8686 Mon Sep 17 00:00:00 2001 From: jdalton Date: Fri, 3 Apr 2026 21:39:27 -0400 Subject: [PATCH 4/9] =?UTF-8?q?docs:=20add=20HTTP=20request=20rule=20?= =?UTF-8?q?=E2=80=94=20use=20@socketsecurity/lib/http-request,=20not=20fet?= =?UTF-8?q?ch()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CLAUDE.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CLAUDE.md b/CLAUDE.md index 87fda7d10..b4c82634f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -100,6 +100,7 @@ All shared standards (git, testing, code style, cross-platform, CI) defined in s - Backward Compatibility: 🚨 FORBIDDEN to maintain - actively remove when encountered (see canonical CLAUDE.md) - Work Safeguards: MANDATORY commit + backup branch before bulk changes - Safe Deletion: Use `safeDelete()` from `@socketsecurity/lib/fs` (NEVER `fs.rm/rmSync` or `rm -rf`) +- HTTP Requests: Use `httpJson`/`httpText`/`httpRequest` from `@socketsecurity/lib/http-request` (NEVER `fetch()`) ### Documentation Policy From ed1074e02c577dbce000eac14bea94f918303496 Mon Sep 17 00:00:00 2001 From: jdalton Date: Fri, 3 Apr 2026 21:45:00 -0400 Subject: [PATCH 5/9] docs: standardize HTTP request rule format in CLAUDE.md --- CLAUDE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index b4c82634f..73465cb56 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -100,7 +100,7 @@ All shared standards (git, testing, code style, cross-platform, CI) defined in s - Backward Compatibility: 🚨 FORBIDDEN to maintain - actively remove when encountered (see canonical CLAUDE.md) - Work Safeguards: MANDATORY commit + backup branch before bulk changes - Safe Deletion: Use `safeDelete()` from `@socketsecurity/lib/fs` (NEVER `fs.rm/rmSync` or `rm -rf`) -- HTTP Requests: Use `httpJson`/`httpText`/`httpRequest` from `@socketsecurity/lib/http-request` (NEVER `fetch()`) +- HTTP Requests: NEVER use `fetch()` — use `httpJson`/`httpText`/`httpRequest` from `@socketsecurity/lib/http-request` ### Documentation Policy From c9fa9c33151c10374906821236fda4b52ef9142d Mon Sep 17 00:00:00 2001 From: jdalton Date: Fri, 3 Apr 2026 23:18:31 -0400 Subject: [PATCH 6/9] fix: move minimum-release-age to pnpm-workspace.yaml --- .npmrc | 5 ++--- pnpm-workspace.yaml | 4 ++++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.npmrc b/.npmrc index 8ecf23943..a64ba10f3 100644 --- a/.npmrc +++ b/.npmrc @@ -6,9 +6,8 @@ link-workspace-packages=false loglevel=error prefer-workspace-packages=false -# Minimum release age - wait 7 days before installing newly published packages -# pnpm uses minimum-release-age (minutes), npm v11+ uses min-release-age (days) -minimum-release-age=10080 +# Minimum release age for npm v11+ (days). +# pnpm equivalent is in pnpm-workspace.yaml (minimumReleaseAge). min-release-age=7 # Trust policy - prevent downgrade attacks diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index c25ab33c7..0c4158cb8 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,3 +1,7 @@ +settings: + # Wait 7 days (10080 minutes) before installing newly published packages. + minimumReleaseAge: 10080 + packages: - packages/* - '!packages/package-builder/build' From c71fcd50385d6f3674ca6192706777bde1b50f9d Mon Sep 17 00:00:00 2001 From: jdalton Date: Fri, 3 Apr 2026 23:46:27 -0400 Subject: [PATCH 7/9] fix(ci): harden weekly-update workflow - Replace dangerously-skip-permissions with explicit allowedTools - Switch model from sonnet to haiku with max-turns 25 - Reduce timeout from 30 to 15 minutes - Add SFW_BIN wrapper for pnpm in subprocesses - Add diff validation step to reject unexpected file changes - Gate push/PR/summary steps on validation passing --- .github/workflows/weekly-update.yml | 41 ++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/.github/workflows/weekly-update.yml b/.github/workflows/weekly-update.yml index 9f9dc4f8c..3710e1a2e 100644 --- a/.github/workflows/weekly-update.yml +++ b/.github/workflows/weekly-update.yml @@ -96,11 +96,19 @@ jobs: - name: Run updating skill with Claude Code id: claude - timeout-minutes: 30 + timeout-minutes: 15 env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} GITHUB_ACTIONS: 'true' run: | + # 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 @@ -108,8 +116,10 @@ jobs: fi set +e - pnpm exec claude --print --dangerously-skip-permissions \ - --model sonnet \ + pnpm exec claude --print \ + --allowedTools "Bash(pnpm:*)" "Bash(git:*)" "Read" "Write" "Edit" "Glob" "Grep" \ + --model haiku \ + --max-turns 25 \ "$(cat <<'PROMPT' /updating @@ -142,6 +152,25 @@ jobs: echo "success=false" >> $GITHUB_OUTPUT fi + - name: Validate changes + id: validate + if: steps.claude.outputs.success == 'true' + run: | + # Only allow changes to dependency-related files. + 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) ;; + *) UNEXPECTED="$UNEXPECTED $file" ;; + esac + done + if [ -n "$UNEXPECTED" ]; then + echo "::error::Unexpected files modified by Claude:$UNEXPECTED" + echo "valid=false" >> $GITHUB_OUTPUT + else + echo "valid=true" >> $GITHUB_OUTPUT + fi + - name: Check for changes id: changes run: | @@ -152,13 +181,13 @@ jobs: fi - name: Push branch - if: steps.claude.outputs.success == 'true' && steps.changes.outputs.has-changes == 'true' + if: steps.claude.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.claude.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 }} @@ -187,7 +216,7 @@ jobs: --base main - name: Add job summary - if: steps.claude.outputs.success == 'true' && steps.changes.outputs.has-changes == 'true' + if: steps.claude.outputs.success == 'true' && steps.validate.outputs.valid == 'true' && steps.changes.outputs.has-changes == 'true' env: BRANCH_NAME: ${{ steps.branch.outputs.branch }} run: | From 55cd90135eb4918a0f62a7e81372de031806372a Mon Sep 17 00:00:00 2001 From: jdalton Date: Fri, 3 Apr 2026 23:51:02 -0400 Subject: [PATCH 8/9] fix(ci): use sonnet model for CI agent (haiku insufficient for test fixing) --- .github/workflows/weekly-update.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/weekly-update.yml b/.github/workflows/weekly-update.yml index 3710e1a2e..1b3a3f7c7 100644 --- a/.github/workflows/weekly-update.yml +++ b/.github/workflows/weekly-update.yml @@ -118,7 +118,7 @@ jobs: set +e pnpm exec claude --print \ --allowedTools "Bash(pnpm:*)" "Bash(git:*)" "Read" "Write" "Edit" "Glob" "Grep" \ - --model haiku \ + --model sonnet \ --max-turns 25 \ "$(cat <<'PROMPT' /updating From 8307c85a58e824bb5a657b987aeeabd8229be403 Mon Sep 17 00:00:00 2001 From: jdalton Date: Sat, 4 Apr 2026 00:00:30 -0400 Subject: [PATCH 9/9] =?UTF-8?q?feat(ci):=20two-phase=20update=20=E2=80=94?= =?UTF-8?q?=20haiku=20for=20deps,=20sonnet=20escalation=20for=20test=20fix?= =?UTF-8?q?es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/weekly-update.yml | 130 ++++++++++++++++++++++++---- 1 file changed, 115 insertions(+), 15 deletions(-) diff --git a/.github/workflows/weekly-update.yml b/.github/workflows/weekly-update.yml index 1b3a3f7c7..470bde3a1 100644 --- a/.github/workflows/weekly-update.yml +++ b/.github/workflows/weekly-update.yml @@ -94,14 +94,13 @@ jobs: with: gpg-private-key: ${{ secrets.BOT_GPG_PRIVATE_KEY }} - - name: Run updating skill with Claude Code - id: claude - timeout-minutes: 15 + - name: Update dependencies (haiku — fast, cheap) + id: update + timeout-minutes: 10 env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} GITHUB_ACTIONS: 'true' run: | - # 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 @@ -117,9 +116,9 @@ jobs: set +e pnpm exec claude --print \ - --allowedTools "Bash(pnpm:*)" "Bash(git:*)" "Read" "Write" "Edit" "Glob" "Grep" \ - --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" \ + --model haiku \ + --max-turns 15 \ "$(cat <<'PROMPT' /updating @@ -132,7 +131,7 @@ jobs: 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. - Skip running builds, tests, and type checks — CI runs those separately. + Do not run builds or tests — the next step handles that. @@ -142,7 +141,7 @@ jobs: PROMPT )" \ - 2>&1 | tee claude-output.log + 2>&1 | tee claude-update.log CLAUDE_EXIT=${PIPESTATUS[0]} set -e @@ -152,15 +151,112 @@ jobs: echo "success=false" >> $GITHUB_OUTPUT fi + - name: Run tests + id: tests + if: steps.update.outputs.success == '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 + + set +e + pnpm build 2>&1 | tee build.log + BUILD_EXIT=${PIPESTATUS[0]} + + pnpm test 2>&1 | tee test.log + TEST_EXIT=${PIPESTATUS[0]} + set -e + + if [ "$BUILD_EXIT" -eq 0 ] && [ "$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 + 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 + + FAILURE_LOG="$(cat build.log test.log 2>/dev/null)" + + set +e + pnpm exec claude --print \ + --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" \ + --model sonnet \ + --max-turns 25 \ + "$(cat < + 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. + Dependencies were updated in the previous step but build/tests failed. + + + + $FAILURE_LOG + + + + The dependency updates above caused build or test failures. + Diagnose the failures from the logs and fix the code so it builds and tests pass. + Create one atomic commit per fix with a conventional commit message. + Run pnpm build && pnpm test to verify your fixes. + Leave all changes local — the workflow handles pushing and PR creation. + + + + pnpm build succeeds. + pnpm test succeeds. + Each fix has its own commit. + No uncommitted changes remain in the working tree. + + PROMPT + )" \ + 2>&1 | tee claude-fix.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: | + if [ "${{ steps.update.outputs.success }}" = "true" ] && [ "${{ steps.tests.outputs.tests-passed }}" = "true" ]; then + echo "success=true" >> $GITHUB_OUTPUT + elif [ "${{ steps.update.outputs.success }}" = "true" ] && [ "${{ steps.claude.outputs.success }}" = "true" ]; then + echo "success=true" >> $GITHUB_OUTPUT + else + echo "success=false" >> $GITHUB_OUTPUT + fi + - name: Validate changes id: validate - if: steps.claude.outputs.success == 'true' + if: steps.final.outputs.success == 'true' run: | - # Only allow changes to dependency-related files. 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 @@ -181,13 +277,13 @@ jobs: fi - name: Push branch - if: steps.claude.outputs.success == 'true' && steps.validate.outputs.valid == '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.validate.outputs.valid == '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 }} @@ -216,7 +312,7 @@ jobs: --base main - name: Add job summary - if: steps.claude.outputs.success == 'true' && steps.validate.outputs.valid == '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: | @@ -231,7 +327,11 @@ jobs: uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: claude-output-${{ github.run_id }} - path: claude-output.log + path: | + claude-update.log + claude-fix.log + build.log + test.log retention-days: 7 - uses: SocketDev/socket-registry/.github/actions/cleanup-git-signing@6096b06b1790f411714c89c40f72aade2eeaab7c # main