mirror of
https://github.com/getsentry/sentry-for-ai.git
synced 2026-09-14 14:28:22 +08:00
3c78b7064c
* feat(skill-drift): replace gh-aw workflow with matrix-fanout Claude runs
Replaces .github/workflows/skill-drift-check.{md,lock.yml} (single
sequential gh-aw run) with a plain GitHub Actions workflow that fans out
one Claude Sonnet session per SDK skill via matrix strategy.
Each per-skill job reads the last 7 days of merged PRs in the
corresponding SDK repo, opens the actually changed source files at the
merge SHA (not just the diff summary), compares against the local skill
bundle, and emits either a file diff under skills/<skill>/ or a
structured summary for the apply job to act on.
Security
- All actions pinned to a full commit SHA.
- permissions: {} at workflow root; each job grants only what it needs.
- The agent job (detect) runs with contents: read ONLY — no write tokens
reachable from the LLM session.
- Agent tool allowlist restricts Bash to a handful of gh/jq/date
subcommands; no git, curl, or arbitrary shell.
- The apply job is pure deterministic shell with a path allowlist
(skills/<this-skill>/ only) enforced before any commit, push, or PR.
- Inline reviewer assignment in the apply job since PRs opened by
GITHUB_TOKEN do not trigger downstream workflow_run events.
- Trigger surface is schedule + workflow_dispatch only.
Matrix lives in .github/skill-drift-matrix.json so adding a new SDK skill
is a one-line change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(skill-drift): address review feedback in apply job
Four issues flagged on PR #135:
1. (Cursor Bugbot, HIGH) The `artifacts/` directory placed by
download-artifact was being picked up by `git ls-files --others
--exclude-standard` in the apply job's path-allowlist check, which
would have downgraded EVERY create_pr to an issue because no
`artifacts/...` path matches the `^skills/<skill>/` regex. Scope
both `git diff` and `git ls-files` to `-- skills/` so anything
outside that subtree (artifacts/, dotfiles, etc.) is invisible to
the allowlist scan.
2. (Sentry Bot, HIGH) On the path-allowlist and empty-diff bail-out
paths the script switched branches but did not remove the untracked
files left behind by `rsync`. The next iteration would then see
those files via `git ls-files --others` and trigger a false-positive
allowlist violation, cascading the failure across all remaining
skills. Add `git clean -fd -- skills/` at the start of each
create_pr iteration and on both bail-out paths.
3. (Cursor Bugbot, MEDIUM) `git diff --quiet HEAD` does not detect
brand-new untracked files. If the agent's only change was adding a
new `references/*.md`, the empty-diff guard would have silently
skipped the PR. Replace with an `[[ -z "$TOUCHED" ]]` check, since
TOUCHED already includes untracked files.
4. (Sentry Bot, MEDIUM) Parsing the PR number from `gh pr create`
stdout via `basename` is fragile — extra text would corrupt it and
the downstream JS reviewer-assignment step would silently skip.
Look up the number via `gh pr view "$BRANCH" --json number --jq
.number` instead, and log a `::warning::` (plus increment FAILED)
if it still cannot be determined. Also add a warning log in the JS
step when an invalid PR number is encountered.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(skill-drift): second review pass — length caps, retries, pagination, edge cases
Addresses the round of review comments left on commit fea5d4a:
1. (sentry-warden, security) Untrusted SDK PR content reaches issue/PR
bodies via `gh issue create --body "$BODY"` and `gh pr create --body`.
Add a 256-char cap on `title` and a 10000-char cap on `body`, enforced
in both detect's validate step and apply's re-validation. Prepend a
GitHub `> [!WARNING]` alert header to every issue/PR body the agent
produces so human reviewers see "Auto-generated from external SDK
content. Review all links and code suggestions before acting on
them." before reading agent-authored text. Limits the blast radius
of a successful prompt-injection from an SDK PR.
2. (sentry-bot, MEDIUM) Race condition between `gh pr create` and
`gh pr view`. GitHub's API is eventually consistent — the immediate
view can 404 and silently skip reviewer assignment. Retry up to 5
times with a 1s sleep; warn (not silently skip) if all attempts fail.
3. (cursor-bot, MEDIUM) `actions/download-artifact@v4` fails the step
when its pattern matches zero artifacts, before the apply script's
graceful empty-artifact branch can run. Mark the download step
`continue-on-error: true`. The apply script already handles a
missing `artifacts/` directory and now also emits a complete set of
step-outputs (prs/issues/no_drift/failed/opened_pr_nums) on the
no-artifacts early-exit so downstream conditions see consistent
values.
4. (cursor-bot, LOW) `github.rest.pulls.listFiles` in the inline
reviewer-assignment step only returns the first page. Use
`github.paginate(..., per_page: 100)` so PRs with many files don't
miss teams. Matches the convention used by
skill-drift-assign-reviewers.yml.
5. (cursor-bot, LOW) Trailing/leading/double commas in the
`workflow_dispatch` `skills` input parsed as empty tokens, which
then failed the unknown-skill check. Drop empty entries in the jq
pipeline.
6. (cursor-bot, MEDIUM) `title` and `body` were validated as strings
but empty strings passed, which would have broken `git commit -m ""`
and yielded useless empty issues. Require non-empty values for
`create_pr`/`create_issue` (still allowed for `no_drift`, which the
apply job ignores entirely).
WONTFIX: the "hardcoded `main`" comment. This workflow is pinned to
`getsentry/sentry-for-ai` via `if: github.repository ==` guards on
every job; the default branch IS `main`. Parameterizing
`github.event.repository.default_branch` adds complexity for a
portability story we don't have.
Also: update the agent prompt with the new length caps and non-empty
requirement so the agent's output isn't silently rejected by
validation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
117 lines
3.3 KiB
JSON
117 lines
3.3 KiB
JSON
[
|
|
{
|
|
"skill": "sentry-android-sdk",
|
|
"repo": "getsentry/sentry-android",
|
|
"path_filter": "",
|
|
"team": "getsentry/team-mobile"
|
|
},
|
|
{
|
|
"skill": "sentry-browser-sdk",
|
|
"repo": "getsentry/sentry-javascript",
|
|
"path_filter": "packages/browser/ packages/core/",
|
|
"team": "getsentry/team-javascript-sdks"
|
|
},
|
|
{
|
|
"skill": "sentry-cloudflare-sdk",
|
|
"repo": "getsentry/sentry-javascript",
|
|
"path_filter": "packages/cloudflare/ packages/core/",
|
|
"team": "getsentry/team-javascript-sdks"
|
|
},
|
|
{
|
|
"skill": "sentry-cocoa-sdk",
|
|
"repo": "getsentry/sentry-cocoa",
|
|
"path_filter": "",
|
|
"team": "getsentry/team-mobile"
|
|
},
|
|
{
|
|
"skill": "sentry-dotnet-sdk",
|
|
"repo": "getsentry/sentry-dotnet",
|
|
"path_filter": "",
|
|
"team": "getsentry/team-web-sdk-backend"
|
|
},
|
|
{
|
|
"skill": "sentry-elixir-sdk",
|
|
"repo": "getsentry/sentry-elixir",
|
|
"path_filter": "",
|
|
"team": "getsentry/team-web-sdk-backend"
|
|
},
|
|
{
|
|
"skill": "sentry-flutter-sdk",
|
|
"repo": "getsentry/sentry-dart",
|
|
"path_filter": "",
|
|
"team": "getsentry/team-mobile-cross-platform"
|
|
},
|
|
{
|
|
"skill": "sentry-go-sdk",
|
|
"repo": "getsentry/sentry-go",
|
|
"path_filter": "",
|
|
"team": "getsentry/team-web-sdk-backend"
|
|
},
|
|
{
|
|
"skill": "sentry-nestjs-sdk",
|
|
"repo": "getsentry/sentry-javascript",
|
|
"path_filter": "packages/nestjs/ packages/node/ packages/core/",
|
|
"team": "getsentry/team-javascript-sdks"
|
|
},
|
|
{
|
|
"skill": "sentry-nextjs-sdk",
|
|
"repo": "getsentry/sentry-javascript",
|
|
"path_filter": "packages/nextjs/ packages/node/ packages/react/ packages/core/",
|
|
"team": "getsentry/team-javascript-sdks"
|
|
},
|
|
{
|
|
"skill": "sentry-node-sdk",
|
|
"repo": "getsentry/sentry-javascript",
|
|
"path_filter": "packages/node/ packages/bun/ packages/deno/ packages/core/",
|
|
"team": "getsentry/team-javascript-sdks"
|
|
},
|
|
{
|
|
"skill": "sentry-php-sdk",
|
|
"repo": "getsentry/sentry-php",
|
|
"path_filter": "",
|
|
"team": "getsentry/team-web-sdk-backend"
|
|
},
|
|
{
|
|
"skill": "sentry-python-sdk",
|
|
"repo": "getsentry/sentry-python",
|
|
"path_filter": "",
|
|
"team": "getsentry/owners-python-sdk"
|
|
},
|
|
{
|
|
"skill": "sentry-react-native-sdk",
|
|
"repo": "getsentry/sentry-react-native",
|
|
"path_filter": "",
|
|
"team": "getsentry/team-mobile-cross-platform"
|
|
},
|
|
{
|
|
"skill": "sentry-react-router-framework-sdk",
|
|
"repo": "getsentry/sentry-javascript",
|
|
"path_filter": "packages/react-router/ packages/profiling-node/ packages/core/",
|
|
"team": "getsentry/team-javascript-sdks"
|
|
},
|
|
{
|
|
"skill": "sentry-react-sdk",
|
|
"repo": "getsentry/sentry-javascript",
|
|
"path_filter": "packages/react/ packages/browser/ packages/core/",
|
|
"team": "getsentry/team-javascript-sdks"
|
|
},
|
|
{
|
|
"skill": "sentry-ruby-sdk",
|
|
"repo": "getsentry/sentry-ruby",
|
|
"path_filter": "",
|
|
"team": "getsentry/team-web-sdk-backend"
|
|
},
|
|
{
|
|
"skill": "sentry-svelte-sdk",
|
|
"repo": "getsentry/sentry-javascript",
|
|
"path_filter": "packages/svelte/ packages/sveltekit/ packages/browser/ packages/core/",
|
|
"team": "getsentry/team-javascript-sdks"
|
|
},
|
|
{
|
|
"skill": "sentry-tanstack-start-sdk",
|
|
"repo": "getsentry/sentry-javascript",
|
|
"path_filter": "packages/tanstackstart-react/ packages/core/",
|
|
"team": "getsentry/team-javascript-sdks"
|
|
}
|
|
]
|