Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .claude/config/baseline.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"_documentation": "at_commit = SHA of origin/main against which pre-existing debt was snapshotted. Findings on lines touched after this SHA are compared against the baseline; entries already present at at_commit are exempted. schema_version bumps when the shape of 'exemptions' changes. Rule IDs here MUST match rule IDs in rules.yaml exactly (agnostic form, no PY-/JV- prefix).",
"created_at": "2026-07-07T20:00:00Z",
"at_commit": "d19427f5f3b9de409d7e4a655d6ce99362c1a17e",
"schema_version": 1,
"exemptions": {
"LIC-01": {
"reason": "Repo uses REUSE.toml aggregate — pre-existing files exempt",
"detector": "REUSE.toml exists with path='**' aggregate annotation",
"scope": "repo",
"files_exempted_glob": "src/**/*.py"
},
"LIC-02": {
"reason": "Same as LIC-01",
"scope": "repo"
}
}
}
13 changes: 13 additions & 0 deletions .claude/config/module-aliases.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Module name aliases across the Python and Java cloud SDKs.
#
# Consumed by .claude/scripts/check-bdd.sh to resolve the sibling repo's
# feature-file path when the same module has DIFFERENT names in each SDK.
#
# When a module has the SAME name in both SDKs (the common case), no entry
# is needed — check-bdd.sh discovers modules dynamically from `src/` and
# falls back to the identical name in the sibling repo.
#
# Add an entry ONLY when both SDKs ship the same module under different
# names, and add it in the SAME PR that introduces the divergent-name module.

aliases: []
131 changes: 131 additions & 0 deletions .claude/config/rules.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
version: 2

# =====================================================
# rules.yaml — SDK Module Review rule catalog
# All rules default to their tier here; may be overridden per-repo.
# BLOCK_LOCKED rules cannot be softened or suppressed.
# =====================================================

baseline:
file: .claude/config/baseline.json
regenerate_with: orchestrate.sh --baseline

tiers:
SHADOW: { posts: false, blocks: false }
FLAG: { posts: true, blocks: false }
BLOCK: { posts: true, blocks: true }
BLOCK_LOCKED: { posts: true, blocks: true, suppressible: false, downgradable: false }

rules:
# -------- BREAKING FAMILY (all locked) --------
BREAKING-01: { tier: BLOCK_LOCKED }
BREAKING-02: { tier: BLOCK_LOCKED }
BREAKING-03: { tier: FLAG }
BREAKING-04: { tier: FLAG }

# -------- SECRETS (all locked) --------
SEC-01: { tier: BLOCK_LOCKED }
SEC-02: { tier: BLOCK_LOCKED }
SEC-03: { tier: BLOCK_LOCKED }
SEC-04: { tier: BLOCK_LOCKED }
SEC-05: { tier: BLOCK_LOCKED }
SEC-06: { tier: BLOCK_LOCKED }
SEC-07: { tier: BLOCK_LOCKED }
SEC-08: { tier: BLOCK_LOCKED }
SEC-10: { tier: BLOCK_LOCKED }

# -------- LICENSE --------
LIC-01: { tier: BLOCK, predicates: { not: reuse_toml_aggregate_present, diff_scope: file_in_added_set } }
LIC-02: { tier: BLOCK, predicates: { not: reuse_toml_aggregate_present, diff_scope: file_in_added_set } }

# -------- DISCLOSURE --------
DIS-01: { tier: BLOCK, profile: { public: BLOCK, internal: FLAG } }
DIS-02: { tier: BLOCK, profile: { public: BLOCK, internal: PASS } }
DIS-06: { tier: BLOCK_LOCKED }
DIS-07: { tier: BLOCK }
DIS-08: { tier: SHADOW }

# -------- HARDCODE --------
HC-01: { tier: BLOCK }
HC-02: { tier: BLOCK }
HC-03: { tier: BLOCK_LOCKED }
HC-04: { tier: FLAG }
HC-06: { tier: FLAG }

# -------- TELEMETRY --------
PY-TEL-02: { tier: BLOCK }
JV-TEL-02: { tier: BLOCK }
PY-TEL-06: { tier: BLOCK, predicates: { diff_scope: new_decorator_added } }
PY-TEL-08: { tier: SHADOW }

# -------- DOCS --------
DC-01: { tier: BLOCK }
DC-02: { tier: BLOCK }
DC-11: { tier: BLOCK }
DC-12: { tier: BLOCK }
DC-13: { tier: BLOCK }
DC-14: { tier: BLOCK }

# -------- BDD --------
BDD-01: { tier: BLOCK }
BDD-02: { tier: BLOCK }

# -------- PATTERNS --------
PY-PT-01: { tier: BLOCK, predicates: { module_shape: client } }
JV-PT-01: { tier: BLOCK, predicates: { module_shape: client } }
PY-PT-03: { tier: FLAG }
PY-PT-04: { tier: FLAG }
PY-PT-08: { tier: FLAG }
JV-PT-05: { tier: FLAG }

# -------- VERSIONING --------
VER-01: { tier: BLOCK, predicates: { commit_types_or_breaking: [feat, feat!, breaking] } }
VER-07: { tier: FLAG }
PY-VER-08: { tier: SHADOW }
PY-VER-09: { tier: SHADOW }

# -------- ERRORS/LOGGING --------
EL-01: { tier: FLAG }
EL-02: { tier: FLAG }
EL-04: { tier: BLOCK }
PY-EL-11: { tier: SHADOW }

# -------- TESTING --------
TD-01: { tier: FLAG }
TD-10: { tier: BLOCK }
TD-checkbox: { tier: FLAG }

# -------- HTTP --------
HTTP-01: { tier: FLAG, predicates: { diff_scope: line_in_added_set } }

# -------- CONCURRENCY --------
CC-01: { tier: FLAG }
JV-CC-05: { tier: SHADOW }

# -------- DEPS --------
DEP-03: { tier: FLAG }
DEP-04: { tier: BLOCK_LOCKED }

# -------- COMMITS --------
COM-01: { tier: BLOCK }

# -------- DELETION --------
DEL-01: { tier: BLOCK }

# -------- CONSTANTS --------
PY-CON-01: { tier: FLAG }

# -------- BINDING --------
BND-02: { tier: BLOCK_LOCKED }
BND-04: { tier: BLOCK }
BND-05: { tier: BLOCK_LOCKED }

# -------- QUALITY GATE --------
QG-01: { tier: FLAG }
QG-02: { tier: FLAG }

# -------- PR SIZE (advisory, SHADOW during rollout) --------
PR-SIZE-01: { tier: SHADOW }
PR-SIZE-02: { tier: SHADOW }
PR-SIZE-03: { tier: SHADOW }
PR-SIZE-05: { tier: SHADOW }
119 changes: 119 additions & 0 deletions .claude/scripts/lib/apply-suppression.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
#!/usr/bin/env bash
# lib/apply-suppression.sh — filter findings against suppression comments in source.
# Called after a check produces its JSON report; reads suppression tuples and drops findings.
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Source suppression.sh so we don't fork bash+jq per finding.
# shellcheck source=./suppression.sh
source "$SCRIPT_DIR/suppression.sh"

# collect_suppressions_for_files <file1> <file2> ...
# Emits "<file>:<line>:<rule>" tuples for all suppression comments found.
collect_suppressions_for_files() {
for f in "$@"; do
[ -f "$f" ] || continue
parse_line_suppressions "$f" 2>/dev/null || true
parse_file_suppressions "$f" 2>/dev/null || true
done
}

# apply_to_report <report_json> <suppressions_file> → prints filtered report
apply_to_report() {
local report="$1" supp_file="$2"
local suppressed_count=0
local input; input=$(cat "$report")
local n; n=$(echo "$input" | jq '.findings | length')
if [ "$n" -eq 0 ]; then
echo "$input"; return
fi

# Extract all (rule, file, line) tuples in a single jq call — one fork total
# instead of one per finding.
local tuples; tuples=$(echo "$input" | jq -r '.findings[] | "\(.rule)|\(.file)|\(.line)"')

local keep_flags=()
local i=0
while IFS='|' read -r rule file line; do
if [ "$(is_suppressed "$rule" "$file" "$line" "$supp_file")" = "true" ]; then
keep_flags+=("0")
suppressed_count=$((suppressed_count + 1))
else
keep_flags+=("1")
fi
i=$((i + 1))
done <<< "$tuples"

# Build a JSON array of booleans and use jq to filter
local keep_json
if [ "${#keep_flags[@]}" -eq 0 ]; then
keep_json="[]"
else
keep_json=$(printf '%s\n' "${keep_flags[@]}" | jq -s 'map(. == "1")')
fi

echo "$input" | jq \
--argjson keep "$keep_json" \
--argjson suppressed "$suppressed_count" \
'.findings = [.findings[] as $f | ($f | . as $x | (input_line_number - 1) as $i | select($keep[$i]))] // .findings
| .findings = [range(.findings | length) as $i | .findings[$i] | select($keep[$i])]
| .summary.suppressed_count = $suppressed' 2>/dev/null || {
# Fallback: simple length-preserving filter
echo "$input" | jq \
--argjson keep "$keep_json" \
--argjson suppressed "$suppressed_count" \
'(.findings | length) as $n
| .findings = [range($n) | . as $i | (($keep[$i] // true) | if . then $i else null end) | select(. != null)] as $indices
| .findings = [$indices[] as $i | .findings[$i]]
| .summary.suppressed_count = $suppressed'
}
}

# Simpler variant: build kept-list explicitly (safe fallback)
apply_to_report_v2() {
local report="$1" supp_file="$2"
local suppressed_count=0
local input; input=$(cat "$report")
local n; n=$(echo "$input" | jq '.findings | length')
if [ "$n" -eq 0 ]; then
echo "$input"; return
fi

local kept=()
local i=0
while [ "$i" -lt "$n" ]; do
local finding rule file line
finding=$(echo "$input" | jq -c ".findings[$i]")
rule=$(echo "$finding" | jq -r '.rule')
file=$(echo "$finding" | jq -r '.file')
line=$(echo "$finding" | jq -r '.line')

if [ "$(is_suppressed "$rule" "$file" "$line" "$supp_file")" = "true" ]; then
suppressed_count=$((suppressed_count + 1))
else
kept+=("$finding")
fi
i=$((i + 1))
done

local kept_json
if [ "${#kept[@]}" -eq 0 ]; then
kept_json="[]"
else
kept_json=$(printf '%s\n' "${kept[@]}" | jq -s -c '.')
fi

echo "$input" | jq \
--argjson kept "$kept_json" \
--argjson suppressed "$suppressed_count" \
'.findings = $kept | .summary.suppressed_count = $suppressed'
}

if [ "${BASH_SOURCE[0]}" = "${0}" ]; then
cmd="${1:-}"; shift || true
case "$cmd" in
collect) collect_suppressions_for_files "$@" ;;
apply) apply_to_report_v2 "$@" ;; # simpler + safer
*) echo "Usage: apply-suppression.sh {collect|apply} args" >&2; exit 2 ;;
esac
fi
Loading
Loading