Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

maintainer.gno

9.32 Kb · 294 lines
  1// Package maintainer models an AI agent stewarding a versioned on-chain
  2// package — without ever letting it deploy on its own authority.
  3//
  4// The agent can do the tedious, valuable parts: open a version proposal,
  5// collect reviews, record CI test attestations, write release notes,
  6// deprecate a vulnerable version, keep a compatibility matrix. What it
  7// cannot do is ship. Approval is gated by an explicit, machine-checked
  8// policy:
  9//
 10//	tests pass  AND  ≥ MinReviewers independent reviews  AND
 11//	every review scores ≥ MinScore  AND  the challenge window has elapsed
 12//
 13// This is the difference between "an agent with commit access" and "an
 14// agent that drives a process a human (or a DAO, or a policy) still gates."
 15// The realm records *approved* releases; the actual package publish is a
 16// separate, deliberately human/authority step.
 17package maintainer
 18
 19import (
 20	"chain"
 21	"chain/runtime"
 22	"strings"
 23
 24	"gno.land/p/nt/ufmt/v0"
 25)
 26
 27const (
 28	MinReviewers = 2  // independent reviews required
 29	MinScore     = 80 // minimum score every reviewer must give
 30)
 31
 32// Review is one reviewer's score for a proposal.
 33type Review struct {
 34	By     address
 35	Score  uint8 // 0..100
 36	Note   string
 37	Height int64
 38}
 39
 40// Proposal is a candidate new version working its way toward approval.
 41type Proposal struct {
 42	ID             uint64
 43	Version        string
 44	ArtifactHash   string // commitment to the package source
 45	Proposer       address
 46	Reviews        []Review
 47	TestsPass      bool
 48	TestsAttestor  address
 49	OpenedAt       int64
 50	ChallengeUntil int64 // approval blocked until height passes this
 51	Approved       bool
 52	ApprovedAt     int64
 53}
 54
 55// Release is an approved (and possibly later deprecated) version.
 56type Release struct {
 57	Version          string
 58	ArtifactHash     string
 59	ReleasedAt       int64
 60	Deprecated       bool
 61	DeprecatedReason string
 62}
 63
 64var (
 65	proposals []*Proposal        // index 0 => ID 1
 66	releases  []*Release         // in release order
 67	byVersion = map[string]*Release{}
 68)
 69
 70// Propose opens a version proposal with a challenge window of the given
 71// number of blocks (during which reviews accrue and approval is blocked).
 72func Propose(cur realm, version, artifactHash string, challengeBlocks int64) uint64 {
 73	assert(version != "", "empty version")
 74	assert(artifactHash != "", "empty artifact hash")
 75	assert(challengeBlocks >= 0, "negative challenge window")
 76	_, exists := byVersion[version]
 77	assert(!exists, "version already released: "+version)
 78
 79	id := uint64(len(proposals)) + 1
 80	h := runtime.ChainHeight()
 81	proposals = append(proposals, &Proposal{
 82		ID:             id,
 83		Version:        version,
 84		ArtifactHash:   artifactHash,
 85		Proposer:       cur.Previous().Address(),
 86		OpenedAt:       h,
 87		ChallengeUntil: h + challengeBlocks,
 88	})
 89	chain.Emit("VersionProposed", "id", ufmt.Sprintf("%d", id), "version", version)
 90	return id
 91}
 92
 93// AddReview records a reviewer's score. Reviewers must be independent of the
 94// proposer, and each address reviews at most once.
 95func AddReview(cur realm, propID uint64, score uint8, note string) {
 96	p := mustProposal(propID)
 97	assert(!p.Approved, "proposal already approved")
 98	assert(score <= 100, "score must be 0..100")
 99	reviewer := cur.Previous().Address()
100	assert(reviewer != p.Proposer, "proposer cannot review own proposal")
101	for _, r := range p.Reviews {
102		assert(r.By != reviewer, "reviewer already reviewed")
103	}
104	p.Reviews = append(p.Reviews, Review{By: reviewer, Score: score, Note: note, Height: runtime.ChainHeight()})
105	chain.Emit("VersionReviewed", "id", ufmt.Sprintf("%d", propID), "score", ufmt.Sprintf("%d", score))
106}
107
108// AttestTests records whether CI's test suite passed for the proposal.
109func AttestTests(cur realm, propID uint64, pass bool) {
110	p := mustProposal(propID)
111	assert(!p.Approved, "proposal already approved")
112	p.TestsPass = pass
113	p.TestsAttestor = cur.Previous().Address()
114	chain.Emit("TestsAttested", "id", ufmt.Sprintf("%d", propID), "pass", boolStr(pass))
115}
116
117// Approve promotes a proposal to a release iff every policy condition holds.
118// It panics with the first unmet condition — the policy is the gate, not a
119// suggestion.
120func Approve(cur realm, propID uint64) {
121	p := mustProposal(propID)
122	assert(!p.Approved, "proposal already approved")
123	assert(p.TestsPass, "policy: tests have not passed")
124	assert(len(p.Reviews) >= MinReviewers, ufmt.Sprintf("policy: need %d reviews, have %d", MinReviewers, len(p.Reviews)))
125	for _, r := range p.Reviews {
126		assert(r.Score >= MinScore, ufmt.Sprintf("policy: review from %s scored %d < %d", short(r.By), r.Score, MinScore))
127	}
128	assert(runtime.ChainHeight() > p.ChallengeUntil, "policy: challenge window still open")
129
130	p.Approved = true
131	p.ApprovedAt = runtime.ChainHeight()
132	rel := &Release{Version: p.Version, ArtifactHash: p.ArtifactHash, ReleasedAt: p.ApprovedAt}
133	releases = append(releases, rel)
134	byVersion[p.Version] = rel
135	chain.Emit("VersionApproved", "id", ufmt.Sprintf("%d", propID), "version", p.Version)
136}
137
138// Deprecate flags a released version (e.g. a vulnerability was found).
139func Deprecate(cur realm, version, reason string) {
140	rel, ok := byVersion[version]
141	assert(ok, "unknown version: "+version)
142	rel.Deprecated = true
143	rel.DeprecatedReason = reason
144	chain.Emit("VersionDeprecated", "version", version)
145}
146
147// ---- read-only API ----
148
149// LatestVersion returns the most recently released version, or "".
150func LatestVersion() string {
151	if len(releases) == 0 {
152		return ""
153	}
154	return releases[len(releases)-1].Version
155}
156
157// IsDeprecated reports whether a released version is deprecated.
158func IsDeprecated(version string) bool {
159	rel, ok := byVersion[version]
160	return ok && rel.Deprecated
161}
162
163// GetProposal returns a copy of a proposal.
164func GetProposal(id uint64) Proposal { return *mustProposal(id) }
165
166// ---- internal ----
167
168func mustProposal(id uint64) *Proposal {
169	assert(id >= 1 && id <= uint64(len(proposals)), "unknown proposal")
170	return proposals[id-1]
171}
172
173func boolStr(b bool) string {
174	if b {
175		return "yes"
176	}
177	return "no"
178}
179
180func assert(cond bool, msg string) {
181	if !cond {
182		panic(msg)
183	}
184}
185
186// Render shows the release matrix + open proposals, or one proposal at :<id>.
187func Render(path string) string {
188	if path == "" {
189		return renderIndex()
190	}
191	return renderProposal(parseID(path))
192}
193
194func renderIndex() string {
195	var sb strings.Builder
196	sb.WriteString("# Agent Package Maintainer\n\n")
197	sb.WriteString(ufmt.Sprintf("_The agent drives the process; the policy still gates the ship._ "))
198	sb.WriteString(ufmt.Sprintf("Approval needs tests + ≥%d reviews each ≥%d + an elapsed challenge window.\n\n", MinReviewers, MinScore))
199
200	sb.WriteString("## Releases\n\n")
201	if len(releases) == 0 {
202		sb.WriteString("_No releases yet._\n\n")
203	} else {
204		sb.WriteString("| Version | Released @ | State |\n|---|---|---|\n")
205		for _, r := range releases {
206			st := "✅ current"
207			if r.Deprecated {
208				st = "⚠ deprecated: " + r.DeprecatedReason
209			}
210			sb.WriteString(ufmt.Sprintf("| %s | %d | %s |\n", r.Version, r.ReleasedAt, st))
211		}
212		sb.WriteString("\n")
213	}
214
215	sb.WriteString("## Proposals\n\n")
216	if len(proposals) == 0 {
217		sb.WriteString("_None._\n")
218		return sb.String()
219	}
220	sb.WriteString("| # | Version | Tests | Reviews | Approved |\n|---|---|---|---|---|\n")
221	for _, p := range proposals {
222		sb.WriteString(ufmt.Sprintf("| [%d](/r/nym-moultest123/agents/maintainer/v0:%d) | %s | %s | %d | %t |\n",
223			p.ID, p.ID, p.Version, boolStr(p.TestsPass), len(p.Reviews), p.Approved))
224	}
225	return sb.String()
226}
227
228func renderProposal(id uint64) string {
229	p := mustProposal(id)
230	var sb strings.Builder
231	sb.WriteString(ufmt.Sprintf("# Proposal #%d — %s\n\n", p.ID, p.Version))
232	sb.WriteString(ufmt.Sprintf("- **Artifact:** `%s`\n", shortHash(p.ArtifactHash)))
233	sb.WriteString(ufmt.Sprintf("- **Proposer:** %s\n", short(p.Proposer)))
234	sb.WriteString(ufmt.Sprintf("- **Tests pass:** %t\n", p.TestsPass))
235	sb.WriteString(ufmt.Sprintf("- **Challenge window until height:** %d (now %d)\n", p.ChallengeUntil, runtime.ChainHeight()))
236	sb.WriteString(ufmt.Sprintf("- **Approved:** %t\n\n", p.Approved))
237
238	sb.WriteString(ufmt.Sprintf("## Policy checklist\n\n"))
239	sb.WriteString(check(p.TestsPass, "tests pass"))
240	sb.WriteString(check(len(p.Reviews) >= MinReviewers, ufmt.Sprintf("≥%d independent reviews (have %d)", MinReviewers, len(p.Reviews))))
241	minOK := true
242	for _, r := range p.Reviews {
243		if r.Score < MinScore {
244			minOK = false
245		}
246	}
247	sb.WriteString(check(len(p.Reviews) > 0 && minOK, ufmt.Sprintf("every review ≥%d", MinScore)))
248	sb.WriteString(check(runtime.ChainHeight() > p.ChallengeUntil, "challenge window elapsed"))
249	sb.WriteString("\n")
250
251	sb.WriteString(ufmt.Sprintf("## Reviews (%d)\n\n", len(p.Reviews)))
252	if len(p.Reviews) == 0 {
253		sb.WriteString("_None._\n")
254		return sb.String()
255	}
256	sb.WriteString("| Reviewer | Score | Note | Height |\n|---|---|---|---|\n")
257	for _, r := range p.Reviews {
258		sb.WriteString(ufmt.Sprintf("| %s | %d | %s | %d |\n", short(r.By), r.Score, r.Note, r.Height))
259	}
260	return sb.String()
261}
262
263func check(ok bool, label string) string {
264	box := "❌"
265	if ok {
266		box = "✅"
267	}
268	return ufmt.Sprintf("- %s %s\n", box, label)
269}
270
271func parseID(s string) uint64 {
272	var n uint64
273	for i := 0; i < len(s); i++ {
274		ch := s[i]
275		assert(ch >= '0' && ch <= '9', "invalid proposal path: "+s)
276		n = n*10 + uint64(ch-'0')
277	}
278	return n
279}
280
281func short(a address) string {
282	s := a.String()
283	if len(s) <= 12 {
284		return s
285	}
286	return s[:8] + "…" + s[len(s)-4:]
287}
288
289func shortHash(s string) string {
290	if len(s) <= 12 {
291		return s
292	}
293	return s[:6] + "…" + s[len(s)-4:]
294}