opencrabs 0.5.13

The autonomous, self-improving AI agent. Single Rust binary. Every channel. Recommended: the 40MB prebuilt binary for macOS, Linux and Windows: https://github.com/opencrabs/opencrabs/releases
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
#!/usr/bin/env python3
"""sync-sponsors: generate the sponsor surfaces from sponsors.json.

sponsors.json is the single source of truth for who sponsored OpenCrabs,
how much, in what currency, and when. This script renders it into:

  1. SPONSORS.md          the full human-readable ledger
  2. README.md            a short table between <!-- sponsors:start -->
                          and <!-- sponsors:end -->

Modes:
  python3 scripts/sync-sponsors.py            # rewrite both surfaces
  python3 scripts/sync-sponsors.py --check    # exit 1 if either is stale
  python3 scripts/sync-sponsors.py --stdout   # print, write nothing
  python3 scripts/sync-sponsors.py --self-test
  python3 scripts/sync-sponsors.py --as-of 2026-10-17
  python3 scripts/sync-sponsors.py --today    # roll the running totals forward

Reference date: by default the numbers are computed against the ledger's own
"updated" field, so a check run tomorrow over an unchanged ledger still
passes. Pass --today to roll the billings and the usage counter forward, and
bump "updated" in the same commit.

Rules this script enforces so no number in the docs can drift:

  * Currencies are never converted. Each is totalled on its own.
  * Recurring subscriptions are computed as rate times billings to date,
    counted from the start date on its own day of month. Nothing is stored
    as a hand-written total.
  * Credit grants carry no money value, by design. Usage is shown instead.
  * Code contributions are not counted here at all. They are re-derivable
    from GitHub and a hand-maintained copy would rot.
  * Nothing rendered here carries a payment link or names the built-in
    provider. The place a sponsorship comes from is named, the link is not,
    and the provider stays unnamed.

Stdlib only, zero dependencies. Same rule as policy-lint.py.
"""

from __future__ import annotations

import argparse
import calendar
import json
import sys
from datetime import date
from pathlib import Path

ROOT = Path(__file__).resolve().parent.parent
LEDGER = ROOT / "sponsors.json"
SPONSORS_MD = ROOT / "SPONSORS.md"
README = ROOT / "README.md"
START_MARK = "<!-- sponsors:start -->"
END_MARK = "<!-- sponsors:end -->"

SYMBOLS = {"USD": "$", "EUR": "\u20ac", "GBP": "\u00a3"}

KIND_LABEL = {
    "cash": "Cash",
    "in-kind": "In kind",
    "credits": "Credits",
}

EARNINGS_SOURCE = "Account earnings"

GENERATED_BY = (
    "Generated by `scripts/sync-sponsors.py` from [`sponsors.json`](sponsors.json). "
    "Do not edit by hand, run the script."
)


def money(amount: float, currency: str) -> str:
    sym = SYMBOLS.get(currency, "")
    body = f"{amount:,.0f}" if float(amount).is_integer() else f"{amount:,.2f}"
    return f"{sym}{body}" if sym else f"{body} {currency}"


def tokens(count: int) -> str:
    if count >= 1_000_000_000:
        return f"{count / 1_000_000_000:g}B"
    if count >= 1_000_000:
        return f"{count / 1_000_000:g}M"
    return f"{count:,}"


def parse_start(raw: str) -> tuple[int, int, int | None]:
    parts = raw.split("-")
    year, month = int(parts[0]), int(parts[1])
    day = int(parts[2]) if len(parts) > 2 else None
    return year, month, day


def billings_to_date(start: str, as_of: date, fallback_day: int) -> int:
    """Count billing dates from start up to and including as_of.

    The day of month comes from the start date, clamped to the length of
    each month, so a start on the 31st still bills in February.
    """
    year, month, day = parse_start(start)
    day = day or fallback_day
    count = 0
    while True:
        safe_day = min(day, calendar.monthrange(year, month)[1])
        if date(year, month, safe_day) > as_of:
            break
        count += 1
        month += 1
        if month > 12:
            month = 1
            year += 1
    return count


def row_sort_key(row: dict) -> tuple[int, int, int]:
    year, month, day = parse_start(row["date"])
    return (year, month, day or 1)


def human_date(raw: str, precision: str) -> str:
    year, month, day = parse_start(raw)
    name = calendar.month_name[month]
    if precision == "month" or day is None:
        return f"{name} {year}"
    return f"{name} {day}, {year}"


def load_ledger() -> dict:
    return json.loads(LEDGER.read_text())


def validate(ledger: dict) -> None:
    """Refuse a ledger the renderer would silently mis-handle.

    The point is a loud failure instead of a quiet wrong number: an unknown
    kind, a cash row with no amount, or a credits grant that suddenly carries
    a money value are all things that would otherwise render as if fine.
    """
    sponsors = ledger.get("sponsors") or []
    ids = {s.get("id") for s in sponsors}
    if not ids:
        raise SystemExit("sponsors.json lists no sponsors")

    known_kinds = {"cash", "in-kind", "credits"}
    seen: set[str] = set()
    for entry in ledger.get("contributions") or []:
        eid = entry.get("id") or "<no id>"
        if eid in seen:
            raise SystemExit(f"{eid}: duplicate contribution id")
        seen.add(eid)
        if entry.get("sponsor") not in ids:
            raise SystemExit(f"{eid}: unknown sponsor {entry.get('sponsor')!r}")
        kind = entry.get("kind")
        if kind not in known_kinds:
            raise SystemExit(
                f"{eid}: unknown kind {kind!r}, expected one of {sorted(known_kinds)}"
            )
        if kind == "cash":
            if not isinstance(entry.get("amount"), (int, float)):
                raise SystemExit(f"{eid}: cash entry needs a numeric amount")
            if not entry.get("currency"):
                raise SystemExit(f"{eid}: cash entry needs a currency")
            if not entry.get("source"):
                raise SystemExit(
                    f"{eid}: cash entry needs a source, the place the money came from"
                )
        if kind == "in-kind" and not isinstance(entry.get("rate"), dict):
            raise SystemExit(f"{eid}: in-kind entry needs a rate")
        if kind == "credits" and entry.get("amount") is not None:
            raise SystemExit(f"{eid}: credits must not carry a money amount")
        raw = entry.get("start") or entry.get("date")
        if not raw:
            raise SystemExit(f"{eid}: needs a start or a date")
        try:
            parse_start(str(raw))
        except (ValueError, IndexError):
            raise SystemExit(f"{eid}: unparseable date {raw!r}")

    provider = ledger.get("provider")
    if provider and provider.get("disclosed") is not False:
        raise SystemExit(
            "provider.disclosed must be false: the built-in provider is a trade secret"
        )


def self_test() -> int:
    """Prove the arithmetic and the privacy rules before trusting the output."""
    day = date
    billing_cases = [
        ("2026-02-14", day(2026, 2, 13), 14, 0),
        ("2026-02-14", day(2026, 2, 14), 14, 1),
        ("2026-02-14", day(2026, 10, 14), 14, 9),
        ("2026-02-14", day(2026, 10, 17), 14, 9),
        ("2026-02-14", day(2026, 11, 13), 14, 9),
        ("2026-02-14", day(2026, 11, 14), 14, 10),
        ("2026-01-31", day(2026, 2, 28), 31, 2),
    ]
    for start, as_of, fallback, want in billing_cases:
        got = billings_to_date(start, as_of, fallback)
        assert got == want, f"billings_to_date({start}, {as_of}) = {got}, want {want}"

    assert money(30, "USD") == "$30", money(30, "USD")
    assert money(20, "EUR") == "\u20ac20", money(20, "EUR")
    assert tokens(8_000_000_000) == "8B", tokens(8_000_000_000)
    assert tokens(9_000_000_000) == "9B", tokens(9_000_000_000)

    fixture = {
        "billing_day": 14,
        "sponsors": [
            {"id": "a", "name": "A", "role": "r", "sponsoring_since": "2026-02-14"},
            {"id": "b", "name": "B", "role": "r", "sponsoring_since": "2026-02-14"},
        ],
        "contributions": [
            {
                "id": "c1",
                "sponsor": "a",
                "kind": "cash",
                "description": "d",
                "date": "2026-10-01",
                "amount": 8,
                "currency": "USD",
                "source": "Inference top-up",
                "purpose": "p",
            },
            {
                "id": "c2",
                "sponsor": "b",
                "kind": "cash",
                "description": "d",
                "date": "2026-10-02",
                "amount": 20,
                "currency": "EUR",
                "source": "Buy Me a Coffee",
                "purpose": "p",
            },
            {
                "id": "c3",
                "sponsor": "a",
                "kind": "credits",
                "description": "d",
                "start": "2026-05",
                "amount": None,
                "rate": None,
                "purpose": "p",
            },
            {
                "id": "c4",
                "sponsor": "a",
                "kind": "in-kind",
                "description": "d",
                "start": "2026-02-14",
                "rate": {"amount": 30, "currency": "USD", "period": "month"},
                "purpose": "p",
            },
        ],
    }
    validate(fixture)
    built = build(fixture, day(2026, 10, 17))

    # Two currencies stay two totals, and a credits grant never joins them.
    assert built["cash_totals"] == {"USD": 8, "EUR": 20}, built["cash_totals"]
    assert built["kind_totals"] == {"USD": 270}, built["kind_totals"]
    assert len(built["rows"]["credits"]) == 1
    assert len(built["rows"]["cash"]) == 2
    assert built["rows"]["in-kind"][0]["billings"] == 9

    for bad, why in [
        ({**fixture, "sponsors": []}, "no sponsors"),
        ({**fixture, "provider": {"name": "leak", "disclosed": True}}, "disclosed provider"),
    ]:
        try:
            validate(bad)
        except SystemExit:
            pass
        else:
            raise AssertionError(f"validate accepted {why}")

    for entry, why in [
        ({**fixture["contributions"][0], "kind": "nonsense"}, "unknown kind"),
        ({**fixture["contributions"][0], "amount": None}, "cash with no amount"),
        ({**fixture["contributions"][0], "source": ""}, "cash with no source"),
        ({**fixture["contributions"][2], "amount": 100}, "credits with a money value"),
        ({**fixture["contributions"][0], "sponsor": "ghost"}, "unknown sponsor"),
    ]:
        bad = {**fixture, "contributions": [entry]}
        try:
            validate(bad)
        except SystemExit:
            pass
        else:
            raise AssertionError(f"validate accepted {why}")

    # The rendered surface names the place a sponsorship came from, but carries
    # no payment link and links nowhere in the disclosure section.
    md = render_sponsors_md(fixture, built)
    disclosure = md.split("## Disclosure", 1)[1].split("## How to sponsor", 1)[0]
    assert "http" not in disclosure, "the disclosure section must carry no link"
    assert "buymeacoffee" not in md.lower(), "no payment link may be rendered"
    assert "$8" in md and "\u20ac20" in md, "cash must render per currency"
    assert "$270" in md, "in-kind must render as rate times billings"

    print("self-test: OK")
    return 0


def build(ledger: dict, as_of: date) -> dict:
    sponsors = {s["id"]: s for s in ledger["sponsors"]}
    billing_day = int(ledger.get("billing_day", 1))
    rows = {"cash": [], "in-kind": [], "credits": []}

    for entry in ledger["contributions"]:
        sponsor = sponsors[entry["sponsor"]]
        kind = entry["kind"]
        if not sponsor.get("public", True) or not entry.get("public", True):
            continue
        if kind == "cash":
            rows["cash"].append({**entry, "sponsor_name": sponsor["name"]})
        elif kind == "in-kind" and entry.get("rate"):
            billings = billings_to_date(entry["start"], as_of, billing_day)
            rate = entry["rate"]
            rows["in-kind"].append(
                {
                    **entry,
                    "sponsor_name": sponsor["name"],
                    "billings": billings,
                    "committed": rate["amount"] * billings,
                    "currency": rate["currency"],
                }
            )
        elif kind == "credits":
            rows["credits"].append({**entry, "sponsor_name": sponsor["name"]})

    cash_totals: dict[str, float] = {}
    for row in rows["cash"]:
        cash_totals[row["currency"]] = cash_totals.get(row["currency"], 0) + row["amount"]

    kind_totals: dict[str, float] = {}
    for row in rows["in-kind"]:
        kind_totals[row["currency"]] = kind_totals.get(row["currency"], 0) + row["committed"]

    return {
        "sponsors": sponsors,
        "rows": rows,
        "cash_totals": cash_totals,
        "kind_totals": kind_totals,
        "as_of": as_of,
    }


def render_sponsors_md(ledger: dict, built: dict) -> str:
    as_of = built["as_of"]
    rows = built["rows"]
    out: list[str] = []
    out.append("<!-- This file is generated. See scripts/sync-sponsors.py -->")
    out.append("")
    out.append("# \U0001f91d Sponsors")
    out.append("")
    out.append(
        "OpenCrabs is community funded. No venture capital, no investors, no "
        "company behind it. The people listed here pay for the subscriptions, "
        "the inference and the credits that keep the agent running, and some of "
        "them also write the code."
    )
    out.append("")
    out.append(
        f"Numbers below are derived from [`sponsors.json`](sponsors.json) as of "
        f"**{human_date(as_of.isoformat(), 'day')}**. Currencies are never "
        "converted, so each one is totalled on its own."
    )
    out.append("")

    out.append("## Totals")
    out.append("")
    out.append("| Type | Total | How it is counted |")
    out.append("|---|---|---|")
    for currency, total in sorted(built["cash_totals"].items()):
        out.append(
            f"| Cash | **{money(total, currency)}** | Transferred, not converted |"
        )
    for currency, total in sorted(built["kind_totals"].items()):
        out.append(
            f"| In kind | **{money(total, currency)}** | Subscription rate times billings to date |"
        )
    for row in rows["credits"]:
        usage = row.get("usage") or {}
        span = ""
        if usage.get("estimate_low") and usage.get("estimate_high"):
            span = (
                f"{tokens(usage['estimate_low'])} to "
                f"{tokens(usage['estimate_high'])} {usage.get('unit', 'tokens')}"
            )
        out.append(
            f"| Credits | **{span or 'granted'}** | {row['description']}. No money value recorded |"
        )
    out.append("")

    out.append("## Who is behind it")
    out.append("")
    out.append("| Sponsor | Role | Sponsoring since | Code since |")
    out.append("|---|---|---|---|")
    for sponsor in ledger["sponsors"]:
        if not sponsor.get("public", True):
            continue
        code = sponsor.get("code")
        code_cell = human_date(code["since"], "day") if code else "No code"
        out.append(
            f"| [{sponsor['name']}](https://github.com/{sponsor['github']}) "
            if sponsor.get("github")
            else f"| {sponsor['name']} "
        )
        out[-1] += (
            f"| {sponsor['role']} | {human_date(sponsor['sponsoring_since'], 'day')} "
            f"| {code_cell} |"
        )
    out.append("")

    if rows["cash"]:
        out.append("## Cash")
        out.append("")
        out.append("| Date | Sponsor | Amount | Source | For |")
        out.append("|---|---|---|---|---|")
        for row in sorted(rows["cash"], key=row_sort_key):
            out.append(
                f"| {human_date(row['date'], row.get('date_precision', 'day'))} "
                f"| {row['sponsor_name']} | {money(row['amount'], row['currency'])} "
                f"| {row['source']} | {row['purpose']} |"
            )
        out.append("")
        earned = [r for r in rows["cash"] if r["source"] == EARNINGS_SOURCE]
        if earned:
            total = sum(r["amount"] for r in earned)
            out.append(
                f"Of the cash above, {money(total, earned[0]['currency'])} is "
                "money the account had already earned, moved back into inference "
                "rather than taken out. The rest is paid from a sponsor's own pocket."
            )
            out.append("")

    if rows["in-kind"]:
        out.append("## Subscriptions paid directly")
        out.append("")
        out.append(
            "These are recurring costs a sponsor pays out of pocket rather than "
            "money handed to the project. They fund both the development loop and "
            "model access for everyone using OpenCrabs in Telegram and Discord."
        )
        out.append("")
        out.append("| Sponsor | Subscription | Rate | Since | Billings to date | Committed |")
        out.append("|---|---|---|---|---|---|")
        for row in rows["in-kind"]:
            out.append(
                f"| {row['sponsor_name']} | {row['description']} "
                f"| {money(row['rate']['amount'], row['rate']['currency'])}/month "
                f"| {human_date(row['start'], 'day')} | {row['billings']} "
                f"| {money(row['committed'], row['currency'])} |"
            )
        out.append("")
        replacements = [r for r in rows["in-kind"] if r.get("replacement_rate")]
        if replacements:
            out.append("### Replacement value today")
            out.append("")
            out.append(
                "What the same subscriptions would cost at current list prices. "
                "Shown for context. The totals above record what was actually "
                "committed, not this."
            )
            out.append("")
            out.append("| Sponsor | Subscription | List price today | Billings to date | Value today |")
            out.append("|---|---|---|---|---|")
            for row in replacements:
                rate = row["replacement_rate"]
                value = rate["amount"] * row["billings"]
                out.append(
                    f"| {row['sponsor_name']} | {row['description']} "
                    f"| {money(rate['amount'], rate['currency'])}/month "
                    f"| {row['billings']} | {money(value, rate['currency'])} |"
                )
            out.append("")

    if rows["credits"]:
        out.append("## Credits and access")
        out.append("")
        out.append(
            "Granted credits carry no money amount here on purpose. Nobody paid a "
            "rate for them, so any figure would be invented. Usage is recorded instead."
        )
        out.append("")
        out.append("| Sponsor | Programme | Since | Usage |")
        out.append("|---|---|---|---|")
        for row in rows["credits"]:
            usage = row.get("usage") or {}
            span = ""
            if usage.get("estimate_low") and usage.get("estimate_high"):
                span = (
                    f"{tokens(usage['estimate_low'])} to "
                    f"{tokens(usage['estimate_high'])} {usage.get('unit', 'tokens')} burned"
                )
            out.append(
                f"| {row['sponsor_name']} | {row['description']} "
                f"| {human_date(row['start'], row.get('start_precision', 'day'))} "
                f"| {span or 'Granted'} |"
            )
        out.append("")

    out.append("## Where the money goes")
    out.append("")
    out.append(
        "Subscriptions first, then inference, then GPU. The first cash "
        "contributions went straight into the recurring subscriptions. Everything "
        "since has gone to inference credits."
    )
    out.append("")
    out.append("## Disclosure")
    out.append("")
    out.append(
        "Two things are kept out of this page on purpose. The inference accounts "
        "listed above pay for the built-in provider that serves free model access "
        "to every OpenCrabs user, and that provider is not named and not linked "
        "here. Payment links are not published either: the place a sponsorship "
        "comes from is named, the link is not."
    )
    out.append("")
    out.append("## How to sponsor")
    out.append("")
    out.append(
        "Sponsorship comes in through Buy Me a Coffee, or open an issue and say "
        "what you would like to fund. Sponsorship is not required to use "
        "OpenCrabs, and every feature stays free for everyone."
    )
    out.append("")
    out.append("---")
    out.append("")
    out.append(GENERATED_BY)
    out.append("")
    return "\n".join(out)


def render_readme_block(ledger: dict, built: dict) -> str:
    out: list[str] = []
    out.append(START_MARK)
    out.append("### Community funded")
    out.append("")
    out.append(
        "OpenCrabs has no investors. The people below cover the subscriptions, "
        "the inference and the credits, and some of them write the code too. "
        f"Totals as of {human_date(built['as_of'].isoformat(), 'day')}."
    )
    out.append("")
    out.append("| Sponsor | Role | Sponsoring since |")
    out.append("|---|---|---|")
    for sponsor in ledger["sponsors"]:
        if not sponsor.get("public", True):
            continue
        out.append(
            f"| {sponsor['name']} | {sponsor['role']} "
            f"| {human_date(sponsor['sponsoring_since'], 'day')} |"
        )
    out.append("")
    totals = []
    cash_parts = [
        f"**{money(total, currency)}**"
        for currency, total in sorted(built["cash_totals"].items())
    ]
    if cash_parts:
        totals.append(" + ".join(cash_parts) + " in cash")
    for currency, total in sorted(built["kind_totals"].items()):
        totals.append(f"**{money(total, currency)}** in subscriptions")
    for row in built["rows"]["credits"]:
        usage = row.get("usage") or {}
        if usage.get("estimate_low"):
            totals.append(
                f"**{tokens(usage['estimate_low'])} to "
                f"{tokens(usage['estimate_high'])} tokens** of granted credits"
            )
    out.append("To date: " + ", ".join(totals) + ".")
    out.append("")
    out.append("Full ledger: [SPONSORS.md](SPONSORS.md)")
    out.append(END_MARK)
    return "\n".join(out)


def splice_readme(readme: str, block: str) -> str:
    if START_MARK not in readme or END_MARK not in readme:
        raise SystemExit(
            f"README is missing the sponsor markers. Expected both "
            f"{START_MARK} and {END_MARK}."
        )
    head, rest = readme.split(START_MARK, 1)
    _, tail = rest.split(END_MARK, 1)
    return head + block + tail


def reference_date(ledger: dict, override: str | None, force_today: bool) -> date:
    """Pick the date the running numbers are computed against.

    Default is the ledger's own "updated" field, so a check run the day after a
    commit still passes. --today rolls the billings forward.
    """
    if override:
        return date.fromisoformat(override)
    if force_today:
        return date.today()
    raw = ledger.get("updated")
    return date.fromisoformat(raw) if raw else date.today()


def main() -> int:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--check", action="store_true", help="exit 1 if surfaces are stale")
    parser.add_argument("--stdout", action="store_true", help="print, write nothing")
    parser.add_argument("--self-test", action="store_true", help="test the math, write nothing")
    parser.add_argument("--as-of", help="reference date, YYYY-MM-DD")
    parser.add_argument(
        "--today",
        action="store_true",
        help="compute against today instead of the ledger's updated field",
    )
    args = parser.parse_args()

    if args.self_test:
        return self_test()

    ledger = load_ledger()
    validate(ledger)
    as_of = reference_date(ledger, args.as_of, args.today)
    built = build(ledger, as_of)

    sponsors_md = render_sponsors_md(ledger, built)
    readme_new = splice_readme(README.read_text(), render_readme_block(ledger, built))

    if args.stdout:
        print(sponsors_md)
        print("\n--- README block ---\n")
        print(render_readme_block(ledger, built))
        return 0

    if args.check:
        stale = []
        if not SPONSORS_MD.exists() or SPONSORS_MD.read_text() != sponsors_md:
            stale.append("SPONSORS.md")
        if README.read_text() != readme_new:
            stale.append("README.md sponsor block")
        if stale:
            print("STALE: " + ", ".join(stale))
            print("Run: python3 scripts/sync-sponsors.py")
            return 1
        print(f"OK: sponsor surfaces match sponsors.json (as of {as_of})")
        return 0

    SPONSORS_MD.write_text(sponsors_md)
    README.write_text(readme_new)
    print(f"Wrote SPONSORS.md and the README sponsor block (as of {as_of})")
    age = (date.today() - as_of).days
    if age > 35:
        print(
            f"note: the ledger reference date {as_of} is {age} days old. "
            "Run with --today and bump sponsors.json's updated field to roll the "
            "running totals forward."
        )
    return 0


if __name__ == "__main__":
    sys.exit(main())