@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.
- package/README.md +173 -0
- package/hooks/.gitkeep +0 -0
- package/hooks/install.sh +115 -0
- package/hooks/uninstall.sh +70 -0
- package/package.json +42 -0
- package/skills/contribute/SKILL.md +457 -0
- package/skills/contribute/agents/draft-writer.md +110 -0
- package/skills/contribute/agents/repo-analyzer.md +68 -0
- package/skills/contribute/agents/researcher.md +246 -0
- package/skills/contribute/agents/scout.md +182 -0
- package/skills/contribute/agents/test-runner.md +70 -0
- package/skills/contribute/assets/claim-template.md +22 -0
- package/skills/contribute/assets/evidence-template.md +32 -0
- package/skills/contribute/assets/pr-template.md +49 -0
- package/skills/contribute/references/candidate-file-format.md +259 -0
- package/skills/contribute/references/workflow-guide.md +153 -0
- package/skills/contribute/scripts/audit-overrides.sh +180 -0
- package/skills/contribute/scripts/catalog-coverage.sh +99 -0
- package/skills/contribute/scripts/gate-runner.sh +191 -0
- package/skills/contribute/scripts/gates/a01-already-assigned.sh +24 -0
- package/skills/contribute/scripts/gates/a02-already-shipped.sh +31 -0
- package/skills/contribute/scripts/gates/a03-duplicate-flagged.sh +22 -0
- package/skills/contribute/scripts/gates/a04-issue-age.sh +80 -0
- package/skills/contribute/scripts/gates/a05-issue-still-open.sh +33 -0
- package/skills/contribute/scripts/gates/a06-claim-etiquette-required.sh +36 -0
- package/skills/contribute/scripts/gates/a09-mention-routing.sh +63 -0
- package/skills/contribute/scripts/gates/b01-base-branch.sh +29 -0
- package/skills/contribute/scripts/gates/b02-branch-naming.sh +34 -0
- package/skills/contribute/scripts/gates/b03-clone-fresh.sh +33 -0
- package/skills/contribute/scripts/gates/b05-dco-signoff.sh +50 -0
- package/skills/contribute/scripts/gates/b06-commit-format.sh +44 -0
- package/skills/contribute/scripts/gates/b07-scope-files.sh +68 -0
- package/skills/contribute/scripts/gates/b12-new-deps.sh +40 -0
- package/skills/contribute/scripts/gates/b14-local-checks.sh +55 -0
- package/skills/contribute/scripts/gates/b16-local-check-allowlist.sh +32 -0
- package/skills/contribute/scripts/gates/c01-draft-first.sh +23 -0
- package/skills/contribute/scripts/gates/c02-pr-title-format.sh +36 -0
- package/skills/contribute/scripts/gates/c03-pr-body-sections.sh +58 -0
- package/skills/contribute/scripts/gates/c04-ui-screenshots.sh +38 -0
- package/skills/contribute/scripts/gates/c05-test-evidence.sh +31 -0
- package/skills/contribute/scripts/gates/c07-coauthor-banned.sh +30 -0
- package/skills/contribute/scripts/gates/c09-issue-link.sh +31 -0
- package/skills/contribute/scripts/gates/c11-no-force-push.sh +32 -0
- package/skills/contribute/scripts/gates/c12-ci-green.sh +29 -0
- package/skills/contribute/scripts/gates/c13-bots-passed.sh +62 -0
- package/skills/contribute/scripts/gates/c16-no-self-merge.sh +24 -0
- package/skills/contribute/scripts/gates/c19-body-claim-vs-diff.sh +64 -0
- package/skills/contribute/scripts/gates/d02-no-ai-bug-reports.sh +48 -0
- package/skills/contribute/scripts/gates/d03-no-ai-pr-reviews.sh +42 -0
- package/skills/contribute/scripts/gates/d05-no-reopen.sh +25 -0
- package/skills/contribute/scripts/gates/e02-ai-strike-track.sh +57 -0
- package/skills/contribute/scripts/gates/e04-fork-target.sh +39 -0
- package/skills/contribute/scripts/gates/f01-license-compat.sh +92 -0
- package/skills/contribute/scripts/gates/f03-fixtures-clean.sh +30 -0
- package/skills/contribute/scripts/gates/f04-override-disclosure.sh +49 -0
- package/skills/contribute/scripts/gates/g01-no-vendored-edits.sh +52 -0
- package/skills/contribute/scripts/gates/g02-protected-paths.sh +30 -0
- package/skills/contribute/scripts/gates/g03-no-changelog-edits.sh +37 -0
- package/skills/contribute/scripts/gates/g04-no-version-bump.sh +36 -0
- package/skills/contribute/scripts/gates/g06-override-rate-limit.sh +31 -0
- package/skills/contribute/scripts/gates/lib/preamble.sh +105 -0
- package/skills/contribute/scripts/lint-candidate.sh +149 -0
- package/skills/contribute/scripts/researcher-build.sh +456 -0
- package/skills/contribute/scripts/test-known-traps.sh +142 -0
- package/skills/contribute/scripts/test-override-audit.sh +102 -0
- package/skills/contribute/scripts/test-plug-in.sh +113 -0
- package/skills/contribute/scripts/test-scout-refresh.sh +157 -0
- package/skills/contribute/scripts/test-stale-dossier-refresh.sh +96 -0
- 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 (`'` 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
|