Spam Scanner
- Status: active trust-boundary and operator reference
- Owner: ClawSweeper spam-intake maintainers
- Source of truth:
.github/workflows/spam-comment-intake.yml, - Last verified:
openclaw/clawsweeper@647503ec44b8e777dd172adf974a945367da0d19 - Update when: event intake, candidate filtering, workflow inputs, model routing,
.github/workflows/spam-scanner.yml, and src/repair/spam-*
audit records, or mutation policy changes
Read when changing ClawSweeper comment spam detection, audit records, or org blocking policy.
The spam scanner is an audit-only intake lane. It watches new GitHub comments, applies cheap deterministic filters, sends likely candidates to the internal model, and writes durable audit records. It does not block users, hide comments, label items, reply, or mutate target repositories.
This lane is deliberately separate from the weekly issue/PR review cadence. Spam cost scales with new comments, not with the total open issue count.
Default behavior:
- target repo:
openclaw/openclaw - model:
internal - schedule: hourly cron at minute 17
- catch-up window: 3 hours
- cap: 100 prioritized comments, selected from a bounded recent over-fetch
- dedupe: comment kind, id, and
updated_at - action:
none
The first live audit-only run on 2026-05-11 scanned 50 recent comments, found no model candidates, and published results/spam-scanner-latest.json.
#Workflow
The workflow is .github/workflows/spam-scanner.yml.
Inputs:
target_repo: repository to scanlookback_minutes: fallback window for scheduled/manual catch-upsince: optional explicit ISO lower boundmax_comments: cap for prioritized scanned commentscomment_ids: exact issue comment replayreview_comment_ids: exact PR review comment replayforce_reprocess: ignore the processed-version ledger for replay
The workflow checks out the live ClawSweeper repo plus hydrated generated state, creates a target-read GitHub App token, runs pnpm run repair:spam-scan, and publishes the resulting files through repair:publish-main.
The current scheduled workflow is active, but GitHub cron delivery can lag or drop a newly added workflow's first tick. Manual dispatch is the immediate verification path.
New or edited issue and pull-request review comments also have an event-driven path. The GitHub activity workflow runs the deterministic spam intake filter; target repositories may forward the same normalized activity through clawsweeper_spam_comment_intake. Only comments with a qualifying deterministic signal dispatch clawsweeper_spam_comment, which invokes spam-scanner.yml for that exact comment with max_comments=1. The hourly scan remains the bounded catch-up path.
#Comment Sources
The scanner reads recent comments newest-first and over-fetches a bounded window before applying the max_comments cap. Broad scans prioritize deterministic spam-shaped candidates first, then fill the rest of the scan sample with normal recent comments so stale audit cleanup still runs.
The scanner reads:
- issue and PR conversation comments from
- PR review/diff comments from
repos/<repo>/pulls/comments?since=... - exact issue comments from
repos/<repo>/issues/comments/<id> - exact review comments from
repos/<repo>/pulls/comments/<id>
repos/<repo>/issues/comments?since=...
It also hydrates GraphQL minimization metadata for scanned comment node ids. If GitHub has already minimized a comment as spam or abuse, that becomes a deterministic signal.
Protected authors are skipped before model spend:
OWNERMEMBERCOLLABORATORCONTRIBUTOR- GitHub bot accounts
- configured trusted bots
Outputs in openclaw/clawsweeper-state:
results/spam-scanner-latest.json: latest run summaryresults/spam-scanner.json: durable processed comment-version ledgerresults/spam-audit/<repo-slug>/<kind>-<comment-id>.json: per-comment audit
Audit records include the comment URL, author association, body hash, short body excerpt, deterministic signals, model, model result, and action: none. When a reprocessed comment no longer qualifies as a candidate, its stale per-comment audit file is removed from generated state.
#Detection
Deterministic signals are intentionally simple and cheap:
- GitHub minimized reason contains
spamorabuse - known URL shortener
- service-pitch wording such as web scraping, data extraction, flash sale, or
- priced short service pitch
sample work
GitHub, raw GitHub content, and localhost links are treated as project context, not spam links. Maintainer, collaborator, member, contributor, and trusted bot comments are not sent to the cheap model.
Multiple non-GitHub external links and outside-author external links are recorded as supporting signals only. They do not make a comment a model candidate unless a stronger spam-shaped signal is also present.
Only comments with deterministic signals are sent to the internal model. The model returns strict JSON:
spam_signal:none,low,medium, orhighconfidence: 0-1reasons: short stringsshould_investigate: scheduler hint only
The model result is not an enforcement decision. In audit-only mode it only decides whether an audit record is worth writing and whether a later Codex investigation lane should prioritize the comment.
If the configured model is missing or the model call fails, the workflow still publishes deterministic audit records with model_error instead of failing the spam lane. Model failures must not block monitoring.
#Safety
Current safety properties:
- no org block endpoint is called
- no comment hiding/deletion endpoint is called
- no target labels or replies are written
- full comment bodies are not required in durable state; records store a body
- processed comment versions are deduped, so edits are reviewed but unchanged
hash plus a short excerpt
comments are not reprocessed forever
Future blocking must be a separate apply step. It needs explicit org permission Blocking users: write, maintainer/collaborator allowlisting, and audit records that prove the exact comment and reason for each block.
#Operations
Run manually:
pnpm run build:repair
OPENAI_API_KEY=... pnpm run repair:spam-scan -- \
--write-report \
--repo openclaw/openclaw \
--lookback-minutes 180 \
--max-comments 100
Use exact comment ids for event replays:
pnpm run repair:spam-scan -- --write-report --repo openclaw/openclaw --comment-ids 123
pnpm run repair:spam-scan -- --write-report --repo openclaw/openclaw --review-comment-ids 456
Inspect latest generated state:
git -C ../clawsweeper-state fetch origin state
git -C ../clawsweeper-state show origin/state:results/spam-scanner-latest.json
git -C ../clawsweeper-state ls-tree -r --name-only origin/state results/spam-audit
Manual workflow dispatch:
gh workflow run spam-scanner.yml \
--repo openclaw/clawsweeper \
--ref main \
-f target_repo=openclaw/openclaw \
-f lookback_minutes=180 \
-f max_comments=100 \
-f force_reprocess=false