@@ -53,7 +53,7 @@ type Store interface {
5353 UpsertProject (ctx context.Context , row domain.ProjectRecord ) error
5454 ListPRsBySession (ctx context.Context , sessionID domain.SessionID ) ([]domain.PullRequest , error )
5555 ListChecks (ctx context.Context , prURL string ) ([]domain.PullRequestCheck , error )
56- WriteSCMObservation (ctx context.Context , pr domain.PullRequest , checks []domain.PullRequestCheck , threads []domain.PullRequestReviewThread , comments []domain.PullRequestComment , reviewMode ports.ReviewWriteMode ) error
56+ WriteSCMObservation (ctx context.Context , pr domain.PullRequest , checks []domain.PullRequestCheck , reviews []domain. PullRequestReview , threads []domain.PullRequestReviewThread , comments []domain.PullRequestComment , reviewMode ports.ReviewWriteMode ) error
5757}
5858
5959// Lifecycle is the provider-neutral lifecycle notification sink.
@@ -344,8 +344,8 @@ func (o *Observer) Poll(ctx context.Context) error {
344344 prRefreshOK [key ] = true
345345 continue
346346 }
347- finalPR , finalChecks , finalThreads , finalComments := domainFromObservation (subj .session .ID , prepared , local , opts , now )
348- pr , checks , threads , comments := finalPR , finalChecks , finalThreads , finalComments
347+ finalPR , finalChecks , finalReviews , finalThreads , finalComments := domainFromObservation (subj .session .ID , prepared , local , opts , now )
348+ pr , checks , reviews , threads , comments := finalPR , finalChecks , finalReviews , finalThreads , finalComments
349349 // Lifecycle is allowed to run only after the observed facts are durable,
350350 // but semantic hashes are the observer's acknowledgement cursor. Keep
351351 // changed hashes at their local values until lifecycle succeeds; if the
@@ -362,9 +362,9 @@ func (o *Observer) Poll(ctx context.Context) error {
362362 if prepared .Changed .Review {
363363 pendingOpts .preserveLocalReviewHash = true
364364 }
365- pr , checks , threads , comments = domainFromObservation (subj .session .ID , prepared , local , pendingOpts , now )
365+ pr , checks , reviews , threads , comments = domainFromObservation (subj .session .ID , prepared , local , pendingOpts , now )
366366 }
367- if err := o .store .WriteSCMObservation (ctx , pr , checks , threads , comments , reviewMode ); err != nil {
367+ if err := o .store .WriteSCMObservation (ctx , pr , checks , reviews , threads , comments , reviewMode ); err != nil {
368368 o .logger .Error ("scm observer: DB write failed" , "session" , subj .session .ID , "pr" , pr .URL , "err" , err )
369369 markRepoRefreshFailed (subj .repo )
370370 continue
@@ -375,7 +375,7 @@ func (o *Observer) Poll(ctx context.Context) error {
375375 markRepoRefreshFailed (subj .repo )
376376 continue
377377 }
378- if err := o .store .WriteSCMObservation (ctx , finalPR , finalChecks , nil , nil , ports .ReviewWritePreserve ); err != nil {
378+ if err := o .store .WriteSCMObservation (ctx , finalPR , finalChecks , nil , nil , nil , ports .ReviewWritePreserve ); err != nil {
379379 o .logger .Error ("scm observer: DB lifecycle acknowledgement failed" , "session" , subj .session .ID , "pr" , finalPR .URL , "err" , err )
380380 markRepoRefreshFailed (subj .repo )
381381 continue
@@ -586,7 +586,7 @@ func (o *Observer) discoverNewPRs(ctx context.Context, sessionRepos []sessionRep
586586 // reads every PR of the session from the store. Without this write, an
587587 // open sibling/child discovered in the same poll would not yet be
588588 // durable, and the session could terminate while that PR is still open.
589- if err := o .store .WriteSCMObservation (ctx , known , nil , nil , nil , ports .ReviewWritePreserve ); err != nil {
589+ if err := o .store .WriteSCMObservation (ctx , known , nil , nil , nil , nil , ports .ReviewWritePreserve ); err != nil {
590590 o .logger .Error ("scm observer: persist discovered PR failed" , "session" , sr .session .ID , "pr" , known .URL , "err" , err )
591591 if markRepoFailed != nil {
592592 markRepoFailed (repo )
@@ -793,6 +793,7 @@ func (o *Observer) refreshReviews(ctx context.Context, subjects map[string]*subj
793793 if review .Decision != "" {
794794 obs .Review .Decision = review .Decision
795795 }
796+ obs .Review .Reviews = review .Reviews
796797 obs .Review .Threads = review .Threads
797798 obs .Review .Partial = review .Partial
798799 obs .ObservedAt = now
@@ -850,7 +851,7 @@ func (o *Observer) prepareForPersistence(obs ports.SCMObservation, local domain.
850851 return obs
851852}
852853
853- func domainFromObservation (sessionID domain.SessionID , obs ports.SCMObservation , local domain.PullRequest , opts persistenceOptions , now time.Time ) (domain.PullRequest , []domain.PullRequestCheck , []domain.PullRequestReviewThread , []domain.PullRequestComment ) {
854+ func domainFromObservation (sessionID domain.SessionID , obs ports.SCMObservation , local domain.PullRequest , opts persistenceOptions , now time.Time ) (domain.PullRequest , []domain.PullRequestCheck , []domain.PullRequestReview , []domain. PullRequestReviewThread , []domain.PullRequestComment ) {
854855 metadataHash := metadataSemanticHash (obs )
855856 if opts .preserveLocalMetadataHash {
856857 metadataHash = local .MetadataHash
@@ -924,6 +925,17 @@ func domainFromObservation(sessionID domain.SessionID, obs ports.SCMObservation,
924925 for _ , ch := range obs .CI .Checks {
925926 checks = append (checks , domain.PullRequestCheck {Name : ch .Name , CommitHash : obs .CI .HeadSHA , Status : domain .PRCheckStatus (ch .Status ), Conclusion : ch .Conclusion , URL : ch .URL , Details : ch .ProviderID , LogTail : ch .LogTail , CreatedAt : now })
926927 }
928+ reviews := make ([]domain.PullRequestReview , 0 , len (obs .Review .Reviews ))
929+ for _ , review := range obs .Review .Reviews {
930+ reviews = append (reviews , domain.PullRequestReview {
931+ ID : review .ID ,
932+ Author : review .Author ,
933+ State : domain .ReviewDecision (firstNonEmpty (review .State , string (domain .ReviewNone ))),
934+ URL : review .URL ,
935+ IsBot : review .IsBot ,
936+ SubmittedAt : firstTime (review .SubmittedAt , now ),
937+ })
938+ }
927939 threads := make ([]domain.PullRequestReviewThread , 0 , len (obs .Review .Threads ))
928940 commentCount := 0
929941 for _ , th := range obs .Review .Threads {
@@ -936,7 +948,7 @@ func domainFromObservation(sessionID domain.SessionID, obs ports.SCMObservation,
936948 comments = append (comments , domain.PullRequestComment {ThreadID : th .ID , ID : c .ID , Author : c .Author , File : th .Path , Line : th .Line , Body : c .Body , URL : c .URL , Resolved : th .Resolved , IsBot : c .IsBot || th .IsBot , CreatedAt : now })
937949 }
938950 }
939- return pr , checks , threads , comments
951+ return pr , checks , reviews , threads , comments
940952}
941953
942954func observationFromLocal (repo ports.SCMRepo , pr domain.PullRequest , checks []domain.PullRequestCheck ) ports.SCMObservation {
@@ -1119,10 +1131,11 @@ func ciSemanticHash(ci ports.SCMCIObservation) string {
11191131func reviewSemanticHash (review ports.SCMReviewObservation ) string {
11201132 type reviewHashPayload struct {
11211133 Decision string
1134+ Reviews []ports.SCMReviewSummaryObservation
11221135 Threads []ports.SCMReviewThreadObservation
11231136 Partial bool `json:",omitempty"`
11241137 }
1125- return stableHash (reviewHashPayload {Decision : review .Decision , Threads : review .Threads , Partial : review .Partial })
1138+ return stableHash (reviewHashPayload {Decision : review .Decision , Reviews : review . Reviews , Threads : review .Threads , Partial : review .Partial })
11261139}
11271140
11281141func threadSemanticHash (th ports.SCMReviewThreadObservation ) string {
0 commit comments