Telemetry
FULCRUMAXE can report anonymous counters back to this site. It is off until you turn it on, it sends integers and nothing else, and this page exists so you can decide with the actual message in front of you rather than a description of it.
The site has said since launch that the team’s own telemetry store never leaves the machine it runs on, and that is still true — the numbers on the long record come from this project’s own install. This is a different thing: an opt-in channel for other people’s installs, built so that turning it on costs you nothing you would mind losing.
What it never sends
There is no field for any of these, which is a stronger statement than a promise not to fill them in. The schema is a closed set; the server drops anything outside it before storage and names what it dropped in the response, so a client sending something it should not is told immediately rather than silently ignored.
- Your repository name, owner, or URL
- File paths, branch names, or worktree locations
- Discussion or pull request titles, bodies, or numbers
- Error messages, stack traces, logs, or diffs
- Usernames, email addresses, or tokens
- Your IP address — not stored, and not used for rate limiting either, which keys on the random install id instead
Everything it would send
This is the whole message. Not a summary of it — the complete body, generated from the same file the server validates against, so this page cannot describe a field the endpoint does not accept or omit one it does.
{
"install": "a3f91c47e2b84d60a1f5c93b7e204d8f",
"version": "2026.09.12",
"os": "linux",
"window_hours": 24,
"counts": {
"spawns": 41,
"prs_opened": 12,
"prs_merged": 11,
"gate_pass": 33,
"gate_fail": 4,
"sent_back": 6,
"loop_iterations": 96,
"discussions_filed": 5
},
"failures": {
"rate_limit": 7,
"gate_timeout": 1,
"worktree_contamination": 1
}
}
The counters
Integers over the reporting window, nothing else. Each is capped at 100,000; a larger value is clamped rather than believed.
| Field | Counts |
|---|---|
spawns | Agents spawned |
prs_opened | Pull requests opened |
prs_merged | Pull requests merged |
gate_pass | Merge gates passed |
gate_fail | Merge gates failed |
sent_back | Reviews that sent a change back |
loop_iterations | Loop iterations |
discussions_filed | Discussions filed |
The failure kinds
A closed set. The point of this half is to learn what actually breaks on other people’s machines rather than only how often something ran — but an error string would carry paths, repository names and occasionally credentials, so a client maps its own errors onto these fixed names. Anything else is dropped by the server and named back in the response.
| Field | Means |
|---|---|
rate_limit | Hit a GitHub or model rate limit |
auth | Authentication or token scope failure |
worktree_contamination | A worktree left the checkout on the wrong branch |
gate_timeout | A merge gate never reported |
ci_red | CI failed on the head commit |
sandbox_denied | The sandbox hook refused an action |
spawn_failed | An agent could not be spawned |
plane_mismatch | A call went to the wrong repository plane |
state_dir_missing | The external state directory was absent or broken |
budget_exhausted | The cost ceiling stopped work |
And the rest of the body
install— 32 random hex characters, generated once and resettable by deleting one file. Explicitly not derived from a hostname, a repository, a MAC address or a username: a derived id is a fingerprint whatever it is called.version— the engine’s calendar version, matching/^\d{4}\.\d{2}\.\d{2}$/.os— a family, not a version: linux, macos, windows, other.window_hours— how long the counters cover. One of 1, 24, 168.
What has been reported
Reading the totals…
Turning it off, and erasing it
Off is the default and stays the default; nothing is sent unless you set the dial. The install id is 32 random characters in one file — delete it and the next report is from a stranger. To remove what has already been sent, one request is enough:
curl -X DELETE https://fulcrumaxe.dev/api/telemetry \
-H 'content-type: application/json' \
-d '{"install": "<your id>"}'
No account, no verification, no email. Knowing the id is the only claim anyone could make to it, and asking for more would mean collecting more.
Reporting a bug from inside a run
Separate from the counters, and separate again from
the form on this site:
POST /api/report takes one structured bug or feature request
from an install. The summary is yours to write and free text — so
the endpoint refuses anything that looks like a path, a
stack trace, a diff, an email address or a credential, rather than
accepting it and stripping it. Left alone, the one thing an automated
reporter will do is paste the error it just caught.
GET /api/telemetry and GET /api/report both
return exactly what they accept, without you sending anything first.
Filing one takes a single command:
curl -X POST https://fulcrumaxe.dev/api/report \
-H 'content-type: application/json' \
-d '{
"kind": "bug",
"category": "coldstart",
"version": "2026.09.12",
"summary": "Provisioning halts at the labels step on a repo where
the merge-gate labels already exist, instead of treating
them as satisfied and moving on."
}'
category is one of ten fixed values, listed by the GET.
The summary is yours — and if it contains a path, a stack trace, a
diff or anything credential-shaped the request is refused
rather than cleaned up, because a stripped report looks deliberate and
an automated reporter’s first instinct is to paste the error it
just caught.
This page is generated by tools/build-telemetry-page.py
from lib/telemetry-schema.js, the file the endpoint validates against. Nothing on it is written by hand.