The contribution graph

Half of this ships today, and the halves are worth separating before you read on. Verified email addresses — the substrate all of this rests on — are live: adding an address and confirming it from the link works now, and the endpoints in Confirming the addresses you have committed under below are real ones you can call. The graph itself — the walk over your history, the rendered squares, the private aggregate, the agent handling — is designed and not built. Nothing in the sections after that one is running yet. We publish the design because it decides things you may be acting on now, like which addresses are worth confirming.

The design has one idea in it. A graph here is to be derived from git history rather than from things you did on this website, and a commit counts only when we can prove you wrote it. Everything below follows from those two sentences.

What is designed to count, and what is not

A commit is to be attributed to an account by one of exactly two proofs:

Proof Meaning
email-verified the commit’s author address matches an address confirmed on that account
pushed-by the principal that pushed the commit is that account

Everything else is unverified, and unverified is to count for nothing — no square, no total, no streak. That is the anti-gaming rule, written as a rule rather than as a heuristic.

It has to be. An author line is a string anybody can put in git config user.email, and a graph that counted claims would be a graph you could forge in one command by committing under a stranger’s address.

That much is not merely designed. The lookup that answers whose work is a commit authored by this address exists today, with the confirmation requirement inside it rather than in each caller, and it returns nothing for an unproved address. It ships ahead of its consumer deliberately: the rule belongs with the table it protects, not with the code that will lean on it later.

The consequence people notice first is the good one: anyone may commit under any address they like without inheriting anybody’s history, and you lose nothing by having committed under six addresses across four jobs.

Confirming the addresses you have committed under

This part works today. List what the account already owns:

curl -H "Authorization: Bearer $TOKEN" \
  https://api.weft.sh/v1/users/alice/emails

Add one, and confirm it from the link that arrives in that mailbox:

curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H 'content-type: application/json' \
  -d '{"email":"alice@oldjob.example"}' \
  https://api.weft.sh/v1/users/alice/emails
{ "address": "alice@oldjob.example",
  "status": "check your email",
  "detail": "Confirm the address from the link we sent it. Until then it counts for nothing." }

The link is the only proof, so it goes to the mailbox and never into the response — a claim you could confirm over the API would prove nothing about the mailbox. Addresses are private by default: they are what authorship will be resolved through, and only their owner can read, add or remove them. An address already in use elsewhere is refused with a 409 that deliberately carries no name, because whose address it is, is exactly what a stranger must not be able to ask.

Confirming addresses now is work banked rather than work wasted. The walk, when it exists, runs over history that is already stored, so it has nothing to wait for except itself.

Private repositories, as designed

Everything from here down is design. None of it is running.

Work in a private repository is to be an opt-in aggregate day count, and the aggregate is to be all there is: no repository name, no commit title, no link, at any zoom level, to any viewer. Off by default. The one piece of it that exists today is the profile setting itself, which currently governs nothing because there is no graph for it to govern.

The response is to carry private_included so that a reader can tell a quiet week from an opted-out one. A blank stretch that might mean either is a worse answer than a stated one.

If a repository’s visibility flips, the rows are to be rebucketed — raw authorship kept rather than only the totals, precisely so a private repository going public can render its history and a public one going private can stop rendering it.

Agent-authored commits, as designed

An agent is its own principal with its own identity, and it never borrows a human’s name — that rule is in force today for service principals, and it is what the design leans on. Commits authored by an agent are to render distinctly and never to inflate a human’s graph, including the graph of the person who was running it. An agent principal is to have a graph of its own, and that graph is an operator’s audit trail rather than a résumé.

Agent authorship is to be read from the commit’s trailers, using the same trailer parser Change-Id is read with — one grammar, one implementation, rather than a second one written for this.

Why the design is portable

Because it derives from commits rather than from platform events, a graph built this way moves in both directions.

In: mirror a repository here and its history is already stored, so the walk has a decade of commits available to it from the day the mirror lands rather than from the day you signed up. Mirroring the git content works today; the walk over it does not exist yet.

Out: the inputs are in your clone, and this needs nothing from us at all. You can compute the same numbers yourself right now, with no API and no account:

git log --all --format='%aI %aE' \
  | grep -Fi 'alice@oldjob.example' \
  | cut -dT -f1 | sort | uniq -c

That is the point of computing it this way. A contribution graph that only one company can compute is a lock-in mechanism wearing a résumé’s clothes; this one is a view over data you hold a copy of, which is the same argument the escape hatch makes about the repositories themselves.

Not built, and not planned as part of this: signature verification

Commit signature verification — GPG or SSH — is not part of this design, and is not on this page as a coming-soon.

Signatures are part of the commit object, so they travel with the history and verify with stock git on any clone we serve. What we do not do is check one for you, or render a verified badge from one. Doing that means a per-user key store and a trust model: which keys an account vouches for, what happens when one is rotated, what a signature made by a key that has since been revoked means for a commit from three years ago. That is a design job with real answers required, not a flag to turn on.

email-verified and pushed-by give a graph that cannot be forged by editing a config file, so the graph is not waiting on it, and nothing here blocks adding it later.