-
Notifications
You must be signed in to change notification settings - Fork 362
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: nir1218's evaluation DAO proposal POC (#792)
* first draft * Getting discussion rolling * Revert "first draft" This reverts commit e5739d0. removing handler changes i removing handler changes * moving location of the files * fix typo in folder name * adding .gitkeep * added tests * more tests * rename package * added approvals and refactoring * clean up after adding approvals * Adding more concepts in the code and a readme file * V2 readme file * fix build issues and refactoring * fix horrible spelling mistakes * refactor consts
- Loading branch information
Showing
14 changed files
with
791 additions
and
0 deletions.
There are no files selected for viewing
113 changes: 113 additions & 0 deletions
113
examples/gno.land/r/x/nir1218_evaluation_proposal/EVALUATION.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
# `Contribution Evaluation` | ||
|
||
## Abstract | ||
|
||
This document describes general ideas regarding contributions evaluation, the principles laid out are intendant to be part of the Evaluation DAO. | ||
|
||
## Contents | ||
|
||
- [Concepts](#concepts) | ||
|
||
- [Committee](#committee) | ||
- [Evaluation](#evaluation) | ||
- [Contribution](#contribution) | ||
- [Pull Request](#pull-request) | ||
- [Vote](#vote) | ||
|
||
- [Future Improvements](#future-improvements) | ||
|
||
- [Implementation](#implementation) | ||
|
||
## Concepts | ||
|
||
### General Ideas | ||
|
||
Contributors DAO will designate members of a committee, in the beginning, the evaluation committee members will be the core development team members or any other trusted entity. | ||
A committee will be given the mandate to evaluate a certain set of contributions. | ||
For example, the first committee will evaluate code contributions inside Gno central repository. | ||
A contribution will be associated with a pull request managed in Git. | ||
A Committee as a trusted entity can decide on a category and its corresponding evaluation criteria. | ||
A member can propose to add a category and its corresponding evaluation criteria. | ||
A member can propose a contribution for evaluation. However, the pull request category must be from the list of approved categories. | ||
At the time of writing, a member can vote based on as set of options either "YES" or "NO", all members need to approve a category or a contribution. | ||
|
||
### Committee | ||
|
||
A group of designated members who are given a mandate to act as an evaluation authority. | ||
A DAO may elect a committee and designate its members based on contributions or merits of the members. | ||
A committee member can propose a contribution to avoid spam and confirm viable contributions will be evaluated. | ||
|
||
### Evaluation | ||
|
||
A logical entity to group a certain types of contributions. | ||
|
||
#### Category | ||
|
||
A group of contributions that should be evaluated based on the same principles and guide lines. | ||
An example of a category is a bounty, a chore, a defect, or a document. | ||
|
||
### Contribution | ||
|
||
A contribution is associated with a pull request. | ||
A contribution has an evaluation life cycle. | ||
A submission time is set when a contribution is added. | ||
A last evaluation time is set when a contribution is evaluated and approved by a memeber. | ||
An approval time is set when a contribution is approved by all members (or when a future threshold is reached) | ||
|
||
#### Submission | ||
|
||
Any committee member can submit a contribution. | ||
|
||
#### Status | ||
|
||
When a contribution is submitted its status is set to "proposed", its status will change to "approved" once approved by the committee or to "declined" otherwise. | ||
Intermediate status options such as "negotiation", "discussion", "evaluation" are TBD. | ||
A further discussion around the idea of deleting a contribution is required as it raises questions regarding record keeping, double evaluations, and the motive. | ||
|
||
#### Approval | ||
|
||
A contribution is approved once it reaches a certain threshold. | ||
|
||
### Pull Request | ||
|
||
A pull request from a source control tool namely GitHub. | ||
|
||
### Vote | ||
|
||
#### Voters | ||
|
||
Voters are committee members, all committee members have the right and obligation to vote on a contribution. | ||
|
||
#### Voting Options | ||
|
||
The voting options available to a voter. | ||
A committee may set voting options for its evaluation categories. | ||
The initial option set includes the following options: | ||
|
||
- `YES` | ||
- `NO` | ||
|
||
#### Voting Period | ||
|
||
Voting period is set by the committee, all committee members are obligated to vote within the voting period. | ||
|
||
#### Threshold | ||
|
||
Threshold is the minimum percentage of `YES` votes from the total votes. | ||
|
||
#### Tally Votes | ||
|
||
## Future Improvements | ||
|
||
The current documentation describes the basic ideas as expressed in the code. | ||
Future improvements listed below will be decided based on future discussions and peer reviews. | ||
|
||
- Committee negotiates contributions | ||
- A committee may set voting options for its categories and evaluated contributions, otherwise; the Contributors DAO may set a global | ||
- A committee may set a threshold required for a category or a contribution to be approved, otherwise; the Contributors DAO may set a global threshold and quorum. | ||
- A committee sets evaluation criteria scoring range (1-10), scoring a contribution is essential when there are competing contributions (Game of Realm). Otherwise, the evaluation is a binary decision. Moreover, scoring should be translated to rewards of any sort, or become discussion points durning negotiation about the viability of a contribution. | ||
- Committee members asses contributions based on the evaluation criteria and vote accordingly | ||
|
||
## Implementation | ||
|
||
The implementation written is to express the ideas described above using code. Not all ideas have been implemented. |
51 changes: 51 additions & 0 deletions
51
examples/gno.land/r/x/nir1218_evaluation_proposal/category.gno
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package evaluation | ||
|
||
import ( | ||
"gno.land/p/demo/avl" | ||
) | ||
|
||
type Category struct { | ||
name string | ||
criteria []string | ||
status string | ||
votes avl.Tree | ||
tallyResult TallyResult | ||
} | ||
|
||
func NewCategory(name string, criteria []string) *Category { | ||
tallyResult := TallyResult{} | ||
tallyResult.results.Set(VoteYes, 0) | ||
tallyResult.results.Set(VoteNo, 0) | ||
|
||
c := &Category{ | ||
name: name, | ||
criteria: criteria, | ||
status: "Proposed", | ||
votes: avl.Tree{}, | ||
tallyResult: tallyResult, | ||
} | ||
return c | ||
} | ||
|
||
func (c *Category) Approve() { | ||
// TODO error handling | ||
c.status = "Approved" | ||
} | ||
|
||
func (c Category) Status() string { | ||
return c.status | ||
} | ||
|
||
func (c *Category) Tally() { | ||
// TODO error handling | ||
c.votes.Iterate("", "", func(address string, vote interface{}) bool { | ||
v := vote.(Vote) | ||
value, exists := c.tallyResult.results.Get(v.option) | ||
if !exists { | ||
return false | ||
} | ||
count := value.(int) | ||
c.tallyResult.results.Set(v.option, count+1) | ||
return true | ||
}) | ||
} |
140 changes: 140 additions & 0 deletions
140
examples/gno.land/r/x/nir1218_evaluation_proposal/committee.gno
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
package evaluation | ||
|
||
import ( | ||
"std" | ||
|
||
"gno.land/p/demo/avl" | ||
"gno.land/p/demo/ufmt" | ||
) | ||
|
||
type Committee struct { | ||
members []std.Address // TODO - use avl tree or address set? | ||
categories avl.Tree // A catagory is mapped to a list of evaluation criteria | ||
evaluation *Evaluation | ||
} | ||
|
||
const ApprovedStatus = "Approved" | ||
|
||
func NewCommittee() *Committee { | ||
c := &Committee{ | ||
members: []std.Address{}, | ||
categories: avl.Tree{}, | ||
evaluation: NewEvalutaion(), | ||
} | ||
return c | ||
} | ||
|
||
func (c *Committee) DesignateMembers(members []std.Address) []std.Address { | ||
c.members = append(c.members, members...) | ||
return c.members | ||
} | ||
|
||
func (c *Committee) DismissMembers(members []std.Address) []std.Address { | ||
// TODO | ||
return []std.Address{} | ||
} | ||
|
||
func (c *Committee) AddCategory(name string, criteria []string) bool { | ||
// TODO error handling | ||
if !c.isMember(std.GetOrigCaller()) { | ||
return false | ||
} | ||
category := NewCategory(name, criteria) | ||
c.categories.Set(name, category) | ||
return true | ||
} | ||
|
||
func (c *Committee) ApproveCategory(name string, option string) bool { | ||
if !c.isMember(std.GetOrigCaller()) { | ||
return false | ||
} | ||
|
||
value, exists := c.categories.Get(name) | ||
if !exists { | ||
return false | ||
} | ||
category := value.(*Category) | ||
if category.Status() == ApprovedStatus { | ||
return false | ||
} | ||
|
||
vote := NewVote(std.GetOrigCaller(), option) | ||
category.votes.Set(std.GetOrigCaller().String(), vote) | ||
category.Tally() | ||
|
||
// TODO Add threshold factor for a category approval | ||
// TODO Add quorum factor for a category approval | ||
// Current assumption is all members voted YES so category is approved | ||
|
||
result, exists := category.tallyResult.results.Get(VoteYes) | ||
if !exists { | ||
return false | ||
} | ||
|
||
if result.(int) == len(c.members) { | ||
category.Approve() | ||
return true | ||
} | ||
|
||
return false | ||
} | ||
|
||
// TODO error handling | ||
func (c *Committee) AddContribution(pr *PullRequest, contributor std.Address) (contributionId int, ok bool) { | ||
if !c.isMember(std.GetOrigCaller()) { | ||
return -1, false | ||
} | ||
// Check the category of the PR matches a catagory this committee evaluates | ||
// TODO check the category is an approved category | ||
if c.categories.Has(pr.category) { | ||
return c.evaluation.AddContribution(pr, contributor) | ||
} | ||
|
||
return -1, false | ||
} | ||
|
||
// TODO error handling | ||
func (c *Committee) ApproveContribution(id int, option string) bool { | ||
if !c.isMember(std.GetOrigCaller()) { | ||
return false | ||
} | ||
|
||
value, exists := c.evaluation.contributions.Get(ufmt.Sprintf("%d", id)) | ||
if !exists { | ||
return false | ||
} | ||
contribution := value.(*Contribution) | ||
// Already approved | ||
if contribution.status == ApprovedStatus { | ||
return false | ||
} | ||
|
||
vote := NewVote(std.GetOrigCaller(), option) | ||
contribution.votes = append(contribution.votes, vote) | ||
contribution.Tally() | ||
|
||
// TODO Add threshold factor for a contribution approval | ||
// TODO Add quorum factor for a contribution approval | ||
// Current assumption is all members voted YES so contribution is approved | ||
|
||
result, exists := contribution.tallyResult.results.Get(VoteYes) | ||
if !exists { | ||
return false | ||
} | ||
|
||
if result.(int) == len(c.members) { | ||
contribution.Approve() | ||
return true | ||
} | ||
|
||
return false | ||
} | ||
|
||
func (c *Committee) isMember(m std.Address) bool { | ||
for _, member := range c.members { | ||
if m == member { | ||
return true | ||
} | ||
} | ||
return false | ||
} |
Oops, something went wrong.