Skip to content

Commit 9ed433f

Browse files
committed
fix(mcp): treat stale thread coverage as unknown
1 parent 0386ea3 commit 9ed433f

2 files changed

Lines changed: 19 additions & 8 deletions

File tree

‎internal/app/mcp_recovery_test.go‎

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,16 +80,24 @@ func assertRelatedWorkRecovery(t *testing.T, output mcpcontract.CheckOutput, kin
8080
}
8181

8282
func TestMCPRelatedWorkPreservesUnknownThreadCoverage(t *testing.T) {
83-
for _, coverage := range []string{"missing", "incomplete", "complete"} {
83+
for _, coverage := range []string{"missing", "incomplete", "stale", "equal", "newer"} {
8484
t.Run(coverage, func(t *testing.T) {
8585
ctx := context.Background()
8686
svc := newSearchTestService(t)
87-
repo, err := svc.corpus.UpsertRepository(ctx, corpus.Repository{Owner: "owner", Name: "repo"}, `{}`)
87+
observedAt := time.Date(2026, time.September, 8, 12, 0, 0, 0, time.UTC)
88+
repo, err := svc.corpus.UpsertRepository(ctx, corpus.Repository{Owner: "owner", Name: "repo", SourceUpdatedAt: observedAt}, `{}`)
8889
if err != nil {
8990
t.Fatal(err)
9091
}
9192
if coverage != "missing" {
92-
if err := svc.corpus.AdvanceFacet(ctx, repo.ID, nil, "threads", time.Now(), coverage == "complete", 0); err != nil {
93+
coverageAt := observedAt
94+
switch coverage {
95+
case "stale":
96+
coverageAt = observedAt.Add(-time.Second)
97+
case "newer":
98+
coverageAt = observedAt.Add(time.Second)
99+
}
100+
if err := svc.corpus.AdvanceFacet(ctx, repo.ID, nil, "threads", coverageAt, coverage != "incomplete", 0); err != nil {
93101
t.Fatal(err)
94102
}
95103
}
@@ -104,13 +112,16 @@ func TestMCPRelatedWorkPreservesUnknownThreadCoverage(t *testing.T) {
104112
for _, tool := range []string{"corpus.find_duplicates", "corpus.find_competing_pull_requests"} {
105113
session := connectAuditMCP(t, svc)
106114
out := callMCPTool[mcpcontract.CheckOutput](ctx, t, session, tool, map[string]any{"target": "hypothesis", "id": hypothesis.ID, "limit": 10})
107-
if coverage == "complete" {
108-
if out.Coverage != "complete" {
115+
if out.Total != 0 || len(out.Findings) != 0 {
116+
t.Fatalf("expected empty findings: %+v", out)
117+
}
118+
if coverage == "equal" || coverage == "newer" {
119+
if out.Coverage != "complete" || out.Status != "complete" || out.Recovery != nil {
109120
t.Fatalf("complete corpus: %+v", out)
110121
}
111122
continue
112123
}
113-
if out.Coverage == "complete" || out.Status == "complete" || out.Recovery == nil {
124+
if out.Coverage != "unknown" || out.Status != "partial" || out.Recovery == nil || len(out.Recovery.Then) != 1 || out.Recovery.Then[0].Type() != "sync_threads" {
114125
t.Fatalf("incomplete corpus became negative evidence: %+v", out)
115126
}
116127
}

‎internal/app/mcp_related_work.go‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ func (r *MCPReader) checkRelatedWork(ctx context.Context, subject relatedWorkSub
8181
}
8282
if err == nil && !complete {
8383
result.Status, result.Coverage = "partial", "unknown"
84-
result.Recovery = recoveryPlan("thread_coverage_incomplete", "Stored thread coverage is missing or incomplete. Synchronize repository threads, poll the job, and repeat this check before inferring absence.", mcpcontract.RecoveryAction(mcpcontract.SyncThreadsInput{
84+
result.Recovery = recoveryPlan("thread_coverage_incomplete", "Stored thread coverage is missing, incomplete, or stale. Synchronize repository threads, poll the job, and repeat this check before inferring absence.", mcpcontract.RecoveryAction(mcpcontract.SyncThreadsInput{
8585
Selection: "repositories", Repositories: []mcpcontract.RepositoryRef{{Owner: repo.Owner(), Repo: repo.Repo()}}, Kind: "both", State: "all",
8686
}))
8787
}
@@ -162,7 +162,7 @@ func (r *MCPReader) relatedWorkRepositoryCoverage(ctx context.Context, repo doma
162162
if err != nil {
163163
return true, false, err
164164
}
165-
return true, coverage != nil && coverage.Complete, nil
165+
return true, coverage != nil && coverage.Complete && !coverage.SourceUpdatedAt.Before(stored.SourceUpdatedAt), nil
166166
}
167167

168168
func unavailableRelatedWorkOutput(target, id string, repo domain.RepoRef, limit int, reason, message string, action mcpcontract.ToolCall) mcpcontract.CheckOutput {

0 commit comments

Comments
 (0)