diff --git a/bug/bug.go b/bug/bug.go
index ae662ef1647b282ef1612f9b413220f79d4629c5..e33787709a025f8df1f8591549e0197a018919b5 100644
--- a/bug/bug.go
+++ b/bug/bug.go
@@ -677,6 +677,3 @@ func (bug *Bug) Compile() Snapshot {
 
 	return snap
 }
-
-// Sign post method for gqlgen
-func (bug *Bug) IsAuthored() {}
diff --git a/bug/op_add_comment.go b/bug/op_add_comment.go
index e16ea0ddff5c84c7ee856fa3f7acf398a2817973..78c8847a8818355346ed2ac0cdde606d0421915c 100644
--- a/bug/op_add_comment.go
+++ b/bug/op_add_comment.go
@@ -21,6 +21,9 @@ type AddCommentOperation struct {
 	Files []git.Hash `json:"files"`
 }
 
+// Sign-post method for gqlgen
+func (op *AddCommentOperation) IsOperation() {}
+
 func (op *AddCommentOperation) base() *OpBase {
 	return &op.OpBase
 }
diff --git a/bug/op_create.go b/bug/op_create.go
index 0da95d4dc902fb4bdf6fbcd67f91fb54e85d44c2..b2af438b188d035bc35ba9ba86795340738ac5fa 100644
--- a/bug/op_create.go
+++ b/bug/op_create.go
@@ -22,6 +22,9 @@ type CreateOperation struct {
 	Files   []git.Hash `json:"files"`
 }
 
+// Sign-post method for gqlgen
+func (op *CreateOperation) IsOperation() {}
+
 func (op *CreateOperation) base() *OpBase {
 	return &op.OpBase
 }
diff --git a/bug/op_edit_comment.go b/bug/op_edit_comment.go
index 480204216bd37e419d16eb79c987c5fa4f851aa2..44ee5877c85b84f473050beb75c5a3a370c89854 100644
--- a/bug/op_edit_comment.go
+++ b/bug/op_edit_comment.go
@@ -24,6 +24,9 @@ type EditCommentOperation struct {
 	Files   []git.Hash `json:"files"`
 }
 
+// Sign-post method for gqlgen
+func (op *EditCommentOperation) IsOperation() {}
+
 func (op *EditCommentOperation) base() *OpBase {
 	return &op.OpBase
 }
diff --git a/bug/op_label_change.go b/bug/op_label_change.go
index c911de260f08e1ca30a0180f312eeb476caed9d9..fefe2402a6efa160bbe343fbc0138b9e24fad021 100644
--- a/bug/op_label_change.go
+++ b/bug/op_label_change.go
@@ -21,6 +21,9 @@ type LabelChangeOperation struct {
 	Removed []Label `json:"removed"`
 }
 
+// Sign-post method for gqlgen
+func (op *LabelChangeOperation) IsOperation() {}
+
 func (op *LabelChangeOperation) base() *OpBase {
 	return &op.OpBase
 }
diff --git a/bug/op_noop.go b/bug/op_noop.go
index 16d32297894f92dde4add1916f8d4b9ba98244f9..6364f9187b317f7d835160dc8475bddc54cf96c5 100644
--- a/bug/op_noop.go
+++ b/bug/op_noop.go
@@ -16,6 +16,9 @@ type NoOpOperation struct {
 	OpBase
 }
 
+// Sign-post method for gqlgen
+func (op *NoOpOperation) IsOperation() {}
+
 func (op *NoOpOperation) base() *OpBase {
 	return &op.OpBase
 }
diff --git a/bug/op_set_metadata.go b/bug/op_set_metadata.go
index 67f7e00936af371851a095ad6e7a8df34d2d9a22..23d11461c5fed9296fe84a4927d3722c53c81876 100644
--- a/bug/op_set_metadata.go
+++ b/bug/op_set_metadata.go
@@ -17,6 +17,9 @@ type SetMetadataOperation struct {
 	NewMetadata map[string]string `json:"new_metadata"`
 }
 
+// Sign-post method for gqlgen
+func (op *SetMetadataOperation) IsOperation() {}
+
 func (op *SetMetadataOperation) base() *OpBase {
 	return &op.OpBase
 }
diff --git a/bug/op_set_status.go b/bug/op_set_status.go
index 8a245184dc12b55f7bd1c633617905edf39b3000..eb2c0ba4d9383b5be8a0eb5f76f5c81e84a3a7fc 100644
--- a/bug/op_set_status.go
+++ b/bug/op_set_status.go
@@ -18,6 +18,9 @@ type SetStatusOperation struct {
 	Status Status `json:"status"`
 }
 
+// Sign-post method for gqlgen
+func (op *SetStatusOperation) IsOperation() {}
+
 func (op *SetStatusOperation) base() *OpBase {
 	return &op.OpBase
 }
diff --git a/bug/op_set_title.go b/bug/op_set_title.go
index fadd29a98a090dcd9a90b0f6a9950240aee25bc4..ddd98f0e0a42ddf05ee50b52af69e57828d937f2 100644
--- a/bug/op_set_title.go
+++ b/bug/op_set_title.go
@@ -21,6 +21,9 @@ type SetTitleOperation struct {
 	Was   string `json:"was"`
 }
 
+// Sign-post method for gqlgen
+func (op *SetTitleOperation) IsOperation() {}
+
 func (op *SetTitleOperation) base() *OpBase {
 	return &op.OpBase
 }
diff --git a/bug/operation.go b/bug/operation.go
index dd95e096765649b292f12db5ff1447698c8d9c97..20d44f6c8665b8b34092637e665589b51dcb3299 100644
--- a/bug/operation.go
+++ b/bug/operation.go
@@ -52,6 +52,9 @@ type Operation interface {
 	AllMetadata() map[string]string
 	// GetAuthor return the author identity
 	GetAuthor() identity.Interface
+
+	// sign-post method for gqlgen
+	IsOperation()
 }
 
 func deriveId(data []byte) entity.Id {
diff --git a/cache/filter.go b/cache/filter.go
index 9b1de1d58e2972d56dce8b809cc1b50f81b4aedf..f840067b3d4cf026da24d9c2a4b16c098cedb152 100644
--- a/cache/filter.go
+++ b/cache/filter.go
@@ -4,10 +4,17 @@ import (
 	"strings"
 
 	"github.com/MichaelMure/git-bug/bug"
+	"github.com/MichaelMure/git-bug/entity"
 )
 
+// resolver has the resolving functions needed by filters.
+// This exist mainly to go through the functions of the cache with proper locking.
+type resolver interface {
+	ResolveIdentityExcerpt(id entity.Id) (*IdentityExcerpt, error)
+}
+
 // Filter is a predicate that match a subset of bugs
-type Filter func(repoCache *RepoCache, excerpt *BugExcerpt) bool
+type Filter func(excerpt *BugExcerpt, resolver resolver) bool
 
 // StatusFilter return a Filter that match a bug status
 func StatusFilter(query string) (Filter, error) {
@@ -16,21 +23,21 @@ func StatusFilter(query string) (Filter, error) {
 		return nil, err
 	}
 
-	return func(repoCache *RepoCache, excerpt *BugExcerpt) bool {
+	return func(excerpt *BugExcerpt, resolver resolver) bool {
 		return excerpt.Status == status
 	}, nil
 }
 
 // AuthorFilter return a Filter that match a bug author
 func AuthorFilter(query string) Filter {
-	return func(repoCache *RepoCache, excerpt *BugExcerpt) bool {
+	return func(excerpt *BugExcerpt, resolver resolver) bool {
 		query = strings.ToLower(query)
 
 		// Normal identity
 		if excerpt.AuthorId != "" {
-			author, ok := repoCache.identitiesExcerpts[excerpt.AuthorId]
-			if !ok {
-				panic("missing identity in the cache")
+			author, err := resolver.ResolveIdentityExcerpt(excerpt.AuthorId)
+			if err != nil {
+				panic(err)
 			}
 
 			return author.Match(query)
@@ -43,7 +50,7 @@ func AuthorFilter(query string) Filter {
 
 // LabelFilter return a Filter that match a label
 func LabelFilter(label string) Filter {
-	return func(repoCache *RepoCache, excerpt *BugExcerpt) bool {
+	return func(excerpt *BugExcerpt, resolver resolver) bool {
 		for _, l := range excerpt.Labels {
 			if string(l) == label {
 				return true
@@ -55,13 +62,13 @@ func LabelFilter(label string) Filter {
 
 // ActorFilter return a Filter that match a bug actor
 func ActorFilter(query string) Filter {
-	return func(repoCache *RepoCache, excerpt *BugExcerpt) bool {
+	return func(excerpt *BugExcerpt, resolver resolver) bool {
 		query = strings.ToLower(query)
 
 		for _, id := range excerpt.Actors {
-			identityExcerpt, ok := repoCache.identitiesExcerpts[id]
-			if !ok {
-				panic("missing identity in the cache")
+			identityExcerpt, err := resolver.ResolveIdentityExcerpt(id)
+			if err != nil {
+				panic(err)
 			}
 
 			if identityExcerpt.Match(query) {
@@ -74,13 +81,13 @@ func ActorFilter(query string) Filter {
 
 // ParticipantFilter return a Filter that match a bug participant
 func ParticipantFilter(query string) Filter {
-	return func(repoCache *RepoCache, excerpt *BugExcerpt) bool {
+	return func(excerpt *BugExcerpt, resolver resolver) bool {
 		query = strings.ToLower(query)
 
 		for _, id := range excerpt.Participants {
-			identityExcerpt, ok := repoCache.identitiesExcerpts[id]
-			if !ok {
-				panic("missing identity in the cache")
+			identityExcerpt, err := resolver.ResolveIdentityExcerpt(id)
+			if err != nil {
+				panic(err)
 			}
 
 			if identityExcerpt.Match(query) {
@@ -93,7 +100,7 @@ func ParticipantFilter(query string) Filter {
 
 // TitleFilter return a Filter that match if the title contains the given query
 func TitleFilter(query string) Filter {
-	return func(repo *RepoCache, excerpt *BugExcerpt) bool {
+	return func(excerpt *BugExcerpt, resolver resolver) bool {
 		return strings.Contains(
 			strings.ToLower(excerpt.Title),
 			strings.ToLower(query),
@@ -103,7 +110,7 @@ func TitleFilter(query string) Filter {
 
 // NoLabelFilter return a Filter that match the absence of labels
 func NoLabelFilter() Filter {
-	return func(repoCache *RepoCache, excerpt *BugExcerpt) bool {
+	return func(excerpt *BugExcerpt, resolver resolver) bool {
 		return len(excerpt.Labels) == 0
 	}
 }
@@ -120,32 +127,32 @@ type Filters struct {
 }
 
 // Match check if a bug match the set of filters
-func (f *Filters) Match(repoCache *RepoCache, excerpt *BugExcerpt) bool {
-	if match := f.orMatch(f.Status, repoCache, excerpt); !match {
+func (f *Filters) Match(excerpt *BugExcerpt, resolver resolver) bool {
+	if match := f.orMatch(f.Status, excerpt, resolver); !match {
 		return false
 	}
 
-	if match := f.orMatch(f.Author, repoCache, excerpt); !match {
+	if match := f.orMatch(f.Author, excerpt, resolver); !match {
 		return false
 	}
 
-	if match := f.orMatch(f.Participant, repoCache, excerpt); !match {
+	if match := f.orMatch(f.Participant, excerpt, resolver); !match {
 		return false
 	}
 
-	if match := f.orMatch(f.Actor, repoCache, excerpt); !match {
+	if match := f.orMatch(f.Actor, excerpt, resolver); !match {
 		return false
 	}
 
-	if match := f.andMatch(f.Label, repoCache, excerpt); !match {
+	if match := f.andMatch(f.Label, excerpt, resolver); !match {
 		return false
 	}
 
-	if match := f.andMatch(f.NoFilters, repoCache, excerpt); !match {
+	if match := f.andMatch(f.NoFilters, excerpt, resolver); !match {
 		return false
 	}
 
-	if match := f.andMatch(f.Title, repoCache, excerpt); !match {
+	if match := f.andMatch(f.Title, excerpt, resolver); !match {
 		return false
 	}
 
@@ -153,28 +160,28 @@ func (f *Filters) Match(repoCache *RepoCache, excerpt *BugExcerpt) bool {
 }
 
 // Check if any of the filters provided match the bug
-func (*Filters) orMatch(filters []Filter, repoCache *RepoCache, excerpt *BugExcerpt) bool {
+func (*Filters) orMatch(filters []Filter, excerpt *BugExcerpt, resolver resolver) bool {
 	if len(filters) == 0 {
 		return true
 	}
 
 	match := false
 	for _, f := range filters {
-		match = match || f(repoCache, excerpt)
+		match = match || f(excerpt, resolver)
 	}
 
 	return match
 }
 
 // Check if all of the filters provided match the bug
-func (*Filters) andMatch(filters []Filter, repoCache *RepoCache, excerpt *BugExcerpt) bool {
+func (*Filters) andMatch(filters []Filter, excerpt *BugExcerpt, resolver resolver) bool {
 	if len(filters) == 0 {
 		return true
 	}
 
 	match := true
 	for _, f := range filters {
-		match = match && f(repoCache, excerpt)
+		match = match && f(excerpt, resolver)
 	}
 
 	return match
diff --git a/cache/filter_test.go b/cache/filter_test.go
index a47d2ad731f5dff308a844df5addb1bdf28ca6c3..2c7fabc0e9951acec8b22e765a8aa504831268bb 100644
--- a/cache/filter_test.go
+++ b/cache/filter_test.go
@@ -28,7 +28,7 @@ func TestTitleFilter(t *testing.T) {
 		t.Run(tt.name, func(t *testing.T) {
 			filter := TitleFilter(tt.query)
 			excerpt := &BugExcerpt{Title: tt.title}
-			assert.Equal(t, tt.match, filter(nil, excerpt))
+			assert.Equal(t, tt.match, filter(excerpt, nil))
 		})
 	}
 }
diff --git a/cache/repo_cache.go b/cache/repo_cache.go
index 18be9b5a59651aa6d0bff69a62cc6de32659b1d9..c8d6bd69ba424100ff36b56daa14fe4cd9c4ed5a 100644
--- a/cache/repo_cache.go
+++ b/cache/repo_cache.go
@@ -10,6 +10,7 @@ import (
 	"path"
 	"sort"
 	"strconv"
+	"sync"
 	"time"
 
 	"github.com/pkg/errors"
@@ -57,11 +58,13 @@ type RepoCache struct {
 	// the underlying repo
 	repo repository.ClockedRepo
 
+	muBug sync.RWMutex
 	// excerpt of bugs data for all bugs
 	bugExcerpts map[entity.Id]*BugExcerpt
 	// bug loaded in memory
 	bugs map[entity.Id]*BugCache
 
+	muIdentity sync.RWMutex
 	// excerpt of identities data for all identities
 	identitiesExcerpts map[entity.Id]*IdentityExcerpt
 	// identities loaded in memory
@@ -157,6 +160,11 @@ func (c *RepoCache) lock() error {
 }
 
 func (c *RepoCache) Close() error {
+	c.muBug.Lock()
+	defer c.muBug.Unlock()
+	c.muIdentity.Lock()
+	defer c.muIdentity.Unlock()
+
 	c.identities = make(map[entity.Id]*IdentityCache)
 	c.identitiesExcerpts = nil
 	c.bugs = make(map[entity.Id]*BugCache)
@@ -169,12 +177,16 @@ func (c *RepoCache) Close() error {
 // bugUpdated is a callback to trigger when the excerpt of a bug changed,
 // that is each time a bug is updated
 func (c *RepoCache) bugUpdated(id entity.Id) error {
+	c.muBug.Lock()
+
 	b, ok := c.bugs[id]
 	if !ok {
+		c.muBug.Unlock()
 		panic("missing bug in the cache")
 	}
 
 	c.bugExcerpts[id] = NewBugExcerpt(b.bug, b.Snapshot())
+	c.muBug.Unlock()
 
 	// we only need to write the bug cache
 	return c.writeBugCache()
@@ -183,12 +195,16 @@ func (c *RepoCache) bugUpdated(id entity.Id) error {
 // identityUpdated is a callback to trigger when the excerpt of an identity
 // changed, that is each time an identity is updated
 func (c *RepoCache) identityUpdated(id entity.Id) error {
+	c.muIdentity.Lock()
+
 	i, ok := c.identities[id]
 	if !ok {
+		c.muIdentity.Unlock()
 		panic("missing identity in the cache")
 	}
 
 	c.identitiesExcerpts[id] = NewIdentityExcerpt(i.Identity)
+	c.muIdentity.Unlock()
 
 	// we only need to write the identity cache
 	return c.writeIdentityCache()
@@ -205,6 +221,9 @@ func (c *RepoCache) load() error {
 
 // load will try to read from the disk the bug cache file
 func (c *RepoCache) loadBugCache() error {
+	c.muBug.Lock()
+	defer c.muBug.Unlock()
+
 	f, err := os.Open(bugCacheFilePath(c.repo))
 	if err != nil {
 		return err
@@ -234,6 +253,9 @@ func (c *RepoCache) loadBugCache() error {
 
 // load will try to read from the disk the identity cache file
 func (c *RepoCache) loadIdentityCache() error {
+	c.muIdentity.Lock()
+	defer c.muIdentity.Unlock()
+
 	f, err := os.Open(identityCacheFilePath(c.repo))
 	if err != nil {
 		return err
@@ -272,6 +294,9 @@ func (c *RepoCache) write() error {
 
 // write will serialize on disk the bug cache file
 func (c *RepoCache) writeBugCache() error {
+	c.muBug.RLock()
+	defer c.muBug.RUnlock()
+
 	var data bytes.Buffer
 
 	aux := struct {
@@ -304,6 +329,9 @@ func (c *RepoCache) writeBugCache() error {
 
 // write will serialize on disk the identity cache file
 func (c *RepoCache) writeIdentityCache() error {
+	c.muIdentity.RLock()
+	defer c.muIdentity.RUnlock()
+
 	var data bytes.Buffer
 
 	aux := struct {
@@ -343,6 +371,11 @@ func identityCacheFilePath(repo repository.Repo) string {
 }
 
 func (c *RepoCache) buildCache() error {
+	c.muBug.Lock()
+	defer c.muBug.Unlock()
+	c.muIdentity.Lock()
+	defer c.muIdentity.Unlock()
+
 	_, _ = fmt.Fprintf(os.Stderr, "Building identity cache... ")
 
 	c.identitiesExcerpts = make(map[entity.Id]*IdentityExcerpt)
@@ -378,9 +411,24 @@ func (c *RepoCache) buildCache() error {
 	return nil
 }
 
+// ResolveBugExcerpt retrieve a BugExcerpt matching the exact given id
+func (c *RepoCache) ResolveBugExcerpt(id entity.Id) (*BugExcerpt, error) {
+	c.muBug.RLock()
+	defer c.muBug.RUnlock()
+
+	e, ok := c.bugExcerpts[id]
+	if !ok {
+		return nil, bug.ErrBugNotExist
+	}
+
+	return e, nil
+}
+
 // ResolveBug retrieve a bug matching the exact given id
 func (c *RepoCache) ResolveBug(id entity.Id) (*BugCache, error) {
+	c.muBug.RLock()
 	cached, ok := c.bugs[id]
+	c.muBug.RUnlock()
 	if ok {
 		return cached, nil
 	}
@@ -391,19 +439,20 @@ func (c *RepoCache) ResolveBug(id entity.Id) (*BugCache, error) {
 	}
 
 	cached = NewBugCache(c, b)
+
+	c.muBug.Lock()
 	c.bugs[id] = cached
+	c.muBug.Unlock()
 
 	return cached, nil
 }
 
-// ResolveBugExcerpt retrieve a BugExcerpt matching the exact given id
-func (c *RepoCache) ResolveBugExcerpt(id entity.Id) (*BugExcerpt, error) {
-	e, ok := c.bugExcerpts[id]
-	if !ok {
-		return nil, bug.ErrBugNotExist
-	}
-
-	return e, nil
+// ResolveBugExcerptPrefix retrieve a BugExcerpt matching an id prefix. It fails if multiple
+// bugs match.
+func (c *RepoCache) ResolveBugExcerptPrefix(prefix string) (*BugExcerpt, error) {
+	return c.ResolveBugExcerptMatcher(func(excerpt *BugExcerpt) bool {
+		return excerpt.Id.HasPrefix(prefix)
+	})
 }
 
 // ResolveBugPrefix retrieve a bug matching an id prefix. It fails if multiple
@@ -423,7 +472,26 @@ func (c *RepoCache) ResolveBugCreateMetadata(key string, value string) (*BugCach
 	})
 }
 
+func (c *RepoCache) ResolveBugExcerptMatcher(f func(*BugExcerpt) bool) (*BugExcerpt, error) {
+	id, err := c.resolveBugMatcher(f)
+	if err != nil {
+		return nil, err
+	}
+	return c.ResolveBugExcerpt(id)
+}
+
 func (c *RepoCache) ResolveBugMatcher(f func(*BugExcerpt) bool) (*BugCache, error) {
+	id, err := c.resolveBugMatcher(f)
+	if err != nil {
+		return nil, err
+	}
+	return c.ResolveBug(id)
+}
+
+func (c *RepoCache) resolveBugMatcher(f func(*BugExcerpt) bool) (entity.Id, error) {
+	c.muBug.RLock()
+	defer c.muBug.RUnlock()
+
 	// preallocate but empty
 	matching := make([]entity.Id, 0, 5)
 
@@ -434,18 +502,21 @@ func (c *RepoCache) ResolveBugMatcher(f func(*BugExcerpt) bool) (*BugCache, erro
 	}
 
 	if len(matching) > 1 {
-		return nil, bug.NewErrMultipleMatchBug(matching)
+		return entity.UnsetId, bug.NewErrMultipleMatchBug(matching)
 	}
 
 	if len(matching) == 0 {
-		return nil, bug.ErrBugNotExist
+		return entity.UnsetId, bug.ErrBugNotExist
 	}
 
-	return c.ResolveBug(matching[0])
+	return matching[0], nil
 }
 
 // QueryBugs return the id of all Bug matching the given Query
 func (c *RepoCache) QueryBugs(query *Query) []entity.Id {
+	c.muBug.RLock()
+	defer c.muBug.RUnlock()
+
 	if query == nil {
 		return c.AllBugsIds()
 	}
@@ -453,7 +524,7 @@ func (c *RepoCache) QueryBugs(query *Query) []entity.Id {
 	var filtered []*BugExcerpt
 
 	for _, excerpt := range c.bugExcerpts {
-		if query.Match(c, excerpt) {
+		if query.Match(excerpt, c) {
 			filtered = append(filtered, excerpt)
 		}
 	}
@@ -488,6 +559,9 @@ func (c *RepoCache) QueryBugs(query *Query) []entity.Id {
 
 // AllBugsIds return all known bug ids
 func (c *RepoCache) AllBugsIds() []entity.Id {
+	c.muBug.RLock()
+	defer c.muBug.RUnlock()
+
 	result := make([]entity.Id, len(c.bugExcerpts))
 
 	i := 0
@@ -505,6 +579,9 @@ func (c *RepoCache) AllBugsIds() []entity.Id {
 // labels are defined in a configuration file. Until that, the default behavior
 // is to return the list of labels already used.
 func (c *RepoCache) ValidLabels() []bug.Label {
+	c.muBug.RLock()
+	defer c.muBug.RUnlock()
+
 	set := map[bug.Label]interface{}{}
 
 	for _, excerpt := range c.bugExcerpts {
@@ -564,12 +641,15 @@ func (c *RepoCache) NewBugRaw(author *IdentityCache, unixTime int64, title strin
 		return nil, nil, err
 	}
 
+	c.muBug.Lock()
 	if _, has := c.bugs[b.Id()]; has {
+		c.muBug.Unlock()
 		return nil, nil, fmt.Errorf("bug %s already exist in the cache", b.Id())
 	}
 
 	cached := NewBugCache(c, b)
 	c.bugs[b.Id()] = cached
+	c.muBug.Unlock()
 
 	// force the write of the excerpt
 	err = c.bugUpdated(b.Id())
@@ -615,7 +695,9 @@ func (c *RepoCache) MergeAll(remote string) <-chan entity.MergeResult {
 			switch result.Status {
 			case entity.MergeStatusNew, entity.MergeStatusUpdated:
 				i := result.Entity.(*identity.Identity)
+				c.muIdentity.Lock()
 				c.identitiesExcerpts[result.Id] = NewIdentityExcerpt(i)
+				c.muIdentity.Unlock()
 			}
 		}
 
@@ -631,7 +713,9 @@ func (c *RepoCache) MergeAll(remote string) <-chan entity.MergeResult {
 			case entity.MergeStatusNew, entity.MergeStatusUpdated:
 				b := result.Entity.(*bug.Bug)
 				snap := b.Compile()
+				c.muBug.Lock()
 				c.bugExcerpts[result.Id] = NewBugExcerpt(b, &snap)
+				c.muBug.Unlock()
 			}
 		}
 
@@ -745,9 +829,24 @@ func repoIsAvailable(repo repository.Repo) error {
 	return nil
 }
 
+// ResolveIdentityExcerpt retrieve a IdentityExcerpt matching the exact given id
+func (c *RepoCache) ResolveIdentityExcerpt(id entity.Id) (*IdentityExcerpt, error) {
+	c.muIdentity.RLock()
+	defer c.muIdentity.RUnlock()
+
+	e, ok := c.identitiesExcerpts[id]
+	if !ok {
+		return nil, identity.ErrIdentityNotExist
+	}
+
+	return e, nil
+}
+
 // ResolveIdentity retrieve an identity matching the exact given id
 func (c *RepoCache) ResolveIdentity(id entity.Id) (*IdentityCache, error) {
+	c.muIdentity.RLock()
 	cached, ok := c.identities[id]
+	c.muIdentity.RUnlock()
 	if ok {
 		return cached, nil
 	}
@@ -758,19 +857,20 @@ func (c *RepoCache) ResolveIdentity(id entity.Id) (*IdentityCache, error) {
 	}
 
 	cached = NewIdentityCache(c, i)
+
+	c.muIdentity.Lock()
 	c.identities[id] = cached
+	c.muIdentity.Unlock()
 
 	return cached, nil
 }
 
-// ResolveIdentityExcerpt retrieve a IdentityExcerpt matching the exact given id
-func (c *RepoCache) ResolveIdentityExcerpt(id entity.Id) (*IdentityExcerpt, error) {
-	e, ok := c.identitiesExcerpts[id]
-	if !ok {
-		return nil, identity.ErrIdentityNotExist
-	}
-
-	return e, nil
+// ResolveIdentityExcerptPrefix retrieve a IdentityExcerpt matching an id prefix.
+// It fails if multiple identities match.
+func (c *RepoCache) ResolveIdentityExcerptPrefix(prefix string) (*IdentityExcerpt, error) {
+	return c.ResolveIdentityExcerptMatcher(func(excerpt *IdentityExcerpt) bool {
+		return excerpt.Id.HasPrefix(prefix)
+	})
 }
 
 // ResolveIdentityPrefix retrieve an Identity matching an id prefix.
@@ -789,7 +889,26 @@ func (c *RepoCache) ResolveIdentityImmutableMetadata(key string, value string) (
 	})
 }
 
+func (c *RepoCache) ResolveIdentityExcerptMatcher(f func(*IdentityExcerpt) bool) (*IdentityExcerpt, error) {
+	id, err := c.resolveIdentityMatcher(f)
+	if err != nil {
+		return nil, err
+	}
+	return c.ResolveIdentityExcerpt(id)
+}
+
 func (c *RepoCache) ResolveIdentityMatcher(f func(*IdentityExcerpt) bool) (*IdentityCache, error) {
+	id, err := c.resolveIdentityMatcher(f)
+	if err != nil {
+		return nil, err
+	}
+	return c.ResolveIdentity(id)
+}
+
+func (c *RepoCache) resolveIdentityMatcher(f func(*IdentityExcerpt) bool) (entity.Id, error) {
+	c.muIdentity.RLock()
+	defer c.muIdentity.RUnlock()
+
 	// preallocate but empty
 	matching := make([]entity.Id, 0, 5)
 
@@ -800,18 +919,21 @@ func (c *RepoCache) ResolveIdentityMatcher(f func(*IdentityExcerpt) bool) (*Iden
 	}
 
 	if len(matching) > 1 {
-		return nil, identity.NewErrMultipleMatch(matching)
+		return entity.UnsetId, identity.NewErrMultipleMatch(matching)
 	}
 
 	if len(matching) == 0 {
-		return nil, identity.ErrIdentityNotExist
+		return entity.UnsetId, identity.ErrIdentityNotExist
 	}
 
-	return c.ResolveIdentity(matching[0])
+	return matching[0], nil
 }
 
 // AllIdentityIds return all known identity ids
 func (c *RepoCache) AllIdentityIds() []entity.Id {
+	c.muIdentity.RLock()
+	defer c.muIdentity.RUnlock()
+
 	result := make([]entity.Id, len(c.identitiesExcerpts))
 
 	i := 0
@@ -829,6 +951,9 @@ func (c *RepoCache) SetUserIdentity(i *IdentityCache) error {
 		return err
 	}
 
+	c.muIdentity.RLock()
+	defer c.muIdentity.RUnlock()
+
 	// Make sure that everything is fine
 	if _, ok := c.identities[i.Id()]; !ok {
 		panic("SetUserIdentity while the identity is not from the cache, something is wrong")
@@ -847,6 +972,9 @@ func (c *RepoCache) GetUserIdentity() (*IdentityCache, error) {
 		}
 	}
 
+	c.muIdentity.Lock()
+	defer c.muIdentity.Unlock()
+
 	i, err := identity.GetUserIdentity(c.repo)
 	if err != nil {
 		return nil, err
@@ -859,6 +987,25 @@ func (c *RepoCache) GetUserIdentity() (*IdentityCache, error) {
 	return cached, nil
 }
 
+func (c *RepoCache) GetUserIdentityExcerpt() (*IdentityExcerpt, error) {
+	if c.userIdentityId == "" {
+		id, err := identity.GetUserIdentityId(c.repo)
+		if err != nil {
+			return nil, err
+		}
+		c.userIdentityId = id
+	}
+
+	c.muIdentity.RLock()
+	defer c.muIdentity.RUnlock()
+
+	excerpt, ok := c.identitiesExcerpts[c.userIdentityId]
+	if !ok {
+		return nil, fmt.Errorf("cache: missing identity excerpt %v", c.userIdentityId)
+	}
+	return excerpt, nil
+}
+
 func (c *RepoCache) IsUserIdentitySet() (bool, error) {
 	return identity.IsUserIdentitySet(c.repo)
 }
@@ -902,12 +1049,14 @@ func (c *RepoCache) finishIdentity(i *identity.Identity, metadata map[string]str
 		return nil, err
 	}
 
+	c.muIdentity.Lock()
 	if _, has := c.identities[i.Id()]; has {
 		return nil, fmt.Errorf("identity %s already exist in the cache", i.Id())
 	}
 
 	cached := NewIdentityCache(c, i)
 	c.identities[i.Id()] = cached
+	c.muIdentity.Unlock()
 
 	// force the write of the excerpt
 	err = c.identityUpdated(i.Id())
diff --git a/go.mod b/go.mod
index 2c5d5e474e41df922feba130e09bd90dc445e58b..b48d402d567f79b70bb343f330254a962182cbc9 100644
--- a/go.mod
+++ b/go.mod
@@ -3,7 +3,7 @@ module github.com/MichaelMure/git-bug
 go 1.11
 
 require (
-	github.com/99designs/gqlgen v0.10.3-0.20200205113530-b941b970f0b6
+	github.com/99designs/gqlgen v0.10.3-0.20200209012558-b7a58a1c0e4b
 	github.com/MichaelMure/go-term-text v0.2.6
 	github.com/araddon/dateparse v0.0.0-20190622164848-0fb0a474d195
 	github.com/awesome-gocui/gocui v0.6.1-0.20191115151952-a34ffb055986
diff --git a/go.sum b/go.sum
index 8fc1e2909d92d657c69a58787782343a5456c24f..20f4f1873307175427997d08905a0b80682cbf97 100644
--- a/go.sum
+++ b/go.sum
@@ -1,11 +1,11 @@
-github.com/99designs/gqlgen v0.10.3-0.20200205113530-b941b970f0b6 h1:WU+9Z7AoCyl+HlB2kE0O+4/3BaVqLQfnX5dHigV5p8A=
-github.com/99designs/gqlgen v0.10.3-0.20200205113530-b941b970f0b6/go.mod h1:28v/ATDVwPUriwNtAIrQEhRHXJjdi5dVGqxSqPna1I8=
+github.com/99designs/gqlgen v0.10.3-0.20200208093655-ab8d62b67dd0 h1:ADy3XJwhOYg6Pb90XeXazWvO+9gpOsgLuaM1buZUZOY=
+github.com/99designs/gqlgen v0.10.3-0.20200208093655-ab8d62b67dd0/go.mod h1:dfBhwZKMcSYiYRMTs8qWF+Oha6782e1xPfgRmVal9I8=
+github.com/99designs/gqlgen v0.10.3-0.20200209012558-b7a58a1c0e4b h1:510xa84qGbDemwTHNio4cLWkdKFxxJgVtsIOH+Ku8bo=
+github.com/99designs/gqlgen v0.10.3-0.20200209012558-b7a58a1c0e4b/go.mod h1:dfBhwZKMcSYiYRMTs8qWF+Oha6782e1xPfgRmVal9I8=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/Masterminds/glide v0.13.2/go.mod h1:STyF5vcenH/rUqTEv+/hBXlSTo7KYwg2oc2f4tzPWic=
 github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
 github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA=
-github.com/MichaelMure/go-term-text v0.2.4 h1:h+lAsjG5o3oNvaJeh7OzE8zdSiB/VyJo9JzSnncrZv4=
-github.com/MichaelMure/go-term-text v0.2.4/go.mod h1:o2Z5T3b28F4kwAojGvvNdbzjHf9t18vbQ7E2pmTe2Ww=
 github.com/MichaelMure/go-term-text v0.2.6 h1:dSmJSzk2iI5xWymSMrMbdVM1bxYWu3DjDFhdcJvAuqA=
 github.com/MichaelMure/go-term-text v0.2.6/go.mod h1:o2Z5T3b28F4kwAojGvvNdbzjHf9t18vbQ7E2pmTe2Ww=
 github.com/agnivade/levenshtein v1.0.1 h1:3oJU7J3FGFmyhn8KHjmVaZCN5hxTr7GxgRue+sxIXdQ=
@@ -36,8 +36,6 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
 github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
-github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
-github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
 github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
 github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
@@ -69,6 +67,7 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 github.com/matryer/moq v0.0.0-20200106131100-75d0ddfc0007/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
 github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
@@ -132,6 +131,8 @@ github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e/go.mod h1:/HUd
 github.com/vektah/gqlparser v1.2.1 h1:C+L7Go/eUbN0w6Y0kaiq2W6p2wN5j8wU82EdDXxDivc=
 github.com/vektah/gqlparser v1.2.1/go.mod h1:bkVf0FX+Stjg/MHnm8mEyubuaArhNEqfQhF+OTiAL74=
 github.com/vektah/gqlparser v1.3.1 h1:8b0IcD3qZKWJQHSzynbDlrtP3IxVydZ2DZepCGofqfU=
+github.com/vektah/gqlparser v1.3.1 h1:8b0IcD3qZKWJQHSzynbDlrtP3IxVydZ2DZepCGofqfU=
+github.com/vektah/gqlparser v1.3.1/go.mod h1:bkVf0FX+Stjg/MHnm8mEyubuaArhNEqfQhF+OTiAL74=
 github.com/vektah/gqlparser v1.3.1/go.mod h1:bkVf0FX+Stjg/MHnm8mEyubuaArhNEqfQhF+OTiAL74=
 github.com/xanzy/go-gitlab v0.22.1 h1:TVxgHmoa35jQL+9FCkG0nwPDxU9dQZXknBTDtGaSFno=
 github.com/xanzy/go-gitlab v0.22.1/go.mod h1:t4Bmvnxj7k37S4Y17lfLx+nLqkf/oQwT2HagfWKv5Og=
diff --git a/graphql/connections/connections.go b/graphql/connections/connections.go
index 82ad2514944bd2332a51ccf0fa710423f13babc5..0083f8b245f566c0b4bf8bb54b5bc093893f143e 100644
--- a/graphql/connections/connections.go
+++ b/graphql/connections/connections.go
@@ -1,6 +1,6 @@
 //go:generate genny -in=connection_template.go -out=gen_lazy_bug.go gen "Name=LazyBug NodeType=entity.Id EdgeType=LazyBugEdge ConnectionType=models.BugConnection"
 //go:generate genny -in=connection_template.go -out=gen_lazy_identity.go gen "Name=LazyIdentity NodeType=entity.Id EdgeType=LazyIdentityEdge ConnectionType=models.IdentityConnection"
-//go:generate genny -in=connection_template.go -out=gen_identity.go gen "Name=Identity NodeType=identity.Interface EdgeType=models.IdentityEdge ConnectionType=models.IdentityConnection"
+//go:generate genny -in=connection_template.go -out=gen_identity.go gen "Name=Identity NodeType=models.IdentityWrapper EdgeType=models.IdentityEdge ConnectionType=models.IdentityConnection"
 //go:generate genny -in=connection_template.go -out=gen_operation.go gen "Name=Operation NodeType=bug.Operation EdgeType=models.OperationEdge ConnectionType=models.OperationConnection"
 //go:generate genny -in=connection_template.go -out=gen_comment.go gen "Name=Comment NodeType=bug.Comment EdgeType=models.CommentEdge ConnectionType=models.CommentConnection"
 //go:generate genny -in=connection_template.go -out=gen_timeline.go gen "Name=TimelineItem NodeType=bug.TimelineItem EdgeType=models.TimelineItemEdge ConnectionType=models.TimelineItemConnection"
diff --git a/graphql/connections/lazy_identity.go b/graphql/connections/edges.go
similarity index 57%
rename from graphql/connections/lazy_identity.go
rename to graphql/connections/edges.go
index 3274dd7e418524a82a18198fc7380093ac00c24e..4e37fcd9109afba9d512a827fdc4846c51a0b468 100644
--- a/graphql/connections/lazy_identity.go
+++ b/graphql/connections/edges.go
@@ -2,6 +2,17 @@ package connections
 
 import "github.com/MichaelMure/git-bug/entity"
 
+// LazyBugEdge is a special relay edge used to implement a lazy loading connection
+type LazyBugEdge struct {
+	Id     entity.Id
+	Cursor string
+}
+
+// GetCursor return the cursor of a LazyBugEdge
+func (lbe LazyBugEdge) GetCursor() string {
+	return lbe.Cursor
+}
+
 // LazyIdentityEdge is a special relay edge used to implement a lazy loading connection
 type LazyIdentityEdge struct {
 	Id     entity.Id
diff --git a/graphql/connections/gen_identity.go b/graphql/connections/gen_identity.go
index b52b6f96d247095f9fdfa59675629d456975ed23..061e8936f028ce49e504f3f7064cb8824576e967 100644
--- a/graphql/connections/gen_identity.go
+++ b/graphql/connections/gen_identity.go
@@ -8,23 +8,22 @@ import (
 	"fmt"
 
 	"github.com/MichaelMure/git-bug/graphql/models"
-	"github.com/MichaelMure/git-bug/identity"
 )
 
-// IdentityInterfaceEdgeMaker define a function that take a identity.Interface and an offset and
+// ModelsIdentityWrapperEdgeMaker define a function that take a models.IdentityWrapper and an offset and
 // create an Edge.
-type IdentityEdgeMaker func(value identity.Interface, offset int) Edge
+type IdentityEdgeMaker func(value models.IdentityWrapper, offset int) Edge
 
 // IdentityConMaker define a function that create a models.IdentityConnection
 type IdentityConMaker func(
 	edges []*models.IdentityEdge,
-	nodes []identity.Interface,
+	nodes []models.IdentityWrapper,
 	info *models.PageInfo,
 	totalCount int) (*models.IdentityConnection, error)
 
 // IdentityCon will paginate a source according to the input of a relay connection
-func IdentityCon(source []identity.Interface, edgeMaker IdentityEdgeMaker, conMaker IdentityConMaker, input models.ConnectionInput) (*models.IdentityConnection, error) {
-	var nodes []identity.Interface
+func IdentityCon(source []models.IdentityWrapper, edgeMaker IdentityEdgeMaker, conMaker IdentityConMaker, input models.ConnectionInput) (*models.IdentityConnection, error) {
+	var nodes []models.IdentityWrapper
 	var edges []*models.IdentityEdge
 	var cursors []string
 	var pageInfo = &models.PageInfo{}
diff --git a/graphql/connections/lazy_bug.go b/graphql/connections/lazy_bug.go
deleted file mode 100644
index 00692e8b669701dcf32d11329b1a5db5a71a7cc2..0000000000000000000000000000000000000000
--- a/graphql/connections/lazy_bug.go
+++ /dev/null
@@ -1,14 +0,0 @@
-package connections
-
-import "github.com/MichaelMure/git-bug/entity"
-
-// LazyBugEdge is a special relay edge used to implement a lazy loading connection
-type LazyBugEdge struct {
-	Id     entity.Id
-	Cursor string
-}
-
-// GetCursor return the cursor of a LazyBugEdge
-func (lbe LazyBugEdge) GetCursor() string {
-	return lbe.Cursor
-}
diff --git a/graphql/gqlgen.yml b/graphql/gqlgen.yml
index fc27cc2d3c94b38da0e75d82a82e1f5c412e4fb2..fe33b8c742bc5735b68a82c6c049553233270552 100644
--- a/graphql/gqlgen.yml
+++ b/graphql/gqlgen.yml
@@ -10,13 +10,24 @@ models:
   RepositoryMutation:
     model: github.com/MichaelMure/git-bug/graphql/models.RepositoryMutation
   Bug:
-    model: github.com/MichaelMure/git-bug/bug.Snapshot
+    model: github.com/MichaelMure/git-bug/graphql/models.BugWrapper
+    fields:
+      actors:
+        resolver: true
+      participants:
+        resolver: true
+      comments:
+        resolver: true
+      timeline:
+        resolver: true
+      operations:
+        resolver: true
   Color:
     model: image/color.RGBA
   Comment:
     model: github.com/MichaelMure/git-bug/bug.Comment
   Identity:
-    model: github.com/MichaelMure/git-bug/identity.Interface
+    model: github.com/MichaelMure/git-bug/graphql/models.IdentityWrapper
   Label:
     model: github.com/MichaelMure/git-bug/bug.Label
   Hash:
diff --git a/graphql/graph/gen_graph.go b/graphql/graph/gen_graph.go
index 5e882142548ea63ae20a52115715158bbab83aea..679133772871417f2fa0ef0c7d7bfb06fca638f5 100644
--- a/graphql/graph/gen_graph.go
+++ b/graphql/graph/gen_graph.go
@@ -17,7 +17,6 @@ import (
 	"github.com/99designs/gqlgen/graphql/introspection"
 	"github.com/MichaelMure/git-bug/bug"
 	"github.com/MichaelMure/git-bug/graphql/models"
-	"github.com/MichaelMure/git-bug/identity"
 	"github.com/MichaelMure/git-bug/util/git"
 	"github.com/vektah/gqlparser"
 	"github.com/vektah/gqlparser/ast"
@@ -45,6 +44,7 @@ type ResolverRoot interface {
 	AddCommentTimelineItem() AddCommentTimelineItemResolver
 	Bug() BugResolver
 	Color() ColorResolver
+	Comment() CommentResolver
 	CommentHistoryStep() CommentHistoryStepResolver
 	CreateOperation() CreateOperationResolver
 	CreateTimelineItem() CreateTimelineItemResolver
@@ -370,55 +370,59 @@ type ComplexityRoot struct {
 
 type AddCommentOperationResolver interface {
 	ID(ctx context.Context, obj *bug.AddCommentOperation) (string, error)
-
+	Author(ctx context.Context, obj *bug.AddCommentOperation) (models.IdentityWrapper, error)
 	Date(ctx context.Context, obj *bug.AddCommentOperation) (*time.Time, error)
 }
 type AddCommentTimelineItemResolver interface {
 	ID(ctx context.Context, obj *bug.AddCommentTimelineItem) (string, error)
+	Author(ctx context.Context, obj *bug.AddCommentTimelineItem) (models.IdentityWrapper, error)
 
 	CreatedAt(ctx context.Context, obj *bug.AddCommentTimelineItem) (*time.Time, error)
 	LastEdit(ctx context.Context, obj *bug.AddCommentTimelineItem) (*time.Time, error)
 }
 type BugResolver interface {
-	ID(ctx context.Context, obj *bug.Snapshot) (string, error)
-	HumanID(ctx context.Context, obj *bug.Snapshot) (string, error)
-	Status(ctx context.Context, obj *bug.Snapshot) (models.Status, error)
+	ID(ctx context.Context, obj models.BugWrapper) (string, error)
+	HumanID(ctx context.Context, obj models.BugWrapper) (string, error)
+	Status(ctx context.Context, obj models.BugWrapper) (models.Status, error)
 
-	LastEdit(ctx context.Context, obj *bug.Snapshot) (*time.Time, error)
-	Actors(ctx context.Context, obj *bug.Snapshot, after *string, before *string, first *int, last *int) (*models.IdentityConnection, error)
-	Participants(ctx context.Context, obj *bug.Snapshot, after *string, before *string, first *int, last *int) (*models.IdentityConnection, error)
-	Comments(ctx context.Context, obj *bug.Snapshot, after *string, before *string, first *int, last *int) (*models.CommentConnection, error)
-	Timeline(ctx context.Context, obj *bug.Snapshot, after *string, before *string, first *int, last *int) (*models.TimelineItemConnection, error)
-	Operations(ctx context.Context, obj *bug.Snapshot, after *string, before *string, first *int, last *int) (*models.OperationConnection, error)
+	Actors(ctx context.Context, obj models.BugWrapper, after *string, before *string, first *int, last *int) (*models.IdentityConnection, error)
+	Participants(ctx context.Context, obj models.BugWrapper, after *string, before *string, first *int, last *int) (*models.IdentityConnection, error)
+	Comments(ctx context.Context, obj models.BugWrapper, after *string, before *string, first *int, last *int) (*models.CommentConnection, error)
+	Timeline(ctx context.Context, obj models.BugWrapper, after *string, before *string, first *int, last *int) (*models.TimelineItemConnection, error)
+	Operations(ctx context.Context, obj models.BugWrapper, after *string, before *string, first *int, last *int) (*models.OperationConnection, error)
 }
 type ColorResolver interface {
 	R(ctx context.Context, obj *color.RGBA) (int, error)
 	G(ctx context.Context, obj *color.RGBA) (int, error)
 	B(ctx context.Context, obj *color.RGBA) (int, error)
 }
+type CommentResolver interface {
+	Author(ctx context.Context, obj *bug.Comment) (models.IdentityWrapper, error)
+}
 type CommentHistoryStepResolver interface {
 	Date(ctx context.Context, obj *bug.CommentHistoryStep) (*time.Time, error)
 }
 type CreateOperationResolver interface {
 	ID(ctx context.Context, obj *bug.CreateOperation) (string, error)
-
+	Author(ctx context.Context, obj *bug.CreateOperation) (models.IdentityWrapper, error)
 	Date(ctx context.Context, obj *bug.CreateOperation) (*time.Time, error)
 }
 type CreateTimelineItemResolver interface {
 	ID(ctx context.Context, obj *bug.CreateTimelineItem) (string, error)
+	Author(ctx context.Context, obj *bug.CreateTimelineItem) (models.IdentityWrapper, error)
 
 	CreatedAt(ctx context.Context, obj *bug.CreateTimelineItem) (*time.Time, error)
 	LastEdit(ctx context.Context, obj *bug.CreateTimelineItem) (*time.Time, error)
 }
 type EditCommentOperationResolver interface {
 	ID(ctx context.Context, obj *bug.EditCommentOperation) (string, error)
-
+	Author(ctx context.Context, obj *bug.EditCommentOperation) (models.IdentityWrapper, error)
 	Date(ctx context.Context, obj *bug.EditCommentOperation) (*time.Time, error)
 	Target(ctx context.Context, obj *bug.EditCommentOperation) (string, error)
 }
 type IdentityResolver interface {
-	ID(ctx context.Context, obj identity.Interface) (string, error)
-	HumanID(ctx context.Context, obj identity.Interface) (string, error)
+	ID(ctx context.Context, obj models.IdentityWrapper) (string, error)
+	HumanID(ctx context.Context, obj models.IdentityWrapper) (string, error)
 }
 type LabelResolver interface {
 	Name(ctx context.Context, obj *bug.Label) (string, error)
@@ -426,7 +430,7 @@ type LabelResolver interface {
 }
 type LabelChangeOperationResolver interface {
 	ID(ctx context.Context, obj *bug.LabelChangeOperation) (string, error)
-
+	Author(ctx context.Context, obj *bug.LabelChangeOperation) (models.IdentityWrapper, error)
 	Date(ctx context.Context, obj *bug.LabelChangeOperation) (*time.Time, error)
 }
 type LabelChangeResultResolver interface {
@@ -434,7 +438,7 @@ type LabelChangeResultResolver interface {
 }
 type LabelChangeTimelineItemResolver interface {
 	ID(ctx context.Context, obj *bug.LabelChangeTimelineItem) (string, error)
-
+	Author(ctx context.Context, obj *bug.LabelChangeTimelineItem) (models.IdentityWrapper, error)
 	Date(ctx context.Context, obj *bug.LabelChangeTimelineItem) (*time.Time, error)
 }
 type MutationResolver interface {
@@ -453,32 +457,32 @@ type QueryResolver interface {
 }
 type RepositoryResolver interface {
 	AllBugs(ctx context.Context, obj *models.Repository, after *string, before *string, first *int, last *int, query *string) (*models.BugConnection, error)
-	Bug(ctx context.Context, obj *models.Repository, prefix string) (*bug.Snapshot, error)
+	Bug(ctx context.Context, obj *models.Repository, prefix string) (models.BugWrapper, error)
 	AllIdentities(ctx context.Context, obj *models.Repository, after *string, before *string, first *int, last *int) (*models.IdentityConnection, error)
-	Identity(ctx context.Context, obj *models.Repository, prefix string) (identity.Interface, error)
-	UserIdentity(ctx context.Context, obj *models.Repository) (identity.Interface, error)
+	Identity(ctx context.Context, obj *models.Repository, prefix string) (models.IdentityWrapper, error)
+	UserIdentity(ctx context.Context, obj *models.Repository) (models.IdentityWrapper, error)
 	ValidLabels(ctx context.Context, obj *models.Repository, after *string, before *string, first *int, last *int) (*models.LabelConnection, error)
 }
 type SetStatusOperationResolver interface {
 	ID(ctx context.Context, obj *bug.SetStatusOperation) (string, error)
-
+	Author(ctx context.Context, obj *bug.SetStatusOperation) (models.IdentityWrapper, error)
 	Date(ctx context.Context, obj *bug.SetStatusOperation) (*time.Time, error)
 	Status(ctx context.Context, obj *bug.SetStatusOperation) (models.Status, error)
 }
 type SetStatusTimelineItemResolver interface {
 	ID(ctx context.Context, obj *bug.SetStatusTimelineItem) (string, error)
-
+	Author(ctx context.Context, obj *bug.SetStatusTimelineItem) (models.IdentityWrapper, error)
 	Date(ctx context.Context, obj *bug.SetStatusTimelineItem) (*time.Time, error)
 	Status(ctx context.Context, obj *bug.SetStatusTimelineItem) (models.Status, error)
 }
 type SetTitleOperationResolver interface {
 	ID(ctx context.Context, obj *bug.SetTitleOperation) (string, error)
-
+	Author(ctx context.Context, obj *bug.SetTitleOperation) (models.IdentityWrapper, error)
 	Date(ctx context.Context, obj *bug.SetTitleOperation) (*time.Time, error)
 }
 type SetTitleTimelineItemResolver interface {
 	ID(ctx context.Context, obj *bug.SetTitleTimelineItem) (string, error)
-
+	Author(ctx context.Context, obj *bug.SetTitleTimelineItem) (models.IdentityWrapper, error)
 	Date(ctx context.Context, obj *bug.SetTitleTimelineItem) (*time.Time, error)
 }
 
@@ -1873,877 +1877,642 @@ func (ec *executionContext) introspectType(name string) (*introspection.Type, er
 	return introspection.WrapTypeFromDef(parsedSchema, parsedSchema.Types[name]), nil
 }
 
-var parsedSchema = gqlparser.MustLoadSchema(
-	&ast.Source{Name: "schema.graphql", Input: `input AddCommentInput {
-	"""
-	A unique identifier for the client performing the mutation.
-	"""
-	clientMutationId: String
-	"""
-	"The name of the repository. If not set, the default repository is used.
-	"""
-	repoRef: String
-	"""
-	The bug ID's prefix.
-	"""
-	prefix: String!
-	"""
-	The first message of the new bug.
-	"""
-	message: String!
-	"""
-	The collection of file's hash required for the first message.
-	"""
-	files: [Hash!]
+var sources = []*ast.Source{
+	&ast.Source{Name: "schema/bug.graphql", Input: `"""Represents a comment on a bug."""
+type Comment implements Authored {
+  """The author of this comment."""
+  author: Identity!
+
+  """The message of this comment."""
+  message: String!
+
+  """All media's hash referenced in this comment"""
+  files: [Hash!]!
 }
-type AddCommentOperation implements Operation & Authored {
-	"""
-	The identifier of the operation
-	"""
-	id: String!
-	"""
-	The author of this object.
-	"""
-	author: Identity!
-	"""
-	The datetime when this operation was issued.
-	"""
-	date: Time!
-	message: String!
-	files: [Hash!]!
+
+type CommentConnection {
+  edges: [CommentEdge!]!
+  nodes: [Comment!]!
+  pageInfo: PageInfo!
+  totalCount: Int!
 }
-type AddCommentPayload {
-	"""
-	A unique identifier for the client performing the mutation.
-	"""
-	clientMutationId: String
-	"""
-	The affected bug.
-	"""
-	bug: Bug!
-	"""
-	The resulting operation.
-	"""
-	operation: AddCommentOperation!
-}
-"""
-AddCommentTimelineItem is a TimelineItem that represent a Comment and its edition history
-"""
-type AddCommentTimelineItem implements TimelineItem & Authored {
-	"""
-	The identifier of the source operation
-	"""
-	id: String!
-	author: Identity!
-	message: String!
-	messageIsEmpty: Boolean!
-	files: [Hash!]!
-	createdAt: Time!
-	lastEdit: Time!
-	edited: Boolean!
-	history: [CommentHistoryStep!]!
-}
-"""
-An object that has an author.
-"""
-interface Authored {
-	"""
-	The author of this object.
-	"""
-	author: Identity!
+
+type CommentEdge {
+  cursor: String!
+  node: Comment!
 }
+
+enum Status {
+  OPEN
+  CLOSED
+}
+
 type Bug implements Authored {
-	"""
-	The identifier for this bug
-	"""
-	id: String!
-	"""
-	The human version (truncated) identifier for this bug
-	"""
-	humanId: String!
-	status: Status!
-	title: String!
-	labels: [Label!]!
-	author: Identity!
-	createdAt: Time!
-	lastEdit: Time!
-	"""
-	The actors of the bug. Actors are Identity that have interacted with the bug.
-	"""
-	actors("""
-	Returns the elements in the list that come after the specified cursor.
-	"""
-	after: String, """
-	Returns the elements in the list that come before the specified cursor.
-	"""
-	before: String, """
-	Returns the first _n_ elements from the list.
-	"""
-	first: Int, """
-	Returns the last _n_ elements from the list.
-	"""
-	last: Int): IdentityConnection!
-	"""
-	The participants of the bug. Participants are Identity that have created or
-	  added a comment on the bug.
-	"""
-	participants("""
-	Returns the elements in the list that come after the specified cursor.
-	"""
-	after: String, """
-	Returns the elements in the list that come before the specified cursor.
-	"""
-	before: String, """
-	Returns the first _n_ elements from the list.
-	"""
-	first: Int, """
-	Returns the last _n_ elements from the list.
-	"""
-	last: Int): IdentityConnection!
-	comments("""
-	Returns the elements in the list that come after the specified cursor.
-	"""
-	after: String, """
-	Returns the elements in the list that come before the specified cursor.
-	"""
-	before: String, """
-	Returns the first _n_ elements from the list.
-	"""
-	first: Int, """
-	Returns the last _n_ elements from the list.
-	"""
-	last: Int): CommentConnection!
-	timeline("""
-	Returns the elements in the list that come after the specified cursor.
-	"""
-	after: String, """
-	Returns the elements in the list that come before the specified cursor.
-	"""
-	before: String, """
-	Returns the first _n_ elements from the list.
-	"""
-	first: Int, """
-	Returns the last _n_ elements from the list.
-	"""
-	last: Int): TimelineItemConnection!
-	operations("""
-	Returns the elements in the list that come after the specified cursor.
-	"""
-	after: String, """
-	Returns the elements in the list that come before the specified cursor.
-	"""
-	before: String, """
-	Returns the first _n_ elements from the list.
-	"""
-	first: Int, """
-	Returns the last _n_ elements from the list.
-	"""
-	last: Int): OperationConnection!
-}
-"""
-The connection type for Bug.
-"""
+  """The identifier for this bug"""
+  id: String!
+  """The human version (truncated) identifier for this bug"""
+  humanId: String!
+  status: Status!
+  title: String!
+  labels: [Label!]!
+  author: Identity!
+  createdAt: Time!
+  lastEdit: Time!
+
+  """The actors of the bug. Actors are Identity that have interacted with the bug."""
+  actors(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+    """Returns the elements in the list that come before the specified cursor."""
+    before: String
+    """Returns the first _n_ elements from the list."""
+    first: Int
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): IdentityConnection!
+
+  """The participants of the bug. Participants are Identity that have created or
+  added a comment on the bug."""
+  participants(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+    """Returns the elements in the list that come before the specified cursor."""
+    before: String
+    """Returns the first _n_ elements from the list."""
+    first: Int
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): IdentityConnection!
+
+  comments(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+    """Returns the elements in the list that come before the specified cursor."""
+    before: String
+    """Returns the first _n_ elements from the list."""
+    first: Int
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): CommentConnection!
+
+  timeline(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+    """Returns the elements in the list that come before the specified cursor."""
+    before: String
+    """Returns the first _n_ elements from the list."""
+    first: Int
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): TimelineItemConnection!
+
+  operations(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+    """Returns the elements in the list that come before the specified cursor."""
+    before: String
+    """Returns the first _n_ elements from the list."""
+    first: Int
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): OperationConnection!
+}
+
+"""The connection type for Bug."""
 type BugConnection {
-	"""
-	A list of edges.
-	"""
-	edges: [BugEdge!]!
-	nodes: [Bug!]!
-	"""
-	Information to aid in pagination.
-	"""
-	pageInfo: PageInfo!
-	"""
-	Identifies the total count of items in the connection.
-	"""
-	totalCount: Int!
-}
-"""
-An edge in a connection.
-"""
+  """A list of edges."""
+  edges: [BugEdge!]!
+  nodes: [Bug!]!
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
 type BugEdge {
-	"""
-	A cursor for use in pagination.
-	"""
-	cursor: String!
-	"""
-	The item at the end of the edge.
-	"""
-	node: Bug!
+  """A cursor for use in pagination."""
+  cursor: String!
+  """The item at the end of the edge."""
+  node: Bug!
 }
+`, BuiltIn: false},
+	&ast.Source{Name: "schema/identity.graphql", Input: `"""Represents an identity"""
+type Identity {
+    """The identifier for this identity"""
+    id: String!
+    """The human version (truncated) identifier for this identity"""
+    humanId: String!
+    """The name of the person, if known."""
+    name: String
+    """The email of the person, if known."""
+    email: String
+    """A non-empty string to display, representing the identity, based on the non-empty values."""
+    displayName: String!
+    """An url to an avatar"""
+    avatarUrl: String
+    """isProtected is true if the chain of git commits started to be signed.
+    If that's the case, only signed commit with a valid key for this identity can be added."""
+    isProtected: Boolean!
+}
+
+type IdentityConnection {
+    edges: [IdentityEdge!]!
+    nodes: [Identity!]!
+    pageInfo: PageInfo!
+    totalCount: Int!
+}
+
+type IdentityEdge {
+    cursor: String!
+    node: Identity!
+}`, BuiltIn: false},
+	&ast.Source{Name: "schema/label.graphql", Input: `"""Label for a bug."""
+type Label {
+    """The name of the label."""
+    name: String!
+    """Color of the label."""
+    color: Color!
+}
+
+type LabelConnection {
+    edges: [LabelEdge!]!
+    nodes: [Label!]!
+    pageInfo: PageInfo!
+    totalCount: Int!
+}
+
+type LabelEdge {
+    cursor: String!
+    node: Label!
+}`, BuiltIn: false},
+	&ast.Source{Name: "schema/mutations.graphql", Input: `input NewBugInput {
+    """A unique identifier for the client performing the mutation."""
+    clientMutationId: String
+    """"The name of the repository. If not set, the default repository is used."""
+    repoRef: String
+    """The title of the new bug."""
+    title: String!
+    """The first message of the new bug."""
+    message: String!
+    """The collection of file's hash required for the first message."""
+    files: [Hash!]
+}
+
+type NewBugPayload {
+    """A unique identifier for the client performing the mutation."""
+    clientMutationId: String
+    """The created bug."""
+    bug: Bug!
+    """The resulting operation."""
+    operation: CreateOperation!
+}
+
+input AddCommentInput {
+    """A unique identifier for the client performing the mutation."""
+    clientMutationId: String
+    """"The name of the repository. If not set, the default repository is used."""
+    repoRef: String
+    """The bug ID's prefix."""
+    prefix: String!
+    """The first message of the new bug."""
+    message: String!
+    """The collection of file's hash required for the first message."""
+    files: [Hash!]
+}
+
+type AddCommentPayload {
+    """A unique identifier for the client performing the mutation."""
+    clientMutationId: String
+    """The affected bug."""
+    bug: Bug!
+    """The resulting operation."""
+    operation: AddCommentOperation!
+}
+
 input ChangeLabelInput {
-	"""
-	A unique identifier for the client performing the mutation.
-	"""
-	clientMutationId: String
-	"""
-	"The name of the repository. If not set, the default repository is used.
-	"""
-	repoRef: String
-	"""
-	The bug ID's prefix.
-	"""
-	prefix: String!
-	"""
-	The list of label to add.
-	"""
-	added: [String!]
-	"""
-	The list of label to remove.
-	"""
-	Removed: [String!]
+    """A unique identifier for the client performing the mutation."""
+    clientMutationId: String
+    """"The name of the repository. If not set, the default repository is used."""
+    repoRef: String
+    """The bug ID's prefix."""
+    prefix: String!
+    """The list of label to add."""
+    added: [String!]
+    """The list of label to remove."""
+    Removed: [String!]
 }
-type ChangeLabelPayload {
-	"""
-	A unique identifier for the client performing the mutation.
-	"""
-	clientMutationId: String
-	"""
-	The affected bug.
-	"""
-	bug: Bug!
-	"""
-	The resulting operation.
-	"""
-	operation: LabelChangeOperation!
-	"""
-	The effect each source label had.
-	"""
-	results: [LabelChangeResult]!
+
+enum LabelChangeStatus {
+    ADDED
+    REMOVED
+    DUPLICATE_IN_OP
+    ALREADY_EXIST
+    DOESNT_EXIST
 }
-input CloseBugInput {
-	"""
-	A unique identifier for the client performing the mutation.
-	"""
-	clientMutationId: String
-	"""
-	"The name of the repository. If not set, the default repository is used.
-	"""
-	repoRef: String
-	"""
-	The bug ID's prefix.
-	"""
-	prefix: String!
+
+type LabelChangeResult {
+    """The source label."""
+    label: Label!
+    """The effect this label had."""
+    status: LabelChangeStatus!
 }
-type CloseBugPayload {
-	"""
-	A unique identifier for the client performing the mutation.
-	"""
-	clientMutationId: String
-	"""
-	The affected bug.
-	"""
-	bug: Bug!
-	"""
-	The resulting operation.
-	"""
-	operation: SetStatusOperation!
-}
-"""
-Defines a color by red, green and blue components.
-"""
-type Color {
-	"""
-	Red component of the color.
-	"""
-	R: Int!
-	"""
-	Green component of the color.
-	"""
-	G: Int!
-	"""
-	Blue component of the color.
-	"""
-	B: Int!
-}
-"""
-Represents a comment on a bug.
-"""
-type Comment implements Authored {
-	"""
-	The author of this comment.
-	"""
-	author: Identity!
-	"""
-	The message of this comment.
-	"""
-	message: String!
-	"""
-	All media's hash referenced in this comment
-	"""
-	files: [Hash!]!
+
+type ChangeLabelPayload {
+    """A unique identifier for the client performing the mutation."""
+    clientMutationId: String
+    """The affected bug."""
+    bug: Bug!
+    """The resulting operation."""
+    operation: LabelChangeOperation!
+    """The effect each source label had."""
+    results: [LabelChangeResult]!
 }
-type CommentConnection {
-	edges: [CommentEdge!]!
-	nodes: [Comment!]!
-	pageInfo: PageInfo!
-	totalCount: Int!
+
+input OpenBugInput {
+    """A unique identifier for the client performing the mutation."""
+    clientMutationId: String
+    """"The name of the repository. If not set, the default repository is used."""
+    repoRef: String
+    """The bug ID's prefix."""
+    prefix: String!
 }
-type CommentEdge {
-	cursor: String!
-	node: Comment!
+
+type OpenBugPayload {
+    """A unique identifier for the client performing the mutation."""
+    clientMutationId: String
+    """The affected bug."""
+    bug: Bug!
+    """The resulting operation."""
+    operation: SetStatusOperation!
 }
-"""
-CommentHistoryStep hold one version of a message in the history
-"""
-type CommentHistoryStep {
-	message: String!
-	date: Time!
+
+input CloseBugInput {
+    """A unique identifier for the client performing the mutation."""
+    clientMutationId: String
+    """"The name of the repository. If not set, the default repository is used."""
+    repoRef: String
+    """The bug ID's prefix."""
+    prefix: String!
 }
-input CommitAsNeededInput {
-	"""
-	A unique identifier for the client performing the mutation.
-	"""
-	clientMutationId: String
-	"""
-	"The name of the repository. If not set, the default repository is used.
-	"""
-	repoRef: String
-	"""
-	The bug ID's prefix.
-	"""
-	prefix: String!
+
+type CloseBugPayload {
+    """A unique identifier for the client performing the mutation."""
+    clientMutationId: String
+    """The affected bug."""
+    bug: Bug!
+    """The resulting operation."""
+    operation: SetStatusOperation!
 }
-type CommitAsNeededPayload {
-	"""
-	A unique identifier for the client performing the mutation.
-	"""
-	clientMutationId: String
-	"""
-	The affected bug.
-	"""
-	bug: Bug!
+
+input SetTitleInput {
+    """A unique identifier for the client performing the mutation."""
+    clientMutationId: String
+    """"The name of the repository. If not set, the default repository is used."""
+    repoRef: String
+    """The bug ID's prefix."""
+    prefix: String!
+    """The new title."""
+    title: String!
+}
+
+type SetTitlePayload {
+    """A unique identifier for the client performing the mutation."""
+    clientMutationId: String
+    """The affected bug."""
+    bug: Bug!
+    """The resulting operation"""
+    operation: SetTitleOperation!
 }
+
 input CommitInput {
-	"""
-	A unique identifier for the client performing the mutation.
-	"""
-	clientMutationId: String
-	"""
-	"The name of the repository. If not set, the default repository is used.
-	"""
-	repoRef: String
-	"""
-	The bug ID's prefix.
-	"""
-	prefix: String!
+    """A unique identifier for the client performing the mutation."""
+    clientMutationId: String
+    """"The name of the repository. If not set, the default repository is used."""
+    repoRef: String
+    """The bug ID's prefix."""
+    prefix: String!
 }
+
 type CommitPayload {
-	"""
-	A unique identifier for the client performing the mutation.
-	"""
-	clientMutationId: String
-	"""
-	The affected bug.
-	"""
-	bug: Bug!
+    """A unique identifier for the client performing the mutation."""
+    clientMutationId: String
+    """The affected bug."""
+    bug: Bug!
 }
-type CreateOperation implements Operation & Authored {
-	"""
-	The identifier of the operation
-	"""
-	id: String!
-	"""
-	The author of this object.
-	"""
-	author: Identity!
-	"""
-	The datetime when this operation was issued.
-	"""
-	date: Time!
-	title: String!
-	message: String!
-	files: [Hash!]!
-}
-"""
-CreateTimelineItem is a TimelineItem that represent the creation of a bug and its message edition history
-"""
-type CreateTimelineItem implements TimelineItem & Authored {
-	"""
-	The identifier of the source operation
-	"""
-	id: String!
-	author: Identity!
-	message: String!
-	messageIsEmpty: Boolean!
-	files: [Hash!]!
-	createdAt: Time!
-	lastEdit: Time!
-	edited: Boolean!
-	history: [CommentHistoryStep!]!
+
+input CommitAsNeededInput {
+    """A unique identifier for the client performing the mutation."""
+    clientMutationId: String
+    """"The name of the repository. If not set, the default repository is used."""
+    repoRef: String
+    """The bug ID's prefix."""
+    prefix: String!
 }
-type EditCommentOperation implements Operation & Authored {
-	"""
-	The identifier of the operation
-	"""
-	id: String!
-	"""
-	The author of this object.
-	"""
-	author: Identity!
-	"""
-	The datetime when this operation was issued.
-	"""
-	date: Time!
-	target: String!
-	message: String!
-	files: [Hash!]!
+
+type CommitAsNeededPayload {
+    """A unique identifier for the client performing the mutation."""
+    clientMutationId: String
+    """The affected bug."""
+    bug: Bug!
 }
-scalar Hash
-"""
-Represents an identity
-"""
-type Identity {
-	"""
-	The identifier for this identity
-	"""
-	id: String!
-	"""
-	The human version (truncated) identifier for this identity
-	"""
-	humanId: String!
-	"""
-	The name of the person, if known.
-	"""
-	name: String
-	"""
-	The email of the person, if known.
-	"""
-	email: String
-	"""
-	A non-empty string to display, representing the identity, based on the non-empty values.
-	"""
-	displayName: String!
-	"""
-	An url to an avatar
-	"""
-	avatarUrl: String
-	"""
-	isProtected is true if the chain of git commits started to be signed.
-	    If that's the case, only signed commit with a valid key for this identity can be added.
-	"""
-	isProtected: Boolean!
+`, BuiltIn: false},
+	&ast.Source{Name: "schema/operations.graphql", Input: `"""An operation applied to a bug."""
+interface Operation {
+    """The identifier of the operation"""
+    id: String!
+    """The operations author."""
+    author: Identity!
+    """The datetime when this operation was issued."""
+    date: Time!
 }
-type IdentityConnection {
-	edges: [IdentityEdge!]!
-	nodes: [Identity!]!
-	pageInfo: PageInfo!
-	totalCount: Int!
+
+# Connection
+
+"""The connection type for an Operation"""
+type OperationConnection {
+    edges: [OperationEdge!]!
+    nodes: [Operation!]!
+    pageInfo: PageInfo!
+    totalCount: Int!
 }
-type IdentityEdge {
-	cursor: String!
-	node: Identity!
+
+"""Represent an Operation"""
+type OperationEdge {
+    cursor: String!
+    node: Operation!
 }
-"""
-Label for a bug.
-"""
-type Label {
-	"""
-	The name of the label.
-	"""
-	name: String!
-	"""
-	Color of the label.
-	"""
-	color: Color!
+
+# Operations
+
+type CreateOperation implements Operation & Authored {
+    """The identifier of the operation"""
+    id: String!
+    """The author of this object."""
+    author: Identity!
+    """The datetime when this operation was issued."""
+    date: Time!
+
+    title: String!
+    message: String!
+    files: [Hash!]!
 }
-type LabelChangeOperation implements Operation & Authored {
-	"""
-	The identifier of the operation
-	"""
-	id: String!
-	"""
-	The author of this object.
-	"""
-	author: Identity!
-	"""
-	The datetime when this operation was issued.
-	"""
-	date: Time!
-	added: [Label!]!
-	removed: [Label!]!
+
+type SetTitleOperation implements Operation & Authored {
+    """The identifier of the operation"""
+    id: String!
+    """The author of this object."""
+    author: Identity!
+    """The datetime when this operation was issued."""
+    date: Time!
+
+    title: String!
+    was: String!
 }
-type LabelChangeResult {
-	"""
-	The source label.
-	"""
-	label: Label!
-	"""
-	The effect this label had.
-	"""
-	status: LabelChangeStatus!
+
+type AddCommentOperation implements Operation & Authored {
+    """The identifier of the operation"""
+    id: String!
+    """The author of this object."""
+    author: Identity!
+    """The datetime when this operation was issued."""
+    date: Time!
+
+    message: String!
+    files: [Hash!]!
 }
-enum LabelChangeStatus {
-	ADDED
-	REMOVED
-	DUPLICATE_IN_OP
-	ALREADY_EXIST
-	DOESNT_EXIST
-}
-"""
-LabelChangeTimelineItem is a TimelineItem that represent a change in the labels of a bug
-"""
-type LabelChangeTimelineItem implements TimelineItem & Authored {
-	"""
-	The identifier of the source operation
-	"""
-	id: String!
-	author: Identity!
-	date: Time!
-	added: [Label!]!
-	removed: [Label!]!
+
+type EditCommentOperation implements Operation & Authored {
+    """The identifier of the operation"""
+    id: String!
+    """The author of this object."""
+    author: Identity!
+    """The datetime when this operation was issued."""
+    date: Time!
+
+    target: String!
+    message: String!
+    files: [Hash!]!
 }
-type LabelConnection {
-	edges: [LabelEdge!]!
-	nodes: [Label!]!
-	pageInfo: PageInfo!
-	totalCount: Int!
+
+type SetStatusOperation implements Operation & Authored {
+    """The identifier of the operation"""
+    id: String!
+    """The author of this object."""
+    author: Identity!
+    """The datetime when this operation was issued."""
+    date: Time!
+
+    status: Status!
 }
-type LabelEdge {
-	cursor: String!
-	node: Label!
+
+type LabelChangeOperation implements Operation & Authored {
+    """The identifier of the operation"""
+    id: String!
+    """The author of this object."""
+    author: Identity!
+    """The datetime when this operation was issued."""
+    date: Time!
+
+    added: [Label!]!
+    removed: [Label!]!
+}
+`, BuiltIn: false},
+	&ast.Source{Name: "schema/repository.graphql", Input: `
+type Repository {
+    """All the bugs"""
+    allBugs(
+        """Returns the elements in the list that come after the specified cursor."""
+        after: String
+        """Returns the elements in the list that come before the specified cursor."""
+        before: String
+        """Returns the first _n_ elements from the list."""
+        first: Int
+        """Returns the last _n_ elements from the list."""
+        last: Int
+        """A query to select and order bugs"""
+        query: String
+    ): BugConnection!
+
+    bug(prefix: String!): Bug
+
+    """All the identities"""
+    allIdentities(
+        """Returns the elements in the list that come after the specified cursor."""
+        after: String
+        """Returns the elements in the list that come before the specified cursor."""
+        before: String
+        """Returns the first _n_ elements from the list."""
+        first: Int
+        """Returns the last _n_ elements from the list."""
+        last: Int
+    ): IdentityConnection!
+
+    identity(prefix: String!): Identity
+
+    """The identity created or selected by the user as its own"""
+    userIdentity: Identity
+
+    """List of valid labels."""
+    validLabels(
+        """Returns the elements in the list that come after the specified cursor."""
+        after: String
+        """Returns the elements in the list that come before the specified cursor."""
+        before: String
+        """Returns the first _n_ elements from the list."""
+        first: Int
+        """Returns the last _n_ elements from the list."""
+        last: Int
+    ): LabelConnection!
+}`, BuiltIn: false},
+	&ast.Source{Name: "schema/root.graphql", Input: `type Query {
+    """The default unnamend repository."""
+    defaultRepository: Repository
+    """Access a repository by reference/name."""
+    repository(ref: String!): Repository
+
+    #TODO: connection for all repositories
 }
+
 type Mutation {
-	"""
-	Create a new bug
-	"""
-	newBug(input: NewBugInput!): NewBugPayload!
-	"""
-	Add a new comment to a bug
-	"""
-	addComment(input: AddCommentInput!): AddCommentPayload!
-	"""
-	Add or remove a set of label on a bug
-	"""
-	changeLabels(input: ChangeLabelInput): ChangeLabelPayload!
-	"""
-	Change a bug's status to open
-	"""
-	openBug(input: OpenBugInput!): OpenBugPayload!
-	"""
-	Change a bug's status to closed
-	"""
-	closeBug(input: CloseBugInput!): CloseBugPayload!
-	"""
-	Change a bug's title
-	"""
-	setTitle(input: SetTitleInput!): SetTitlePayload!
-	"""
-	Commit write the pending operations into storage. This mutation fail if nothing is pending
-	"""
-	commit(input: CommitInput!): CommitPayload!
-	"""
-	Commit write the pending operations into storage. This mutation succed if nothing is pending
-	"""
-	commitAsNeeded(input: CommitAsNeededInput!): CommitAsNeededPayload!
-}
-input NewBugInput {
-	"""
-	A unique identifier for the client performing the mutation.
-	"""
-	clientMutationId: String
-	"""
-	"The name of the repository. If not set, the default repository is used.
-	"""
-	repoRef: String
-	"""
-	The title of the new bug.
-	"""
-	title: String!
-	"""
-	The first message of the new bug.
-	"""
-	message: String!
-	"""
-	The collection of file's hash required for the first message.
-	"""
-	files: [Hash!]
-}
-type NewBugPayload {
-	"""
-	A unique identifier for the client performing the mutation.
-	"""
-	clientMutationId: String
-	"""
-	The created bug.
-	"""
-	bug: Bug!
-	"""
-	The resulting operation.
-	"""
-	operation: CreateOperation!
+    """Create a new bug"""
+    newBug(input: NewBugInput!): NewBugPayload!
+    """Add a new comment to a bug"""
+    addComment(input: AddCommentInput!): AddCommentPayload!
+    """Add or remove a set of label on a bug"""
+    changeLabels(input: ChangeLabelInput): ChangeLabelPayload!
+    """Change a bug's status to open"""
+    openBug(input: OpenBugInput!): OpenBugPayload!
+    """Change a bug's status to closed"""
+    closeBug(input: CloseBugInput!): CloseBugPayload!
+    """Change a bug's title"""
+    setTitle(input: SetTitleInput!): SetTitlePayload!
+    """Commit write the pending operations into storage. This mutation fail if nothing is pending"""
+    commit(input: CommitInput!): CommitPayload!
+    """Commit write the pending operations into storage. This mutation succed if nothing is pending"""
+    commitAsNeeded(input: CommitAsNeededInput!): CommitAsNeededPayload!
+}
+`, BuiltIn: false},
+	&ast.Source{Name: "schema/timeline.graphql", Input: `"""An item in the timeline of events"""
+interface TimelineItem {
+    """The identifier of the source operation"""
+    id: String!
 }
-input OpenBugInput {
-	"""
-	A unique identifier for the client performing the mutation.
-	"""
-	clientMutationId: String
-	"""
-	"The name of the repository. If not set, the default repository is used.
-	"""
-	repoRef: String
-	"""
-	The bug ID's prefix.
-	"""
-	prefix: String!
+
+"""CommentHistoryStep hold one version of a message in the history"""
+type CommentHistoryStep {
+    message: String!
+    date: Time!
 }
-type OpenBugPayload {
-	"""
-	A unique identifier for the client performing the mutation.
-	"""
-	clientMutationId: String
-	"""
-	The affected bug.
-	"""
-	bug: Bug!
-	"""
-	The resulting operation.
-	"""
-	operation: SetStatusOperation!
-}
-"""
-An operation applied to a bug.
-"""
-interface Operation {
-	"""
-	The identifier of the operation
-	"""
-	id: String!
-	"""
-	The operations author.
-	"""
-	author: Identity!
-	"""
-	The datetime when this operation was issued.
-	"""
-	date: Time!
-}
-"""
-The connection type for an Operation
-"""
-type OperationConnection {
-	edges: [OperationEdge!]!
-	nodes: [Operation!]!
-	pageInfo: PageInfo!
-	totalCount: Int!
-}
-"""
-Represent an Operation
-"""
-type OperationEdge {
-	cursor: String!
-	node: Operation!
+
+# Connection
+
+"""The connection type for TimelineItem"""
+type TimelineItemConnection {
+    edges: [TimelineItemEdge!]!
+    nodes: [TimelineItem!]!
+    pageInfo: PageInfo!
+    totalCount: Int!
 }
-"""
-Information about pagination in a connection.
-"""
-type PageInfo {
-	"""
-	When paginating forwards, are there more items?
-	"""
-	hasNextPage: Boolean!
-	"""
-	When paginating backwards, are there more items?
-	"""
-	hasPreviousPage: Boolean!
-	"""
-	When paginating backwards, the cursor to continue.
-	"""
-	startCursor: String!
-	"""
-	When paginating forwards, the cursor to continue.
-	"""
-	endCursor: String!
-}
-type Query {
-	"""
-	The default unnamend repository.
-	"""
-	defaultRepository: Repository
-	"""
-	Access a repository by reference/name.
-	"""
-	repository(ref: String!): Repository
+
+"""Represent a TimelineItem"""
+type TimelineItemEdge {
+    cursor: String!
+    node: TimelineItem!
 }
-type Repository {
-	"""
-	All the bugs
-	"""
-	allBugs("""
-	Returns the elements in the list that come after the specified cursor.
-	"""
-	after: String, """
-	Returns the elements in the list that come before the specified cursor.
-	"""
-	before: String, """
-	Returns the first _n_ elements from the list.
-	"""
-	first: Int, """
-	Returns the last _n_ elements from the list.
-	"""
-	last: Int, """
-	A query to select and order bugs
-	"""
-	query: String): BugConnection!
-	bug(prefix: String!): Bug
-	"""
-	All the identities
-	"""
-	allIdentities("""
-	Returns the elements in the list that come after the specified cursor.
-	"""
-	after: String, """
-	Returns the elements in the list that come before the specified cursor.
-	"""
-	before: String, """
-	Returns the first _n_ elements from the list.
-	"""
-	first: Int, """
-	Returns the last _n_ elements from the list.
-	"""
-	last: Int): IdentityConnection!
-	identity(prefix: String!): Identity
-	"""
-	The identity created or selected by the user as its own
-	"""
-	userIdentity: Identity
-	"""
-	List of valid labels.
-	"""
-	validLabels("""
-	Returns the elements in the list that come after the specified cursor.
-	"""
-	after: String, """
-	Returns the elements in the list that come before the specified cursor.
-	"""
-	before: String, """
-	Returns the first _n_ elements from the list.
-	"""
-	first: Int, """
-	Returns the last _n_ elements from the list.
-	"""
-	last: Int): LabelConnection!
+
+# Items
+
+"""CreateTimelineItem is a TimelineItem that represent the creation of a bug and its message edition history"""
+type CreateTimelineItem implements TimelineItem & Authored {
+    """The identifier of the source operation"""
+    id: String!
+    author: Identity!
+    message: String!
+    messageIsEmpty: Boolean!
+    files: [Hash!]!
+    createdAt: Time!
+    lastEdit: Time!
+    edited: Boolean!
+    history: [CommentHistoryStep!]!
+}
+
+"""AddCommentTimelineItem is a TimelineItem that represent a Comment and its edition history"""
+type AddCommentTimelineItem implements TimelineItem & Authored {
+    """The identifier of the source operation"""
+    id: String!
+    author: Identity!
+    message: String!
+    messageIsEmpty: Boolean!
+    files: [Hash!]!
+    createdAt: Time!
+    lastEdit: Time!
+    edited: Boolean!
+    history: [CommentHistoryStep!]!
+}
+
+"""LabelChangeTimelineItem is a TimelineItem that represent a change in the labels of a bug"""
+type LabelChangeTimelineItem implements TimelineItem & Authored {
+    """The identifier of the source operation"""
+    id: String!
+    author: Identity!
+    date: Time!
+    added: [Label!]!
+    removed: [Label!]!
 }
-type SetStatusOperation implements Operation & Authored {
-	"""
-	The identifier of the operation
-	"""
-	id: String!
-	"""
-	The author of this object.
-	"""
-	author: Identity!
-	"""
-	The datetime when this operation was issued.
-	"""
-	date: Time!
-	status: Status!
-}
-"""
-SetStatusTimelineItem is a TimelineItem that represent a change in the status of a bug
-"""
+
+"""SetStatusTimelineItem is a TimelineItem that represent a change in the status of a bug"""
 type SetStatusTimelineItem implements TimelineItem & Authored {
-	"""
-	The identifier of the source operation
-	"""
-	id: String!
-	author: Identity!
-	date: Time!
-	status: Status!
-}
-input SetTitleInput {
-	"""
-	A unique identifier for the client performing the mutation.
-	"""
-	clientMutationId: String
-	"""
-	"The name of the repository. If not set, the default repository is used.
-	"""
-	repoRef: String
-	"""
-	The bug ID's prefix.
-	"""
-	prefix: String!
-	"""
-	The new title.
-	"""
-	title: String!
+    """The identifier of the source operation"""
+    id: String!
+    author: Identity!
+    date: Time!
+    status: Status!
 }
-type SetTitleOperation implements Operation & Authored {
-	"""
-	The identifier of the operation
-	"""
-	id: String!
-	"""
-	The author of this object.
-	"""
-	author: Identity!
-	"""
-	The datetime when this operation was issued.
-	"""
-	date: Time!
-	title: String!
-	was: String!
-}
-type SetTitlePayload {
-	"""
-	A unique identifier for the client performing the mutation.
-	"""
-	clientMutationId: String
-	"""
-	The affected bug.
-	"""
-	bug: Bug!
-	"""
-	The resulting operation
-	"""
-	operation: SetTitleOperation!
-}
-"""
-LabelChangeTimelineItem is a TimelineItem that represent a change in the title of a bug
-"""
+
+"""LabelChangeTimelineItem is a TimelineItem that represent a change in the title of a bug"""
 type SetTitleTimelineItem implements TimelineItem & Authored {
-	"""
-	The identifier of the source operation
-	"""
-	id: String!
-	author: Identity!
-	date: Time!
-	title: String!
-	was: String!
+    """The identifier of the source operation"""
+    id: String!
+    author: Identity!
+    date: Time!
+    title: String!
+    was: String!
+}
+`, BuiltIn: false},
+	&ast.Source{Name: "schema/types.graphql", Input: `scalar Time
+scalar Hash
+
+"""Defines a color by red, green and blue components."""
+type Color {
+    """Red component of the color."""
+    R: Int!
+    """Green component of the color."""
+    G: Int!
+    """Blue component of the color."""
+    B: Int!
 }
-enum Status {
-	OPEN
-	CLOSED
+
+"""Information about pagination in a connection."""
+type PageInfo {
+    """When paginating forwards, are there more items?"""
+    hasNextPage: Boolean!
+    """When paginating backwards, are there more items?"""
+    hasPreviousPage: Boolean!
+    """When paginating backwards, the cursor to continue."""
+    startCursor: String!
+    """When paginating forwards, the cursor to continue."""
+    endCursor: String!
 }
-scalar Time
-"""
-An item in the timeline of events
-"""
-interface TimelineItem {
-	"""
-	The identifier of the source operation
-	"""
-	id: String!
-}
-"""
-The connection type for TimelineItem
-"""
-type TimelineItemConnection {
-	edges: [TimelineItemEdge!]!
-	nodes: [TimelineItem!]!
-	pageInfo: PageInfo!
-	totalCount: Int!
-}
-"""
-Represent a TimelineItem
-"""
-type TimelineItemEdge {
-	cursor: String!
-	node: TimelineItem!
+
+"""An object that has an author."""
+interface Authored {
+    """The author of this object."""
+    author: Identity!
 }
-`},
-)
+`, BuiltIn: false},
+}
+var parsedSchema = gqlparser.MustLoadSchema(sources...)
 
 // endregion ************************** generated!.gotpl **************************
 
@@ -3310,13 +3079,13 @@ func (ec *executionContext) _AddCommentOperation_author(ctx context.Context, fie
 		Object:   "AddCommentOperation",
 		Field:    field,
 		Args:     nil,
-		IsMethod: false,
+		IsMethod: true,
 	}
 
 	ctx = graphql.WithFieldContext(ctx, fc)
 	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
 		ctx = rctx // use context from middleware stack in children
-		return obj.Author, nil
+		return ec.resolvers.AddCommentOperation().Author(rctx, obj)
 	})
 	if err != nil {
 		ec.Error(ctx, err)
@@ -3328,9 +3097,9 @@ func (ec *executionContext) _AddCommentOperation_author(ctx context.Context, fie
 		}
 		return graphql.Null
 	}
-	res := resTmp.(identity.Interface)
+	res := resTmp.(models.IdentityWrapper)
 	fc.Result = res
-	return ec.marshalNIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋidentityᚐInterface(ctx, field.Selections, res)
+	return ec.marshalNIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐIdentityWrapper(ctx, field.Selections, res)
 }
 
 func (ec *executionContext) _AddCommentOperation_date(ctx context.Context, field graphql.CollectedField, obj *bug.AddCommentOperation) (ret graphql.Marshaler) {
@@ -3495,9 +3264,9 @@ func (ec *executionContext) _AddCommentPayload_bug(ctx context.Context, field gr
 		}
 		return graphql.Null
 	}
-	res := resTmp.(*bug.Snapshot)
+	res := resTmp.(models.BugWrapper)
 	fc.Result = res
-	return ec.marshalNBug2ᚖgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋbugᚐSnapshot(ctx, field.Selections, res)
+	return ec.marshalNBug2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐBugWrapper(ctx, field.Selections, res)
 }
 
 func (ec *executionContext) _AddCommentPayload_operation(ctx context.Context, field graphql.CollectedField, obj *models.AddCommentPayload) (ret graphql.Marshaler) {
@@ -3579,13 +3348,13 @@ func (ec *executionContext) _AddCommentTimelineItem_author(ctx context.Context,
 		Object:   "AddCommentTimelineItem",
 		Field:    field,
 		Args:     nil,
-		IsMethod: false,
+		IsMethod: true,
 	}
 
 	ctx = graphql.WithFieldContext(ctx, fc)
 	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
 		ctx = rctx // use context from middleware stack in children
-		return obj.Author, nil
+		return ec.resolvers.AddCommentTimelineItem().Author(rctx, obj)
 	})
 	if err != nil {
 		ec.Error(ctx, err)
@@ -3597,9 +3366,9 @@ func (ec *executionContext) _AddCommentTimelineItem_author(ctx context.Context,
 		}
 		return graphql.Null
 	}
-	res := resTmp.(identity.Interface)
+	res := resTmp.(models.IdentityWrapper)
 	fc.Result = res
-	return ec.marshalNIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋidentityᚐInterface(ctx, field.Selections, res)
+	return ec.marshalNIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐIdentityWrapper(ctx, field.Selections, res)
 }
 
 func (ec *executionContext) _AddCommentTimelineItem_message(ctx context.Context, field graphql.CollectedField, obj *bug.AddCommentTimelineItem) (ret graphql.Marshaler) {
@@ -3840,7 +3609,7 @@ func (ec *executionContext) _AddCommentTimelineItem_history(ctx context.Context,
 	return ec.marshalNCommentHistoryStep2ᚕgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋbugᚐCommentHistoryStepᚄ(ctx, field.Selections, res)
 }
 
-func (ec *executionContext) _Bug_id(ctx context.Context, field graphql.CollectedField, obj *bug.Snapshot) (ret graphql.Marshaler) {
+func (ec *executionContext) _Bug_id(ctx context.Context, field graphql.CollectedField, obj models.BugWrapper) (ret graphql.Marshaler) {
 	defer func() {
 		if r := recover(); r != nil {
 			ec.Error(ctx, ec.Recover(ctx, r))
@@ -3874,7 +3643,7 @@ func (ec *executionContext) _Bug_id(ctx context.Context, field graphql.Collected
 	return ec.marshalNString2string(ctx, field.Selections, res)
 }
 
-func (ec *executionContext) _Bug_humanId(ctx context.Context, field graphql.CollectedField, obj *bug.Snapshot) (ret graphql.Marshaler) {
+func (ec *executionContext) _Bug_humanId(ctx context.Context, field graphql.CollectedField, obj models.BugWrapper) (ret graphql.Marshaler) {
 	defer func() {
 		if r := recover(); r != nil {
 			ec.Error(ctx, ec.Recover(ctx, r))
@@ -3908,7 +3677,7 @@ func (ec *executionContext) _Bug_humanId(ctx context.Context, field graphql.Coll
 	return ec.marshalNString2string(ctx, field.Selections, res)
 }
 
-func (ec *executionContext) _Bug_status(ctx context.Context, field graphql.CollectedField, obj *bug.Snapshot) (ret graphql.Marshaler) {
+func (ec *executionContext) _Bug_status(ctx context.Context, field graphql.CollectedField, obj models.BugWrapper) (ret graphql.Marshaler) {
 	defer func() {
 		if r := recover(); r != nil {
 			ec.Error(ctx, ec.Recover(ctx, r))
@@ -3942,7 +3711,7 @@ func (ec *executionContext) _Bug_status(ctx context.Context, field graphql.Colle
 	return ec.marshalNStatus2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐStatus(ctx, field.Selections, res)
 }
 
-func (ec *executionContext) _Bug_title(ctx context.Context, field graphql.CollectedField, obj *bug.Snapshot) (ret graphql.Marshaler) {
+func (ec *executionContext) _Bug_title(ctx context.Context, field graphql.CollectedField, obj models.BugWrapper) (ret graphql.Marshaler) {
 	defer func() {
 		if r := recover(); r != nil {
 			ec.Error(ctx, ec.Recover(ctx, r))
@@ -3953,13 +3722,13 @@ func (ec *executionContext) _Bug_title(ctx context.Context, field graphql.Collec
 		Object:   "Bug",
 		Field:    field,
 		Args:     nil,
-		IsMethod: false,
+		IsMethod: true,
 	}
 
 	ctx = graphql.WithFieldContext(ctx, fc)
 	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
 		ctx = rctx // use context from middleware stack in children
-		return obj.Title, nil
+		return obj.Title(), nil
 	})
 	if err != nil {
 		ec.Error(ctx, err)
@@ -3976,7 +3745,7 @@ func (ec *executionContext) _Bug_title(ctx context.Context, field graphql.Collec
 	return ec.marshalNString2string(ctx, field.Selections, res)
 }
 
-func (ec *executionContext) _Bug_labels(ctx context.Context, field graphql.CollectedField, obj *bug.Snapshot) (ret graphql.Marshaler) {
+func (ec *executionContext) _Bug_labels(ctx context.Context, field graphql.CollectedField, obj models.BugWrapper) (ret graphql.Marshaler) {
 	defer func() {
 		if r := recover(); r != nil {
 			ec.Error(ctx, ec.Recover(ctx, r))
@@ -3987,13 +3756,13 @@ func (ec *executionContext) _Bug_labels(ctx context.Context, field graphql.Colle
 		Object:   "Bug",
 		Field:    field,
 		Args:     nil,
-		IsMethod: false,
+		IsMethod: true,
 	}
 
 	ctx = graphql.WithFieldContext(ctx, fc)
 	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
 		ctx = rctx // use context from middleware stack in children
-		return obj.Labels, nil
+		return obj.Labels(), nil
 	})
 	if err != nil {
 		ec.Error(ctx, err)
@@ -4010,7 +3779,7 @@ func (ec *executionContext) _Bug_labels(ctx context.Context, field graphql.Colle
 	return ec.marshalNLabel2ᚕgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋbugᚐLabelᚄ(ctx, field.Selections, res)
 }
 
-func (ec *executionContext) _Bug_author(ctx context.Context, field graphql.CollectedField, obj *bug.Snapshot) (ret graphql.Marshaler) {
+func (ec *executionContext) _Bug_author(ctx context.Context, field graphql.CollectedField, obj models.BugWrapper) (ret graphql.Marshaler) {
 	defer func() {
 		if r := recover(); r != nil {
 			ec.Error(ctx, ec.Recover(ctx, r))
@@ -4021,13 +3790,13 @@ func (ec *executionContext) _Bug_author(ctx context.Context, field graphql.Colle
 		Object:   "Bug",
 		Field:    field,
 		Args:     nil,
-		IsMethod: false,
+		IsMethod: true,
 	}
 
 	ctx = graphql.WithFieldContext(ctx, fc)
 	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
 		ctx = rctx // use context from middleware stack in children
-		return obj.Author, nil
+		return obj.Author()
 	})
 	if err != nil {
 		ec.Error(ctx, err)
@@ -4039,12 +3808,12 @@ func (ec *executionContext) _Bug_author(ctx context.Context, field graphql.Colle
 		}
 		return graphql.Null
 	}
-	res := resTmp.(identity.Interface)
+	res := resTmp.(models.IdentityWrapper)
 	fc.Result = res
-	return ec.marshalNIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋidentityᚐInterface(ctx, field.Selections, res)
+	return ec.marshalNIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐIdentityWrapper(ctx, field.Selections, res)
 }
 
-func (ec *executionContext) _Bug_createdAt(ctx context.Context, field graphql.CollectedField, obj *bug.Snapshot) (ret graphql.Marshaler) {
+func (ec *executionContext) _Bug_createdAt(ctx context.Context, field graphql.CollectedField, obj models.BugWrapper) (ret graphql.Marshaler) {
 	defer func() {
 		if r := recover(); r != nil {
 			ec.Error(ctx, ec.Recover(ctx, r))
@@ -4055,13 +3824,13 @@ func (ec *executionContext) _Bug_createdAt(ctx context.Context, field graphql.Co
 		Object:   "Bug",
 		Field:    field,
 		Args:     nil,
-		IsMethod: false,
+		IsMethod: true,
 	}
 
 	ctx = graphql.WithFieldContext(ctx, fc)
 	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
 		ctx = rctx // use context from middleware stack in children
-		return obj.CreatedAt, nil
+		return obj.CreatedAt(), nil
 	})
 	if err != nil {
 		ec.Error(ctx, err)
@@ -4078,7 +3847,7 @@ func (ec *executionContext) _Bug_createdAt(ctx context.Context, field graphql.Co
 	return ec.marshalNTime2timeᚐTime(ctx, field.Selections, res)
 }
 
-func (ec *executionContext) _Bug_lastEdit(ctx context.Context, field graphql.CollectedField, obj *bug.Snapshot) (ret graphql.Marshaler) {
+func (ec *executionContext) _Bug_lastEdit(ctx context.Context, field graphql.CollectedField, obj models.BugWrapper) (ret graphql.Marshaler) {
 	defer func() {
 		if r := recover(); r != nil {
 			ec.Error(ctx, ec.Recover(ctx, r))
@@ -4095,7 +3864,7 @@ func (ec *executionContext) _Bug_lastEdit(ctx context.Context, field graphql.Col
 	ctx = graphql.WithFieldContext(ctx, fc)
 	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
 		ctx = rctx // use context from middleware stack in children
-		return ec.resolvers.Bug().LastEdit(rctx, obj)
+		return obj.LastEdit(), nil
 	})
 	if err != nil {
 		ec.Error(ctx, err)
@@ -4107,12 +3876,12 @@ func (ec *executionContext) _Bug_lastEdit(ctx context.Context, field graphql.Col
 		}
 		return graphql.Null
 	}
-	res := resTmp.(*time.Time)
+	res := resTmp.(time.Time)
 	fc.Result = res
-	return ec.marshalNTime2ᚖtimeᚐTime(ctx, field.Selections, res)
+	return ec.marshalNTime2timeᚐTime(ctx, field.Selections, res)
 }
 
-func (ec *executionContext) _Bug_actors(ctx context.Context, field graphql.CollectedField, obj *bug.Snapshot) (ret graphql.Marshaler) {
+func (ec *executionContext) _Bug_actors(ctx context.Context, field graphql.CollectedField, obj models.BugWrapper) (ret graphql.Marshaler) {
 	defer func() {
 		if r := recover(); r != nil {
 			ec.Error(ctx, ec.Recover(ctx, r))
@@ -4153,7 +3922,7 @@ func (ec *executionContext) _Bug_actors(ctx context.Context, field graphql.Colle
 	return ec.marshalNIdentityConnection2ᚖgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐIdentityConnection(ctx, field.Selections, res)
 }
 
-func (ec *executionContext) _Bug_participants(ctx context.Context, field graphql.CollectedField, obj *bug.Snapshot) (ret graphql.Marshaler) {
+func (ec *executionContext) _Bug_participants(ctx context.Context, field graphql.CollectedField, obj models.BugWrapper) (ret graphql.Marshaler) {
 	defer func() {
 		if r := recover(); r != nil {
 			ec.Error(ctx, ec.Recover(ctx, r))
@@ -4194,7 +3963,7 @@ func (ec *executionContext) _Bug_participants(ctx context.Context, field graphql
 	return ec.marshalNIdentityConnection2ᚖgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐIdentityConnection(ctx, field.Selections, res)
 }
 
-func (ec *executionContext) _Bug_comments(ctx context.Context, field graphql.CollectedField, obj *bug.Snapshot) (ret graphql.Marshaler) {
+func (ec *executionContext) _Bug_comments(ctx context.Context, field graphql.CollectedField, obj models.BugWrapper) (ret graphql.Marshaler) {
 	defer func() {
 		if r := recover(); r != nil {
 			ec.Error(ctx, ec.Recover(ctx, r))
@@ -4235,7 +4004,7 @@ func (ec *executionContext) _Bug_comments(ctx context.Context, field graphql.Col
 	return ec.marshalNCommentConnection2ᚖgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐCommentConnection(ctx, field.Selections, res)
 }
 
-func (ec *executionContext) _Bug_timeline(ctx context.Context, field graphql.CollectedField, obj *bug.Snapshot) (ret graphql.Marshaler) {
+func (ec *executionContext) _Bug_timeline(ctx context.Context, field graphql.CollectedField, obj models.BugWrapper) (ret graphql.Marshaler) {
 	defer func() {
 		if r := recover(); r != nil {
 			ec.Error(ctx, ec.Recover(ctx, r))
@@ -4276,7 +4045,7 @@ func (ec *executionContext) _Bug_timeline(ctx context.Context, field graphql.Col
 	return ec.marshalNTimelineItemConnection2ᚖgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐTimelineItemConnection(ctx, field.Selections, res)
 }
 
-func (ec *executionContext) _Bug_operations(ctx context.Context, field graphql.CollectedField, obj *bug.Snapshot) (ret graphql.Marshaler) {
+func (ec *executionContext) _Bug_operations(ctx context.Context, field graphql.CollectedField, obj models.BugWrapper) (ret graphql.Marshaler) {
 	defer func() {
 		if r := recover(); r != nil {
 			ec.Error(ctx, ec.Recover(ctx, r))
@@ -4380,9 +4149,9 @@ func (ec *executionContext) _BugConnection_nodes(ctx context.Context, field grap
 		}
 		return graphql.Null
 	}
-	res := resTmp.([]*bug.Snapshot)
+	res := resTmp.([]models.BugWrapper)
 	fc.Result = res
-	return ec.marshalNBug2ᚕᚖgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋbugᚐSnapshotᚄ(ctx, field.Selections, res)
+	return ec.marshalNBug2ᚕgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐBugWrapperᚄ(ctx, field.Selections, res)
 }
 
 func (ec *executionContext) _BugConnection_pageInfo(ctx context.Context, field graphql.CollectedField, obj *models.BugConnection) (ret graphql.Marshaler) {
@@ -4516,9 +4285,9 @@ func (ec *executionContext) _BugEdge_node(ctx context.Context, field graphql.Col
 		}
 		return graphql.Null
 	}
-	res := resTmp.(*bug.Snapshot)
+	res := resTmp.(models.BugWrapper)
 	fc.Result = res
-	return ec.marshalNBug2ᚖgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋbugᚐSnapshot(ctx, field.Selections, res)
+	return ec.marshalNBug2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐBugWrapper(ctx, field.Selections, res)
 }
 
 func (ec *executionContext) _ChangeLabelPayload_clientMutationId(ctx context.Context, field graphql.CollectedField, obj *models.ChangeLabelPayload) (ret graphql.Marshaler) {
@@ -4581,9 +4350,9 @@ func (ec *executionContext) _ChangeLabelPayload_bug(ctx context.Context, field g
 		}
 		return graphql.Null
 	}
-	res := resTmp.(*bug.Snapshot)
+	res := resTmp.(models.BugWrapper)
 	fc.Result = res
-	return ec.marshalNBug2ᚖgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋbugᚐSnapshot(ctx, field.Selections, res)
+	return ec.marshalNBug2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐBugWrapper(ctx, field.Selections, res)
 }
 
 func (ec *executionContext) _ChangeLabelPayload_operation(ctx context.Context, field graphql.CollectedField, obj *models.ChangeLabelPayload) (ret graphql.Marshaler) {
@@ -4714,9 +4483,9 @@ func (ec *executionContext) _CloseBugPayload_bug(ctx context.Context, field grap
 		}
 		return graphql.Null
 	}
-	res := resTmp.(*bug.Snapshot)
+	res := resTmp.(models.BugWrapper)
 	fc.Result = res
-	return ec.marshalNBug2ᚖgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋbugᚐSnapshot(ctx, field.Selections, res)
+	return ec.marshalNBug2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐBugWrapper(ctx, field.Selections, res)
 }
 
 func (ec *executionContext) _CloseBugPayload_operation(ctx context.Context, field graphql.CollectedField, obj *models.CloseBugPayload) (ret graphql.Marshaler) {
@@ -4866,13 +4635,13 @@ func (ec *executionContext) _Comment_author(ctx context.Context, field graphql.C
 		Object:   "Comment",
 		Field:    field,
 		Args:     nil,
-		IsMethod: false,
+		IsMethod: true,
 	}
 
 	ctx = graphql.WithFieldContext(ctx, fc)
 	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
 		ctx = rctx // use context from middleware stack in children
-		return obj.Author, nil
+		return ec.resolvers.Comment().Author(rctx, obj)
 	})
 	if err != nil {
 		ec.Error(ctx, err)
@@ -4884,9 +4653,9 @@ func (ec *executionContext) _Comment_author(ctx context.Context, field graphql.C
 		}
 		return graphql.Null
 	}
-	res := resTmp.(identity.Interface)
+	res := resTmp.(models.IdentityWrapper)
 	fc.Result = res
-	return ec.marshalNIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋidentityᚐInterface(ctx, field.Selections, res)
+	return ec.marshalNIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐIdentityWrapper(ctx, field.Selections, res)
 }
 
 func (ec *executionContext) _Comment_message(ctx context.Context, field graphql.CollectedField, obj *bug.Comment) (ret graphql.Marshaler) {
@@ -5289,9 +5058,9 @@ func (ec *executionContext) _CommitAsNeededPayload_bug(ctx context.Context, fiel
 		}
 		return graphql.Null
 	}
-	res := resTmp.(*bug.Snapshot)
+	res := resTmp.(models.BugWrapper)
 	fc.Result = res
-	return ec.marshalNBug2ᚖgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋbugᚐSnapshot(ctx, field.Selections, res)
+	return ec.marshalNBug2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐBugWrapper(ctx, field.Selections, res)
 }
 
 func (ec *executionContext) _CommitPayload_clientMutationId(ctx context.Context, field graphql.CollectedField, obj *models.CommitPayload) (ret graphql.Marshaler) {
@@ -5354,9 +5123,9 @@ func (ec *executionContext) _CommitPayload_bug(ctx context.Context, field graphq
 		}
 		return graphql.Null
 	}
-	res := resTmp.(*bug.Snapshot)
+	res := resTmp.(models.BugWrapper)
 	fc.Result = res
-	return ec.marshalNBug2ᚖgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋbugᚐSnapshot(ctx, field.Selections, res)
+	return ec.marshalNBug2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐBugWrapper(ctx, field.Selections, res)
 }
 
 func (ec *executionContext) _CreateOperation_id(ctx context.Context, field graphql.CollectedField, obj *bug.CreateOperation) (ret graphql.Marshaler) {
@@ -5404,13 +5173,13 @@ func (ec *executionContext) _CreateOperation_author(ctx context.Context, field g
 		Object:   "CreateOperation",
 		Field:    field,
 		Args:     nil,
-		IsMethod: false,
+		IsMethod: true,
 	}
 
 	ctx = graphql.WithFieldContext(ctx, fc)
 	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
 		ctx = rctx // use context from middleware stack in children
-		return obj.Author, nil
+		return ec.resolvers.CreateOperation().Author(rctx, obj)
 	})
 	if err != nil {
 		ec.Error(ctx, err)
@@ -5422,9 +5191,9 @@ func (ec *executionContext) _CreateOperation_author(ctx context.Context, field g
 		}
 		return graphql.Null
 	}
-	res := resTmp.(identity.Interface)
+	res := resTmp.(models.IdentityWrapper)
 	fc.Result = res
-	return ec.marshalNIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋidentityᚐInterface(ctx, field.Selections, res)
+	return ec.marshalNIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐIdentityWrapper(ctx, field.Selections, res)
 }
 
 func (ec *executionContext) _CreateOperation_date(ctx context.Context, field graphql.CollectedField, obj *bug.CreateOperation) (ret graphql.Marshaler) {
@@ -5608,13 +5377,13 @@ func (ec *executionContext) _CreateTimelineItem_author(ctx context.Context, fiel
 		Object:   "CreateTimelineItem",
 		Field:    field,
 		Args:     nil,
-		IsMethod: false,
+		IsMethod: true,
 	}
 
 	ctx = graphql.WithFieldContext(ctx, fc)
 	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
 		ctx = rctx // use context from middleware stack in children
-		return obj.Author, nil
+		return ec.resolvers.CreateTimelineItem().Author(rctx, obj)
 	})
 	if err != nil {
 		ec.Error(ctx, err)
@@ -5626,9 +5395,9 @@ func (ec *executionContext) _CreateTimelineItem_author(ctx context.Context, fiel
 		}
 		return graphql.Null
 	}
-	res := resTmp.(identity.Interface)
+	res := resTmp.(models.IdentityWrapper)
 	fc.Result = res
-	return ec.marshalNIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋidentityᚐInterface(ctx, field.Selections, res)
+	return ec.marshalNIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐIdentityWrapper(ctx, field.Selections, res)
 }
 
 func (ec *executionContext) _CreateTimelineItem_message(ctx context.Context, field graphql.CollectedField, obj *bug.CreateTimelineItem) (ret graphql.Marshaler) {
@@ -5914,13 +5683,13 @@ func (ec *executionContext) _EditCommentOperation_author(ctx context.Context, fi
 		Object:   "EditCommentOperation",
 		Field:    field,
 		Args:     nil,
-		IsMethod: false,
+		IsMethod: true,
 	}
 
 	ctx = graphql.WithFieldContext(ctx, fc)
 	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
 		ctx = rctx // use context from middleware stack in children
-		return obj.Author, nil
+		return ec.resolvers.EditCommentOperation().Author(rctx, obj)
 	})
 	if err != nil {
 		ec.Error(ctx, err)
@@ -5932,9 +5701,9 @@ func (ec *executionContext) _EditCommentOperation_author(ctx context.Context, fi
 		}
 		return graphql.Null
 	}
-	res := resTmp.(identity.Interface)
+	res := resTmp.(models.IdentityWrapper)
 	fc.Result = res
-	return ec.marshalNIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋidentityᚐInterface(ctx, field.Selections, res)
+	return ec.marshalNIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐIdentityWrapper(ctx, field.Selections, res)
 }
 
 func (ec *executionContext) _EditCommentOperation_date(ctx context.Context, field graphql.CollectedField, obj *bug.EditCommentOperation) (ret graphql.Marshaler) {
@@ -6073,7 +5842,7 @@ func (ec *executionContext) _EditCommentOperation_files(ctx context.Context, fie
 	return ec.marshalNHash2ᚕgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋutilᚋgitᚐHashᚄ(ctx, field.Selections, res)
 }
 
-func (ec *executionContext) _Identity_id(ctx context.Context, field graphql.CollectedField, obj identity.Interface) (ret graphql.Marshaler) {
+func (ec *executionContext) _Identity_id(ctx context.Context, field graphql.CollectedField, obj models.IdentityWrapper) (ret graphql.Marshaler) {
 	defer func() {
 		if r := recover(); r != nil {
 			ec.Error(ctx, ec.Recover(ctx, r))
@@ -6107,7 +5876,7 @@ func (ec *executionContext) _Identity_id(ctx context.Context, field graphql.Coll
 	return ec.marshalNString2string(ctx, field.Selections, res)
 }
 
-func (ec *executionContext) _Identity_humanId(ctx context.Context, field graphql.CollectedField, obj identity.Interface) (ret graphql.Marshaler) {
+func (ec *executionContext) _Identity_humanId(ctx context.Context, field graphql.CollectedField, obj models.IdentityWrapper) (ret graphql.Marshaler) {
 	defer func() {
 		if r := recover(); r != nil {
 			ec.Error(ctx, ec.Recover(ctx, r))
@@ -6141,7 +5910,7 @@ func (ec *executionContext) _Identity_humanId(ctx context.Context, field graphql
 	return ec.marshalNString2string(ctx, field.Selections, res)
 }
 
-func (ec *executionContext) _Identity_name(ctx context.Context, field graphql.CollectedField, obj identity.Interface) (ret graphql.Marshaler) {
+func (ec *executionContext) _Identity_name(ctx context.Context, field graphql.CollectedField, obj models.IdentityWrapper) (ret graphql.Marshaler) {
 	defer func() {
 		if r := recover(); r != nil {
 			ec.Error(ctx, ec.Recover(ctx, r))
@@ -6172,7 +5941,7 @@ func (ec *executionContext) _Identity_name(ctx context.Context, field graphql.Co
 	return ec.marshalOString2string(ctx, field.Selections, res)
 }
 
-func (ec *executionContext) _Identity_email(ctx context.Context, field graphql.CollectedField, obj identity.Interface) (ret graphql.Marshaler) {
+func (ec *executionContext) _Identity_email(ctx context.Context, field graphql.CollectedField, obj models.IdentityWrapper) (ret graphql.Marshaler) {
 	defer func() {
 		if r := recover(); r != nil {
 			ec.Error(ctx, ec.Recover(ctx, r))
@@ -6189,7 +5958,7 @@ func (ec *executionContext) _Identity_email(ctx context.Context, field graphql.C
 	ctx = graphql.WithFieldContext(ctx, fc)
 	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
 		ctx = rctx // use context from middleware stack in children
-		return obj.Email(), nil
+		return obj.Email()
 	})
 	if err != nil {
 		ec.Error(ctx, err)
@@ -6203,7 +5972,7 @@ func (ec *executionContext) _Identity_email(ctx context.Context, field graphql.C
 	return ec.marshalOString2string(ctx, field.Selections, res)
 }
 
-func (ec *executionContext) _Identity_displayName(ctx context.Context, field graphql.CollectedField, obj identity.Interface) (ret graphql.Marshaler) {
+func (ec *executionContext) _Identity_displayName(ctx context.Context, field graphql.CollectedField, obj models.IdentityWrapper) (ret graphql.Marshaler) {
 	defer func() {
 		if r := recover(); r != nil {
 			ec.Error(ctx, ec.Recover(ctx, r))
@@ -6237,7 +6006,7 @@ func (ec *executionContext) _Identity_displayName(ctx context.Context, field gra
 	return ec.marshalNString2string(ctx, field.Selections, res)
 }
 
-func (ec *executionContext) _Identity_avatarUrl(ctx context.Context, field graphql.CollectedField, obj identity.Interface) (ret graphql.Marshaler) {
+func (ec *executionContext) _Identity_avatarUrl(ctx context.Context, field graphql.CollectedField, obj models.IdentityWrapper) (ret graphql.Marshaler) {
 	defer func() {
 		if r := recover(); r != nil {
 			ec.Error(ctx, ec.Recover(ctx, r))
@@ -6254,7 +6023,7 @@ func (ec *executionContext) _Identity_avatarUrl(ctx context.Context, field graph
 	ctx = graphql.WithFieldContext(ctx, fc)
 	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
 		ctx = rctx // use context from middleware stack in children
-		return obj.AvatarUrl(), nil
+		return obj.AvatarUrl()
 	})
 	if err != nil {
 		ec.Error(ctx, err)
@@ -6268,7 +6037,7 @@ func (ec *executionContext) _Identity_avatarUrl(ctx context.Context, field graph
 	return ec.marshalOString2string(ctx, field.Selections, res)
 }
 
-func (ec *executionContext) _Identity_isProtected(ctx context.Context, field graphql.CollectedField, obj identity.Interface) (ret graphql.Marshaler) {
+func (ec *executionContext) _Identity_isProtected(ctx context.Context, field graphql.CollectedField, obj models.IdentityWrapper) (ret graphql.Marshaler) {
 	defer func() {
 		if r := recover(); r != nil {
 			ec.Error(ctx, ec.Recover(ctx, r))
@@ -6285,7 +6054,7 @@ func (ec *executionContext) _Identity_isProtected(ctx context.Context, field gra
 	ctx = graphql.WithFieldContext(ctx, fc)
 	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
 		ctx = rctx // use context from middleware stack in children
-		return obj.IsProtected(), nil
+		return obj.IsProtected()
 	})
 	if err != nil {
 		ec.Error(ctx, err)
@@ -6365,9 +6134,9 @@ func (ec *executionContext) _IdentityConnection_nodes(ctx context.Context, field
 		}
 		return graphql.Null
 	}
-	res := resTmp.([]identity.Interface)
+	res := resTmp.([]models.IdentityWrapper)
 	fc.Result = res
-	return ec.marshalNIdentity2ᚕgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋidentityᚐInterfaceᚄ(ctx, field.Selections, res)
+	return ec.marshalNIdentity2ᚕgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐIdentityWrapperᚄ(ctx, field.Selections, res)
 }
 
 func (ec *executionContext) _IdentityConnection_pageInfo(ctx context.Context, field graphql.CollectedField, obj *models.IdentityConnection) (ret graphql.Marshaler) {
@@ -6501,9 +6270,9 @@ func (ec *executionContext) _IdentityEdge_node(ctx context.Context, field graphq
 		}
 		return graphql.Null
 	}
-	res := resTmp.(identity.Interface)
+	res := resTmp.(models.IdentityWrapper)
 	fc.Result = res
-	return ec.marshalNIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋidentityᚐInterface(ctx, field.Selections, res)
+	return ec.marshalNIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐIdentityWrapper(ctx, field.Selections, res)
 }
 
 func (ec *executionContext) _Label_name(ctx context.Context, field graphql.CollectedField, obj *bug.Label) (ret graphql.Marshaler) {
@@ -6619,13 +6388,13 @@ func (ec *executionContext) _LabelChangeOperation_author(ctx context.Context, fi
 		Object:   "LabelChangeOperation",
 		Field:    field,
 		Args:     nil,
-		IsMethod: false,
+		IsMethod: true,
 	}
 
 	ctx = graphql.WithFieldContext(ctx, fc)
 	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
 		ctx = rctx // use context from middleware stack in children
-		return obj.Author, nil
+		return ec.resolvers.LabelChangeOperation().Author(rctx, obj)
 	})
 	if err != nil {
 		ec.Error(ctx, err)
@@ -6637,9 +6406,9 @@ func (ec *executionContext) _LabelChangeOperation_author(ctx context.Context, fi
 		}
 		return graphql.Null
 	}
-	res := resTmp.(identity.Interface)
+	res := resTmp.(models.IdentityWrapper)
 	fc.Result = res
-	return ec.marshalNIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋidentityᚐInterface(ctx, field.Selections, res)
+	return ec.marshalNIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐIdentityWrapper(ctx, field.Selections, res)
 }
 
 func (ec *executionContext) _LabelChangeOperation_date(ctx context.Context, field graphql.CollectedField, obj *bug.LabelChangeOperation) (ret graphql.Marshaler) {
@@ -6857,13 +6626,13 @@ func (ec *executionContext) _LabelChangeTimelineItem_author(ctx context.Context,
 		Object:   "LabelChangeTimelineItem",
 		Field:    field,
 		Args:     nil,
-		IsMethod: false,
+		IsMethod: true,
 	}
 
 	ctx = graphql.WithFieldContext(ctx, fc)
 	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
 		ctx = rctx // use context from middleware stack in children
-		return obj.Author, nil
+		return ec.resolvers.LabelChangeTimelineItem().Author(rctx, obj)
 	})
 	if err != nil {
 		ec.Error(ctx, err)
@@ -6875,9 +6644,9 @@ func (ec *executionContext) _LabelChangeTimelineItem_author(ctx context.Context,
 		}
 		return graphql.Null
 	}
-	res := resTmp.(identity.Interface)
+	res := resTmp.(models.IdentityWrapper)
 	fc.Result = res
-	return ec.marshalNIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋidentityᚐInterface(ctx, field.Selections, res)
+	return ec.marshalNIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐIdentityWrapper(ctx, field.Selections, res)
 }
 
 func (ec *executionContext) _LabelChangeTimelineItem_date(ctx context.Context, field graphql.CollectedField, obj *bug.LabelChangeTimelineItem) (ret graphql.Marshaler) {
@@ -7574,9 +7343,9 @@ func (ec *executionContext) _NewBugPayload_bug(ctx context.Context, field graphq
 		}
 		return graphql.Null
 	}
-	res := resTmp.(*bug.Snapshot)
+	res := resTmp.(models.BugWrapper)
 	fc.Result = res
-	return ec.marshalNBug2ᚖgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋbugᚐSnapshot(ctx, field.Selections, res)
+	return ec.marshalNBug2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐBugWrapper(ctx, field.Selections, res)
 }
 
 func (ec *executionContext) _NewBugPayload_operation(ctx context.Context, field graphql.CollectedField, obj *models.NewBugPayload) (ret graphql.Marshaler) {
@@ -7673,9 +7442,9 @@ func (ec *executionContext) _OpenBugPayload_bug(ctx context.Context, field graph
 		}
 		return graphql.Null
 	}
-	res := resTmp.(*bug.Snapshot)
+	res := resTmp.(models.BugWrapper)
 	fc.Result = res
-	return ec.marshalNBug2ᚖgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋbugᚐSnapshot(ctx, field.Selections, res)
+	return ec.marshalNBug2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐBugWrapper(ctx, field.Selections, res)
 }
 
 func (ec *executionContext) _OpenBugPayload_operation(ctx context.Context, field graphql.CollectedField, obj *models.OpenBugPayload) (ret graphql.Marshaler) {
@@ -8264,9 +8033,9 @@ func (ec *executionContext) _Repository_bug(ctx context.Context, field graphql.C
 	if resTmp == nil {
 		return graphql.Null
 	}
-	res := resTmp.(*bug.Snapshot)
+	res := resTmp.(models.BugWrapper)
 	fc.Result = res
-	return ec.marshalOBug2ᚖgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋbugᚐSnapshot(ctx, field.Selections, res)
+	return ec.marshalOBug2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐBugWrapper(ctx, field.Selections, res)
 }
 
 func (ec *executionContext) _Repository_allIdentities(ctx context.Context, field graphql.CollectedField, obj *models.Repository) (ret graphql.Marshaler) {
@@ -8343,9 +8112,9 @@ func (ec *executionContext) _Repository_identity(ctx context.Context, field grap
 	if resTmp == nil {
 		return graphql.Null
 	}
-	res := resTmp.(identity.Interface)
+	res := resTmp.(models.IdentityWrapper)
 	fc.Result = res
-	return ec.marshalOIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋidentityᚐInterface(ctx, field.Selections, res)
+	return ec.marshalOIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐIdentityWrapper(ctx, field.Selections, res)
 }
 
 func (ec *executionContext) _Repository_userIdentity(ctx context.Context, field graphql.CollectedField, obj *models.Repository) (ret graphql.Marshaler) {
@@ -8374,9 +8143,9 @@ func (ec *executionContext) _Repository_userIdentity(ctx context.Context, field
 	if resTmp == nil {
 		return graphql.Null
 	}
-	res := resTmp.(identity.Interface)
+	res := resTmp.(models.IdentityWrapper)
 	fc.Result = res
-	return ec.marshalOIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋidentityᚐInterface(ctx, field.Selections, res)
+	return ec.marshalOIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐIdentityWrapper(ctx, field.Selections, res)
 }
 
 func (ec *executionContext) _Repository_validLabels(ctx context.Context, field graphql.CollectedField, obj *models.Repository) (ret graphql.Marshaler) {
@@ -8465,13 +8234,13 @@ func (ec *executionContext) _SetStatusOperation_author(ctx context.Context, fiel
 		Object:   "SetStatusOperation",
 		Field:    field,
 		Args:     nil,
-		IsMethod: false,
+		IsMethod: true,
 	}
 
 	ctx = graphql.WithFieldContext(ctx, fc)
 	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
 		ctx = rctx // use context from middleware stack in children
-		return obj.Author, nil
+		return ec.resolvers.SetStatusOperation().Author(rctx, obj)
 	})
 	if err != nil {
 		ec.Error(ctx, err)
@@ -8483,9 +8252,9 @@ func (ec *executionContext) _SetStatusOperation_author(ctx context.Context, fiel
 		}
 		return graphql.Null
 	}
-	res := resTmp.(identity.Interface)
+	res := resTmp.(models.IdentityWrapper)
 	fc.Result = res
-	return ec.marshalNIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋidentityᚐInterface(ctx, field.Selections, res)
+	return ec.marshalNIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐIdentityWrapper(ctx, field.Selections, res)
 }
 
 func (ec *executionContext) _SetStatusOperation_date(ctx context.Context, field graphql.CollectedField, obj *bug.SetStatusOperation) (ret graphql.Marshaler) {
@@ -8601,13 +8370,13 @@ func (ec *executionContext) _SetStatusTimelineItem_author(ctx context.Context, f
 		Object:   "SetStatusTimelineItem",
 		Field:    field,
 		Args:     nil,
-		IsMethod: false,
+		IsMethod: true,
 	}
 
 	ctx = graphql.WithFieldContext(ctx, fc)
 	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
 		ctx = rctx // use context from middleware stack in children
-		return obj.Author, nil
+		return ec.resolvers.SetStatusTimelineItem().Author(rctx, obj)
 	})
 	if err != nil {
 		ec.Error(ctx, err)
@@ -8619,9 +8388,9 @@ func (ec *executionContext) _SetStatusTimelineItem_author(ctx context.Context, f
 		}
 		return graphql.Null
 	}
-	res := resTmp.(identity.Interface)
+	res := resTmp.(models.IdentityWrapper)
 	fc.Result = res
-	return ec.marshalNIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋidentityᚐInterface(ctx, field.Selections, res)
+	return ec.marshalNIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐIdentityWrapper(ctx, field.Selections, res)
 }
 
 func (ec *executionContext) _SetStatusTimelineItem_date(ctx context.Context, field graphql.CollectedField, obj *bug.SetStatusTimelineItem) (ret graphql.Marshaler) {
@@ -8737,13 +8506,13 @@ func (ec *executionContext) _SetTitleOperation_author(ctx context.Context, field
 		Object:   "SetTitleOperation",
 		Field:    field,
 		Args:     nil,
-		IsMethod: false,
+		IsMethod: true,
 	}
 
 	ctx = graphql.WithFieldContext(ctx, fc)
 	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
 		ctx = rctx // use context from middleware stack in children
-		return obj.Author, nil
+		return ec.resolvers.SetTitleOperation().Author(rctx, obj)
 	})
 	if err != nil {
 		ec.Error(ctx, err)
@@ -8755,9 +8524,9 @@ func (ec *executionContext) _SetTitleOperation_author(ctx context.Context, field
 		}
 		return graphql.Null
 	}
-	res := resTmp.(identity.Interface)
+	res := resTmp.(models.IdentityWrapper)
 	fc.Result = res
-	return ec.marshalNIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋidentityᚐInterface(ctx, field.Selections, res)
+	return ec.marshalNIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐIdentityWrapper(ctx, field.Selections, res)
 }
 
 func (ec *executionContext) _SetTitleOperation_date(ctx context.Context, field graphql.CollectedField, obj *bug.SetTitleOperation) (ret graphql.Marshaler) {
@@ -8922,9 +8691,9 @@ func (ec *executionContext) _SetTitlePayload_bug(ctx context.Context, field grap
 		}
 		return graphql.Null
 	}
-	res := resTmp.(*bug.Snapshot)
+	res := resTmp.(models.BugWrapper)
 	fc.Result = res
-	return ec.marshalNBug2ᚖgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋbugᚐSnapshot(ctx, field.Selections, res)
+	return ec.marshalNBug2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐBugWrapper(ctx, field.Selections, res)
 }
 
 func (ec *executionContext) _SetTitlePayload_operation(ctx context.Context, field graphql.CollectedField, obj *models.SetTitlePayload) (ret graphql.Marshaler) {
@@ -9006,13 +8775,13 @@ func (ec *executionContext) _SetTitleTimelineItem_author(ctx context.Context, fi
 		Object:   "SetTitleTimelineItem",
 		Field:    field,
 		Args:     nil,
-		IsMethod: false,
+		IsMethod: true,
 	}
 
 	ctx = graphql.WithFieldContext(ctx, fc)
 	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
 		ctx = rctx // use context from middleware stack in children
-		return obj.Author, nil
+		return ec.resolvers.SetTitleTimelineItem().Author(rctx, obj)
 	})
 	if err != nil {
 		ec.Error(ctx, err)
@@ -9024,9 +8793,9 @@ func (ec *executionContext) _SetTitleTimelineItem_author(ctx context.Context, fi
 		}
 		return graphql.Null
 	}
-	res := resTmp.(identity.Interface)
+	res := resTmp.(models.IdentityWrapper)
 	fc.Result = res
-	return ec.marshalNIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋidentityᚐInterface(ctx, field.Selections, res)
+	return ec.marshalNIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐIdentityWrapper(ctx, field.Selections, res)
 }
 
 func (ec *executionContext) _SetTitleTimelineItem_date(ctx context.Context, field graphql.CollectedField, obj *bug.SetTitleTimelineItem) (ret graphql.Marshaler) {
@@ -10687,7 +10456,7 @@ func (ec *executionContext) _Authored(ctx context.Context, sel ast.SelectionSet,
 			return graphql.Null
 		}
 		return ec._Comment(ctx, sel, obj)
-	case *bug.Snapshot:
+	case models.BugWrapper:
 		if obj == nil {
 			return graphql.Null
 		}
@@ -10861,10 +10630,19 @@ func (ec *executionContext) _AddCommentOperation(ctx context.Context, sel ast.Se
 				return res
 			})
 		case "author":
-			out.Values[i] = ec._AddCommentOperation_author(ctx, field, obj)
-			if out.Values[i] == graphql.Null {
-				atomic.AddUint32(&invalids, 1)
-			}
+			field := field
+			out.Concurrently(i, func() (res graphql.Marshaler) {
+				defer func() {
+					if r := recover(); r != nil {
+						ec.Error(ctx, ec.Recover(ctx, r))
+					}
+				}()
+				res = ec._AddCommentOperation_author(ctx, field, obj)
+				if res == graphql.Null {
+					atomic.AddUint32(&invalids, 1)
+				}
+				return res
+			})
 		case "date":
 			field := field
 			out.Concurrently(i, func() (res graphql.Marshaler) {
@@ -10960,10 +10738,19 @@ func (ec *executionContext) _AddCommentTimelineItem(ctx context.Context, sel ast
 				return res
 			})
 		case "author":
-			out.Values[i] = ec._AddCommentTimelineItem_author(ctx, field, obj)
-			if out.Values[i] == graphql.Null {
-				atomic.AddUint32(&invalids, 1)
-			}
+			field := field
+			out.Concurrently(i, func() (res graphql.Marshaler) {
+				defer func() {
+					if r := recover(); r != nil {
+						ec.Error(ctx, ec.Recover(ctx, r))
+					}
+				}()
+				res = ec._AddCommentTimelineItem_author(ctx, field, obj)
+				if res == graphql.Null {
+					atomic.AddUint32(&invalids, 1)
+				}
+				return res
+			})
 		case "message":
 			out.Values[i] = ec._AddCommentTimelineItem_message(ctx, field, obj)
 			if out.Values[i] == graphql.Null {
@@ -11030,7 +10817,7 @@ func (ec *executionContext) _AddCommentTimelineItem(ctx context.Context, sel ast
 
 var bugImplementors = []string{"Bug", "Authored"}
 
-func (ec *executionContext) _Bug(ctx context.Context, sel ast.SelectionSet, obj *bug.Snapshot) graphql.Marshaler {
+func (ec *executionContext) _Bug(ctx context.Context, sel ast.SelectionSet, obj models.BugWrapper) graphql.Marshaler {
 	fields := graphql.CollectFields(ec.OperationContext, sel, bugImplementors)
 
 	out := graphql.NewFieldSet(fields)
@@ -11102,19 +10889,10 @@ func (ec *executionContext) _Bug(ctx context.Context, sel ast.SelectionSet, obj
 				atomic.AddUint32(&invalids, 1)
 			}
 		case "lastEdit":
-			field := field
-			out.Concurrently(i, func() (res graphql.Marshaler) {
-				defer func() {
-					if r := recover(); r != nil {
-						ec.Error(ctx, ec.Recover(ctx, r))
-					}
-				}()
-				res = ec._Bug_lastEdit(ctx, field, obj)
-				if res == graphql.Null {
-					atomic.AddUint32(&invalids, 1)
-				}
-				return res
-			})
+			out.Values[i] = ec._Bug_lastEdit(ctx, field, obj)
+			if out.Values[i] == graphql.Null {
+				atomic.AddUint32(&invalids, 1)
+			}
 		case "actors":
 			field := field
 			out.Concurrently(i, func() (res graphql.Marshaler) {
@@ -11419,19 +11197,28 @@ func (ec *executionContext) _Comment(ctx context.Context, sel ast.SelectionSet,
 		case "__typename":
 			out.Values[i] = graphql.MarshalString("Comment")
 		case "author":
-			out.Values[i] = ec._Comment_author(ctx, field, obj)
-			if out.Values[i] == graphql.Null {
-				invalids++
-			}
+			field := field
+			out.Concurrently(i, func() (res graphql.Marshaler) {
+				defer func() {
+					if r := recover(); r != nil {
+						ec.Error(ctx, ec.Recover(ctx, r))
+					}
+				}()
+				res = ec._Comment_author(ctx, field, obj)
+				if res == graphql.Null {
+					atomic.AddUint32(&invalids, 1)
+				}
+				return res
+			})
 		case "message":
 			out.Values[i] = ec._Comment_message(ctx, field, obj)
 			if out.Values[i] == graphql.Null {
-				invalids++
+				atomic.AddUint32(&invalids, 1)
 			}
 		case "files":
 			out.Values[i] = ec._Comment_files(ctx, field, obj)
 			if out.Values[i] == graphql.Null {
-				invalids++
+				atomic.AddUint32(&invalids, 1)
 			}
 		default:
 			panic("unknown field " + strconv.Quote(field.Name))
@@ -11643,10 +11430,19 @@ func (ec *executionContext) _CreateOperation(ctx context.Context, sel ast.Select
 				return res
 			})
 		case "author":
-			out.Values[i] = ec._CreateOperation_author(ctx, field, obj)
-			if out.Values[i] == graphql.Null {
-				atomic.AddUint32(&invalids, 1)
-			}
+			field := field
+			out.Concurrently(i, func() (res graphql.Marshaler) {
+				defer func() {
+					if r := recover(); r != nil {
+						ec.Error(ctx, ec.Recover(ctx, r))
+					}
+				}()
+				res = ec._CreateOperation_author(ctx, field, obj)
+				if res == graphql.Null {
+					atomic.AddUint32(&invalids, 1)
+				}
+				return res
+			})
 		case "date":
 			field := field
 			out.Concurrently(i, func() (res graphql.Marshaler) {
@@ -11713,10 +11509,19 @@ func (ec *executionContext) _CreateTimelineItem(ctx context.Context, sel ast.Sel
 				return res
 			})
 		case "author":
-			out.Values[i] = ec._CreateTimelineItem_author(ctx, field, obj)
-			if out.Values[i] == graphql.Null {
-				atomic.AddUint32(&invalids, 1)
-			}
+			field := field
+			out.Concurrently(i, func() (res graphql.Marshaler) {
+				defer func() {
+					if r := recover(); r != nil {
+						ec.Error(ctx, ec.Recover(ctx, r))
+					}
+				}()
+				res = ec._CreateTimelineItem_author(ctx, field, obj)
+				if res == graphql.Null {
+					atomic.AddUint32(&invalids, 1)
+				}
+				return res
+			})
 		case "message":
 			out.Values[i] = ec._CreateTimelineItem_message(ctx, field, obj)
 			if out.Values[i] == graphql.Null {
@@ -11807,10 +11612,19 @@ func (ec *executionContext) _EditCommentOperation(ctx context.Context, sel ast.S
 				return res
 			})
 		case "author":
-			out.Values[i] = ec._EditCommentOperation_author(ctx, field, obj)
-			if out.Values[i] == graphql.Null {
-				atomic.AddUint32(&invalids, 1)
-			}
+			field := field
+			out.Concurrently(i, func() (res graphql.Marshaler) {
+				defer func() {
+					if r := recover(); r != nil {
+						ec.Error(ctx, ec.Recover(ctx, r))
+					}
+				}()
+				res = ec._EditCommentOperation_author(ctx, field, obj)
+				if res == graphql.Null {
+					atomic.AddUint32(&invalids, 1)
+				}
+				return res
+			})
 		case "date":
 			field := field
 			out.Concurrently(i, func() (res graphql.Marshaler) {
@@ -11862,7 +11676,7 @@ func (ec *executionContext) _EditCommentOperation(ctx context.Context, sel ast.S
 
 var identityImplementors = []string{"Identity"}
 
-func (ec *executionContext) _Identity(ctx context.Context, sel ast.SelectionSet, obj identity.Interface) graphql.Marshaler {
+func (ec *executionContext) _Identity(ctx context.Context, sel ast.SelectionSet, obj models.IdentityWrapper) graphql.Marshaler {
 	fields := graphql.CollectFields(ec.OperationContext, sel, identityImplementors)
 
 	out := graphql.NewFieldSet(fields)
@@ -12076,10 +11890,19 @@ func (ec *executionContext) _LabelChangeOperation(ctx context.Context, sel ast.S
 				return res
 			})
 		case "author":
-			out.Values[i] = ec._LabelChangeOperation_author(ctx, field, obj)
-			if out.Values[i] == graphql.Null {
-				atomic.AddUint32(&invalids, 1)
-			}
+			field := field
+			out.Concurrently(i, func() (res graphql.Marshaler) {
+				defer func() {
+					if r := recover(); r != nil {
+						ec.Error(ctx, ec.Recover(ctx, r))
+					}
+				}()
+				res = ec._LabelChangeOperation_author(ctx, field, obj)
+				if res == graphql.Null {
+					atomic.AddUint32(&invalids, 1)
+				}
+				return res
+			})
 		case "date":
 			field := field
 			out.Concurrently(i, func() (res graphql.Marshaler) {
@@ -12182,10 +12005,19 @@ func (ec *executionContext) _LabelChangeTimelineItem(ctx context.Context, sel as
 				return res
 			})
 		case "author":
-			out.Values[i] = ec._LabelChangeTimelineItem_author(ctx, field, obj)
-			if out.Values[i] == graphql.Null {
-				atomic.AddUint32(&invalids, 1)
-			}
+			field := field
+			out.Concurrently(i, func() (res graphql.Marshaler) {
+				defer func() {
+					if r := recover(); r != nil {
+						ec.Error(ctx, ec.Recover(ctx, r))
+					}
+				}()
+				res = ec._LabelChangeTimelineItem_author(ctx, field, obj)
+				if res == graphql.Null {
+					atomic.AddUint32(&invalids, 1)
+				}
+				return res
+			})
 		case "date":
 			field := field
 			out.Concurrently(i, func() (res graphql.Marshaler) {
@@ -12720,10 +12552,19 @@ func (ec *executionContext) _SetStatusOperation(ctx context.Context, sel ast.Sel
 				return res
 			})
 		case "author":
-			out.Values[i] = ec._SetStatusOperation_author(ctx, field, obj)
-			if out.Values[i] == graphql.Null {
-				atomic.AddUint32(&invalids, 1)
-			}
+			field := field
+			out.Concurrently(i, func() (res graphql.Marshaler) {
+				defer func() {
+					if r := recover(); r != nil {
+						ec.Error(ctx, ec.Recover(ctx, r))
+					}
+				}()
+				res = ec._SetStatusOperation_author(ctx, field, obj)
+				if res == graphql.Null {
+					atomic.AddUint32(&invalids, 1)
+				}
+				return res
+			})
 		case "date":
 			field := field
 			out.Concurrently(i, func() (res graphql.Marshaler) {
@@ -12789,10 +12630,19 @@ func (ec *executionContext) _SetStatusTimelineItem(ctx context.Context, sel ast.
 				return res
 			})
 		case "author":
-			out.Values[i] = ec._SetStatusTimelineItem_author(ctx, field, obj)
-			if out.Values[i] == graphql.Null {
-				atomic.AddUint32(&invalids, 1)
-			}
+			field := field
+			out.Concurrently(i, func() (res graphql.Marshaler) {
+				defer func() {
+					if r := recover(); r != nil {
+						ec.Error(ctx, ec.Recover(ctx, r))
+					}
+				}()
+				res = ec._SetStatusTimelineItem_author(ctx, field, obj)
+				if res == graphql.Null {
+					atomic.AddUint32(&invalids, 1)
+				}
+				return res
+			})
 		case "date":
 			field := field
 			out.Concurrently(i, func() (res graphql.Marshaler) {
@@ -12858,10 +12708,19 @@ func (ec *executionContext) _SetTitleOperation(ctx context.Context, sel ast.Sele
 				return res
 			})
 		case "author":
-			out.Values[i] = ec._SetTitleOperation_author(ctx, field, obj)
-			if out.Values[i] == graphql.Null {
-				atomic.AddUint32(&invalids, 1)
-			}
+			field := field
+			out.Concurrently(i, func() (res graphql.Marshaler) {
+				defer func() {
+					if r := recover(); r != nil {
+						ec.Error(ctx, ec.Recover(ctx, r))
+					}
+				}()
+				res = ec._SetTitleOperation_author(ctx, field, obj)
+				if res == graphql.Null {
+					atomic.AddUint32(&invalids, 1)
+				}
+				return res
+			})
 		case "date":
 			field := field
 			out.Concurrently(i, func() (res graphql.Marshaler) {
@@ -12957,10 +12816,19 @@ func (ec *executionContext) _SetTitleTimelineItem(ctx context.Context, sel ast.S
 				return res
 			})
 		case "author":
-			out.Values[i] = ec._SetTitleTimelineItem_author(ctx, field, obj)
-			if out.Values[i] == graphql.Null {
-				atomic.AddUint32(&invalids, 1)
-			}
+			field := field
+			out.Concurrently(i, func() (res graphql.Marshaler) {
+				defer func() {
+					if r := recover(); r != nil {
+						ec.Error(ctx, ec.Recover(ctx, r))
+					}
+				}()
+				res = ec._SetTitleTimelineItem_author(ctx, field, obj)
+				if res == graphql.Null {
+					atomic.AddUint32(&invalids, 1)
+				}
+				return res
+			})
 		case "date":
 			field := field
 			out.Concurrently(i, func() (res graphql.Marshaler) {
@@ -13361,11 +13229,17 @@ func (ec *executionContext) marshalNBoolean2bool(ctx context.Context, sel ast.Se
 	return res
 }
 
-func (ec *executionContext) marshalNBug2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋbugᚐSnapshot(ctx context.Context, sel ast.SelectionSet, v bug.Snapshot) graphql.Marshaler {
-	return ec._Bug(ctx, sel, &v)
+func (ec *executionContext) marshalNBug2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐBugWrapper(ctx context.Context, sel ast.SelectionSet, v models.BugWrapper) graphql.Marshaler {
+	if v == nil {
+		if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
+			ec.Errorf(ctx, "must not be null")
+		}
+		return graphql.Null
+	}
+	return ec._Bug(ctx, sel, v)
 }
 
-func (ec *executionContext) marshalNBug2ᚕᚖgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋbugᚐSnapshotᚄ(ctx context.Context, sel ast.SelectionSet, v []*bug.Snapshot) graphql.Marshaler {
+func (ec *executionContext) marshalNBug2ᚕgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐBugWrapperᚄ(ctx context.Context, sel ast.SelectionSet, v []models.BugWrapper) graphql.Marshaler {
 	ret := make(graphql.Array, len(v))
 	var wg sync.WaitGroup
 	isLen1 := len(v) == 1
@@ -13389,7 +13263,7 @@ func (ec *executionContext) marshalNBug2ᚕᚖgithubᚗcomᚋMichaelMureᚋgit
 			if !isLen1 {
 				defer wg.Done()
 			}
-			ret[i] = ec.marshalNBug2ᚖgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋbugᚐSnapshot(ctx, sel, v[i])
+			ret[i] = ec.marshalNBug2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐBugWrapper(ctx, sel, v[i])
 		}
 		if isLen1 {
 			f(i)
@@ -13402,16 +13276,6 @@ func (ec *executionContext) marshalNBug2ᚕᚖgithubᚗcomᚋMichaelMureᚋgit
 	return ret
 }
 
-func (ec *executionContext) marshalNBug2ᚖgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋbugᚐSnapshot(ctx context.Context, sel ast.SelectionSet, v *bug.Snapshot) graphql.Marshaler {
-	if v == nil {
-		if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
-			ec.Errorf(ctx, "must not be null")
-		}
-		return graphql.Null
-	}
-	return ec._Bug(ctx, sel, v)
-}
-
 func (ec *executionContext) marshalNBugConnection2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐBugConnection(ctx context.Context, sel ast.SelectionSet, v models.BugConnection) graphql.Marshaler {
 	return ec._BugConnection(ctx, sel, &v)
 }
@@ -13768,7 +13632,7 @@ func (ec *executionContext) marshalNHash2ᚕgithubᚗcomᚋMichaelMureᚋgitᚑb
 	return ret
 }
 
-func (ec *executionContext) marshalNIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋidentityᚐInterface(ctx context.Context, sel ast.SelectionSet, v identity.Interface) graphql.Marshaler {
+func (ec *executionContext) marshalNIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐIdentityWrapper(ctx context.Context, sel ast.SelectionSet, v models.IdentityWrapper) graphql.Marshaler {
 	if v == nil {
 		if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
 			ec.Errorf(ctx, "must not be null")
@@ -13778,7 +13642,7 @@ func (ec *executionContext) marshalNIdentity2githubᚗcomᚋMichaelMureᚋgitᚑ
 	return ec._Identity(ctx, sel, v)
 }
 
-func (ec *executionContext) marshalNIdentity2ᚕgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋidentityᚐInterfaceᚄ(ctx context.Context, sel ast.SelectionSet, v []identity.Interface) graphql.Marshaler {
+func (ec *executionContext) marshalNIdentity2ᚕgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐIdentityWrapperᚄ(ctx context.Context, sel ast.SelectionSet, v []models.IdentityWrapper) graphql.Marshaler {
 	ret := make(graphql.Array, len(v))
 	var wg sync.WaitGroup
 	isLen1 := len(v) == 1
@@ -13802,7 +13666,7 @@ func (ec *executionContext) marshalNIdentity2ᚕgithubᚗcomᚋMichaelMureᚋgit
 			if !isLen1 {
 				defer wg.Done()
 			}
-			ret[i] = ec.marshalNIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋidentityᚐInterface(ctx, sel, v[i])
+			ret[i] = ec.marshalNIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐIdentityWrapper(ctx, sel, v[i])
 		}
 		if isLen1 {
 			f(i)
@@ -14684,11 +14548,7 @@ func (ec *executionContext) marshalOBoolean2ᚖbool(ctx context.Context, sel ast
 	return ec.marshalOBoolean2bool(ctx, sel, *v)
 }
 
-func (ec *executionContext) marshalOBug2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋbugᚐSnapshot(ctx context.Context, sel ast.SelectionSet, v bug.Snapshot) graphql.Marshaler {
-	return ec._Bug(ctx, sel, &v)
-}
-
-func (ec *executionContext) marshalOBug2ᚖgithubᚗcomᚋMichaelMureᚋgitᚑbugᚋbugᚐSnapshot(ctx context.Context, sel ast.SelectionSet, v *bug.Snapshot) graphql.Marshaler {
+func (ec *executionContext) marshalOBug2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐBugWrapper(ctx context.Context, sel ast.SelectionSet, v models.BugWrapper) graphql.Marshaler {
 	if v == nil {
 		return graphql.Null
 	}
@@ -14739,7 +14599,7 @@ func (ec *executionContext) marshalOHash2ᚕgithubᚗcomᚋMichaelMureᚋgitᚑb
 	return ret
 }
 
-func (ec *executionContext) marshalOIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋidentityᚐInterface(ctx context.Context, sel ast.SelectionSet, v identity.Interface) graphql.Marshaler {
+func (ec *executionContext) marshalOIdentity2githubᚗcomᚋMichaelMureᚋgitᚑbugᚋgraphqlᚋmodelsᚐIdentityWrapper(ctx context.Context, sel ast.SelectionSet, v models.IdentityWrapper) graphql.Marshaler {
 	if v == nil {
 		return graphql.Null
 	}
diff --git a/graphql/models/gen_models.go b/graphql/models/gen_models.go
index 5498960b3f04adbef69a083832a27618843bc267..b3e1465512d1bfb86b63af60326f22af5deeb085 100644
--- a/graphql/models/gen_models.go
+++ b/graphql/models/gen_models.go
@@ -8,7 +8,6 @@ import (
 	"strconv"
 
 	"github.com/MichaelMure/git-bug/bug"
-	"github.com/MichaelMure/git-bug/identity"
 	"github.com/MichaelMure/git-bug/util/git"
 )
 
@@ -34,7 +33,7 @@ type AddCommentPayload struct {
 	// A unique identifier for the client performing the mutation.
 	ClientMutationID *string `json:"clientMutationId"`
 	// The affected bug.
-	Bug *bug.Snapshot `json:"bug"`
+	Bug BugWrapper `json:"bug"`
 	// The resulting operation.
 	Operation *bug.AddCommentOperation `json:"operation"`
 }
@@ -42,8 +41,8 @@ type AddCommentPayload struct {
 // The connection type for Bug.
 type BugConnection struct {
 	// A list of edges.
-	Edges []*BugEdge      `json:"edges"`
-	Nodes []*bug.Snapshot `json:"nodes"`
+	Edges []*BugEdge   `json:"edges"`
+	Nodes []BugWrapper `json:"nodes"`
 	// Information to aid in pagination.
 	PageInfo *PageInfo `json:"pageInfo"`
 	// Identifies the total count of items in the connection.
@@ -55,7 +54,7 @@ type BugEdge struct {
 	// A cursor for use in pagination.
 	Cursor string `json:"cursor"`
 	// The item at the end of the edge.
-	Node *bug.Snapshot `json:"node"`
+	Node BugWrapper `json:"node"`
 }
 
 type ChangeLabelInput struct {
@@ -75,7 +74,7 @@ type ChangeLabelPayload struct {
 	// A unique identifier for the client performing the mutation.
 	ClientMutationID *string `json:"clientMutationId"`
 	// The affected bug.
-	Bug *bug.Snapshot `json:"bug"`
+	Bug BugWrapper `json:"bug"`
 	// The resulting operation.
 	Operation *bug.LabelChangeOperation `json:"operation"`
 	// The effect each source label had.
@@ -95,7 +94,7 @@ type CloseBugPayload struct {
 	// A unique identifier for the client performing the mutation.
 	ClientMutationID *string `json:"clientMutationId"`
 	// The affected bug.
-	Bug *bug.Snapshot `json:"bug"`
+	Bug BugWrapper `json:"bug"`
 	// The resulting operation.
 	Operation *bug.SetStatusOperation `json:"operation"`
 }
@@ -125,7 +124,7 @@ type CommitAsNeededPayload struct {
 	// A unique identifier for the client performing the mutation.
 	ClientMutationID *string `json:"clientMutationId"`
 	// The affected bug.
-	Bug *bug.Snapshot `json:"bug"`
+	Bug BugWrapper `json:"bug"`
 }
 
 type CommitInput struct {
@@ -141,19 +140,19 @@ type CommitPayload struct {
 	// A unique identifier for the client performing the mutation.
 	ClientMutationID *string `json:"clientMutationId"`
 	// The affected bug.
-	Bug *bug.Snapshot `json:"bug"`
+	Bug BugWrapper `json:"bug"`
 }
 
 type IdentityConnection struct {
-	Edges      []*IdentityEdge      `json:"edges"`
-	Nodes      []identity.Interface `json:"nodes"`
-	PageInfo   *PageInfo            `json:"pageInfo"`
-	TotalCount int                  `json:"totalCount"`
+	Edges      []*IdentityEdge   `json:"edges"`
+	Nodes      []IdentityWrapper `json:"nodes"`
+	PageInfo   *PageInfo         `json:"pageInfo"`
+	TotalCount int               `json:"totalCount"`
 }
 
 type IdentityEdge struct {
-	Cursor string             `json:"cursor"`
-	Node   identity.Interface `json:"node"`
+	Cursor string          `json:"cursor"`
+	Node   IdentityWrapper `json:"node"`
 }
 
 type LabelConnection struct {
@@ -185,7 +184,7 @@ type NewBugPayload struct {
 	// A unique identifier for the client performing the mutation.
 	ClientMutationID *string `json:"clientMutationId"`
 	// The created bug.
-	Bug *bug.Snapshot `json:"bug"`
+	Bug BugWrapper `json:"bug"`
 	// The resulting operation.
 	Operation *bug.CreateOperation `json:"operation"`
 }
@@ -203,7 +202,7 @@ type OpenBugPayload struct {
 	// A unique identifier for the client performing the mutation.
 	ClientMutationID *string `json:"clientMutationId"`
 	// The affected bug.
-	Bug *bug.Snapshot `json:"bug"`
+	Bug BugWrapper `json:"bug"`
 	// The resulting operation.
 	Operation *bug.SetStatusOperation `json:"operation"`
 }
@@ -249,7 +248,7 @@ type SetTitlePayload struct {
 	// A unique identifier for the client performing the mutation.
 	ClientMutationID *string `json:"clientMutationId"`
 	// The affected bug.
-	Bug *bug.Snapshot `json:"bug"`
+	Bug BugWrapper `json:"bug"`
 	// The resulting operation
 	Operation *bug.SetTitleOperation `json:"operation"`
 }
diff --git a/graphql/models/lazy_bug.go b/graphql/models/lazy_bug.go
new file mode 100644
index 0000000000000000000000000000000000000000..6034e80d808702bb09034e5fc0fd7726cbfbb03b
--- /dev/null
+++ b/graphql/models/lazy_bug.go
@@ -0,0 +1,215 @@
+package models
+
+import (
+	"sync"
+	"time"
+
+	"github.com/MichaelMure/git-bug/bug"
+	"github.com/MichaelMure/git-bug/cache"
+	"github.com/MichaelMure/git-bug/entity"
+)
+
+// BugWrapper is an interface used by the GraphQL resolvers to handle a bug.
+// Depending on the situation, a Bug can already be fully loaded in memory or not.
+// This interface is used to wrap either a lazyBug or a loadedBug depending on the situation.
+type BugWrapper interface {
+	Id() entity.Id
+	LastEdit() time.Time
+	Status() bug.Status
+	Title() string
+	Comments() ([]bug.Comment, error)
+	Labels() []bug.Label
+	Author() (IdentityWrapper, error)
+	Actors() ([]IdentityWrapper, error)
+	Participants() ([]IdentityWrapper, error)
+	CreatedAt() time.Time
+	Timeline() ([]bug.TimelineItem, error)
+	Operations() ([]bug.Operation, error)
+
+	IsAuthored()
+}
+
+var _ BugWrapper = &lazyBug{}
+
+// lazyBug is a lazy-loading wrapper that fetch data from the cache (BugExcerpt) in priority,
+// and load the complete bug and snapshot only when necessary.
+type lazyBug struct {
+	cache   *cache.RepoCache
+	excerpt *cache.BugExcerpt
+
+	mu   sync.Mutex
+	snap *bug.Snapshot
+}
+
+func NewLazyBug(cache *cache.RepoCache, excerpt *cache.BugExcerpt) *lazyBug {
+	return &lazyBug{
+		cache:   cache,
+		excerpt: excerpt,
+	}
+}
+
+func (lb *lazyBug) load() error {
+	if lb.snap != nil {
+		return nil
+	}
+
+	lb.mu.Lock()
+	defer lb.mu.Unlock()
+
+	b, err := lb.cache.ResolveBug(lb.excerpt.Id)
+	if err != nil {
+		return err
+	}
+
+	lb.snap = b.Snapshot()
+	return nil
+}
+
+func (lb *lazyBug) identity(id entity.Id) (IdentityWrapper, error) {
+	i, err := lb.cache.ResolveIdentityExcerpt(id)
+	if err != nil {
+		return nil, err
+	}
+	return &lazyIdentity{cache: lb.cache, excerpt: i}, nil
+}
+
+// Sign post method for gqlgen
+func (lb *lazyBug) IsAuthored() {}
+
+func (lb *lazyBug) Id() entity.Id {
+	return lb.excerpt.Id
+}
+
+func (lb *lazyBug) LastEdit() time.Time {
+	return time.Unix(lb.excerpt.EditUnixTime, 0)
+}
+
+func (lb *lazyBug) Status() bug.Status {
+	return lb.excerpt.Status
+}
+
+func (lb *lazyBug) Title() string {
+	return lb.excerpt.Title
+}
+
+func (lb *lazyBug) Comments() ([]bug.Comment, error) {
+	err := lb.load()
+	if err != nil {
+		return nil, err
+	}
+	return lb.snap.Comments, nil
+}
+
+func (lb *lazyBug) Labels() []bug.Label {
+	return lb.excerpt.Labels
+}
+
+func (lb *lazyBug) Author() (IdentityWrapper, error) {
+	return lb.identity(lb.excerpt.AuthorId)
+}
+
+func (lb *lazyBug) Actors() ([]IdentityWrapper, error) {
+	result := make([]IdentityWrapper, len(lb.excerpt.Actors))
+	for i, actorId := range lb.excerpt.Actors {
+		actor, err := lb.identity(actorId)
+		if err != nil {
+			return nil, err
+		}
+		result[i] = actor
+	}
+	return result, nil
+}
+
+func (lb *lazyBug) Participants() ([]IdentityWrapper, error) {
+	result := make([]IdentityWrapper, len(lb.excerpt.Participants))
+	for i, participantId := range lb.excerpt.Participants {
+		participant, err := lb.identity(participantId)
+		if err != nil {
+			return nil, err
+		}
+		result[i] = participant
+	}
+	return result, nil
+}
+
+func (lb *lazyBug) CreatedAt() time.Time {
+	return time.Unix(lb.excerpt.CreateUnixTime, 0)
+}
+
+func (lb *lazyBug) Timeline() ([]bug.TimelineItem, error) {
+	err := lb.load()
+	if err != nil {
+		return nil, err
+	}
+	return lb.snap.Timeline, nil
+}
+
+func (lb *lazyBug) Operations() ([]bug.Operation, error) {
+	err := lb.load()
+	if err != nil {
+		return nil, err
+	}
+	return lb.snap.Operations, nil
+}
+
+var _ BugWrapper = &loadedBug{}
+
+type loadedBug struct {
+	*bug.Snapshot
+}
+
+func NewLoadedBug(snap *bug.Snapshot) *loadedBug {
+	return &loadedBug{Snapshot: snap}
+}
+
+func (l *loadedBug) LastEdit() time.Time {
+	return l.Snapshot.LastEditTime()
+}
+
+func (l *loadedBug) Status() bug.Status {
+	return l.Snapshot.Status
+}
+
+func (l *loadedBug) Title() string {
+	return l.Snapshot.Title
+}
+
+func (l *loadedBug) Comments() ([]bug.Comment, error) {
+	return l.Snapshot.Comments, nil
+}
+
+func (l *loadedBug) Labels() []bug.Label {
+	return l.Snapshot.Labels
+}
+
+func (l *loadedBug) Author() (IdentityWrapper, error) {
+	return NewLoadedIdentity(l.Snapshot.Author), nil
+}
+
+func (l *loadedBug) Actors() ([]IdentityWrapper, error) {
+	res := make([]IdentityWrapper, len(l.Snapshot.Actors))
+	for i, actor := range l.Snapshot.Actors {
+		res[i] = NewLoadedIdentity(actor)
+	}
+	return res, nil
+}
+
+func (l *loadedBug) Participants() ([]IdentityWrapper, error) {
+	res := make([]IdentityWrapper, len(l.Snapshot.Participants))
+	for i, participant := range l.Snapshot.Participants {
+		res[i] = NewLoadedIdentity(participant)
+	}
+	return res, nil
+}
+
+func (l *loadedBug) CreatedAt() time.Time {
+	return l.Snapshot.CreatedAt
+}
+
+func (l *loadedBug) Timeline() ([]bug.TimelineItem, error) {
+	return l.Snapshot.Timeline, nil
+}
+
+func (l *loadedBug) Operations() ([]bug.Operation, error) {
+	return l.Snapshot.Operations, nil
+}
diff --git a/graphql/models/lazy_identity.go b/graphql/models/lazy_identity.go
new file mode 100644
index 0000000000000000000000000000000000000000..bbd36be31a73cc0cc38fb2d25ca940a4eaf0c91f
--- /dev/null
+++ b/graphql/models/lazy_identity.go
@@ -0,0 +1,167 @@
+package models
+
+import (
+	"fmt"
+	"sync"
+
+	"github.com/MichaelMure/git-bug/cache"
+	"github.com/MichaelMure/git-bug/entity"
+	"github.com/MichaelMure/git-bug/identity"
+	"github.com/MichaelMure/git-bug/util/lamport"
+	"github.com/MichaelMure/git-bug/util/timestamp"
+)
+
+// IdentityWrapper is an interface used by the GraphQL resolvers to handle an identity.
+// Depending on the situation, an Identity can already be fully loaded in memory or not.
+// This interface is used to wrap either a lazyIdentity or a loadedIdentity depending on the situation.
+type IdentityWrapper interface {
+	Id() entity.Id
+	Name() string
+	Email() (string, error)
+	AvatarUrl() (string, error)
+	Keys() ([]*identity.Key, error)
+	ValidKeysAtTime(time lamport.Time) ([]*identity.Key, error)
+	DisplayName() string
+	IsProtected() (bool, error)
+	LastModificationLamport() (lamport.Time, error)
+	LastModification() (timestamp.Timestamp, error)
+}
+
+var _ IdentityWrapper = &lazyIdentity{}
+
+type lazyIdentity struct {
+	cache   *cache.RepoCache
+	excerpt *cache.IdentityExcerpt
+
+	mu sync.Mutex
+	id *cache.IdentityCache
+}
+
+func NewLazyIdentity(cache *cache.RepoCache, excerpt *cache.IdentityExcerpt) *lazyIdentity {
+	return &lazyIdentity{
+		cache:   cache,
+		excerpt: excerpt,
+	}
+}
+
+func (li *lazyIdentity) load() (*cache.IdentityCache, error) {
+	if li.id != nil {
+		return li.id, nil
+	}
+
+	li.mu.Lock()
+	defer li.mu.Unlock()
+
+	id, err := li.cache.ResolveIdentity(li.excerpt.Id)
+	if err != nil {
+		return nil, fmt.Errorf("cache: missing identity %v", li.excerpt.Id)
+	}
+	li.id = id
+	return id, nil
+}
+
+func (li *lazyIdentity) Id() entity.Id {
+	return li.excerpt.Id
+}
+
+func (li *lazyIdentity) Name() string {
+	return li.excerpt.Name
+}
+
+func (li *lazyIdentity) Email() (string, error) {
+	id, err := li.load()
+	if err != nil {
+		return "", err
+	}
+	return id.Email(), nil
+}
+
+func (li *lazyIdentity) AvatarUrl() (string, error) {
+	id, err := li.load()
+	if err != nil {
+		return "", err
+	}
+	return id.AvatarUrl(), nil
+}
+
+func (li *lazyIdentity) Keys() ([]*identity.Key, error) {
+	id, err := li.load()
+	if err != nil {
+		return nil, err
+	}
+	return id.Keys(), nil
+}
+
+func (li *lazyIdentity) ValidKeysAtTime(time lamport.Time) ([]*identity.Key, error) {
+	id, err := li.load()
+	if err != nil {
+		return nil, err
+	}
+	return id.ValidKeysAtTime(time), nil
+}
+
+func (li *lazyIdentity) DisplayName() string {
+	return li.excerpt.DisplayName()
+}
+
+func (li *lazyIdentity) IsProtected() (bool, error) {
+	id, err := li.load()
+	if err != nil {
+		return false, err
+	}
+	return id.IsProtected(), nil
+}
+
+func (li *lazyIdentity) LastModificationLamport() (lamport.Time, error) {
+	id, err := li.load()
+	if err != nil {
+		return 0, err
+	}
+	return id.LastModificationLamport(), nil
+}
+
+func (li *lazyIdentity) LastModification() (timestamp.Timestamp, error) {
+	id, err := li.load()
+	if err != nil {
+		return 0, err
+	}
+	return id.LastModification(), nil
+}
+
+var _ IdentityWrapper = &loadedIdentity{}
+
+type loadedIdentity struct {
+	identity.Interface
+}
+
+func NewLoadedIdentity(id identity.Interface) *loadedIdentity {
+	return &loadedIdentity{Interface: id}
+}
+
+func (l loadedIdentity) Email() (string, error) {
+	return l.Interface.Email(), nil
+}
+
+func (l loadedIdentity) AvatarUrl() (string, error) {
+	return l.Interface.AvatarUrl(), nil
+}
+
+func (l loadedIdentity) Keys() ([]*identity.Key, error) {
+	return l.Interface.Keys(), nil
+}
+
+func (l loadedIdentity) ValidKeysAtTime(time lamport.Time) ([]*identity.Key, error) {
+	return l.Interface.ValidKeysAtTime(time), nil
+}
+
+func (l loadedIdentity) IsProtected() (bool, error) {
+	return l.Interface.IsProtected(), nil
+}
+
+func (l loadedIdentity) LastModificationLamport() (lamport.Time, error) {
+	return l.Interface.LastModificationLamport(), nil
+}
+
+func (l loadedIdentity) LastModification() (timestamp.Timestamp, error) {
+	return l.Interface.LastModification(), nil
+}
diff --git a/graphql/resolvers/bug.go b/graphql/resolvers/bug.go
index 8f994c0b5ae8bac1fcfe9d73cb2386780c0d3ded..fd8f4b6ea61bd6ab033070f4dc7d4a9fce88e4cb 100644
--- a/graphql/resolvers/bug.go
+++ b/graphql/resolvers/bug.go
@@ -2,32 +2,30 @@ package resolvers
 
 import (
 	"context"
-	"time"
 
 	"github.com/MichaelMure/git-bug/bug"
 	"github.com/MichaelMure/git-bug/graphql/connections"
 	"github.com/MichaelMure/git-bug/graphql/graph"
 	"github.com/MichaelMure/git-bug/graphql/models"
-	"github.com/MichaelMure/git-bug/identity"
 )
 
 var _ graph.BugResolver = &bugResolver{}
 
 type bugResolver struct{}
 
-func (bugResolver) ID(ctx context.Context, obj *bug.Snapshot) (string, error) {
+func (bugResolver) ID(_ context.Context, obj models.BugWrapper) (string, error) {
 	return obj.Id().String(), nil
 }
 
-func (bugResolver) HumanID(ctx context.Context, obj *bug.Snapshot) (string, error) {
+func (bugResolver) HumanID(_ context.Context, obj models.BugWrapper) (string, error) {
 	return obj.Id().Human(), nil
 }
 
-func (bugResolver) Status(ctx context.Context, obj *bug.Snapshot) (models.Status, error) {
-	return convertStatus(obj.Status)
+func (bugResolver) Status(_ context.Context, obj models.BugWrapper) (models.Status, error) {
+	return convertStatus(obj.Status())
 }
 
-func (bugResolver) Comments(ctx context.Context, obj *bug.Snapshot, after *string, before *string, first *int, last *int) (*models.CommentConnection, error) {
+func (bugResolver) Comments(_ context.Context, obj models.BugWrapper, after *string, before *string, first *int, last *int) (*models.CommentConnection, error) {
 	input := models.ConnectionInput{
 		Before: before,
 		After:  after,
@@ -55,10 +53,15 @@ func (bugResolver) Comments(ctx context.Context, obj *bug.Snapshot, after *strin
 		}, nil
 	}
 
-	return connections.CommentCon(obj.Comments, edger, conMaker, input)
+	comments, err := obj.Comments()
+	if err != nil {
+		return nil, err
+	}
+
+	return connections.CommentCon(comments, edger, conMaker, input)
 }
 
-func (bugResolver) Operations(ctx context.Context, obj *bug.Snapshot, after *string, before *string, first *int, last *int) (*models.OperationConnection, error) {
+func (bugResolver) Operations(_ context.Context, obj models.BugWrapper, after *string, before *string, first *int, last *int) (*models.OperationConnection, error) {
 	input := models.ConnectionInput{
 		Before: before,
 		After:  after,
@@ -82,10 +85,15 @@ func (bugResolver) Operations(ctx context.Context, obj *bug.Snapshot, after *str
 		}, nil
 	}
 
-	return connections.OperationCon(obj.Operations, edger, conMaker, input)
+	ops, err := obj.Operations()
+	if err != nil {
+		return nil, err
+	}
+
+	return connections.OperationCon(ops, edger, conMaker, input)
 }
 
-func (bugResolver) Timeline(ctx context.Context, obj *bug.Snapshot, after *string, before *string, first *int, last *int) (*models.TimelineItemConnection, error) {
+func (bugResolver) Timeline(_ context.Context, obj models.BugWrapper, after *string, before *string, first *int, last *int) (*models.TimelineItemConnection, error) {
 	input := models.ConnectionInput{
 		Before: before,
 		After:  after,
@@ -109,15 +117,15 @@ func (bugResolver) Timeline(ctx context.Context, obj *bug.Snapshot, after *strin
 		}, nil
 	}
 
-	return connections.TimelineItemCon(obj.Timeline, edger, conMaker, input)
-}
+	timeline, err := obj.Timeline()
+	if err != nil {
+		return nil, err
+	}
 
-func (bugResolver) LastEdit(ctx context.Context, obj *bug.Snapshot) (*time.Time, error) {
-	t := obj.LastEditTime()
-	return &t, nil
+	return connections.TimelineItemCon(timeline, edger, conMaker, input)
 }
 
-func (bugResolver) Actors(ctx context.Context, obj *bug.Snapshot, after *string, before *string, first *int, last *int) (*models.IdentityConnection, error) {
+func (bugResolver) Actors(_ context.Context, obj models.BugWrapper, after *string, before *string, first *int, last *int) (*models.IdentityConnection, error) {
 	input := models.ConnectionInput{
 		Before: before,
 		After:  after,
@@ -125,14 +133,14 @@ func (bugResolver) Actors(ctx context.Context, obj *bug.Snapshot, after *string,
 		Last:   last,
 	}
 
-	edger := func(actor identity.Interface, offset int) connections.Edge {
+	edger := func(actor models.IdentityWrapper, offset int) connections.Edge {
 		return models.IdentityEdge{
 			Node:   actor,
 			Cursor: connections.OffsetToCursor(offset),
 		}
 	}
 
-	conMaker := func(edges []*models.IdentityEdge, nodes []identity.Interface, info *models.PageInfo, totalCount int) (*models.IdentityConnection, error) {
+	conMaker := func(edges []*models.IdentityEdge, nodes []models.IdentityWrapper, info *models.PageInfo, totalCount int) (*models.IdentityConnection, error) {
 		return &models.IdentityConnection{
 			Edges:      edges,
 			Nodes:      nodes,
@@ -141,10 +149,15 @@ func (bugResolver) Actors(ctx context.Context, obj *bug.Snapshot, after *string,
 		}, nil
 	}
 
-	return connections.IdentityCon(obj.Actors, edger, conMaker, input)
+	actors, err := obj.Actors()
+	if err != nil {
+		return nil, err
+	}
+
+	return connections.IdentityCon(actors, edger, conMaker, input)
 }
 
-func (bugResolver) Participants(ctx context.Context, obj *bug.Snapshot, after *string, before *string, first *int, last *int) (*models.IdentityConnection, error) {
+func (bugResolver) Participants(_ context.Context, obj models.BugWrapper, after *string, before *string, first *int, last *int) (*models.IdentityConnection, error) {
 	input := models.ConnectionInput{
 		Before: before,
 		After:  after,
@@ -152,14 +165,14 @@ func (bugResolver) Participants(ctx context.Context, obj *bug.Snapshot, after *s
 		Last:   last,
 	}
 
-	edger := func(participant identity.Interface, offset int) connections.Edge {
+	edger := func(participant models.IdentityWrapper, offset int) connections.Edge {
 		return models.IdentityEdge{
 			Node:   participant,
 			Cursor: connections.OffsetToCursor(offset),
 		}
 	}
 
-	conMaker := func(edges []*models.IdentityEdge, nodes []identity.Interface, info *models.PageInfo, totalCount int) (*models.IdentityConnection, error) {
+	conMaker := func(edges []*models.IdentityEdge, nodes []models.IdentityWrapper, info *models.PageInfo, totalCount int) (*models.IdentityConnection, error) {
 		return &models.IdentityConnection{
 			Edges:      edges,
 			Nodes:      nodes,
@@ -168,5 +181,10 @@ func (bugResolver) Participants(ctx context.Context, obj *bug.Snapshot, after *s
 		}, nil
 	}
 
-	return connections.IdentityCon(obj.Participants, edger, conMaker, input)
+	participants, err := obj.Participants()
+	if err != nil {
+		return nil, err
+	}
+
+	return connections.IdentityCon(participants, edger, conMaker, input)
 }
diff --git a/graphql/resolvers/color.go b/graphql/resolvers/color.go
index dc6f1b9cbac1eea3e3064e2ad79d971cf0cfe875..8dc1309536def0b29a0658eec6512be985a1a461 100644
--- a/graphql/resolvers/color.go
+++ b/graphql/resolvers/color.go
@@ -11,14 +11,14 @@ var _ graph.ColorResolver = &colorResolver{}
 
 type colorResolver struct{}
 
-func (colorResolver) R(ctx context.Context, obj *color.RGBA) (int, error) {
+func (colorResolver) R(_ context.Context, obj *color.RGBA) (int, error) {
 	return int(obj.R), nil
 }
 
-func (colorResolver) G(ctx context.Context, obj *color.RGBA) (int, error) {
+func (colorResolver) G(_ context.Context, obj *color.RGBA) (int, error) {
 	return int(obj.G), nil
 }
 
-func (colorResolver) B(ctx context.Context, obj *color.RGBA) (int, error) {
+func (colorResolver) B(_ context.Context, obj *color.RGBA) (int, error) {
 	return int(obj.B), nil
 }
diff --git a/graphql/resolvers/comment.go b/graphql/resolvers/comment.go
new file mode 100644
index 0000000000000000000000000000000000000000..b142712ad82ab104f9575ed9c598b3f26ac6f2f7
--- /dev/null
+++ b/graphql/resolvers/comment.go
@@ -0,0 +1,17 @@
+package resolvers
+
+import (
+	"context"
+
+	"github.com/MichaelMure/git-bug/bug"
+	"github.com/MichaelMure/git-bug/graphql/graph"
+	"github.com/MichaelMure/git-bug/graphql/models"
+)
+
+var _ graph.CommentResolver = &commentResolver{}
+
+type commentResolver struct{}
+
+func (c commentResolver) Author(_ context.Context, obj *bug.Comment) (models.IdentityWrapper, error) {
+	return models.NewLoadedIdentity(obj.Author), nil
+}
diff --git a/graphql/resolvers/identity.go b/graphql/resolvers/identity.go
index d36669d0a3abd5a615517f2ce060dba7c7eb878f..b8aa72a7983a1041b3108c774102bea57e5124b6 100644
--- a/graphql/resolvers/identity.go
+++ b/graphql/resolvers/identity.go
@@ -4,18 +4,18 @@ import (
 	"context"
 
 	"github.com/MichaelMure/git-bug/graphql/graph"
-	"github.com/MichaelMure/git-bug/identity"
+	"github.com/MichaelMure/git-bug/graphql/models"
 )
 
 var _ graph.IdentityResolver = &identityResolver{}
 
 type identityResolver struct{}
 
-func (identityResolver) ID(ctx context.Context, obj identity.Interface) (string, error) {
+func (identityResolver) ID(ctx context.Context, obj models.IdentityWrapper) (string, error) {
 	return obj.Id().String(), nil
 }
 
-func (r identityResolver) HumanID(ctx context.Context, obj identity.Interface) (string, error) {
+func (r identityResolver) HumanID(ctx context.Context, obj models.IdentityWrapper) (string, error) {
 	return obj.Id().Human(), nil
 
 }
diff --git a/graphql/resolvers/mutation.go b/graphql/resolvers/mutation.go
index b4f8845a7c70126bfb1dfc403159e8e0b64c47f4..889edbc8fa3011c6d0fad207fee4318547352917 100644
--- a/graphql/resolvers/mutation.go
+++ b/graphql/resolvers/mutation.go
@@ -23,7 +23,7 @@ func (r mutationResolver) getRepo(ref *string) (*cache.RepoCache, error) {
 	return r.cache.DefaultRepo()
 }
 
-func (r mutationResolver) NewBug(ctx context.Context, input models.NewBugInput) (*models.NewBugPayload, error) {
+func (r mutationResolver) NewBug(_ context.Context, input models.NewBugInput) (*models.NewBugPayload, error) {
 	repo, err := r.getRepo(input.RepoRef)
 	if err != nil {
 		return nil, err
@@ -36,12 +36,12 @@ func (r mutationResolver) NewBug(ctx context.Context, input models.NewBugInput)
 
 	return &models.NewBugPayload{
 		ClientMutationID: input.ClientMutationID,
-		Bug:              b.Snapshot(),
+		Bug:              models.NewLoadedBug(b.Snapshot()),
 		Operation:        op,
 	}, nil
 }
 
-func (r mutationResolver) AddComment(ctx context.Context, input models.AddCommentInput) (*models.AddCommentPayload, error) {
+func (r mutationResolver) AddComment(_ context.Context, input models.AddCommentInput) (*models.AddCommentPayload, error) {
 	repo, err := r.getRepo(input.RepoRef)
 	if err != nil {
 		return nil, err
@@ -59,12 +59,12 @@ func (r mutationResolver) AddComment(ctx context.Context, input models.AddCommen
 
 	return &models.AddCommentPayload{
 		ClientMutationID: input.ClientMutationID,
-		Bug:              b.Snapshot(),
+		Bug:              models.NewLoadedBug(b.Snapshot()),
 		Operation:        op,
 	}, nil
 }
 
-func (r mutationResolver) ChangeLabels(ctx context.Context, input *models.ChangeLabelInput) (*models.ChangeLabelPayload, error) {
+func (r mutationResolver) ChangeLabels(_ context.Context, input *models.ChangeLabelInput) (*models.ChangeLabelPayload, error) {
 	repo, err := r.getRepo(input.RepoRef)
 	if err != nil {
 		return nil, err
@@ -87,13 +87,13 @@ func (r mutationResolver) ChangeLabels(ctx context.Context, input *models.Change
 
 	return &models.ChangeLabelPayload{
 		ClientMutationID: input.ClientMutationID,
-		Bug:              b.Snapshot(),
+		Bug:              models.NewLoadedBug(b.Snapshot()),
 		Operation:        op,
 		Results:          resultsPtr,
 	}, nil
 }
 
-func (r mutationResolver) OpenBug(ctx context.Context, input models.OpenBugInput) (*models.OpenBugPayload, error) {
+func (r mutationResolver) OpenBug(_ context.Context, input models.OpenBugInput) (*models.OpenBugPayload, error) {
 	repo, err := r.getRepo(input.RepoRef)
 	if err != nil {
 		return nil, err
@@ -111,12 +111,12 @@ func (r mutationResolver) OpenBug(ctx context.Context, input models.OpenBugInput
 
 	return &models.OpenBugPayload{
 		ClientMutationID: input.ClientMutationID,
-		Bug:              b.Snapshot(),
+		Bug:              models.NewLoadedBug(b.Snapshot()),
 		Operation:        op,
 	}, nil
 }
 
-func (r mutationResolver) CloseBug(ctx context.Context, input models.CloseBugInput) (*models.CloseBugPayload, error) {
+func (r mutationResolver) CloseBug(_ context.Context, input models.CloseBugInput) (*models.CloseBugPayload, error) {
 	repo, err := r.getRepo(input.RepoRef)
 	if err != nil {
 		return nil, err
@@ -134,12 +134,12 @@ func (r mutationResolver) CloseBug(ctx context.Context, input models.CloseBugInp
 
 	return &models.CloseBugPayload{
 		ClientMutationID: input.ClientMutationID,
-		Bug:              b.Snapshot(),
+		Bug:              models.NewLoadedBug(b.Snapshot()),
 		Operation:        op,
 	}, nil
 }
 
-func (r mutationResolver) SetTitle(ctx context.Context, input models.SetTitleInput) (*models.SetTitlePayload, error) {
+func (r mutationResolver) SetTitle(_ context.Context, input models.SetTitleInput) (*models.SetTitlePayload, error) {
 	repo, err := r.getRepo(input.RepoRef)
 	if err != nil {
 		return nil, err
@@ -157,12 +157,12 @@ func (r mutationResolver) SetTitle(ctx context.Context, input models.SetTitleInp
 
 	return &models.SetTitlePayload{
 		ClientMutationID: input.ClientMutationID,
-		Bug:              b.Snapshot(),
+		Bug:              models.NewLoadedBug(b.Snapshot()),
 		Operation:        op,
 	}, nil
 }
 
-func (r mutationResolver) Commit(ctx context.Context, input models.CommitInput) (*models.CommitPayload, error) {
+func (r mutationResolver) Commit(_ context.Context, input models.CommitInput) (*models.CommitPayload, error) {
 	repo, err := r.getRepo(input.RepoRef)
 	if err != nil {
 		return nil, err
@@ -180,11 +180,11 @@ func (r mutationResolver) Commit(ctx context.Context, input models.CommitInput)
 
 	return &models.CommitPayload{
 		ClientMutationID: input.ClientMutationID,
-		Bug:              b.Snapshot(),
+		Bug:              models.NewLoadedBug(b.Snapshot()),
 	}, nil
 }
 
-func (r mutationResolver) CommitAsNeeded(ctx context.Context, input models.CommitAsNeededInput) (*models.CommitAsNeededPayload, error) {
+func (r mutationResolver) CommitAsNeeded(_ context.Context, input models.CommitAsNeededInput) (*models.CommitAsNeededPayload, error) {
 	repo, err := r.getRepo(input.RepoRef)
 	if err != nil {
 		return nil, err
@@ -202,6 +202,6 @@ func (r mutationResolver) CommitAsNeeded(ctx context.Context, input models.Commi
 
 	return &models.CommitAsNeededPayload{
 		ClientMutationID: input.ClientMutationID,
-		Bug:              b.Snapshot(),
+		Bug:              models.NewLoadedBug(b.Snapshot()),
 	}, nil
 }
diff --git a/graphql/resolvers/operations.go b/graphql/resolvers/operations.go
index ec803c1f99e9378785aa7398847e09ffa1f7c351..29110cf39fa80f2d07593d8d746404e7ff699301 100644
--- a/graphql/resolvers/operations.go
+++ b/graphql/resolvers/operations.go
@@ -14,11 +14,15 @@ var _ graph.CreateOperationResolver = createOperationResolver{}
 
 type createOperationResolver struct{}
 
-func (createOperationResolver) ID(ctx context.Context, obj *bug.CreateOperation) (string, error) {
+func (createOperationResolver) ID(_ context.Context, obj *bug.CreateOperation) (string, error) {
 	return obj.Id().String(), nil
 }
 
-func (createOperationResolver) Date(ctx context.Context, obj *bug.CreateOperation) (*time.Time, error) {
+func (createOperationResolver) Author(_ context.Context, obj *bug.CreateOperation) (models.IdentityWrapper, error) {
+	return models.NewLoadedIdentity(obj.Author), nil
+}
+
+func (createOperationResolver) Date(_ context.Context, obj *bug.CreateOperation) (*time.Time, error) {
 	t := obj.Time()
 	return &t, nil
 }
@@ -27,11 +31,15 @@ var _ graph.AddCommentOperationResolver = addCommentOperationResolver{}
 
 type addCommentOperationResolver struct{}
 
-func (addCommentOperationResolver) ID(ctx context.Context, obj *bug.AddCommentOperation) (string, error) {
+func (addCommentOperationResolver) ID(_ context.Context, obj *bug.AddCommentOperation) (string, error) {
 	return obj.Id().String(), nil
 }
 
-func (addCommentOperationResolver) Date(ctx context.Context, obj *bug.AddCommentOperation) (*time.Time, error) {
+func (addCommentOperationResolver) Author(_ context.Context, obj *bug.AddCommentOperation) (models.IdentityWrapper, error) {
+	return models.NewLoadedIdentity(obj.Author), nil
+}
+
+func (addCommentOperationResolver) Date(_ context.Context, obj *bug.AddCommentOperation) (*time.Time, error) {
 	t := obj.Time()
 	return &t, nil
 }
@@ -40,15 +48,19 @@ var _ graph.EditCommentOperationResolver = editCommentOperationResolver{}
 
 type editCommentOperationResolver struct{}
 
-func (editCommentOperationResolver) ID(ctx context.Context, obj *bug.EditCommentOperation) (string, error) {
+func (editCommentOperationResolver) ID(_ context.Context, obj *bug.EditCommentOperation) (string, error) {
 	return obj.Id().String(), nil
 }
 
-func (editCommentOperationResolver) Target(ctx context.Context, obj *bug.EditCommentOperation) (string, error) {
+func (editCommentOperationResolver) Target(_ context.Context, obj *bug.EditCommentOperation) (string, error) {
 	return obj.Target.String(), nil
 }
 
-func (editCommentOperationResolver) Date(ctx context.Context, obj *bug.EditCommentOperation) (*time.Time, error) {
+func (editCommentOperationResolver) Author(_ context.Context, obj *bug.EditCommentOperation) (models.IdentityWrapper, error) {
+	return models.NewLoadedIdentity(obj.Author), nil
+}
+
+func (editCommentOperationResolver) Date(_ context.Context, obj *bug.EditCommentOperation) (*time.Time, error) {
 	t := obj.Time()
 	return &t, nil
 }
@@ -57,11 +69,15 @@ var _ graph.LabelChangeOperationResolver = labelChangeOperationResolver{}
 
 type labelChangeOperationResolver struct{}
 
-func (labelChangeOperationResolver) ID(ctx context.Context, obj *bug.LabelChangeOperation) (string, error) {
+func (labelChangeOperationResolver) ID(_ context.Context, obj *bug.LabelChangeOperation) (string, error) {
 	return obj.Id().String(), nil
 }
 
-func (labelChangeOperationResolver) Date(ctx context.Context, obj *bug.LabelChangeOperation) (*time.Time, error) {
+func (labelChangeOperationResolver) Author(_ context.Context, obj *bug.LabelChangeOperation) (models.IdentityWrapper, error) {
+	return models.NewLoadedIdentity(obj.Author), nil
+}
+
+func (labelChangeOperationResolver) Date(_ context.Context, obj *bug.LabelChangeOperation) (*time.Time, error) {
 	t := obj.Time()
 	return &t, nil
 }
@@ -70,16 +86,20 @@ var _ graph.SetStatusOperationResolver = setStatusOperationResolver{}
 
 type setStatusOperationResolver struct{}
 
-func (setStatusOperationResolver) ID(ctx context.Context, obj *bug.SetStatusOperation) (string, error) {
+func (setStatusOperationResolver) ID(_ context.Context, obj *bug.SetStatusOperation) (string, error) {
 	return obj.Id().String(), nil
 }
 
-func (setStatusOperationResolver) Date(ctx context.Context, obj *bug.SetStatusOperation) (*time.Time, error) {
+func (setStatusOperationResolver) Author(_ context.Context, obj *bug.SetStatusOperation) (models.IdentityWrapper, error) {
+	return models.NewLoadedIdentity(obj.Author), nil
+}
+
+func (setStatusOperationResolver) Date(_ context.Context, obj *bug.SetStatusOperation) (*time.Time, error) {
 	t := obj.Time()
 	return &t, nil
 }
 
-func (setStatusOperationResolver) Status(ctx context.Context, obj *bug.SetStatusOperation) (models.Status, error) {
+func (setStatusOperationResolver) Status(_ context.Context, obj *bug.SetStatusOperation) (models.Status, error) {
 	return convertStatus(obj.Status)
 }
 
@@ -87,11 +107,15 @@ var _ graph.SetTitleOperationResolver = setTitleOperationResolver{}
 
 type setTitleOperationResolver struct{}
 
-func (setTitleOperationResolver) ID(ctx context.Context, obj *bug.SetTitleOperation) (string, error) {
+func (setTitleOperationResolver) ID(_ context.Context, obj *bug.SetTitleOperation) (string, error) {
 	return obj.Id().String(), nil
 }
 
-func (setTitleOperationResolver) Date(ctx context.Context, obj *bug.SetTitleOperation) (*time.Time, error) {
+func (setTitleOperationResolver) Author(_ context.Context, obj *bug.SetTitleOperation) (models.IdentityWrapper, error) {
+	return models.NewLoadedIdentity(obj.Author), nil
+}
+
+func (setTitleOperationResolver) Date(_ context.Context, obj *bug.SetTitleOperation) (*time.Time, error) {
 	t := obj.Time()
 	return &t, nil
 }
diff --git a/graphql/resolvers/query.go b/graphql/resolvers/query.go
index aa4e1d85bfca89b720073973f42787d9d75e8314..9503ccf45ea424a4622898c1d7f4482b87f1924d 100644
--- a/graphql/resolvers/query.go
+++ b/graphql/resolvers/query.go
@@ -14,7 +14,7 @@ type rootQueryResolver struct {
 	cache *cache.MultiRepoCache
 }
 
-func (r rootQueryResolver) DefaultRepository(ctx context.Context) (*models.Repository, error) {
+func (r rootQueryResolver) DefaultRepository(_ context.Context) (*models.Repository, error) {
 	repo, err := r.cache.DefaultRepo()
 
 	if err != nil {
@@ -27,7 +27,7 @@ func (r rootQueryResolver) DefaultRepository(ctx context.Context) (*models.Repos
 	}, nil
 }
 
-func (r rootQueryResolver) Repository(ctx context.Context, ref string) (*models.Repository, error) {
+func (r rootQueryResolver) Repository(_ context.Context, ref string) (*models.Repository, error) {
 	repo, err := r.cache.ResolveRepo(ref)
 
 	if err != nil {
diff --git a/graphql/resolvers/repo.go b/graphql/resolvers/repo.go
index 5a37ae1875ae0844f8180ab22dbc820a98c73606..3b0aa9a10ba0007fcfbd4a0bc966635df89f8689 100644
--- a/graphql/resolvers/repo.go
+++ b/graphql/resolvers/repo.go
@@ -9,14 +9,13 @@ import (
 	"github.com/MichaelMure/git-bug/graphql/connections"
 	"github.com/MichaelMure/git-bug/graphql/graph"
 	"github.com/MichaelMure/git-bug/graphql/models"
-	"github.com/MichaelMure/git-bug/identity"
 )
 
 var _ graph.RepositoryResolver = &repoResolver{}
 
 type repoResolver struct{}
 
-func (repoResolver) AllBugs(ctx context.Context, obj *models.Repository, after *string, before *string, first *int, last *int, queryStr *string) (*models.BugConnection, error) {
+func (repoResolver) AllBugs(_ context.Context, obj *models.Repository, after *string, before *string, first *int, last *int, queryStr *string) (*models.BugConnection, error) {
 	input := models.ConnectionInput{
 		Before: before,
 		After:  after,
@@ -49,22 +48,21 @@ func (repoResolver) AllBugs(ctx context.Context, obj *models.Repository, after *
 	// The conMaker will finally load and compile bugs from git to replace the selected edges
 	conMaker := func(lazyBugEdges []*connections.LazyBugEdge, lazyNode []entity.Id, info *models.PageInfo, totalCount int) (*models.BugConnection, error) {
 		edges := make([]*models.BugEdge, len(lazyBugEdges))
-		nodes := make([]*bug.Snapshot, len(lazyBugEdges))
+		nodes := make([]models.BugWrapper, len(lazyBugEdges))
 
 		for i, lazyBugEdge := range lazyBugEdges {
-			b, err := obj.Repo.ResolveBug(lazyBugEdge.Id)
-
+			excerpt, err := obj.Repo.ResolveBugExcerpt(lazyBugEdge.Id)
 			if err != nil {
 				return nil, err
 			}
 
-			snap := b.Snapshot()
+			b := models.NewLazyBug(obj.Repo, excerpt)
 
 			edges[i] = &models.BugEdge{
 				Cursor: lazyBugEdge.Cursor,
-				Node:   snap,
+				Node:   b,
 			}
-			nodes[i] = snap
+			nodes[i] = b
 		}
 
 		return &models.BugConnection{
@@ -78,17 +76,16 @@ func (repoResolver) AllBugs(ctx context.Context, obj *models.Repository, after *
 	return connections.LazyBugCon(source, edger, conMaker, input)
 }
 
-func (repoResolver) Bug(ctx context.Context, obj *models.Repository, prefix string) (*bug.Snapshot, error) {
-	b, err := obj.Repo.ResolveBugPrefix(prefix)
-
+func (repoResolver) Bug(_ context.Context, obj *models.Repository, prefix string) (models.BugWrapper, error) {
+	excerpt, err := obj.Repo.ResolveBugExcerptPrefix(prefix)
 	if err != nil {
 		return nil, err
 	}
 
-	return b.Snapshot(), nil
+	return models.NewLazyBug(obj.Repo, excerpt), nil
 }
 
-func (repoResolver) AllIdentities(ctx context.Context, obj *models.Repository, after *string, before *string, first *int, last *int) (*models.IdentityConnection, error) {
+func (repoResolver) AllIdentities(_ context.Context, obj *models.Repository, after *string, before *string, first *int, last *int) (*models.IdentityConnection, error) {
 	input := models.ConnectionInput{
 		Before: before,
 		After:  after,
@@ -110,22 +107,21 @@ func (repoResolver) AllIdentities(ctx context.Context, obj *models.Repository, a
 	// The conMaker will finally load and compile identities from git to replace the selected edges
 	conMaker := func(lazyIdentityEdges []*connections.LazyIdentityEdge, lazyNode []entity.Id, info *models.PageInfo, totalCount int) (*models.IdentityConnection, error) {
 		edges := make([]*models.IdentityEdge, len(lazyIdentityEdges))
-		nodes := make([]identity.Interface, len(lazyIdentityEdges))
+		nodes := make([]models.IdentityWrapper, len(lazyIdentityEdges))
 
 		for k, lazyIdentityEdge := range lazyIdentityEdges {
-			i, err := obj.Repo.ResolveIdentity(lazyIdentityEdge.Id)
-
+			excerpt, err := obj.Repo.ResolveIdentityExcerpt(lazyIdentityEdge.Id)
 			if err != nil {
 				return nil, err
 			}
 
-			ii := identity.Interface(i.Identity)
+			i := models.NewLazyIdentity(obj.Repo, excerpt)
 
 			edges[k] = &models.IdentityEdge{
 				Cursor: lazyIdentityEdge.Cursor,
-				Node:   i.Identity,
+				Node:   i,
 			}
-			nodes[k] = ii
+			nodes[k] = i
 		}
 
 		return &models.IdentityConnection{
@@ -139,27 +135,25 @@ func (repoResolver) AllIdentities(ctx context.Context, obj *models.Repository, a
 	return connections.LazyIdentityCon(source, edger, conMaker, input)
 }
 
-func (repoResolver) Identity(ctx context.Context, obj *models.Repository, prefix string) (identity.Interface, error) {
-	i, err := obj.Repo.ResolveIdentityPrefix(prefix)
-
+func (repoResolver) Identity(_ context.Context, obj *models.Repository, prefix string) (models.IdentityWrapper, error) {
+	excerpt, err := obj.Repo.ResolveIdentityExcerptPrefix(prefix)
 	if err != nil {
 		return nil, err
 	}
 
-	return i.Identity, nil
+	return models.NewLazyIdentity(obj.Repo, excerpt), nil
 }
 
-func (repoResolver) UserIdentity(ctx context.Context, obj *models.Repository) (identity.Interface, error) {
-	i, err := obj.Repo.GetUserIdentity()
-
+func (repoResolver) UserIdentity(_ context.Context, obj *models.Repository) (models.IdentityWrapper, error) {
+	excerpt, err := obj.Repo.GetUserIdentityExcerpt()
 	if err != nil {
 		return nil, err
 	}
 
-	return i.Identity, nil
+	return models.NewLazyIdentity(obj.Repo, excerpt), nil
 }
 
-func (resolver repoResolver) ValidLabels(ctx context.Context, obj *models.Repository, after *string, before *string, first *int, last *int) (*models.LabelConnection, error) {
+func (resolver repoResolver) ValidLabels(_ context.Context, obj *models.Repository, after *string, before *string, first *int, last *int) (*models.LabelConnection, error) {
 	input := models.ConnectionInput{
 		Before: before,
 		After:  after,
diff --git a/graphql/resolvers/root.go b/graphql/resolvers/root.go
index 3b129f3b97646f2d1f5abc3597f793f5b662fa25..9973ff59afe83cb4f7d0a1b9387a66afa16d127c 100644
--- a/graphql/resolvers/root.go
+++ b/graphql/resolvers/root.go
@@ -42,6 +42,10 @@ func (RootResolver) Color() graph.ColorResolver {
 	return &colorResolver{}
 }
 
+func (r RootResolver) Comment() graph.CommentResolver {
+	return &commentResolver{}
+}
+
 func (RootResolver) Label() graph.LabelResolver {
 	return &labelResolver{}
 }
diff --git a/graphql/resolvers/timeline.go b/graphql/resolvers/timeline.go
index b206f89814125189d3c4cd93063c0a91ee07ba56..acf236f8ada7df9f50efb425eea7ad1922db1ea8 100644
--- a/graphql/resolvers/timeline.go
+++ b/graphql/resolvers/timeline.go
@@ -13,7 +13,7 @@ var _ graph.CommentHistoryStepResolver = commentHistoryStepResolver{}
 
 type commentHistoryStepResolver struct{}
 
-func (commentHistoryStepResolver) Date(ctx context.Context, obj *bug.CommentHistoryStep) (*time.Time, error) {
+func (commentHistoryStepResolver) Date(_ context.Context, obj *bug.CommentHistoryStep) (*time.Time, error) {
 	t := obj.UnixTime.Time()
 	return &t, nil
 }
@@ -22,16 +22,20 @@ var _ graph.AddCommentTimelineItemResolver = addCommentTimelineItemResolver{}
 
 type addCommentTimelineItemResolver struct{}
 
-func (addCommentTimelineItemResolver) ID(ctx context.Context, obj *bug.AddCommentTimelineItem) (string, error) {
+func (addCommentTimelineItemResolver) ID(_ context.Context, obj *bug.AddCommentTimelineItem) (string, error) {
 	return obj.Id().String(), nil
 }
 
-func (addCommentTimelineItemResolver) CreatedAt(ctx context.Context, obj *bug.AddCommentTimelineItem) (*time.Time, error) {
+func (addCommentTimelineItemResolver) Author(_ context.Context, obj *bug.AddCommentTimelineItem) (models.IdentityWrapper, error) {
+	return models.NewLoadedIdentity(obj.Author), nil
+}
+
+func (addCommentTimelineItemResolver) CreatedAt(_ context.Context, obj *bug.AddCommentTimelineItem) (*time.Time, error) {
 	t := obj.CreatedAt.Time()
 	return &t, nil
 }
 
-func (addCommentTimelineItemResolver) LastEdit(ctx context.Context, obj *bug.AddCommentTimelineItem) (*time.Time, error) {
+func (addCommentTimelineItemResolver) LastEdit(_ context.Context, obj *bug.AddCommentTimelineItem) (*time.Time, error) {
 	t := obj.LastEdit.Time()
 	return &t, nil
 }
@@ -40,16 +44,20 @@ var _ graph.CreateTimelineItemResolver = createTimelineItemResolver{}
 
 type createTimelineItemResolver struct{}
 
-func (createTimelineItemResolver) ID(ctx context.Context, obj *bug.CreateTimelineItem) (string, error) {
+func (createTimelineItemResolver) ID(_ context.Context, obj *bug.CreateTimelineItem) (string, error) {
 	return obj.Id().String(), nil
 }
 
-func (createTimelineItemResolver) CreatedAt(ctx context.Context, obj *bug.CreateTimelineItem) (*time.Time, error) {
+func (r createTimelineItemResolver) Author(_ context.Context, obj *bug.CreateTimelineItem) (models.IdentityWrapper, error) {
+	return models.NewLoadedIdentity(obj.Author), nil
+}
+
+func (createTimelineItemResolver) CreatedAt(_ context.Context, obj *bug.CreateTimelineItem) (*time.Time, error) {
 	t := obj.CreatedAt.Time()
 	return &t, nil
 }
 
-func (createTimelineItemResolver) LastEdit(ctx context.Context, obj *bug.CreateTimelineItem) (*time.Time, error) {
+func (createTimelineItemResolver) LastEdit(_ context.Context, obj *bug.CreateTimelineItem) (*time.Time, error) {
 	t := obj.LastEdit.Time()
 	return &t, nil
 }
@@ -58,11 +66,15 @@ var _ graph.LabelChangeTimelineItemResolver = labelChangeTimelineItem{}
 
 type labelChangeTimelineItem struct{}
 
-func (labelChangeTimelineItem) ID(ctx context.Context, obj *bug.LabelChangeTimelineItem) (string, error) {
+func (labelChangeTimelineItem) ID(_ context.Context, obj *bug.LabelChangeTimelineItem) (string, error) {
 	return obj.Id().String(), nil
 }
 
-func (labelChangeTimelineItem) Date(ctx context.Context, obj *bug.LabelChangeTimelineItem) (*time.Time, error) {
+func (i labelChangeTimelineItem) Author(_ context.Context, obj *bug.LabelChangeTimelineItem) (models.IdentityWrapper, error) {
+	return models.NewLoadedIdentity(obj.Author), nil
+}
+
+func (labelChangeTimelineItem) Date(_ context.Context, obj *bug.LabelChangeTimelineItem) (*time.Time, error) {
 	t := obj.UnixTime.Time()
 	return &t, nil
 }
@@ -71,16 +83,20 @@ var _ graph.SetStatusTimelineItemResolver = setStatusTimelineItem{}
 
 type setStatusTimelineItem struct{}
 
-func (setStatusTimelineItem) ID(ctx context.Context, obj *bug.SetStatusTimelineItem) (string, error) {
+func (setStatusTimelineItem) ID(_ context.Context, obj *bug.SetStatusTimelineItem) (string, error) {
 	return obj.Id().String(), nil
 }
 
-func (setStatusTimelineItem) Date(ctx context.Context, obj *bug.SetStatusTimelineItem) (*time.Time, error) {
+func (i setStatusTimelineItem) Author(_ context.Context, obj *bug.SetStatusTimelineItem) (models.IdentityWrapper, error) {
+	return models.NewLoadedIdentity(obj.Author), nil
+}
+
+func (setStatusTimelineItem) Date(_ context.Context, obj *bug.SetStatusTimelineItem) (*time.Time, error) {
 	t := obj.UnixTime.Time()
 	return &t, nil
 }
 
-func (setStatusTimelineItem) Status(ctx context.Context, obj *bug.SetStatusTimelineItem) (models.Status, error) {
+func (setStatusTimelineItem) Status(_ context.Context, obj *bug.SetStatusTimelineItem) (models.Status, error) {
 	return convertStatus(obj.Status)
 }
 
@@ -88,11 +104,15 @@ var _ graph.SetTitleTimelineItemResolver = setTitleTimelineItem{}
 
 type setTitleTimelineItem struct{}
 
-func (setTitleTimelineItem) ID(ctx context.Context, obj *bug.SetTitleTimelineItem) (string, error) {
+func (setTitleTimelineItem) ID(_ context.Context, obj *bug.SetTitleTimelineItem) (string, error) {
 	return obj.Id().String(), nil
 }
 
-func (setTitleTimelineItem) Date(ctx context.Context, obj *bug.SetTitleTimelineItem) (*time.Time, error) {
+func (i setTitleTimelineItem) Author(_ context.Context, obj *bug.SetTitleTimelineItem) (models.IdentityWrapper, error) {
+	return models.NewLoadedIdentity(obj.Author), nil
+}
+
+func (setTitleTimelineItem) Date(_ context.Context, obj *bug.SetTitleTimelineItem) (*time.Time, error) {
 	t := obj.UnixTime.Time()
 	return &t, nil
 }
diff --git a/identity/identity.go b/identity/identity.go
index c33a8818b3fd2ea1e189f26c1e11ed7f906d18fd..b5222a3e9fc6cf64cc3c91ef09ec4308875f1580 100644
--- a/identity/identity.go
+++ b/identity/identity.go
@@ -227,17 +227,35 @@ func SetUserIdentity(repo repository.RepoConfig, identity *Identity) error {
 
 // GetUserIdentity read the current user identity, set with a git config entry
 func GetUserIdentity(repo repository.Repo) (*Identity, error) {
-	configs, err := repo.LocalConfig().ReadAll(identityConfigKey)
+	id, err := GetUserIdentityId(repo)
 	if err != nil {
 		return nil, err
 	}
 
+	i, err := ReadLocal(repo, id)
+	if err == ErrIdentityNotExist {
+		innerErr := repo.LocalConfig().RemoveAll(identityConfigKey)
+		if innerErr != nil {
+			_, _ = fmt.Fprintln(os.Stderr, errors.Wrap(innerErr, "can't clear user identity").Error())
+		}
+		return nil, err
+	}
+
+	return i, nil
+}
+
+func GetUserIdentityId(repo repository.Repo) (entity.Id, error) {
+	configs, err := repo.LocalConfig().ReadAll(identityConfigKey)
+	if err != nil {
+		return entity.UnsetId, err
+	}
+
 	if len(configs) == 0 {
-		return nil, ErrNoIdentitySet
+		return entity.UnsetId, ErrNoIdentitySet
 	}
 
 	if len(configs) > 1 {
-		return nil, ErrMultipleIdentitiesSet
+		return entity.UnsetId, ErrMultipleIdentitiesSet
 	}
 
 	var id entity.Id
@@ -246,19 +264,10 @@ func GetUserIdentity(repo repository.Repo) (*Identity, error) {
 	}
 
 	if err := id.Validate(); err != nil {
-		return nil, err
-	}
-
-	i, err := ReadLocal(repo, id)
-	if err == ErrIdentityNotExist {
-		innerErr := repo.LocalConfig().RemoveAll(identityConfigKey)
-		if innerErr != nil {
-			_, _ = fmt.Fprintln(os.Stderr, errors.Wrap(innerErr, "can't clear user identity").Error())
-		}
-		return nil, err
+		return entity.UnsetId, err
 	}
 
-	return i, nil
+	return id, nil
 }
 
 // IsUserIdentitySet say if the user has set his identity