@intentsolutionsio/contributing-clanker 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/README.md +173 -0
  2. package/hooks/.gitkeep +0 -0
  3. package/hooks/install.sh +115 -0
  4. package/hooks/uninstall.sh +70 -0
  5. package/package.json +42 -0
  6. package/skills/contribute/SKILL.md +457 -0
  7. package/skills/contribute/agents/draft-writer.md +110 -0
  8. package/skills/contribute/agents/repo-analyzer.md +68 -0
  9. package/skills/contribute/agents/researcher.md +246 -0
  10. package/skills/contribute/agents/scout.md +182 -0
  11. package/skills/contribute/agents/test-runner.md +70 -0
  12. package/skills/contribute/assets/claim-template.md +22 -0
  13. package/skills/contribute/assets/evidence-template.md +32 -0
  14. package/skills/contribute/assets/pr-template.md +49 -0
  15. package/skills/contribute/references/candidate-file-format.md +259 -0
  16. package/skills/contribute/references/workflow-guide.md +153 -0
  17. package/skills/contribute/scripts/audit-overrides.sh +180 -0
  18. package/skills/contribute/scripts/catalog-coverage.sh +99 -0
  19. package/skills/contribute/scripts/gate-runner.sh +191 -0
  20. package/skills/contribute/scripts/gates/a01-already-assigned.sh +24 -0
  21. package/skills/contribute/scripts/gates/a02-already-shipped.sh +31 -0
  22. package/skills/contribute/scripts/gates/a03-duplicate-flagged.sh +22 -0
  23. package/skills/contribute/scripts/gates/a04-issue-age.sh +80 -0
  24. package/skills/contribute/scripts/gates/a05-issue-still-open.sh +33 -0
  25. package/skills/contribute/scripts/gates/a06-claim-etiquette-required.sh +36 -0
  26. package/skills/contribute/scripts/gates/a09-mention-routing.sh +63 -0
  27. package/skills/contribute/scripts/gates/b01-base-branch.sh +29 -0
  28. package/skills/contribute/scripts/gates/b02-branch-naming.sh +34 -0
  29. package/skills/contribute/scripts/gates/b03-clone-fresh.sh +33 -0
  30. package/skills/contribute/scripts/gates/b05-dco-signoff.sh +50 -0
  31. package/skills/contribute/scripts/gates/b06-commit-format.sh +44 -0
  32. package/skills/contribute/scripts/gates/b07-scope-files.sh +68 -0
  33. package/skills/contribute/scripts/gates/b12-new-deps.sh +40 -0
  34. package/skills/contribute/scripts/gates/b14-local-checks.sh +55 -0
  35. package/skills/contribute/scripts/gates/b16-local-check-allowlist.sh +32 -0
  36. package/skills/contribute/scripts/gates/c01-draft-first.sh +23 -0
  37. package/skills/contribute/scripts/gates/c02-pr-title-format.sh +36 -0
  38. package/skills/contribute/scripts/gates/c03-pr-body-sections.sh +58 -0
  39. package/skills/contribute/scripts/gates/c04-ui-screenshots.sh +38 -0
  40. package/skills/contribute/scripts/gates/c05-test-evidence.sh +31 -0
  41. package/skills/contribute/scripts/gates/c07-coauthor-banned.sh +30 -0
  42. package/skills/contribute/scripts/gates/c09-issue-link.sh +31 -0
  43. package/skills/contribute/scripts/gates/c11-no-force-push.sh +32 -0
  44. package/skills/contribute/scripts/gates/c12-ci-green.sh +29 -0
  45. package/skills/contribute/scripts/gates/c13-bots-passed.sh +62 -0
  46. package/skills/contribute/scripts/gates/c16-no-self-merge.sh +24 -0
  47. package/skills/contribute/scripts/gates/c19-body-claim-vs-diff.sh +64 -0
  48. package/skills/contribute/scripts/gates/d02-no-ai-bug-reports.sh +48 -0
  49. package/skills/contribute/scripts/gates/d03-no-ai-pr-reviews.sh +42 -0
  50. package/skills/contribute/scripts/gates/d05-no-reopen.sh +25 -0
  51. package/skills/contribute/scripts/gates/e02-ai-strike-track.sh +57 -0
  52. package/skills/contribute/scripts/gates/e04-fork-target.sh +39 -0
  53. package/skills/contribute/scripts/gates/f01-license-compat.sh +92 -0
  54. package/skills/contribute/scripts/gates/f03-fixtures-clean.sh +30 -0
  55. package/skills/contribute/scripts/gates/f04-override-disclosure.sh +49 -0
  56. package/skills/contribute/scripts/gates/g01-no-vendored-edits.sh +52 -0
  57. package/skills/contribute/scripts/gates/g02-protected-paths.sh +30 -0
  58. package/skills/contribute/scripts/gates/g03-no-changelog-edits.sh +37 -0
  59. package/skills/contribute/scripts/gates/g04-no-version-bump.sh +36 -0
  60. package/skills/contribute/scripts/gates/g06-override-rate-limit.sh +31 -0
  61. package/skills/contribute/scripts/gates/lib/preamble.sh +105 -0
  62. package/skills/contribute/scripts/lint-candidate.sh +149 -0
  63. package/skills/contribute/scripts/researcher-build.sh +456 -0
  64. package/skills/contribute/scripts/test-known-traps.sh +142 -0
  65. package/skills/contribute/scripts/test-override-audit.sh +102 -0
  66. package/skills/contribute/scripts/test-plug-in.sh +113 -0
  67. package/skills/contribute/scripts/test-scout-refresh.sh +157 -0
  68. package/skills/contribute/scripts/test-stale-dossier-refresh.sh +96 -0
  69. package/skills/contribute/scripts/transition.sh +260 -0
@@ -0,0 +1,259 @@
1
+ # Candidate file format
2
+
3
+ Canonical specification for the markdown candidate files at
4
+ `~/.contribute-system/candidates/<owner>__<repo>__issue<N>.md`.
5
+
6
+ Each candidate file is the per-issue tracker. The frontmatter is the queryable
7
+ layer; the body holds drafts and structured sections that gates read at
8
+ transition time. **Manual edits in the body survive refresh** — only the
9
+ frontmatter is auto-managed.
10
+
11
+ ---
12
+
13
+ ## Filename
14
+
15
+ ```
16
+ ~/.contribute-system/candidates/<owner>__<repo>__issue<N>.md
17
+ ```
18
+
19
+ - `<owner>` and `<repo>` are joined by **double** underscore (`__`)
20
+ - `<N>` is the upstream issue number (or PR number for backfilled candidates that have no linked issue)
21
+ - One file per upstream issue; never multiple files for the same issue
22
+
23
+ ---
24
+
25
+ ## Frontmatter (YAML, between two `---` lines)
26
+
27
+ The frontmatter is canonical state. Every gate reads from it. `transition.sh`
28
+ writes to it atomically when state moves.
29
+
30
+ | Field | Required | Type | Read by | Notes |
31
+ |---|---|---|---|---|
32
+ | `discovered_at` | yes | ISO8601 UTC | scout/researcher | when scout first wrote the candidate |
33
+ | `repo` | yes | `<owner>/<repo>` | every gate | upstream repo |
34
+ | `issue_number` | yes | int | A-phase gates | upstream issue # |
35
+ | `issue_url` | yes | URL | (info) | direct link |
36
+ | `star_tier` | yes | `niche` / `emerging` / `established` / `mainstream` | scout ranking | from `star_count` |
37
+ | `star_count` | yes | int | scout ranking | |
38
+ | `repo_lang` | yes | string | scout filter | primary language |
39
+ | `competing_prs` | yes | int | A02 gate | scout-detected open PRs claiming this issue |
40
+ | `primary_label` | yes | string | scout heuristic | upstream's primary label |
41
+ | `scout_score` | yes | float (0–1) | scout ranking | momentum signal |
42
+ | `status` | yes | enum (see below) | every gate + transition.sh | lifecycle state |
43
+ | `last_refreshed` | yes | ISO8601 UTC | researcher | when scout/researcher last touched |
44
+ | `merge_probability_pct` | optional | int (0–100) | scout ranking | empirical merge rate at this repo |
45
+ | `research_path` | optional | absolute path | every gate that reads dossier | path to per-repo dossier; auto-resolved if empty |
46
+ | `pr_number` | optional | int | reconciliation | set after PR opens |
47
+ | `pr_url` | optional | URL | reconciliation | set after PR opens |
48
+ | `branch` | optional | string | B/C-phase gates | local feature branch name |
49
+ | `disabled_gates` | optional | array of gate IDs | gate-runner | per-candidate gate disables (escape hatch) |
50
+
51
+ ### `status` enum
52
+
53
+ ```
54
+ open → discovered, not yet vetted
55
+ shortlist → vetted, queued for next claim
56
+ claimed → posted claim comment, waiting for "go ahead" / no objection
57
+ working → actively coding (local clone exists, diff in progress)
58
+ submitted → PR or Design Issue opened upstream
59
+ merged → upstream merged the PR
60
+ dropped → closed without merge (failure log entry created in dossier)
61
+ ```
62
+
63
+ `transition.sh` enforces the legal transitions and atomically updates the
64
+ field on success.
65
+
66
+ ---
67
+
68
+ ## Body sections
69
+
70
+ The body holds drafts + evidence. Sections are populated at different lifecycle
71
+ stages by different agents. Manual edits survive refresh.
72
+
73
+ ### Section inventory
74
+
75
+ Order, owner, and which gates read each section:
76
+
77
+ | Section | Populated at | Owner | Read by gates |
78
+ |---|---|---|---|
79
+ | `# <repo>#<N> — <title>` | discovery | scout | (display only) |
80
+ | `## Why scout flagged this` | discovery | scout | (info; helps reviewer) |
81
+ | `## Scope` | qualification | user / @repo-analyzer | B07 (must enumerate planned scope) |
82
+ | `## Files to touch` | qualification | user / @repo-analyzer | B07 (must list specific files) |
83
+ | `## Claim comment draft` | claim | @draft-writer | A06 (etiquette comment must reference dossier excerpts) |
84
+ | `## Issue body draft` | submit (Design Issue path) | @draft-writer | C03, C09 (issue body sections + issue link) |
85
+ | `## PR title` | submit | @draft-writer | C02 (regex match against `pr_title_regex` in dossier) |
86
+ | `## PR body` | submit | @draft-writer | C03, C05, C09, C19 (body sections + test evidence + issue link + claim-vs-diff) |
87
+ | `## Test results` | working | @test-runner | C05 (concrete evidence required pre-submit) |
88
+ | `## Review draft` | review (post-merge sometimes) | @draft-writer | D02, D03 (no-AI-reviews-without-disclosure) |
89
+ | `## Safety override disclosure` | submit | user (only if --override-gate used) | F04 (mandatory if any gate was overridden) |
90
+
91
+ ### Required sections by lifecycle stage
92
+
93
+ | Status | Sections that MUST exist | Sections that MAY exist |
94
+ |---|---|---|
95
+ | `open` | none beyond H1 | `## Why scout flagged this` |
96
+ | `shortlist` | `## Scope`, `## Files to touch` | `## Claim comment draft` |
97
+ | `claimed` | `## Claim comment draft` | `## Scope`, `## Files to touch` |
98
+ | `working` | scope + files + claim draft | `## Test results` (early evidence) |
99
+ | `submitted` | `## PR title`, `## PR body`, `## Test results` | `## Issue body draft` (if Design Issue path), `## Safety override disclosure` (if any overrides) |
100
+ | `merged` | everything from `submitted` | none added |
101
+ | `dropped` | everything that existed | (failure-log entry in dossier referenced here) |
102
+
103
+ Backfilled candidates (created retroactively for already-open PRs) may
104
+ legitimately skip the early-stage sections — their gates will SKIP rather
105
+ than BLOCK, which is correct behavior.
106
+
107
+ ---
108
+
109
+ ## Worked example (a `submitted`-state candidate)
110
+
111
+ ```markdown
112
+ ---
113
+ discovered_at: 2026-04-15T09:00:00Z
114
+ repo: example-org/example-repo
115
+ issue_number: 42
116
+ issue_url: https://github.com/example-org/example-repo/issues/42
117
+ star_tier: mainstream
118
+ star_count: 15000
119
+ repo_lang: TypeScript
120
+ competing_prs: 0
121
+ primary_label: bug
122
+ scout_score: 0.82
123
+ status: submitted
124
+ pr_number: 137
125
+ pr_url: https://github.com/example-org/example-repo/pull/137
126
+ branch: fix/42-null-deref
127
+ research_path: /home/jeremy/.contribute-system/research/example-org__example-repo.md
128
+ last_refreshed: 2026-04-20T14:30:00Z
129
+ ---
130
+
131
+ # example-org/example-repo #42 — null deref in formatter
132
+
133
+ ## Why scout flagged this
134
+
135
+ Open >7d, primary_label=bug, momentum_score: 0.82 (3 maintainer comments
136
+ in last week, no competing PRs).
137
+
138
+ ## Scope
139
+
140
+ Replace the unchecked `.format()` call in `src/format.ts` with a guarded
141
+ variant that returns `null` instead of throwing.
142
+
143
+ ## Files to touch
144
+
145
+ - `src/format.ts` (1-2 lines)
146
+ - `src/__tests__/format.test.ts` (add 2 cases for null + undefined)
147
+
148
+ ## Claim comment draft
149
+
150
+ Hi! I'd like to take this. Plan: replace `.format()` with a guarded
151
+ variant that returns `null` for nullish input, mirroring the pattern in
152
+ `src/parse.ts`. ETA: end of week. Will open a Design Issue first per your
153
+ CONTRIBUTING.md.
154
+
155
+ ## PR title
156
+
157
+ fix: guard against null input in formatter (#42)
158
+
159
+ ## PR body
160
+
161
+ ## Problem
162
+
163
+ `format()` throws on `null` / `undefined` input where callers expect a
164
+ nullish-passthrough.
165
+
166
+ ## Proposed solution
167
+
168
+ Add a guard at the top: if input is nullish, return `null`. Matches the
169
+ pattern already used in `src/parse.ts`.
170
+
171
+ ## Test results
172
+
173
+ Added 2 unit tests covering null + undefined input. Full suite green.
174
+
175
+ Closes #42.
176
+
177
+ ## Test results
178
+
179
+ ```
180
+ PASS src/__tests__/format.test.ts (2 added)
181
+ Tests: 78 passed, 78 total
182
+ Duration: 1.2s
183
+ ```
184
+ ```
185
+
186
+ ---
187
+
188
+ ## How agents populate sections
189
+
190
+ | Agent | Populates | When |
191
+ |---|---|---|
192
+ | `@scout` | `discovered_at`, frontmatter, `# <title>`, `## Why scout flagged this` | first discovery |
193
+ | `@researcher` | `research_path` link only — body untouched | when building/refreshing the dossier |
194
+ | `@repo-analyzer` | `## Scope`, `## Files to touch` | qualification |
195
+ | `@draft-writer` | `## Claim comment draft`, `## PR title`, `## PR body`, `## Issue body draft`, `## Review draft` | claim / submit / review |
196
+ | `@test-runner` | `## Test results` | working |
197
+ | user | any section | any time — manual edits survive refresh |
198
+
199
+ `transition.sh` updates frontmatter only (status, pr_number, last_refreshed).
200
+ It never writes to the body.
201
+
202
+ ---
203
+
204
+ ## What backfilling looks like
205
+
206
+ When a PR exists upstream but no candidate file ever tracked it, you can
207
+ backfill one with frontmatter + minimal body. Most B/C-phase gates will
208
+ SKIP rather than BLOCK because the body sections aren't present — that's
209
+ correct behavior. Once the PR merges, the candidate becomes a `merged`
210
+ record for the failure log / institutional memory.
211
+
212
+ A backfill template:
213
+
214
+ ```markdown
215
+ ---
216
+ discovered_at: <PR createdAt>
217
+ repo: <owner>/<repo>
218
+ issue_number: <linked issue # or PR #>
219
+ issue_url: <PR url>
220
+ star_tier: <derived from star_count>
221
+ star_count: <gh repo view ... .stargazerCount>
222
+ repo_lang: <gh repo view ... .primaryLanguage.name>
223
+ competing_prs: 0
224
+ primary_label: backfill
225
+ scout_score: 0.7
226
+ status: submitted
227
+ pr_number: <PR #>
228
+ pr_url: <PR url>
229
+ branch: <gh pr view ... .headRefName>
230
+ research_path: <absolute dossier path>
231
+ backfilled_at: <now ISO8601>
232
+ last_refreshed: <now ISO8601>
233
+ ---
234
+
235
+ # <repo> #<PR#> — <title>
236
+
237
+ ## Why this candidate exists
238
+
239
+ Backfilled into the lifecycle workflow on <date>. Pre-dates the candidate
240
+ system being wired up; created so reconciliation can pick up state changes
241
+ when the PR merges or closes.
242
+
243
+ ## Source
244
+
245
+ - PR: <url>
246
+ - Branch: `<branch>`
247
+ - Linked issue: <issue # or "(none)">
248
+ ```
249
+
250
+ ---
251
+
252
+ ## Cross-references
253
+
254
+ - Lifecycle workflow: `000-docs/006-AT-SPEC-lifecycle-workflow.md`
255
+ - Gate inventory: `000-docs/005-AT-SPEC-gate-inventory.md`
256
+ - `agents/draft-writer.md` — agent that populates `## PR body` etc.
257
+ - `agents/test-runner.md` — agent that populates `## Test results`
258
+ - `scripts/transition.sh` — updates frontmatter atomically
259
+ - `scripts/gates/lib/preamble.sh` — gates' shared helpers for reading sections
@@ -0,0 +1,153 @@
1
+ # Workflow Guide — long-form
2
+
3
+ ## Table of Contents
4
+
5
+ 1. [Daily rhythm](#daily-rhythm)
6
+ 2. [The 5-step workflow in depth](#the-5-step-workflow-in-depth)
7
+ 3. [Project-specific gotchas](#project-specific-gotchas)
8
+ 4. [Tracker hygiene](#tracker-hygiene)
9
+ 5. [Money & payment programs](#money--payment-programs)
10
+
11
+ ---
12
+
13
+ ## Daily rhythm
14
+
15
+ The first thing the skill does on invoke is Step 0 (refresh state). After that, the natural conversation follows whatever the user is actually trying to do — review status, scout new work, qualify a candidate, draft a submission. The 5-step DISCOVER → QUALIFY → CLAIM → WORK → SUBMIT framing is the long form; most days touch only one or two steps.
16
+
17
+ | Time of day | Typical action |
18
+ |-------------|----------------|
19
+ | Morning | Step 0 (state summary) → reconcile any drift |
20
+ | Mid-morning | Step 1 (discover) if queue is thin |
21
+ | Workblocks | Step 4 (work) on whatever's claimed |
22
+ | End of day | Step 5 (draft submission) for tomorrow's review |
23
+
24
+ ## The 5-step workflow in depth
25
+
26
+ ### 1. DISCOVER — finding paid issues
27
+
28
+ Three sources, in priority order:
29
+
30
+ **Tracker (already-curated)** — `bounties` table rows where status is `open`, `qualified`, or `drafting`. These have already passed a competition / staleness check and represent the user's vetted queue.
31
+
32
+ **Live GitHub label search** — anything labeled `bounty`, `💰 Bounty`, or repo-specific bounty labels in the tracked orgs. New every day; needs qualifying before it's actionable.
33
+
34
+ **Algora boards** — browse-only via web. Their public API needs auth and the rate limits make it not worth scripting. The boards to know:
35
+
36
+ | Org | Stack | Reward range |
37
+ |-----|-------|--------------|
38
+ | mediar-ai (screenpipe) | Rust + TS/Bun | $25–500 |
39
+ | tscircuit | React/PCB | $25–150 |
40
+ | golemcloud | Rust/WASM | up to $3.5K |
41
+ | calcom | TS/Next.js | $20–500 |
42
+ | twentyhq | TS | varies |
43
+ | formbricks | TS | varies |
44
+ | trigger-dev | TS | varies |
45
+
46
+ **Gumroad** — single tracking issue at `antiwork/gumroad#1055`. Lists all active SCSS-to-Tailwind file conversions. $1.5K per file.
47
+
48
+ ### 2. QUALIFY — eligibility + competition + responsiveness
49
+
50
+ Three things to check, fast:
51
+
52
+ **Competition** — `gh pr list --repo <owner>/<repo> --search "<issue#>" --state=all`. If 2+ open PRs already, skip. If one open PR is stale (>14 days no activity), the issue may free up — note and revisit.
53
+
54
+ **Maintainer responsiveness** — `gh api repos/<owner>/<repo>/commits` for recent activity dates. No commit in 60 days = likely abandoned. Avg PR merge lag from `gh pr list --state=closed --limit 10 --json mergedAt,createdAt` gives a sense of how long submissions sit.
55
+
56
+ **Friction** — does the repo require a CLA? Does CONTRIBUTING.md ask for design docs / RFCs before code? Is there a PR template that asks for AI disclosure? All of these are fine, but they affect the time-to-merge math.
57
+
58
+ ### 3. CLAIM — staking the work
59
+
60
+ Most upstreams accept a plain comment ("hey, I'd like to take this on, plan is X, ETA Y"). Algora-managed issues use Algora's `/bounty` slash command on the issue itself. Some programs (Cortex specifically) require an AI disclosure phrase in the first comment.
61
+
62
+ After posting, update the tracker so Step 0 reflects the claim. Forgetting this is the #1 source of tracker drift.
63
+
64
+ ### 4. WORK — actually doing the thing
65
+
66
+ Work happens in the upstream clone under `~/000-projects/contributing-clanker/<repo>/`. Each clone has its own `CLAUDE.md` with stack-specific commands and conventions.
67
+
68
+ The two non-obvious rules:
69
+
70
+ - **Don't push to forks until tests pass locally.** Pushing then force-pushing is noisy and wastes CI cycles.
71
+ - **Match the upstream's tone.** screenpipe is lowercase; calcom is sentence case; PostHog is sentence case ("Product analytics" not "Product Analytics"). The maintainer notices.
72
+
73
+ ### 5. SUBMIT — design issue first, PR after approval
74
+
75
+ The repo's `CLAUDE.md` says it explicitly: auto-opening PRs creates whack-a-mole slopfests. Open a Design Issue with diff preview + test results, wait for the maintainer to approve the approach, *then* open a PR.
76
+
77
+ The exception: if the upstream's CONTRIBUTING.md explicitly asks for direct PRs (some repos prefer this for small fixes), follow their convention.
78
+
79
+ ## Project-specific gotchas
80
+
81
+ ### screenpipe
82
+
83
+ - Two test suites: `cargo test` for the Rust core, `bun test` for the Tauri app. Both must pass.
84
+ - Lowercase logging and UI text — match the existing codebase tone.
85
+ - No toast errors — use empty states / skeletons / inline errors instead.
86
+ - `@ts-ignore` comments are intentional; don't remove them.
87
+ - Escape HTML properly in JSX (`&apos;` etc. inside string attributes).
88
+
89
+ ### cortex
90
+
91
+ - CLA required before first PR — sign at the link in `cortex/CLA.md`.
92
+ - Demo video required (before/after for bugs, feature demo for new work).
93
+ - AI disclosure phrase required in the PR body.
94
+ - Tests with >80% coverage (their bar, not optional).
95
+ - No force-push — merge commits only.
96
+
97
+ ### posthog
98
+
99
+ - ALWAYS wrap commands in `flox activate -- bash -c "..."`. Running pytest directly will fail.
100
+ - Type hints required in Python.
101
+ - No mypy (they ditched it for being too slow).
102
+ - Tailwind preferred over inline styles.
103
+ - Avoid direct `dayjs` imports — use `lib/dayjs`.
104
+ - Conventional commits: `feat(scope):`, `fix(scope):`, lowercase, no period.
105
+
106
+ ### calcom / cal-com
107
+
108
+ - Two clones in the workspace. Prefer `calcom/` (newer).
109
+ - yarn workspaces — run from the monorepo root, not subpackage.
110
+
111
+ ### vertex-ai-samples (Google)
112
+
113
+ - CLA required at https://cla.developers.google.com/
114
+ - One notebook per PR.
115
+ - Lint via Docker: `docker run -v ${PWD}:/setup/app gcr.io/cloud-devrel-public-resources/notebook_linter:latest <notebook>.ipynb`
116
+
117
+ ### zio-blocks
118
+
119
+ - Scala 3, pure FP, sbt.
120
+ - `sbt scalafmtCheckAll` is part of the gate.
121
+ - Schema migrations have $2-4K rewards — biggest individual paydays in the workspace.
122
+
123
+ ### feishin
124
+
125
+ - Electron + React + pnpm.
126
+ - ESLint + Stylelint both run.
127
+
128
+ ### gumroad
129
+
130
+ - $1,500 per SCSS file converted to Tailwind.
131
+ - Email `bounties@antiwork.com` with PR link + payment email after merge.
132
+ - Stripe payout (bank, PayPal, crypto).
133
+ - Issue `#1055` is the index of available files.
134
+
135
+ ## Tracker hygiene
136
+
137
+ The tracker only stays useful if its status reflects reality. Two operations keep it honest:
138
+
139
+ **Reconcile** — for every row with a `pr_number`, check the live PR state via `gh pr view`. Update the row's `status` to `completed` (merged), `cancelled` (closed-not-merged), or `submitted` (open). Do this at least weekly, or after any "mass push" day.
140
+
141
+ **Re-import from CSV** — the canonical CSV at `~/000-projects/contributing-clanker/000-docs/002-PM-BKLG-contribution-tracker.csv` is the human-edited backlog. If the CSV gets new rows (manual additions), re-run the importer to land them in SQLite. Idempotent — existing IDs are skipped.
142
+
143
+ ## Money & payment programs
144
+
145
+ | Program | Payment mechanism | Speed |
146
+ |---------|------------------|-------|
147
+ | Algora | Platform handles automatically (120+ countries) | Days |
148
+ | Gumroad | Email `bounties@antiwork.com` post-merge, Stripe payout | ~1 week |
149
+ | Cortex | Bitcoin (preferred), USDC, or PayPal | <48 hours |
150
+ | Tscircuit / Golem / others on Algora | Same as Algora | Days |
151
+ | Ad-hoc GitHub bounty (no platform) | Negotiated case-by-case | Variable |
152
+
153
+ The local `001-BL-TRCK-payment-tracker.md` doc tracks paid vs pending across all programs. After a payment lands, update both that doc AND the `bounties.payment_status` column.
@@ -0,0 +1,180 @@
1
+ #!/usr/bin/env bash
2
+ # audit-overrides.sh — report override frequency and reasons across gates.
3
+ #
4
+ # Closes beads contributing-clanker-15b.3 and contributing-clanker-i4y.1
5
+ # (both ask for the same reporter: every --override-gate logged with reason +
6
+ # override frequency by gate).
7
+ #
8
+ # Reads ~/.contribute-system/log.jsonl. Aggregates two event types:
9
+ # - gate_override → an engineer pressed --override-gate=<ID> "reason"
10
+ # - gate_run → every gate verdict (BLOCK / PASS / WARN / etc.)
11
+ #
12
+ # Output: per-gate row with [overrides, blocks, override_rate, top_reason].
13
+ # Sorted by override_rate desc — the most-overridden gates surface first.
14
+ # A high override_rate means the gate is either too strict (false-positive
15
+ # heavy) or genuinely catching real risk that engineers consciously accept.
16
+ # Either way, it's the signal worth surfacing.
17
+ #
18
+ # Usage:
19
+ # audit-overrides.sh # all-time, all repos
20
+ # audit-overrides.sh --since=30 # last 30 days only
21
+ # audit-overrides.sh --scope=org:posthog # only posthog/* repos
22
+ # audit-overrides.sh --scope=repo:foo/bar # exact repo
23
+ # audit-overrides.sh --json # JSON output (no table)
24
+ # audit-overrides.sh --gate=A05 # drill into one gate
25
+
26
+ set -uo pipefail
27
+
28
+ LOG="${HOME}/.contribute-system/log.jsonl"
29
+ SINCE_DAYS=""
30
+ SCOPE=""
31
+ GATE_FILTER=""
32
+ JSON_OUT=0
33
+
34
+ while [[ $# -gt 0 ]]; do
35
+ case "$1" in
36
+ --since=*) SINCE_DAYS="${1#*=}" ;;
37
+ --scope=*) SCOPE="${1#*=}" ;;
38
+ --gate=*) GATE_FILTER="${1#*=}" ;;
39
+ --json) JSON_OUT=1 ;;
40
+ -h|--help)
41
+ sed -n '2,30p' "$0"
42
+ exit 0
43
+ ;;
44
+ *)
45
+ /usr/bin/printf 'unknown arg: %s\n' "$1" >&2
46
+ exit 2
47
+ ;;
48
+ esac
49
+ shift
50
+ done
51
+
52
+ if [[ ! -f "$LOG" ]]; then
53
+ /usr/bin/printf 'no log file at %s — run a transition first\n' "$LOG" >&2
54
+ exit 0
55
+ fi
56
+
57
+ # Build a jq filter that applies --since + --scope + --gate filters before
58
+ # aggregation. The repo field is on .details.repo for gate_run, and on
59
+ # .details.candidate (a path) for gate_override — gate_override events don't
60
+ # carry a repo string directly, so for gate_override we extract from the path
61
+ # pattern <owner>__<repo>__issue<N>.md when present, falling back to "" when
62
+ # not parseable. That's why scope filtering for overrides is best-effort.
63
+
64
+ since_filter='true'
65
+ if [[ -n "$SINCE_DAYS" ]]; then
66
+ cutoff=$(/usr/bin/date -u -d "$SINCE_DAYS days ago" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null \
67
+ || /usr/bin/date -u -v-"${SINCE_DAYS}"d +%Y-%m-%dT%H:%M:%SZ)
68
+ since_filter=".ts >= \"$cutoff\""
69
+ fi
70
+
71
+ case "$SCOPE" in
72
+ org:*)
73
+ owner="${SCOPE#org:}"
74
+ scope_run='(.details.repo // "") | startswith("'"$owner"'/")'
75
+ scope_ovr='(.details.candidate // "") | test("/'"$owner"'__")'
76
+ ;;
77
+ repo:*)
78
+ repo="${SCOPE#repo:}"
79
+ scope_run='(.details.repo // "") == "'"$repo"'"'
80
+ owner_repo_path=$(/usr/bin/printf '%s' "$repo" | /usr/bin/sed 's|/|__|')
81
+ scope_ovr='(.details.candidate // "") | test("/'"$owner_repo_path"'__")'
82
+ ;;
83
+ "")
84
+ scope_run='true'
85
+ scope_ovr='true'
86
+ ;;
87
+ *)
88
+ /usr/bin/printf 'invalid --scope (use org:OWNER or repo:OWNER/NAME)\n' >&2
89
+ exit 2
90
+ ;;
91
+ esac
92
+
93
+ gate_filter_run='true'
94
+ gate_filter_ovr='true'
95
+ if [[ -n "$GATE_FILTER" ]]; then
96
+ # Gate IDs in gate_run are lowercase ("a05"); in gate_override they're as
97
+ # passed by the engineer (often uppercase "A05"). Match case-insensitively.
98
+ gate_filter_run='(.details.gate // "" | ascii_downcase) == "'"$(/usr/bin/printf '%s' "$GATE_FILTER" | /usr/bin/tr '[:upper:]' '[:lower:]')"'"'
99
+ gate_filter_ovr='(.details.gate // "" | ascii_downcase) == "'"$(/usr/bin/printf '%s' "$GATE_FILTER" | /usr/bin/tr '[:upper:]' '[:lower:]')"'"'
100
+ fi
101
+
102
+ # Phase 1: aggregate override counts + reasons per gate
103
+ overrides_json=$(jq -cs --slurpfile _ <(/usr/bin/printf '[]') '
104
+ map(select(.event == "gate_override" and ('"$since_filter"') and ('"$scope_ovr"') and ('"$gate_filter_ovr"')))
105
+ | group_by(.details.gate // "" | ascii_downcase)
106
+ | map({
107
+ gate: (.[0].details.gate // "" | ascii_downcase),
108
+ overrides: length,
109
+ reasons: (map(.details.reason // "") | unique)
110
+ })
111
+ ' "$LOG" 2>/dev/null || /usr/bin/printf '[]')
112
+
113
+ # Phase 2: aggregate BLOCK count per gate (denominator for override rate)
114
+ blocks_json=$(jq -cs '
115
+ map(select(.event == "gate_run" and (.details.severity // "") == "BLOCK"
116
+ and ('"$since_filter"') and ('"$scope_run"') and ('"$gate_filter_run"')))
117
+ | group_by(.details.gate // "" | ascii_downcase)
118
+ | map({
119
+ gate: (.[0].details.gate // "" | ascii_downcase),
120
+ blocks: length
121
+ })
122
+ ' "$LOG" 2>/dev/null || /usr/bin/printf '[]')
123
+
124
+ # Phase 3: merge and compute rate
125
+ merged=$(jq -cn --argjson o "$overrides_json" --argjson b "$blocks_json" '
126
+ ($o + $b)
127
+ | group_by(.gate)
128
+ | map({
129
+ gate: .[0].gate,
130
+ overrides: ((map(.overrides // 0) | add) // 0),
131
+ blocks: ((map(.blocks // 0) | add) // 0),
132
+ reasons: ((map(.reasons // []) | add | unique) // [])
133
+ })
134
+ | map(. + {
135
+ override_rate: (if .blocks > 0 then (.overrides / .blocks * 100 | floor) else null end),
136
+ top_reason: (.reasons | first // "")
137
+ })
138
+ | sort_by(if .override_rate == null then -1 else -.override_rate end)
139
+ ')
140
+
141
+ if [[ "$JSON_OUT" -eq 1 ]]; then
142
+ /usr/bin/printf '%s\n' "$merged" | jq .
143
+ exit 0
144
+ fi
145
+
146
+ # Render text table
147
+ header_parts=()
148
+ [[ -n "$SINCE_DAYS" ]] && header_parts+=("last ${SINCE_DAYS}d")
149
+ [[ -n "$SCOPE" ]] && header_parts+=("scope=${SCOPE}")
150
+ [[ -n "$GATE_FILTER" ]] && header_parts+=("gate=${GATE_FILTER}")
151
+ [[ ${#header_parts[@]} -eq 0 ]] && header_parts+=("all-time")
152
+ header=$(/usr/bin/printf '%s, ' "${header_parts[@]}" | /usr/bin/sed 's/, $//')
153
+ /usr/bin/printf '\nOverride audit — %s\n' "$header"
154
+ /usr/bin/printf '%s\n' '─────────────────────────────────────────────────────────────────────────'
155
+ /usr/bin/printf '%-6s %10s %8s %10s %s\n' "GATE" "OVERRIDES" "BLOCKS" "RATE" "TOP REASON"
156
+ /usr/bin/printf '%s\n' '─────────────────────────────────────────────────────────────────────────'
157
+
158
+ count=$(/usr/bin/printf '%s' "$merged" | jq 'length')
159
+ if [[ "$count" -eq 0 ]]; then
160
+ /usr/bin/printf ' (no override events match the filter)\n\n'
161
+ exit 0
162
+ fi
163
+
164
+ /usr/bin/printf '%s\n' "$merged" | jq -r '
165
+ .[] | [
166
+ .gate,
167
+ .overrides,
168
+ .blocks,
169
+ (if .override_rate == null then "n/a" else "\(.override_rate)%" end),
170
+ (if (.top_reason | length) > 50 then (.top_reason[0:47] + "...") else .top_reason end)
171
+ ] | @tsv
172
+ ' | /usr/bin/awk -F'\t' '{ printf "%-6s %10s %8s %10s %s\n", $1, $2, $3, $4, $5 }'
173
+
174
+ /usr/bin/printf '%s\n\n' '─────────────────────────────────────────────────────────────────────────'
175
+
176
+ # Surface insight
177
+ high_rate=$(/usr/bin/printf '%s' "$merged" | jq -r '[.[] | select(.override_rate != null and .override_rate >= 50)] | length')
178
+ if [[ "$high_rate" -gt 0 ]]; then
179
+ /usr/bin/printf ' ⚠ %d gate(s) overridden ≥50%% of the time — investigate false positives.\n\n' "$high_rate"
180
+ fi