Skip to content

Commit b7a62bc

Browse files
authored
Merge f37cf93 into f6cdbf0
2 parents f6cdbf0 + f37cf93 commit b7a62bc

6 files changed

Lines changed: 333 additions & 10 deletions

File tree

.claude/skills/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@
88
!test/**
99
!btrace-perfetto/
1010
!btrace-perfetto/**
11+
!check-code-attribution/
12+
!check-code-attribution/**
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
---
2+
name: check-code-attribution
3+
description: Per-file check of vendored code attribution — headers, THIRD_PARTY_NOTICES.md entries, and license compatibility
4+
allowed-tools: Bash Read Grep Glob
5+
---
6+
7+
# Check Code Attribution
8+
9+
You are reviewing changed files for third-party code attribution compliance in **sentry-java**, an MIT-licensed repository.
10+
11+
## Local runs — discover changed files first
12+
13+
When running locally (not via Warden), determine which files changed on this branch:
14+
15+
```bash
16+
MB=$(git merge-base HEAD origin/main 2>/dev/null || git merge-base HEAD main)
17+
git diff --name-only "${MB}"..HEAD
18+
```
19+
20+
Then run the Quick triage and subsequent checks on **every** file in that list. Warden's `ignorePaths` in `warden.toml` lists the paths to skip — apply the same exclusions locally.
21+
22+
### Warden CLI (optional local parity check)
23+
24+
Warden does **not** use Cursor auth. Before running Warden locally, configure a provider (same model family as `warden.toml`, or override with `-m`):
25+
26+
```bash
27+
# Option A: Anthropic API key (matches CI model in warden.toml)
28+
export WARDEN_ANTHROPIC_API_KEY=sk-ant-... # or: export ANTHROPIC_API_KEY=sk-ant-...
29+
30+
# Option B: Pi OAuth / API key store (~/.pi/agent/auth.json)
31+
npx pi # then run /login and pick Anthropic (or another provider)
32+
33+
# Option C: Different provider for a one-off run
34+
export WARDEN_OPENAI_API_KEY=sk-...
35+
npx @sentry/warden origin/main..HEAD --skill check-code-attribution -m openai/gpt-5.5 -vv
36+
```
37+
38+
```bash
39+
npx @sentry/warden origin/main..HEAD --skill check-code-attribution -vv
40+
```
41+
42+
If you only need attribution review in the IDE, `/check-code-attribution` in Cursor does not require Warden credentials.
43+
44+
When running via Warden, the changed file is already provided — skip branch-wide discovery, but follow **Warden execution** below.
45+
46+
## Warden execution
47+
48+
Warden analyzes one changed file per run (whole-file mode). Complete every Quick triage step — the diff alone is not sufficient.
49+
50+
**Mandatory on every run (do not skip):**
51+
52+
1. `Read` the first 20 lines of the changed file.
53+
2. `Grep` `THIRD_PARTY_NOTICES.md` for the class name (filename without extension, e.g. `ANRWatchDog` for `ANRWatchDog.java`).
54+
3. When Bash is available, compare the merge-base header:
55+
```bash
56+
MB=$(git merge-base HEAD origin/main 2>/dev/null || git merge-base HEAD main)
57+
git show "${MB}:<file-path>" | head -50
58+
```
59+
60+
**Do not dismiss findings because:**
61+
62+
- A `THIRD_PARTY_NOTICES.md` entry exists — file headers are still required; NOTICES does not replace them.
63+
- The diff only removes a header comment block — removed `-` lines with `Copyright`, `Licensed under`, license disclaimers, or vendoring language ("adapted from", etc.) mean attribution was stripped.
64+
- The header says "Adapted from …" but omits copyright holder or license name — flag missing header fields.
65+
66+
For `THIRD_PARTY_NOTICES.md` runs: for every **removed** entry in the diff, use `Read` or `Glob` to confirm whether Scope files still exist with attribution headers. If they do, the entry must not be removed.
67+
68+
## Quick triage
69+
70+
Sentry's own files carry **no** copyright headers — any copyright/license line indicates third-party code. Every file that reaches this skill is in scope — do not skip files based on extension.
71+
72+
If this file is `THIRD_PARTY_NOTICES.md`, go to the THIRD_PARTY_NOTICES section below.
73+
74+
For all other files, perform these checks **before** deciding whether to proceed:
75+
76+
1. **Read the file header** — use the Read tool to read the first 20 lines of the file. Look for vendored-code signals: `Copyright`, `Licensed under`, `SPDX-License-Identifier`, or vendoring language ("adapted from", "backported from", "based on", "copied from", "derived from", "inspired by", "ported from", "translated from", "vendored").
77+
2. **Check THIRD_PARTY_NOTICES.md** — use Grep to search `THIRD_PARTY_NOTICES.md` for the file name without extension (e.g., search for `ANRWatchDog` when reviewing `ANRWatchDog.java`). A match means this is a known vendored file.
78+
3. **Scan the diff** — check for vendored-code signals on both added (`+`) and **removed (`-`)** lines. Removed copyright/license lines ARE signals — they mean attribution is being stripped.
79+
80+
**A signal in ANY of these three sources means this is vendored code — proceed to the vendored source file section.**
81+
82+
A file referenced in THIRD_PARTY_NOTICES.md is ALWAYS vendored, even if its current header has no attribution.
83+
84+
**If none of the three sources have signals, report no findings and stop.**
85+
86+
---
87+
88+
## If this file is `THIRD_PARTY_NOTICES.md`
89+
90+
Validate the changed entries using the diff context:
91+
92+
1. For each added or modified entry, verify it has all required fields: **Source URL**, **License name**, **Copyright**, **Scope** (file paths), and **full license text** in a fenced code block.
93+
2. For each Scope path, verify the file(s) exist (use Glob or Read).
94+
3. Flag new license types — especially copyleft or AGPL.
95+
4. Flag orphaned entries whose Scope files no longer exist.
96+
5. For **removed** entries (lines prefixed with `-` in the diff), use Read to check whether the Scope files still exist and still have attribution headers. If they do, the entry must not be removed.
97+
6. Check **copyright consistency** — the Copyright field must match the copyright line inside the embedded license text. Flag mismatches.
98+
99+
---
100+
101+
## If this is a vendored file
102+
103+
### 1. Check attribution header
104+
105+
The file must have a license header near the top (before the `package` statement in Java/Kotlin files) with:
106+
- Library name or origin
107+
- Copyright year and holder
108+
- License name
109+
- Source URL
110+
111+
Exact wording and comment style may vary. Only flag **missing fields**, not formatting.
112+
113+
Compare the current header (from the Read in Quick triage) against the THIRD_PARTY_NOTICES.md entry. For example, if the NOTICES entry says this file is MIT-licensed by "Salomon BRYS" but the current header has no copyright or license mention, the header was stripped.
114+
115+
When Bash is available (local runs), also compare against the merge-base version for additional context:
116+
```bash
117+
MB=$(git merge-base HEAD origin/main 2>/dev/null || git merge-base HEAD main)
118+
git show "${MB}:<file-path>" | head -50
119+
```
120+
121+
Flag these issues:
122+
- **Header stripped** — file is in NOTICES but current header has no attribution
123+
- **Header truncated** — header is present but missing required fields (e.g., copyright line removed, license disclaimer removed)
124+
- **Header inconsistent** — header contradicts what the NOTICES entry says
125+
- **Diff removes attribution lines**`Copyright`, `Licensed under`, etc. appear on removed lines in the diff
126+
127+
### 2. Check THIRD_PARTY_NOTICES.md entry
128+
129+
From the Grep in Quick triage: if no matching entry exists, flag it as missing. A valid entry needs: Source URL, License name, Copyright, Scope, full license text.
130+
131+
### 3. Check license compatibility
132+
133+
Classify the license per Sentry's Open Source Legal Policy (https://open.sentry.io/licensing/):
134+
135+
| Tier | Examples | Severity |
136+
|-----------------|-------------------------------------------------|----------------------------------------------|
137+
| Permissive | MIT, BSD, Apache 2.0, ISC, CC0, Unlicense, Zlib | Allowed |
138+
| Weak copyleft | LGPL, MPL, EPL, CDDL | **high** — requires review |
139+
| Strong copyleft | GPL, QPL, Sleepycat, OSL | **high** — requires legal review |
140+
| AGPL || **high** — absolute ban, must be removed |
141+
| No license || **high** — assume no permission |
142+
143+
---
144+
145+
## If this is a deleted vendored file
146+
147+
If the diff deletes a file and the removed lines contained attribution headers, check whether `THIRD_PARTY_NOTICES.md` still references it — the entry should be updated or removed.
148+
149+
---
150+
151+
## Severity guide
152+
153+
| Level | Use for |
154+
|------------|-----------------------------------------------------------------------------------------------------------------------------------------------------|
155+
| **high** | 🚨 License violations: AGPL, copyleft, unlicensed, no-license code |
156+
| **medium** | ⚠️ Missing attribution header fields, stripped headers, missing/inconsistent NOTICES entries, deleted/renamed vendored files needing NOTICES update |
157+
| **low** | 👀 Attribution present but could be improved |
158+
159+
Note: Warden relies on these severity levels when deciding whether to make comments on PRs or require changes.
160+
161+
## Output
162+
163+
**No issues → empty response (say nothing).**
164+
165+
Otherwise, number each finding and order by severity (most severe first). Use this exact format — note the escaped period (`\.`) to prevent markdown list collapsing, and the blank line between entries:
166+
167+
```
168+
1\. <severity emoji> **<fully qualified class name>**
169+
<what's wrong and how to fix it — one or two lines>
170+
171+
2\. <severity emoji> **<fully qualified class name>**
172+
<what's wrong and how to fix it — one or two lines>
173+
```
174+
175+
Rules:
176+
- **Escape the period** after the number (`1\.` not `1.`) so markdown does not collapse entries into a tight list.
177+
- Use the emoji from the severity guide (🚨, ⚠️, or 👀) — not the word.
178+
- Use fully qualified Java class names (e.g. `io.sentry.CircularFifoQueue`), not file paths.
179+
- Start the description on a new indented line below the class name.
180+
- Leave an empty line between each numbered finding.
181+
- For license issues, include the policy link.

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,6 @@ spy.log
3737
# Auto-generated by dotagents — do not commit these files.
3838
agents.lock
3939
.agents/.gitignore
40+
41+
# Warden local run logs
42+
.warden/logs/

sentry/src/main/java/io/sentry/CircularFifoQueue.java

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,12 @@
11
/*
22
* Adapted from https://github.com/apache/commons-collections/blob/fce46cdcc6fa33ba9472921d4b3ec3f548d8cbcc/src/main/java/org/apache/commons/collections4/queue/CircularFifoQueue.java
33
*
4-
* Licensed to the Apache Software Foundation (ASF) under one or more
5-
* contributor license agreements. See the NOTICE file distributed with
6-
* this work for additional information regarding copyright ownership.
7-
* The ASF licenses this file to You under the Apache License, Version 2.0
4+
* Copyright 2025 The Apache Software Foundation.
5+
* Licensed under the Apache License, Version 2.0
86
* (the "License"); you may not use this file except in compliance with
97
* the License. You may obtain a copy of the License at
108
*
119
* http://www.apache.org/licenses/LICENSE-2.0
12-
*
13-
* Unless required by applicable law or agreed to in writing, software
14-
* distributed under the License is distributed on an "AS IS" BASIS,
15-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16-
* See the License for the specific language governing permissions and
17-
* limitations under the License.
1810
*/
1911

2012
package io.sentry;
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright 2024 Example JSON Project Contributors.
3+
* SPDX-License-Identifier: GPL
4+
* https://github.com/example-json/compact-writer
5+
*/
6+
package io.sentry.util;
7+
8+
import java.io.IOException;
9+
import java.io.Writer;
10+
11+
/**
12+
* A lightweight JSON writer that produces compact (no whitespace) output.
13+
*/
14+
public final class CompactJsonWriter {
15+
16+
private final Writer out;
17+
private boolean needsComma;
18+
19+
public CompactJsonWriter(Writer out) {
20+
this.out = out;
21+
this.needsComma = false;
22+
}
23+
24+
public CompactJsonWriter beginObject() throws IOException {
25+
out.write('{');
26+
needsComma = false;
27+
return this;
28+
}
29+
30+
public CompactJsonWriter endObject() throws IOException {
31+
out.write('}');
32+
needsComma = true;
33+
return this;
34+
}
35+
36+
public CompactJsonWriter name(String name) throws IOException {
37+
if (needsComma) {
38+
out.write(',');
39+
}
40+
out.write('"');
41+
out.write(name);
42+
out.write("\":");
43+
needsComma = false;
44+
return this;
45+
}
46+
47+
public CompactJsonWriter value(String value) throws IOException {
48+
out.write('"');
49+
out.write(value);
50+
out.write('"');
51+
needsComma = true;
52+
return this;
53+
}
54+
55+
public void flush() throws IOException {
56+
out.flush();
57+
}
58+
}

warden.toml

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
version = 1
2+
3+
[defaults]
4+
model = "anthropic/claude-opus-4-6"
5+
maxTurns = 30
6+
7+
# Attribution findings are policy checks; a second verifier pass often rejects valid
8+
# header/NOTICES mismatches (e.g. "NOTICES still documents it").
9+
[defaults.verification]
10+
enabled = false
11+
12+
# Skip generated/binary files from analysis
13+
[[defaults.chunking.filePatterns]]
14+
pattern = "**/*.api"
15+
mode = "skip"
16+
17+
[[defaults.chunking.filePatterns]]
18+
pattern = "**/gradlew"
19+
mode = "skip"
20+
21+
[[defaults.chunking.filePatterns]]
22+
pattern = "**/gradlew.bat"
23+
mode = "skip"
24+
25+
# Whole-file mode: attribution needs file header + NOTICES cross-check, not isolated hunks
26+
[[defaults.chunking.filePatterns]]
27+
pattern = "**/*.java"
28+
mode = "whole-file"
29+
30+
[[defaults.chunking.filePatterns]]
31+
pattern = "**/*.kt"
32+
mode = "whole-file"
33+
34+
[[defaults.chunking.filePatterns]]
35+
pattern = "THIRD_PARTY_NOTICES.md"
36+
mode = "whole-file"
37+
38+
# Coalesce hunks aggressively for any remaining per-hunk files
39+
[defaults.chunking.coalesce]
40+
enabled = true
41+
maxGapLines = 100
42+
maxChunkSize = 16000
43+
44+
[[skills]]
45+
name = "check-code-attribution"
46+
failOn = "high"
47+
reportOn = "low"
48+
maxTurns = 30
49+
ignorePaths = [
50+
# Infrastructure directories
51+
".claude/**",
52+
".cursor/**",
53+
".github/**",
54+
".gradle/**",
55+
".idea/**",
56+
".mvn/**",
57+
"gradle/**",
58+
# Generated files
59+
"**/generated/**",
60+
"**/ksp/**",
61+
"**/*.aidl",
62+
"**/*.api",
63+
"**/*.interp",
64+
"**/*.tokens",
65+
"**/*.g.kt",
66+
"**/*.pb.java",
67+
"**/databinding/*Binding.java",
68+
"**/grpc/*Grpc.java",
69+
"**/gradlew",
70+
"**/gradlew.bat",
71+
"**/mvnw",
72+
"**/mvnw.cmd",
73+
# Binary files
74+
"**/*.jar",
75+
# Warden infrastructure
76+
".warden/**",
77+
"warden.toml",
78+
]
79+
80+
[[skills.triggers]]
81+
type = "pull_request"
82+
actions = ["opened", "synchronize"]
83+
requestChanges = true # Post a blocking "Request Changes" PR comment when failOn-level findings exist.
84+
failCheck = true # Red X on task in GitHub when failOn-level findings exist (currently non-blocking).
85+
86+
[[skills.triggers]]
87+
type = "local"

0 commit comments

Comments
 (0)