Dandelion Mané decentralion @protocol Nomadic building @sourcecred, a reputation protocol for open collaboration they/them

Super simple charts via Plottable and D3

An artistic project to simulate natural selection in the browser

Implementing the recursive Bhattacharrya kernel

an Abalone AI programming tournament

Animal Game, implemented in C with serialization

Visualize flows of bitcoins

Engine for an abalone implementation for an AI programming competition

Useful reusable modules written in C

Canvas vs SVG perf testing

delete branch sourcecred/cred

delete branch : amico-seth-transfer

delete time in 2 days

push eventsourcecred/cred

commit sha 298d93cbe096b4a786c7fd9869521092c876d213

Record transfer from @s_ben to @amico-dj (#20)

push time in 2 days

PR merged sourcecred/cred

+10 -0

1 comment

1 changed file

decentralion

pr closed time in 2 days

push eventsourcecred/cred

commit sha 76a5db6175f8935711b5236940740128293728dc

Record transfer from @s_ben to @amico-dj

push time in 2 days

push eventsourcecred/cred

commit sha 5d950a2e61d58f5ee3a77cf2fdf1c09f5ae31834

Record transfer from @s_ben to @amico-dj

push time in 2 days

PR opened sourcecred/cred

Reviewers
+9 -0

0 comment

1 changed file

pr created time in 2 days

create barnchsourcecred/cred

created branch time in 2 days

delete branch sourcecred/cred

delete branch : rename-amico

delete time in 2 days

push eventsourcecred/cred

commit sha 364f5cfa5d940222cdfe75438c7344f805289b40

Change distribution history to account for rename (#19) Since the grain distribution tracker is not yet aware of the identities plugin, now that we've created an identity for amico (cf #18), we should rewrite history so that the past distributions to amico are recorded properly. Test plan: git grep sabba returns no hits.

push time in 2 days

PR merged sourcecred/cred

Since the grain distribution tracker is not yet aware of the identities plugin, now that we've created an identity for amico (cf #18), we should rewrite history so that the past distributions to amico are recorded properly.

Test plan: git grep sabba returns no hits.

+4 -4

0 comment

1 changed file

decentralion

pr closed time in 2 days

PR opened sourcecred/cred

Reviewers

Since the grain distribution tracker is not yet aware of the identities plugin, now that we've created an identity for amico (cf #18), we should rewrite history so that the past distributions to amico are recorded properly.

Test plan: git grep sabba returns no hits.

+4 -4

0 comment

1 changed file

pr created time in 2 days

create barnchsourcecred/cred

created branch time in 2 days

delete branch sourcecred/cred

delete branch : create-amico

delete time in 2 days

push eventsourcecred/cred

commit sha a6505365a573112ef5596cd8297b709fa12d2fa9

Create an identity entry for amico (#18)

push time in 2 days

PR merged sourcecred/cred

+2 -1

0 comment

1 changed file

decentralion

pr closed time in 2 days

push eventsourcecred/cred

commit sha d5a57fccb5279947c1f2f73bbaf64684ebd8be28

Create an identity entry for amico

push time in 2 days

PR opened sourcecred/cred

Reviewers
+2 -1

0 comment

1 changed file

pr created time in 2 days

create barnchsourcecred/cred

created branch time in 2 days

push eventsourcecred/cred

commit sha e7c2bef95bc94a484d4ae9b2115fa75d027470c6

Distribution for W22

push time in 2 days

push eventsourcecred/cred

commit sha fbfd1e7f59430d20c0273d87b1bef38209646ddc

Commit grain distribution for W21

push time in 2 days

push eventsourcecred/cred

commit sha 7d9e5deb68b8aaa07ee899580e5f2ba9e2b0043e

Cred update

push time in 2 days

push eventsourcecred/sourcecred

commit sha f7e65aa933262acf0d7234a206e83dba117575c5

epoch node translation

push time in 2 days

+As a community, we want to encourage diversity in all aspects, and set a standard+for respectful and appropriate conduct across all communication channels. For+this purpose, we have created this code of conduct, which serves as a guide for+interaction with the SourceCred community. It is adopted from [several well known](#license)+Code of Conducts of open source projects.++## People++In the following sections:++ - **we/our**: refers to the members of the SourceCred community, who exist on equal footing.+ - **SourceCred**: refers to the general project or community.+ - **Benevolent Dictator**: refers to @decentralion

I'm with @vsoch in being a bit uncomfortable with "Servant" here.

I like using the word "maintainer"; for me (coming from open-source) maintainers have authority over the domain that they are maintaining, and can do things like block changes, remove problematic contributors, etc. Since the community values are something we care about, I propose the term "Values Maintainer" here.

vsoch

comment created time in 3 days

+# An introduction to calculating Cred and Grain++I’m starting from the assumption that you have some familiarity with the SourceCred project. If you don’t I’d suggest you check out Dandelion's [20 minute talk (+Qs) from SustainWeb3](https://www.youtube.com/watch?v=yVTqRLekRl4), and some of the other documents in this repo.++Overall, I’m going to throw around some math, but you should be able to get solid intuition on this if you’re comfortable talking about a [graph](https://en.wikipedia.org/wiki/Graph_theory) as a set of nodes and edges connecting them, where the edges can be directed and have weights. I’ll also talk some about [the PageRank algorithm](https://en.wikipedia.org/wiki/PageRank), for computing “importance” of nodes in a graph based on the “importance” of nodes connected to it, but I’ll try to motivate the intuition behind it as well.++## The high level overview++SourceCred takes information about contributions and generates a weights graph, and this post will first explain what that graph is and where it comes from. From this weights graph, it runs PageRank to generate a graph of Cred. This graph of Cred is then used to distribute grain. The grain can then be kept or traded, and soon will be able to be used to influence future graph weights. This process is diagrammed in the figure below.+ +![](https://docs.google.com/drawings/u/1/d/s50mxZJ1DFD_yr4OFUxUd9g/image?w=551&h=250&rev=57&ac=1&parent=1kRxnuKAogf_cE0y6u2QYcjQimE0-jrBZ-LxcXKOUITw)++> Figure 1. A very high level overview of how SourceCred allocates Cred and Grain, distinguishing between actions taken by contributors and those taken by the SourceCred algorithm.++The rest of this doc outlines how collaboration data is gathered and structured, and how the exact amounts of Cred and Grain are calculated, and ends with some future directions SourceCred as a project is considering.++## Components of contributing in SourceCred++SourceCred first generates a weights graph from the input data. In this graph, a node is a contributor or contribution, and edges represent some sort of connection between the nodes. These connections are intended to include all possible ways that a contribution or contributor on a platform can express dependencies, thanks, or flows of value. Examples would include anything from a mention on a forum post to liking or merging a PR on GitHub.++#### Nodes:++Contributions are represented as nodes in the graph, and are given addresses to identify them uniquely. These addresses include a prefix, which takes the form: organization making the plugin that generates the node + name of the plugin + type of contribution (e.g. topic/post/user/like).++Contributors are also represented as nodes in the graph. These nodes are similarly prefixed, and connected to contributions with directed edges, as illustrated in the figure below. For example, if an author node creates an issue, the author node gets an incoming edge from the issue node. Or if an author of a Discourse post mentions another contributor in the post, the post creates an outbound edge coming from the author.++Lastly, initiatives are another type of node that serve as a high level place to connect relevant nodes and discuss governance. It's similar to a tracking issue, but generalized across the weights graph, providing a way to organize planned work for a well-scoped task.++#### Weights:++Both nodes and edges have weights, and an edge object in the SourceCred implementation can have forward and backwards weight. We’ll get into what these weights mean shortly, but for now, it’s worth noting that these weights can easily be set by address prefix. Setting by prefix is implemented as a multiplicative update; for example, setting issue comments to be worth twice an issue emoji reaction creates a 2:1 ratio that is preserved by decreasing GitHub’s edge weights by 2 relative to those of Discourse. Additionally, nodes can have self edges, which is relevant when a piece of content is edited, or an contribution references itself.+  +![](https://docs.google.com/drawings/u/1/d/sIDpfiaPlACrxNScrfsISkQ/image?w=186&h=230&rev=47&ac=1&parent=1kRxnuKAogf_cE0y6u2QYcjQimE0-jrBZ-LxcXKOUITw)++> Figure 2: An example of a weights graph, where the nodes would be contributors or contributions, and the directed, weighted edges are connections. It’s a [directed graph](https://en.wikipedia.org/wiki/Graph_theory#Directed_graph) with weights on nodes and edges.++#### Timestamps:++Additionally, both edges and some nodes have timestamps. Edges are associated with actions or events, and as such, always have a timestamp. For nodes that correspond to a contribution, those nodes have a timestamp corresponding to the moment of the contribution. However, nodes corresponding to contributors do not have time stamps, since the contributors exist in the algorithm across time.++#### Summary up to this point:++Now that we know about components of the weights graph, their properties, and how they fit together, let’s cover how they’re generated.++## Generating the weights graph++#### Plugins:++SourceCred uses APIs of collaboration platforms to gather information about contributions. Currently there are plugins for GitHub and Discourse ([code](https://github.com/sourcecred/sourcecred/tree/master/src/plugins)), and we’re enthusiastic to have more plugins, so we can account for contributions in different venues. This includes Discord (in progress), and could extend to Twitter or citations on the [arXiv](https://arxiv.org/) or [eprint](https://eprint.iacr.org/).++#### Weights:++Because these plugins incorporate data from all sorts of contributions and output a single weighted, directed graph, there needs to be a way to quantitatively compare actions (e.g. authoring a GitHub issue vs responding or referencing it). To accomplish this, there’s a set of constants that determine weights for nodes and edges. For an example set of constants, see the below figure, captured from [sourcecred.io/cred/timeline/@sourcecred/](https://sourcecred.io/cred/timeline/@sourcecred/) (follow the link and click the “Show weight configuration” button).++![](https://lh6.googleusercontent.com/BDSrcvjcCXXfKQ0NSiHIYuAh8fvTaE0Akl0g2-PLdB88fv5TXZS7btltL0YTe77TLOT7nyImct7sPrKATbAQKUwukM3bdxgVfIJCf-xDZo0nvGnFINMoqXq41glKAI9hA2EnThed)++> Figure 3. A set of weight sliders to set parameters controlling the relative weights of different contributions.++Additionally, parameters, cred, and grain are all scoped to a community. This means that the community that supports a project sets and controls the parameters. For that matter, it’s worth reiterating that (lowercase) cred and grain generated for a project are specific to that project. SourceCred is about providing community control, keeping humans in the loop, both for gaming mitigation and general adaptability. At the bottom line, it’s important to keep the humans in control, because good governance technology only works with the consent of the governed.++#### Manual input:++It’s also worth noting that not all contributions occur on platforms with automated data fetching, and thus the capability exists to individually add them to the weighted graph directly at this stage. It’s clear that there will always be data that has to be incorporated manually, and how to incorporate those contributions, especially those not easily quantified, remains an active area of discussion.++#### Merging graphs:++A plugin will use the API for the collaborative workspace app or platform to generate a graph of nodes and edges. One interesting caveat is that there isn't necessarily one most-intuitively correct way to merge two weighted graphs if they share either edges or nodes (i.e. how would we calculate the combined weight). Currently, the graph nodes are nearly entirely disjoint, the weights are added together, and identities are collapsed into one.++#### Summary up to this point:++So at this point, we’ve got data from all our sources combined in one weighted, directed graph, where contributions and contributors are nodes, interactions and connections between nodes are edges, each have a relative weight based on parameters the community has set, and we’re ready to run PageRank on it.++## SourceCred’s PageRank++The next goal will be to generate an evaluation of the relative importance of nodes based on our weighted graph, specifically the weights of the nodes and edges.++#### PageRank explained elsewhere++Before reading on, it’s important to understand what PageRank does. There are other good explanations out there, and keeping this brief seems useful, so I won’t add another explanation. If you like over preparing, Brilliant.com’s explanation of [Markov Chains](https://brilliant.org/wiki/markov-chains/#transition-matrices), [Stationary Distributions](https://brilliant.org/wiki/stationary-distributions/) and [their quiz](https://brilliant.org/practice/basic-markov-chains) is more than sufficient. If you’re a visual thinker, [these slides](https://www.cl.cam.ac.uk/teaching/1516/InfoRtrv/lecture8-link-analysis-2x2.pdf) provide some nice figures.++<img src="https://upload.wikimedia.org/wikipedia/commons/6/69/PageRank-hi-res.png" width="400">++> Figure 4. An illustration of PageRank, in which the size of each node is proportional to the total size of the nodes linking to it.++#### Stationary distribution intuition++The simplest formulation of PageRank operates over an unweighted graph, treating all links as equivalent, as illustrated in the figure above. It assigns all outbound links equal probability and calculates the stationary distribution of node weights. (Intuitively, the stationary distribution is the equilibrium fraction of water that ends up in each node “tank” if you run a pump along each edge for a while – for “reasonable” graph configurations you’d expect the water distribution to end up the same regardless of where you dump the water initially.)++#### Stationary distribution as an eigenvector++If you’ve spend more time in linear algebra land, you’ll be pleased to hear that common way to compute this (in algebraic graph theory) is to convert the graph to a matrix _P_ where each entry <img src="https://render.githubusercontent.com/render/math?math=P_{uv}"> is the transition probability from node _u_ to node _v_. Each dimension corresponds to a node, and the matrix maps from all nodes to all nodes. Then finding the stationary distribution is the same as finding [the eigenvector](https://www.youtube.com/watch?v=PFDu9oVAE-g) of the transition matrix that corresponds to eigenvalue 1; by the definition of an eigenvector, this vector is the distribution of value to each node such that if value flowed down each edge in proportion to its weight, the resulting value distribution would remain the same.++#### Incorporate edge weights++We can adapt the algorithm to account for edge weights on the input graph fairly intuitively – we could add weights to the links by increasing the number of edges for some types of actions (e.g. 👍=1 edge, ♥️=2 edges). In the probability matrix formulation, it’s simple to increase the transition probability of one edge. Then, instead of assigning equal probability to all outbound edges, we can simply normalize the weights so that they sum to one and this provides us with transition probabilities needed for <img src="https://render.githubusercontent.com/render/math?math=P_{uv}">.++#### Incorporate node weights++There’s also a cleverly simple way to account for the existence of node weights. One requirement for the existence of a stationary distribution that was skipped is the concept of ergodicity, wherein every state must be reachable from any other state, given a long enough probabilistic walk across the graph’s edges†. To achieve ergodicity, SourceCred uses a "teleportation" system by adding a probability, ⍺, of teleporting to a random location in the graph. SourceCred currently implements this with a seed node, which all nodes teleporting to with likelihood ⍺. From the seed node, the probability of landing on a given node in the graph is then given by the node weights. If we use _A_ to describe the weights of each node in a vector form, then our old transition matrix <img src="https://render.githubusercontent.com/render/math?math=P_{uv}"> becomes the new transition matrix <img src="https://render.githubusercontent.com/render/math?math=(1-\alpha)P_{uv} + \alpha A">. The stationary distribution is then the vector <img src="https://render.githubusercontent.com/render/math?math=\vec v"> such that <img src="https://render.githubusercontent.com/render/math?math=\left((1-\alpha)P_{uv} + \alpha A\right)\vec v = \vec v">.++#### The origin of Cred++In this way, Cred originates at the weighted nodes and flows out to actions from there. Interestingly, this is mathematically equivalent to adding edges to each and every node, with edge-weight proportional to ⍺ times the node weight. We can interpret the necessity of teleportation in the following way: because some contributions may receive but flow no cred, there need to be sources of Cred to calculate an equilibrium value distribution, which requires some contributions to mint cred.++#### Cred-minting philosophy++As a matter of design philosophy, we believe cred should be minted at moments where there is review of an action, rather than the action itself. This reduces the danger of spam and reinforces the idea that contributors dictate what is valuable. For other projects adopting SourceCred, contributions that mint cred could also be actions performed by a moderator or maintainer.++#### Summary up to this point:++In this way, PageRank takes weights in and outputs scores that value contributions based on their relationship to each other. The eigenvector <img src="https://render.githubusercontent.com/render/math?math=\vec v"> then consists of the Cred scores for the various nodes, including the contributors, so we’ve accomplished our goal!++## Cred over time++#### Time-based Cred constraints++SourceCred has additionally innovated on PageRank to accommodate properties that Cred should demonstrate over time. The most important properties in this respect are that:++-   a contributor should never lose cred. They ought to become more diluted as other contributors continue to earn new cred, but they’ll still continue to earn Grain on that Cred.+-   a past action that was recently found to be valuable in the present should cause new Cred to flow to that action.++#### Time epochs and fibration++To simplify the computational overhead and storage required to compute Cred over time, SourceCred is moving toward a fibration model where a node is added per contributor, per unit time (currently weekly for SourceCred). In this model, epoch nodes intermediate flows to a user from any nodes during a period of time, and have a fixed transition probability to the user’s node, rather than a weight from which transition probabilities are calculated. The remaining transition probability is then shared between the seed node (for the teleportation vector) and the other node weights. This will be implemented by distinguishing graph edges, which have fixed weights, from markov edges, which have fixed transition probabilities from epoch nodes. This is then input into the mechanism that calculates the transition probabilities.++## Grain dynamics++#### How Grain is generated++Once SourceCred calculates the Cred for individual contributors, that Cred is linked to an identity that is an amalgamation of their identities across the platforms (e.g. GitHub handle or Discord username). A constant amount of SourceCred Grain (¤) is produced per unit time, currently 15000¤ per week. This amount is distributed according to two different distribution mechanisms.++-   20% of the grain is “fast,” and is disbursed to nodes in proportion to the cred earned by each node in the past week. This is intended to reward new participants.+-   80% of the grain is “slow,” and is disbursed in proportion to the difference between total grain you've been distributed, and what that node’s proportional fair Cred would be over all history

Basically, the "slow" distribution tries to ensure that everyone is rewarded fairly based on their lifetime contributions. This is particularly important for people who were under-recognized in the past; say that last year you did work that no-one thought was important, but actually was quite pivotal. Last year, you got underpaid because we didn't think it was worth much; but now that your cred is higher, we should make you whole. the slow payment will "catch you up" for your past underpayment.

On the other hand, it's possible we'll change the algorithm or weights such that we now think you were over-paid in the past. If so, you won't receive any slow payments for a while.

There are some interesting second-order dynamics here relating the rate of "cred inflation" vs "grain inflation".

• If we're increasing grain faster than cred, then the slow payment will pay all contributors (even inactive contributors) because the lifetime grain-per-cred value is increasing
• If we're inflating cred faster than grain, then the slow payment will only pay recipients of "new" cred, i.e. an inactive contributor whose cred stays constant would stop receiving slow payments (because they already reached target grain-per-cred for their amount of cred)
miyazono

comment created time in 3 days

+# An introduction to calculating Cred and Grain++I’m starting from the assumption that you have some familiarity with the SourceCred project. If you don’t I’d suggest you check out Dandelion's [20 minute talk (+Qs) from SustainWeb3](https://www.youtube.com/watch?v=yVTqRLekRl4), and some of the other documents in this repo.++Overall, I’m going to throw around some math, but you should be able to get solid intuition on this if you’re comfortable talking about a [graph](https://en.wikipedia.org/wiki/Graph_theory) as a set of nodes and edges connecting them, where the edges can be directed and have weights. I’ll also talk some about [the PageRank algorithm](https://en.wikipedia.org/wiki/PageRank), for computing “importance” of nodes in a graph based on the “importance” of nodes connected to it, but I’ll try to motivate the intuition behind it as well.++## The high level overview++SourceCred takes information about contributions and generates a weights graph, and this post will first explain what that graph is and where it comes from. From this weights graph, it runs PageRank to generate a graph of Cred. This graph of Cred is then used to distribute grain. The grain can then be kept or traded, and soon will be able to be used to influence future graph weights. This process is diagrammed in the figure below.+ +![](https://docs.google.com/drawings/u/1/d/s50mxZJ1DFD_yr4OFUxUd9g/image?w=551&h=250&rev=57&ac=1&parent=1kRxnuKAogf_cE0y6u2QYcjQimE0-jrBZ-LxcXKOUITw)++> Figure 1. A very high level overview of how SourceCred allocates Cred and Grain, distinguishing between actions taken by contributors and those taken by the SourceCred algorithm.++The rest of this doc outlines how collaboration data is gathered and structured, and how the exact amounts of Cred and Grain are calculated, and ends with some future directions SourceCred as a project is considering.++## Components of contributing in SourceCred++SourceCred first generates a weights graph from the input data. In this graph, a node is a contributor or contribution, and edges represent some sort of connection between the nodes. These connections are intended to include all possible ways that a contribution or contributor on a platform can express dependencies, thanks, or flows of value. Examples would include anything from a mention on a forum post to liking or merging a PR on GitHub.++#### Nodes:++Contributions are represented as nodes in the graph, and are given addresses to identify them uniquely. These addresses include a prefix, which takes the form: organization making the plugin that generates the node + name of the plugin + type of contribution (e.g. topic/post/user/like).++Contributors are also represented as nodes in the graph. These nodes are similarly prefixed, and connected to contributions with directed edges, as illustrated in the figure below. For example, if an author node creates an issue, the author node gets an incoming edge from the issue node. Or if an author of a Discourse post mentions another contributor in the post, the post creates an outbound edge coming from the author.++Lastly, initiatives are another type of node that serve as a high level place to connect relevant nodes and discuss governance. It's similar to a tracking issue, but generalized across the weights graph, providing a way to organize planned work for a well-scoped task.++#### Weights:++Both nodes and edges have weights, and an edge object in the SourceCred implementation can have forward and backwards weight. We’ll get into what these weights mean shortly, but for now, it’s worth noting that these weights can easily be set by address prefix. Setting by prefix is implemented as a multiplicative update; for example, setting issue comments to be worth twice an issue emoji reaction creates a 2:1 ratio that is preserved by decreasing GitHub’s edge weights by 2 relative to those of Discourse. Additionally, nodes can have self edges, which is relevant when a piece of content is edited, or an contribution references itself.+  +![](https://docs.google.com/drawings/u/1/d/sIDpfiaPlACrxNScrfsISkQ/image?w=186&h=230&rev=47&ac=1&parent=1kRxnuKAogf_cE0y6u2QYcjQimE0-jrBZ-LxcXKOUITw)++> Figure 2: An example of a weights graph, where the nodes would be contributors or contributions, and the directed, weighted edges are connections. It’s a [directed graph](https://en.wikipedia.org/wiki/Graph_theory#Directed_graph) with weights on nodes and edges.++#### Timestamps:++Additionally, both edges and some nodes have timestamps. Edges are associated with actions or events, and as such, always have a timestamp. For nodes that correspond to a contribution, those nodes have a timestamp corresponding to the moment of the contribution. However, nodes corresponding to contributors do not have time stamps, since the contributors exist in the algorithm across time.++#### Summary up to this point:++Now that we know about components of the weights graph, their properties, and how they fit together, let’s cover how they’re generated.++## Generating the weights graph++#### Plugins:++SourceCred uses APIs of collaboration platforms to gather information about contributions. Currently there are plugins for GitHub and Discourse ([code](https://github.com/sourcecred/sourcecred/tree/master/src/plugins)), and we’re enthusiastic to have more plugins, so we can account for contributions in different venues. This includes Discord (in progress), and could extend to Twitter or citations on the [arXiv](https://arxiv.org/) or [eprint](https://eprint.iacr.org/).++#### Weights:++Because these plugins incorporate data from all sorts of contributions and output a single weighted, directed graph, there needs to be a way to quantitatively compare actions (e.g. authoring a GitHub issue vs responding or referencing it). To accomplish this, there’s a set of constants that determine weights for nodes and edges. For an example set of constants, see the below figure, captured from [sourcecred.io/cred/timeline/@sourcecred/](https://sourcecred.io/cred/timeline/@sourcecred/) (follow the link and click the “Show weight configuration” button).++![](https://lh6.googleusercontent.com/BDSrcvjcCXXfKQ0NSiHIYuAh8fvTaE0Akl0g2-PLdB88fv5TXZS7btltL0YTe77TLOT7nyImct7sPrKATbAQKUwukM3bdxgVfIJCf-xDZo0nvGnFINMoqXq41glKAI9hA2EnThed)++> Figure 3. A set of weight sliders to set parameters controlling the relative weights of different contributions.++Additionally, parameters, cred, and grain are all scoped to a community. This means that the community that supports a project sets and controls the parameters. For that matter, it’s worth reiterating that (lowercase) cred and grain generated for a project are specific to that project. SourceCred is about providing community control, keeping humans in the loop, both for gaming mitigation and general adaptability. At the bottom line, it’s important to keep the humans in control, because good governance technology only works with the consent of the governed.++#### Manual input:++It’s also worth noting that not all contributions occur on platforms with automated data fetching, and thus the capability exists to individually add them to the weighted graph directly at this stage. It’s clear that there will always be data that has to be incorporated manually, and how to incorporate those contributions, especially those not easily quantified, remains an active area of discussion.++#### Merging graphs:++A plugin will use the API for the collaborative workspace app or platform to generate a graph of nodes and edges. One interesting caveat is that there isn't necessarily one most-intuitively correct way to merge two weighted graphs if they share either edges or nodes (i.e. how would we calculate the combined weight). Currently, the graph nodes are nearly entirely disjoint, the weights are added together, and identities are collapsed into one.++#### Summary up to this point:++So at this point, we’ve got data from all our sources combined in one weighted, directed graph, where contributions and contributors are nodes, interactions and connections between nodes are edges, each have a relative weight based on parameters the community has set, and we’re ready to run PageRank on it.++## SourceCred’s PageRank++The next goal will be to generate an evaluation of the relative importance of nodes based on our weighted graph, specifically the weights of the nodes and edges.++#### PageRank explained elsewhere++Before reading on, it’s important to understand what PageRank does. There are other good explanations out there, and keeping this brief seems useful, so I won’t add another explanation. If you like over preparing, Brilliant.com’s explanation of [Markov Chains](https://brilliant.org/wiki/markov-chains/#transition-matrices), [Stationary Distributions](https://brilliant.org/wiki/stationary-distributions/) and [their quiz](https://brilliant.org/practice/basic-markov-chains) is more than sufficient. If you’re a visual thinker, [these slides](https://www.cl.cam.ac.uk/teaching/1516/InfoRtrv/lecture8-link-analysis-2x2.pdf) provide some nice figures.++<img src="https://upload.wikimedia.org/wikipedia/commons/6/69/PageRank-hi-res.png" width="400">++> Figure 4. An illustration of PageRank, in which the size of each node is proportional to the total size of the nodes linking to it.++#### Stationary distribution intuition++The simplest formulation of PageRank operates over an unweighted graph, treating all links as equivalent, as illustrated in the figure above. It assigns all outbound links equal probability and calculates the stationary distribution of node weights. (Intuitively, the stationary distribution is the equilibrium fraction of water that ends up in each node “tank” if you run a pump along each edge for a while – for “reasonable” graph configurations you’d expect the water distribution to end up the same regardless of where you dump the water initially.)++#### Stationary distribution as an eigenvector++If you’ve spend more time in linear algebra land, you’ll be pleased to hear that common way to compute this (in algebraic graph theory) is to convert the graph to a matrix _P_ where each entry <img src="https://render.githubusercontent.com/render/math?math=P_{uv}"> is the transition probability from node _u_ to node _v_. Each dimension corresponds to a node, and the matrix maps from all nodes to all nodes. Then finding the stationary distribution is the same as finding [the eigenvector](https://www.youtube.com/watch?v=PFDu9oVAE-g) of the transition matrix that corresponds to eigenvalue 1; by the definition of an eigenvector, this vector is the distribution of value to each node such that if value flowed down each edge in proportion to its weight, the resulting value distribution would remain the same.++#### Incorporate edge weights++We can adapt the algorithm to account for edge weights on the input graph fairly intuitively – we could add weights to the links by increasing the number of edges for some types of actions (e.g. 👍=1 edge, ♥️=2 edges). In the probability matrix formulation, it’s simple to increase the transition probability of one edge. Then, instead of assigning equal probability to all outbound edges, we can simply normalize the weights so that they sum to one and this provides us with transition probabilities needed for <img src="https://render.githubusercontent.com/render/math?math=P_{uv}">.++#### Incorporate node weights++There’s also a cleverly simple way to account for the existence of node weights. One requirement for the existence of a stationary distribution that was skipped is the concept of ergodicity, wherein every state must be reachable from any other state, given a long enough probabilistic walk across the graph’s edges†. To achieve ergodicity, SourceCred uses a "teleportation" system by adding a probability, ⍺, of teleporting to a random location in the graph. SourceCred currently implements this with a seed node, which all nodes teleporting to with likelihood ⍺. From the seed node, the probability of landing on a given node in the graph is then given by the node weights. If we use _A_ to describe the weights of each node in a vector form, then our old transition matrix <img src="https://render.githubusercontent.com/render/math?math=P_{uv}"> becomes the new transition matrix <img src="https://render.githubusercontent.com/render/math?math=(1-\alpha)P_{uv} + \alpha A">. The stationary distribution is then the vector <img src="https://render.githubusercontent.com/render/math?math=\vec v"> such that <img src="https://render.githubusercontent.com/render/math?math=\left((1-\alpha)P_{uv} + \alpha A\right)\vec v = \vec v">.++#### The origin of Cred++In this way, Cred originates at the weighted nodes and flows out to actions from there. Interestingly, this is mathematically equivalent to adding edges to each and every node, with edge-weight proportional to ⍺ times the node weight. We can interpret the necessity of teleportation in the following way: because some contributions may receive but flow no cred, there need to be sources of Cred to calculate an equilibrium value distribution, which requires some contributions to mint cred.++#### Cred-minting philosophy++As a matter of design philosophy, we believe cred should be minted at moments where there is review of an action, rather than the action itself. This reduces the danger of spam and reinforces the idea that contributors dictate what is valuable. For other projects adopting SourceCred, contributions that mint cred could also be actions performed by a moderator or maintainer.++#### Summary up to this point:++In this way, PageRank takes weights in and outputs scores that value contributions based on their relationship to each other. The eigenvector <img src="https://render.githubusercontent.com/render/math?math=\vec v"> then consists of the Cred scores for the various nodes, including the contributors, so we’ve accomplished our goal!++## Cred over time++#### Time-based Cred constraints++SourceCred has additionally innovated on PageRank to accommodate properties that Cred should demonstrate over time. The most important properties in this respect are that:++-   a contributor should never lose cred. They ought to become more diluted as other contributors continue to earn new cred, but they’ll still continue to earn Grain on that Cred.+-   a past action that was recently found to be valuable in the present should cause new Cred to flow to that action.++#### Time epochs and fibration++To simplify the computational overhead and storage required to compute Cred over time, SourceCred is moving toward a fibration model where a node is added per contributor, per unit time (currently weekly for SourceCred). In this model, epoch nodes intermediate flows to a user from any nodes during a period of time, and have a fixed transition probability to the user’s node, rather than a weight from which transition probabilities are calculated. The remaining transition probability is then shared between the seed node (for the teleportation vector) and the other node weights. This will be implemented by distinguishing graph edges, which have fixed weights, from markov edges, which have fixed transition probabilities from epoch nodes. This is then input into the mechanism that calculates the transition probabilities.++## Grain dynamics++#### How Grain is generated++Once SourceCred calculates the Cred for individual contributors, that Cred is linked to an identity that is an amalgamation of their identities across the platforms (e.g. GitHub handle or Discord username). A constant amount of SourceCred Grain (¤) is produced per unit time, currently 15000¤ per week. This amount is distributed according to two different distribution mechanisms.++-   20% of the grain is “fast,” and is disbursed to nodes in proportion to the cred earned by each node in the past week. This is intended to reward new participants.

I would focus less on "new participants" and more on "fast feedback"

miyazono

comment created time in 3 days

+# An introduction to calculating Cred and Grain++I’m starting from the assumption that you have some familiarity with the SourceCred project. If you don’t I’d suggest you check out Dandelion's [20 minute talk (+Qs) from SustainWeb3](https://www.youtube.com/watch?v=yVTqRLekRl4), and some of the other documents in this repo.++Overall, I’m going to throw around some math, but you should be able to get solid intuition on this if you’re comfortable talking about a [graph](https://en.wikipedia.org/wiki/Graph_theory) as a set of nodes and edges connecting them, where the edges can be directed and have weights. I’ll also talk some about [the PageRank algorithm](https://en.wikipedia.org/wiki/PageRank), for computing “importance” of nodes in a graph based on the “importance” of nodes connected to it, but I’ll try to motivate the intuition behind it as well.++## The high level overview++SourceCred takes information about contributions and generates a weights graph, and this post will first explain what that graph is and where it comes from. From this weights graph, it runs PageRank to generate a graph of Cred. This graph of Cred is then used to distribute grain. The grain can then be kept or traded, and soon will be able to be used to influence future graph weights. This process is diagrammed in the figure below.+ +![](https://docs.google.com/drawings/u/1/d/s50mxZJ1DFD_yr4OFUxUd9g/image?w=551&h=250&rev=57&ac=1&parent=1kRxnuKAogf_cE0y6u2QYcjQimE0-jrBZ-LxcXKOUITw)++> Figure 1. A very high level overview of how SourceCred allocates Cred and Grain, distinguishing between actions taken by contributors and those taken by the SourceCred algorithm.++The rest of this doc outlines how collaboration data is gathered and structured, and how the exact amounts of Cred and Grain are calculated, and ends with some future directions SourceCred as a project is considering.++## Components of contributing in SourceCred++SourceCred first generates a weights graph from the input data. In this graph, a node is a contributor or contribution, and edges represent some sort of connection between the nodes. These connections are intended to include all possible ways that a contribution or contributor on a platform can express dependencies, thanks, or flows of value. Examples would include anything from a mention on a forum post to liking or merging a PR on GitHub.++#### Nodes:++Contributions are represented as nodes in the graph, and are given addresses to identify them uniquely. These addresses include a prefix, which takes the form: organization making the plugin that generates the node + name of the plugin + type of contribution (e.g. topic/post/user/like).++Contributors are also represented as nodes in the graph. These nodes are similarly prefixed, and connected to contributions with directed edges, as illustrated in the figure below. For example, if an author node creates an issue, the author node gets an incoming edge from the issue node. Or if an author of a Discourse post mentions another contributor in the post, the post creates an outbound edge coming from the author.++Lastly, initiatives are another type of node that serve as a high level place to connect relevant nodes and discuss governance. It's similar to a tracking issue, but generalized across the weights graph, providing a way to organize planned work for a well-scoped task.++#### Weights:++Both nodes and edges have weights, and an edge object in the SourceCred implementation can have forward and backwards weight. We’ll get into what these weights mean shortly, but for now, it’s worth noting that these weights can easily be set by address prefix. Setting by prefix is implemented as a multiplicative update; for example, setting issue comments to be worth twice an issue emoji reaction creates a 2:1 ratio that is preserved by decreasing GitHub’s edge weights by 2 relative to those of Discourse. Additionally, nodes can have self edges, which is relevant when a piece of content is edited, or an contribution references itself.+  +![](https://docs.google.com/drawings/u/1/d/sIDpfiaPlACrxNScrfsISkQ/image?w=186&h=230&rev=47&ac=1&parent=1kRxnuKAogf_cE0y6u2QYcjQimE0-jrBZ-LxcXKOUITw)++> Figure 2: An example of a weights graph, where the nodes would be contributors or contributions, and the directed, weighted edges are connections. It’s a [directed graph](https://en.wikipedia.org/wiki/Graph_theory#Directed_graph) with weights on nodes and edges.++#### Timestamps:++Additionally, both edges and some nodes have timestamps. Edges are associated with actions or events, and as such, always have a timestamp. For nodes that correspond to a contribution, those nodes have a timestamp corresponding to the moment of the contribution. However, nodes corresponding to contributors do not have time stamps, since the contributors exist in the algorithm across time.++#### Summary up to this point:++Now that we know about components of the weights graph, their properties, and how they fit together, let’s cover how they’re generated.++## Generating the weights graph++#### Plugins:++SourceCred uses APIs of collaboration platforms to gather information about contributions. Currently there are plugins for GitHub and Discourse ([code](https://github.com/sourcecred/sourcecred/tree/master/src/plugins)), and we’re enthusiastic to have more plugins, so we can account for contributions in different venues. This includes Discord (in progress), and could extend to Twitter or citations on the [arXiv](https://arxiv.org/) or [eprint](https://eprint.iacr.org/).++#### Weights:++Because these plugins incorporate data from all sorts of contributions and output a single weighted, directed graph, there needs to be a way to quantitatively compare actions (e.g. authoring a GitHub issue vs responding or referencing it). To accomplish this, there’s a set of constants that determine weights for nodes and edges. For an example set of constants, see the below figure, captured from [sourcecred.io/cred/timeline/@sourcecred/](https://sourcecred.io/cred/timeline/@sourcecred/) (follow the link and click the “Show weight configuration” button).++![](https://lh6.googleusercontent.com/BDSrcvjcCXXfKQ0NSiHIYuAh8fvTaE0Akl0g2-PLdB88fv5TXZS7btltL0YTe77TLOT7nyImct7sPrKATbAQKUwukM3bdxgVfIJCf-xDZo0nvGnFINMoqXq41glKAI9hA2EnThed)++> Figure 3. A set of weight sliders to set parameters controlling the relative weights of different contributions.++Additionally, parameters, cred, and grain are all scoped to a community. This means that the community that supports a project sets and controls the parameters. For that matter, it’s worth reiterating that (lowercase) cred and grain generated for a project are specific to that project. SourceCred is about providing community control, keeping humans in the loop, both for gaming mitigation and general adaptability. At the bottom line, it’s important to keep the humans in control, because good governance technology only works with the consent of the governed.++#### Manual input:++It’s also worth noting that not all contributions occur on platforms with automated data fetching, and thus the capability exists to individually add them to the weighted graph directly at this stage. It’s clear that there will always be data that has to be incorporated manually, and how to incorporate those contributions, especially those not easily quantified, remains an active area of discussion.++#### Merging graphs:++A plugin will use the API for the collaborative workspace app or platform to generate a graph of nodes and edges. One interesting caveat is that there isn't necessarily one most-intuitively correct way to merge two weighted graphs if they share either edges or nodes (i.e. how would we calculate the combined weight). Currently, the graph nodes are nearly entirely disjoint, the weights are added together, and identities are collapsed into one.++#### Summary up to this point:++So at this point, we’ve got data from all our sources combined in one weighted, directed graph, where contributions and contributors are nodes, interactions and connections between nodes are edges, each have a relative weight based on parameters the community has set, and we’re ready to run PageRank on it.++## SourceCred’s PageRank++The next goal will be to generate an evaluation of the relative importance of nodes based on our weighted graph, specifically the weights of the nodes and edges.++#### PageRank explained elsewhere++Before reading on, it’s important to understand what PageRank does. There are other good explanations out there, and keeping this brief seems useful, so I won’t add another explanation. If you like over preparing, Brilliant.com’s explanation of [Markov Chains](https://brilliant.org/wiki/markov-chains/#transition-matrices), [Stationary Distributions](https://brilliant.org/wiki/stationary-distributions/) and [their quiz](https://brilliant.org/practice/basic-markov-chains) is more than sufficient. If you’re a visual thinker, [these slides](https://www.cl.cam.ac.uk/teaching/1516/InfoRtrv/lecture8-link-analysis-2x2.pdf) provide some nice figures.++<img src="https://upload.wikimedia.org/wikipedia/commons/6/69/PageRank-hi-res.png" width="400">++> Figure 4. An illustration of PageRank, in which the size of each node is proportional to the total size of the nodes linking to it.++#### Stationary distribution intuition++The simplest formulation of PageRank operates over an unweighted graph, treating all links as equivalent, as illustrated in the figure above. It assigns all outbound links equal probability and calculates the stationary distribution of node weights. (Intuitively, the stationary distribution is the equilibrium fraction of water that ends up in each node “tank” if you run a pump along each edge for a while – for “reasonable” graph configurations you’d expect the water distribution to end up the same regardless of where you dump the water initially.)++#### Stationary distribution as an eigenvector++If you’ve spend more time in linear algebra land, you’ll be pleased to hear that common way to compute this (in algebraic graph theory) is to convert the graph to a matrix _P_ where each entry <img src="https://render.githubusercontent.com/render/math?math=P_{uv}"> is the transition probability from node _u_ to node _v_. Each dimension corresponds to a node, and the matrix maps from all nodes to all nodes. Then finding the stationary distribution is the same as finding [the eigenvector](https://www.youtube.com/watch?v=PFDu9oVAE-g) of the transition matrix that corresponds to eigenvalue 1; by the definition of an eigenvector, this vector is the distribution of value to each node such that if value flowed down each edge in proportion to its weight, the resulting value distribution would remain the same.++#### Incorporate edge weights++We can adapt the algorithm to account for edge weights on the input graph fairly intuitively – we could add weights to the links by increasing the number of edges for some types of actions (e.g. 👍=1 edge, ♥️=2 edges). In the probability matrix formulation, it’s simple to increase the transition probability of one edge. Then, instead of assigning equal probability to all outbound edges, we can simply normalize the weights so that they sum to one and this provides us with transition probabilities needed for <img src="https://render.githubusercontent.com/render/math?math=P_{uv}">.++#### Incorporate node weights++There’s also a cleverly simple way to account for the existence of node weights. One requirement for the existence of a stationary distribution that was skipped is the concept of ergodicity, wherein every state must be reachable from any other state, given a long enough probabilistic walk across the graph’s edges†. To achieve ergodicity, SourceCred uses a "teleportation" system by adding a probability, ⍺, of teleporting to a random location in the graph. SourceCred currently implements this with a seed node, which all nodes teleporting to with likelihood ⍺. From the seed node, the probability of landing on a given node in the graph is then given by the node weights. If we use _A_ to describe the weights of each node in a vector form, then our old transition matrix <img src="https://render.githubusercontent.com/render/math?math=P_{uv}"> becomes the new transition matrix <img src="https://render.githubusercontent.com/render/math?math=(1-\alpha)P_{uv} + \alpha A">. The stationary distribution is then the vector <img src="https://render.githubusercontent.com/render/math?math=\vec v"> such that <img src="https://render.githubusercontent.com/render/math?math=\left((1-\alpha)P_{uv} + \alpha A\right)\vec v = \vec v">.++#### The origin of Cred++In this way, Cred originates at the weighted nodes and flows out to actions from there. Interestingly, this is mathematically equivalent to adding edges to each and every node, with edge-weight proportional to ⍺ times the node weight. We can interpret the necessity of teleportation in the following way: because some contributions may receive but flow no cred, there need to be sources of Cred to calculate an equilibrium value distribution, which requires some contributions to mint cred.++#### Cred-minting philosophy++As a matter of design philosophy, we believe cred should be minted at moments where there is review of an action, rather than the action itself. This reduces the danger of spam and reinforces the idea that contributors dictate what is valuable. For other projects adopting SourceCred, contributions that mint cred could also be actions performed by a moderator or maintainer.++#### Summary up to this point:++In this way, PageRank takes weights in and outputs scores that value contributions based on their relationship to each other. The eigenvector <img src="https://render.githubusercontent.com/render/math?math=\vec v"> then consists of the Cred scores for the various nodes, including the contributors, so we’ve accomplished our goal!++## Cred over time++#### Time-based Cred constraints++SourceCred has additionally innovated on PageRank to accommodate properties that Cred should demonstrate over time. The most important properties in this respect are that:++-   a contributor should never lose cred. They ought to become more diluted as other contributors continue to earn new cred, but they’ll still continue to earn Grain on that Cred.+-   a past action that was recently found to be valuable in the present should cause new Cred to flow to that action.++#### Time epochs and fibration++To simplify the computational overhead and storage required to compute Cred over time, SourceCred is moving toward a fibration model where a node is added per contributor, per unit time (currently weekly for SourceCred). In this model, epoch nodes intermediate flows to a user from any nodes during a period of time, and have a fixed transition probability to the user’s node, rather than a weight from which transition probabilities are calculated. The remaining transition probability is then shared between the seed node (for the teleportation vector) and the other node weights. This will be implemented by distinguishing graph edges, which have fixed weights, from markov edges, which have fixed transition probabilities from epoch nodes. This is then input into the mechanism that calculates the transition probabilities.++## Grain dynamics++#### How Grain is generated++Once SourceCred calculates the Cred for individual contributors, that Cred is linked to an identity that is an amalgamation of their identities across the platforms (e.g. GitHub handle or Discord username). A constant amount of SourceCred Grain (¤) is produced per unit time, currently 15000¤ per week. This amount is distributed according to two different distribution mechanisms.++-   20% of the grain is “fast,” and is disbursed to nodes in proportion to the cred earned by each node in the past week. This is intended to reward new participants.+-   80% of the grain is “slow,” and is disbursed in proportion to the difference between total grain you've been distributed, and what that node’s proportional fair Cred would be over all history++As a result, if you didn't do anything this week, you have only slow Grain. Alternatively, if you joined this week, you’ll only have fast Grain. The fast component can potentially over-distribute grain, but it will get diluted by other contributions.++#### How Grain is traded:++Grain payouts are currently tracked by Dandelion in an observable notebook linked to in [this Discourse thread](https://discourse.sourcecred.io/t/sourcecred-contributor-payouts/298). Additionally, Grain distributions will vest over time in order to smooth out the peakiness of grain distribution, as described in [this Discourse thread](https://discourse.sourcecred.io/t/grain-vesting/636). Even now, Grain can be sold or given. In fact, [Protocol Labs](https://protocol.ai/) supports SourceCred by purchasing Grain directly from contributors. At present, the transfer of Grain is executed by recording changes to the observable notebook on GitHub [here](https://github.com/sourcecred/cred).

Mention that we currently consider grain a "preview" or experimental balance, though we plan to honor it going forward.

miyazono

comment created time in 3 days

+# An introduction to calculating Cred and Grain++I’m starting from the assumption that you have some familiarity with the SourceCred project. If you don’t I’d suggest you check out Dandelion's [20 minute talk (+Qs) from SustainWeb3](https://www.youtube.com/watch?v=yVTqRLekRl4), and some of the other documents in this repo.++Overall, I’m going to throw around some math, but you should be able to get solid intuition on this if you’re comfortable talking about a [graph](https://en.wikipedia.org/wiki/Graph_theory) as a set of nodes and edges connecting them, where the edges can be directed and have weights. I’ll also talk some about [the PageRank algorithm](https://en.wikipedia.org/wiki/PageRank), for computing “importance” of nodes in a graph based on the “importance” of nodes connected to it, but I’ll try to motivate the intuition behind it as well.++## The high level overview++SourceCred takes information about contributions and generates a weights graph, and this post will first explain what that graph is and where it comes from. From this weights graph, it runs PageRank to generate a graph of Cred. This graph of Cred is then used to distribute grain. The grain can then be kept or traded, and soon will be able to be used to influence future graph weights. This process is diagrammed in the figure below.+ +![](https://docs.google.com/drawings/u/1/d/s50mxZJ1DFD_yr4OFUxUd9g/image?w=551&h=250&rev=57&ac=1&parent=1kRxnuKAogf_cE0y6u2QYcjQimE0-jrBZ-LxcXKOUITw)++> Figure 1. A very high level overview of how SourceCred allocates Cred and Grain, distinguishing between actions taken by contributors and those taken by the SourceCred algorithm.++The rest of this doc outlines how collaboration data is gathered and structured, and how the exact amounts of Cred and Grain are calculated, and ends with some future directions SourceCred as a project is considering.++## Components of contributing in SourceCred++SourceCred first generates a weights graph from the input data. In this graph, a node is a contributor or contribution, and edges represent some sort of connection between the nodes. These connections are intended to include all possible ways that a contribution or contributor on a platform can express dependencies, thanks, or flows of value. Examples would include anything from a mention on a forum post to liking or merging a PR on GitHub.++#### Nodes:++Contributions are represented as nodes in the graph, and are given addresses to identify them uniquely. These addresses include a prefix, which takes the form: organization making the plugin that generates the node + name of the plugin + type of contribution (e.g. topic/post/user/like).++Contributors are also represented as nodes in the graph. These nodes are similarly prefixed, and connected to contributions with directed edges, as illustrated in the figure below. For example, if an author node creates an issue, the author node gets an incoming edge from the issue node. Or if an author of a Discourse post mentions another contributor in the post, the post creates an outbound edge coming from the author.++Lastly, initiatives are another type of node that serve as a high level place to connect relevant nodes and discuss governance. It's similar to a tracking issue, but generalized across the weights graph, providing a way to organize planned work for a well-scoped task.++#### Weights:++Both nodes and edges have weights, and an edge object in the SourceCred implementation can have forward and backwards weight. We’ll get into what these weights mean shortly, but for now, it’s worth noting that these weights can easily be set by address prefix. Setting by prefix is implemented as a multiplicative update; for example, setting issue comments to be worth twice an issue emoji reaction creates a 2:1 ratio that is preserved by decreasing GitHub’s edge weights by 2 relative to those of Discourse. Additionally, nodes can have self edges, which is relevant when a piece of content is edited, or an contribution references itself.+  +![](https://docs.google.com/drawings/u/1/d/sIDpfiaPlACrxNScrfsISkQ/image?w=186&h=230&rev=47&ac=1&parent=1kRxnuKAogf_cE0y6u2QYcjQimE0-jrBZ-LxcXKOUITw)++> Figure 2: An example of a weights graph, where the nodes would be contributors or contributions, and the directed, weighted edges are connections. It’s a [directed graph](https://en.wikipedia.org/wiki/Graph_theory#Directed_graph) with weights on nodes and edges.++#### Timestamps:++Additionally, both edges and some nodes have timestamps. Edges are associated with actions or events, and as such, always have a timestamp. For nodes that correspond to a contribution, those nodes have a timestamp corresponding to the moment of the contribution. However, nodes corresponding to contributors do not have time stamps, since the contributors exist in the algorithm across time.++#### Summary up to this point:++Now that we know about components of the weights graph, their properties, and how they fit together, let’s cover how they’re generated.++## Generating the weights graph++#### Plugins:++SourceCred uses APIs of collaboration platforms to gather information about contributions. Currently there are plugins for GitHub and Discourse ([code](https://github.com/sourcecred/sourcecred/tree/master/src/plugins)), and we’re enthusiastic to have more plugins, so we can account for contributions in different venues. This includes Discord (in progress), and could extend to Twitter or citations on the [arXiv](https://arxiv.org/) or [eprint](https://eprint.iacr.org/).++#### Weights:++Because these plugins incorporate data from all sorts of contributions and output a single weighted, directed graph, there needs to be a way to quantitatively compare actions (e.g. authoring a GitHub issue vs responding or referencing it). To accomplish this, there’s a set of constants that determine weights for nodes and edges. For an example set of constants, see the below figure, captured from [sourcecred.io/cred/timeline/@sourcecred/](https://sourcecred.io/cred/timeline/@sourcecred/) (follow the link and click the “Show weight configuration” button).++![](https://lh6.googleusercontent.com/BDSrcvjcCXXfKQ0NSiHIYuAh8fvTaE0Akl0g2-PLdB88fv5TXZS7btltL0YTe77TLOT7nyImct7sPrKATbAQKUwukM3bdxgVfIJCf-xDZo0nvGnFINMoqXq41glKAI9hA2EnThed)++> Figure 3. A set of weight sliders to set parameters controlling the relative weights of different contributions.++Additionally, parameters, cred, and grain are all scoped to a community. This means that the community that supports a project sets and controls the parameters. For that matter, it’s worth reiterating that (lowercase) cred and grain generated for a project are specific to that project. SourceCred is about providing community control, keeping humans in the loop, both for gaming mitigation and general adaptability. At the bottom line, it’s important to keep the humans in control, because good governance technology only works with the consent of the governed.++#### Manual input:++It’s also worth noting that not all contributions occur on platforms with automated data fetching, and thus the capability exists to individually add them to the weighted graph directly at this stage. It’s clear that there will always be data that has to be incorporated manually, and how to incorporate those contributions, especially those not easily quantified, remains an active area of discussion.++#### Merging graphs:++A plugin will use the API for the collaborative workspace app or platform to generate a graph of nodes and edges. One interesting caveat is that there isn't necessarily one most-intuitively correct way to merge two weighted graphs if they share either edges or nodes (i.e. how would we calculate the combined weight). Currently, the graph nodes are nearly entirely disjoint, the weights are added together, and identities are collapsed into one.++#### Summary up to this point:++So at this point, we’ve got data from all our sources combined in one weighted, directed graph, where contributions and contributors are nodes, interactions and connections between nodes are edges, each have a relative weight based on parameters the community has set, and we’re ready to run PageRank on it.++## SourceCred’s PageRank++The next goal will be to generate an evaluation of the relative importance of nodes based on our weighted graph, specifically the weights of the nodes and edges.++#### PageRank explained elsewhere++Before reading on, it’s important to understand what PageRank does. There are other good explanations out there, and keeping this brief seems useful, so I won’t add another explanation. If you like over preparing, Brilliant.com’s explanation of [Markov Chains](https://brilliant.org/wiki/markov-chains/#transition-matrices), [Stationary Distributions](https://brilliant.org/wiki/stationary-distributions/) and [their quiz](https://brilliant.org/practice/basic-markov-chains) is more than sufficient. If you’re a visual thinker, [these slides](https://www.cl.cam.ac.uk/teaching/1516/InfoRtrv/lecture8-link-analysis-2x2.pdf) provide some nice figures.++<img src="https://upload.wikimedia.org/wikipedia/commons/6/69/PageRank-hi-res.png" width="400">++> Figure 4. An illustration of PageRank, in which the size of each node is proportional to the total size of the nodes linking to it.++#### Stationary distribution intuition++The simplest formulation of PageRank operates over an unweighted graph, treating all links as equivalent, as illustrated in the figure above. It assigns all outbound links equal probability and calculates the stationary distribution of node weights. (Intuitively, the stationary distribution is the equilibrium fraction of water that ends up in each node “tank” if you run a pump along each edge for a while – for “reasonable” graph configurations you’d expect the water distribution to end up the same regardless of where you dump the water initially.)++#### Stationary distribution as an eigenvector++If you’ve spend more time in linear algebra land, you’ll be pleased to hear that common way to compute this (in algebraic graph theory) is to convert the graph to a matrix _P_ where each entry <img src="https://render.githubusercontent.com/render/math?math=P_{uv}"> is the transition probability from node _u_ to node _v_. Each dimension corresponds to a node, and the matrix maps from all nodes to all nodes. Then finding the stationary distribution is the same as finding [the eigenvector](https://www.youtube.com/watch?v=PFDu9oVAE-g) of the transition matrix that corresponds to eigenvalue 1; by the definition of an eigenvector, this vector is the distribution of value to each node such that if value flowed down each edge in proportion to its weight, the resulting value distribution would remain the same.++#### Incorporate edge weights++We can adapt the algorithm to account for edge weights on the input graph fairly intuitively – we could add weights to the links by increasing the number of edges for some types of actions (e.g. 👍=1 edge, ♥️=2 edges). In the probability matrix formulation, it’s simple to increase the transition probability of one edge. Then, instead of assigning equal probability to all outbound edges, we can simply normalize the weights so that they sum to one and this provides us with transition probabilities needed for <img src="https://render.githubusercontent.com/render/math?math=P_{uv}">.++#### Incorporate node weights++There’s also a cleverly simple way to account for the existence of node weights. One requirement for the existence of a stationary distribution that was skipped is the concept of ergodicity, wherein every state must be reachable from any other state, given a long enough probabilistic walk across the graph’s edges†. To achieve ergodicity, SourceCred uses a "teleportation" system by adding a probability, ⍺, of teleporting to a random location in the graph. SourceCred currently implements this with a seed node, which all nodes teleporting to with likelihood ⍺. From the seed node, the probability of landing on a given node in the graph is then given by the node weights. If we use _A_ to describe the weights of each node in a vector form, then our old transition matrix <img src="https://render.githubusercontent.com/render/math?math=P_{uv}"> becomes the new transition matrix <img src="https://render.githubusercontent.com/render/math?math=(1-\alpha)P_{uv} + \alpha A">. The stationary distribution is then the vector <img src="https://render.githubusercontent.com/render/math?math=\vec v"> such that <img src="https://render.githubusercontent.com/render/math?math=\left((1-\alpha)P_{uv} + \alpha A\right)\vec v = \vec v">.++#### The origin of Cred++In this way, Cred originates at the weighted nodes and flows out to actions from there. Interestingly, this is mathematically equivalent to adding edges to each and every node, with edge-weight proportional to ⍺ times the node weight. We can interpret the necessity of teleportation in the following way: because some contributions may receive but flow no cred, there need to be sources of Cred to calculate an equilibrium value distribution, which requires some contributions to mint cred.++#### Cred-minting philosophy++As a matter of design philosophy, we believe cred should be minted at moments where there is review of an action, rather than the action itself. This reduces the danger of spam and reinforces the idea that contributors dictate what is valuable. For other projects adopting SourceCred, contributions that mint cred could also be actions performed by a moderator or maintainer.++#### Summary up to this point:++In this way, PageRank takes weights in and outputs scores that value contributions based on their relationship to each other. The eigenvector <img src="https://render.githubusercontent.com/render/math?math=\vec v"> then consists of the Cred scores for the various nodes, including the contributors, so we’ve accomplished our goal!++## Cred over time++#### Time-based Cred constraints++SourceCred has additionally innovated on PageRank to accommodate properties that Cred should demonstrate over time. The most important properties in this respect are that:++-   a contributor should never lose cred. They ought to become more diluted as other contributors continue to earn new cred, but they’ll still continue to earn Grain on that Cred.

"A contributor should never lose cred" -- I don't agree with this. Consider the following example.

We've boosted feature X so that it's worth 100 cred. I go ahead and do initial work on feature X, and receive most of that 100 cred. It transpires that my work was buggy and unmaintainable, and so now new contributors need to come and rewrite feature. Their new edges dilute the 100 cred, so now I only get (say) 30 cred. I've now "lost" cred.

miyazono

comment created time in 3 days

+# An introduction to calculating Cred and Grain++I’m starting from the assumption that you have some familiarity with the SourceCred project. If you don’t I’d suggest you check out Dandelion's [20 minute talk (+Qs) from SustainWeb3](https://www.youtube.com/watch?v=yVTqRLekRl4), and some of the other documents in this repo.++Overall, I’m going to throw around some math, but you should be able to get solid intuition on this if you’re comfortable talking about a [graph](https://en.wikipedia.org/wiki/Graph_theory) as a set of nodes and edges connecting them, where the edges can be directed and have weights. I’ll also talk some about [the PageRank algorithm](https://en.wikipedia.org/wiki/PageRank), for computing “importance” of nodes in a graph based on the “importance” of nodes connected to it, but I’ll try to motivate the intuition behind it as well.++## The high level overview++SourceCred takes information about contributions and generates a weights graph, and this post will first explain what that graph is and where it comes from. From this weights graph, it runs PageRank to generate a graph of Cred. This graph of Cred is then used to distribute grain. The grain can then be kept or traded, and soon will be able to be used to influence future graph weights. This process is diagrammed in the figure below.+ +![](https://docs.google.com/drawings/u/1/d/s50mxZJ1DFD_yr4OFUxUd9g/image?w=551&h=250&rev=57&ac=1&parent=1kRxnuKAogf_cE0y6u2QYcjQimE0-jrBZ-LxcXKOUITw)++> Figure 1. A very high level overview of how SourceCred allocates Cred and Grain, distinguishing between actions taken by contributors and those taken by the SourceCred algorithm.++The rest of this doc outlines how collaboration data is gathered and structured, and how the exact amounts of Cred and Grain are calculated, and ends with some future directions SourceCred as a project is considering.++## Components of contributing in SourceCred++SourceCred first generates a weights graph from the input data. In this graph, a node is a contributor or contribution, and edges represent some sort of connection between the nodes. These connections are intended to include all possible ways that a contribution or contributor on a platform can express dependencies, thanks, or flows of value. Examples would include anything from a mention on a forum post to liking or merging a PR on GitHub.++#### Nodes:++Contributions are represented as nodes in the graph, and are given addresses to identify them uniquely. These addresses include a prefix, which takes the form: organization making the plugin that generates the node + name of the plugin + type of contribution (e.g. topic/post/user/like).++Contributors are also represented as nodes in the graph. These nodes are similarly prefixed, and connected to contributions with directed edges, as illustrated in the figure below. For example, if an author node creates an issue, the author node gets an incoming edge from the issue node. Or if an author of a Discourse post mentions another contributor in the post, the post creates an outbound edge coming from the author.++Lastly, initiatives are another type of node that serve as a high level place to connect relevant nodes and discuss governance. It's similar to a tracking issue, but generalized across the weights graph, providing a way to organize planned work for a well-scoped task.++#### Weights:++Both nodes and edges have weights, and an edge object in the SourceCred implementation can have forward and backwards weight. We’ll get into what these weights mean shortly, but for now, it’s worth noting that these weights can easily be set by address prefix. Setting by prefix is implemented as a multiplicative update; for example, setting issue comments to be worth twice an issue emoji reaction creates a 2:1 ratio that is preserved by decreasing GitHub’s edge weights by 2 relative to those of Discourse. Additionally, nodes can have self edges, which is relevant when a piece of content is edited, or an contribution references itself.+  +![](https://docs.google.com/drawings/u/1/d/sIDpfiaPlACrxNScrfsISkQ/image?w=186&h=230&rev=47&ac=1&parent=1kRxnuKAogf_cE0y6u2QYcjQimE0-jrBZ-LxcXKOUITw)++> Figure 2: An example of a weights graph, where the nodes would be contributors or contributions, and the directed, weighted edges are connections. It’s a [directed graph](https://en.wikipedia.org/wiki/Graph_theory#Directed_graph) with weights on nodes and edges.++#### Timestamps:++Additionally, both edges and some nodes have timestamps. Edges are associated with actions or events, and as such, always have a timestamp. For nodes that correspond to a contribution, those nodes have a timestamp corresponding to the moment of the contribution. However, nodes corresponding to contributors do not have time stamps, since the contributors exist in the algorithm across time.++#### Summary up to this point:++Now that we know about components of the weights graph, their properties, and how they fit together, let’s cover how they’re generated.++## Generating the weights graph++#### Plugins:++SourceCred uses APIs of collaboration platforms to gather information about contributions. Currently there are plugins for GitHub and Discourse ([code](https://github.com/sourcecred/sourcecred/tree/master/src/plugins)), and we’re enthusiastic to have more plugins, so we can account for contributions in different venues. This includes Discord (in progress), and could extend to Twitter or citations on the [arXiv](https://arxiv.org/) or [eprint](https://eprint.iacr.org/).++#### Weights:++Because these plugins incorporate data from all sorts of contributions and output a single weighted, directed graph, there needs to be a way to quantitatively compare actions (e.g. authoring a GitHub issue vs responding or referencing it). To accomplish this, there’s a set of constants that determine weights for nodes and edges. For an example set of constants, see the below figure, captured from [sourcecred.io/cred/timeline/@sourcecred/](https://sourcecred.io/cred/timeline/@sourcecred/) (follow the link and click the “Show weight configuration” button).++![](https://lh6.googleusercontent.com/BDSrcvjcCXXfKQ0NSiHIYuAh8fvTaE0Akl0g2-PLdB88fv5TXZS7btltL0YTe77TLOT7nyImct7sPrKATbAQKUwukM3bdxgVfIJCf-xDZo0nvGnFINMoqXq41glKAI9hA2EnThed)++> Figure 3. A set of weight sliders to set parameters controlling the relative weights of different contributions.++Additionally, parameters, cred, and grain are all scoped to a community. This means that the community that supports a project sets and controls the parameters. For that matter, it’s worth reiterating that (lowercase) cred and grain generated for a project are specific to that project. SourceCred is about providing community control, keeping humans in the loop, both for gaming mitigation and general adaptability. At the bottom line, it’s important to keep the humans in control, because good governance technology only works with the consent of the governed.++#### Manual input:++It’s also worth noting that not all contributions occur on platforms with automated data fetching, and thus the capability exists to individually add them to the weighted graph directly at this stage. It’s clear that there will always be data that has to be incorporated manually, and how to incorporate those contributions, especially those not easily quantified, remains an active area of discussion.++#### Merging graphs:++A plugin will use the API for the collaborative workspace app or platform to generate a graph of nodes and edges. One interesting caveat is that there isn't necessarily one most-intuitively correct way to merge two weighted graphs if they share either edges or nodes (i.e. how would we calculate the combined weight). Currently, the graph nodes are nearly entirely disjoint, the weights are added together, and identities are collapsed into one.++#### Summary up to this point:++So at this point, we’ve got data from all our sources combined in one weighted, directed graph, where contributions and contributors are nodes, interactions and connections between nodes are edges, each have a relative weight based on parameters the community has set, and we’re ready to run PageRank on it.++## SourceCred’s PageRank++The next goal will be to generate an evaluation of the relative importance of nodes based on our weighted graph, specifically the weights of the nodes and edges.++#### PageRank explained elsewhere++Before reading on, it’s important to understand what PageRank does. There are other good explanations out there, and keeping this brief seems useful, so I won’t add another explanation. If you like over preparing, Brilliant.com’s explanation of [Markov Chains](https://brilliant.org/wiki/markov-chains/#transition-matrices), [Stationary Distributions](https://brilliant.org/wiki/stationary-distributions/) and [their quiz](https://brilliant.org/practice/basic-markov-chains) is more than sufficient. If you’re a visual thinker, [these slides](https://www.cl.cam.ac.uk/teaching/1516/InfoRtrv/lecture8-link-analysis-2x2.pdf) provide some nice figures.++<img src="https://upload.wikimedia.org/wikipedia/commons/6/69/PageRank-hi-res.png" width="400">++> Figure 4. An illustration of PageRank, in which the size of each node is proportional to the total size of the nodes linking to it.++#### Stationary distribution intuition++The simplest formulation of PageRank operates over an unweighted graph, treating all links as equivalent, as illustrated in the figure above. It assigns all outbound links equal probability and calculates the stationary distribution of node weights. (Intuitively, the stationary distribution is the equilibrium fraction of water that ends up in each node “tank” if you run a pump along each edge for a while – for “reasonable” graph configurations you’d expect the water distribution to end up the same regardless of where you dump the water initially.)++#### Stationary distribution as an eigenvector++If you’ve spend more time in linear algebra land, you’ll be pleased to hear that common way to compute this (in algebraic graph theory) is to convert the graph to a matrix _P_ where each entry <img src="https://render.githubusercontent.com/render/math?math=P_{uv}"> is the transition probability from node _u_ to node _v_. Each dimension corresponds to a node, and the matrix maps from all nodes to all nodes. Then finding the stationary distribution is the same as finding [the eigenvector](https://www.youtube.com/watch?v=PFDu9oVAE-g) of the transition matrix that corresponds to eigenvalue 1; by the definition of an eigenvector, this vector is the distribution of value to each node such that if value flowed down each edge in proportion to its weight, the resulting value distribution would remain the same.++#### Incorporate edge weights++We can adapt the algorithm to account for edge weights on the input graph fairly intuitively – we could add weights to the links by increasing the number of edges for some types of actions (e.g. 👍=1 edge, ♥️=2 edges). In the probability matrix formulation, it’s simple to increase the transition probability of one edge. Then, instead of assigning equal probability to all outbound edges, we can simply normalize the weights so that they sum to one and this provides us with transition probabilities needed for <img src="https://render.githubusercontent.com/render/math?math=P_{uv}">.++#### Incorporate node weights++There’s also a cleverly simple way to account for the existence of node weights. One requirement for the existence of a stationary distribution that was skipped is the concept of ergodicity, wherein every state must be reachable from any other state, given a long enough probabilistic walk across the graph’s edges†. To achieve ergodicity, SourceCred uses a "teleportation" system by adding a probability, ⍺, of teleporting to a random location in the graph. SourceCred currently implements this with a seed node, which all nodes teleporting to with likelihood ⍺. From the seed node, the probability of landing on a given node in the graph is then given by the node weights. If we use _A_ to describe the weights of each node in a vector form, then our old transition matrix <img src="https://render.githubusercontent.com/render/math?math=P_{uv}"> becomes the new transition matrix <img src="https://render.githubusercontent.com/render/math?math=(1-\alpha)P_{uv} + \alpha A">. The stationary distribution is then the vector <img src="https://render.githubusercontent.com/render/math?math=\vec v"> such that <img src="https://render.githubusercontent.com/render/math?math=\left((1-\alpha)P_{uv} + \alpha A\right)\vec v = \vec v">.++#### The origin of Cred++In this way, Cred originates at the weighted nodes and flows out to actions from there. Interestingly, this is mathematically equivalent to adding edges to each and every node, with edge-weight proportional to ⍺ times the node weight. We can interpret the necessity of teleportation in the following way: because some contributions may receive but flow no cred, there need to be sources of Cred to calculate an equilibrium value distribution, which requires some contributions to mint cred.++#### Cred-minting philosophy++As a matter of design philosophy, we believe cred should be minted at moments where there is review of an action, rather than the action itself. This reduces the danger of spam and reinforces the idea that contributors dictate what is valuable. For other projects adopting SourceCred, contributions that mint cred could also be actions performed by a moderator or maintainer.++#### Summary up to this point:++In this way, PageRank takes weights in and outputs scores that value contributions based on their relationship to each other. The eigenvector <img src="https://render.githubusercontent.com/render/math?math=\vec v"> then consists of the Cred scores for the various nodes, including the contributors, so we’ve accomplished our goal!++## Cred over time++#### Time-based Cred constraints++SourceCred has additionally innovated on PageRank to accommodate properties that Cred should demonstrate over time. The most important properties in this respect are that:++-   a contributor should never lose cred. They ought to become more diluted as other contributors continue to earn new cred, but they’ll still continue to earn Grain on that Cred.+-   a past action that was recently found to be valuable in the present should cause new Cred to flow to that action.++#### Time epochs and fibration++To simplify the computational overhead and storage required to compute Cred over time, SourceCred is moving toward a fibration model where a node is added per contributor, per unit time (currently weekly for SourceCred). In this model, epoch nodes intermediate flows to a user from any nodes during a period of time, and have a fixed transition probability to the user’s node, rather than a weight from which transition probabilities are calculated. The remaining transition probability is then shared between the seed node (for the teleportation vector) and the other node weights. This will be implemented by distinguishing graph edges, which have fixed weights, from markov edges, which have fixed transition probabilities from epoch nodes. This is then input into the mechanism that calculates the transition probabilities.

I recommend before/after diagram like the one that I drew earlier today. :)

miyazono

comment created time in 3 days

+# An introduction to calculating Cred and Grain++I’m starting from the assumption that you have some familiarity with the SourceCred project. If you don’t I’d suggest you check out Dandelion's [20 minute talk (+Qs) from SustainWeb3](https://www.youtube.com/watch?v=yVTqRLekRl4), and some of the other documents in this repo.++Overall, I’m going to throw around some math, but you should be able to get solid intuition on this if you’re comfortable talking about a [graph](https://en.wikipedia.org/wiki/Graph_theory) as a set of nodes and edges connecting them, where the edges can be directed and have weights. I’ll also talk some about [the PageRank algorithm](https://en.wikipedia.org/wiki/PageRank), for computing “importance” of nodes in a graph based on the “importance” of nodes connected to it, but I’ll try to motivate the intuition behind it as well.++## The high level overview++SourceCred takes information about contributions and generates a weights graph, and this post will first explain what that graph is and where it comes from. From this weights graph, it runs PageRank to generate a graph of Cred. This graph of Cred is then used to distribute grain. The grain can then be kept or traded, and soon will be able to be used to influence future graph weights. This process is diagrammed in the figure below.+ +![](https://docs.google.com/drawings/u/1/d/s50mxZJ1DFD_yr4OFUxUd9g/image?w=551&h=250&rev=57&ac=1&parent=1kRxnuKAogf_cE0y6u2QYcjQimE0-jrBZ-LxcXKOUITw)++> Figure 1. A very high level overview of how SourceCred allocates Cred and Grain, distinguishing between actions taken by contributors and those taken by the SourceCred algorithm.++The rest of this doc outlines how collaboration data is gathered and structured, and how the exact amounts of Cred and Grain are calculated, and ends with some future directions SourceCred as a project is considering.++## Components of contributing in SourceCred++SourceCred first generates a weights graph from the input data. In this graph, a node is a contributor or contribution, and edges represent some sort of connection between the nodes. These connections are intended to include all possible ways that a contribution or contributor on a platform can express dependencies, thanks, or flows of value. Examples would include anything from a mention on a forum post to liking or merging a PR on GitHub.++#### Nodes:++Contributions are represented as nodes in the graph, and are given addresses to identify them uniquely. These addresses include a prefix, which takes the form: organization making the plugin that generates the node + name of the plugin + type of contribution (e.g. topic/post/user/like).++Contributors are also represented as nodes in the graph. These nodes are similarly prefixed, and connected to contributions with directed edges, as illustrated in the figure below. For example, if an author node creates an issue, the author node gets an incoming edge from the issue node. Or if an author of a Discourse post mentions another contributor in the post, the post creates an outbound edge coming from the author.++Lastly, initiatives are another type of node that serve as a high level place to connect relevant nodes and discuss governance. It's similar to a tracking issue, but generalized across the weights graph, providing a way to organize planned work for a well-scoped task.++#### Weights:++Both nodes and edges have weights, and an edge object in the SourceCred implementation can have forward and backwards weight. We’ll get into what these weights mean shortly, but for now, it’s worth noting that these weights can easily be set by address prefix. Setting by prefix is implemented as a multiplicative update; for example, setting issue comments to be worth twice an issue emoji reaction creates a 2:1 ratio that is preserved by decreasing GitHub’s edge weights by 2 relative to those of Discourse. Additionally, nodes can have self edges, which is relevant when a piece of content is edited, or an contribution references itself.+  +![](https://docs.google.com/drawings/u/1/d/sIDpfiaPlACrxNScrfsISkQ/image?w=186&h=230&rev=47&ac=1&parent=1kRxnuKAogf_cE0y6u2QYcjQimE0-jrBZ-LxcXKOUITw)++> Figure 2: An example of a weights graph, where the nodes would be contributors or contributions, and the directed, weighted edges are connections. It’s a [directed graph](https://en.wikipedia.org/wiki/Graph_theory#Directed_graph) with weights on nodes and edges.++#### Timestamps:++Additionally, both edges and some nodes have timestamps. Edges are associated with actions or events, and as such, always have a timestamp. For nodes that correspond to a contribution, those nodes have a timestamp corresponding to the moment of the contribution. However, nodes corresponding to contributors do not have time stamps, since the contributors exist in the algorithm across time.++#### Summary up to this point:++Now that we know about components of the weights graph, their properties, and how they fit together, let’s cover how they’re generated.++## Generating the weights graph++#### Plugins:++SourceCred uses APIs of collaboration platforms to gather information about contributions. Currently there are plugins for GitHub and Discourse ([code](https://github.com/sourcecred/sourcecred/tree/master/src/plugins)), and we’re enthusiastic to have more plugins, so we can account for contributions in different venues. This includes Discord (in progress), and could extend to Twitter or citations on the [arXiv](https://arxiv.org/) or [eprint](https://eprint.iacr.org/).++#### Weights:++Because these plugins incorporate data from all sorts of contributions and output a single weighted, directed graph, there needs to be a way to quantitatively compare actions (e.g. authoring a GitHub issue vs responding or referencing it). To accomplish this, there’s a set of constants that determine weights for nodes and edges. For an example set of constants, see the below figure, captured from [sourcecred.io/cred/timeline/@sourcecred/](https://sourcecred.io/cred/timeline/@sourcecred/) (follow the link and click the “Show weight configuration” button).++![](https://lh6.googleusercontent.com/BDSrcvjcCXXfKQ0NSiHIYuAh8fvTaE0Akl0g2-PLdB88fv5TXZS7btltL0YTe77TLOT7nyImct7sPrKATbAQKUwukM3bdxgVfIJCf-xDZo0nvGnFINMoqXq41glKAI9hA2EnThed)++> Figure 3. A set of weight sliders to set parameters controlling the relative weights of different contributions.++Additionally, parameters, cred, and grain are all scoped to a community. This means that the community that supports a project sets and controls the parameters. For that matter, it’s worth reiterating that (lowercase) cred and grain generated for a project are specific to that project. SourceCred is about providing community control, keeping humans in the loop, both for gaming mitigation and general adaptability. At the bottom line, it’s important to keep the humans in control, because good governance technology only works with the consent of the governed.++#### Manual input:++It’s also worth noting that not all contributions occur on platforms with automated data fetching, and thus the capability exists to individually add them to the weighted graph directly at this stage. It’s clear that there will always be data that has to be incorporated manually, and how to incorporate those contributions, especially those not easily quantified, remains an active area of discussion.++#### Merging graphs:++A plugin will use the API for the collaborative workspace app or platform to generate a graph of nodes and edges. One interesting caveat is that there isn't necessarily one most-intuitively correct way to merge two weighted graphs if they share either edges or nodes (i.e. how would we calculate the combined weight). Currently, the graph nodes are nearly entirely disjoint, the weights are added together, and identities are collapsed into one.++#### Summary up to this point:++So at this point, we’ve got data from all our sources combined in one weighted, directed graph, where contributions and contributors are nodes, interactions and connections between nodes are edges, each have a relative weight based on parameters the community has set, and we’re ready to run PageRank on it.++## SourceCred’s PageRank++The next goal will be to generate an evaluation of the relative importance of nodes based on our weighted graph, specifically the weights of the nodes and edges.++#### PageRank explained elsewhere++Before reading on, it’s important to understand what PageRank does. There are other good explanations out there, and keeping this brief seems useful, so I won’t add another explanation. If you like over preparing, Brilliant.com’s explanation of [Markov Chains](https://brilliant.org/wiki/markov-chains/#transition-matrices), [Stationary Distributions](https://brilliant.org/wiki/stationary-distributions/) and [their quiz](https://brilliant.org/practice/basic-markov-chains) is more than sufficient. If you’re a visual thinker, [these slides](https://www.cl.cam.ac.uk/teaching/1516/InfoRtrv/lecture8-link-analysis-2x2.pdf) provide some nice figures.++<img src="https://upload.wikimedia.org/wikipedia/commons/6/69/PageRank-hi-res.png" width="400">++> Figure 4. An illustration of PageRank, in which the size of each node is proportional to the total size of the nodes linking to it.++#### Stationary distribution intuition++The simplest formulation of PageRank operates over an unweighted graph, treating all links as equivalent, as illustrated in the figure above. It assigns all outbound links equal probability and calculates the stationary distribution of node weights. (Intuitively, the stationary distribution is the equilibrium fraction of water that ends up in each node “tank” if you run a pump along each edge for a while – for “reasonable” graph configurations you’d expect the water distribution to end up the same regardless of where you dump the water initially.)++#### Stationary distribution as an eigenvector++If you’ve spend more time in linear algebra land, you’ll be pleased to hear that common way to compute this (in algebraic graph theory) is to convert the graph to a matrix _P_ where each entry <img src="https://render.githubusercontent.com/render/math?math=P_{uv}"> is the transition probability from node _u_ to node _v_. Each dimension corresponds to a node, and the matrix maps from all nodes to all nodes. Then finding the stationary distribution is the same as finding [the eigenvector](https://www.youtube.com/watch?v=PFDu9oVAE-g) of the transition matrix that corresponds to eigenvalue 1; by the definition of an eigenvector, this vector is the distribution of value to each node such that if value flowed down each edge in proportion to its weight, the resulting value distribution would remain the same.++#### Incorporate edge weights++We can adapt the algorithm to account for edge weights on the input graph fairly intuitively – we could add weights to the links by increasing the number of edges for some types of actions (e.g. 👍=1 edge, ♥️=2 edges). In the probability matrix formulation, it’s simple to increase the transition probability of one edge. Then, instead of assigning equal probability to all outbound edges, we can simply normalize the weights so that they sum to one and this provides us with transition probabilities needed for <img src="https://render.githubusercontent.com/render/math?math=P_{uv}">.++#### Incorporate node weights++There’s also a cleverly simple way to account for the existence of node weights. One requirement for the existence of a stationary distribution that was skipped is the concept of ergodicity, wherein every state must be reachable from any other state, given a long enough probabilistic walk across the graph’s edges†. To achieve ergodicity, SourceCred uses a "teleportation" system by adding a probability, ⍺, of teleporting to a random location in the graph. SourceCred currently implements this with a seed node, which all nodes teleporting to with likelihood ⍺. From the seed node, the probability of landing on a given node in the graph is then given by the node weights. If we use _A_ to describe the weights of each node in a vector form, then our old transition matrix <img src="https://render.githubusercontent.com/render/math?math=P_{uv}"> becomes the new transition matrix <img src="https://render.githubusercontent.com/render/math?math=(1-\alpha)P_{uv} + \alpha A">. The stationary distribution is then the vector <img src="https://render.githubusercontent.com/render/math?math=\vec v"> such that <img src="https://render.githubusercontent.com/render/math?math=\left((1-\alpha)P_{uv} + \alpha A\right)\vec v = \vec v">.++#### The origin of Cred++In this way, Cred originates at the weighted nodes and flows out to actions from there. Interestingly, this is mathematically equivalent to adding edges to each and every node, with edge-weight proportional to ⍺ times the node weight. We can interpret the necessity of teleportation in the following way: because some contributions may receive but flow no cred, there need to be sources of Cred to calculate an equilibrium value distribution, which requires some contributions to mint cred.++#### Cred-minting philosophy

I'd like to see an explanation of cred minting earlier in the doc. I've been explaining it like:

• Cred flows on the graph, but it needs to "start" somewhere
• We have it start on nodes that have non-zero weight. Thus, we can say that these nodes "mint" new cred.
• The total amount of cred received by contributors will equal the total node weight, i.e. the total amount minted.
• As an example, if I boost a pull request so that it's node weight goes from 0 to 10, then there is 10 new cred in existence, and it will flow out from that pull request to everything that touches the pull.
miyazono

comment created time in 3 days

+# An introduction to calculating Cred and Grain++I’m starting from the assumption that you have some familiarity with the SourceCred project. If you don’t I’d suggest you check out Dandelion's [20 minute talk (+Qs) from SustainWeb3](https://www.youtube.com/watch?v=yVTqRLekRl4), and some of the other documents in this repo.++Overall, I’m going to throw around some math, but you should be able to get solid intuition on this if you’re comfortable talking about a [graph](https://en.wikipedia.org/wiki/Graph_theory) as a set of nodes and edges connecting them, where the edges can be directed and have weights. I’ll also talk some about [the PageRank algorithm](https://en.wikipedia.org/wiki/PageRank), for computing “importance” of nodes in a graph based on the “importance” of nodes connected to it, but I’ll try to motivate the intuition behind it as well.++## The high level overview++SourceCred takes information about contributions and generates a weights graph, and this post will first explain what that graph is and where it comes from. From this weights graph, it runs PageRank to generate a graph of Cred. This graph of Cred is then used to distribute grain. The grain can then be kept or traded, and soon will be able to be used to influence future graph weights. This process is diagrammed in the figure below.+ +![](https://docs.google.com/drawings/u/1/d/s50mxZJ1DFD_yr4OFUxUd9g/image?w=551&h=250&rev=57&ac=1&parent=1kRxnuKAogf_cE0y6u2QYcjQimE0-jrBZ-LxcXKOUITw)++> Figure 1. A very high level overview of how SourceCred allocates Cred and Grain, distinguishing between actions taken by contributors and those taken by the SourceCred algorithm.++The rest of this doc outlines how collaboration data is gathered and structured, and how the exact amounts of Cred and Grain are calculated, and ends with some future directions SourceCred as a project is considering.++## Components of contributing in SourceCred++SourceCred first generates a weights graph from the input data. In this graph, a node is a contributor or contribution, and edges represent some sort of connection between the nodes. These connections are intended to include all possible ways that a contribution or contributor on a platform can express dependencies, thanks, or flows of value. Examples would include anything from a mention on a forum post to liking or merging a PR on GitHub.++#### Nodes:++Contributions are represented as nodes in the graph, and are given addresses to identify them uniquely. These addresses include a prefix, which takes the form: organization making the plugin that generates the node + name of the plugin + type of contribution (e.g. topic/post/user/like).++Contributors are also represented as nodes in the graph. These nodes are similarly prefixed, and connected to contributions with directed edges, as illustrated in the figure below. For example, if an author node creates an issue, the author node gets an incoming edge from the issue node. Or if an author of a Discourse post mentions another contributor in the post, the post creates an outbound edge coming from the author.++Lastly, initiatives are another type of node that serve as a high level place to connect relevant nodes and discuss governance. It's similar to a tracking issue, but generalized across the weights graph, providing a way to organize planned work for a well-scoped task.++#### Weights:++Both nodes and edges have weights, and an edge object in the SourceCred implementation can have forward and backwards weight. We’ll get into what these weights mean shortly, but for now, it’s worth noting that these weights can easily be set by address prefix. Setting by prefix is implemented as a multiplicative update; for example, setting issue comments to be worth twice an issue emoji reaction creates a 2:1 ratio that is preserved by decreasing GitHub’s edge weights by 2 relative to those of Discourse. Additionally, nodes can have self edges, which is relevant when a piece of content is edited, or an contribution references itself.+  +![](https://docs.google.com/drawings/u/1/d/sIDpfiaPlACrxNScrfsISkQ/image?w=186&h=230&rev=47&ac=1&parent=1kRxnuKAogf_cE0y6u2QYcjQimE0-jrBZ-LxcXKOUITw)++> Figure 2: An example of a weights graph, where the nodes would be contributors or contributions, and the directed, weighted edges are connections. It’s a [directed graph](https://en.wikipedia.org/wiki/Graph_theory#Directed_graph) with weights on nodes and edges.++#### Timestamps:++Additionally, both edges and some nodes have timestamps. Edges are associated with actions or events, and as such, always have a timestamp. For nodes that correspond to a contribution, those nodes have a timestamp corresponding to the moment of the contribution. However, nodes corresponding to contributors do not have time stamps, since the contributors exist in the algorithm across time.++#### Summary up to this point:++Now that we know about components of the weights graph, their properties, and how they fit together, let’s cover how they’re generated.++## Generating the weights graph++#### Plugins:++SourceCred uses APIs of collaboration platforms to gather information about contributions. Currently there are plugins for GitHub and Discourse ([code](https://github.com/sourcecred/sourcecred/tree/master/src/plugins)), and we’re enthusiastic to have more plugins, so we can account for contributions in different venues. This includes Discord (in progress), and could extend to Twitter or citations on the [arXiv](https://arxiv.org/) or [eprint](https://eprint.iacr.org/).++#### Weights:++Because these plugins incorporate data from all sorts of contributions and output a single weighted, directed graph, there needs to be a way to quantitatively compare actions (e.g. authoring a GitHub issue vs responding or referencing it). To accomplish this, there’s a set of constants that determine weights for nodes and edges. For an example set of constants, see the below figure, captured from [sourcecred.io/cred/timeline/@sourcecred/](https://sourcecred.io/cred/timeline/@sourcecred/) (follow the link and click the “Show weight configuration” button).++![](https://lh6.googleusercontent.com/BDSrcvjcCXXfKQ0NSiHIYuAh8fvTaE0Akl0g2-PLdB88fv5TXZS7btltL0YTe77TLOT7nyImct7sPrKATbAQKUwukM3bdxgVfIJCf-xDZo0nvGnFINMoqXq41glKAI9hA2EnThed)++> Figure 3. A set of weight sliders to set parameters controlling the relative weights of different contributions.++Additionally, parameters, cred, and grain are all scoped to a community. This means that the community that supports a project sets and controls the parameters. For that matter, it’s worth reiterating that (lowercase) cred and grain generated for a project are specific to that project. SourceCred is about providing community control, keeping humans in the loop, both for gaming mitigation and general adaptability. At the bottom line, it’s important to keep the humans in control, because good governance technology only works with the consent of the governed.++#### Manual input:++It’s also worth noting that not all contributions occur on platforms with automated data fetching, and thus the capability exists to individually add them to the weighted graph directly at this stage. It’s clear that there will always be data that has to be incorporated manually, and how to incorporate those contributions, especially those not easily quantified, remains an active area of discussion.++#### Merging graphs:++A plugin will use the API for the collaborative workspace app or platform to generate a graph of nodes and edges. One interesting caveat is that there isn't necessarily one most-intuitively correct way to merge two weighted graphs if they share either edges or nodes (i.e. how would we calculate the combined weight). Currently, the graph nodes are nearly entirely disjoint, the weights are added together, and identities are collapsed into one.++#### Summary up to this point:++So at this point, we’ve got data from all our sources combined in one weighted, directed graph, where contributions and contributors are nodes, interactions and connections between nodes are edges, each have a relative weight based on parameters the community has set, and we’re ready to run PageRank on it.++## SourceCred’s PageRank

CredRank ;)

miyazono

comment created time in 3 days

+# An introduction to calculating Cred and Grain++I’m starting from the assumption that you have some familiarity with the SourceCred project. If you don’t I’d suggest you check out Dandelion's [20 minute talk (+Qs) from SustainWeb3](https://www.youtube.com/watch?v=yVTqRLekRl4), and some of the other documents in this repo.++Overall, I’m going to throw around some math, but you should be able to get solid intuition on this if you’re comfortable talking about a [graph](https://en.wikipedia.org/wiki/Graph_theory) as a set of nodes and edges connecting them, where the edges can be directed and have weights. I’ll also talk some about [the PageRank algorithm](https://en.wikipedia.org/wiki/PageRank), for computing “importance” of nodes in a graph based on the “importance” of nodes connected to it, but I’ll try to motivate the intuition behind it as well.++## The high level overview++SourceCred takes information about contributions and generates a weights graph, and this post will first explain what that graph is and where it comes from. From this weights graph, it runs PageRank to generate a graph of Cred. This graph of Cred is then used to distribute grain. The grain can then be kept or traded, and soon will be able to be used to influence future graph weights. This process is diagrammed in the figure below.+ +![](https://docs.google.com/drawings/u/1/d/s50mxZJ1DFD_yr4OFUxUd9g/image?w=551&h=250&rev=57&ac=1&parent=1kRxnuKAogf_cE0y6u2QYcjQimE0-jrBZ-LxcXKOUITw)++> Figure 1. A very high level overview of how SourceCred allocates Cred and Grain, distinguishing between actions taken by contributors and those taken by the SourceCred algorithm.++The rest of this doc outlines how collaboration data is gathered and structured, and how the exact amounts of Cred and Grain are calculated, and ends with some future directions SourceCred as a project is considering.++## Components of contributing in SourceCred++SourceCred first generates a weights graph from the input data. In this graph, a node is a contributor or contribution, and edges represent some sort of connection between the nodes. These connections are intended to include all possible ways that a contribution or contributor on a platform can express dependencies, thanks, or flows of value. Examples would include anything from a mention on a forum post to liking or merging a PR on GitHub.++#### Nodes:++Contributions are represented as nodes in the graph, and are given addresses to identify them uniquely. These addresses include a prefix, which takes the form: organization making the plugin that generates the node + name of the plugin + type of contribution (e.g. topic/post/user/like).++Contributors are also represented as nodes in the graph. These nodes are similarly prefixed, and connected to contributions with directed edges, as illustrated in the figure below. For example, if an author node creates an issue, the author node gets an incoming edge from the issue node. Or if an author of a Discourse post mentions another contributor in the post, the post creates an outbound edge coming from the author.++Lastly, initiatives are another type of node that serve as a high level place to connect relevant nodes and discuss governance. It's similar to a tracking issue, but generalized across the weights graph, providing a way to organize planned work for a well-scoped task.++#### Weights:++Both nodes and edges have weights, and an edge object in the SourceCred implementation can have forward and backwards weight. We’ll get into what these weights mean shortly, but for now, it’s worth noting that these weights can easily be set by address prefix. Setting by prefix is implemented as a multiplicative update; for example, setting issue comments to be worth twice an issue emoji reaction creates a 2:1 ratio that is preserved by decreasing GitHub’s edge weights by 2 relative to those of Discourse. Additionally, nodes can have self edges, which is relevant when a piece of content is edited, or an contribution references itself.+  +![](https://docs.google.com/drawings/u/1/d/sIDpfiaPlACrxNScrfsISkQ/image?w=186&h=230&rev=47&ac=1&parent=1kRxnuKAogf_cE0y6u2QYcjQimE0-jrBZ-LxcXKOUITw)++> Figure 2: An example of a weights graph, where the nodes would be contributors or contributions, and the directed, weighted edges are connections. It’s a [directed graph](https://en.wikipedia.org/wiki/Graph_theory#Directed_graph) with weights on nodes and edges.++#### Timestamps:++Additionally, both edges and some nodes have timestamps. Edges are associated with actions or events, and as such, always have a timestamp. For nodes that correspond to a contribution, those nodes have a timestamp corresponding to the moment of the contribution. However, nodes corresponding to contributors do not have time stamps, since the contributors exist in the algorithm across time.++#### Summary up to this point:++Now that we know about components of the weights graph, their properties, and how they fit together, let’s cover how they’re generated.++## Generating the weights graph++#### Plugins:++SourceCred uses APIs of collaboration platforms to gather information about contributions. Currently there are plugins for GitHub and Discourse ([code](https://github.com/sourcecred/sourcecred/tree/master/src/plugins)), and we’re enthusiastic to have more plugins, so we can account for contributions in different venues. This includes Discord (in progress), and could extend to Twitter or citations on the [arXiv](https://arxiv.org/) or [eprint](https://eprint.iacr.org/).++#### Weights:++Because these plugins incorporate data from all sorts of contributions and output a single weighted, directed graph, there needs to be a way to quantitatively compare actions (e.g. authoring a GitHub issue vs responding or referencing it). To accomplish this, there’s a set of constants that determine weights for nodes and edges. For an example set of constants, see the below figure, captured from [sourcecred.io/cred/timeline/@sourcecred/](https://sourcecred.io/cred/timeline/@sourcecred/) (follow the link and click the “Show weight configuration” button).++![](https://lh6.googleusercontent.com/BDSrcvjcCXXfKQ0NSiHIYuAh8fvTaE0Akl0g2-PLdB88fv5TXZS7btltL0YTe77TLOT7nyImct7sPrKATbAQKUwukM3bdxgVfIJCf-xDZo0nvGnFINMoqXq41glKAI9hA2EnThed)++> Figure 3. A set of weight sliders to set parameters controlling the relative weights of different contributions.++Additionally, parameters, cred, and grain are all scoped to a community. This means that the community that supports a project sets and controls the parameters. For that matter, it’s worth reiterating that (lowercase) cred and grain generated for a project are specific to that project. SourceCred is about providing community control, keeping humans in the loop, both for gaming mitigation and general adaptability. At the bottom line, it’s important to keep the humans in control, because good governance technology only works with the consent of the governed.++#### Manual input:++It’s also worth noting that not all contributions occur on platforms with automated data fetching, and thus the capability exists to individually add them to the weighted graph directly at this stage. It’s clear that there will always be data that has to be incorporated manually, and how to incorporate those contributions, especially those not easily quantified, remains an active area of discussion.++#### Merging graphs:++A plugin will use the API for the collaborative workspace app or platform to generate a graph of nodes and edges. One interesting caveat is that there isn't necessarily one most-intuitively correct way to merge two weighted graphs if they share either edges or nodes (i.e. how would we calculate the combined weight). Currently, the graph nodes are nearly entirely disjoint, the weights are added together, and identities are collapsed into one.++#### Summary up to this point:++So at this point, we’ve got data from all our sources combined in one weighted, directed graph, where contributions and contributors are nodes, interactions and connections between nodes are edges, each have a relative weight based on parameters the community has set, and we’re ready to run PageRank on it.++## SourceCred’s PageRank++The next goal will be to generate an evaluation of the relative importance of nodes based on our weighted graph, specifically the weights of the nodes and edges.++#### PageRank explained elsewhere++Before reading on, it’s important to understand what PageRank does. There are other good explanations out there, and keeping this brief seems useful, so I won’t add another explanation. If you like over preparing, Brilliant.com’s explanation of [Markov Chains](https://brilliant.org/wiki/markov-chains/#transition-matrices), [Stationary Distributions](https://brilliant.org/wiki/stationary-distributions/) and [their quiz](https://brilliant.org/practice/basic-markov-chains) is more than sufficient. If you’re a visual thinker, [these slides](https://www.cl.cam.ac.uk/teaching/1516/InfoRtrv/lecture8-link-analysis-2x2.pdf) provide some nice figures.++<img src="https://upload.wikimedia.org/wikipedia/commons/6/69/PageRank-hi-res.png" width="400">++> Figure 4. An illustration of PageRank, in which the size of each node is proportional to the total size of the nodes linking to it.++#### Stationary distribution intuition++The simplest formulation of PageRank operates over an unweighted graph, treating all links as equivalent, as illustrated in the figure above. It assigns all outbound links equal probability and calculates the stationary distribution of node weights. (Intuitively, the stationary distribution is the equilibrium fraction of water that ends up in each node “tank” if you run a pump along each edge for a while – for “reasonable” graph configurations you’d expect the water distribution to end up the same regardless of where you dump the water initially.)++#### Stationary distribution as an eigenvector++If you’ve spend more time in linear algebra land, you’ll be pleased to hear that common way to compute this (in algebraic graph theory) is to convert the graph to a matrix _P_ where each entry <img src="https://render.githubusercontent.com/render/math?math=P_{uv}"> is the transition probability from node _u_ to node _v_. Each dimension corresponds to a node, and the matrix maps from all nodes to all nodes. Then finding the stationary distribution is the same as finding [the eigenvector](https://www.youtube.com/watch?v=PFDu9oVAE-g) of the transition matrix that corresponds to eigenvalue 1; by the definition of an eigenvector, this vector is the distribution of value to each node such that if value flowed down each edge in proportion to its weight, the resulting value distribution would remain the same.++#### Incorporate edge weights++We can adapt the algorithm to account for edge weights on the input graph fairly intuitively – we could add weights to the links by increasing the number of edges for some types of actions (e.g. 👍=1 edge, ♥️=2 edges). In the probability matrix formulation, it’s simple to increase the transition probability of one edge. Then, instead of assigning equal probability to all outbound edges, we can simply normalize the weights so that they sum to one and this provides us with transition probabilities needed for <img src="https://render.githubusercontent.com/render/math?math=P_{uv}">.++#### Incorporate node weights++There’s also a cleverly simple way to account for the existence of node weights. One requirement for the existence of a stationary distribution that was skipped is the concept of ergodicity, wherein every state must be reachable from any other state, given a long enough probabilistic walk across the graph’s edges†. To achieve ergodicity, SourceCred uses a "teleportation" system by adding a probability, ⍺, of teleporting to a random location in the graph. SourceCred currently implements this with a seed node, which all nodes teleporting to with likelihood ⍺. From the seed node, the probability of landing on a given node in the graph is then given by the node weights. If we use _A_ to describe the weights of each node in a vector form, then our old transition matrix <img src="https://render.githubusercontent.com/render/math?math=P_{uv}"> becomes the new transition matrix <img src="https://render.githubusercontent.com/render/math?math=(1-\alpha)P_{uv} + \alpha A">. The stationary distribution is then the vector <img src="https://render.githubusercontent.com/render/math?math=\vec v"> such that <img src="https://render.githubusercontent.com/render/math?math=\left((1-\alpha)P_{uv} + \alpha A\right)\vec v = \vec v">.++#### The origin of Cred++In this way, Cred originates at the weighted nodes and flows out to actions from there. Interestingly, this is mathematically equivalent to adding edges to each and every node, with edge-weight proportional to ⍺ times the node weight. We can interpret the necessity of teleportation in the following way: because some contributions may receive but flow no cred, there need to be sources of Cred to calculate an equilibrium value distribution, which requires some contributions to mint cred.

"because some contributions may receive but flow no cred" -- I don't know what this means, and it doesn't line up with my intuitions. Every node that receives cred also flows it (to its adjacencies, and/or out to the seed vector)

miyazono

comment created time in 3 days

+# An introduction to calculating Cred and Grain++I’m starting from the assumption that you have some familiarity with the SourceCred project. If you don’t I’d suggest you check out Dandelion's [20 minute talk (+Qs) from SustainWeb3](https://www.youtube.com/watch?v=yVTqRLekRl4), and some of the other documents in this repo.++Overall, I’m going to throw around some math, but you should be able to get solid intuition on this if you’re comfortable talking about a [graph](https://en.wikipedia.org/wiki/Graph_theory) as a set of nodes and edges connecting them, where the edges can be directed and have weights. I’ll also talk some about [the PageRank algorithm](https://en.wikipedia.org/wiki/PageRank), for computing “importance” of nodes in a graph based on the “importance” of nodes connected to it, but I’ll try to motivate the intuition behind it as well.++## The high level overview++SourceCred takes information about contributions and generates a weights graph, and this post will first explain what that graph is and where it comes from. From this weights graph, it runs PageRank to generate a graph of Cred. This graph of Cred is then used to distribute grain. The grain can then be kept or traded, and soon will be able to be used to influence future graph weights. This process is diagrammed in the figure below.+ +![](https://docs.google.com/drawings/u/1/d/s50mxZJ1DFD_yr4OFUxUd9g/image?w=551&h=250&rev=57&ac=1&parent=1kRxnuKAogf_cE0y6u2QYcjQimE0-jrBZ-LxcXKOUITw)++> Figure 1. A very high level overview of how SourceCred allocates Cred and Grain, distinguishing between actions taken by contributors and those taken by the SourceCred algorithm.++The rest of this doc outlines how collaboration data is gathered and structured, and how the exact amounts of Cred and Grain are calculated, and ends with some future directions SourceCred as a project is considering.++## Components of contributing in SourceCred++SourceCred first generates a weights graph from the input data. In this graph, a node is a contributor or contribution, and edges represent some sort of connection between the nodes. These connections are intended to include all possible ways that a contribution or contributor on a platform can express dependencies, thanks, or flows of value. Examples would include anything from a mention on a forum post to liking or merging a PR on GitHub.++#### Nodes:++Contributions are represented as nodes in the graph, and are given addresses to identify them uniquely. These addresses include a prefix, which takes the form: organization making the plugin that generates the node + name of the plugin + type of contribution (e.g. topic/post/user/like).++Contributors are also represented as nodes in the graph. These nodes are similarly prefixed, and connected to contributions with directed edges, as illustrated in the figure below. For example, if an author node creates an issue, the author node gets an incoming edge from the issue node. Or if an author of a Discourse post mentions another contributor in the post, the post creates an outbound edge coming from the author.++Lastly, initiatives are another type of node that serve as a high level place to connect relevant nodes and discuss governance. It's similar to a tracking issue, but generalized across the weights graph, providing a way to organize planned work for a well-scoped task.++#### Weights:++Both nodes and edges have weights, and an edge object in the SourceCred implementation can have forward and backwards weight. We’ll get into what these weights mean shortly, but for now, it’s worth noting that these weights can easily be set by address prefix. Setting by prefix is implemented as a multiplicative update; for example, setting issue comments to be worth twice an issue emoji reaction creates a 2:1 ratio that is preserved by decreasing GitHub’s edge weights by 2 relative to those of Discourse. Additionally, nodes can have self edges, which is relevant when a piece of content is edited, or an contribution references itself.+  +![](https://docs.google.com/drawings/u/1/d/sIDpfiaPlACrxNScrfsISkQ/image?w=186&h=230&rev=47&ac=1&parent=1kRxnuKAogf_cE0y6u2QYcjQimE0-jrBZ-LxcXKOUITw)++> Figure 2: An example of a weights graph, where the nodes would be contributors or contributions, and the directed, weighted edges are connections. It’s a [directed graph](https://en.wikipedia.org/wiki/Graph_theory#Directed_graph) with weights on nodes and edges.++#### Timestamps:++Additionally, both edges and some nodes have timestamps. Edges are associated with actions or events, and as such, always have a timestamp. For nodes that correspond to a contribution, those nodes have a timestamp corresponding to the moment of the contribution. However, nodes corresponding to contributors do not have time stamps, since the contributors exist in the algorithm across time.++#### Summary up to this point:++Now that we know about components of the weights graph, their properties, and how they fit together, let’s cover how they’re generated.++## Generating the weights graph++#### Plugins:++SourceCred uses APIs of collaboration platforms to gather information about contributions. Currently there are plugins for GitHub and Discourse ([code](https://github.com/sourcecred/sourcecred/tree/master/src/plugins)), and we’re enthusiastic to have more plugins, so we can account for contributions in different venues. This includes Discord (in progress), and could extend to Twitter or citations on the [arXiv](https://arxiv.org/) or [eprint](https://eprint.iacr.org/).++#### Weights:++Because these plugins incorporate data from all sorts of contributions and output a single weighted, directed graph, there needs to be a way to quantitatively compare actions (e.g. authoring a GitHub issue vs responding or referencing it). To accomplish this, there’s a set of constants that determine weights for nodes and edges. For an example set of constants, see the below figure, captured from [sourcecred.io/cred/timeline/@sourcecred/](https://sourcecred.io/cred/timeline/@sourcecred/) (follow the link and click the “Show weight configuration” button).++![](https://lh6.googleusercontent.com/BDSrcvjcCXXfKQ0NSiHIYuAh8fvTaE0Akl0g2-PLdB88fv5TXZS7btltL0YTe77TLOT7nyImct7sPrKATbAQKUwukM3bdxgVfIJCf-xDZo0nvGnFINMoqXq41glKAI9hA2EnThed)++> Figure 3. A set of weight sliders to set parameters controlling the relative weights of different contributions.++Additionally, parameters, cred, and grain are all scoped to a community. This means that the community that supports a project sets and controls the parameters. For that matter, it’s worth reiterating that (lowercase) cred and grain generated for a project are specific to that project. SourceCred is about providing community control, keeping humans in the loop, both for gaming mitigation and general adaptability. At the bottom line, it’s important to keep the humans in control, because good governance technology only works with the consent of the governed.++#### Manual input:++It’s also worth noting that not all contributions occur on platforms with automated data fetching, and thus the capability exists to individually add them to the weighted graph directly at this stage. It’s clear that there will always be data that has to be incorporated manually, and how to incorporate those contributions, especially those not easily quantified, remains an active area of discussion.++#### Merging graphs:++A plugin will use the API for the collaborative workspace app or platform to generate a graph of nodes and edges. One interesting caveat is that there isn't necessarily one most-intuitively correct way to merge two weighted graphs if they share either edges or nodes (i.e. how would we calculate the combined weight). Currently, the graph nodes are nearly entirely disjoint, the weights are added together, and identities are collapsed into one.++#### Summary up to this point:++So at this point, we’ve got data from all our sources combined in one weighted, directed graph, where contributions and contributors are nodes, interactions and connections between nodes are edges, each have a relative weight based on parameters the community has set, and we’re ready to run PageRank on it.++## SourceCred’s PageRank++The next goal will be to generate an evaluation of the relative importance of nodes based on our weighted graph, specifically the weights of the nodes and edges.++#### PageRank explained elsewhere++Before reading on, it’s important to understand what PageRank does. There are other good explanations out there, and keeping this brief seems useful, so I won’t add another explanation. If you like over preparing, Brilliant.com’s explanation of [Markov Chains](https://brilliant.org/wiki/markov-chains/#transition-matrices), [Stationary Distributions](https://brilliant.org/wiki/stationary-distributions/) and [their quiz](https://brilliant.org/practice/basic-markov-chains) is more than sufficient. If you’re a visual thinker, [these slides](https://www.cl.cam.ac.uk/teaching/1516/InfoRtrv/lecture8-link-analysis-2x2.pdf) provide some nice figures.++<img src="https://upload.wikimedia.org/wikipedia/commons/6/69/PageRank-hi-res.png" width="400">++> Figure 4. An illustration of PageRank, in which the size of each node is proportional to the total size of the nodes linking to it.++#### Stationary distribution intuition++The simplest formulation of PageRank operates over an unweighted graph, treating all links as equivalent, as illustrated in the figure above. It assigns all outbound links equal probability and calculates the stationary distribution of node weights. (Intuitively, the stationary distribution is the equilibrium fraction of water that ends up in each node “tank” if you run a pump along each edge for a while – for “reasonable” graph configurations you’d expect the water distribution to end up the same regardless of where you dump the water initially.)

Personally, I like the metaphor of a river system where water has ponds where it accumulates, and channels that link the ponds. At every pond, some of the water seeps into the earth, but it bubbles back up at "spring" (some special ponds have springs).

This way you can get an intuition for the seed behavior.

miyazono

comment created time in 3 days

+# An introduction to calculating Cred and Grain++I’m starting from the assumption that you have some familiarity with the SourceCred project. If you don’t I’d suggest you check out Dandelion's [20 minute talk (+Qs) from SustainWeb3](https://www.youtube.com/watch?v=yVTqRLekRl4), and some of the other documents in this repo.++Overall, I’m going to throw around some math, but you should be able to get solid intuition on this if you’re comfortable talking about a [graph](https://en.wikipedia.org/wiki/Graph_theory) as a set of nodes and edges connecting them, where the edges can be directed and have weights. I’ll also talk some about [the PageRank algorithm](https://en.wikipedia.org/wiki/PageRank), for computing “importance” of nodes in a graph based on the “importance” of nodes connected to it, but I’ll try to motivate the intuition behind it as well.++## The high level overview++SourceCred takes information about contributions and generates a weights graph, and this post will first explain what that graph is and where it comes from. From this weights graph, it runs PageRank to generate a graph of Cred. This graph of Cred is then used to distribute grain. The grain can then be kept or traded, and soon will be able to be used to influence future graph weights. This process is diagrammed in the figure below.+ +![](https://docs.google.com/drawings/u/1/d/s50mxZJ1DFD_yr4OFUxUd9g/image?w=551&h=250&rev=57&ac=1&parent=1kRxnuKAogf_cE0y6u2QYcjQimE0-jrBZ-LxcXKOUITw)++> Figure 1. A very high level overview of how SourceCred allocates Cred and Grain, distinguishing between actions taken by contributors and those taken by the SourceCred algorithm.++The rest of this doc outlines how collaboration data is gathered and structured, and how the exact amounts of Cred and Grain are calculated, and ends with some future directions SourceCred as a project is considering.++## Components of contributing in SourceCred++SourceCred first generates a weights graph from the input data. In this graph, a node is a contributor or contribution, and edges represent some sort of connection between the nodes. These connections are intended to include all possible ways that a contribution or contributor on a platform can express dependencies, thanks, or flows of value. Examples would include anything from a mention on a forum post to liking or merging a PR on GitHub.++#### Nodes:++Contributions are represented as nodes in the graph, and are given addresses to identify them uniquely. These addresses include a prefix, which takes the form: organization making the plugin that generates the node + name of the plugin + type of contribution (e.g. topic/post/user/like).++Contributors are also represented as nodes in the graph. These nodes are similarly prefixed, and connected to contributions with directed edges, as illustrated in the figure below. For example, if an author node creates an issue, the author node gets an incoming edge from the issue node. Or if an author of a Discourse post mentions another contributor in the post, the post creates an outbound edge coming from the author.++Lastly, initiatives are another type of node that serve as a high level place to connect relevant nodes and discuss governance. It's similar to a tracking issue, but generalized across the weights graph, providing a way to organize planned work for a well-scoped task.++#### Weights:++Both nodes and edges have weights, and an edge object in the SourceCred implementation can have forward and backwards weight. We’ll get into what these weights mean shortly, but for now, it’s worth noting that these weights can easily be set by address prefix. Setting by prefix is implemented as a multiplicative update; for example, setting issue comments to be worth twice an issue emoji reaction creates a 2:1 ratio that is preserved by decreasing GitHub’s edge weights by 2 relative to those of Discourse. Additionally, nodes can have self edges, which is relevant when a piece of content is edited, or an contribution references itself.+  +![](https://docs.google.com/drawings/u/1/d/sIDpfiaPlACrxNScrfsISkQ/image?w=186&h=230&rev=47&ac=1&parent=1kRxnuKAogf_cE0y6u2QYcjQimE0-jrBZ-LxcXKOUITw)++> Figure 2: An example of a weights graph, where the nodes would be contributors or contributions, and the directed, weighted edges are connections. It’s a [directed graph](https://en.wikipedia.org/wiki/Graph_theory#Directed_graph) with weights on nodes and edges.++#### Timestamps:++Additionally, both edges and some nodes have timestamps. Edges are associated with actions or events, and as such, always have a timestamp. For nodes that correspond to a contribution, those nodes have a timestamp corresponding to the moment of the contribution. However, nodes corresponding to contributors do not have time stamps, since the contributors exist in the algorithm across time.++#### Summary up to this point:++Now that we know about components of the weights graph, their properties, and how they fit together, let’s cover how they’re generated.++## Generating the weights graph++#### Plugins:++SourceCred uses APIs of collaboration platforms to gather information about contributions. Currently there are plugins for GitHub and Discourse ([code](https://github.com/sourcecred/sourcecred/tree/master/src/plugins)), and we’re enthusiastic to have more plugins, so we can account for contributions in different venues. This includes Discord (in progress), and could extend to Twitter or citations on the [arXiv](https://arxiv.org/) or [eprint](https://eprint.iacr.org/).++#### Weights:++Because these plugins incorporate data from all sorts of contributions and output a single weighted, directed graph, there needs to be a way to quantitatively compare actions (e.g. authoring a GitHub issue vs responding or referencing it). To accomplish this, there’s a set of constants that determine weights for nodes and edges. For an example set of constants, see the below figure, captured from [sourcecred.io/cred/timeline/@sourcecred/](https://sourcecred.io/cred/timeline/@sourcecred/) (follow the link and click the “Show weight configuration” button).++![](https://lh6.googleusercontent.com/BDSrcvjcCXXfKQ0NSiHIYuAh8fvTaE0Akl0g2-PLdB88fv5TXZS7btltL0YTe77TLOT7nyImct7sPrKATbAQKUwukM3bdxgVfIJCf-xDZo0nvGnFINMoqXq41glKAI9hA2EnThed)++> Figure 3. A set of weight sliders to set parameters controlling the relative weights of different contributions.++Additionally, parameters, cred, and grain are all scoped to a community. This means that the community that supports a project sets and controls the parameters. For that matter, it’s worth reiterating that (lowercase) cred and grain generated for a project are specific to that project. SourceCred is about providing community control, keeping humans in the loop, both for gaming mitigation and general adaptability. At the bottom line, it’s important to keep the humans in control, because good governance technology only works with the consent of the governed.++#### Manual input:++It’s also worth noting that not all contributions occur on platforms with automated data fetching, and thus the capability exists to individually add them to the weighted graph directly at this stage. It’s clear that there will always be data that has to be incorporated manually, and how to incorporate those contributions, especially those not easily quantified, remains an active area of discussion.++#### Merging graphs:+

"weights are added together" could be misleading since it implies that we mathematically add them. Really we take the union of the weights, and throw an error on non-disjoint weights.

miyazono

comment created time in 3 days

+# An introduction to calculating Cred and Grain++I’m starting from the assumption that you have some familiarity with the SourceCred project. If you don’t I’d suggest you check out Dandelion's [20 minute talk (+Qs) from SustainWeb3](https://www.youtube.com/watch?v=yVTqRLekRl4), and some of the other documents in this repo.++Overall, I’m going to throw around some math, but you should be able to get solid intuition on this if you’re comfortable talking about a [graph](https://en.wikipedia.org/wiki/Graph_theory) as a set of nodes and edges connecting them, where the edges can be directed and have weights. I’ll also talk some about [the PageRank algorithm](https://en.wikipedia.org/wiki/PageRank), for computing “importance” of nodes in a graph based on the “importance” of nodes connected to it, but I’ll try to motivate the intuition behind it as well.++## The high level overview++SourceCred takes information about contributions and generates a weights graph, and this post will first explain what that graph is and where it comes from. From this weights graph, it runs PageRank to generate a graph of Cred. This graph of Cred is then used to distribute grain. The grain can then be kept or traded, and soon will be able to be used to influence future graph weights. This process is diagrammed in the figure below.+ +![](https://docs.google.com/drawings/u/1/d/s50mxZJ1DFD_yr4OFUxUd9g/image?w=551&h=250&rev=57&ac=1&parent=1kRxnuKAogf_cE0y6u2QYcjQimE0-jrBZ-LxcXKOUITw)++> Figure 1. A very high level overview of how SourceCred allocates Cred and Grain, distinguishing between actions taken by contributors and those taken by the SourceCred algorithm.++The rest of this doc outlines how collaboration data is gathered and structured, and how the exact amounts of Cred and Grain are calculated, and ends with some future directions SourceCred as a project is considering.++## Components of contributing in SourceCred++SourceCred first generates a weights graph from the input data. In this graph, a node is a contributor or contribution, and edges represent some sort of connection between the nodes. These connections are intended to include all possible ways that a contribution or contributor on a platform can express dependencies, thanks, or flows of value. Examples would include anything from a mention on a forum post to liking or merging a PR on GitHub.++#### Nodes:++Contributions are represented as nodes in the graph, and are given addresses to identify them uniquely. These addresses include a prefix, which takes the form: organization making the plugin that generates the node + name of the plugin + type of contribution (e.g. topic/post/user/like).++Contributors are also represented as nodes in the graph. These nodes are similarly prefixed, and connected to contributions with directed edges, as illustrated in the figure below. For example, if an author node creates an issue, the author node gets an incoming edge from the issue node. Or if an author of a Discourse post mentions another contributor in the post, the post creates an outbound edge coming from the author.++Lastly, initiatives are another type of node that serve as a high level place to connect relevant nodes and discuss governance. It's similar to a tracking issue, but generalized across the weights graph, providing a way to organize planned work for a well-scoped task.++#### Weights:++Both nodes and edges have weights, and an edge object in the SourceCred implementation can have forward and backwards weight. We’ll get into what these weights mean shortly, but for now, it’s worth noting that these weights can easily be set by address prefix. Setting by prefix is implemented as a multiplicative update; for example, setting issue comments to be worth twice an issue emoji reaction creates a 2:1 ratio that is preserved by decreasing GitHub’s edge weights by 2 relative to those of Discourse. Additionally, nodes can have self edges, which is relevant when a piece of content is edited, or an contribution references itself.+  +![](https://docs.google.com/drawings/u/1/d/sIDpfiaPlACrxNScrfsISkQ/image?w=186&h=230&rev=47&ac=1&parent=1kRxnuKAogf_cE0y6u2QYcjQimE0-jrBZ-LxcXKOUITw)++> Figure 2: An example of a weights graph, where the nodes would be contributors or contributions, and the directed, weighted edges are connections. It’s a [directed graph](https://en.wikipedia.org/wiki/Graph_theory#Directed_graph) with weights on nodes and edges.++#### Timestamps:++Additionally, both edges and some nodes have timestamps. Edges are associated with actions or events, and as such, always have a timestamp. For nodes that correspond to a contribution, those nodes have a timestamp corresponding to the moment of the contribution. However, nodes corresponding to contributors do not have time stamps, since the contributors exist in the algorithm across time.

We're likely to change it so that nodes never have timestamps, but all edges do (as of CredRank, Fibration Edition (tm))

miyazono

comment created time in 3 days

+# An introduction to calculating Cred and Grain++I’m starting from the assumption that you have some familiarity with the SourceCred project. If you don’t I’d suggest you check out Dandelion's [20 minute talk (+Qs) from SustainWeb3](https://www.youtube.com/watch?v=yVTqRLekRl4), and some of the other documents in this repo.++Overall, I’m going to throw around some math, but you should be able to get solid intuition on this if you’re comfortable talking about a [graph](https://en.wikipedia.org/wiki/Graph_theory) as a set of nodes and edges connecting them, where the edges can be directed and have weights. I’ll also talk some about [the PageRank algorithm](https://en.wikipedia.org/wiki/PageRank), for computing “importance” of nodes in a graph based on the “importance” of nodes connected to it, but I’ll try to motivate the intuition behind it as well.++## The high level overview++SourceCred takes information about contributions and generates a weights graph, and this post will first explain what that graph is and where it comes from. From this weights graph, it runs PageRank to generate a graph of Cred. This graph of Cred is then used to distribute grain. The grain can then be kept or traded, and soon will be able to be used to influence future graph weights. This process is diagrammed in the figure below.+ +![](https://docs.google.com/drawings/u/1/d/s50mxZJ1DFD_yr4OFUxUd9g/image?w=551&h=250&rev=57&ac=1&parent=1kRxnuKAogf_cE0y6u2QYcjQimE0-jrBZ-LxcXKOUITw)++> Figure 1. A very high level overview of how SourceCred allocates Cred and Grain, distinguishing between actions taken by contributors and those taken by the SourceCred algorithm.++The rest of this doc outlines how collaboration data is gathered and structured, and how the exact amounts of Cred and Grain are calculated, and ends with some future directions SourceCred as a project is considering.++## Components of contributing in SourceCred++SourceCred first generates a weights graph from the input data. In this graph, a node is a contributor or contribution, and edges represent some sort of connection between the nodes. These connections are intended to include all possible ways that a contribution or contributor on a platform can express dependencies, thanks, or flows of value. Examples would include anything from a mention on a forum post to liking or merging a PR on GitHub.++#### Nodes:++Contributions are represented as nodes in the graph, and are given addresses to identify them uniquely. These addresses include a prefix, which takes the form: organization making the plugin that generates the node + name of the plugin + type of contribution (e.g. topic/post/user/like).++Contributors are also represented as nodes in the graph. These nodes are similarly prefixed, and connected to contributions with directed edges, as illustrated in the figure below. For example, if an author node creates an issue, the author node gets an incoming edge from the issue node. Or if an author of a Discourse post mentions another contributor in the post, the post creates an outbound edge coming from the author.++Lastly, initiatives are another type of node that serve as a high level place to connect relevant nodes and discuss governance. It's similar to a tracking issue, but generalized across the weights graph, providing a way to organize planned work for a well-scoped task.++#### Weights:++Both nodes and edges have weights, and an edge object in the SourceCred implementation can have forward and backwards weight. We’ll get into what these weights mean shortly, but for now, it’s worth noting that these weights can easily be set by address prefix. Setting by prefix is implemented as a multiplicative update; for example, setting issue comments to be worth twice an issue emoji reaction creates a 2:1 ratio that is preserved by decreasing GitHub’s edge weights by 2 relative to those of Discourse. Additionally, nodes can have self edges, which is relevant when a piece of content is edited, or an contribution references itself.+  +![](https://docs.google.com/drawings/u/1/d/sIDpfiaPlACrxNScrfsISkQ/image?w=186&h=230&rev=47&ac=1&parent=1kRxnuKAogf_cE0y6u2QYcjQimE0-jrBZ-LxcXKOUITw)

I think this can be too abstract for the reader. If we have an ongoing "simple example subgraph" that we establish above, then here we can attach weights to that graph, and the reader can follow along more easily. ALso, worth remembering that almost all graph nodes have a weight of 0, since nonzero weight corresponds to cred minting.

miyazono

comment created time in 3 days

+# An introduction to calculating Cred and Grain++I’m starting from the assumption that you have some familiarity with the SourceCred project. If you don’t I’d suggest you check out Dandelion's [20 minute talk (+Qs) from SustainWeb3](https://www.youtube.com/watch?v=yVTqRLekRl4), and some of the other documents in this repo.++Overall, I’m going to throw around some math, but you should be able to get solid intuition on this if you’re comfortable talking about a [graph](https://en.wikipedia.org/wiki/Graph_theory) as a set of nodes and edges connecting them, where the edges can be directed and have weights. I’ll also talk some about [the PageRank algorithm](https://en.wikipedia.org/wiki/PageRank), for computing “importance” of nodes in a graph based on the “importance” of nodes connected to it, but I’ll try to motivate the intuition behind it as well.++## The high level overview++SourceCred takes information about contributions and generates a weights graph, and this post will first explain what that graph is and where it comes from. From this weights graph, it runs PageRank to generate a graph of Cred. This graph of Cred is then used to distribute grain. The grain can then be kept or traded, and soon will be able to be used to influence future graph weights. This process is diagrammed in the figure below.+ +![](https://docs.google.com/drawings/u/1/d/s50mxZJ1DFD_yr4OFUxUd9g/image?w=551&h=250&rev=57&ac=1&parent=1kRxnuKAogf_cE0y6u2QYcjQimE0-jrBZ-LxcXKOUITw)++> Figure 1. A very high level overview of how SourceCred allocates Cred and Grain, distinguishing between actions taken by contributors and those taken by the SourceCred algorithm.++The rest of this doc outlines how collaboration data is gathered and structured, and how the exact amounts of Cred and Grain are calculated, and ends with some future directions SourceCred as a project is considering.++## Components of contributing in SourceCred++SourceCred first generates a weights graph from the input data. In this graph, a node is a contributor or contribution, and edges represent some sort of connection between the nodes. These connections are intended to include all possible ways that a contribution or contributor on a platform can express dependencies, thanks, or flows of value. Examples would include anything from a mention on a forum post to liking or merging a PR on GitHub.++#### Nodes:++Contributions are represented as nodes in the graph, and are given addresses to identify them uniquely. These addresses include a prefix, which takes the form: organization making the plugin that generates the node + name of the plugin + type of contribution (e.g. topic/post/user/like).

I'm not sure if this detailed stuff on node addresses belong in an intro document... it's relevant for someone who wants to write a plugin, but otherwise "below the fold". the UI generally hides node addresses.

miyazono

comment created time in 3 days

+# An introduction to calculating Cred and Grain++I’m starting from the assumption that you have some familiarity with the SourceCred project. If you don’t I’d suggest you check out Dandelion's [20 minute talk (+Qs) from SustainWeb3](https://www.youtube.com/watch?v=yVTqRLekRl4), and some of the other documents in this repo.++Overall, I’m going to throw around some math, but you should be able to get solid intuition on this if you’re comfortable talking about a [graph](https://en.wikipedia.org/wiki/Graph_theory) as a set of nodes and edges connecting them, where the edges can be directed and have weights. I’ll also talk some about [the PageRank algorithm](https://en.wikipedia.org/wiki/PageRank), for computing “importance” of nodes in a graph based on the “importance” of nodes connected to it, but I’ll try to motivate the intuition behind it as well.++## The high level overview++SourceCred takes information about contributions and generates a weights graph, and this post will first explain what that graph is and where it comes from. From this weights graph, it runs PageRank to generate a graph of Cred. This graph of Cred is then used to distribute grain. The grain can then be kept or traded, and soon will be able to be used to influence future graph weights. This process is diagrammed in the figure below.+ +![](https://docs.google.com/drawings/u/1/d/s50mxZJ1DFD_yr4OFUxUd9g/image?w=551&h=250&rev=57&ac=1&parent=1kRxnuKAogf_cE0y6u2QYcjQimE0-jrBZ-LxcXKOUITw)++> Figure 1. A very high level overview of how SourceCred allocates Cred and Grain, distinguishing between actions taken by contributors and those taken by the SourceCred algorithm.++The rest of this doc outlines how collaboration data is gathered and structured, and how the exact amounts of Cred and Grain are calculated, and ends with some future directions SourceCred as a project is considering.++## Components of contributing in SourceCred++SourceCred first generates a weights graph from the input data. In this graph, a node is a contributor or contribution, and edges represent some sort of connection between the nodes. These connections are intended to include all possible ways that a contribution or contributor on a platform can express dependencies, thanks, or flows of value. Examples would include anything from a mention on a forum post to liking or merging a PR on GitHub.

I recommend including a diagram showing a concrete example, e.g. a post on Discourse that references a GitHub pull request, with connections to authors, etc.

miyazono

comment created time in 3 days

+# An introduction to calculating Cred and Grain++I’m starting from the assumption that you have some familiarity with the SourceCred project. If you don’t I’d suggest you check out Dandelion's [20 minute talk (+Qs) from SustainWeb3](https://www.youtube.com/watch?v=yVTqRLekRl4), and some of the other documents in this repo.++Overall, I’m going to throw around some math, but you should be able to get solid intuition on this if you’re comfortable talking about a [graph](https://en.wikipedia.org/wiki/Graph_theory) as a set of nodes and edges connecting them, where the edges can be directed and have weights. I’ll also talk some about [the PageRank algorithm](https://en.wikipedia.org/wiki/PageRank), for computing “importance” of nodes in a graph based on the “importance” of nodes connected to it, but I’ll try to motivate the intuition behind it as well.++## The high level overview++SourceCred takes information about contributions and generates a weights graph, and this post will first explain what that graph is and where it comes from. From this weights graph, it runs PageRank to generate a graph of Cred. This graph of Cred is then used to distribute grain. The grain can then be kept or traded, and soon will be able to be used to influence future graph weights. This process is diagrammed in the figure below.+ +![](https://docs.google.com/drawings/u/1/d/s50mxZJ1DFD_yr4OFUxUd9g/image?w=551&h=250&rev=57&ac=1&parent=1kRxnuKAogf_cE0y6u2QYcjQimE0-jrBZ-LxcXKOUITw)++> Figure 1. A very high level overview of how SourceCred allocates Cred and Grain, distinguishing between actions taken by contributors and those taken by the SourceCred algorithm.++The rest of this doc outlines how collaboration data is gathered and structured, and how the exact amounts of Cred and Grain are calculated, and ends with some future directions SourceCred as a project is considering.++## Components of contributing in SourceCred++SourceCred first generates a weights graph from the input data. In this graph, a node is a contributor or contribution, and edges represent some sort of connection between the nodes. These connections are intended to include all possible ways that a contribution or contributor on a platform can express dependencies, thanks, or flows of value. Examples would include anything from a mention on a forum post to liking or merging a PR on GitHub.++#### Nodes:++Contributions are represented as nodes in the graph, and are given addresses to identify them uniquely. These addresses include a prefix, which takes the form: organization making the plugin that generates the node + name of the plugin + type of contribution (e.g. topic/post/user/like).++Contributors are also represented as nodes in the graph. These nodes are similarly prefixed, and connected to contributions with directed edges, as illustrated in the figure below. For example, if an author node creates an issue, the author node gets an incoming edge from the issue node. Or if an author of a Discourse post mentions another contributor in the post, the post creates an outbound edge coming from the author.++Lastly, initiatives are another type of node that serve as a high level place to connect relevant nodes and discuss governance. It's similar to a tracking issue, but generalized across the weights graph, providing a way to organize planned work for a well-scoped task.++#### Weights:++Both nodes and edges have weights, and an edge object in the SourceCred implementation can have forward and backwards weight. We’ll get into what these weights mean shortly, but for now, it’s worth noting that these weights can easily be set by address prefix. Setting by prefix is implemented as a multiplicative update; for example, setting issue comments to be worth twice an issue emoji reaction creates a 2:1 ratio that is preserved by decreasing GitHub’s edge weights by 2 relative to those of Discourse. Additionally, nodes can have self edges, which is relevant when a piece of content is edited, or an contribution references itself.+

Yeah, we generally call them loops.

miyazono

comment created time in 3 days

+# An introduction to calculating Cred and Grain++I’m starting from the assumption that you have some familiarity with the SourceCred project. If you don’t I’d suggest you check out Dandelion's [20 minute talk (+Qs) from SustainWeb3](https://www.youtube.com/watch?v=yVTqRLekRl4), and some of the other documents in this repo.++Overall, I’m going to throw around some math, but you should be able to get solid intuition on this if you’re comfortable talking about a [graph](https://en.wikipedia.org/wiki/Graph_theory) as a set of nodes and edges connecting them, where the edges can be directed and have weights. I’ll also talk some about [the PageRank algorithm](https://en.wikipedia.org/wiki/PageRank), for computing “importance” of nodes in a graph based on the “importance” of nodes connected to it, but I’ll try to motivate the intuition behind it as well.++## The high level overview++SourceCred takes information about contributions and generates a weights graph, and this post will first explain what that graph is and where it comes from. From this weights graph, it runs PageRank to generate a graph of Cred. This graph of Cred is then used to distribute grain. The grain can then be kept or traded, and soon will be able to be used to influence future graph weights. This process is diagrammed in the figure below.+ +![](https://docs.google.com/drawings/u/1/d/s50mxZJ1DFD_yr4OFUxUd9g/image?w=551&h=250&rev=57&ac=1&parent=1kRxnuKAogf_cE0y6u2QYcjQimE0-jrBZ-LxcXKOUITw)

We'll want to commit the diagram image in this repo, not a link out to Google docs.

Feedback on the diagram:

• The "contribute" and "data in location" sections are unclear -- I recommend using specific examples like GitHub and Discourse.
• Replace "weights graph" with "contribution graph"
• Replace "PageRank" with "CredRank" :D
• Replace "cred graph" with "cred scores"
miyazono

comment created time in 3 days

+# An introduction to calculating Cred and Grain++I’m starting from the assumption that you have some familiarity with the SourceCred project. If you don’t I’d suggest you check out Dandelion's [20 minute talk (+Qs) from SustainWeb3](https://www.youtube.com/watch?v=yVTqRLekRl4), and some of the other documents in this repo.++Overall, I’m going to throw around some math, but you should be able to get solid intuition on this if you’re comfortable talking about a [graph](https://en.wikipedia.org/wiki/Graph_theory) as a set of nodes and edges connecting them, where the edges can be directed and have weights. I’ll also talk some about [the PageRank algorithm](https://en.wikipedia.org/wiki/PageRank), for computing “importance” of nodes in a graph based on the “importance” of nodes connected to it, but I’ll try to motivate the intuition behind it as well.++## The high level overview++SourceCred takes information about contributions and generates a weights graph, and this post will first explain what that graph is and where it comes from. From this weights graph, it runs PageRank to generate a graph of Cred. This graph of Cred is then used to distribute grain. The grain can then be kept or traded, and soon will be able to be used to influence future graph weights. This process is diagrammed in the figure below.

We generally call it a "contribution graph". Calling it a "weights" graph is confusing IMO: what's most interesting about the graph is that it represents contributions and connections between them; the fact that we also store weights in the graph is not as conceptually important.

miyazono

comment created time in 3 days

push eventdecentralion/cred-graph-data-exploration

commit sha bc2a172a18ff70d3b5a00b3f104a9cbd67b806d7

push time in 3 days

push eventsourcecred/sourcecred

commit sha 721606f60162a7f950e32c2fa54b4ebe74441162

push time in 3 days

push eventsourcecred/sourcecred

commit sha d3c24a0c64be99fb5cf4c59cb7c0feb6f7313579

easiest game I’ve ever played

push time in 3 days

push eventdecentralion/cred-graph-data-exploration

commit sha 09a51dd7d6d60cd1edd8baf9360d90142180f6bd

update scsc after fixing edge direction :-)

push time in 3 days

create barnchdecentralion/cred-graph-data-exploration

created branch time in 4 days

created repositorydecentralion/cred-graph-data-exploration

created time in 4 days

push eventsourcecred/sourcecred

add credGraph, mpg, and pagerank to api index

push time in 4 days

push eventsourcecred/sourcecred

commit sha a41c6345322121a9260acdd06cb930d189fc3b51

changes to credGraph and markovProcessGraph; expose weights, refactor options

commit sha 95079906c3e8c94fb028e55d5e8a44aa0e00808d

add pagerank.js for new-style markov process graphs

commit sha 44f6cffce22afa2ca405064aced1085109160642

factor raw field initializer constructor out of static method would be easier if these weren’t classes…

commit sha 6e18b0173ee8c8ed91fcb0490fd91216d9be4529

mpg: tojson, fromjson

commit sha 524f035ba796feeb75f76b2a1a86fd963f7ca19c

commit sha 492aa3c1a01b58ab8bd48059bbe13ca05d9de13c

fixup! factor raw field initializer constructor out of static method

commit sha e511a9ae210d90617cb07b5d35a926343e202246

fixup! mpg: tojson, fromjson

commit sha 668eee432a2a60a2cbd512d441f9f712a1114300

commit sha e2c63420311f5828d6724e72a7b085a26d5bd9d4

save the credgraph

push time in 4 days

IssuesEvent

This was not fixed. I am a rascal.

wchargin

comment created time in 4 days

push eventsourcecred/sourcecred

wip -- credGraph

commit sha 1a1c8aa0d9ceaa5a7ca094a31eaf5685c0ae73f7

credgraph

push time in 4 days

push eventsourcecred/sourcecred

commit sha 9dd9bdf2b1b07d2928dd8a58d104b0e26a4522db

wip -- fix bug and add plugin dec

commit sha b29d00675a58e3ab625f4a1459c467dcf75c2ac6

fixup declaration

push time in 4 days

push eventsourcecred/sourcecred

commit sha 67e36214c4ec945c8d25abedbb53d301dd40a1e3

convert legay pagerank to use MPG

push time in 4 days

push eventsourcecred/sourcecred

commit sha fb3a58d4b7feb153b2d2a3f485fefa059fefaa9d

fixup credgraph

push time in 4 days

push eventsourcecred/sourcecred

commit sha 60c5f4caf968d9bf017b4fc86722d79fbdb4c324

fixup flow error // default

commit sha e8b349c5843cec288c271aa5594e227ab3904c46

wip on credGraph and new pagerank

push time in 5 days

issue closedsourcecred/sourcecred

Right now a lot of the essential logic for running PageRank--namely generating the weights and turning the weights into an EdgeEvaluator--lives in app/credExplorer/weights. This means that the logic for running PageRank is commingled with the frontend code, which blocks #704 and (as a consequence) #935 and #703.

This issue is tracking work to move all of the essential logic for computing PageRank out of app and into the new analysis module.

Work so far: #944, #946, #947, #948, #949, #950, #965, #966

closed time in 8 days

decentralion

This was completed a long time ago.

decentralion

comment created time in 8 days

push eventsourcecred/sourcecred

Improve distributionToCred (#1654) This commit makes several small improvements to the distributionToCred module: - We rename the output FullTimelineCred data structure to TimelineCredScores, which is more descriptive - We re-organize that data structure so that rather than being an array of {interval, cred} objects, it has an intervals property and a intervalCredScores property, both of which are arrays. This will make downstream usage cleaner. - An unused variable is removed. - We document invariants about the TimelineCredScores data type. - We mark the TimelineCredScores data type opaque, so that clients recieving a TimelineCredScores can trust that the invariants are maintained. Test plan: - The rename is robustly tested by yarn flow. - That the refactor lands without changing existing semantics is robustly tested by yarn test --full, since we snapshot a full cred load; thus we know that cred scores haven't changed. (Also, we have existing unit tests). - The newly documented invariants aren't robustly tested by the test code, but it's easy to see that they hold by reading the algorithm.

commit sha c7f9a3d8f3bd3b2777aa4176be697b2a9f60da96

Add serialization to TimelineCredScores Right now the serialization is super trivial, we just attach a compat header. In the future, we can try encoding the float64 arrays as bytestrings to save space. Test plan: Unit tests included; yarn test passes.

Fix up serialization per @Beanow's review

push time in 12 days

push eventsourcecred/sourcecred

Improve distributionToCred (#1654) This commit makes several small improvements to the distributionToCred module: - We rename the output FullTimelineCred data structure to TimelineCredScores, which is more descriptive - We re-organize that data structure so that rather than being an array of {interval, cred} objects, it has an intervals property and a intervalCredScores property, both of which are arrays. This will make downstream usage cleaner. - An unused variable is removed. - We document invariants about the TimelineCredScores data type. - We mark the TimelineCredScores data type opaque, so that clients recieving a TimelineCredScores can trust that the invariants are maintained. Test plan: - The rename is robustly tested by yarn flow. - That the refactor lands without changing existing semantics is robustly tested by yarn test --full, since we snapshot a full cred load; thus we know that cred scores haven't changed. (Also, we have existing unit tests). - The newly documented invariants aren't robustly tested by the test code, but it's easy to see that they hold by reading the algorithm.

push time in 12 days

PR merged sourcecred/sourcecred

Reviewers

This commit makes several small improvements to the distributionToCred module:

• We rename the output FullTimelineCred data structure to TimelineCredScores, which is more descriptive
• We re-organize that data structure so that rather than being an array of {interval, cred} objects, it has an intervals property and a intervalCredScores property, both of which are arrays. This will make downstream usage cleaner.
• An unused variable is removed.
• We document invariants about the TimelineCredScores data type.
• We mark the TimelineCredScores data type opaque, so that clients recieving a TimelineCredScores can trust that the invariants are maintained.

Test plan:

• The rename is robustly tested by yarn flow.
• That the refactor lands without changing existing semantics is robustly tested by yarn test --full, since we snapshot a full cred load; thus we know that cred scores haven't changed. (Also, we have existing unit tests).
• The newly documented invariants aren't robustly tested by the test code, but it's easy to see that they hold by reading the algorithm.
+81 -66

3 changed files

decentralion

pr closed time in 12 days

PR opened sourcecred/sourcecred

Reviewers

This commit removes any dependency on plugins from the TimelineCred module. Instead, TimelineCred just needs the array of "scoring node prefixes", i.e. the prefix addresses for all nodes that we will use to normalize cred totals.

This is not the end of my cred-pipeline refactors (and I actually intend to eventually remove the TimelineCred module entirely), but it is a nice waypoint, since we can now close #1557.

Test plan: yarn test --full passes.

+83 -75

0 comment

9 changed files

pr created time in 12 days

create barnchsourcecred/sourcecred

created branch time in 12 days

push eventsourcecred/sourcecred

commit sha 32fe756b5bd6311024b9d5e8661c5a56a3d2a8a7

Initiatives: implement conversion from InitiativeFiles (#1648) Helper functions intended to be used in succession by loadDirectory. Only _validateUrl provides helfpul error messages. It's the caller's responsiblity to do this first.

commit sha db94bb50fb3d9a79c29d10980bee2dec429c9d9b

Initiatives: implement loadDirectory (#1649) It's tests are primarily smoke tests, as the underlying helper functions have been tested more extensively.

commit sha 22dac1f585376fea5958919a72edef27cc78a7d5

Improve distributionToCred This commit makes several small improvements to the distributionToCred module: - We rename the output FullTimelineCred data structure to TimelineCredScores, which is more descriptive - We re-organize that data structure so that rather than being an array of {interval, cred} objects, it has an intervals property and a intervalCredScores property, both of which are arrays. This will make downstream usage cleaner. - An unused variable is removed. - We document invariants about the TimelineCredScores data type. - We mark the TimelineCredScores data type opaque, so that clients recieving a TimelineCredScores can trust that the invariants are maintained. Test plan: - The rename is robustly tested by yarn flow. - That the refactor lands without changing existing semantics is robustly tested by yarn test --full, since we snapshot a full cred load; thus we know that cred scores haven't changed. (Also, we have existing unit tests). - The newly documented invariants aren't robustly tested by the test code, but it's easy to see that they hold by reading the algorithm.

commit sha 4db0bcab36ec89bc3100471bdc7e6b8954996d1b

Add serialization to TimelineCredScores Right now the serialization is super trivial, we just attach a compat header. In the future, we can try encoding the float64 arrays as bytestrings to save space. Test plan: Unit tests included; yarn test passes.

commit sha e390c21680b4c4650b3e9ff1a87580eb93166309

Fix up serialization per @Beanow's review

push time in 12 days

push eventsourcecred/sourcecred

commit sha 32fe756b5bd6311024b9d5e8661c5a56a3d2a8a7

Initiatives: implement conversion from InitiativeFiles (#1648) Helper functions intended to be used in succession by loadDirectory. Only _validateUrl provides helfpul error messages. It's the caller's responsiblity to do this first.

commit sha db94bb50fb3d9a79c29d10980bee2dec429c9d9b

Initiatives: implement loadDirectory (#1649) It's tests are primarily smoke tests, as the underlying helper functions have been tested more extensively.

commit sha 22dac1f585376fea5958919a72edef27cc78a7d5

Improve distributionToCred This commit makes several small improvements to the distributionToCred module: - We rename the output FullTimelineCred data structure to TimelineCredScores, which is more descriptive - We re-organize that data structure so that rather than being an array of {interval, cred} objects, it has an intervals property and a intervalCredScores property, both of which are arrays. This will make downstream usage cleaner. - An unused variable is removed. - We document invariants about the TimelineCredScores data type. - We mark the TimelineCredScores data type opaque, so that clients recieving a TimelineCredScores can trust that the invariants are maintained. Test plan: - The rename is robustly tested by yarn flow. - That the refactor lands without changing existing semantics is robustly tested by yarn test --full, since we snapshot a full cred load; thus we know that cred scores haven't changed. (Also, we have existing unit tests). - The newly documented invariants aren't robustly tested by the test code, but it's easy to see that they hold by reading the algorithm.

push time in 12 days

push eventsourcecred/sourcecred

commit sha ebdf581b6c1dcfc185196486d9b8cf1b37fe0d82

Improve distributionToCred This commit makes several small improvements to the distributionToCred module: - We rename the output FullTimelineCred data structure to TimelineCredScores, which is more descriptive - We re-organize that data structure so that rather than being an array of {interval, cred} objects, it has an intervals property and a intervalCredScores property, both of which are arrays. This will make downstream usage cleaner. - An unused variable is removed. - We document invariants about the TimelineCredScores data type. - We mark the TimelineCredScores data type opaque, so that clients recieving a TimelineCredScores can trust that the invariants are maintained. Test plan: - The rename is robustly tested by yarn flow. - That the refactor lands without changing existing semantics is robustly tested by yarn test --full, since we snapshot a full cred load; thus we know that cred scores haven't changed. (Also, we have existing unit tests). - The newly documented invariants aren't robustly tested by the test code, but it's easy to see that they hold by reading the algorithm.

push time in 12 days

 import {type TimelineDistributions} from "./timelinePagerank"; import {NodeAddress, type NodeAddressT} from "../../core/graph";  /**- * Represents the full timeline cred for a graph.+ * Represents cred scores over time.+ *+ * It contains an array of intervals, which give timing information, and an+ * array of CredTimeSlices, which are Float64Arrays. Each CredTimeSlice+ * contains cred scores for an interval. The cred scores are included in+ * node-address-sorted order, and as such the CredScores can only be+ * interpreted in the context of an associated Graph.+ *+ * As invariants, it is guaranteed that:+ * - intervals and intervalCredScores will always have the same length+ * - all of the intervalCredScores will have a consistent implicit node ordering+ *+ * The type is marked opaque so that no-one else can construct instances that+ * don't conform to these invariants.  */-export type FullTimelineCred = $ReadOnlyArray<{|- // The interval for this slice.- +interval: Interval,- // The cred for each node.- // (Uses the graph's canonical node ordering.)- +cred: Float64Array,-|}>;+export opaque type TimelineCredScores: {|+ +intervals:$ReadOnlyArray<Interval>,+  +intervalCredScores: $ReadOnlyArray<Float64Array>, Would have preferred a nit-and-approve still so I can merge off that approval if I adopt your comment. I think adding a comment makes sense when asking a question, or when you would need to see the changed approach to know if you'd want to approve it. In this case, since you're proposing an explicit change outright, it feels sensible to just approve it. decentralion comment created time in 12 days pull request commentsourcecred/sourcecred Sure thing. It's used for the CredGraph module, see https://github.com/sourcecred/sourcecred/pull/1657 decentralion comment created time in 12 days pull request commentsourcecred/sourcecred cc @vsoch -- as of this commit, the whole pipeline for computing cred scores is in core, and in a state that I'm reasonably happy with. Although there's more work to do to make the data format more efficient, and to add helper methods for retrieving useful information from the CredGraph. decentralion comment created time in 12 days PR opened sourcecred/sourcecred Reviewers core/credGraph is a new module which allows you to compute a CredGraph, which, as the name suggests, contains a WeightedGraph, TimelineCredScores, and the CredParameters used to generate it. Thus, you can think of this as a burgeoning replacement for the (ugly and non-core) TimelineCred module inside analysis/. I intend for this to be the canonical module for generating and representing cred scores. In followon PRs, I will migrate the timelineCred module to depend on credGraph, and will "dissolve" all of its non-trivial functionality into credGraph; e.g. credGraph will have methods for retrieving nodes and edges along with relevant cred scores and weights. In the future, I expect we'll also add well-tested and well documented methods for analyzing cred scores, e.g. for computing cred decompositions in the style of the pagerankNodeDecomposition module, except with much cleaner APIs that are consistent with the rest of core. You'll know that this refactor is going well when I entirely remove the src/analysis module. :) Test plan: Review the code, and observe that I've included smoke unit tests. yarn test passes. +246 -0 0 comment 2 changed files pr created time in 12 days pull request commentsourcecred/sourcecred It matches the APIs we're already actually using. When we get the cred, we tend to want just cred score without the intervals: https://github.com/sourcecred/sourcecred/blob/4a4c35bfdc4fadc0c9fb90489ae2386712773375/src/analysis/timeline/timelineCred.js#L33-L40 https://github.com/sourcecred/sourcecred/blob/4a4c35bfdc4fadc0c9fb90489ae2386712773375/src/analysis/timeline/timelineCred.js#L106-L122 Meanwhile when we get the intervals, we tend to just want the intervals on their own. https://github.com/sourcecred/sourcecred/blob/4a4c35bfdc4fadc0c9fb90489ae2386712773375/src/analysis/timeline/timelineCred.js#L99-L104 decentralion comment created time in 12 days pull request commentsourcecred/sourcecred I've pushed a second commit addressing the issue you found @beanow. As an added benefit, the new representation is less grotesquely wasteful of space. :) decentralion comment created time in 12 days push eventsourcecred/sourcecred commit sha 2871cca9b21fe9e23abd6d976ee05624939d43c1 Fix up serialization per @Beanow's review push time in 12 days create barnchsourcecred/sourcecred created branch time in 12 days pull request commentsourcecred/sourcecred You're right... this doesn't actually work from a type perspective. JSON.parse(JSON.stringify(new Float64Array([1]))).constructor ƒ Object() { [native code] }  I'll re-work it. decentralion comment created time in 12 days pull request commentsourcecred/sourcecred Sure. It's terrible. :) > JSON.stringify(new Float64Array([1,2,Math.PI])) "{"0":1,"1":2,"2":3.141592653589793}"  decentralion comment created time in 12 days PR opened sourcecred/sourcecred Reviewers Right now the serialization is super trivial, we just attach a compat header. In the future, we can try encoding the float64 arrays as bytestrings to save space. Test plan: Unit tests included; yarn test passes. +41 -1 0 comment 2 changed files pr created time in 12 days create barnchsourcecred/sourcecred created branch time in 12 days issue openedsourcecred/sourcecred While working on a totally unrelated change, I saw the following failure on dataDirectory.test.js:  FAIL src/backend/dataDirectory.test.js ● src/backend/dataDirectory › DataDirectory › DataDirectory.storeProject › should work whe n sourcecredDirectory doesn't exist SyntaxError: Unexpected end of JSON input at JSON.parse (<anonymous>) 140 | const expectJSONFile = async (name: string, expected: any) => { 141 | const filePath = path.join(expectedProjectDirectory, name); > 142 | const actual = JSON.parse(await fs.readFile(filePath)); | ^ 143 | expect(actual).toEqual(expected); 144 | };  I have no reproduction. cc @beanow, who wrote the test in question. created time in 12 days pull request commentsourcecred/sourcecred cc @wchargin; i'm channeling you by starting to write explicit invariants for joined data types cc @vsoch because it's part of my push to cleanup the cred computation pipeline decentralion comment created time in 12 days PR opened sourcecred/sourcecred Reviewers This commit makes several small improvements to the distributionToCred module: • We rename the output FullTimelineCred data structure to TimelineCredScores, which is more descriptive • We re-organize that data structure so that rather than being an array of {interval, cred} objects, it has an intervals property and a intervalCredScores property, both of which are arrays. This will make downstream usage cleaner. • An unused variable is removed. • We document invariants about the TimelineCredScores data type. • We mark the TimelineCredScores data type opaque, so that clients recieving a TimelineCredScores can trust that the invariants are maintained. Test plan: • The rename is robustly tested by yarn flow. • That the refactor lands without changing existing semantics is robustly tested by yarn test --full, since we snapshot a full cred load; thus we know that cred scores haven't changed. (Also, we have existing unit tests). • The newly documented invariants aren't robustly tested by the test code, but it's easy to see that they hold by reading the algorithm. +79 -66 0 comment 3 changed files pr created time in 12 days create barnchsourcecred/sourcecred created branch time in 12 days issue commentsourcecred/sourcecred What makes retry more robust than bottleneck ? Bottleneck allows you to rate limit the API calls, so when there's a fixed rate (e.g. Discourse), you can set the bottleneck below that level, and (probably) never run out of API budget. Very simple approach. However, if you DO get rate limited (i.e. get HTTP code 529), then bottleneck doesn't have logic for a wait-and-retry strategy, so the process will die. In practice, I set a rate limit of 55 (a little below Discourse's stated limit of 60), because I found if I set the rate limit any higher, it would sometimes die. This was a sign that my approach was not particularly robust; it would have been more robust to wait a little bit and then retry when the rate limit was hit. You might not need either retry though. You could take a more precise approach based on counting API calls, use that number of calls, and then explicitly wait for the next reset timestamp. But I suspect this approach would prove fragile. So I propose using retry, hitting the API until it 529s, and then waiting until the next api reset timestamp. As a side quest, you can improve the Discourse fetcher to use retry as well, and that should improve download performance by about 10% (since we're currently under-using our API quota). BrianLitwin comment created time in 12 days issue commentsourcecred/sourcecred I'm sort of leaning towards the bot being the right move. Creating a bot doesn't seem hard - but you do need to have the appropriate permission, which I don't think I have. Cool, making a bot sounds good to me too. @credbot's first real duty? :) (i.e., i propose the name credbot.) I propose we start by developing against a test Discord instance, i.e. a server named sourcecred-test. Much like our test Discourse forum and our test GitHub organization, this will let us use snapshot testing against an example instance that stays in a known expected state. Please set up that Discord instance, invite @Beanow, @wchargin and myself as admins. We should probably have a public invite link to the Discord which we post in the documentation of of the plugin module. We can put a few test messages there, define a custom reaction, and use it as our test case. I've also given you a temporary grant of admin power on our live Discord, so that you'll be empowered to set up the bot and integrate with our server. Please don't use the admin powers for anything other than working on the plugin; once we have the plugin integrated, we'll retire the adminship. BrianLitwin comment created time in 12 days PR closed sourcecred/sourcecred ## The devDependency eslint-plugin-react was updated from 7.16.0 to 7.17.0. This version is not covered by your current version range. If you don’t accept this pull request, your project will work just like it did before. However, you might be missing out on a bunch of new features, fixes and/or performance improvements from the dependency update. Publisher: ljharb License: MIT <details> <summary>Release Notes for v7.17.0</summary> <h3>Added</h3> <ul> <li><a href="/yannickcr/eslint-plugin-react/blob/v7.17.0/docs/rules/jsx-no-target-blank.md"><code>jsx-no-target-blank</code></a>: add <code>allowReferrer</code> option (<a href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/pull/2478" data-hovercard-type="pull_request" data-hovercard-url="/yannickcr/eslint-plugin-react/pull/2478/hovercard">#2478</a> <a class="user-mention" data-hovercard-type="user" data-hovercard-url="/users/eps1lon/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://urls.greenkeeper.io/eps1lon">@eps1lon</a>)</li> <li><a href="/yannickcr/eslint-plugin-react/blob/v7.17.0/docs/rules/jsx-handler-names.md"><code>jsx-handler-names</code></a>: add <code>checkLocalVariables</code> option (<a href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/pull/2470" data-hovercard-type="pull_request" data-hovercard-url="/yannickcr/eslint-plugin-react/pull/2470/hovercard">#2470</a> <a class="user-mention" data-hovercard-type="user" data-hovercard-url="/users/aub/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://urls.greenkeeper.io/aub">@aub</a>)</li> <li><a href="/yannickcr/eslint-plugin-react/blob/v7.17.0/docs/rules/prop-types.md"><code>prop-types</code></a>: Support Flow Type spread (<a href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/pull/2446" data-hovercard-type="pull_request" data-hovercard-url="/yannickcr/eslint-plugin-react/pull/2446/hovercard">#2446</a> <a class="user-mention" data-hovercard-type="user" data-hovercard-url="/users/moroine/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://urls.greenkeeper.io/moroine">@moroine</a>)</li> <li><a href="/yannickcr/eslint-plugin-react/blob/v7.17.0/docs/rules/jsx-props-no-spreading.md"><code>jsx-props-no-spreading</code></a>: add <code>explicitSpread</code> option to allow explicit spread of props (<a href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/pull/2449" data-hovercard-type="pull_request" data-hovercard-url="/yannickcr/eslint-plugin-react/pull/2449/hovercard">#2449</a> <a class="user-mention" data-hovercard-type="user" data-hovercard-url="/users/pawelnvk/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://urls.greenkeeper.io/pawelnvk">@pawelnvk</a>)</li> <li><a href="/yannickcr/eslint-plugin-react/blob/v7.17.0/docs/rules/jsx-no-target-blank.md"><code>jsx-no-target-blank</code></a>: warn on <code>target={'_blank'}</code> expressions (<a href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/pull/2451" data-hovercard-type="pull_request" data-hovercard-url="/yannickcr/eslint-plugin-react/pull/2451/hovercard">#2451</a> <a class="user-mention" data-hovercard-type="user" data-hovercard-url="/users/timkraut/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://urls.greenkeeper.io/timkraut">@timkraut</a>)</li> </ul> <h3>Fixed</h3> <ul> <li><a href="/yannickcr/eslint-plugin-react/blob/v7.17.0/docs/rules/sort-prop-types.md"><code>sort-prop-types</code></a>, <a href="/yannickcr/eslint-plugin-react/blob/v7.17.0/docs/rules/jsx-sort-default-props.md"><code>jsx-sort-default-props</code></a>: disable broken autofix (<a href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/pull/2505" data-hovercard-type="pull_request" data-hovercard-url="/yannickcr/eslint-plugin-react/pull/2505/hovercard">#2505</a> <a class="user-mention" data-hovercard-type="user" data-hovercard-url="/users/webOS101/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://urls.greenkeeper.io/webOS101">@webOS101</a>)</li> <li><a href="/yannickcr/eslint-plugin-react/blob/v7.17.0/docs/rules/no-typos.md"><code>no-typos</code></a>: improve report location (<a href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/pull/2468" data-hovercard-type="pull_request" data-hovercard-url="/yannickcr/eslint-plugin-react/pull/2468/hovercard">#2468</a> <a class="user-mention" data-hovercard-type="user" data-hovercard-url="/users/golopot/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://urls.greenkeeper.io/golopot">@golopot</a>)</li> <li><a href="/yannickcr/eslint-plugin-react/blob/v7.17.0/docs/rules/jsx-no-literals.md"><code>jsx-no-literals</code></a>: trim whitespace for <code>allowedStrings</code> check (<a href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/pull/2436" data-hovercard-type="pull_request" data-hovercard-url="/yannickcr/eslint-plugin-react/pull/2436/hovercard">#2436</a> <a class="user-mention" data-hovercard-type="user" data-hovercard-url="/users/cainlevy/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://urls.greenkeeper.io/cainlevy">@cainlevy</a>)</li> <li><a href="/yannickcr/eslint-plugin-react/blob/v7.17.0/docs/rules/jsx-curly-brace-presence.md"><code>jsx-curly-brace-presence</code></a>: Fix filter of undefined error with whitespace inside jsx attr curlies (<a href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/pull/2460" data-hovercard-type="pull_request" data-hovercard-url="/yannickcr/eslint-plugin-react/pull/2460/hovercard">#2460</a> <a class="user-mention" data-hovercard-type="user" data-hovercard-url="/users/dustinyoste/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://urls.greenkeeper.io/dustinyoste">@dustinyoste</a>)</li> <li><a href="/yannickcr/eslint-plugin-react/blob/v7.17.0/docs/rules/no-render-return-value.md"><code>no-render-return-value</code></a>: should warn when used in assignment expression ([<a class="issue-link js-issue-link" data-error-text="Failed to load issue title" data-id="506077166" data-permission-text="Issue title is private" data-url="https://github.com/yannickcr/eslint-plugin-react/issues/2462" data-hovercard-type="pull_request" data-hovercard-url="/yannickcr/eslint-plugin-react/pull/2462/hovercard" href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/pull/2462">#2462</a>][] <a class="user-mention" data-hovercard-type="user" data-hovercard-url="/users/jichu4n/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://urls.greenkeeper.io/jichu4n">@jichu4n</a>)</li> <li><a href="/yannickcr/eslint-plugin-react/blob/v7.17.0/docs/rules/jsx-curly-brace-presence.md"><code>jsx-curly-brace-presence</code></a>: allow trailing spaces in literal (<a href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/pull/2448" data-hovercard-type="pull_request" data-hovercard-url="/yannickcr/eslint-plugin-react/pull/2448/hovercard">#2448</a> <a class="user-mention" data-hovercard-type="user" data-hovercard-url="/users/doochik/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://urls.greenkeeper.io/doochik">@doochik</a>)</li> </ul> <h3>Changed</h3> <ul> <li>[Deps] update <code>jsx-ast-utils</code>, <code>object.fromentries</code>, <code>resolve</code></li> <li>[eslint] fix func-names and change object-shorthand to 'always' (<a href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/pull/2483" data-hovercard-type="pull_request" data-hovercard-url="/yannickcr/eslint-plugin-react/pull/2483/hovercard">#2483</a> <a class="user-mention" data-hovercard-type="user" data-hovercard-url="/users/golopot/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://urls.greenkeeper.io/golopot">@golopot</a>)</li> <li>[Docs] <code>jsx-first-prop-new-line</code>: Fix documentation formatting (<a href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/pull/2489" data-hovercard-type="pull_request" data-hovercard-url="/yannickcr/eslint-plugin-react/pull/2489/hovercard">#2489</a> <a class="user-mention" data-hovercard-type="user" data-hovercard-url="/users/pjg/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://urls.greenkeeper.io/pjg">@pjg</a>)</li> <li>[Docs] <a href="/yannickcr/eslint-plugin-react/blob/v7.17.0/docs/rules/prop-types.md"><code>prop-types</code></a>: Update 'skipUndeclared' in rule options (<a href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/pull/2504" data-hovercard-type="pull_request" data-hovercard-url="/yannickcr/eslint-plugin-react/pull/2504/hovercard">#2504</a> <a class="user-mention" data-hovercard-type="user" data-hovercard-url="/users/cjnickel/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://urls.greenkeeper.io/cjnickel">@cjnickel</a>)</li> <li>[Docs] <a href="/yannickcr/eslint-plugin-react/blob/v7.17.0/docs/rules/jsx-first-prop-new-line.md"><code>jsx-first-prop-new-line</code></a>: fix wrong rule name (<a href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/pull/2500" data-hovercard-type="pull_request" data-hovercard-url="/yannickcr/eslint-plugin-react/pull/2500/hovercard">#2500</a> <a class="user-mention" data-hovercard-type="user" data-hovercard-url="/users/zgayjjf/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://urls.greenkeeper.io/zgayjjf">@zgayjjf</a>)</li> <li>[eslint] enable eslint-plugin-eslint-plugin (<a href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/pull/2469" data-hovercard-type="pull_request" data-hovercard-url="/yannickcr/eslint-plugin-react/pull/2469/hovercard">#2469</a> <a class="user-mention" data-hovercard-type="user" data-hovercard-url="/users/golopot/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://urls.greenkeeper.io/golopot">@golopot</a>)</li> <li>[Docs] <a href="/yannickcr/eslint-plugin-react/blob/v7.17.0/docs/rules/jsx-props-no-multi-spaces.md"><code>jsx-props-no-multi-spaces</code></a>: suggest using core rule instead (<a href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/pull/2463" data-hovercard-type="pull_request" data-hovercard-url="/yannickcr/eslint-plugin-react/pull/2463/hovercard">#2463</a> <a class="user-mention" data-hovercard-type="user" data-hovercard-url="/users/golopot/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://urls.greenkeeper.io/golopot">@golopot</a>)</li> <li>[Docs] <a href="/yannickcr/eslint-plugin-react/blob/v7.17.0/docs/rules/jsx-first-prop-new-line.md"><code>jsx-first-prop-new-line</code></a>: add rule options (<a href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/pull/2465" data-hovercard-type="pull_request" data-hovercard-url="/yannickcr/eslint-plugin-react/pull/2465/hovercard">#2465</a> <a class="user-mention" data-hovercard-type="user" data-hovercard-url="/users/SerdarMustafa1/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://urls.greenkeeper.io/SerdarMustafa1">@SerdarMustafa1</a>)</li> <li>[Docs] <a href="/yannickcr/eslint-plugin-react/blob/v7.17.0/docs/rules/jsx-no-target-blank.md"><code>jsx-no-target-blank</code></a>: Add section about overriding for trusted links (<a href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/pull/2438" data-hovercard-type="pull_request" data-hovercard-url="/yannickcr/eslint-plugin-react/pull/2438/hovercard">#2438</a> <a class="user-mention" data-hovercard-type="user" data-hovercard-url="/users/aschriner/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://urls.greenkeeper.io/aschriner">@aschriner</a>)</li> <li>[Docs] fix typo (<a href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/pull/2453" data-hovercard-type="pull_request" data-hovercard-url="/yannickcr/eslint-plugin-react/pull/2453/hovercard">#2453</a> <a class="user-mention" data-hovercard-type="user" data-hovercard-url="/users/cainwatson/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://urls.greenkeeper.io/cainwatson">@cainwatson</a>)</li> <li>[Docs] <a href="/yannickcr/eslint-plugin-react/blob/v7.17.0/docs/rules/no-unused-prop-types.md"><code>no-unused-prop-types</code></a>: clean up prose (<a href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/pull/2273" data-hovercard-type="pull_request" data-hovercard-url="/yannickcr/eslint-plugin-react/pull/2273/hovercard">#2273</a> <a class="user-mention" data-hovercard-type="user" data-hovercard-url="/users/coryhouse/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://urls.greenkeeper.io/coryhouse">@coryhouse</a>)</li> <li>[Docs] <a href="/yannickcr/eslint-plugin-react/blob/v7.17.0/docs/rules/jsx-no-bind.md"><code>jsx-no-bind</code></a>: add section about React Hooks (<a href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/pull/2443" data-hovercard-type="pull_request" data-hovercard-url="/yannickcr/eslint-plugin-react/pull/2443/hovercard">#2443</a> <a class="user-mention" data-hovercard-type="user" data-hovercard-url="/users/kdex/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://urls.greenkeeper.io/kdex">@kdex</a>)</li> </ul> </details> <details> <summary>Commits</summary> <p>The new version differs by 26 commits.</p> <ul> <li><a href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/commit/027ebd98fe29325c84ea816440ce021b05a8ec96"><code>027ebd9</code></a> <code>Update CHANGELOG and bump version</code></li> <li><a href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/commit/89911ded99253b6804f3e26da6a11ed93fcead42"><code>89911de</code></a> <code>[Deps] update <code>jsx-ast-utils</code>, <code>object.fromentries</code>, <code>resolve</code></code></li> <li><a href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/commit/cfef664b0600d0a363aa341c8f417b3b060918b6"><code>cfef664</code></a> <code>[Dev Deps] update <code>@types/eslint</code>, <code>@types/estree</code>, <code>@types/node</code>, <code>coveralls</code>, <code>typescript</code></code></li> <li><a href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/commit/d3b5d717c170b3afb5596effd845f8ca34327978"><code>d3b5d71</code></a> <code> [New] <code>jsx-no-target-blank</code>: add <code>allowReferrer</code> option</code></li> <li><a href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/commit/83ac4c0b6f777cf46082a17c6c85435eb831e4f6"><code>83ac4c0</code></a> <code>[eslint] fix func-names and change object-shorthand to 'always'</code></li> <li><a href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/commit/225b433b21528b3c196626b0e56bfebad4513193"><code>225b433</code></a> <code>[Docs] <code>jsx-first-prop-new-line</code>: Fix documentation formatting</code></li> <li><a href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/commit/62df1dd316df39d7aaed02d6bacd415803301943"><code>62df1dd</code></a> <code>[Docs] <code>prop-types</code>: Update 'skipUndeclared' in rule options</code></li> <li><a href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/commit/87d4cabe8d271b4146436ac15197b13fd67c7a62"><code>87d4cab</code></a> <code>[Docs] <code>jsx-first-prop-new-line</code>: fix wrong rule name</code></li> <li><a href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/commit/55b605f9ca6541a4c655d82b7595bb71b0ed552c"><code>55b605f</code></a> <code>[Fix] <code>sort-prop-types</code>, <code>jsx-sort-default-props</code>: disable broken autofix</code></li> <li><a href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/commit/71c7d01efe41cd448518331fd72bd05bba565680"><code>71c7d01</code></a> <code>[Tests] temporarily pin eslint 6 tests to v6.6</code></li> <li><a href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/commit/809356582ce1123c90bc7a7e9740b0bb7a6eb38f"><code>8093565</code></a> <code>[New] <code>jsx-handler-names</code>: add <code>checkLocalVariables</code> option</code></li> <li><a href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/commit/5be539b9d1b3595b81b081f2b4b98e174530e66f"><code>5be539b</code></a> <code>[Fix] <code>no-typos</code>: improve report location</code></li> <li><a href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/commit/d9d21934427ee3c710d37aa341483dc0b82f12d4"><code>d9d2193</code></a> <code>[eslint] enable eslint-plugin-eslint-plugin</code></li> <li><a href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/commit/39e4396e3f74bdeaf170cfb202b6407eb6f155f2"><code>39e4396</code></a> <code>[Docs] <code>jsx-props-no-multi-spaces</code>: suggest using core rule instead</code></li> <li><a href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/commit/9e4ad2130f2050655ce144d5c77a385062b1b5b1"><code>9e4ad21</code></a> <code>[Docs] <code>jsx-first-prop-new-line</code>: add rule options</code></li> </ul> <p>There are 26 commits in total.</p> <p>See the <a href="https://urls.greenkeeper.io/yannickcr/eslint-plugin-react/compare/fb3210baa13c1c5853fb95a451c1e817840bfde4...027ebd98fe29325c84ea816440ce021b05a8ec96">full diff</a></p> </details> <details> <summary>FAQ and help</summary> There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper. </details> Your Greenkeeper bot :palm_tree: +91 -9 6 comments 2 changed files greenkeeper[bot] pr closed time in 12 days pull request commentsourcecred/sourcecred Going to close this while we wait for the upstream fix to release. greenkeeper[bot] comment created time in 12 days Pull request review commentsourcecred/sourcecred  export async function _readFiles( return map; }++// Checks the provided URL will parse and has no trailing slashes, search or hash.+// Returns the validated URL without trailing slashes or throws.+export function _validateUrl(remoteUrl: string): string {+ try {+ const url = new global.URL(remoteUrl);+ if (url.search) {+ throw URL should not have a search component:${url.search};+    }+    if (url.hash) {+      throw URL should not have a hash component: ${url.hash};+ }+ return url.toString().replace(/\/+$/, "");+  } catch (e) {+    throw new Error(+      Provided initiatives directory URL was invalid: ${remoteUrl}\n${e}+    );+  }+}++// Converts the InitiativeFiles we've read to Initiatives.+export function _convertToInitiatives(+  directory: InitiativesDirectory,+  map: NamesToInitiativeFiles+): $ReadOnlyArray<Initiative> {+ const initiatives = []; nit: could have used Array.from(map.entries()).map which I find somewhat cleaner, due to my bias towards functional rather than imperative/mutating style. However, clearly an instance of not-how-i-would-have-done-it; no change requested. Beanow comment created time in 12 days pull request commentsourcecred/sourcecred Thank you @wchargin! I love your practice of debugging issues like this, and then filing super clean-and-actionable repros on upstream repositories. greenkeeper[bot] comment created time in 12 days Pull request review commentsourcecred/sourcecred  export function _initiativeAddress( ...NodeAddress.toParts(_trackerAddress(remoteUrl, fileName)) ); }++// Checks the path exists and is a directory.+// Returns the absolute path or throws.+export async function _validatePath(localPath: string): Promise<string> {+ const absPath = path.resolve(localPath);+ if (!(await fs.exists(absPath))) {+ throw new Error(+ Provided initiatives directory does not exist at:${absPath}+    );+  }+  if (!(await fs.lstat(absPath)).isDirectory()) {+    throw new Error(+      Provided initiatives directory is not a directory at: ${absPath}+ );+ }+ return absPath;+}++// Gets all *.json filenames in the given directory.+export async function _findFiles(+ localPath: string+): Promise<$ReadOnlyArray<string>> {+  const absoluteFileNames = await globby(path.join(localPath, "*.json"));+  return absoluteFileNames.map((a) => path.basename(a));+}++type NamesToInitiativeFiles = Map<string, InitiativeFile>;++// Reads all given filenames in the given directory, validating them as compat.+export async function _readFiles(+  localPath: string,+  fileNames: $ReadOnlyArray<string>+): Promise<NamesToInitiativeFiles> {+ const map: NamesToInitiativeFiles = new Map();++ // TODO(perf): can do this as a stream or in parallel.+ const sortedFileNames = [...fileNames].sort(); Thanks, I appreciate the comment. Beanow comment created time in 12 days Pull request review commentsourcecred/sourcecred  export function _initiativeAddress( ...NodeAddress.toParts(_trackerAddress(remoteUrl, fileName)) ); }++// Checks the path exists and is a directory.+// Returns the absolute path or throws.+export async function _validatePath(localPath: string): Promise<string> {+ const absPath = path.resolve(localPath);+ if (!(await fs.exists(absPath))) {+ throw new Error(+ Provided initiatives directory does not exist at:${absPath}+    );+  }+  if (!(await fs.lstat(absPath)).isDirectory()) {+    throw new Error(+      Provided initiatives directory is not a directory at: ${absPath}+ );+ }+ return absPath;+}++// Gets all *.json filenames in the given directory.+export async function _findFiles(+ localPath: string+): Promise<$ReadOnlyArray<string>> {+  const absoluteFileNames = await globby(path.join(localPath, "*.json"));

Oh, I didn't realize that Windows has a different file path representation. Which is kind of embarrassing as I now see that I've written bugs around backslash handling in the past. Cool, let's revisit this later. :)

Beanow

comment created time in 12 days

 export function fromJSON(j: Compatible<any>): InitiativeFile { export function toJSON(m: InitiativeFile): Compatible<InitiativeFile> {   return toCompat(COMPAT_INFO, m); }++/**+ * When provided with the initiative NodeAddressT of an InitiativeFile this extracts+ * the URL from it. Or null when the address is not for an InitiativeFile.+ */+export function initiativeFileURL(address: NodeAddressT): string | null {+  const initiativeFilePrefix = NodeAddress.append(+    initiativeNodeType.prefix,+    INITIATIVE_FILE_SUBTYPE+  );++  if (!NodeAddress.hasPrefix(address, initiativeFilePrefix)) {+    return null;+  }

Cool, thanks. I love that we have Flow to ensure that all clients will need to handle the case where it doesn't correspond to an initiative.

Beanow

comment created time in 12 days

delete branch sourcecred/sourcecred

delete time in 12 days

push eventsourcecred/sourcecred

commit sha 579554d2653aa52d26ca58fee6e51595fd85d1ab

Add core.weights and core.weightedGraph to api (#1652) This commit modifies the api declaration file so that it includes core.weights and core.weightedGraph. These are both important modules for interfacing with Cred (loading a WeightedGraph, and defining custom weights), so it's natural for them to be included in the API. Test plan: Change is a trivial extension of the pattern in api.js. yarn flow passes, and I believe no further testing is _required_; however, I will use this in an Observable notebook prior to merging.

push time in 12 days

PR merged sourcecred/sourcecred

Reviewers

This commit modifies the api declaration file so that it includes core.weights and core.weightedGraph. These are both important modules for interfacing with Cred (loading a WeightedGraph, and defining custom weights), so it's natural for them to be included in the API.

Test plan: Change is a trivial extension of the pattern in api.js. yarn flow passes, and I believe no further testing is required; however, I will use this in an Observable notebook prior to merging.

+4 -0

0 comment

1 changed file

decentralion

pr closed time in 12 days

 export function _initiativeAddress(     ...NodeAddress.toParts(_trackerAddress(remoteUrl, fileName))   ); }++// Checks the path exists and is a directory.+// Returns the absolute path or throws.+export async function _validatePath(localPath: string): Promise<string> {+  const absPath = path.resolve(localPath);+  if (!(await fs.exists(absPath))) {+    throw new Error(+      Provided initiatives directory does not exist at: ${absPath}+ );+ }+ if (!(await fs.lstat(absPath)).isDirectory()) {+ throw new Error(+ Provided initiatives directory is not a directory at:${absPath}+    );+  }+  return absPath;+}++// Gets all *.json filenames in the given directory.+export async function _findFiles(+  localPath: string+): Promise<$ReadOnlyArray<string>> {+ const absoluteFileNames = await globby(path.join(localPath, "*.json"));+ return absoluteFileNames.map((a) => path.basename(a));+}++type NamesToInitiativeFiles = Map<string, InitiativeFile>;++// Reads all given filenames in the given directory, validating them as compat.+export async function _readFiles(+ localPath: string,+ fileNames:$ReadOnlyArray<string>+): Promise<NamesToInitiativeFiles> {+  const map: NamesToInitiativeFiles = new Map();++  // TODO(perf): can do this as a stream or in parallel.+  const sortedFileNames = [...fileNames].sort();

The eventual type we'll arrive at is: $ReadOnlyArray<Initiative>. This means it's an ordered type. [] and ROA are both ordered. :) During prototyping I had several test flakes. So I thought, it would be good for reproducibility of loads as well to side with caution and sort them. Rather than sort them in tests only. As this order will be relevant in all parts of the system up to the WeightedGraph. Once in a graph it can be sorted by it's address. Makes sense! Beanow comment created time in 13 days Pull request review commentsourcecred/sourcecred  export function _initiativeAddress( ...NodeAddress.toParts(_trackerAddress(remoteUrl, fileName)) ); }++// Checks the path exists and is a directory.+// Returns the absolute path or throws.+export async function _validatePath(localPath: string): Promise<string> {+ const absPath = path.resolve(localPath);+ if (!(await fs.exists(absPath))) {+ throw new Error(+ Provided initiatives directory does not exist at:${absPath}+    );+  }+  if (!(await fs.lstat(absPath)).isDirectory()) {+    throw new Error(+      Provided initiatives directory is not a directory at: ${absPath}+ );+ }+ return absPath;+}++// Gets all *.json filenames in the given directory.+export async function _findFiles(+ localPath: string+): Promise<$ReadOnlyArray<string>> {+  const absoluteFileNames = await globby(path.join(localPath, "*.json"));

I don't think we need to represent it as an array in the id / NodeAddress. We could just have the full subpath be a component, as in ["sourcecred", "initiative", "INITIATIVE_FILE", "https://github.com/sourcecred/cred/master/", "path/to/my/file.json"].

Beanow

comment created time in 13 days

Here are the docs on rate limits. They don't have fixed rate limits, see:

Because we may change rate limits at any time and rate limits can be different per application, rate limits should not be hard coded into your bot/application. In order to properly support our dynamic rate limits, your bot/application should parse for our rate limits in response headers and locally prevent exceeding the limits as they change.

So that will be an added bit of complexity.

You may want to take a look at the Discourse Fetcher for some inspiration for having a class that manages interacting with the API. However, I was a little lazy when setting up the rate limiting there, and used a hardcoded approach based on bottleneck rather than a more robust approach based on retry. So don't copy what I did with the rate limiter since it won't work for Discord... but do consider improving the Discourse plugin after writing the retry logic for Discord. :)

BrianLitwin

comment created time in 13 days

Here are the docs on authentication. Wonder if we should make a bot account or just get the oauth token?

BrianLitwin

comment created time in 13 days

Overall, this makes sense to me. I agree with @Beanow that we shouldn't have to ask for messages earlier than our oldest cache entry, since presumably no-one will time travel and add messages to the past.

Some questions:

• What API keys are needed?
• What rate limits apply?
• How will we find the :sourcecred: reactions?
• How are users represented? Presumably we'll identify user nodes by username-id-increment pairs like decentralion#8636. We can then add these to the identity plugin.
• Do we have any back of the envelope for how many messages are on our server? (To get a sense of how many queries we'll need to fully update.)
BrianLitwin

comment created time in 13 days

 export function fromJSON(j: Compatible<any>): InitiativeFile { export function toJSON(m: InitiativeFile): Compatible<InitiativeFile> {   return toCompat(COMPAT_INFO, m); }++/**+ * When provided with the initiative NodeAddressT of an InitiativeFile this extracts+ * the URL from it. Or null when the address is not for an InitiativeFile.+ */+export function initiativeFileURL(address: NodeAddressT): string | null {+  const initiativeFilePrefix = NodeAddress.append(+    initiativeNodeType.prefix,+    INITIATIVE_FILE_SUBTYPE+  );++  if (!NodeAddress.hasPrefix(address, initiativeFilePrefix)) {+    return null;+  }++  const parts = NodeAddress.toParts(address);+  const remoteUrl = parts[4];+  const fileName = parts[5];+  return ${remoteUrl}/${fileName};+}++// Creates the InitiativeId for an InitiativeFile.+export function _initiativeFileId(+  remoteUrl: string,

nit: might be nice to take InitiativeDirectory object as the first argument, for more type safety / clarity

Beanow

comment created time in 13 days

 export function fromJSON(j: Compatible<any>): InitiativeFile { export function toJSON(m: InitiativeFile): Compatible<InitiativeFile> {   return toCompat(COMPAT_INFO, m); }++/**+ * When provided with the initiative NodeAddressT of an InitiativeFile this extracts+ * the URL from it. Or null when the address is not for an InitiativeFile.+ */+export function initiativeFileURL(address: NodeAddressT): string | null {+  const initiativeFilePrefix = NodeAddress.append(+    initiativeNodeType.prefix,+    INITIATIVE_FILE_SUBTYPE+  );++  if (!NodeAddress.hasPrefix(address, initiativeFilePrefix)) {+    return null;+  }

Can you explain why we want this behavior, rather than throwing an error when we got a non-initiative address?

Beanow

comment created time in 13 days

more