package service

import (
	"context"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"net/url"
	"os"
	"path"
	"sort"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	"gitcode-mcp/internal/audit"
	"gitcode-mcp/internal/buildinfo"
	"gitcode-mcp/internal/cache"
	"gitcode-mcp/internal/config"
	"gitcode-mcp/internal/diagnostics"
	"gitcode-mcp/internal/feedback"
	"gitcode-mcp/internal/gitcode"
	"gitcode-mcp/internal/index"
	"gitcode-mcp/internal/rag"
)

const (
	SearchModeHybrid   = "hybrid"
	SearchModeFullText = index.SearchModeFullText
)

type RAGSearchProvider func(context.Context, rag.SearchRequest) (rag.SearchResult, error)

type Service struct {
	store                     cache.Store
	client                    gitcode.Client
	now                       func() time.Time
	lockPath                  string
	providerMode              gitcode.ProviderMode
	writeCredentialPresent    bool
	feedbackProviderAvailable bool
	feedbackConfig            feedback.Config
	ragSearch                 RAGSearchProvider
}

func (s *Service) ConfigureRAGSearch(cfg config.Config) {
	if !cfg.RAG.Search.Hybrid {
		s.ragSearch = nil
		return
	}
	ops := rag.NewOperations(s.store, cfg, rag.OperationsOptions{})
	s.ragSearch = ops.Search
}

func (s *Service) SetRAGSearchProvider(provider RAGSearchProvider) {
	s.ragSearch = provider
}

type auditClaimStore interface {
	ClaimAuditEvent(context.Context, cache.AuditTrailEntry) (bool, error)
}

type auditGenerationClaimStore interface {
	ClaimAuditEventGeneration(context.Context, cache.AuditTrailEntry, *time.Time) (bool, error)
	TransitionAuditEventGeneration(context.Context, cache.AuditTrailEntry, time.Time, string) (bool, error)
}

func New(store cache.Store) *Service {
	svc, err := NewWithMode(store, gitcode.ProviderModeFixture, "", ServiceConfig{})
	if err != nil {
		return &Service{store: store, client: sanitizedFixtureClient{}, now: func() time.Time { return time.Now().UTC() }, providerMode: gitcode.ProviderModeFixture}
	}
	return svc
}

func NewWithClient(store cache.Store, client gitcode.Client) *Service {
	return NewWithClientConfig(store, client, ServiceConfig{})
}

func NewWithClientConfig(store cache.Store, client gitcode.Client, cfg ServiceConfig) *Service {
	svc := New(store)
	svc.client = client
	svc.providerMode = gitcode.ProviderMode("custom")
	svc.writeCredentialPresent = true
	svc.feedbackProviderAvailable = true
	svc.lockPath = serviceLockPath(cfg.LockPath)
	svc.ConfigureFeedback(cfg.Feedback)
	return svc
}

func (s *Service) ProviderMode() gitcode.ProviderMode {
	if s.providerMode == "" {
		return gitcode.ProviderModeFixture
	}
	return s.providerMode
}

func NewWithMode(store cache.Store, mode gitcode.ProviderMode, token string, cfg ServiceConfig) (*Service, error) {
	switch mode {
	case gitcode.ProviderModeFixture:
		return &Service{
			store:                     store,
			client:                    sanitizedFixtureClient{},
			now:                       func() time.Time { return time.Now().UTC() },
			lockPath:                  serviceLockPath(cfg.LockPath),
			providerMode:              gitcode.ProviderModeFixture,
			feedbackConfig:            normalizeFeedbackConfig(cfg.Feedback),
			writeCredentialPresent:    cfg.WriteCredentialPresent,
			feedbackProviderAvailable: cfg.FeedbackProviderAvailable,
		}, nil
	case gitcode.ProviderModeLive:
		token = strings.TrimSpace(token)
		if token == "" {
			return nil, gitcode.ErrProviderUnavailable{Reason: "live provider requires a token"}
		}
		baseURL := strings.TrimSpace(cfg.BaseURL)
		if baseURL == "" {
			baseURL = "https://gitcode.com"
		}
		timeout := cfg.Timeout
		maxResponseSize := cfg.MaxResponseSize
		if maxResponseSize <= 0 {
			maxResponseSize = 10 << 20
		}
		maxRetries := cfg.MaxRetries
		userAgent := cfg.UserAgent
		if userAgent == "" {
			userAgent = "gitcode-mcp"
		}
		client, err := gitcode.NewHTTPClient(gitcode.Config{
			BaseURL:         baseURL,
			Token:           token,
			Timeout:         timeout,
			MaxResponseSize: maxResponseSize,
			MaxRetries:      maxRetries,
			UserAgent:       userAgent,
			Pagination:      cfg.Pagination,
			RateLimitRPS:    cfg.RateLimitRPS,
			RateLimitBurst:  cfg.RateLimitBurst,
		})
		if err != nil {
			return nil, err
		}
		return &Service{
			store:                     store,
			client:                    gitcode.Client(client),
			now:                       func() time.Time { return time.Now().UTC() },
			lockPath:                  serviceLockPath(cfg.LockPath),
			providerMode:              gitcode.ProviderModeLive,
			writeCredentialPresent:    true,
			feedbackProviderAvailable: true,
			feedbackConfig:            normalizeFeedbackConfig(cfg.Feedback),
		}, nil
	case gitcode.ProviderModeUnavailable:
		return nil, gitcode.ErrProviderUnavailable{Reason: "provider unavailable"}
	default:
		return nil, gitcode.ErrProviderUnavailable{Reason: "unknown provider mode " + string(mode)}
	}
}

func serviceLockPath(lockPath string) string {
	// Empty means the store's own writer authority, not a process-global
	// fallback. Explicit overrides still intentionally share their lock.
	return strings.TrimSpace(lockPath)
}

type bulkWriterAdmissionKey struct{}

type bulkWriterAdmission struct {
	service  *Service
	lockPath string
	repoID   string
	active   *atomic.Bool
}

func (s *Service) acquireBulkWriter(ctx context.Context, repoID, operation string) (context.Context, func(), error) {
	if held, ok := ctx.Value(bulkWriterAdmissionKey{}).(bulkWriterAdmission); ok && held.service == s && held.lockPath == s.lockPath && held.repoID == repoID && held.active != nil && held.active.Load() {
		return ctx, func() {}, nil
	}
	lease, err := s.store.AcquireWriter(ctx, cache.WriterRequest{Operation: operation, RepoID: repoID, LockPath: s.lockPath})
	if err != nil {
		return ctx, nil, err
	}
	active := &atomic.Bool{}
	active.Store(true)
	admitted := context.WithValue(ctx, bulkWriterAdmissionKey{}, bulkWriterAdmission{service: s, lockPath: s.lockPath, repoID: repoID, active: active})
	var once sync.Once
	return admitted, func() {
		once.Do(func() {
			active.Store(false)
			_ = s.store.ReleaseWriter(context.Background(), lease)
		})
	}, nil
}

type sanitizedFixtureClient struct{}

func (sanitizedFixtureClient) FixtureBoundaryMode() string {
	return gitcode.FixtureBoundaryMode
}

func (sanitizedFixtureClient) FixtureMarkerIDs() []string {
	return gitcode.FixtureMarkerIDs()
}

func (sanitizedFixtureClient) ListIssues(context.Context, gitcode.IssueListRequest) (gitcode.Page[gitcode.IssueSummary], error) {
	now := fixtureNow()
	return gitcode.Page[gitcode.IssueSummary]{Items: []gitcode.IssueSummary{{Number: 42, Title: "Fixture Issue", State: "open", CreatedAt: now, UpdatedAt: now}}, Page: 1, PerPage: 1, TotalCount: 1}, nil
}

func (sanitizedFixtureClient) GetIssue(context.Context, gitcode.IssueRequest) (gitcode.Issue, error) {
	now := fixtureNow()
	return gitcode.Issue{Number: 42, Title: "Fixture Issue", Body: "# Issue\n\nremote fixture issue body for offline search test", State: "open", CreatedAt: now, UpdatedAt: now}, nil
}

func (sanitizedFixtureClient) ListIssueComments(context.Context, gitcode.IssueRequest) (gitcode.Page[gitcode.Comment], error) {
	now := fixtureNow()
	return gitcode.Page[gitcode.Comment]{Items: []gitcode.Comment{{ID: "c1", Author: "fixture-user", Body: "comment", CreatedAt: now, UpdatedAt: now}}, Page: 1, PerPage: 1, TotalCount: 1}, nil
}

func (sanitizedFixtureClient) ListPRs(context.Context, gitcode.PRListRequest) (gitcode.Page[gitcode.PullRequest], error) {
	return gitcode.Page[gitcode.PullRequest]{}, nil
}

func (sanitizedFixtureClient) GetPR(context.Context, gitcode.PRRequest) (gitcode.PullRequest, error) {
	return gitcode.PullRequest{}, ErrInvalidQuery{Field: "pull_request", Message: "fixture pull request is unavailable"}
}

func (sanitizedFixtureClient) ListPRComments(context.Context, gitcode.PRRequest) (gitcode.Page[gitcode.PRComment], error) {
	return gitcode.Page[gitcode.PRComment]{}, nil
}

func (sanitizedFixtureClient) CreatePR(context.Context, gitcode.CreatePRRequest, gitcode.WriteOptions) (gitcode.WriteResult[gitcode.PullRequest], error) {
	return gitcode.WriteResult[gitcode.PullRequest]{}, gitcode.FixtureReadOnlyError("CreatePR")
}

func (sanitizedFixtureClient) UpdatePR(context.Context, gitcode.UpdatePRRequest, gitcode.WriteOptions) (gitcode.WriteResult[gitcode.PullRequest], error) {
	return gitcode.WriteResult[gitcode.PullRequest]{}, gitcode.FixtureReadOnlyError("UpdatePR")
}

func (sanitizedFixtureClient) MergePR(context.Context, gitcode.MergePRRequest, gitcode.WriteOptions) (gitcode.WriteResult[gitcode.PullRequest], error) {
	return gitcode.WriteResult[gitcode.PullRequest]{}, gitcode.FixtureReadOnlyError("MergePR")
}

func (sanitizedFixtureClient) LinkPRIssue(context.Context, gitcode.LinkPRIssueRequest, gitcode.WriteOptions) (gitcode.WriteResult[[]gitcode.Issue], error) {
	return gitcode.WriteResult[[]gitcode.Issue]{}, gitcode.FixtureReadOnlyError("LinkPRIssue")
}

func (sanitizedFixtureClient) GetWikiPage(context.Context, gitcode.WikiPageRequest) (gitcode.WikiPage, error) {
	now := fixtureNow()
	return gitcode.WikiPage{Slug: "Home", Title: "Fixture Wiki", Body: "# Wiki\n\nremote fixture wiki body for offline search test", Revision: "rev-home", CreatedAt: now, UpdatedAt: now}, nil
}

func (sanitizedFixtureClient) ListWikiPages(context.Context, gitcode.WikiListRequest) (gitcode.Page[gitcode.WikiPage], error) {
	now := fixtureNow()
	return gitcode.Page[gitcode.WikiPage]{Items: []gitcode.WikiPage{{Slug: "Home", Title: "Fixture Wiki", Body: "# Wiki\n\nremote fixture wiki body for offline search test", Revision: "rev-home", CreatedAt: now, UpdatedAt: now}}, Page: 1, PerPage: 1, TotalCount: 1}, nil
}

func (sanitizedFixtureClient) Search(context.Context, gitcode.SearchRequest) (gitcode.Page[gitcode.SearchResult], error) {
	return gitcode.Page[gitcode.SearchResult]{}, nil
}

func (sanitizedFixtureClient) ListIssueAttachments(context.Context, gitcode.IssueRequest) (gitcode.Page[gitcode.AttachmentSummary], error) {
	return gitcode.Page[gitcode.AttachmentSummary]{}, nil
}

func (sanitizedFixtureClient) GetAttachment(context.Context, gitcode.AttachmentRequest) (gitcode.AttachmentBody, error) {
	return gitcode.AttachmentBody{}, ErrInvalidQuery{Field: "attachment", Message: "fixture attachment is unavailable"}
}

func (sanitizedFixtureClient) CreateIssue(context.Context, gitcode.CreateIssueRequest, gitcode.WriteOptions) (gitcode.WriteResult[gitcode.Issue], error) {
	return gitcode.WriteResult[gitcode.Issue]{}, gitcode.FixtureReadOnlyError("sanitized fixture write")
}

func (sanitizedFixtureClient) UpdateIssue(context.Context, gitcode.UpdateIssueRequest, gitcode.WriteOptions) (gitcode.WriteResult[gitcode.Issue], error) {
	return gitcode.WriteResult[gitcode.Issue]{}, gitcode.FixtureReadOnlyError("sanitized fixture write")
}

func (sanitizedFixtureClient) CreateIssueComment(context.Context, gitcode.CreateIssueCommentRequest, gitcode.WriteOptions) (gitcode.WriteResult[gitcode.Comment], error) {
	return gitcode.WriteResult[gitcode.Comment]{}, gitcode.FixtureReadOnlyError("sanitized fixture write")
}

func (sanitizedFixtureClient) UpdateIssueComment(context.Context, gitcode.UpdateIssueCommentRequest, gitcode.WriteOptions) (gitcode.WriteResult[gitcode.Comment], error) {
	return gitcode.WriteResult[gitcode.Comment]{}, gitcode.FixtureReadOnlyError("sanitized fixture write")
}

func (sanitizedFixtureClient) CreatePRComment(context.Context, gitcode.CreatePRCommentRequest, gitcode.WriteOptions) (gitcode.WriteResult[gitcode.PRComment], error) {
	return gitcode.WriteResult[gitcode.PRComment]{}, gitcode.FixtureReadOnlyError("sanitized fixture write")
}

func (sanitizedFixtureClient) CreatePRReviewComment(context.Context, gitcode.CreatePRReviewCommentRequest, gitcode.WriteOptions) (gitcode.WriteResult[gitcode.PRComment], error) {
	return gitcode.WriteResult[gitcode.PRComment]{}, gitcode.FixtureReadOnlyError("sanitized fixture write")
}

func (sanitizedFixtureClient) ReplyPRReviewComment(context.Context, gitcode.ReplyPRReviewCommentRequest, gitcode.WriteOptions) (gitcode.WriteResult[gitcode.PRComment], error) {
	return gitcode.WriteResult[gitcode.PRComment]{}, gitcode.FixtureReadOnlyError("sanitized fixture write")
}

func (sanitizedFixtureClient) CreateWikiPage(context.Context, gitcode.CreateWikiPageRequest, gitcode.WriteOptions) (gitcode.WriteResult[gitcode.WikiPage], error) {
	return gitcode.WriteResult[gitcode.WikiPage]{}, gitcode.FixtureReadOnlyError("sanitized fixture write")
}

func (sanitizedFixtureClient) UpdateWikiPage(context.Context, gitcode.UpdateWikiPageRequest, gitcode.WriteOptions) (gitcode.WriteResult[gitcode.WikiPage], error) {
	return gitcode.WriteResult[gitcode.WikiPage]{}, gitcode.FixtureReadOnlyError("sanitized fixture write")
}

func (sanitizedFixtureClient) DeleteWikiPage(context.Context, gitcode.DeleteWikiPageRequest, gitcode.WriteOptions) (gitcode.WriteResult[gitcode.WikiPage], error) {
	return gitcode.WriteResult[gitcode.WikiPage]{}, gitcode.FixtureReadOnlyError("sanitized fixture write")
}

func (sanitizedFixtureClient) AddLabel(context.Context, gitcode.LabelRequest, gitcode.WriteOptions) (gitcode.WriteResult[gitcode.Issue], error) {
	return gitcode.WriteResult[gitcode.Issue]{}, gitcode.FixtureReadOnlyError("sanitized fixture write")
}

func (sanitizedFixtureClient) RemoveLabel(context.Context, gitcode.LabelRequest, gitcode.WriteOptions) (gitcode.WriteResult[gitcode.Issue], error) {
	return gitcode.WriteResult[gitcode.Issue]{}, gitcode.FixtureReadOnlyError("sanitized fixture write")
}

func (sanitizedFixtureClient) ListMilestones(context.Context, gitcode.MilestoneListRequest) (gitcode.Page[gitcode.Milestone], error) {
	return gitcode.Page[gitcode.Milestone]{}, gitcode.FixtureReadOnlyError("sanitized fixture write")
}

func (sanitizedFixtureClient) ListPushRemoteMirrors(context.Context, gitcode.PushMirrorListRequest) ([]gitcode.PushMirror, error) {
	return nil, gitcode.FixtureReadOnlyError("sanitized fixture read")
}

func (sanitizedFixtureClient) TriggerPushRemoteMirror(context.Context, gitcode.PushMirrorTriggerRequest, gitcode.WriteOptions) (gitcode.WriteResult[gitcode.PushMirror], error) {
	return gitcode.WriteResult[gitcode.PushMirror]{}, gitcode.FixtureReadOnlyError("sanitized fixture write")
}

func (sanitizedFixtureClient) GetMilestone(context.Context, gitcode.MilestoneRequest) (gitcode.Milestone, error) {
	return gitcode.Milestone{}, gitcode.FixtureReadOnlyError("sanitized fixture write")
}

func (sanitizedFixtureClient) CreateMilestone(context.Context, gitcode.MilestoneWriteRequest, gitcode.WriteOptions) (gitcode.WriteResult[gitcode.Milestone], error) {
	return gitcode.WriteResult[gitcode.Milestone]{}, gitcode.FixtureReadOnlyError("sanitized fixture write")
}

func (sanitizedFixtureClient) UpdateMilestone(context.Context, gitcode.MilestoneWriteRequest, gitcode.WriteOptions) (gitcode.WriteResult[gitcode.Milestone], error) {
	return gitcode.WriteResult[gitcode.Milestone]{}, gitcode.FixtureReadOnlyError("sanitized fixture write")
}

func (sanitizedFixtureClient) GetRelease(context.Context, gitcode.ReleaseRequest) (gitcode.Release, error) {
	return gitcode.Release{}, gitcode.FixtureReadOnlyError("sanitized fixture write")
}

func (sanitizedFixtureClient) CreateRelease(context.Context, gitcode.ReleaseWriteRequest, gitcode.WriteOptions) (gitcode.WriteResult[gitcode.Release], error) {
	return gitcode.WriteResult[gitcode.Release]{}, gitcode.FixtureReadOnlyError("sanitized fixture write")
}

func (sanitizedFixtureClient) UpdateRelease(context.Context, gitcode.ReleaseWriteRequest, gitcode.WriteOptions) (gitcode.WriteResult[gitcode.Release], error) {
	return gitcode.WriteResult[gitcode.Release]{}, gitcode.FixtureReadOnlyError("sanitized fixture write")
}

func fixtureNow() time.Time {
	return time.Date(2026, 6, 20, 10, 0, 0, 0, time.UTC)
}

func (s *Service) AddRepository(ctx context.Context, req AddRepositoryRequest) (RepositoryBinding, error) {
	repo, err := normalizeRepositoryRequest(req, s.now())
	if err != nil {
		return RepositoryBinding{}, err
	}
	cacheRepo := cache.RepositoryBinding{RepoID: repo.RepoID, Owner: repo.Owner, Name: repo.Name, APIBaseURL: repo.APIBaseURL, DisplayName: repo.DisplayName, CreatedAt: repo.CreatedAt, UpdatedAt: repo.UpdatedAt, Aliases: repo.Aliases}
	for _, scope := range repo.Scopes {
		cacheRepo.Scopes = append(cacheRepo.Scopes, cache.RepositoryScope(scope))
	}
	if err := s.store.AddRepository(ctx, cacheRepo); err != nil {
		if cache.IsConstraintError(err) {
			return RepositoryBinding{}, ErrConflict{Kind: "repository", ID: repo.RepoID, Message: "repo_id or repository alias already exists"}
		}
		return RepositoryBinding{}, err
	}
	return repo, nil
}

func (s *Service) UpdateRepository(ctx context.Context, req AddRepositoryRequest) (RepositoryBinding, error) {
	repo, err := normalizeRepositoryRequest(req, s.now())
	if err != nil {
		return RepositoryBinding{}, err
	}
	existing, err := s.store.GetRepository(ctx, repo.RepoID)
	if err != nil {
		return RepositoryBinding{}, normalizeError(err, "repository", repo.RepoID)
	}
	repo.CreatedAt = existing.CreatedAt
	cacheRepo := cache.RepositoryBinding{RepoID: repo.RepoID, Owner: repo.Owner, Name: repo.Name, APIBaseURL: repo.APIBaseURL, DisplayName: repo.DisplayName, CreatedAt: repo.CreatedAt, UpdatedAt: repo.UpdatedAt, Aliases: repo.Aliases}
	for _, scope := range repo.Scopes {
		cacheRepo.Scopes = append(cacheRepo.Scopes, cache.RepositoryScope(scope))
	}
	updater, ok := s.store.(interface {
		UpdateRepository(context.Context, cache.RepositoryBinding) error
	})
	if !ok {
		return RepositoryBinding{}, ErrInvalidQuery{Field: "repository", Message: "repository updates are unavailable for this store"}
	}
	if err := updater.UpdateRepository(ctx, cacheRepo); err != nil {
		if cache.IsConstraintError(err) {
			return RepositoryBinding{}, ErrConflict{Kind: "repository", ID: repo.RepoID, Message: "repository alias already exists"}
		}
		return RepositoryBinding{}, err
	}
	return repo, nil
}

func ValidateRepositoryBinding(req AddRepositoryRequest, now time.Time) (RepositoryBinding, error) {
	return normalizeRepositoryRequest(req, now)
}

// SetRepositoryScopes updates an existing exact identity in the selected cache.
// Unlike binding replacement, it cannot overwrite concurrent metadata changes.
// It neither contacts the provider nor changes configuration or cached content.
func (s *Service) SetRepositoryScopes(ctx context.Context, repoID string, raw []string) (RepositoryBinding, error) {
	repoID = strings.TrimSpace(repoID)
	if repoID == "" {
		return RepositoryBinding{}, ErrInvalidQuery{Field: "repo", Message: "an existing exact repo id is required"}
	}
	scopes, err := normalizeRepositoryScopes(raw)
	if err != nil {
		return RepositoryBinding{}, err
	}
	updater, ok := s.store.(interface {
		SetRepositoryScopes(context.Context, string, []cache.RepositoryScope, time.Time) error
	})
	if !ok {
		return RepositoryBinding{}, ErrInvalidQuery{Field: "repository", Message: "scope updates are unavailable for this store"}
	}
	// Binding metadata follows the physical cache authority (also used by
	// migration), not the legacy configurable provider/sync lock lane.
	lease, err := s.store.AcquireWriter(ctx, cache.WriterRequest{Operation: "repo-set-scopes", RepoID: repoID})
	if err != nil {
		return RepositoryBinding{}, err
	}
	defer s.store.ReleaseWriter(context.Background(), lease)
	storedScopes := make([]cache.RepositoryScope, len(scopes))
	for i, scope := range scopes {
		storedScopes[i] = cache.RepositoryScope(scope)
	}
	if err := updater.SetRepositoryScopes(ctx, repoID, storedScopes, s.now()); err != nil {
		return RepositoryBinding{}, normalizeError(err, "repository", repoID)
	}
	got, err := s.store.GetRepository(ctx, repoID)
	if err != nil {
		return RepositoryBinding{}, normalizeError(err, "repository", repoID)
	}
	if len(got.Scopes) != len(storedScopes) {
		return RepositoryBinding{}, ErrConflict{Kind: "repository", ID: repoID, Message: "scopes changed before readback; inspect repo status"}
	}
	for i, scope := range got.Scopes {
		if scope != storedScopes[i] {
			return RepositoryBinding{}, ErrConflict{Kind: "repository", ID: repoID, Message: "scopes changed before readback; inspect repo status"}
		}
	}
	return RepositoryBinding{RepoID: got.RepoID, Owner: got.Owner, Name: got.Name, APIBaseURL: sanitizeAPIBaseURL(got.APIBaseURL), Scopes: scopes, DisplayName: got.DisplayName, Aliases: got.Aliases, CreatedAt: got.CreatedAt, UpdatedAt: got.UpdatedAt}, nil
}

func (s *Service) ResetLiveCache(ctx context.Context, req ResetLiveCacheRequest) (ResetLiveCacheResult, error) {
	repoID, err := s.requireRepo(ctx, req.RepoID, "cache reset live")
	if err != nil {
		return ResetLiveCacheResult{}, err
	}
	lease, err := s.store.AcquireWriter(ctx, cache.WriterRequest{Operation: "reset-live", RepoID: repoID, LockPath: s.lockPath})
	if err != nil {
		return ResetLiveCacheResult{}, err
	}
	defer s.store.ReleaseWriter(context.Background(), lease)
	if err := s.store.ResetLive(ctx, repoID); err != nil {
		return ResetLiveCacheResult{}, normalizeError(err, "cache reset live", repoID)
	}
	return ResetLiveCacheResult{RepoID: repoID, Reset: "live"}, nil
}

func (s *Service) CacheStatus(ctx context.Context, req CacheStatusRequest) (CacheStatusResult, error) {
	repoID, err := s.requireRepo(ctx, req.RepoID, "cache-status")
	if err != nil {
		return CacheStatusResult{}, err
	}
	counts, err := s.store.RecordCounts(ctx, repoID)
	if err != nil {
		return CacheStatusResult{}, normalizeError(err, "cache", repoID)
	}
	walCapable, journalMode, err := s.store.WALCapable(ctx)
	if err != nil {
		return CacheStatusResult{}, normalizeError(err, "cache", repoID)
	}
	freshness, err := s.freshnessReport(ctx, repoID, index.ChunkQuery{RepoID: repoID})
	if err != nil {
		return CacheStatusResult{}, err
	}
	byWarning := map[string]int{}
	for _, warning := range freshness.Warnings {
		byWarning[warning.Code]++
	}
	return CacheStatusResult{RepoID: repoID, WALCapable: walCapable, JournalMode: journalMode, Records: counts.Records, Comments: counts.Comments, IdentityAliases: counts.IdentityAliases, SyncEvents: counts.SyncEvents, AuditRows: counts.AuditRows, Snapshots: counts.Snapshots, SnapshotChunks: counts.SnapshotChunks, Chunks: counts.Chunks, RemoteRevisions: counts.RemoteRevisions, RAGNamespaces: counts.RAGNamespaces, RAGEmbeddings: counts.RAGEmbeddings, RAGIndexRuns: counts.RAGIndexRuns, IndexFreshnessWarnings: len(freshness.Warnings), IndexFreshnessByWarning: byWarning}, nil
}

func (s *Service) RepositoryStatus(ctx context.Context, req RepositoryStatusRequest) (RepositoryStatus, error) {
	repoID := strings.TrimSpace(req.RepoID)
	if repoID == "" {
		return RepositoryStatus{}, ErrInvalidQuery{Field: "repo", Message: "repo is required"}
	}
	repo, err := s.store.GetRepository(ctx, repoID)
	if err != nil {
		if isCacheNotFound(err) {
			status := RepositoryStatus{RepoID: repoID, BindingState: "missing", AvailableBindings: []string{}}
			repos, listErr := s.store.ListRepositories(ctx)
			if listErr != nil {
				return status, normalizeError(listErr, "repository bindings", repoID)
			}
			status.AvailableBindings, status.SuggestedRepoID = repositoryBindingHints(repoID, repos, 8)
			return status, ErrNotFound{Kind: "repository", ID: repoID}
		}
		return RepositoryStatus{}, normalizeError(err, "repository", repoID)
	}
	schemaVersion, err := s.store.SchemaVersion(ctx)
	if err != nil {
		return RepositoryStatus{}, normalizeError(err, "cache schema", repoID)
	}
	counts, err := s.store.RecordCounts(ctx, repoID)
	if err != nil {
		return RepositoryStatus{}, normalizeError(err, "cache", repoID)
	}
	issues, err := s.store.ListRecords(ctx, cache.RecordFilter{RepoID: repoID, Type: "issue"})
	if err != nil {
		return RepositoryStatus{}, normalizeError(err, "issues", repoID)
	}
	queueState := "schema_unavailable"
	var queueSummary *IssueCommentQueueSummary
	if schemaVersion >= cache.IssueCommentSyncSchemaVersion() {
		queue, err := s.store.IssueCommentSyncSummary(ctx, repoID)
		if err != nil {
			return RepositoryStatus{}, normalizeError(err, "issue comments", repoID)
		}
		queueState = "available"
		queueSummary = &IssueCommentQueueSummary{Phase: "status", Pending: queue.Pending, Deferred: queue.Deferred, Complete: queue.Complete, Total: queue.Total}
	}
	build := buildinfo.Current()
	expectedSchemaVersion := cache.CurrentSchemaVersion()
	status := RepositoryStatus{
		RepoID:                     repo.RepoID,
		Owner:                      repo.Owner,
		Name:                       repo.Name,
		APIBaseURL:                 sanitizeAPIBaseURL(repo.APIBaseURL),
		DisplayName:                repo.DisplayName,
		Aliases:                    append([]string(nil), repo.Aliases...),
		BindingState:               "ready",
		AliasConflictState:         "none",
		CacheState:                 repositoryCacheState(schemaVersion, expectedSchemaVersion),
		IndexState:                 "unknown",
		BinaryVersion:              build.Version,
		BinaryCommit:               build.ShortCommit(),
		BinaryBuildDate:            build.Date,
		BinaryVersionSource:        build.Source,
		CacheSchemaVersion:         schemaVersion,
		ExpectedCacheSchemaVersion: expectedSchemaVersion,
		IssueRecords:               len(issues),
		IssueComments:              counts.Comments,
		IssueCommentQueueState:     queueState,
		IssueCommentQueue:          queueSummary,
	}
	for _, scope := range repo.Scopes {
		status.Scopes = append(status.Scopes, RepositoryScope(scope))
	}
	return status, nil
}

func repositoryBindingHints(requested string, repos []cache.RepositoryBinding, limit int) ([]string, string) {
	requested = strings.TrimSpace(requested)
	availableSet := make(map[string]struct{}, len(repos))
	strongSet := make(map[string]struct{})
	for _, repo := range repos {
		if strings.TrimSpace(repo.RepoID) == "" {
			continue
		}
		availableSet[repo.RepoID] = struct{}{}
		matched := strings.EqualFold(strings.TrimSpace(repo.Name), requested)
		for _, alias := range repo.Aliases {
			matched = matched || strings.EqualFold(strings.TrimSpace(alias), requested)
		}
		if matched {
			strongSet[repo.RepoID] = struct{}{}
		}
	}
	available := sortedKeys(availableSet)
	if limit > 0 && len(available) > limit {
		available = available[:limit]
	}
	strong := sortedKeys(strongSet)
	suggested := ""
	if len(strong) == 1 {
		suggested = strong[0]
	}
	return available, suggested
}

func repositoryCacheState(detected, expected int) string {
	switch {
	case detected == expected:
		return "ready"
	case detected > 0 && detected < expected:
		return "migration_required"
	case detected > expected:
		return "binary_upgrade_required"
	default:
		return "unknown"
	}
}

func normalizeRepositoryRequest(req AddRepositoryRequest, now time.Time) (RepositoryBinding, error) {
	repo := RepositoryBinding{RepoID: strings.TrimSpace(req.RepoID), Owner: strings.TrimSpace(req.Owner), Name: strings.TrimSpace(req.Name), APIBaseURL: strings.TrimSpace(req.APIBaseURL), DisplayName: strings.TrimSpace(req.DisplayName), CreatedAt: now.UTC(), UpdatedAt: now.UTC()}
	if repo.RepoID == "" {
		return RepositoryBinding{}, ErrInvalidQuery{Field: "repo", Message: "repo is required"}
	}
	if repo.Owner == "" {
		return RepositoryBinding{}, ErrInvalidQuery{Field: "owner", Message: "owner is required"}
	}
	if repo.Name == "" {
		return RepositoryBinding{}, ErrInvalidQuery{Field: "name", Message: "name is required"}
	}
	parsed, err := url.Parse(repo.APIBaseURL)
	if err != nil || parsed.Scheme == "" || parsed.Host == "" {
		return RepositoryBinding{}, ErrInvalidQuery{Field: "api-base-url", Message: "valid api base url is required"}
	}
	parsed.User = nil
	repo.APIBaseURL = sanitizeAPIBaseURL(parsed.String())
	scopes, err := normalizeRepositoryScopes(req.Scopes)
	if err != nil {
		return RepositoryBinding{}, err
	}
	repo.Scopes = scopes
	repo.Aliases = normalizeAliases(req.Aliases)
	return repo, nil
}

func normalizeRepositoryScopes(raw []string) ([]RepositoryScope, error) {
	seen := map[RepositoryScope]bool{}
	for _, value := range raw {
		for _, part := range strings.Split(value, ",") {
			scope := RepositoryScope(strings.ToLower(strings.TrimSpace(part)))
			if scope == "" {
				continue
			}
			switch scope {
			case "pull", "pulls", "pr", "prs", "pull_request", "pull_requests", "comment", "comments":
				scope = RepositoryScopeIssues
			}
			if scope != RepositoryScopeIssues && scope != RepositoryScopeWiki {
				return nil, ErrInvalidQuery{Field: "scopes", Message: "scopes must contain issues, wiki, pulls, or comments"}
			}
			seen[scope] = true
		}
	}
	if len(seen) == 0 {
		return nil, ErrInvalidQuery{Field: "scopes", Message: "at least one scope is required"}
	}
	out := []RepositoryScope{}
	for _, scope := range []RepositoryScope{RepositoryScopeIssues, RepositoryScopeWiki} {
		if seen[scope] {
			out = append(out, scope)
		}
	}
	return out, nil
}

func normalizeAliases(raw []string) []string {
	seen := map[string]bool{}
	aliases := []string{}
	for _, value := range raw {
		for _, part := range strings.Split(value, ",") {
			alias := strings.TrimSpace(part)
			if alias == "" || seen[alias] {
				continue
			}
			seen[alias] = true
			aliases = append(aliases, alias)
		}
	}
	sort.Strings(aliases)
	return aliases
}

func sanitizeAPIBaseURL(raw string) string {
	parsed, err := url.Parse(raw)
	if err != nil {
		return ""
	}
	parsed.User = nil
	query := parsed.Query()
	for key := range query {
		lower := strings.ToLower(key)
		if strings.Contains(lower, "token") || strings.Contains(lower, "secret") || strings.Contains(lower, "key") || strings.Contains(lower, "auth") {
			query.Del(key)
		}
	}
	parsed.RawQuery = query.Encode()
	return parsed.String()
}

func (s *Service) SearchSources(ctx context.Context, req SearchSourcesRequest) (SearchSourcesResult, error) {
	repoID, err := s.requireRepo(ctx, req.RepoID, "search")
	if err != nil {
		return SearchSourcesResult{}, err
	}
	if strings.TrimSpace(req.Query) == "" {
		return SearchSourcesResult{}, ErrInvalidQuery{Field: "query", Message: "query is required"}
	}
	mode := strings.ToLower(strings.TrimSpace(req.Mode))
	mode = strings.ReplaceAll(mode, "-", "_")
	if mode == "" {
		mode = SearchModeHybrid
	}
	if mode != SearchModeHybrid && mode != SearchModeFullText {
		return SearchSourcesResult{}, ErrInvalidQuery{Field: "mode", Message: "mode must be hybrid or full_text"}
	}
	limit, err := SourceQueryPage(req.Limit, req.Offset)
	if err != nil {
		return SearchSourcesResult{}, err
	}
	lexicalLimit := limit + req.Offset
	semanticLimit := lexicalLimit
	if semanticLimit < 40 {
		semanticLimit = 40
	}
	if semanticLimit > 400 {
		semanticLimit = 400
	}
	lexical, updated, err := s.searchSourceLexicalCandidates(ctx, repoID, req, lexicalLimit)
	if err != nil {
		return SearchSourcesResult{}, err
	}
	base := SearchSourcesResult{
		RepoID:        repoID,
		Query:         req.Query,
		RequestedMode: mode,
		EffectiveMode: SearchModeFullText,
		SearchMode:    SearchModeFullText,
		RAGState:      "disabled",
		Repair:        SearchRepairStatus{State: "not_needed"},
		Limit:         limit,
		Offset:        req.Offset,
	}
	if mode == SearchModeFullText {
		base.RAGState = "not_requested"
		base.Results = pageSearchSources(lexical, req.Offset, limit)
		for i := range base.Results {
			base.Results[i].Rank = req.Offset + i + 1
			base.Results[i].Match.LexicalRank = req.Offset + i + 1
			base.Results[i].Match.LexicalScore = base.Results[i].Score
			base.Results[i].Match.ExactMatch = s.exactSourceMatch(ctx, repoID, req.Query, base.Results[i])
			base.Results[i].Match.FusionScore = base.Results[i].Score
			base.Results[i].Citations = []SearchCitation{}
		}
		return base, nil
	}
	if s.ragSearch == nil {
		base.FallbackReason = "rag_disabled"
		base.Repair.State = "enable_required"
		base.Results = pageSearchSources(lexical, req.Offset, limit)
		decorateLexicalFallback(ctx, s, &base, repoID, req.Offset)
		return base, nil
	}
	semanticSourceIDs, err := s.searchSemanticSourceIDs(ctx, repoID, req)
	if err != nil {
		return SearchSourcesResult{}, err
	}
	ragLimit := semanticLimit * 2
	ragResult, ragErr := s.ragSearch(ctx, rag.SearchRequest{RepoID: repoID, Query: req.Query, SourceIDs: semanticSourceIDs, TopK: semanticLimit, Limit: ragLimit})
	base.Coverage = searchCoverageFromRAG(ragResult)
	if err := ctx.Err(); err != nil {
		return SearchSourcesResult{}, err
	}
	if ragErr != nil {
		reason, ok := ragProviderErrorFallbackReason(ragErr)
		if !ok {
			return SearchSourcesResult{}, ragErr
		}
		base.RAGState = "unavailable"
		base.FallbackReason = reason
		base.Repair.State = "needed"
		base.Results = pageSearchSources(lexical, req.Offset, limit)
		decorateLexicalFallback(ctx, s, &base, repoID, req.Offset)
		return base, nil
	}
	if reason := ragFallbackReason(ragResult); reason != "" {
		base.RAGState = ragStateForFallback(ragResult)
		base.FallbackReason = reason
		base.Repair.State = "needed"
		base.Results = pageSearchSources(lexical, req.Offset, limit)
		decorateLexicalFallback(ctx, s, &base, repoID, req.Offset)
		return base, nil
	}
	base.EffectiveMode = SearchModeHybrid
	base.SearchMode = SearchModeHybrid
	base.RAGState = "ready"
	base.Repair.State = "not_needed"
	if base.Coverage.MissingChunks > 0 || base.Coverage.StaleChunks > 0 {
		base.RAGState = "partial"
		base.Repair.State = "needed"
	}
	out, err := s.fuseSearchSources(ctx, repoID, req, lexical, ragResult, updated)
	if err != nil {
		return SearchSourcesResult{}, err
	}
	base.Results = pageSearchSources(out, req.Offset, limit)
	for i := range base.Results {
		base.Results[i].Rank = req.Offset + i + 1
	}
	return base, nil
}

func ragProviderErrorFallbackReason(err error) (string, bool) {
	var providerErr *rag.ProviderError
	if !errors.As(err, &providerErr) {
		return "", false
	}
	switch providerErr.Class {
	case rag.ProviderFailureUnavailable:
		return "provider_unavailable", true
	case rag.ProviderFailureModelMissing:
		return "model_missing", true
	case rag.ProviderFailureTimeout:
		return "query_embedding_timeout", true
	default:
		return "query_embedding_failed", true
	}
}

func (s *Service) searchSemanticSourceIDs(ctx context.Context, repoID string, req SearchSourcesRequest) ([]string, error) {
	if req.Kind == "" && req.Provenance == "" {
		return nil, nil
	}
	sources, err := s.store.ListSources(ctx, cache.SourceFilter{RepoID: repoID, Kind: req.Kind, Provenance: cache.Provenance(req.Provenance)})
	if err != nil {
		return nil, normalizeError(err, "search", req.Query)
	}
	ids := make([]string, 0, len(sources))
	for _, source := range sources {
		ids = append(ids, source.ID)
	}
	return ids, nil
}

func (s *Service) searchSourceLexicalCandidates(ctx context.Context, repoID string, req SearchSourcesRequest, limit int) ([]SearchSourceResult, map[string]time.Time, error) {
	results, err := s.store.SearchSources(ctx, cache.SearchQuery{RepoID: repoID, Query: req.Query, Kind: req.Kind, Provenance: cache.Provenance(req.Provenance), Limit: limit})
	if err != nil {
		return nil, nil, normalizeError(err, "search", req.Query)
	}
	out := make([]SearchSourceResult, 0, len(results))
	updated := map[string]time.Time{}
	for _, result := range results {
		source, err := s.store.GetSourceScoped(ctx, repoID, result.ID)
		if err != nil {
			return nil, nil, normalizeError(err, "source", result.ID)
		}
		updated[result.ID] = source.UpdatedAt.UTC()
		line := nullableLine(result.Line)
		out = append(out, SearchSourceResult{RepoID: source.RepoID, ID: result.ID, Path: result.Path, Title: result.Title, Kind: source.Kind, Status: source.Status, Provenance: string(source.Provenance), Snippet: result.Snippet, LineStart: line, LineEnd: line, Score: result.Score, Citations: []SearchCitation{}})
	}
	if stableID, err := s.resolveScopedStableID(ctx, repoID, req.Query, "", ""); err == nil {
		seen := false
		for _, item := range out {
			if item.ID == stableID {
				seen = true
				break
			}
		}
		if !seen {
			source, getErr := s.store.GetSourceScoped(ctx, repoID, stableID)
			if getErr == nil && (req.Kind == "" || source.Kind == req.Kind) && (req.Provenance == "" || string(source.Provenance) == req.Provenance) {
				line := 1
				snippet := strings.TrimSpace(source.Title)
				if body := strings.TrimSpace(source.Body); body != "" {
					snippet = body
					if len([]rune(snippet)) > 160 {
						snippet = string([]rune(snippet)[:160]) + "..."
					}
				}
				out = append(out, SearchSourceResult{RepoID: source.RepoID, ID: source.ID, Path: source.Path, Title: source.Title, Kind: source.Kind, Status: source.Status, Provenance: string(source.Provenance), Snippet: snippet, LineStart: &line, LineEnd: &line, Score: 100, Citations: []SearchCitation{}})
				updated[source.ID] = source.UpdatedAt.UTC()
			}
		}
	}
	sort.SliceStable(out, func(i, j int) bool {
		if out[i].Score != out[j].Score {
			return out[i].Score > out[j].Score
		}
		if !updated[out[i].ID].Equal(updated[out[j].ID]) {
			return updated[out[i].ID].Before(updated[out[j].ID])
		}
		return out[i].ID < out[j].ID
	})
	return out, updated, nil
}

type semanticSourceCandidate struct {
	result    SearchSourceResult
	score     float64
	citations []scoredSearchCitation
}

type scoredSearchCitation struct {
	citation SearchCitation
	score    float64
}

func (s *Service) fuseSearchSources(ctx context.Context, repoID string, req SearchSourcesRequest, lexical []SearchSourceResult, ragResult rag.SearchResult, updated map[string]time.Time) ([]SearchSourceResult, error) {
	candidates := make(map[string]SearchSourceResult, len(lexical)+len(ragResult.Results))
	for i, result := range lexical {
		result.Match.LexicalRank = i + 1
		result.Match.LexicalScore = result.Score
		candidates[result.ID] = result
	}
	semantic := map[string]*semanticSourceCandidate{}
	for _, hit := range ragResult.Results {
		if hit.Score.Semantic <= 0 || strings.TrimSpace(hit.SourceID) == "" {
			continue
		}
		source, err := s.store.GetSourceScoped(ctx, repoID, hit.SourceID)
		if err != nil {
			if isCacheNotFound(err) {
				continue
			}
			return nil, normalizeError(err, "source", hit.SourceID)
		}
		if req.Kind != "" && source.Kind != req.Kind {
			continue
		}
		if req.Provenance != "" && string(source.Provenance) != req.Provenance {
			continue
		}
		item := semantic[source.ID]
		if item == nil {
			item = &semanticSourceCandidate{result: SearchSourceResult{RepoID: source.RepoID, ID: source.ID, Path: source.Path, Title: source.Title, Kind: source.Kind, Status: source.Status, Provenance: string(source.Provenance), Citations: []SearchCitation{}}}
			semantic[source.ID] = item
			updated[source.ID] = source.UpdatedAt.UTC()
		}
		if hit.Score.Semantic > item.score {
			item.score = hit.Score.Semantic
		}
		duplicate := false
		for _, citation := range item.citations {
			if citation.citation.ChunkID == hit.ChunkID {
				duplicate = true
				break
			}
		}
		if !duplicate {
			item.citations = append(item.citations, scoredSearchCitation{citation: SearchCitation{ChunkID: hit.ChunkID, LineStart: hit.LineStart, LineEnd: hit.LineEnd, Snippet: hit.Snippet}, score: hit.Score.Semantic})
		}
	}
	semanticOrder := make([]*semanticSourceCandidate, 0, len(semantic))
	for _, item := range semantic {
		sort.SliceStable(item.citations, func(i, j int) bool {
			if item.citations[i].score != item.citations[j].score {
				return item.citations[i].score > item.citations[j].score
			}
			return item.citations[i].citation.ChunkID < item.citations[j].citation.ChunkID
		})
		if len(item.citations) > 3 {
			item.citations = item.citations[:3]
		}
		for _, citation := range item.citations {
			item.result.Citations = append(item.result.Citations, citation.citation)
		}
		semanticOrder = append(semanticOrder, item)
	}
	sort.SliceStable(semanticOrder, func(i, j int) bool {
		if semanticOrder[i].score != semanticOrder[j].score {
			return semanticOrder[i].score > semanticOrder[j].score
		}
		return semanticOrder[i].result.ID < semanticOrder[j].result.ID
	})
	for i, item := range semanticOrder {
		result, ok := candidates[item.result.ID]
		if !ok {
			result = item.result
			if len(result.Citations) > 0 {
				result.Snippet = result.Citations[0].Snippet
				result.LineStart = nullableLine(result.Citations[0].LineStart)
				result.LineEnd = nullableLine(result.Citations[0].LineEnd)
			}
		} else {
			result.Citations = item.result.Citations
		}
		result.Match.SemanticRank = i + 1
		result.Match.SemanticScore = item.score
		candidates[result.ID] = result
	}
	out := make([]SearchSourceResult, 0, len(candidates))
	for _, result := range candidates {
		result.Match.ExactMatch = s.exactSourceMatch(ctx, repoID, req.Query, result)
		score := 0.0
		if result.Match.LexicalRank > 0 {
			score += 1.0 / float64(60+result.Match.LexicalRank)
		}
		if result.Match.SemanticRank > 0 {
			score += 1.0 / float64(60+result.Match.SemanticRank)
		}
		if result.Match.ExactMatch {
			score += 1
		}
		result.Match.FusionScore = score
		result.Score = score
		if result.Citations == nil {
			result.Citations = []SearchCitation{}
		}
		out = append(out, result)
	}
	sort.SliceStable(out, func(i, j int) bool {
		if out[i].Score != out[j].Score {
			return out[i].Score > out[j].Score
		}
		if out[i].Match.ExactMatch != out[j].Match.ExactMatch {
			return out[i].Match.ExactMatch
		}
		if out[i].Match.SemanticRank != out[j].Match.SemanticRank {
			if out[i].Match.SemanticRank == 0 {
				return false
			}
			if out[j].Match.SemanticRank == 0 {
				return true
			}
			return out[i].Match.SemanticRank < out[j].Match.SemanticRank
		}
		return out[i].ID < out[j].ID
	})
	return out, nil
}

func (s *Service) exactSourceMatch(ctx context.Context, repoID, query string, result SearchSourceResult) bool {
	query = strings.TrimSpace(query)
	if query == "" {
		return false
	}
	if strings.EqualFold(query, result.ID) || strings.EqualFold(query, result.Title) || strings.EqualFold(query, result.Path) {
		return true
	}
	identities, err := s.store.GetIdentityMapScoped(ctx, repoID, result.ID)
	if err != nil {
		return false
	}
	for _, identity := range identities {
		if strings.EqualFold(query, identity.Alias) || strings.EqualFold(query, identity.AliasType+":"+identity.Alias) {
			return true
		}
	}
	return false
}

func decorateLexicalFallback(ctx context.Context, s *Service, result *SearchSourcesResult, repoID string, offset int) {
	for i := range result.Results {
		result.Results[i].Rank = offset + i + 1
		result.Results[i].Match.LexicalRank = offset + i + 1
		result.Results[i].Match.LexicalScore = result.Results[i].Score
		result.Results[i].Match.ExactMatch = s.exactSourceMatch(ctx, repoID, result.Query, result.Results[i])
		result.Results[i].Match.FusionScore = result.Results[i].Score
		if result.Results[i].Citations == nil {
			result.Results[i].Citations = []SearchCitation{}
		}
	}
}

func pageSearchSources(results []SearchSourceResult, offset, limit int) []SearchSourceResult {
	if offset >= len(results) {
		return []SearchSourceResult{}
	}
	end := offset + limit
	if end > len(results) {
		end = len(results)
	}
	return append([]SearchSourceResult(nil), results[offset:end]...)
}

func searchCoverageFromRAG(result rag.SearchResult) SearchRAGCoverage {
	coverage := SearchRAGCoverage{
		EligibleChunks:    result.Coverage.TotalChunks,
		EmbeddedChunks:    result.Coverage.EmbeddedChunks,
		MissingChunks:     result.Coverage.MissingChunks,
		StaleChunks:       result.Coverage.StaleChunks,
		FailedChunks:      result.Coverage.FailedChunks,
		NamespaceID:       result.Namespace.ID,
		ContentGeneration: result.Coverage.ContentGeneration,
		CoveredGeneration: result.Coverage.CoveredGeneration,
	}
	if coverage.EligibleChunks > 0 {
		coverage.Ratio = float64(coverage.EmbeddedChunks) / float64(coverage.EligibleChunks)
	}
	return coverage
}

func ragFallbackReason(result rag.SearchResult) string {
	switch result.Status {
	case rag.RAGSearchStatusReady, rag.RAGSearchStatusNoResults:
		return ""
	case rag.RAGSearchStatusNoNamespace:
		return "rag_namespace_missing"
	case rag.RAGSearchStatusEmpty:
		return "rag_namespace_stale"
	case rag.RAGSearchStatusProviderNotReady:
		if result.FailureClass == rag.ProviderFailureModelMissing {
			return "model_missing"
		}
		if result.Provider.Ready {
			if result.FailureClass == rag.ProviderFailureTimeout {
				return "query_embedding_timeout"
			}
			return "query_embedding_failed"
		}
		return "provider_unavailable"
	default:
		return "provider_unavailable"
	}
}

func ragStateForFallback(result rag.SearchResult) string {
	switch result.Status {
	case rag.RAGSearchStatusNoNamespace:
		return "missing"
	case rag.RAGSearchStatusEmpty:
		return "stale"
	default:
		return "unavailable"
	}
}

func (s *Service) GetSource(ctx context.Context, req GetSourceRequest) (SourceRecord, error) {
	repoID, err := s.requireRepo(ctx, req.RepoID, "get")
	if err != nil {
		return SourceRecord{}, err
	}
	id, err := s.resolveScopedStableID(ctx, repoID, req.ID, req.AliasType, req.AliasID)
	if err != nil {
		return SourceRecord{}, err
	}
	source, err := s.store.GetSourceScoped(ctx, repoID, id)
	if err != nil {
		return SourceRecord{}, normalizeError(err, "source", id)
	}
	links, err := s.store.ListLinks(ctx, cache.LinkFilter{RepoID: repoID, SourceID: source.ID})
	if err != nil {
		return SourceRecord{}, normalizeError(err, "links", source.ID)
	}
	backlinks, err := s.getBacklinks(ctx, GetBacklinksRequest{RepoID: repoID, ID: source.ID})
	if err != nil && !IsCacheEmpty(err) {
		return SourceRecord{}, err
	}
	return sourceRecord(source, links, backlinks.Backlinks), nil
}

func (s *Service) ListSources(ctx context.Context, req ListSourcesRequest) (ListSourcesResult, error) {
	limit, err := SourceQueryPage(req.Limit, req.Offset)
	if err != nil {
		return ListSourcesResult{}, err
	}
	req.Limit = limit
	return s.listSources(ctx, req)
}

// Aggregate sync-status retains its historical unbounded omission contract.
// Public list/search pagination must not silently truncate aggregate coverage.
func (s *Service) listSources(ctx context.Context, req ListSourcesRequest) (ListSourcesResult, error) {
	repoID, err := s.requireRepo(ctx, req.RepoID, "list")
	if err != nil {
		return ListSourcesResult{}, err
	}
	sources, err := s.store.ListSources(ctx, cache.SourceFilter{RepoID: repoID, Kind: req.Kind, Status: req.Status, Provenance: cache.Provenance(req.Provenance), Limit: req.limitPlusOffset()})
	if err != nil {
		return ListSourcesResult{}, normalizeError(err, "sources", "")
	}
	if len(sources) == 0 {
		return ListSourcesResult{}, ErrCacheEmpty{Message: "cache has no sources"}
	}
	sort.SliceStable(sources, func(i, j int) bool {
		if sources[i].ID != sources[j].ID {
			return sources[i].ID < sources[j].ID
		}
		return sources[i].Path < sources[j].Path
	})
	sources = sliceSources(sources, req.Offset, req.Limit)
	out := make([]SourceSummary, 0, len(sources))
	for _, source := range sources {
		out = append(out, sourceSummary(source))
	}
	return ListSourcesResult{RepoID: repoID, Results: out, Limit: req.Limit, Offset: req.Offset}, nil
}

func (s *Service) GetBacklinks(ctx context.Context, req GetBacklinksRequest) (BacklinksResult, error) {
	limit, err := SourceQueryPage(req.Limit, req.Offset)
	if err != nil {
		return BacklinksResult{}, err
	}
	req.Limit = limit
	return s.getBacklinks(ctx, req)
}

// Embedded source records retain their complete backlink graph; only the public
// standalone query supplies a normalized page limit.
func (s *Service) getBacklinks(ctx context.Context, req GetBacklinksRequest) (BacklinksResult, error) {
	repoID, err := s.requireRepo(ctx, req.RepoID, "backlinks")
	if err != nil {
		return BacklinksResult{}, err
	}
	id, err := s.resolveScopedStableID(ctx, repoID, req.ID, req.AliasType, req.AliasID)
	if err != nil {
		return BacklinksResult{}, err
	}
	backlinks, err := s.store.GetBacklinksScoped(ctx, repoID, id)
	if err != nil {
		return BacklinksResult{}, normalizeError(err, "backlinks", id)
	}
	sort.SliceStable(backlinks, func(i, j int) bool {
		if backlinks[i].ID != backlinks[j].ID {
			return backlinks[i].ID < backlinks[j].ID
		}
		return backlinks[i].Path < backlinks[j].Path
	})
	backlinks = sliceSources(backlinks, req.Offset, req.Limit)
	out := make([]BacklinkResult, 0, len(backlinks))
	for _, source := range backlinks {
		out = append(out, BacklinkResult{SourceSummary: sourceSummary(source), TargetID: id})
	}
	return BacklinksResult{RepoID: repoID, ID: id, Backlinks: out, Limit: req.Limit, Offset: req.Offset}, nil
}

func (s *Service) ResolveID(ctx context.Context, req ResolveIDRequest) (ResolvedID, error) {
	repoID, err := s.requireRepo(ctx, req.RepoID, "resolve")
	if err != nil {
		return ResolvedID{}, err
	}
	id, err := s.resolveScopedStableID(ctx, repoID, req.ID, req.AliasType, req.AliasID)
	if err != nil {
		return ResolvedID{}, err
	}
	source, err := s.store.GetSourceScoped(ctx, repoID, id)
	if err != nil {
		return ResolvedID{}, normalizeError(err, "source", id)
	}
	stableID, issueNumber := sourceIssueIdentity(source)
	return ResolvedID{RepoID: source.RepoID, ID: source.ID, StableSourceID: stableID, IssueNumber: issueNumber, Path: source.Path, RemoteAlias: remoteAlias(source.Aliases), Kind: source.Kind, Title: source.Title}, nil
}

func (s *Service) GetSnippet(ctx context.Context, req SnippetRequest) (SnippetResult, error) {
	repoID, err := s.requireRepo(ctx, req.RepoID, "snippet")
	if err != nil {
		return SnippetResult{}, err
	}
	id, err := s.resolveScopedStableID(ctx, repoID, req.ID, req.AliasType, req.AliasID)
	if err != nil {
		return SnippetResult{}, err
	}
	if req.LineStart > 0 || req.LineEnd > 0 {
		return s.snippetFromLines(ctx, repoID, id, req.LineStart, req.LineEnd)
	}
	if req.ChunkID != "" {
		return s.snippetFromChunk(ctx, repoID, id, req.ChunkID)
	}
	return SnippetResult{}, ErrInvalidQuery{Field: "range", Message: "line range or chunk id is required"}
}

func (s *Service) ListChunks(ctx context.Context, req ChunkQuery) (ChunkQueryResult, error) {
	repoID, err := s.requireRepo(ctx, req.RepoID, "list-chunks")
	if err != nil {
		return ChunkQueryResult{}, err
	}
	req.RepoID = repoID
	chunks, err := s.store.ListChunks(ctx, cache.ChunkFilter{RepoID: req.RepoID, SourceID: req.SourceID, RecordID: req.RecordID, SnapshotID: req.SnapshotID, Policy: string(req.Policy)})
	if err != nil {
		return ChunkQueryResult{}, normalizeError(err, "chunks", req.SourceID)
	}
	freshness, err := s.freshnessReport(ctx, repoID, req)
	if err != nil {
		return ChunkQueryResult{}, err
	}
	return index.NewMemoryChunkIndex(indexChunks(chunks)).ListChunksWithWarnings(ctx, req, freshness.Warnings)
}

func (s *Service) SearchChunks(ctx context.Context, req ChunkSearchQuery) (ChunkQueryResult, error) {
	repoID, err := s.requireRepo(ctx, req.RepoID, "search-chunks")
	if err != nil {
		return ChunkQueryResult{}, err
	}
	req.RepoID = repoID
	chunks, err := s.store.ListChunks(ctx, cache.ChunkFilter{RepoID: req.RepoID, SourceID: req.SourceID, RecordID: req.RecordID, SnapshotID: req.SnapshotID, Policy: string(req.Policy)})
	if err != nil {
		return ChunkQueryResult{}, normalizeError(err, "chunks", req.SourceID)
	}
	freshness, err := s.freshnessReport(ctx, repoID, req.ChunkQuery)
	if err != nil {
		return ChunkQueryResult{}, err
	}
	return index.NewMemoryChunkIndex(indexChunks(chunks)).SearchChunksWithWarnings(ctx, req, freshness.Warnings)
}

func (s *Service) GetChunkSnippet(ctx context.Context, req SnippetQuery) (ChunkQueryResult, error) {
	repoID, err := s.requireRepo(ctx, req.RepoID, "get-snippet")
	if err != nil {
		return ChunkQueryResult{}, err
	}
	req.RepoID = repoID
	chunks, err := s.store.ListChunks(ctx, cache.ChunkFilter{RepoID: req.RepoID, SourceID: req.SourceID, RecordID: req.RecordID, SnapshotID: req.SnapshotID, Policy: string(req.Policy)})
	if err != nil {
		return ChunkQueryResult{}, normalizeError(err, "chunks", req.SourceID)
	}
	freshnessQuery := index.ChunkQuery{RepoID: req.RepoID, SourceID: req.SourceID, RecordID: req.RecordID, SnapshotID: req.SnapshotID, Policy: req.Policy}
	freshness, err := s.freshnessReport(ctx, repoID, freshnessQuery)
	if err != nil {
		return ChunkQueryResult{}, err
	}
	return index.NewMemoryChunkIndex(indexChunks(chunks)).GetSnippetWithWarnings(ctx, req, freshness.Warnings)
}

func (s *Service) GetSyncStatus(ctx context.Context, req SyncStatusRequest) (SyncStatusResult, error) {
	repoID, err := s.requireRepo(ctx, req.RepoID, "sync-status")
	if err != nil {
		return SyncStatusResult{}, err
	}
	id, err := s.resolveScopedStableID(ctx, repoID, req.ID, req.AliasType, req.AliasID)
	if err != nil {
		return SyncStatusResult{}, err
	}
	source, err := s.store.GetSourceScoped(ctx, repoID, id)
	if err != nil {
		return SyncStatusResult{}, normalizeError(err, "source", id)
	}
	status, err := s.store.GetSyncStatusScoped(ctx, repoID, id)
	if err != nil {
		return SyncStatusResult{}, normalizeError(err, "sync status", id)
	}
	freshness := freshnessFor(source, status)
	result := SyncStatusResult{RepoID: status.RepoID, SourceID: status.SourceID, RemoteType: status.RemoteType, RemoteID: status.RemoteID, RemoteRevision: status.RemoteRevision, Status: status.Status, Freshness: freshness, Provenance: string(source.Provenance), LocalUpdatedAt: source.UpdatedAt.UTC(), LastFetchedAt: status.LastFetchedAt.UTC()}
	if source.Kind == "issue" {
		queued, ok, err := s.store.GetIssueCommentSync(ctx, repoID, id)
		if err != nil {
			return SyncStatusResult{}, err
		}
		if ok {
			result.IssueComments = &IssueCommentCoverageStatus{Status: queued.Status, RemoteRevision: queued.RemoteRevision, ExpectedCount: queued.ExpectedCount, Attempts: queued.Attempts, LastErrorClass: queued.LastErrorClass, RetryAfter: queued.RetryAfter, LastAttemptAt: queued.LastAttemptAt.UTC(), QueueUpdatedAt: queued.UpdatedAt.UTC()}
		}
	}
	return result, nil
}

func (s *Service) SyncStatus(ctx context.Context, req ListSourcesRequest) (SyncStatusSummaryResult, error) {
	repoID, err := s.requireRepo(ctx, req.RepoID, "sync-status")
	if err != nil {
		return SyncStatusSummaryResult{}, err
	}
	listed, err := s.listSources(ctx, req)
	if err != nil {
		if IsCacheEmpty(err) {
			return SyncStatusSummaryResult{RepoID: repoID, CacheEmpty: true, Limit: req.Limit, Offset: req.Offset}, nil
		}
		return SyncStatusSummaryResult{}, err
	}
	completedEvents, err := s.store.ListCompletedSyncEventsScoped(ctx, repoID)
	if err != nil {
		return SyncStatusSummaryResult{}, err
	}
	sourceToLatestCompleted := map[string]cache.SyncEvent{}
	for _, event := range completedEvents {
		existing, ok := sourceToLatestCompleted[event.SourceID]
		if !ok || event.CompletedAt.After(existing.CompletedAt) {
			sourceToLatestCompleted[event.SourceID] = event
		}
	}
	result := SyncStatusSummaryResult{RepoID: repoID, Limit: req.Limit, Offset: req.Offset, Results: []SyncStatusResult{}}
	var latestCompleted cache.SyncEvent
	for _, source := range listed.Results {
		status, err := s.GetSyncStatus(ctx, SyncStatusRequest{RepoID: repoID, ID: source.ID})
		if err != nil {
			if IsNotFound(err) {
				result.Warnings = append(result.Warnings, "sync_status_missing:"+source.ID)
				result.StaleCount++
				continue
			}
			return SyncStatusSummaryResult{}, err
		}
		result.Results = append(result.Results, status)
		if status.Freshness == FreshnessFresh || status.Status == "fresh" {
			result.FreshCount++
		} else {
			result.StaleCount++
		}
		if status.LastFetchedAt.After(result.LastSyncAt) {
			result.LastSyncAt = status.LastFetchedAt.UTC()
		}
		if event, ok := sourceToLatestCompleted[source.ID]; ok && event.CompletedAt.After(latestCompleted.CompletedAt) {
			latestCompleted = event
		}
	}
	if !latestCompleted.StartedAt.IsZero() {
		result.LastSyncStartedAt = latestCompleted.StartedAt.UTC()
	}
	if !latestCompleted.CompletedAt.IsZero() {
		result.LastSyncCompletedAt = latestCompleted.CompletedAt.UTC()
	}
	result.ZeroDelta = latestCompleted.ZeroDelta
	result.CacheEmpty = len(result.Results) == 0 && len(result.Warnings) == 0
	queue, err := s.store.IssueCommentSyncSummary(ctx, repoID)
	if err != nil {
		return SyncStatusSummaryResult{}, err
	}
	result.IssueComments = &IssueCommentQueueSummary{Phase: "status", Pending: queue.Pending, Deferred: queue.Deferred, Complete: queue.Complete, Total: queue.Total}
	return result, nil
}

func (s *Service) RecentChanges(ctx context.Context, req RecentChangesRequest) (RecentChangesResult, error) {
	repoID, err := s.requireRepo(ctx, req.RepoID, "recent")
	if err != nil {
		return RecentChangesResult{}, err
	}
	sources, err := s.store.ListSources(ctx, cache.SourceFilter{RepoID: repoID, Kind: req.Kind, Status: req.Status})
	if err != nil {
		return RecentChangesResult{}, normalizeError(err, "sources", "")
	}
	if len(sources) == 0 {
		return RecentChangesResult{}, ErrCacheEmpty{Message: "cache has no sources"}
	}
	sort.SliceStable(sources, func(i, j int) bool {
		if !sources[i].UpdatedAt.Equal(sources[j].UpdatedAt) {
			return sources[i].UpdatedAt.After(sources[j].UpdatedAt)
		}
		return sources[i].ID < sources[j].ID
	})
	sources = sliceSources(sources, req.Offset, req.Limit)
	out := make([]RecentChangeResult, 0, len(sources))
	for _, source := range sources {
		stableID, issueNumber := sourceIssueIdentity(source)
		out = append(out, RecentChangeResult{RepoID: source.RepoID, ID: source.ID, StableSourceID: stableID, IssueNumber: issueNumber, Path: source.Path, Title: source.Title, Kind: source.Kind, Status: source.Status, UpdatedAt: source.UpdatedAt.UTC()})
	}
	return RecentChangesResult{RepoID: repoID, Results: out, Limit: req.Limit, Offset: req.Offset}, nil
}

func (s *Service) LinkCheck(ctx context.Context, req LinkCheckRequest) (LinkCheckResult, error) {
	repoID, err := s.requireRepo(ctx, req.RepoID, "link-check")
	if err != nil {
		return LinkCheckResult{}, err
	}
	links, err := s.store.ListLinks(ctx, cache.LinkFilter{RepoID: repoID})
	if err != nil {
		return LinkCheckResult{}, normalizeError(err, "links", "")
	}
	result := LinkCheckResult{RepoID: repoID, CheckedCount: len(links), SuggestedAliases: map[string][]string{}}
	for _, link := range links {
		if _, err := s.store.GetSourceScoped(ctx, repoID, link.TargetID); err != nil {
			if isCacheNotFound(err) {
				result.BrokenLinks = append(result.BrokenLinks, BrokenLinkResult{RepoID: link.RepoID, SourceID: link.SourceID, TargetID: link.TargetID, Kind: link.Kind, Text: link.Text})
				continue
			}
			return LinkCheckResult{}, normalizeError(err, "source", link.TargetID)
		}
	}
	result.BrokenCount = len(result.BrokenLinks)
	if req.Strict && result.BrokenCount > 0 {
		return result, ErrLinkCheckFailed{BrokenCount: result.BrokenCount}
	}
	return result, nil
}

func (s *Service) StaleIndex(ctx context.Context, req StaleIndexRequest) (StaleIndexResult, error) {
	repoID, err := s.requireRepo(ctx, req.RepoID, "stale-index")
	if err != nil {
		return StaleIndexResult{}, err
	}
	report, err := s.freshnessReport(ctx, repoID, index.ChunkQuery{RepoID: repoID})
	if err != nil {
		return StaleIndexResult{}, err
	}
	affected := map[string]struct{}{}
	missing := map[string]struct{}{}
	var lastIndexed time.Time
	for _, record := range report.Records {
		if !record.IndexedAt.IsZero() && record.IndexedAt.After(lastIndexed) {
			lastIndexed = record.IndexedAt
		}
		if record.WarningCode == "" {
			continue
		}
		affected[record.SourceID] = struct{}{}
		for _, target := range record.MissingTargetIDs {
			missing[target] = struct{}{}
		}
	}
	result := StaleIndexResult{RepoID: repoID, StaleCount: len(report.Warnings), AffectedSourceIDs: sortedKeys(affected), MissingTargetIDs: sortedKeys(missing), LastIndexedAt: lastIndexed.UTC(), Warnings: report.Warnings, Records: report.Records}
	if req.Strict && result.StaleCount > 0 {
		return result, ErrStaleIndex{StaleCount: result.StaleCount}
	}
	return result, nil
}

func (s *Service) ExportSnapshot(ctx context.Context, req ExportSnapshotRequest) (ExportSnapshotResult, error) {
	format := normalizeSnapshotFormat(req.Format)
	snapshot, err := s.storedSnapshot(ctx, req)
	if err != nil {
		return ExportSnapshotResult{}, err
	}
	content, err := renderSnapshotContent(snapshot, format)
	if err != nil {
		return ExportSnapshotResult{}, err
	}
	hash := sha256.Sum256(content)
	result := ExportSnapshotResult{RepoID: snapshot.RepoID, SnapshotID: req.SnapshotID, Format: format, RecordCount: len(snapshot.Sources), GeneratedAt: snapshot.CreatedAt, ContentHash: hex.EncodeToString(hash[:]), InlineContent: string(content), Warnings: warningCodes(snapshot.Warnings)}
	if result.SnapshotID == "" {
		result.SnapshotID = snapshot.ManifestHash
		if len(result.SnapshotID) > 32 {
			result.SnapshotID = result.SnapshotID[:32]
		}
	}
	if req.OutputPath != "" {
		if err := os.WriteFile(req.OutputPath, content, 0o600); err != nil {
			return ExportSnapshotResult{}, err
		}
		result.OutputPath = req.OutputPath
		if req.InlineLimit <= 0 || len(content) > req.InlineLimit {
			result.InlineContent = ""
		}
	}
	return result, nil
}

func (s *Service) DiffSnapshot(ctx context.Context, req DiffSnapshotRequest) (DiffSnapshotResult, error) {
	format := normalizeSnapshotFormat(req.Format)
	if req.BaseSnapshotID == "" || req.HeadSnapshotID == "" {
		if req.Base.Kind != "" || req.Head.Kind != "" || req.BaseContent != "" || req.BaseSnapshotContent != "" {
			baseRef := req.Base
			if baseRef.Kind == "" {
				base := req.BaseContent
				if base == "" {
					base = req.BaseSnapshotContent
				}
				if base == "" {
					baseRef = SnapshotRef{Kind: "current", Format: format}
				} else {
					baseRef = SnapshotRef{Kind: "bytes", Bytes: []byte(base), Format: format}
				}
			}
			headRef := req.Head
			if headRef.Kind == "" {
				headRef = SnapshotRef{Kind: "current", Format: format}
			}
			base, err := s.loadSnapshotRef(ctx, req.RepoID, baseRef, format)
			if err != nil {
				return DiffSnapshotResult{}, err
			}
			head, err := s.loadSnapshotRef(ctx, req.RepoID, headRef, format)
			if err != nil {
				return DiffSnapshotResult{}, err
			}
			result := diffSnapshots(base, head)
			result.RepoID = req.RepoID
			result.BaseSnapshotID = req.BaseSnapshotID
			result.HeadSnapshotID = req.HeadSnapshotID
			if result.BaseSnapshotID == "" {
				result.BaseSnapshotID = req.Base.Path
			}
			if result.HeadSnapshotID == "" {
				result.HeadSnapshotID = req.Head.Path
			}
			result.Format = format

			baseBytes, _ := renderSnapshotContent(base, format)
			headBytes, _ := renderSnapshotContent(head, format)
			result.DiffText = simpleDiff(string(baseBytes), string(headBytes))
			return result, nil
		}
		current, err := s.createStoredSnapshot(ctx, req.RepoID, ExportSnapshotRequest{RepoID: req.RepoID, Format: format, IncludeBody: true})
		if err != nil {
			return DiffSnapshotResult{}, err
		}
		req.BaseSnapshotID = current.ID
		req.HeadSnapshotID = current.ID
	}
	base, err := s.storedSnapshot(ctx, ExportSnapshotRequest{RepoID: req.RepoID, SnapshotID: req.BaseSnapshotID, Format: format, IncludeBody: true})
	if err != nil {
		if IsNotFound(err) {
			return DiffSnapshotResult{}, ErrNotFound{Kind: "base_id", ID: req.BaseSnapshotID}
		}
		return DiffSnapshotResult{}, err
	}
	head, err := s.storedSnapshot(ctx, ExportSnapshotRequest{RepoID: req.RepoID, SnapshotID: req.HeadSnapshotID, Format: format, IncludeBody: true})
	if err != nil {
		if IsNotFound(err) {
			return DiffSnapshotResult{}, ErrNotFound{Kind: "head_id", ID: req.HeadSnapshotID}
		}
		return DiffSnapshotResult{}, err
	}
	result := diffSnapshots(base, head)
	result.RepoID = req.RepoID
	result.BaseSnapshotID = req.BaseSnapshotID
	result.HeadSnapshotID = req.HeadSnapshotID
	result.Format = format
	baseBytes, _ := renderSnapshotContent(base, format)
	headBytes, _ := renderSnapshotContent(head, format)
	result.DiffText = simpleDiff(string(baseBytes), string(headBytes))
	return result, nil
}

func (s *Service) Ingest(ctx context.Context, req OperationRequest) (OperationResult, error) {
	if err := ctx.Err(); err != nil {
		return OperationResult{}, err
	}
	sources, err := s.store.ListSources(ctx, cache.SourceFilter{})
	if err != nil && !isCacheNotFound(err) {
		return OperationResult{}, normalizeError(err, "sources", "")
	}
	if len(sources) == 0 {
		if err := s.seedMinimumCorpus(ctx); err != nil {
			return OperationResult{}, err
		}
		sources, err = s.store.ListSources(ctx, cache.SourceFilter{})
		if err != nil {
			return OperationResult{}, normalizeError(err, "sources", "")
		}
	}
	return OperationResult{Command: "ingest", Status: "ok", ProcessedCount: len(sources), Evidence: operationMode(req.Mode), GeneratedAt: s.now().UTC()}, nil
}

func (s *Service) Index(ctx context.Context, req OperationRequest) (OperationResult, error) {
	if err := ctx.Err(); err != nil {
		return OperationResult{}, err
	}
	repoID, err := s.requireRepo(ctx, req.RepoID, "index")
	if err != nil {
		return OperationResult{}, err
	}
	sources, err := s.store.ListSources(ctx, cache.SourceFilter{RepoID: repoID})
	if err != nil {
		if isCacheNotFound(err) {
			return OperationResult{Command: "index", Status: "ok", Evidence: operationMode(req.Mode), GeneratedAt: s.now().UTC()}, nil
		}
		return OperationResult{}, normalizeError(err, "sources", "")
	}
	processed := 0
	for _, source := range sources {
		chunks := index.ChunkSource(indexSourceRecord(source), index.ParseSource(indexSourceRecord(source)))
		for _, chunk := range chunks {
			if _, err := s.store.UpsertChunk(ctx, cacheChunk(chunk)); err != nil {
				return OperationResult{}, normalizeError(err, "chunk", chunk.ID)
			}
		}
		processed++
	}
	stored, err := s.createStoredSnapshot(ctx, repoID, ExportSnapshotRequest{RepoID: repoID, Format: "json", IncludeBody: true})
	if err != nil {
		return OperationResult{}, err
	}
	return OperationResult{Command: "index", Status: "ok", ProcessedCount: processed, Evidence: "snapshot_id=" + stored.ID, GeneratedAt: s.now().UTC()}, nil
}

func (s *Service) SyncToCache(ctx context.Context, req SyncRequest) (_ SyncResult, retErr error) {
	ctx, retainAttempt := beginSyncHTTPAttempt(ctx)
	defer func() { retErr = retainAttempt(retErr) }()
	if err := ctx.Err(); err != nil {
		return SyncResult{}, diagnostics.RetainPreDispatchInterruption(err)
	}
	repoID, err := s.requireRepo(ctx, req.RepoID, "sync")
	if err != nil {
		return SyncResult{}, err
	}
	req.RepoID = repoID
	key := strings.TrimSpace(req.IdempotencyKey)
	if key == "" {
		key = s.syncIdempotencyKey(req)
	}
	if event, err := s.store.GetSyncEventByKey(ctx, key); err != nil {
		return SyncResult{}, err
	} else if event != nil {
		switch event.Status {
		case "succeeded":
			result := syncResultFromEvent(*event, s.now().UTC())
			result.Replayed = true
			return result, nil
		case "in_progress":
			return SyncResult{}, ErrSyncInProgress{EventID: event.ID, IdempotencyKey: key}
		}
	}
	lease, err := s.store.AcquireWriter(ctx, cache.WriterRequest{Operation: "sync", RepoID: repoID, LockPath: s.lockPath})
	if err != nil {
		return SyncResult{}, err
	}
	defer s.store.ReleaseWriter(context.Background(), lease)
	remoteType, remoteID, err := s.syncTarget(ctx, req)
	if err != nil {
		return SyncResult{}, err
	}
	if err := s.validateRepoScope(ctx, repoID, remoteType); err != nil {
		return SyncResult{}, err
	}
	eventID := syncEventID(key)
	syncStartedAt := s.now().UTC()
	inProgress := cache.SyncEvent{RepoID: repoID, ID: eventID, SourceID: s.syncEventSourceID(ctx, req, remoteType, remoteID), RemoteType: remoteType, RemoteID: remoteID, Status: "in_progress", IdempotencyKey: key, Message: "sync started", CreatedAt: syncStartedAt, StartedAt: syncStartedAt}
	inProgressRecorded := true
	if _, err := s.store.GetSourceScoped(ctx, repoID, inProgress.SourceID); err == nil {
		if err := s.store.RecordSyncEvent(ctx, inProgress); err != nil {
			return SyncResult{}, err
		}
	} else if isCacheNotFound(err) {
		inProgressRecorded = false
	} else {
		return SyncResult{}, err
	}
	if err := s.store.IntegrityCheck(ctx); err != nil {
		failure := s.normalizeSyncFailure(err, req, remoteType, remoteID)
		if inProgressRecorded {
			_ = s.store.RecordSyncEvent(ctx, failedSyncEvent(inProgress, failure, s.now().UTC()))
		}
		return SyncResult{}, failure
	}
	graph, counts, err := s.fetchAndStage(ctx, req, remoteType, remoteID)
	if err != nil {
		failure := s.normalizeSyncFailure(err, req, remoteType, remoteID)
		if inProgressRecorded {
			_ = s.store.RecordSyncEvent(ctx, failedSyncEvent(inProgress, failure, s.now().UTC()))
		}
		if markErr := s.markMissingRemote(ctx, inProgress, failure, remoteType, remoteID); markErr != nil {
			return SyncResult{}, markErr
		}
		return SyncResult{}, failure
	}
	if err := s.validateLiveSourceGraph(graph); err != nil {
		failure := s.normalizeSyncFailure(err, req, remoteType, remoteID)
		if inProgressRecorded {
			_ = s.store.RecordSyncEvent(ctx, failedSyncEvent(inProgress, failure, s.now().UTC()))
		}
		return SyncResult{}, failure
	}
	syncCompletedAt := s.now().UTC()
	revision := ""
	if graph.SyncStatus != nil {
		revision = graph.SyncStatus.RemoteRevision
	}
	zeroDelta := counts.Fetched > 0 && counts.Skipped == counts.Fetched && counts.Updated == 0 && counts.Inserted == 0 && counts.Conflicts == 0
	graph.SyncEvents = append(graph.SyncEvents, cache.SyncEvent{ID: eventID, SourceID: graph.Source.ID, RemoteType: remoteType, RemoteID: remoteID, RemoteRevision: revision, Status: "succeeded", IdempotencyKey: key, Message: syncEventMessage(counts), CreatedAt: syncCompletedAt, StartedAt: syncStartedAt, CompletedAt: syncCompletedAt, ZeroDelta: zeroDelta})
	if err := s.store.UpsertSyncGraph(ctx, s.syncGraphFromSourceGraph(req.RepoID, graph)); err != nil {
		if inProgressRecorded {
			_ = s.store.RecordSyncEvent(ctx, failedSyncEvent(inProgress, err, s.now().UTC()))
		}
		return SyncResult{}, err
	}
	if err := s.recordTargetedIssueCommentCoverage(ctx, remoteType, graph, counts); err != nil {
		if inProgressRecorded {
			_ = s.store.RecordSyncEvent(ctx, failedSyncEvent(inProgress, err, s.now().UTC()))
		}
		return SyncResult{}, err
	}
	if err := s.store.Checkpoint(ctx, "sync-complete"); err != nil {
		if inProgressRecorded {
			_ = s.store.RecordSyncEvent(ctx, failedSyncEvent(inProgress, err, s.now().UTC()))
		}
		return SyncResult{}, err
	}
	stored, err := s.store.GetSourceScoped(ctx, repoID, graph.Source.ID)
	if err != nil {
		return SyncResult{}, err
	}
	return SyncResult{IdempotencyKey: key, Status: "succeeded", Counts: counts, SyncEventID: eventID, Freshness: string(FreshnessFresh), Record: sourceSummary(stored), GeneratedAt: syncCompletedAt, StartedAt: syncStartedAt, CompletedAt: syncCompletedAt, ZeroDelta: zeroDelta}, nil
}

// SyncResources processes multiple SyncRequest values independently via SyncToCache.
// Each resource is synced atomically; failures do not short-circuit remaining resources.
// On any failure, the returned (*SyncResourcesResult, error) pair carries a PartialSyncError
// with structured per-resource failure details. Results always contains entries from
// successful SyncToCache calls; Failures contains entries from failed calls.
// Callers should check PartialSyncError before using the result.
func (s *Service) SyncResources(ctx context.Context, reqs []SyncRequest) (_ *SyncResourcesResult, retErr error) {
	ctx, retainAttempt := beginSyncHTTPAttempt(ctx)
	defer func() { retErr = retainAttempt(retErr) }()
	result := &SyncResourcesResult{
		Results:  make([]SyncResult, 0, len(reqs)),
		Failures: make([]ResourceError, 0),
	}
	var partial *PartialSyncError
	for i, req := range reqs {
		if err := ctx.Err(); err != nil {
			re := newResourceErrorWithMessage(req.StableID, "", diagnostics.RetainPreDispatchInterruption(err), fmt.Sprintf("sync resources: context cancelled at request %d", i))
			result.Failures = append(result.Failures, re)
			continue
		}
		syncResult, err := s.SyncToCache(ctx, req)
		if err != nil {
			remoteType := ""
			if req.AliasType != "" {
				remoteType = req.AliasType
			}
			sourceID := req.StableID
			if sourceID == "" {
				sourceID = req.RemoteAlias
			}
			re := newResourceError(sourceID, remoteType, err)
			result.Failures = append(result.Failures, re)
			continue
		}
		result.Results = append(result.Results, syncResult)
	}
	result.SuccessCount = len(result.Results)
	result.FailureCount = len(result.Failures)
	if result.FailureCount > 0 {
		partial = &PartialSyncError{
			Errors:       result.Failures,
			SuccessCount: result.SuccessCount,
			FailureCount: result.FailureCount,
		}
	}
	if partial != nil {
		return result, partial
	}
	return result, nil
}

func (s *Service) BulkSyncIssues(ctx context.Context, req BulkSyncRequest) (_ *SyncResourcesResult, retErr error) {
	ctx, retainAttempt := beginSyncHTTPAttempt(ctx)
	defer func() { retErr = retainAttempt(retErr) }()
	if err := ctx.Err(); err != nil {
		if req.Bounds != nil {
			diag := SyncDiagnosticCancelled
			if errors.Is(err, context.DeadlineExceeded) {
				diag = SyncDiagnosticTimeout
			}
			return nil, &PartialSyncError{Errors: nil, SuccessCount: 0, FailureCount: 0, Diagnostic: diag}
		}
		return nil, err
	}
	ctx = withBulkRateLimitProgress(ctx, bulkProgressChan(req))
	repoID, err := s.requireRepo(ctx, req.RepoID, "bulk-sync-issues")
	if err != nil {
		return nil, err
	}
	req.RepoID = repoID
	ctx, releaseWriter, err := s.acquireBulkWriter(ctx, repoID, "bulk-sync-issues")
	if err != nil {
		return nil, err
	}
	defer releaseWriter()
	s.ensureBulkIdempotencyKey(&req, "issues")
	if err := s.validateRepoScope(ctx, repoID, "issues"); err != nil {
		return nil, err
	}
	route, err := s.BuildAdapterRoute(ctx, repoID, RepositoryScopeIssues)
	if err != nil {
		return nil, err
	}

	if req.Bounds == nil {
		page, err := s.client.ListIssues(ctx, gitcode.IssueListRequest{Owner: route.Owner, Repo: route.Name, State: "all", OrderBy: "updated_at", Direction: "desc", Page: req.Page, PerPage: req.PerPage})
		if err != nil && !recoverableIssueListPage(err) {
			page.Items = nil
		}
		result := &SyncResourcesResult{Results: make([]SyncResult, 0, len(page.Items)), Failures: make([]ResourceError, 0)}
		if err == nil || len(page.Items) > 0 {
			result.PagesListed = 1
		}
		result.RecordsListed = len(page.Items)
		beforeCount := len(result.Results)
		beforeDeferred := syncResultsDeferredCount(result.Results)
		s.stageIssuePage(ctx, req, page.Items, result, 0)
		emitProgress(req.ProgressChan, ProgressEvent{Collection: "issues", Page: firstNonZeroInt(req.Page, 1), RecordsFetched: len(result.Results) - beforeCount, RecordsDeferred: syncResultsDeferredCount(result.Results) - beforeDeferred})
		if err != nil {
			normalized := s.normalizeSyncFailure(err, SyncRequest{RepoID: req.RepoID, RemoteAlias: "issue:*"}, "issues", "*")
			result.Failures = append(result.Failures, newResourceErrorWithMessage("issue:*", "issues", normalized, err.Error()))
			result.TraversalStatus = "partial"
		}
		result.SuccessCount = len(result.Results)
		result.FailureCount = len(result.Failures)
		if summaryErr := s.attachIssueCommentQueueSummary(ctx, result, repoID, "parent_backfill"); summaryErr != nil {
			if result.FailureCount > 0 {
				return result, partialSyncWithLocalCause(result, summaryErr)
			}
			return result, summaryErr
		}
		if result.FailureCount > 0 {
			return result, &PartialSyncError{Errors: result.Failures, SuccessCount: result.SuccessCount, FailureCount: result.FailureCount}
		}
		return result, nil
	}

	return s.bulkSyncIssuesBounded(ctx, req, route)
}

func (s *Service) bulkSyncIssuesBounded(ctx context.Context, req BulkSyncRequest, route RepositoryRoute) (*SyncResourcesResult, error) {
	bounds := req.Bounds
	result := &SyncResourcesResult{Results: make([]SyncResult, 0), Failures: make([]ResourceError, 0), Ordering: syncOrderingUpdatedAtDesc}
	watermark, watermarkOK, err := s.completeSyncWatermark(ctx, req.RepoID, "issue")
	if err != nil {
		return bulkSyncFailureResult(err, "issue:*", "issues")
	}
	setWatermarkSummary(result, watermark, watermarkOK)
	var observed syncHighWatermark
	currentPage := req.Page
	if currentPage < 1 {
		currentPage = 1
	}
	perPage := req.PerPage
	if perPage < 1 {
		perPage = 10
	}
	totalRequested := bounds.MaxRecords
	if totalRequested <= 0 && bounds.MaxPages > 0 {
		totalRequested = bounds.MaxPages * perPage
	}
	recordsAdvanced := 0
	pagesAdvanced := 0

	for {
		if ctx.Err() != nil {
			diag := SyncDiagnosticCancelled
			if errors.Is(ctx.Err(), context.DeadlineExceeded) {
				diag = SyncDiagnosticTimeout
				result.TraversalStatus = "timeout"
			} else {
				result.TraversalStatus = "cancelled"
			}
			result.SuccessCount = len(result.Results)
			result.FailureCount = len(result.Failures)
			s.recordSyncFrontierBestEffort(ctx, req.RepoID, "issue", result, observed)
			return result, &PartialSyncError{Errors: result.Failures, SuccessCount: result.SuccessCount, FailureCount: result.FailureCount, Diagnostic: diag, TotalRequested: totalRequested}
		}
		if bounds.MaxListPages > 0 && result.PagesListed >= bounds.MaxListPages {
			result.StopReason, result.TraversalStatus = "max_list_pages", "bounded"
			break
		}
		if bounds.MaxPages > 0 && pagesAdvanced >= bounds.MaxPages {
			result.StopReason = "max_pages"
			result.TraversalStatus = "bounded"
			break
		}
		if bounds.MaxRecords > 0 && recordsAdvanced >= bounds.MaxRecords {
			result.StopReason = "max_records"
			result.TraversalStatus = "bounded"
			break
		}
		page, err := s.client.ListIssues(ctx, gitcode.IssueListRequest{Owner: route.Owner, Repo: route.Name, State: "all", OrderBy: "updated_at", Direction: "desc", Page: currentPage, PerPage: perPage})
		if err != nil {
			if recoverableIssueListPage(err) && len(page.Items) > 0 {
				result.PagesListed++
				result.RecordsListed += len(page.Items)
				observeIssueHighWatermark(&observed, page.Items)
				items, _, skippedByWatermark := filterIssueSummariesByCompleteWatermark(page.Items, watermark, watermarkOK)
				result.SkippedByWatermark += skippedByWatermark
				remaining := 0
				if bounds.MaxRecords > 0 {
					remaining = bounds.MaxRecords - recordsAdvanced
				}
				beforeCount := len(result.Results)
				beforeDeferred := syncResultsDeferredCount(result.Results)
				advanced, _ := s.stageIssuePage(ctx, req, items, result, remaining)
				recordsAdvanced += advanced
				emitProgress(bounds.ProgressChan, ProgressEvent{Collection: "issues", Page: currentPage, RecordsFetched: len(result.Results) - beforeCount, RecordsDeferred: syncResultsDeferredCount(result.Results) - beforeDeferred})
			}
			result.SuccessCount = len(result.Results)
			result.TraversalStatus = "partial"
			re := newResourceErrorWithMessage("issue:*", "issues", s.normalizeSyncFailure(err, SyncRequest{RepoID: req.RepoID, RemoteAlias: "issue:*"}, "issues", "*"), err.Error())
			result.Failures = append(result.Failures, re)
			result.FailureCount = len(result.Failures)
			if summaryErr := s.attachIssueCommentQueueSummary(ctx, result, req.RepoID, "parent_backfill"); summaryErr != nil {
				return result, partialSyncWithLocalCause(result, summaryErr)
			}
			s.recordSyncFrontierBestEffort(ctx, req.RepoID, "issue", result, observed)
			return result, &PartialSyncError{Errors: result.Failures, SuccessCount: result.SuccessCount, FailureCount: result.FailureCount, TotalRequested: totalRequested}
		}
		result.PagesListed++
		result.RecordsListed += len(page.Items)
		observeIssueHighWatermark(&observed, page.Items)
		items, stopByWatermark, skippedByWatermark := filterIssueSummariesByCompleteWatermark(page.Items, watermark, watermarkOK)
		result.SkippedByWatermark += skippedByWatermark
		beforeCount := len(result.Results)
		beforeDeferred := syncResultsDeferredCount(result.Results)
		remaining := 0
		if bounds.MaxRecords > 0 {
			remaining = bounds.MaxRecords - recordsAdvanced
		}
		advanced, boundedByRecords := s.stageIssuePage(ctx, req, items, result, remaining)
		recordsAdvanced += advanced
		if advanced > 0 {
			pagesAdvanced++
		}
		recordsFetched := len(result.Results) - beforeCount
		emitProgress(bounds.ProgressChan, ProgressEvent{Collection: "issues", Page: currentPage, RecordsFetched: recordsFetched, RecordsDeferred: syncResultsDeferredCount(result.Results) - beforeDeferred})
		if stopByWatermark {
			result.StopReason = "watermark"
			result.TraversalStatus = "complete"
			result.WatermarkStatus = "used"
			result.WatermarkReason = "previous_complete_frontier"
			break
		}
		if len(page.Items) < perPage {
			result.StopReason = "end_of_collection"
			result.TraversalStatus = "complete"
			break
		}
		if boundedByRecords {
			result.StopReason = "max_records"
			result.TraversalStatus = "bounded"
			break
		}
		currentPage++
	}
	result.SuccessCount = len(result.Results)
	result.FailureCount = len(result.Failures)
	if summaryErr := s.attachIssueCommentQueueSummary(ctx, result, req.RepoID, "parent_backfill"); summaryErr != nil {
		return result, summaryErr
	}
	if result.FailureCount > 0 {
		result.TraversalStatus = "partial"
		s.recordSyncFrontierBestEffort(ctx, req.RepoID, "issue", result, observed)
		return result, &PartialSyncError{Errors: result.Failures, SuccessCount: result.SuccessCount, FailureCount: result.FailureCount}
	}
	if err := s.recordSyncFrontier(ctx, req.RepoID, "issue", result, observed); err != nil {
		return bulkSyncFailureResult(err, "issue:*", "issues")
	}
	return result, nil
}

func recoverableIssueListPage(err error) bool {
	var partial gitcode.ErrPartialResponse
	if errors.As(err, &partial) {
		return true
	}
	var malformed gitcode.ErrMalformedJSON
	return errors.As(err, &malformed)
}

func (s *Service) stageIssuePage(ctx context.Context, req BulkSyncRequest, items []gitcode.IssueSummary, result *SyncResourcesResult, maxAdvances int) (int, bool) {
	advanced := 0
	for _, summary := range items {
		if maxAdvances > 0 && advanced >= maxAdvances {
			return advanced, true
		}
		remoteID := strconv.Itoa(summary.Number)
		issue := gitcode.Issue{ID: summary.ID, Number: summary.Number, Title: summary.Title, Body: summary.Body, Status: summary.Status, State: summary.State, Comments: summary.Comments, Labels: summary.Labels, CreatedAt: summary.CreatedAt, UpdatedAt: summary.UpdatedAt}
		syncReq := SyncRequest{RepoID: req.RepoID, AliasType: "issue", AliasID: remoteID, IdempotencyKey: scopedBulkSyncKey(req.IdempotencyKey, "issue", remoteID), MaxAttempts: req.MaxAttempts, MaxSize: req.MaxSize}
		graph, counts, err := s.stageIssueParent(ctx, syncReq, "issue", remoteID, issue)
		if err != nil {
			result.Failures = append(result.Failures, newResourceError(remoteID, "issue", err))
			continue
		}
		counts.Listed = 1
		completedAt := s.now().UTC()
		eventID := syncEventID(syncReq.IdempotencyKey)
		zeroDelta := counts.Fetched > 0 && counts.Skipped == counts.Fetched && counts.Updated == 0 && counts.Inserted == 0 && counts.Conflicts == 0
		graph.SyncEvents = append(graph.SyncEvents, cache.SyncEvent{ID: eventID, SourceID: graph.Source.ID, RemoteType: "issue", RemoteID: remoteID, RemoteRevision: graph.SyncStatus.RemoteRevision, Status: "succeeded", IdempotencyKey: syncReq.IdempotencyKey, Message: syncEventMessage(counts), CreatedAt: completedAt, StartedAt: completedAt, CompletedAt: completedAt, ZeroDelta: zeroDelta})
		if err := s.store.UpsertSyncGraph(ctx, s.syncGraphFromSourceGraph(req.RepoID, graph)); err != nil {
			result.Failures = append(result.Failures, newResourceError(remoteID, "issue", err))
			continue
		}
		queued, err := s.enqueueIssueCommentSync(ctx, graph, issue)
		if err != nil {
			result.Failures = append(result.Failures, newResourceError(remoteID, "issue_comments", err))
			continue
		}
		if queued {
			counts.Deferred++
		}
		stored, err := s.store.GetSourceScoped(ctx, req.RepoID, graph.Source.ID)
		if err != nil {
			result.Failures = append(result.Failures, newResourceError(remoteID, "issue", err))
			continue
		}
		result.Results = append(result.Results, SyncResult{IdempotencyKey: syncReq.IdempotencyKey, Status: "succeeded", Counts: counts, SyncEventID: eventID, Freshness: string(FreshnessFresh), Record: sourceSummary(stored), GeneratedAt: completedAt, StartedAt: completedAt, CompletedAt: completedAt, ZeroDelta: zeroDelta})
		if counts.SkippedByRevision == 0 {
			advanced++
		}
	}
	return advanced, false
}

func (s *Service) enqueueIssueCommentSync(ctx context.Context, graph cache.SourceGraph, issue gitcode.Issue) (bool, error) {
	item, clearComments, queued, err := s.prepareIssueCommentSync(ctx, graph, issue)
	if err != nil {
		return false, err
	}
	if clearComments {
		if err := s.store.ReplaceRecordComments(ctx, item.RepoID, item.SourceID, nil); err != nil {
			return false, err
		}
	}
	if err := s.store.UpsertIssueCommentSync(ctx, item); err != nil {
		return false, err
	}
	return queued, nil
}

func (s *Service) prepareIssueCommentSync(ctx context.Context, graph cache.SourceGraph, issue gitcode.Issue) (cache.IssueCommentSync, bool, bool, error) {
	if graph.SyncStatus == nil {
		return cache.IssueCommentSync{}, false, false, errors.New("issue comment queue requires parent sync status")
	}
	now := s.now().UTC()
	item := cache.IssueCommentSync{
		RepoID:         graph.Source.RepoID,
		SourceID:       graph.Source.ID,
		IssueNumber:    issue.Number,
		RemoteID:       graph.SyncStatus.RemoteID,
		ProviderID:     issue.ID,
		RemoteRevision: graph.SyncStatus.RemoteRevision,
		ExpectedCount:  issue.Comments,
		Status:         "pending",
		UpdatedAt:      now,
	}
	if issue.Comments == 0 {
		item.Status = "complete"
	}
	existing, ok, err := s.store.GetIssueCommentSync(ctx, item.RepoID, item.SourceID)
	if err != nil {
		return cache.IssueCommentSync{}, false, false, err
	}
	if ok && existing.RemoteRevision == item.RemoteRevision {
		existing.IssueNumber = item.IssueNumber
		existing.RemoteID = item.RemoteID
		existing.ProviderID = item.ProviderID
		existing.ExpectedCount = item.ExpectedCount
		existing.UpdatedAt = now
		if issue.Comments == 0 {
			existing.Status = "complete"
			existing.LastErrorClass = ""
			existing.RetryAfter = ""
		}
		return existing, issue.Comments == 0, existing.Status != "complete", nil
	}
	return item, issue.Comments == 0, item.Status != "complete", nil
}

func (s *Service) BulkSyncIssueComments(ctx context.Context, req BulkSyncRequest) (_ *SyncResourcesResult, retErr error) {
	ctx, retainAttempt := beginSyncHTTPAttempt(ctx)
	defer func() { retErr = retainAttempt(retErr) }()
	if err := ctx.Err(); err != nil {
		return nil, err
	}
	repoID, err := s.requireRepo(ctx, req.RepoID, "bulk-sync-issue-comments")
	if err != nil {
		return nil, err
	}
	req.RepoID = repoID
	ctx, releaseWriter, err := s.acquireBulkWriter(ctx, repoID, "bulk-sync-issue-comments")
	if err != nil {
		return nil, err
	}
	defer releaseWriter()
	if err := s.validateRepoScope(ctx, repoID, "issues"); err != nil {
		return nil, err
	}
	ctx = withBulkRateLimitProgress(ctx, bulkProgressChan(req))
	var repairedSourceIDs []string
	if repairer, ok := s.store.(interface {
		RepairIssueProviderPlaceholders(context.Context, string) ([]string, error)
	}); ok {
		repairedSourceIDs, err = repairer.RepairIssueProviderPlaceholders(ctx, repoID)
		if err != nil {
			return nil, err
		}
	}
	if err := s.projectRepairedIssueCommentCache(ctx, repoID, repairedSourceIDs); err != nil {
		return nil, err
	}
	if err := s.seedLegacyIssueCommentQueue(ctx, repoID); err != nil {
		return nil, err
	}
	pending, err := s.store.ListIssueCommentSync(ctx, cache.IssueCommentSyncFilter{RepoID: repoID, Statuses: []string{"pending", "deferred"}})
	if err != nil {
		return nil, err
	}
	if len(pending) == 0 {
		result := &SyncResourcesResult{Results: []SyncResult{}, Failures: []ResourceError{}, Ordering: "queue_updated_at_asc", TraversalStatus: "complete", StopReason: "queue_empty"}
		if err := s.attachIssueCommentQueueSummary(ctx, result, repoID, "drain"); err != nil {
			return result, err
		}
		return result, nil
	}
	route, err := s.BuildAdapterRoute(ctx, repoID, RepositoryScopeIssues)
	if err != nil {
		return nil, err
	}
	if req.IncrementalQueue {
		return s.bulkSyncIssueCommentsPerIssue(ctx, req, route, "incremental_queue")
	}
	lister, aggregateAvailable := s.client.(gitcode.RepositoryIssueCommentLister)
	_, parentsComplete, frontierErr := s.completeSyncWatermark(ctx, repoID, "issue")
	if frontierErr != nil {
		return nil, frontierErr
	}
	if aggregateAvailable && parentsComplete {
		items, err := s.store.ListIssueCommentSync(ctx, cache.IssueCommentSyncFilter{RepoID: repoID})
		if err != nil {
			return nil, err
		}
		result, aggregateErr := s.bulkSyncRepositoryIssueComments(ctx, req, route, lister, pending, items)
		if aggregateErr == nil {
			return result, nil
		}
		if !repositoryIssueCommentsUnsupported(aggregateErr) {
			return result, aggregateErr
		}
		return s.bulkSyncIssueCommentsPerIssue(ctx, req, route, "aggregate_endpoint_unsupported")
	}
	reason := "aggregate_not_implemented"
	if aggregateAvailable && !parentsComplete {
		reason = "parent_frontier_incomplete"
	}
	return s.bulkSyncIssueCommentsPerIssue(ctx, req, route, reason)
}

func (s *Service) projectRepairedIssueCommentCache(ctx context.Context, repoID string, sourceIDs []string) error {
	for _, sourceID := range sourceIDs {
		item, ok, err := s.store.GetIssueCommentSync(ctx, repoID, sourceID)
		if err != nil {
			return err
		}
		if !ok {
			continue
		}
		if err := s.projectPendingIssueCommentCache(ctx, []cache.IssueCommentSync{item}); err != nil {
			return err
		}
	}
	return nil
}

func (s *Service) projectPendingIssueCommentCache(ctx context.Context, pending []cache.IssueCommentSync) error {
	for _, item := range pending {
		record, err := s.store.GetRecord(ctx, item.RepoID, item.SourceID)
		if err != nil {
			return err
		}
		if len(record.Comments) == 0 {
			continue
		}
		if err := s.projectIssueComments(ctx, item, record.Comments); err != nil {
			return err
		}
	}
	return nil
}

func (s *Service) bulkSyncIssueCommentsPerIssue(ctx context.Context, req BulkSyncRequest, route RepositoryRoute, fallbackReason string) (*SyncResourcesResult, error) {
	limit := 0
	if req.Bounds != nil {
		limit = req.Bounds.MaxRecords
		if limit <= 0 && req.Bounds.MaxPages > 0 {
			if req.IncrementalQueue {
				limit = req.Bounds.MaxPages
			} else {
				perPage := req.PerPage
				if perPage <= 0 {
					perPage = 100
				}
				limit = req.Bounds.MaxPages * perPage
			}
		}
	}
	items, err := s.store.ListIssueCommentSync(ctx, cache.IssueCommentSyncFilter{RepoID: req.RepoID, Statuses: []string{"pending", "deferred"}, Limit: limit})
	if err != nil {
		return nil, err
	}
	result := &SyncResourcesResult{Results: []SyncResult{}, Failures: []ResourceError{}, Ordering: "queue_updated_at_asc", TraversalStatus: "complete", StopReason: "queue_empty"}
	for _, item := range items {
		if err := ctx.Err(); err != nil {
			result.TraversalStatus = "cancelled"
			result.StopReason = "cancelled"
			break
		}
		result.StopReason = "queue_drained"
		syncResult, syncErr := s.syncIssueCommentItem(ctx, route, item)
		if syncErr == nil {
			result.Results = append(result.Results, syncResult)
			emitProgress(req.ProgressChan, ProgressEvent{Collection: "issue_comments", Phase: "drain", RecordsFetched: 1})
			continue
		}
		if isDeferredIssueCommentsRead(syncErr) {
			item.Status = "deferred"
			item.Attempts++
			item.LastAttemptAt = s.now().UTC()
			item.UpdatedAt = item.LastAttemptAt
			item.LastErrorClass, _, _, _ = resourceFailureMetadata(syncErr)
			if item.LastErrorClass == "" {
				item.LastErrorClass = "comments_read"
			}
			if wait := retryDelay(syncErr); wait > 0 {
				item.RetryAfter = wait.String()
			}
			if err := s.store.UpsertIssueCommentSync(ctx, item); err != nil {
				result.Failures = append(result.Failures, newResourceError(item.RemoteID, "issue_comments", err))
				continue
			}
			source, sourceErr := s.store.GetSourceScoped(ctx, req.RepoID, item.SourceID)
			if sourceErr == nil {
				now := s.now().UTC()
				result.Results = append(result.Results, SyncResult{Status: "deferred", Freshness: string(FreshnessStale), Counts: SyncCounts{Fetched: 1, FetchedDetail: 1, Deferred: 1}, Record: sourceSummary(source), GeneratedAt: now, StartedAt: now, CompletedAt: now})
			}
			emitProgress(req.ProgressChan, ProgressEvent{Collection: "issue_comments", Phase: "deferred", RecordsDeferred: 1, Endpoint: resourceEndpoint(syncErr), RetryAfter: item.RetryAfter})
			result.TraversalStatus = "deferred"
			result.StopReason = deferredIssueCommentStopReason(syncErr)
			break
		}
		result.Failures = append(result.Failures, newResourceError(item.RemoteID, "issue_comments", syncErr))
	}
	result.SuccessCount = len(result.Results)
	result.FailureCount = len(result.Failures)
	result.PagesListed = len(items)
	result.RecordsListed = len(items)
	if err := s.attachIssueCommentQueueSummary(ctx, result, req.RepoID, "drain"); err != nil {
		return result, err
	}
	result.IssueComments.Strategy = "per_issue_fallback"
	result.IssueComments.FallbackReason = fallbackReason
	if limit > 0 && len(items) == limit && result.TraversalStatus == "complete" && result.IssueComments != nil && result.IssueComments.Pending+result.IssueComments.Deferred > 0 {
		result.TraversalStatus = "bounded"
		result.StopReason = "max_records"
	}
	if result.FailureCount > 0 {
		return result, &PartialSyncError{Errors: result.Failures, SuccessCount: result.SuccessCount, FailureCount: result.FailureCount}
	}
	return result, nil
}

func (s *Service) bulkSyncRepositoryIssueComments(ctx context.Context, req BulkSyncRequest, route RepositoryRoute, lister gitcode.RepositoryIssueCommentLister, pending, allItems []cache.IssueCommentSync) (*SyncResourcesResult, error) {
	result := &SyncResourcesResult{Results: []SyncResult{}, Failures: []ResourceError{}, Ordering: "repository_comment_order", TraversalStatus: "complete"}
	pendingBySource := make(map[string]cache.IssueCommentSync, len(pending))
	byProviderID := make(map[string]cache.IssueCommentSync, len(allItems))
	byNumber := make(map[int]cache.IssueCommentSync, len(allItems))
	for _, item := range pending {
		pendingBySource[item.SourceID] = item
	}
	for _, item := range allItems {
		if id := strings.TrimSpace(item.ProviderID); id != "" {
			byProviderID[id] = item
		}
		if item.IssueNumber > 0 {
			byNumber[item.IssueNumber] = item
		}
	}
	seen := make(map[string]map[string]struct{}, len(pending))
	seenSources := make(map[string]map[string]struct{}, len(pending))
	expectedTotal := 0
	// An interrupted aggregate traversal restarts at page one. Page-level upserts
	// make that retry idempotent, while starting later could falsely reconcile a
	// parent after missing comments from earlier pages.
	pageNumber := 1
	perPage := req.PerPage
	if perPage < 1 {
		perPage = 100
	}
	if req.Bounds != nil && req.Bounds.MaxRecords > 0 && req.Bounds.MaxRecords < perPage {
		perPage = req.Bounds.MaxRecords
	}
	for {
		if err := ctx.Err(); err != nil {
			result.TraversalStatus = "cancelled"
			result.StopReason = "cancelled"
			if errors.Is(err, context.DeadlineExceeded) {
				result.TraversalStatus = "timeout"
				result.StopReason = "timeout"
			}
			if summaryErr := s.attachRepositoryIssueCommentSummary(context.WithoutCancel(ctx), result, req.RepoID, len(pending)); summaryErr != nil {
				return result, summaryErr
			}
			diagnostic := SyncDiagnosticCancelled
			if errors.Is(err, context.DeadlineExceeded) {
				diagnostic = SyncDiagnosticTimeout
			}
			return result, &PartialSyncError{Errors: result.Failures, SuccessCount: result.SuccessCount, FailureCount: result.FailureCount, Diagnostic: diagnostic}
		}
		if req.Bounds != nil && req.Bounds.MaxPages > 0 && result.PagesListed >= req.Bounds.MaxPages {
			result.TraversalStatus = "bounded"
			result.StopReason = "max_pages"
			break
		}
		if req.Bounds != nil && req.Bounds.MaxRecords > 0 {
			if result.RecordsListed >= req.Bounds.MaxRecords {
				result.TraversalStatus = "bounded"
				result.StopReason = "max_records"
				break
			}
			if result.RecordsListed > 0 && result.RecordsListed+perPage > req.Bounds.MaxRecords {
				result.TraversalStatus = "bounded"
				result.StopReason = "max_records"
				break
			}
		}
		page, err := lister.ListRepositoryIssueComments(ctx, gitcode.RepositoryIssueCommentListRequest{Owner: route.Owner, Repo: route.Name, Page: pageNumber, PerPage: perPage})
		if err != nil {
			if repositoryIssueCommentsUnsupported(err) && result.PagesListed == 0 {
				return result, err
			}
			var rateLimited gitcode.ErrRateLimited
			if errors.As(err, &rateLimited) {
				result.TraversalStatus = "deferred"
				result.StopReason = "rate_limited"
				result.FailureCount = len(result.Failures)
				emitProgress(req.ProgressChan, ProgressEvent{Collection: "issue_comments", Phase: "aggregate_deferred", Endpoint: rateLimited.Endpoint, RetryAfter: rateLimited.RetryAfter.String()})
				if summaryErr := s.attachRepositoryIssueCommentSummary(ctx, result, req.RepoID, len(pending)); summaryErr != nil {
					return result, summaryErr
				}
				if result.FailureCount > 0 {
					return result, &PartialSyncError{Errors: result.Failures, SuccessCount: 0, FailureCount: result.FailureCount}
				}
				return result, nil
			}
			result.TraversalStatus = "partial"
			result.StopReason = "provider_error"
			result.Failures = append(result.Failures, newResourceError("issue-comment:*", "issue_comments", err))
			result.FailureCount = len(result.Failures)
			if summaryErr := s.attachRepositoryIssueCommentSummary(ctx, result, req.RepoID, len(pending)); summaryErr != nil {
				return result, summaryErr
			}
			return result, &PartialSyncError{Errors: result.Failures, SuccessCount: result.SuccessCount, FailureCount: result.FailureCount}
		}
		result.PagesListed++
		result.RecordsListed += len(page.Items)
		if page.TotalCount > 0 {
			if expectedTotal > 0 && expectedTotal != page.TotalCount {
				result.Failures = append(result.Failures, newResourceError("issue-comment:*", "issue_comment_reconciliation", issueCommentReconciliationError(fmt.Sprintf("aggregate total_count changed from %d to %d during traversal", expectedTotal, page.TotalCount))))
			} else {
				expectedTotal = page.TotalCount
			}
		}
		pageComments := make(map[string][]cache.RecordComment)
		for _, comment := range page.Items {
			item, reconcileErr := reconcileRepositoryIssueCommentParent(comment, byProviderID, byNumber)
			if reconcileErr != nil {
				result.Failures = append(result.Failures, newResourceError(strings.TrimSpace(comment.ID), "issue_comment_reconciliation", issueCommentReconciliationError(reconcileErr.Error())))
				continue
			}
			if _, needsRefresh := pendingBySource[item.SourceID]; !needsRefresh {
				continue
			}
			cached, err := cachedIssueComment(comment, item, s.now().UTC())
			if err != nil {
				result.Failures = append(result.Failures, newResourceError(strings.TrimSpace(comment.ID), "issue_comment_reconciliation", err))
				continue
			}
			commentSourceID, err := s.upsertIssueCommentProjection(ctx, item, cached)
			if err != nil {
				result.Failures = append(result.Failures, newResourceError(strings.TrimSpace(comment.ID), "issue_comments", err))
				continue
			}
			pageComments[item.SourceID] = append(pageComments[item.SourceID], cached)
			if seen[item.SourceID] == nil {
				seen[item.SourceID] = map[string]struct{}{}
			}
			seen[item.SourceID][cached.CommentID] = struct{}{}
			if seenSources[item.SourceID] == nil {
				seenSources[item.SourceID] = map[string]struct{}{}
			}
			seenSources[item.SourceID][commentSourceID] = struct{}{}
		}
		for sourceID, comments := range pageComments {
			if err := s.store.UpsertRecordComments(ctx, req.RepoID, sourceID, comments); err != nil {
				result.Failures = append(result.Failures, newResourceError(sourceID, "issue_comments", err))
			}
		}
		emitProgress(req.ProgressChan, ProgressEvent{Collection: "issue_comments", Phase: "aggregate_page", Page: pageNumber, RecordsListed: len(page.Items), RecordsFetched: len(page.Items), RecordsFailed: len(result.Failures)})
		if page.NextPage <= 0 {
			result.StopReason = "end_of_collection"
			break
		}
		if page.NextPage <= pageNumber {
			result.Failures = append(result.Failures, newResourceError("issue-comment:*", "issue_comment_reconciliation", issueCommentReconciliationError(fmt.Sprintf("aggregate pagination did not advance from page %d", pageNumber))))
			result.TraversalStatus = "partial"
			result.StopReason = "pagination_invalid"
			break
		}
		pageNumber = page.NextPage
	}
	if result.TraversalStatus == "bounded" {
		result.FailureCount = len(result.Failures)
		if result.FailureCount > 0 {
			result.TraversalStatus = "partial"
			result.StopReason = "reconciliation_failed"
		}
		if summaryErr := s.attachRepositoryIssueCommentSummary(ctx, result, req.RepoID, len(pending)); summaryErr != nil {
			return result, summaryErr
		}
		if result.FailureCount > 0 {
			return result, &PartialSyncError{Errors: result.Failures, SuccessCount: 0, FailureCount: result.FailureCount}
		}
		return result, nil
	}
	if expectedTotal > 0 && result.RecordsListed != expectedTotal {
		result.Failures = append(result.Failures, newResourceError("issue-comment:*", "issue_comment_reconciliation", issueCommentReconciliationError(fmt.Sprintf("aggregate listed %d comments, expected total_count %d", result.RecordsListed, expectedTotal))))
	}
	if len(result.Failures) > 0 {
		result.TraversalStatus = "partial"
		result.StopReason = "reconciliation_failed"
		result.FailureCount = len(result.Failures)
		if summaryErr := s.attachRepositoryIssueCommentSummary(ctx, result, req.RepoID, len(pending)); summaryErr != nil {
			return result, summaryErr
		}
		return result, &PartialSyncError{Errors: result.Failures, SuccessCount: 0, FailureCount: result.FailureCount}
	}
	for _, item := range pending {
		commentIDs := make([]string, 0, len(seen[item.SourceID]))
		for id := range seen[item.SourceID] {
			commentIDs = append(commentIDs, id)
		}
		sort.Strings(commentIDs)
		if item.ExpectedCount >= 0 && len(commentIDs) != item.ExpectedCount {
			err := issueCommentReconciliationError(fmt.Sprintf("aggregate comments for issue %d = %d, expected %d", item.IssueNumber, len(commentIDs), item.ExpectedCount))
			result.Failures = append(result.Failures, newResourceError(item.RemoteID, "issue_comment_reconciliation", err))
			item.Attempts++
			item.LastAttemptAt = s.now().UTC()
			item.UpdatedAt = item.LastAttemptAt
			item.LastErrorClass = "live_graph_invalid"
			_ = s.store.UpsertIssueCommentSync(ctx, item)
			continue
		}
		if err := s.store.ReconcileRecordComments(ctx, req.RepoID, item.SourceID, commentIDs); err != nil {
			result.Failures = append(result.Failures, newResourceError(item.RemoteID, "issue_comments", err))
			continue
		}
		commentSourceIDs := make([]string, 0, len(seenSources[item.SourceID]))
		for id := range seenSources[item.SourceID] {
			commentSourceIDs = append(commentSourceIDs, id)
		}
		sort.Strings(commentSourceIDs)
		if err := s.store.ReconcileChildSources(ctx, req.RepoID, item.SourceID, "issue_comment", commentSourceIDs); err != nil {
			result.Failures = append(result.Failures, newResourceError(item.RemoteID, "issue_comments", err))
			continue
		}
		now := s.now().UTC()
		item.Status = "complete"
		item.ExpectedCount = len(commentIDs)
		item.Attempts++
		item.LastErrorClass = ""
		item.RetryAfter = ""
		item.LastAttemptAt = now
		item.UpdatedAt = now
		if err := s.store.UpsertIssueCommentSync(ctx, item); err != nil {
			result.Failures = append(result.Failures, newResourceError(item.RemoteID, "issue_comments", err))
			continue
		}
		source, err := s.store.GetSourceScoped(ctx, req.RepoID, item.SourceID)
		if err != nil {
			result.Failures = append(result.Failures, newResourceError(item.RemoteID, "issue_comments", err))
			continue
		}
		result.Results = append(result.Results, SyncResult{Status: "succeeded", Freshness: string(FreshnessFresh), Counts: SyncCounts{Fetched: 1, Listed: len(commentIDs)}, Record: sourceSummary(source), GeneratedAt: now, StartedAt: now, CompletedAt: now})
	}
	result.SuccessCount = len(result.Results)
	result.FailureCount = len(result.Failures)
	if result.FailureCount > 0 {
		result.TraversalStatus = "partial"
		result.StopReason = "reconciliation_failed"
	}
	if summaryErr := s.attachRepositoryIssueCommentSummary(ctx, result, req.RepoID, len(pending)); summaryErr != nil {
		return result, summaryErr
	}
	if result.FailureCount > 0 {
		return result, &PartialSyncError{Errors: result.Failures, SuccessCount: result.SuccessCount, FailureCount: result.FailureCount}
	}
	return result, nil
}

func (s *Service) attachRepositoryIssueCommentSummary(ctx context.Context, result *SyncResourcesResult, repoID string, attempted int) error {
	if err := s.attachIssueCommentQueueSummary(ctx, result, repoID, "aggregate_backfill"); err != nil {
		return err
	}
	result.IssueComments.Strategy = "repository_aggregate"
	result.IssueComments.Attempted = attempted
	result.IssueComments.Drained = result.SuccessCount
	result.IssueComments.AggregateRequests = result.PagesListed
	result.IssueComments.CommentsListed = result.RecordsListed
	for _, failure := range result.Failures {
		if failure.RemoteType == "issue_comment_reconciliation" {
			result.IssueComments.Unreconciled++
		}
	}
	if result.TraversalStatus == "complete" && result.FailureCount == 0 {
		avoided := attempted - result.PagesListed
		if avoided > 0 {
			result.IssueComments.ParentRequestsAvoided = avoided
		}
	}
	return nil
}

func reconcileRepositoryIssueCommentParent(comment gitcode.Comment, byProviderID map[string]cache.IssueCommentSync, byNumber map[int]cache.IssueCommentSync) (cache.IssueCommentSync, error) {
	provider, providerOK := byProviderID[strings.TrimSpace(comment.IssueID)]
	number, numberOK := byNumber[comment.IssueNumber]
	if providerOK && numberOK && provider.SourceID != number.SourceID {
		return cache.IssueCommentSync{}, fmt.Errorf("comment %s parent ids resolve to different cached issues", comment.ID)
	}
	if providerOK {
		return provider, nil
	}
	if numberOK {
		return number, nil
	}
	return cache.IssueCommentSync{}, fmt.Errorf("comment %s parent issue is not present in the completed parent cache", comment.ID)
}

func cachedIssueComment(comment gitcode.Comment, item cache.IssueCommentSync, now time.Time) (cache.RecordComment, error) {
	commentID := strings.TrimSpace(comment.ID)
	if commentID == "" {
		return cache.RecordComment{}, ErrSyncFailure{Mode: "live_graph_invalid", Cause: ErrInvalidQuery{Field: "live_graph", Message: "comment missing provider id"}}
	}
	updated := comment.UpdatedAt.UTC()
	if updated.IsZero() {
		updated = now
	}
	created := comment.CreatedAt.UTC()
	if created.IsZero() {
		created = updated
	}
	return cache.RecordComment{RepoID: item.RepoID, RecordID: item.SourceID, CommentID: commentID, Author: comment.Author, Body: comment.Body, ContentHash: contentHash(commentID, comment.Author, comment.Body), RemoteRevision: contentHash(updated), CreatedAt: created, UpdatedAt: updated}, nil
}

func (s *Service) upsertIssueCommentProjection(ctx context.Context, item cache.IssueCommentSync, comment cache.RecordComment) (string, error) {
	graph, stableID, err := s.stageIssueCommentProjection(ctx, item, comment)
	if err != nil {
		return "", err
	}
	if err := s.store.UpsertSyncGraph(ctx, s.syncGraphFromSourceGraph(item.RepoID, graph)); err != nil {
		return "", err
	}
	return stableID, nil
}

// stageIssueCommentProjection validates and normalizes an issue comment without
// publishing it. Durable sync uses this to assemble the complete parent batch
// before entering the cache's single atomic commit.
func (s *Service) stageIssueCommentProjection(ctx context.Context, item cache.IssueCommentSync, comment cache.RecordComment) (cache.SourceGraph, string, error) {
	commentID := strings.TrimSpace(comment.CommentID)
	if commentID == "" {
		return cache.SourceGraph{}, "", s.liveGraphError("comment missing provider id")
	}
	remoteID := issueCommentRemoteID(item.IssueNumber, commentID)
	stableID := s.resolveOrFallback(ctx, item.RepoID, "issue_comment", remoteID, issueCommentStableID(item.IssueNumber, commentID))
	if err := s.guardRemoteAlias(ctx, item.RepoID, "issue_comment", remoteID, stableID); err != nil {
		return cache.SourceGraph{}, "", err
	}
	now := s.now().UTC()
	updated := comment.UpdatedAt.UTC()
	if updated.IsZero() {
		updated = now
	}
	created := comment.CreatedAt.UTC()
	if created.IsZero() {
		created = updated
	}
	hash := comment.ContentHash
	if hash == "" {
		hash = contentHash(commentID, comment.Author, comment.Body)
	}
	revision := comment.RemoteRevision
	if revision == "" {
		revision = contentHash(updated)
	}
	source := cache.Source{
		RepoID:      item.RepoID,
		ID:          stableID,
		Kind:        "issue_comment",
		Path:        fmt.Sprintf("issues/%d/comments/%s.md", item.IssueNumber, safeIDPart(commentID)),
		Title:       fmt.Sprintf("Issue #%d comment %s", item.IssueNumber, commentID),
		Body:        comment.Body,
		Status:      "current",
		ContentHash: hash,
		CreatedAt:   created,
		UpdatedAt:   updated,
	}
	graph := cache.SourceGraph{
		Source:        source,
		Identities:    []cache.Identity{{RepoID: item.RepoID, SourceID: stableID, AliasType: "issue_comment", Alias: remoteID, Remote: cache.RemoteAlias{Type: "issue_comment", ID: remoteID}}},
		Links:         []cache.Link{{RepoID: item.RepoID, SourceID: stableID, TargetID: item.SourceID, Kind: "parent", Text: "issue"}},
		Chunks:        chunksForSource(source),
		ReplaceChunks: true,
		SyncStatus:    &cache.SyncStatus{RepoID: item.RepoID, SourceID: stableID, RemoteType: "issue_comment", RemoteID: remoteID, RemoteRevision: revision, Status: "fresh", LastFetchedAt: now},
	}
	return graph, stableID, nil
}

func (s *Service) projectIssueComments(ctx context.Context, item cache.IssueCommentSync, comments []cache.RecordComment) error {
	sourceIDs := make([]string, 0, len(comments))
	for _, comment := range comments {
		sourceID, err := s.upsertIssueCommentProjection(ctx, item, comment)
		if err != nil {
			return err
		}
		sourceIDs = append(sourceIDs, sourceID)
	}
	sort.Strings(sourceIDs)
	return s.store.ReconcileChildSources(ctx, item.RepoID, item.SourceID, "issue_comment", sourceIDs)
}

func repositoryIssueCommentsUnsupported(err error) bool {
	var capability gitcode.ErrUnsupportedCapability
	return errors.As(err, &capability) && capability.CapabilityKey == "repository_issue_comments"
}

func issueCommentReconciliationError(message string) error {
	return ErrSyncFailure{
		Mode:           "live_graph_invalid",
		RecoveryAction: "refresh issue parents with `sync --issues`, then retry `sync --issue-comments`",
		Cause:          ErrInvalidQuery{Field: "live_graph", Message: message},
	}
}

func (s *Service) seedLegacyIssueCommentQueue(ctx context.Context, repoID string) error {
	records, err := s.store.ListRecords(ctx, cache.RecordFilter{RepoID: repoID, Type: "issue"})
	if err != nil {
		return err
	}
	for _, record := range records {
		if _, ok, err := s.store.GetIssueCommentSync(ctx, repoID, record.ID); err != nil {
			return err
		} else if ok {
			continue
		}
		number, err := strconv.Atoi(record.RemoteID)
		if err != nil || number <= 0 {
			continue
		}
		providerID := ""
		identities, err := s.store.GetIdentityMapScoped(ctx, repoID, record.ID)
		if err != nil {
			return err
		}
		for _, identity := range identities {
			if identity.AliasType == "gitcode_issue_id" {
				providerID = identity.Alias
				break
			}
		}
		if err := s.store.UpsertIssueCommentSync(ctx, cache.IssueCommentSync{RepoID: repoID, SourceID: record.ID, IssueNumber: number, RemoteID: record.RemoteID, ProviderID: providerID, RemoteRevision: record.RemoteRevision, ExpectedCount: -1, Status: "pending", UpdatedAt: s.now().UTC()}); err != nil {
			return err
		}
	}
	return nil
}

func (s *Service) syncIssueCommentItem(ctx context.Context, route RepositoryRoute, item cache.IssueCommentSync) (SyncResult, error) {
	comments, err := s.client.ListIssueComments(ctx, gitcode.IssueRequest{Owner: route.Owner, Repo: route.Name, Number: item.IssueNumber, KnownRemoteAlias: true, RemoteAlias: item.RemoteID})
	if err != nil {
		return SyncResult{}, err
	}
	now := s.now().UTC()
	cached := make([]cache.RecordComment, 0, len(comments.Items))
	for _, comment := range comments.Items {
		if s.syncProviderMode() == gitcode.ProviderModeLive && !s.liveCommentParentReconciles(comment, item.RemoteID, item.ProviderID) {
			return SyncResult{}, s.liveGraphError("comment parent issue id is unreconciled")
		}
		recordComment, err := cachedIssueComment(comment, item, now)
		if err != nil {
			return SyncResult{}, err
		}
		cached = append(cached, recordComment)
	}
	if err := s.store.ReplaceRecordComments(ctx, item.RepoID, item.SourceID, cached); err != nil {
		return SyncResult{}, err
	}
	if err := s.projectIssueComments(ctx, item, cached); err != nil {
		return SyncResult{}, err
	}
	item.Status = "complete"
	item.ExpectedCount = len(cached)
	item.Attempts++
	item.LastErrorClass = ""
	item.RetryAfter = ""
	item.LastAttemptAt = now
	item.UpdatedAt = now
	if err := s.store.UpsertIssueCommentSync(ctx, item); err != nil {
		return SyncResult{}, err
	}
	source, err := s.store.GetSourceScoped(ctx, item.RepoID, item.SourceID)
	if err != nil {
		return SyncResult{}, err
	}
	return SyncResult{Status: "succeeded", Freshness: string(FreshnessFresh), Counts: SyncCounts{Fetched: 1, FetchedDetail: 1}, Record: sourceSummary(source), GeneratedAt: now, StartedAt: now, CompletedAt: now}, nil
}

func (s *Service) attachIssueCommentQueueSummary(ctx context.Context, result *SyncResourcesResult, repoID, phase string) error {
	summary, err := s.store.IssueCommentSyncSummary(ctx, repoID)
	if err != nil {
		return err
	}
	queue := &IssueCommentQueueSummary{Phase: phase, Pending: summary.Pending, Deferred: summary.Deferred, Complete: summary.Complete, Total: summary.Total}
	if result != nil {
		for _, item := range result.Results {
			queue.Attempted += item.Counts.FetchedDetail
			if item.Status == "succeeded" && item.Counts.FetchedDetail > 0 {
				queue.Drained++
			}
		}
		result.IssueComments = queue
	}
	return nil
}

func syncResultsDeferredCount(results []SyncResult) int {
	total := 0
	for _, result := range results {
		total += result.Counts.Deferred
	}
	return total
}

func (s *Service) BulkSyncPullRequests(ctx context.Context, req BulkSyncRequest) (_ *SyncResourcesResult, retErr error) {
	ctx, retainAttempt := beginSyncHTTPAttempt(ctx)
	defer func() { retErr = retainAttempt(retErr) }()
	if err := ctx.Err(); err != nil {
		if req.Bounds != nil {
			diag := SyncDiagnosticCancelled
			if errors.Is(err, context.DeadlineExceeded) {
				diag = SyncDiagnosticTimeout
			}
			return nil, &PartialSyncError{Errors: nil, SuccessCount: 0, FailureCount: 0, Diagnostic: diag}
		}
		return nil, err
	}
	ctx = withBulkRateLimitProgress(ctx, bulkProgressChan(req))
	repoID, err := s.requireRepo(ctx, req.RepoID, "bulk-sync-pulls")
	if err != nil {
		return nil, err
	}
	req.RepoID = repoID
	ctx, releaseWriter, err := s.acquireBulkWriter(ctx, repoID, "bulk-sync-pulls")
	if err != nil {
		return nil, err
	}
	defer releaseWriter()
	s.ensureBulkIdempotencyKey(&req, "pulls")
	if err := s.validateRepoScope(ctx, repoID, "pull_request"); err != nil {
		return nil, err
	}
	route, err := s.BuildAdapterRoute(ctx, repoID, RepositoryScopeIssues)
	if err != nil {
		return nil, err
	}

	if req.Bounds == nil {
		page, err := s.client.ListPRs(ctx, gitcode.PRListRequest{Owner: route.Owner, Repo: route.Name, State: "all", OrderBy: "updated_at", Direction: "desc", Page: req.Page, PerPage: req.PerPage})
		if err != nil {
			return bulkSyncFailureResult(s.normalizeSyncFailure(err, SyncRequest{RepoID: req.RepoID, RemoteAlias: "pull_request:*"}, "pull_request", "*"), "pull_request:*", "pull_request")
		}
		result := &SyncResourcesResult{Results: make([]SyncResult, 0, len(page.Items)), Failures: make([]ResourceError, 0)}
		beforeCount := len(result.Results)
		s.stagePullRequestPage(ctx, req, page.Items, result)
		emitProgress(req.ProgressChan, ProgressEvent{Collection: "pulls", Page: firstNonZeroInt(req.Page, 1), RecordsFetched: len(result.Results) - beforeCount})
		result.SuccessCount = len(result.Results)
		result.FailureCount = len(result.Failures)
		if result.FailureCount > 0 {
			return result, &PartialSyncError{Errors: result.Failures, SuccessCount: result.SuccessCount, FailureCount: result.FailureCount}
		}
		return result, nil
	}

	return s.bulkSyncPullRequestsBounded(ctx, req, route)
}

func (s *Service) bulkSyncPullRequestsBounded(ctx context.Context, req BulkSyncRequest, route RepositoryRoute) (*SyncResourcesResult, error) {
	bounds := req.Bounds
	result := &SyncResourcesResult{Results: make([]SyncResult, 0), Failures: make([]ResourceError, 0), Ordering: syncOrderingUpdatedAtDesc}
	watermark, watermarkOK, err := s.completeSyncWatermark(ctx, req.RepoID, "pull_request")
	if err != nil {
		return bulkSyncFailureResult(err, "pull_request:*", "pull_request")
	}
	setWatermarkSummary(result, watermark, watermarkOK)
	var observed syncHighWatermark
	currentPage := req.Page
	if currentPage < 1 {
		currentPage = 1
	}
	perPage := req.PerPage
	if perPage < 1 {
		perPage = 10
	}
	totalRequested := bounds.MaxRecords
	if totalRequested <= 0 && bounds.MaxPages > 0 {
		totalRequested = bounds.MaxPages * perPage
	}

	for pageNum := 0; ; pageNum++ {
		if ctx.Err() != nil {
			diag := SyncDiagnosticCancelled
			if errors.Is(ctx.Err(), context.DeadlineExceeded) {
				diag = SyncDiagnosticTimeout
				result.TraversalStatus = "timeout"
			} else {
				result.TraversalStatus = "cancelled"
			}
			result.SuccessCount = len(result.Results)
			result.FailureCount = len(result.Failures)
			s.recordSyncFrontierBestEffort(ctx, req.RepoID, "pull_request", result, observed)
			return result, &PartialSyncError{Errors: result.Failures, SuccessCount: result.SuccessCount, FailureCount: result.FailureCount, Diagnostic: diag, TotalRequested: totalRequested}
		}
		if bounds.MaxListPages > 0 && result.PagesListed >= bounds.MaxListPages {
			result.StopReason, result.TraversalStatus = "max_list_pages", "bounded"
			break
		}
		if bounds.MaxPages > 0 && pageNum >= bounds.MaxPages {
			result.StopReason = "max_pages"
			result.TraversalStatus = "bounded"
			break
		}
		if bounds.MaxRecords > 0 && len(result.Results) >= bounds.MaxRecords {
			result.StopReason = "max_records"
			result.TraversalStatus = "bounded"
			break
		}
		page, err := s.client.ListPRs(ctx, gitcode.PRListRequest{Owner: route.Owner, Repo: route.Name, State: "all", OrderBy: "updated_at", Direction: "desc", Page: currentPage, PerPage: perPage})
		if err != nil {
			normalized := s.normalizeSyncFailure(err, SyncRequest{RepoID: req.RepoID, RemoteAlias: "pull_request:*"}, "pull_request", "*")
			result.Failures = append(result.Failures, newResourceErrorWithMessage("pull_request:*", "pull_request", normalized, err.Error()))
			result.SuccessCount = len(result.Results)
			result.FailureCount = len(result.Failures)
			result.TraversalStatus = "partial"
			s.recordSyncFrontierBestEffort(ctx, req.RepoID, "pull_request", result, observed)
			return result, &PartialSyncError{Errors: result.Failures, SuccessCount: result.SuccessCount, FailureCount: result.FailureCount, TotalRequested: totalRequested}
		}
		result.PagesListed++
		result.RecordsListed += len(page.Items)
		observePullRequestHighWatermark(&observed, page.Items)
		items, stopByWatermark, skippedByWatermark := filterPullRequestsByCompleteWatermark(page.Items, watermark, watermarkOK)
		result.SkippedByWatermark += skippedByWatermark
		if bounds.MaxRecords > 0 {
			remaining := bounds.MaxRecords - len(result.Results)
			if remaining <= 0 {
				result.StopReason = "max_records"
				result.TraversalStatus = "bounded"
				break
			}
			if len(items) > remaining {
				items = items[:remaining]
			}
		}
		beforeCount := len(result.Results)
		s.stagePullRequestPage(ctx, req, items, result)
		recordsFetched := len(result.Results) - beforeCount
		emitProgress(bounds.ProgressChan, ProgressEvent{Collection: "pulls", Page: currentPage, RecordsFetched: recordsFetched})
		if stopByWatermark {
			result.StopReason = "watermark"
			result.TraversalStatus = "complete"
			result.WatermarkStatus = "used"
			result.WatermarkReason = "previous_complete_frontier"
			break
		}
		if len(page.Items) < perPage {
			result.StopReason = "end_of_collection"
			result.TraversalStatus = "complete"
			break
		}
		currentPage++
	}
	result.SuccessCount = len(result.Results)
	result.FailureCount = len(result.Failures)
	if result.FailureCount > 0 {
		result.TraversalStatus = "partial"
		s.recordSyncFrontierBestEffort(ctx, req.RepoID, "pull_request", result, observed)
		return result, &PartialSyncError{Errors: result.Failures, SuccessCount: result.SuccessCount, FailureCount: result.FailureCount}
	}
	if err := s.recordSyncFrontier(ctx, req.RepoID, "pull_request", result, observed); err != nil {
		return bulkSyncFailureResult(err, "pull_request:*", "pull_request")
	}
	return result, nil
}

func (s *Service) stagePullRequestPage(ctx context.Context, req BulkSyncRequest, items []gitcode.PullRequest, result *SyncResourcesResult) {
	for _, pr := range items {
		remoteID := strconv.Itoa(pr.Number)
		syncReq := SyncRequest{RepoID: req.RepoID, AliasType: "pull_request", AliasID: remoteID, IdempotencyKey: scopedBulkSyncKey(req.IdempotencyKey, "pull_request", remoteID), MaxAttempts: req.MaxAttempts, MaxSize: req.MaxSize}
		graph, counts, err := s.stagePullRequest(ctx, syncReq, "pull_request", remoteID, pr)
		if err != nil {
			result.Failures = append(result.Failures, newResourceError(remoteID, "pull_request", err))
			continue
		}
		counts.Listed = 1
		completedAt := s.now().UTC()
		eventID := syncEventID(syncReq.IdempotencyKey)
		zeroDelta := counts.Fetched > 0 && counts.Skipped == counts.Fetched && counts.Updated == 0 && counts.Inserted == 0 && counts.Conflicts == 0
		graph.SyncEvents = append(graph.SyncEvents, cache.SyncEvent{ID: eventID, SourceID: graph.Source.ID, RemoteType: "pull_request", RemoteID: remoteID, RemoteRevision: graph.SyncStatus.RemoteRevision, Status: "succeeded", IdempotencyKey: syncReq.IdempotencyKey, Message: syncEventMessage(counts), CreatedAt: completedAt, StartedAt: completedAt, CompletedAt: completedAt, ZeroDelta: zeroDelta})
		if err := s.store.UpsertSyncGraph(ctx, s.syncGraphFromSourceGraph(req.RepoID, graph)); err != nil {
			result.Failures = append(result.Failures, newResourceError(remoteID, "pull_request", err))
			continue
		}
		stored, err := s.store.GetSourceScoped(ctx, req.RepoID, graph.Source.ID)
		if err != nil {
			result.Failures = append(result.Failures, newResourceError(remoteID, "pull_request", err))
			continue
		}
		result.Results = append(result.Results, SyncResult{IdempotencyKey: syncReq.IdempotencyKey, Status: "succeeded", Counts: counts, SyncEventID: eventID, Freshness: string(FreshnessFresh), Record: sourceSummary(stored), GeneratedAt: completedAt, StartedAt: completedAt, CompletedAt: completedAt, ZeroDelta: zeroDelta})
	}
}

const (
	syncOrderingUpdatedAtDesc = "updated_at_desc"
	syncFilterStateAll        = "state=all"
)

type syncHighWatermark struct {
	UpdatedAt time.Time
	RemoteID  string
	Number    int
}

func (s *Service) completeSyncWatermark(ctx context.Context, repoID, remoteType string) (cache.SyncFrontier, bool, error) {
	frontier, ok, err := s.store.GetSyncFrontier(ctx, repoID, remoteType, syncOrderingUpdatedAtDesc, syncFilterStateAll)
	if err != nil || !ok {
		return cache.SyncFrontier{}, false, err
	}
	return frontier, frontier.Status == "complete" && !frontier.HighUpdatedAt.IsZero(), nil
}

func setWatermarkSummary(result *SyncResourcesResult, frontier cache.SyncFrontier, ok bool) {
	if ok {
		result.WatermarkStatus = "eligible"
		result.WatermarkReason = "previous_complete_frontier"
		return
	}
	result.WatermarkStatus = "disabled"
	if frontier.Status != "" {
		result.WatermarkReason = "previous_frontier_" + frontier.Status
		return
	}
	result.WatermarkReason = "no_complete_frontier_metadata"
}

func observeIssueHighWatermark(mark *syncHighWatermark, items []gitcode.IssueSummary) {
	for _, item := range items {
		observeHighWatermark(mark, item.UpdatedAt, strconv.Itoa(item.Number), item.Number)
	}
}

func observePullRequestHighWatermark(mark *syncHighWatermark, items []gitcode.PullRequest) {
	for _, item := range items {
		observeHighWatermark(mark, item.UpdatedAt, strconv.Itoa(item.Number), item.Number)
	}
}

func observeHighWatermark(mark *syncHighWatermark, updatedAt time.Time, remoteID string, number int) {
	updatedAt = updatedAt.UTC()
	if updatedAt.IsZero() {
		return
	}
	if mark.UpdatedAt.IsZero() || updatedAt.After(mark.UpdatedAt) || (updatedAt.Equal(mark.UpdatedAt) && number > mark.Number) {
		mark.UpdatedAt = updatedAt
		mark.RemoteID = remoteID
		mark.Number = number
	}
}

func filterIssueSummariesByCompleteWatermark(items []gitcode.IssueSummary, frontier cache.SyncFrontier, ok bool) ([]gitcode.IssueSummary, bool, int) {
	if !ok {
		return items, false, 0
	}
	out := make([]gitcode.IssueSummary, 0, len(items))
	skipped := 0
	stop := false
	for _, item := range items {
		if item.UpdatedAt.IsZero() || !item.UpdatedAt.UTC().Before(frontier.HighUpdatedAt) {
			out = append(out, item)
			continue
		}
		skipped++
		stop = true
	}
	return out, stop, skipped
}

func filterPullRequestsByCompleteWatermark(items []gitcode.PullRequest, frontier cache.SyncFrontier, ok bool) ([]gitcode.PullRequest, bool, int) {
	if !ok {
		return items, false, 0
	}
	out := make([]gitcode.PullRequest, 0, len(items))
	skipped := 0
	stop := false
	for _, item := range items {
		if item.UpdatedAt.IsZero() || !item.UpdatedAt.UTC().Before(frontier.HighUpdatedAt) {
			out = append(out, item)
			continue
		}
		skipped++
		stop = true
	}
	return out, stop, skipped
}

func (s *Service) recordSyncFrontier(ctx context.Context, repoID, remoteType string, result *SyncResourcesResult, high syncHighWatermark) error {
	frontier, err := s.syncFrontierForResult(ctx, repoID, remoteType, result, high)
	if err != nil || frontier == nil {
		return err
	}
	return s.store.UpsertSyncFrontier(ctx, *frontier)
}

func (s *Service) syncFrontierForResult(ctx context.Context, repoID, remoteType string, result *SyncResourcesResult, high syncHighWatermark) (*cache.SyncFrontier, error) {
	status := result.TraversalStatus
	if result.StopReason == "watermark" || result.StopReason == "end_of_collection" {
		status = "complete"
	}
	if status == "" {
		status = "partial"
	}
	previous, ok, err := s.store.GetSyncFrontier(ctx, repoID, remoteType, syncOrderingUpdatedAtDesc, syncFilterStateAll)
	if err != nil {
		return nil, err
	}
	if ok && previous.Status == "complete" && status != "complete" {
		// Keep the last proven full-corpus watermark while a bounded head pass is
		// still working toward it. Maintenance stores the in-progress checkpoint
		// and candidate high watermark separately.
		return nil, nil
	}
	frontier := cache.SyncFrontier{
		RepoID:        repoID,
		RemoteType:    remoteType,
		Ordering:      syncOrderingUpdatedAtDesc,
		FilterKey:     syncFilterStateAll,
		Status:        status,
		HighUpdatedAt: high.UpdatedAt,
		HighRemoteID:  high.RemoteID,
		HighNumber:    high.Number,
		StopReason:    result.StopReason,
		PagesListed:   result.PagesListed,
		RecordsListed: result.RecordsListed,
		UpdatedAt:     s.now().UTC(),
	}
	return &frontier, nil
}

func (s *Service) recordSyncFrontierBestEffort(ctx context.Context, repoID, remoteType string, result *SyncResourcesResult, high syncHighWatermark) {
	if ctx.Err() != nil {
		ctx = context.WithoutCancel(ctx)
	}
	_ = s.recordSyncFrontier(ctx, repoID, remoteType, result, high)
}

func (s *Service) BulkSyncPRComments(ctx context.Context, req BulkSyncRequest) (_ *SyncResourcesResult, retErr error) {
	ctx, retainAttempt := beginSyncHTTPAttempt(ctx)
	defer func() { retErr = retainAttempt(retErr) }()
	remoteAlias := strings.TrimSpace(req.RemoteAlias)
	targetedPRNumber := 0
	if remoteAlias != "" {
		if req.Page != 0 || req.PerPage != 0 || (req.Bounds != nil && (req.Bounds.MaxPages != 0 || req.Bounds.MaxRecords != 0)) {
			return nil, ErrInvalidQuery{Field: "bounds", Message: "targeted PR comment sync does not accept page, per_page, max_pages, or max_records"}
		}
		var err error
		targetedPRNumber, err = targetedPRCommentNumber(remoteAlias)
		if err != nil {
			return nil, err
		}
	}
	if err := ctx.Err(); err != nil {
		if req.Bounds != nil {
			diag := SyncDiagnosticCancelled
			if errors.Is(err, context.DeadlineExceeded) {
				diag = SyncDiagnosticTimeout
			}
			return nil, &PartialSyncError{Errors: nil, SuccessCount: 0, FailureCount: 0, Diagnostic: diag}
		}
		return nil, err
	}
	ctx = withBulkRateLimitProgress(ctx, bulkProgressChan(req))
	repoID, err := s.requireRepo(ctx, req.RepoID, "bulk-sync-pr-comments")
	if err != nil {
		return nil, err
	}
	req.RepoID = repoID
	if targetedPRNumber == 0 {
		var releaseWriter func()
		ctx, releaseWriter, err = s.acquireBulkWriter(ctx, repoID, "bulk-sync-pr-comments")
		if err != nil {
			return nil, err
		}
		defer releaseWriter()
	}
	s.ensureBulkIdempotencyKey(&req, "pr_comments")
	if err := s.validateRepoScope(ctx, repoID, "pull_request"); err != nil {
		return nil, err
	}
	route, err := s.BuildAdapterRoute(ctx, repoID, RepositoryScopeIssues)
	if err != nil {
		return nil, err
	}
	var prSources []cache.Source
	if targetedPRNumber > 0 {
		identity, err := s.store.ResolveAliasScoped(ctx, repoID, cache.RemoteAlias{Type: "pull_request", ID: strconv.Itoa(targetedPRNumber)})
		if err != nil {
			if isCacheNotFound(err) {
				return nil, ErrInvalidQuery{Field: "remote_alias", Message: fmt.Sprintf("pull request %d is not cached; sync --pulls --input pr:%d first", targetedPRNumber, targetedPRNumber)}
			}
			return nil, normalizeError(err, "pull_request", strconv.Itoa(targetedPRNumber))
		}
		source, err := s.store.GetSourceScoped(ctx, repoID, identity.SourceID)
		if err != nil {
			if isCacheNotFound(err) {
				return nil, ErrInvalidQuery{Field: "remote_alias", Message: fmt.Sprintf("pull request %d is not cached; sync --pulls --input pr:%d first", targetedPRNumber, targetedPRNumber)}
			}
			return nil, normalizeError(err, "pull_request", strconv.Itoa(targetedPRNumber))
		}
		if source.Kind != "pull_request" {
			return nil, ErrInvalidQuery{Field: "remote_alias", Message: fmt.Sprintf("pull_request:%d resolves to cached %s %s, not a pull request", targetedPRNumber, source.Kind, source.ID)}
		}
		prSources = []cache.Source{source}
	} else {
		prSources, err = s.store.ListSources(ctx, cache.SourceFilter{RepoID: repoID, Kind: "pull_request"})
		if err != nil {
			if isCacheNotFound(err) {
				return &SyncResourcesResult{Results: []SyncResult{}, Failures: []ResourceError{}}, nil
			}
			return nil, normalizeError(err, "sources", repoID)
		}
	}
	if targetedPRNumber > 0 {
		var releaseWriter func()
		ctx, releaseWriter, err = s.acquireTargetedPRCommentWriter(ctx, repoID, targetedPRCommentWriterWait)
		if err != nil {
			return nil, err
		}
		defer releaseWriter()
	}
	sort.SliceStable(prSources, func(i, j int) bool { return prSources[i].ID < prSources[j].ID })
	start := 0
	if req.Bounds != nil && req.Page > 1 {
		start = req.Page - 1
	}
	if start > len(prSources) {
		start = len(prSources)
	}
	end := len(prSources)
	bounded := false
	if req.Bounds != nil && req.Bounds.MaxPages > 0 && end > start+req.Bounds.MaxPages {
		end = start + req.Bounds.MaxPages
		bounded = true
	}
	prSources = prSources[start:end]

	result := &SyncResourcesResult{Results: make([]SyncResult, 0), Failures: make([]ResourceError, 0), Ordering: "source_id_asc", PagesListed: len(prSources), RecordsListed: len(prSources), TraversalStatus: "complete", StopReason: "end_of_collection"}
	if bounded {
		result.TraversalStatus = "bounded"
		result.StopReason = "max_pages"
	}
	totalRequested := 0
	if req.Bounds != nil {
		totalRequested = req.Bounds.MaxRecords
	}
	for idx, source := range prSources {
		if ctx.Err() != nil {
			diag := SyncDiagnosticCancelled
			if errors.Is(ctx.Err(), context.DeadlineExceeded) {
				diag = SyncDiagnosticTimeout
			}
			result.TraversalStatus, result.StopReason = "cancelled", "cancelled"
			if diag == SyncDiagnosticTimeout {
				result.TraversalStatus, result.StopReason = "timeout", "timeout"
			}
			result.SuccessCount = len(result.Results)
			result.FailureCount = len(result.Failures)
			return result, &PartialSyncError{Errors: result.Failures, SuccessCount: result.SuccessCount, FailureCount: result.FailureCount, Diagnostic: diag, TotalRequested: totalRequested}
		}
		if req.Bounds != nil && req.Bounds.MaxRecords > 0 && len(result.Results) >= req.Bounds.MaxRecords {
			result.TraversalStatus, result.StopReason = "bounded", "max_records"
			break
		}
		prNumber := targetedPRNumber
		if prNumber == 0 {
			var ok bool
			prNumber, ok = pullRequestNumberFromSource(source)
			if !ok {
				err := ErrInvalidQuery{Field: "pull_request", Message: "cached pull request has no numeric remote alias"}
				result.Failures = append(result.Failures, newResourceErrorWithMessage(source.ID, "pr_comment", err, "cached pull request has no numeric remote alias"))
				continue
			}
		}
		if prNumber <= 0 {
			err := ErrInvalidQuery{Field: "pull_request", Message: "cached pull request has no numeric remote alias"}
			result.Failures = append(result.Failures, newResourceErrorWithMessage(source.ID, "pr_comment", err, "cached pull request has no numeric remote alias"))
			continue
		}
		page, err := s.client.ListPRComments(ctx, gitcode.PRRequest{Owner: route.Owner, Repo: route.Name, Number: prNumber})
		if err != nil {
			normalized := s.normalizeSyncFailure(err, SyncRequest{RepoID: req.RepoID, RemoteAlias: fmt.Sprintf("pr_comment:%d:*", prNumber)}, "pr_comment", strconv.Itoa(prNumber))
			result.Failures = append(result.Failures, newResourceErrorWithMessage(fmt.Sprintf("PR-%d", prNumber), "pr_comment", normalized, err.Error()))
			continue
		}
		items := page.Items
		if req.Bounds != nil && req.Bounds.MaxRecords > 0 {
			remaining := req.Bounds.MaxRecords - len(result.Results)
			if remaining <= 0 {
				break
			}
			if len(items) > remaining {
				items = items[:remaining]
				result.TraversalStatus, result.StopReason = "bounded", "max_records"
			}
		}
		beforeCount := len(result.Results)
		s.stagePRCommentPage(ctx, req, prNumber, source.ID, items, result)
		emitProgress(bulkProgressChan(req), ProgressEvent{Collection: "pr_comments", Page: idx + 1, RecordsFetched: len(result.Results) - beforeCount})
	}
	result.SuccessCount = len(result.Results)
	result.FailureCount = len(result.Failures)
	if result.FailureCount > 0 {
		result.TraversalStatus = "partial"
		result.StopReason = "provider_error"
		return result, &PartialSyncError{Errors: result.Failures, SuccessCount: result.SuccessCount, FailureCount: result.FailureCount, TotalRequested: totalRequested}
	}
	return result, nil
}

func targetedPRCommentNumber(alias string) (int, error) {
	remoteType, remoteID, ok := strings.Cut(strings.TrimSpace(alias), ":")
	if !ok {
		return 0, ErrInvalidQuery{Field: "remote_alias", Message: "targeted PR comment sync requires pr:N"}
	}
	switch strings.ToLower(strings.TrimSpace(remoteType)) {
	case "pr", "pull", "pulls", "pull_request":
	default:
		return 0, ErrInvalidQuery{Field: "remote_alias", Message: "targeted PR comment sync requires a pull request alias such as pr:N"}
	}
	number, err := strconv.Atoi(strings.TrimSpace(remoteID))
	if err != nil || number <= 0 {
		return 0, ErrInvalidQuery{Field: "remote_alias", Message: "targeted PR comment sync requires a positive pull request number"}
	}
	return number, nil
}

func (s *Service) ListPRDiscussions(ctx context.Context, req PRDiscussionRequest) (PRDiscussionsResult, error) {
	if strings.TrimSpace(req.RepoID) == "" {
		return PRDiscussionsResult{}, ErrInvalidQuery{Field: "repo_id", Message: "repo_id is required"}
	}
	if req.Number <= 0 {
		return PRDiscussionsResult{}, ErrInvalidQuery{Field: "number", Message: "positive pull request number is required"}
	}
	repoID, err := s.requireRepo(ctx, req.RepoID, "list-pr-discussions")
	if err != nil {
		return PRDiscussionsResult{}, err
	}
	metadata, err := s.store.ListPRReviewComments(ctx, cache.PRReviewCommentFilter{RepoID: repoID, PRNumber: req.Number})
	if err != nil {
		return PRDiscussionsResult{}, normalizeError(err, "pr_review_comments", repoID)
	}
	positions, err := s.store.ListPRReviewPositions(ctx, cache.PRReviewPositionFilter{RepoID: repoID, PRNumber: req.Number})
	if err != nil {
		return PRDiscussionsResult{}, normalizeError(err, "pr_review_positions", repoID)
	}
	positionsByComment := map[string][]PRReviewPosition{}
	for _, position := range positions {
		positionsByComment[position.CommentID] = append(positionsByComment[position.CommentID], prReviewPositionFromCache(position))
	}
	metadataByComment := make(map[string]cache.PRReviewComment, len(metadata))
	for _, meta := range metadata {
		metadataByComment[meta.CommentID] = meta
	}
	result := PRDiscussionsResult{RepoID: repoID, Number: req.Number, UnresolvedOnly: req.UnresolvedOnly, Discussions: []PRDiscussion{}, GeneratedAt: s.now().UTC()}
	groups := map[string]int{}
	for _, meta := range metadata {
		source, err := s.store.GetSourceScoped(ctx, repoID, meta.SourceID)
		if err != nil {
			return PRDiscussionsResult{}, normalizeError(err, meta.SourceID, repoID)
		}
		comment := prReviewCommentFromCache(meta, source)
		comment.Positions = positionsByComment[meta.CommentID]
		groupID := meta.DiscussionID
		if groupID == "" {
			rootID := meta.CommentID
			parentID := meta.ParentID
			providerDiscussionID := ""
			seen := map[string]bool{rootID: true}
			for parentID != "" && !seen[parentID] {
				seen[parentID] = true
				parent, ok := metadataByComment[parentID]
				if !ok {
					break
				}
				rootID = parent.CommentID
				if parent.DiscussionID != "" {
					providerDiscussionID = parent.DiscussionID
					break
				}
				parentID = parent.ParentID
			}
			groupID = providerDiscussionID
			if groupID == "" {
				groupID = "comment:" + rootID
			}
		}
		idx, ok := groups[groupID]
		if !ok {
			kind := discussionKind(comment)
			replyable := kind == "inline" && !strings.HasPrefix(groupID, "comment:")
			replyDiscussionID := ""
			if replyable {
				replyDiscussionID = groupID
			}
			discussion := PRDiscussion{ID: groupID, Replyable: replyable, ReplyDiscussionID: replyDiscussionID, Kind: kind, Resolved: meta.Resolved, Resolvable: meta.Resolvable, Path: meta.Path, Line: meta.Line, StartLine: meta.StartLine, EndLine: meta.EndLine, Position: firstCurrentPosition(comment.Positions), Comments: []PRReviewComment{}}
			if !replyable {
				discussion.ReplyUnavailableReason = "provider discussion id unavailable; refresh PR discussions before replying"
				if kind != "inline" {
					discussion.ReplyUnavailableReason = "general PR comments do not expose an inline discussion reply target"
				}
			}
			result.Discussions = append(result.Discussions, discussion)
			idx = len(result.Discussions) - 1
			groups[groupID] = idx
		}
		discussion := &result.Discussions[idx]
		if discussion.Kind != "inline" && discussionKind(comment) == "inline" {
			discussion.Kind = "inline"
		}
		if discussion.Path == "" {
			discussion.Path = meta.Path
		}
		if discussion.Line == 0 {
			discussion.Line = meta.Line
		}
		if discussion.StartLine == 0 {
			discussion.StartLine = meta.StartLine
		}
		if discussion.EndLine == 0 {
			discussion.EndLine = meta.EndLine
		}
		if discussion.Position == nil {
			discussion.Position = firstCurrentPosition(comment.Positions)
		}
		discussion.Resolved = mergeDiscussionResolved(discussion.Resolved, meta.Resolved)
		if discussion.Resolvable == nil && meta.Resolvable != nil {
			discussion.Resolvable = meta.Resolvable
		}
		discussion.Comments = append(discussion.Comments, comment)
	}
	if req.UnresolvedOnly {
		filtered := result.Discussions[:0]
		for _, discussion := range result.Discussions {
			if discussion.Resolved == nil || !*discussion.Resolved {
				filtered = append(filtered, discussion)
			}
		}
		result.Discussions = filtered
	}
	return result, nil
}

func progressChan(bounds *SyncBounds) chan<- ProgressEvent {
	if bounds == nil {
		return nil
	}
	return bounds.ProgressChan
}

func bulkProgressChan(req BulkSyncRequest) chan<- ProgressEvent {
	if req.Bounds != nil && req.Bounds.ProgressChan != nil {
		return req.Bounds.ProgressChan
	}
	return req.ProgressChan
}

func withBulkRateLimitProgress(ctx context.Context, ch chan<- ProgressEvent) context.Context {
	if ch == nil {
		return ctx
	}
	return gitcode.WithRateLimitObserver(ctx, func(ev gitcode.RateLimitEvent) {
		emitProgress(ch, progressEventFromRateLimit(ev))
	})
}

func progressEventFromRateLimit(ev gitcode.RateLimitEvent) ProgressEvent {
	progress := ProgressEvent{
		Type:           "rate_limit",
		Endpoint:       ev.Endpoint,
		Attempt:        ev.Attempt,
		RateLimitState: string(ev.Type),
		RateLimitBurst: ev.Burst,
	}
	if ev.Wait >= 0 && (ev.Wait > 0 || ev.RawRetryAfter != "") {
		progress.RetryAfter = ev.Wait.String()
	}
	if !ev.ResumeAt.IsZero() {
		progress.ResumeAt = ev.ResumeAt.UTC().Format(time.RFC3339Nano)
	}
	if ev.RPS > 0 {
		progress.RateLimitRPS = strconv.FormatFloat(ev.RPS, 'f', -1, 64)
	}
	if ev.RawRetryAfter != "" {
		progress.Message = "retry_after=" + ev.RawRetryAfter
	}
	return progress
}

func (s *Service) stagePRCommentPage(ctx context.Context, req BulkSyncRequest, prNumber int, parentSourceID string, items []gitcode.PRComment, result *SyncResourcesResult) {
	for _, comment := range items {
		remoteID := prCommentRemoteID(prNumber, comment.ID)
		syncReq := SyncRequest{RepoID: req.RepoID, AliasType: "pr_comment", AliasID: remoteID, IdempotencyKey: scopedBulkSyncKey(req.IdempotencyKey, "pr_comment", remoteID), MaxAttempts: req.MaxAttempts, MaxSize: req.MaxSize, ParentSourceID: parentSourceID}
		graph, counts, err := s.stagePRComment(ctx, syncReq, "pr_comment", remoteID, prNumber, comment)
		if err != nil {
			result.Failures = append(result.Failures, newResourceError(remoteID, "pr_comment", err))
			continue
		}
		counts.Listed = 1
		completedAt := s.now().UTC()
		eventID := syncEventID(syncReq.IdempotencyKey)
		zeroDelta := counts.Fetched > 0 && counts.Skipped == counts.Fetched && counts.Updated == 0 && counts.Inserted == 0 && counts.Conflicts == 0
		graph.SyncEvents = append(graph.SyncEvents, cache.SyncEvent{ID: eventID, SourceID: graph.Source.ID, RemoteType: "pr_comment", RemoteID: remoteID, RemoteRevision: graph.SyncStatus.RemoteRevision, Status: "succeeded", IdempotencyKey: syncReq.IdempotencyKey, Message: syncEventMessage(counts), CreatedAt: completedAt, StartedAt: completedAt, CompletedAt: completedAt, ZeroDelta: zeroDelta})
		if err := s.store.UpsertSyncGraph(ctx, s.syncGraphFromSourceGraph(req.RepoID, graph)); err != nil {
			result.Failures = append(result.Failures, newResourceError(remoteID, "pr_comment", err))
			continue
		}
		stored, err := s.store.GetSourceScoped(ctx, req.RepoID, graph.Source.ID)
		if err != nil {
			result.Failures = append(result.Failures, newResourceError(remoteID, "pr_comment", err))
			continue
		}
		result.Results = append(result.Results, SyncResult{IdempotencyKey: syncReq.IdempotencyKey, Status: "succeeded", Counts: counts, SyncEventID: eventID, Freshness: string(FreshnessFresh), Record: sourceSummary(stored), GeneratedAt: completedAt, StartedAt: completedAt, CompletedAt: completedAt, ZeroDelta: zeroDelta})
	}
}

func (s *Service) BulkSyncWiki(ctx context.Context, req BulkSyncRequest) (_ *SyncResourcesResult, retErr error) {
	ctx, retainAttempt := beginSyncHTTPAttempt(ctx)
	defer func() { retErr = retainAttempt(retErr) }()
	if err := ctx.Err(); err != nil {
		if req.Bounds != nil {
			diag := SyncDiagnosticCancelled
			if errors.Is(err, context.DeadlineExceeded) {
				diag = SyncDiagnosticTimeout
			}
			return nil, &PartialSyncError{Errors: nil, SuccessCount: 0, FailureCount: 0, Diagnostic: diag}
		}
		return nil, err
	}
	ctx = withBulkRateLimitProgress(ctx, bulkProgressChan(req))
	repoID, err := s.requireRepo(ctx, req.RepoID, "bulk-sync-wiki")
	if err != nil {
		return nil, err
	}
	req.RepoID = repoID
	ctx, releaseWriter, err := s.acquireBulkWriter(ctx, repoID, "bulk-sync-wiki")
	if err != nil {
		return nil, err
	}
	defer releaseWriter()
	s.ensureBulkIdempotencyKey(&req, "wiki")
	if err := s.validateRepoScope(ctx, repoID, "wiki"); err != nil {
		return nil, err
	}
	route, err := s.BuildAdapterRoute(ctx, repoID, RepositoryScopeWiki)
	if err != nil {
		return nil, err
	}

	if req.Bounds == nil {
		page, err := s.client.ListWikiPages(ctx, gitcode.WikiListRequest{Owner: route.Owner, Repo: route.Name, Page: req.Page, PerPage: req.PerPage})
		if err != nil {
			normalized := s.normalizeSyncFailure(err, SyncRequest{RepoID: req.RepoID, RemoteAlias: "wiki:*"}, "wiki", "*")
			var sfErr ErrSyncFailure
			if errors.As(normalized, &sfErr) && sfErr.Mode == "empty_wiki" {
				re := newResourceError("wiki:*", "wiki", normalized)
				result := &SyncResourcesResult{Failures: []ResourceError{re}, FailureCount: 1}
				return result, &PartialSyncError{Errors: result.Failures, FailureCount: 1, Diagnostic: SyncDiagnosticEmptyWiki}
			}
			return bulkSyncFailureResult(normalized, "wiki:*", "wiki")
		}
		result := &SyncResourcesResult{Results: make([]SyncResult, 0, len(page.Items)), Failures: make([]ResourceError, 0)}
		beforeCount := len(result.Results)
		s.stageWikiPage(ctx, req, page.Items, result)
		emitProgress(req.ProgressChan, ProgressEvent{Collection: "wiki", Page: firstNonZeroInt(req.Page, 1), RecordsFetched: len(result.Results) - beforeCount})
		result.SuccessCount = len(result.Results)
		result.FailureCount = len(result.Failures)
		if result.FailureCount > 0 {
			return result, &PartialSyncError{Errors: result.Failures, SuccessCount: result.SuccessCount, FailureCount: result.FailureCount}
		}
		return result, nil
	}

	return s.bulkSyncWikiBounded(ctx, req, route)
}

func (s *Service) bulkSyncWikiBounded(ctx context.Context, req BulkSyncRequest, route RepositoryRoute) (*SyncResourcesResult, error) {
	bounds := req.Bounds
	result := &SyncResourcesResult{Results: make([]SyncResult, 0), Failures: make([]ResourceError, 0), Ordering: "path_asc"}

	var wikiBounds *gitcode.WikiBounds
	totalRequested := bounds.MaxRecords
	if totalRequested <= 0 && bounds.MaxPages > 0 {
		perPage := req.PerPage
		if perPage < 1 {
			perPage = 10
		}
		totalRequested = bounds.MaxPages * perPage
	}

	wikiBounds = &gitcode.WikiBounds{
		MaxRecords: totalRequested,
	}
	if bounds.ProgressChan != nil {
		progressCh := make(chan gitcode.WikiProgressEvent, 10)
		wikiBounds.ProgressChan = progressCh
		go func() {
			for ev := range progressCh {
				emitProgress(bounds.ProgressChan, ProgressEvent{Collection: "wiki", Page: 1, RecordsFetched: ev.RecordsFetched})
			}
		}()
	}

	page, err := s.client.ListWikiPages(ctx, gitcode.WikiListRequest{
		Owner:   route.Owner,
		Repo:    route.Name,
		Page:    req.Page,
		PerPage: req.PerPage,
		Bounds:  wikiBounds,
	})
	if bounds.ProgressChan != nil {
		close(wikiBounds.ProgressChan)
	}

	if err != nil {
		if ctxErr := ctx.Err(); ctxErr != nil {
			diag := SyncDiagnosticCancelled
			if errors.Is(ctxErr, context.DeadlineExceeded) {
				diag = SyncDiagnosticTimeout
			}
			result.SuccessCount = len(result.Results)
			result.FailureCount = len(result.Failures)
			if result.SuccessCount == 0 && result.FailureCount == 0 {
				return nil, &PartialSyncError{Errors: nil, SuccessCount: 0, FailureCount: 0, Diagnostic: diag, TotalRequested: totalRequested, retainedCause: err}
			}
			return result, &PartialSyncError{Errors: result.Failures, SuccessCount: result.SuccessCount, FailureCount: result.FailureCount, Diagnostic: diag, TotalRequested: totalRequested, retainedCause: err}
		}
		result.SuccessCount = len(result.Results)
		result.FailureCount = len(result.Failures)
		normalized := s.normalizeSyncFailure(err, SyncRequest{RepoID: req.RepoID, RemoteAlias: "wiki:*"}, "wiki", "*")
		re := newResourceErrorWithMessage("wiki:*", "wiki", normalized, err.Error())
		result.Failures = append(result.Failures, re)
		result.FailureCount++
		var sfErr ErrSyncFailure
		if errors.As(normalized, &sfErr) && sfErr.Mode == "empty_wiki" {
			return result, &PartialSyncError{Errors: result.Failures, SuccessCount: result.SuccessCount, FailureCount: result.FailureCount, Diagnostic: SyncDiagnosticEmptyWiki, TotalRequested: totalRequested}
		}
		return result, &PartialSyncError{Errors: result.Failures, SuccessCount: result.SuccessCount, FailureCount: result.FailureCount, TotalRequested: totalRequested}
	}

	items := page.Items
	originalCount := len(items)
	if bounds.MaxPages > 0 {
		perPage := req.PerPage
		if perPage < 1 {
			perPage = 10
		}
		maxItems := bounds.MaxPages * perPage
		if len(items) > maxItems {
			items = items[:maxItems]
		}
	}
	if bounds.MaxRecords > 0 && len(items) > bounds.MaxRecords {
		items = items[:bounds.MaxRecords]
	}
	perPage := req.PerPage
	if perPage < 1 {
		perPage = 10
	}
	result.RecordsListed = len(items)
	if len(items) > 0 {
		result.PagesListed = (len(items) + perPage - 1) / perPage
	}
	if page.NextPage > 0 || originalCount > len(items) {
		result.TraversalStatus = "bounded"
		if bounds.MaxRecords > 0 {
			result.StopReason = "max_records"
		} else {
			result.StopReason = "max_pages"
		}
	} else {
		result.TraversalStatus = "complete"
		result.StopReason = "end_of_collection"
	}

	s.stageWikiPage(ctx, req, items, result)

	if ctx.Err() != nil {
		diag := SyncDiagnosticCancelled
		if errors.Is(ctx.Err(), context.DeadlineExceeded) {
			diag = SyncDiagnosticTimeout
		}
		result.SuccessCount = len(result.Results)
		result.FailureCount = len(result.Failures)
		return result, &PartialSyncError{Errors: result.Failures, SuccessCount: result.SuccessCount, FailureCount: result.FailureCount, Diagnostic: diag, TotalRequested: totalRequested}
	}

	result.SuccessCount = len(result.Results)
	result.FailureCount = len(result.Failures)
	if result.FailureCount > 0 {
		return result, &PartialSyncError{Errors: result.Failures, SuccessCount: result.SuccessCount, FailureCount: result.FailureCount}
	}
	return result, nil
}

func (s *Service) stageWikiPage(ctx context.Context, req BulkSyncRequest, items []gitcode.WikiPage, result *SyncResourcesResult) {
	for _, wp := range items {
		if err := ctx.Err(); err != nil {
			result.Failures = append(result.Failures, newResourceError(wp.Slug, "wiki", err))
			continue
		}
		remoteID := strings.TrimSpace(wp.Slug)
		if remoteID == "" {
			err := ErrInvalidQuery{Field: "wiki_slug", Message: "wiki list item missing slug"}
			result.Failures = append(result.Failures, newResourceErrorWithMessage(wp.Slug, "wiki", err, "wiki list item missing slug"))
			continue
		}
		syncReq := SyncRequest{
			RepoID:         req.RepoID,
			AliasType:      "wiki",
			AliasID:        remoteID,
			IdempotencyKey: scopedBulkSyncKey(req.IdempotencyKey, "wiki", remoteID),
			MaxAttempts:    req.MaxAttempts,
			MaxSize:        req.MaxSize,
		}
		syncResult, err := s.syncWikiListItem(ctx, syncReq, wp)
		if err != nil {
			result.Failures = append(result.Failures, newResourceError(remoteID, "wiki", err))
			continue
		}
		result.Results = append(result.Results, syncResult)
	}
}

func (s *Service) syncWikiListItem(ctx context.Context, req SyncRequest, item gitcode.WikiPage) (SyncResult, error) {
	remoteID := strings.TrimSpace(item.Slug)
	if remoteID == "" {
		return SyncResult{}, ErrInvalidQuery{Field: "wiki_slug", Message: "wiki list item missing slug"}
	}
	revision := strings.TrimSpace(item.Revision)
	if revision != "" {
		skipped, ok, err := s.skipWikiListItemByRevision(ctx, req, remoteID, item, revision)
		if err != nil {
			return SyncResult{}, err
		}
		if ok {
			return skipped, nil
		}
	}

	graph, counts, err := s.fetchAndStage(ctx, req, "wiki", remoteID)
	if err != nil {
		return SyncResult{}, err
	}
	counts.Listed = 1
	counts.FetchedDetail = 1
	completedAt := s.now().UTC()
	eventID := syncEventID(req.IdempotencyKey)
	zeroDelta := counts.Fetched > 0 && counts.Skipped == counts.Fetched && counts.Updated == 0 && counts.Inserted == 0 && counts.Conflicts == 0
	graph.SyncEvents = append(graph.SyncEvents, cache.SyncEvent{ID: eventID, SourceID: graph.Source.ID, RemoteType: "wiki", RemoteID: remoteID, RemoteRevision: graph.SyncStatus.RemoteRevision, Status: "succeeded", IdempotencyKey: req.IdempotencyKey, Message: syncEventMessage(counts), CreatedAt: completedAt, StartedAt: completedAt, CompletedAt: completedAt, ZeroDelta: zeroDelta})
	if err := s.store.UpsertSyncGraph(ctx, s.syncGraphFromSourceGraph(req.RepoID, graph)); err != nil {
		return SyncResult{}, err
	}
	stored, err := s.store.GetSourceScoped(ctx, req.RepoID, graph.Source.ID)
	if err != nil {
		return SyncResult{}, err
	}
	return SyncResult{IdempotencyKey: req.IdempotencyKey, Status: "succeeded", Counts: counts, SyncEventID: eventID, Freshness: string(FreshnessFresh), Record: sourceSummary(stored), GeneratedAt: completedAt, StartedAt: completedAt, CompletedAt: completedAt, ZeroDelta: zeroDelta}, nil
}

func (s *Service) skipWikiListItemByRevision(ctx context.Context, req SyncRequest, remoteID string, item gitcode.WikiPage, revision string) (SyncResult, bool, error) {
	stableID := req.StableID
	if stableID == "" {
		stableID = s.resolveOrFallback(ctx, req.RepoID, "wiki", remoteID, liveFallbackSourceID(s.syncProviderMode(), "wiki", remoteID, item.ID))
	}
	if err := s.guardRemoteAlias(ctx, req.RepoID, "wiki", remoteID, stableID); err != nil {
		return SyncResult{}, false, err
	}
	source, err := s.store.GetSourceScoped(ctx, req.RepoID, stableID)
	if err != nil {
		if isCacheNotFound(err) {
			return SyncResult{}, false, nil
		}
		return SyncResult{}, false, err
	}
	status, err := s.store.GetSyncStatusScoped(ctx, req.RepoID, stableID)
	if err != nil {
		if isCacheNotFound(err) {
			return SyncResult{}, false, nil
		}
		return SyncResult{}, false, err
	}
	if source.Kind != "wiki" || source.ContentHash == "" || status.RemoteType != "wiki" || status.RemoteID != remoteID || status.RemoteRevision != revision || status.Status != "fresh" {
		return SyncResult{}, false, nil
	}

	completedAt := s.now().UTC()
	counts := SyncCounts{Fetched: 1, Skipped: 1, Listed: 1, SkippedByRevision: 1}
	eventID := syncEventID(req.IdempotencyKey)
	event := cache.SyncEvent{ID: eventID, SourceID: stableID, RemoteType: "wiki", RemoteID: remoteID, RemoteRevision: revision, Status: "succeeded", IdempotencyKey: req.IdempotencyKey, Message: syncEventMessage(counts), CreatedAt: completedAt, StartedAt: completedAt, CompletedAt: completedAt, ZeroDelta: true}
	graph := cache.SourceGraph{
		Source:     source,
		Identities: []cache.Identity{{RepoID: req.RepoID, SourceID: stableID, AliasType: "wiki", Alias: remoteID, Remote: cache.RemoteAlias{Type: "wiki", ID: remoteID}}},
		SyncStatus: &cache.SyncStatus{RepoID: req.RepoID, SourceID: stableID, RemoteType: "wiki", RemoteID: remoteID, RemoteRevision: revision, Status: "fresh", LastFetchedAt: completedAt},
		SyncEvents: []cache.SyncEvent{event},
	}
	if item.ID != "" && item.ID != remoteID {
		graph.Identities = append(graph.Identities, cache.Identity{RepoID: req.RepoID, SourceID: stableID, AliasType: "gitcode_wiki_id", Alias: item.ID, Remote: cache.RemoteAlias{Type: "gitcode_wiki_id", ID: item.ID}})
	}
	if err := s.store.UpsertSyncGraph(ctx, s.syncGraphFromSourceGraph(req.RepoID, graph)); err != nil {
		return SyncResult{}, false, err
	}
	stored, err := s.store.GetSourceScoped(ctx, req.RepoID, stableID)
	if err != nil {
		return SyncResult{}, false, err
	}
	return SyncResult{IdempotencyKey: req.IdempotencyKey, Status: "succeeded", Counts: counts, SyncEventID: eventID, Freshness: string(FreshnessFresh), Record: sourceSummary(stored), GeneratedAt: completedAt, StartedAt: completedAt, CompletedAt: completedAt, ZeroDelta: true}, true, nil
}

func (s *Service) BulkSyncAll(ctx context.Context, req BulkSyncRequest) (_ *SyncResourcesResult, retErr error) {
	ctx, retainAttempt := beginSyncHTTPAttempt(ctx)
	defer func() { retErr = retainAttempt(retErr) }()
	if err := ctx.Err(); err != nil {
		return nil, err
	}
	repoID, err := s.requireRepo(ctx, req.RepoID, "bulk-sync-all")
	if err != nil {
		return nil, err
	}
	req.RepoID = repoID
	selection, err := s.ResolveSyncSelection(ctx, repoID, SyncSelection{})
	if err != nil {
		return nil, err
	}
	ctx, releaseWriter, err := s.acquireBulkWriter(ctx, repoID, "bulk-sync-all")
	if err != nil {
		return nil, err
	}
	defer releaseWriter()
	var issuesResult, wikiResult *SyncResourcesResult
	var issuesErr, wikiErr error
	var firstDiagnostic SyncDiagnostic

	if selection.Issues {
		issuesResult, issuesErr = s.BulkSyncIssues(ctx, req)
		if issuesErr != nil {
			if partial, ok := extractPartialSyncError(issuesErr); ok && partial.Diagnostic != "" {
				firstDiagnostic = partial.Diagnostic
			}
		}
	}

	if selection.Wiki {
		wikiResult, wikiErr = s.BulkSyncWiki(ctx, req)
		if wikiErr != nil && firstDiagnostic == "" {
			if partial, ok := extractPartialSyncError(wikiErr); ok && partial.Diagnostic != "" {
				firstDiagnostic = partial.Diagnostic
			}
		}
	}

	aggregated := &SyncResourcesResult{
		Results:  make([]SyncResult, 0),
		Failures: make([]ResourceError, 0),
	}
	MergeSyncTraversal(aggregated, issuesResult, issuesErr)
	MergeSyncTraversal(aggregated, wikiResult, wikiErr)
	if issuesResult != nil {
		aggregated.Results = append(aggregated.Results, issuesResult.Results...)
		aggregated.Failures = append(aggregated.Failures, issuesResult.Failures...)
		aggregated.IssueComments = issuesResult.IssueComments
	}
	if wikiResult != nil {
		aggregated.Results = append(aggregated.Results, wikiResult.Results...)
		aggregated.Failures = append(aggregated.Failures, wikiResult.Failures...)
	}
	aggregated.SuccessCount = len(aggregated.Results)
	aggregated.FailureCount = len(aggregated.Failures)

	if issuesErr != nil || wikiErr != nil {
		partial := &PartialSyncError{
			Errors:       aggregated.Failures,
			SuccessCount: aggregated.SuccessCount,
			FailureCount: aggregated.FailureCount,
			Diagnostic:   firstDiagnostic,
		}
		partial.PreserveHTTPAttemptEvidence(issuesErr, wikiErr)
		return aggregated, partial
	}
	if aggregated.FailureCount > 0 {
		return aggregated, &PartialSyncError{
			Errors:       aggregated.Failures,
			SuccessCount: aggregated.SuccessCount,
			FailureCount: aggregated.FailureCount,
		}
	}
	return aggregated, nil
}

func extractPartialSyncError(err error) (*PartialSyncError, bool) {
	var partial *PartialSyncError
	if errors.As(err, &partial) {
		return partial, true
	}
	return nil, false
}

func bulkSyncFailureResult(err error, sourceID, remoteType string) (*SyncResourcesResult, error) {
	re := newResourceError(sourceID, remoteType, err)
	result := &SyncResourcesResult{Failures: []ResourceError{re}, FailureCount: 1}
	return result, &PartialSyncError{Errors: result.Failures, FailureCount: 1}
}

func (s *Service) ensureBulkIdempotencyKey(req *BulkSyncRequest, scope string) {
	if req == nil || strings.TrimSpace(req.IdempotencyKey) != "" {
		return
	}
	key := contentHash("bulk-sync", scope, req.RepoID, s.now().UTC().Format(time.RFC3339Nano))
	if len(key) > 32 {
		key = key[:32]
	}
	req.IdempotencyKey = key
}

func scopedBulkSyncKey(base, scope, id string) string {
	if strings.TrimSpace(base) == "" {
		return ""
	}
	return base + "-" + scope + "-" + id
}

func (s *Service) CreateIssue(ctx context.Context, req WriteCommandRequest) (WriteCommandResult, error) {
	if strings.TrimSpace(req.Title) == "" {
		return WriteCommandResult{}, ErrInvalidQuery{Field: "title", Message: "title is required"}
	}
	if req.ClearMilestone {
		return WriteCommandResult{}, ErrInvalidQuery{Field: "clear_milestone", Message: "clear_milestone is not valid when creating an issue"}
	}
	return s.executeWrite(ctx, "create-issue", req, RepositoryScopeIssues)
}

func (s *Service) UpdateIssue(ctx context.Context, req WriteCommandRequest) (WriteCommandResult, error) {
	if req.Number == 0 && strings.TrimSpace(firstNonEmptyString(req.IssueID, req.ID)) == "" {
		return WriteCommandResult{}, ErrInvalidQuery{Field: "issue", Message: "number or issue_id is required"}
	}
	switch strings.TrimSpace(req.State) {
	case "", "open", "closed":
	default:
		return WriteCommandResult{}, ErrInvalidQuery{Field: "state", Message: "issue state must be open or closed"}
	}
	if strings.TrimSpace(req.Milestone) != "" && req.ClearMilestone {
		return WriteCommandResult{}, ErrInvalidQuery{Field: "milestone", Message: "milestone and clear_milestone are mutually exclusive"}
	}
	return s.executeWrite(ctx, "update-issue", req, RepositoryScopeIssues)
}

func (s *Service) CreatePage(ctx context.Context, req WriteCommandRequest) (WriteCommandResult, error) {
	if strings.TrimSpace(req.Body) == "" || strings.TrimSpace(firstNonEmptyString(req.Path, req.Slug, req.Title)) == "" {
		return WriteCommandResult{}, ErrInvalidQuery{Field: "page", Message: "path and body are required"}
	}
	return s.executeWrite(ctx, "create-page", req, RepositoryScopeWiki)
}

func (s *Service) UpdatePage(ctx context.Context, req WriteCommandRequest) (WriteCommandResult, error) {
	if strings.TrimSpace(firstNonEmptyString(req.Path, req.Slug, req.ID)) == "" {
		return WriteCommandResult{}, ErrInvalidQuery{Field: "page", Message: "path or id is required"}
	}
	return s.executeWrite(ctx, "update-page", req, RepositoryScopeWiki)
}

func (s *Service) DeletePage(ctx context.Context, req WriteCommandRequest) (WriteCommandResult, error) {
	if strings.TrimSpace(firstNonEmptyString(req.Path, req.Slug, req.ID)) == "" {
		return WriteCommandResult{}, ErrInvalidQuery{Field: "page", Message: "path or id is required"}
	}
	return s.executeWrite(ctx, "delete-page", req, RepositoryScopeWiki)
}

func (s *Service) AddComment(ctx context.Context, req WriteCommandRequest) (WriteCommandResult, error) {
	if (req.Number == 0 && strings.TrimSpace(firstNonEmptyString(req.IssueID, req.ID)) == "") || strings.TrimSpace(req.Body) == "" {
		return WriteCommandResult{}, ErrInvalidQuery{Field: "comment", Message: "issue and body are required"}
	}
	return s.executeWrite(ctx, "add-comment", req, RepositoryScopeIssues)
}

func (s *Service) UpdateComment(ctx context.Context, req WriteCommandRequest) (WriteCommandResult, error) {
	if strings.TrimSpace(firstNonEmptyString(req.CommentID, req.ID)) == "" || strings.TrimSpace(req.Body) == "" {
		return WriteCommandResult{}, ErrInvalidQuery{Field: "comment", Message: "comment id and body are required"}
	}
	return s.executeWrite(ctx, "update-comment", req, RepositoryScopeIssues)
}

func (s *Service) CreatePR(ctx context.Context, req WriteCommandRequest) (WriteCommandResult, error) {
	if strings.TrimSpace(req.Title) == "" || strings.TrimSpace(req.Head) == "" || strings.TrimSpace(req.Base) == "" {
		return WriteCommandResult{}, ErrInvalidQuery{Field: "pull_request", Message: "title, head, and base are required"}
	}
	return s.executeWrite(ctx, "create-pr", req, RepositoryScopeIssues)
}

func (s *Service) UpdatePR(ctx context.Context, req WriteCommandRequest) (WriteCommandResult, error) {
	if req.Number == 0 {
		return WriteCommandResult{}, ErrInvalidQuery{Field: "pull_request", Message: "number is required"}
	}
	return s.executeWrite(ctx, "update-pr", req, RepositoryScopeIssues)
}

func (s *Service) MergePR(ctx context.Context, req WriteCommandRequest) (WriteCommandResult, error) {
	if req.Number == 0 {
		return WriteCommandResult{}, ErrInvalidQuery{Field: "pull_request", Message: "number is required"}
	}
	strategy := strings.ToLower(strings.TrimSpace(req.Strategy))
	if strategy == "" {
		strategy = "merge"
	}
	switch strategy {
	case "merge", "squash", "rebase":
		req.Strategy = strategy
	default:
		return WriteCommandResult{}, ErrInvalidQuery{Field: "strategy", Message: "must be merge, squash, or rebase"}
	}
	return s.executeWrite(ctx, "merge-pr", req, RepositoryScopeIssues)
}

func (s *Service) ListMilestones(ctx context.Context, req MilestoneListRequest) (MilestoneListResult, error) {
	repoID := firstNonEmptyString(req.RepoID, req.Repo)
	route, err := s.BuildAdapterRoute(ctx, repoID, RepositoryScopeIssues)
	if err != nil {
		return MilestoneListResult{}, err
	}
	perPage := req.PerPage
	if perPage <= 0 {
		perPage = 100
	}
	page, err := s.client.ListMilestones(ctx, gitcode.MilestoneListRequest{Owner: route.Owner, Repo: route.Name, State: req.State, Page: req.Page, PerPage: perPage})
	if err != nil {
		return MilestoneListResult{}, err
	}
	now := s.now().UTC()
	milestones := make([]MilestoneRecord, 0, len(page.Items))
	for _, milestone := range page.Items {
		_, graph := s.milestoneWriteGraph(route.RepoID, milestone, gitcode.WriteResult[gitcode.Milestone]{Record: milestone, Confirmed: true, Operation: "ListMilestones", RemoteID: milestone.RemoteID, RemoteRevision: milestone.UpdatedAt, BrowserURL: milestone.HTMLURL, ConfirmedAt: now}, now)
		if err := s.store.UpsertRecordGraph(ctx, graph); err != nil {
			return MilestoneListResult{}, err
		}
		milestones = append(milestones, milestoneRecord(milestone))
	}
	return MilestoneListResult{RepoID: route.RepoID, Milestones: milestones, Page: page.Page, PerPage: page.PerPage, Count: len(milestones), Evidence: "adapter-confirmed read with cache refresh", GeneratedAt: now}, nil
}

func (s *Service) ListPushRemoteMirrors(ctx context.Context, req PushMirrorListRequest) (PushMirrorListResult, error) {
	repoID := firstNonEmptyString(req.RepoID, req.Repo)
	route, err := s.BuildAdapterRoute(ctx, repoID, RepositoryScopeIssues)
	if err != nil {
		return PushMirrorListResult{}, err
	}
	mirrors, err := s.client.ListPushRemoteMirrors(ctx, gitcode.PushMirrorListRequest{Owner: route.Owner, Repo: route.Name})
	if err != nil {
		return PushMirrorListResult{}, err
	}
	sort.Slice(mirrors, func(i, j int) bool {
		left, leftErr := strconv.ParseInt(mirrors[i].RemoteID, 10, 64)
		right, rightErr := strconv.ParseInt(mirrors[j].RemoteID, 10, 64)
		if leftErr == nil && rightErr == nil && left != right {
			return left < right
		}
		if mirrors[i].RemoteID != mirrors[j].RemoteID {
			return mirrors[i].RemoteID < mirrors[j].RemoteID
		}
		return mirrors[i].URL < mirrors[j].URL
	})
	now := s.now().UTC()
	records := make([]PushMirrorRecord, 0, len(mirrors))
	for _, mirror := range mirrors {
		record, graph := pushMirrorRecordGraph(route.RepoID, mirror, now)
		if err := s.store.UpsertRecordGraph(ctx, graph); err != nil {
			return PushMirrorListResult{}, normalizeError(err, "push mirror cache", route.RepoID)
		}
		records = append(records, record)
	}
	return PushMirrorListResult{RepoID: route.RepoID, Mirrors: records, Count: len(records), Evidence: "adapter-confirmed read with sanitized cache refresh", GeneratedAt: now}, nil
}

func (s *Service) TriggerPushRemoteMirror(ctx context.Context, req WriteCommandRequest) (WriteCommandResult, error) {
	if req.Mode == WriteModeLive && strings.TrimSpace(req.IdempotencyKey) == "" {
		return WriteCommandResult{}, ErrInvalidQuery{Field: "idempotency_key", Message: "is required for live push mirror triggers"}
	}
	repoID := firstNonEmptyString(req.RepoID, req.Repo)
	route, err := s.BuildAdapterRoute(ctx, repoID, RepositoryScopeIssues)
	if err != nil {
		return WriteCommandResult{}, err
	}
	mirrors, err := s.client.ListPushRemoteMirrors(ctx, gitcode.PushMirrorListRequest{Owner: route.Owner, Repo: route.Name})
	if err != nil {
		return WriteCommandResult{}, err
	}
	mirror, err := resolvePushMirror(mirrors, req.ID)
	if err != nil {
		return WriteCommandResult{}, err
	}
	req.RepoID = route.RepoID
	req.Repo = route.RepoID
	req.ID = mirror.RemoteID
	req.pushMirrorPreviousStatus = mirror.UpdateStatus
	return s.executeWrite(ctx, "trigger-push-mirror", req, RepositoryScopeIssues)
}

func (s *Service) WaitPushRemoteMirror(ctx context.Context, req PushMirrorWaitRequest) (PushMirrorWaitResult, error) {
	timeoutSeconds := req.TimeoutSeconds
	if timeoutSeconds == 0 {
		timeoutSeconds = 120
	}
	if timeoutSeconds < 1 || timeoutSeconds > 600 {
		return PushMirrorWaitResult{}, ErrInvalidQuery{Field: "timeout_seconds", Message: "must be between 1 and 600"}
	}
	pollInterval := req.PollInterval
	if pollInterval <= 0 {
		pollInterval = 2 * time.Second
	}
	timeout := req.Timeout
	if timeout <= 0 {
		timeout = time.Duration(timeoutSeconds) * time.Second
	}
	deadline := time.NewTimer(timeout)
	defer deadline.Stop()
	mirrorID := strings.TrimSpace(req.MirrorID)
	for {
		list, err := s.ListPushRemoteMirrors(ctx, PushMirrorListRequest{RepoID: firstNonEmptyString(req.RepoID, req.Repo)})
		if err != nil {
			return PushMirrorWaitResult{}, err
		}
		record, err := resolvePushMirrorRecord(list.Mirrors, mirrorID)
		if err != nil {
			return PushMirrorWaitResult{}, err
		}
		mirrorID = record.RemoteID
		result := pushMirrorWaitResult(list.RepoID, record, req.After, s.now().UTC())
		if pushMirrorTerminalAfter(record, req.After) {
			result.Evidence = "terminal status confirmed by sanitized live readback"
			return result, nil
		}
		select {
		case <-ctx.Done():
			return PushMirrorWaitResult{}, ctx.Err()
		case <-deadline.C:
			result.Status = "timeout"
			result.Evidence = "timeout while polling sanitized live mirror status"
			result.GeneratedAt = s.now().UTC()
			return result, nil
		case <-time.After(pollInterval):
		}
	}
}

func resolvePushMirror(mirrors []gitcode.PushMirror, mirrorID string) (gitcode.PushMirror, error) {
	id := strings.TrimSpace(mirrorID)
	if id == "" {
		if len(mirrors) != 1 {
			return gitcode.PushMirror{}, ErrPushMirrorSelectionRequired{Count: len(mirrors)}
		}
		return mirrors[0], nil
	}
	for _, mirror := range mirrors {
		if mirror.RemoteID == id || fallbackSourceID("pushmirror", mirror.RemoteID) == id {
			return mirror, nil
		}
	}
	return gitcode.PushMirror{}, ErrPushMirrorNotFound{MirrorID: id}
}

func resolvePushMirrorRecord(mirrors []PushMirrorRecord, mirrorID string) (PushMirrorRecord, error) {
	id := strings.TrimSpace(mirrorID)
	if id == "" {
		if len(mirrors) != 1 {
			return PushMirrorRecord{}, ErrPushMirrorSelectionRequired{Count: len(mirrors)}
		}
		return mirrors[0], nil
	}
	for _, mirror := range mirrors {
		if mirror.RemoteID == id || mirror.ID == id {
			return mirror, nil
		}
	}
	return PushMirrorRecord{}, ErrPushMirrorNotFound{MirrorID: id}
}

func pushMirrorTerminalAfter(mirror PushMirrorRecord, after time.Time) bool {
	status := strings.ToLower(strings.TrimSpace(mirror.UpdateStatus))
	if status != "finished" && status != "failed" {
		return false
	}
	if after.IsZero() {
		return true
	}
	if status == "finished" {
		return !mirror.LastSuccessfulUpdateAt.IsZero() && !mirror.LastSuccessfulUpdateAt.Before(after)
	}
	return !mirror.LastUpdateAt.IsZero() && !mirror.LastUpdateAt.Before(after)
}

func pushMirrorWaitResult(repoID string, mirror PushMirrorRecord, after, now time.Time) PushMirrorWaitResult {
	status := strings.ToLower(strings.TrimSpace(mirror.UpdateStatus))
	if status != "finished" && status != "failed" {
		status = "waiting"
	}
	return PushMirrorWaitResult{
		RepoID:                 repoID,
		MirrorID:               mirror.RemoteID,
		Status:                 status,
		UpdateStatus:           mirror.UpdateStatus,
		NumberOfFailures:       mirror.NumberOfFailures,
		Message:                mirror.Message,
		LastUpdateAt:           mirror.LastUpdateAt,
		LastSuccessfulUpdateAt: mirror.LastSuccessfulUpdateAt,
		After:                  after,
		GeneratedAt:            now,
	}
}

func (s *Service) CreateMilestone(ctx context.Context, req WriteCommandRequest) (WriteCommandResult, error) {
	if err := validateMilestoneCommandFields(req, true); err != nil {
		return WriteCommandResult{}, err
	}
	return s.executeWrite(ctx, "create-milestone", req, RepositoryScopeIssues)
}

func (s *Service) UpdateMilestone(ctx context.Context, req WriteCommandRequest) (WriteCommandResult, error) {
	if strings.TrimSpace(firstNonEmptyString(req.Milestone, req.ID)) == "" {
		return WriteCommandResult{}, ErrInvalidQuery{Field: "milestone", Message: "milestone id or title is required"}
	}
	if err := validateMilestoneCommandFields(req, false); err != nil {
		return WriteCommandResult{}, err
	}
	return s.executeWrite(ctx, "update-milestone", req, RepositoryScopeIssues)
}

func validateMilestoneCommandFields(req WriteCommandRequest, create bool) error {
	err := gitcode.ValidateMilestoneWriteFields(gitcode.MilestoneWriteRequest{Title: req.Title, Description: req.Description, DueOn: req.DueOn, State: req.State}, create)
	var validation gitcode.ErrValidationFailed
	if errors.As(err, &validation) {
		return ErrInvalidQuery{Field: validation.Field, Message: validation.Message}
	}
	return err
}

func (s *Service) SetIssueMilestone(ctx context.Context, req WriteCommandRequest) (WriteCommandResult, error) {
	if req.Number == 0 && strings.TrimSpace(req.IssueID) == "" {
		return WriteCommandResult{}, ErrInvalidQuery{Field: "issue", Message: "issue number or issue_id is required"}
	}
	if strings.TrimSpace(firstNonEmptyString(req.Milestone, req.ID)) == "" {
		return WriteCommandResult{}, ErrInvalidQuery{Field: "milestone", Message: "milestone id or title is required"}
	}
	return s.executeWrite(ctx, "set-issue-milestone", req, RepositoryScopeIssues)
}

func (s *Service) ClearIssueMilestone(ctx context.Context, req WriteCommandRequest) (WriteCommandResult, error) {
	if req.Number == 0 && strings.TrimSpace(firstNonEmptyString(req.IssueID, req.ID)) == "" {
		return WriteCommandResult{}, ErrInvalidQuery{Field: "issue", Message: "issue number or issue_id is required"}
	}
	return s.executeWrite(ctx, "clear-issue-milestone", req, RepositoryScopeIssues)
}

func (s *Service) AddPRComment(ctx context.Context, req WriteCommandRequest) (WriteCommandResult, error) {
	if req.Number == 0 || strings.TrimSpace(req.Body) == "" {
		return WriteCommandResult{}, ErrInvalidQuery{Field: "pr_comment", Message: "pull request number and body are required"}
	}
	return s.executeWrite(ctx, "add-pr-comment", req, RepositoryScopeIssues)
}

func (s *Service) AddPRReviewComment(ctx context.Context, req WriteCommandRequest) (WriteCommandResult, error) {
	if req.Number == 0 || strings.TrimSpace(req.Body) == "" || strings.TrimSpace(req.Path) == "" || (req.Line == 0 && req.Position == 0) {
		return WriteCommandResult{}, ErrInvalidQuery{Field: "pr_review_comment", Message: "pull request number, body, path, and line or position are required"}
	}
	if req.Line == 0 {
		req.Line = req.Position
	}
	if req.Position > 0 && req.Position != req.Line {
		return WriteCommandResult{}, ErrInvalidQuery{Field: "position", Message: "position is a deprecated file-line alias and must equal line when both are supplied"}
	}
	if req.EndLine > 0 && req.EndLine != req.Line {
		return WriteCommandResult{}, ErrInvalidQuery{Field: "end_line", Message: "end_line must equal the anchor line"}
	}
	if req.StartLine > req.Line {
		return WriteCommandResult{}, ErrInvalidQuery{Field: "start_line", Message: "start_line must be less than or equal to the anchor line"}
	}
	req.Position = req.Line
	return s.executeWrite(ctx, "add-pr-review-comment", req, RepositoryScopeIssues)
}

func (s *Service) ReplyPRReviewComment(ctx context.Context, req WriteCommandRequest) (WriteCommandResult, error) {
	if req.Number == 0 || strings.TrimSpace(req.Body) == "" || strings.TrimSpace(req.DiscussionID) == "" || strings.TrimSpace(req.ParentID) == "" {
		return WriteCommandResult{}, ErrInvalidQuery{Field: "pr_review_reply", Message: "pull request number, discussion id, parent comment id, and body are required"}
	}
	return s.executeWrite(ctx, "reply-pr-review-comment", req, RepositoryScopeIssues)
}

func (s *Service) LinkPRIssue(ctx context.Context, req WriteCommandRequest) (WriteCommandResult, error) {
	if req.Number == 0 || (req.IssueNumber == 0 && strings.TrimSpace(req.IssueID) == "") {
		return WriteCommandResult{}, ErrInvalidQuery{Field: "link", Message: "pull request number and issue number or issue_id are required"}
	}
	return s.executeWrite(ctx, "link-pr-issue", req, RepositoryScopeIssues)
}

func (s *Service) AddLabel(ctx context.Context, req WriteCommandRequest) (WriteCommandResult, error) {
	if (req.Number == 0 && strings.TrimSpace(firstNonEmptyString(req.IssueID, req.ID)) == "") || strings.TrimSpace(req.Label) == "" {
		return WriteCommandResult{}, ErrInvalidQuery{Field: "label", Message: "issue and label are required"}
	}
	return s.executeWrite(ctx, "add-label", req, RepositoryScopeIssues)
}

func (s *Service) PublishRelease(ctx context.Context, req PublishReleaseRequest) (PublishReleaseResult, error) {
	if strings.TrimSpace(req.Tag) == "" {
		return PublishReleaseResult{}, ErrInvalidQuery{Field: "tag", Message: "release tag is required"}
	}
	if strings.TrimSpace(req.Title) == "" {
		return PublishReleaseResult{}, ErrInvalidQuery{Field: "title", Message: "release title is required"}
	}
	if strings.TrimSpace(req.Body) == "" {
		return PublishReleaseResult{}, ErrInvalidQuery{Field: "body", Message: "release body is required"}
	}
	if req.Mode != WriteModeDryRun && req.Mode != WriteModeLive {
		return PublishReleaseResult{}, ErrInvalidQuery{Field: "write_mode", Message: "exactly one of dry_run or live is required"}
	}
	route, err := s.BuildAdapterRoute(ctx, firstNonEmptyString(req.RepoID, req.Repo), RepositoryScopeIssues)
	if err != nil {
		return PublishReleaseResult{}, err
	}
	status, err := releaseStatus(req.Status)
	if err != nil {
		return PublishReleaseResult{}, err
	}
	if strings.TrimSpace(req.Ref) == "" {
		req.Ref = "main"
	}
	key, fingerprint := releaseIdempotency(req, status)
	base := PublishReleaseResult{Command: "publish-release", Status: "dry_run_valid", RepoID: route.RepoID, Tag: strings.TrimSpace(req.Tag), ReleaseStatus: status, AssetLinks: req.Assets, IdempotencyKey: key, SourceFingerprint: fingerprint, Evidence: "validated release publish command", GeneratedAt: s.now().UTC()}
	if req.Mode == WriteModeDryRun {
		return base, nil
	}
	if !s.hasWriteCredential() {
		return PublishReleaseResult{}, ErrWriteFailure{Code: "write_missing_credential", RepoID: route.RepoID, IdempotencyKey: key}
	}
	links := make([]gitcode.ReleaseLink, 0, len(req.Assets))
	for _, asset := range req.Assets {
		links = append(links, gitcode.ReleaseLink{Name: strings.TrimSpace(asset.Name), URL: strings.TrimSpace(asset.URL)})
	}
	writeReq := gitcode.ReleaseWriteRequest{
		Owner:         route.Owner,
		Repo:          route.Name,
		TagName:       strings.TrimSpace(req.Tag),
		Ref:           strings.TrimSpace(req.Ref),
		Name:          strings.TrimSpace(req.Title),
		Description:   releaseBodyWithAssets(req.Body, req.Assets),
		ReleaseStatus: status,
		Links:         links,
	}
	existing, err := s.client.GetRelease(ctx, gitcode.ReleaseRequest{Owner: route.Owner, Repo: route.Name, Tag: writeReq.TagName})
	var result gitcode.WriteResult[gitcode.Release]
	if err == nil && strings.TrimSpace(existing.TagName) != "" {
		result, err = s.client.UpdateRelease(ctx, writeReq, gitcode.WriteOptions{IdempotencyKey: key})
	} else if isReleaseNotFound(err) {
		result, err = s.client.CreateRelease(ctx, writeReq, gitcode.WriteOptions{IdempotencyKey: key})
	} else if err != nil {
		return PublishReleaseResult{}, ErrWriteFailure{Code: s.writeAdapterErrorCode(req.Mode, err), RepoID: route.RepoID, IdempotencyKey: key, PayloadSource: failureSource(err), Cause: writeFailureCause(s.writeAdapterErrorCode(req.Mode, err), err)}
	}
	if err != nil {
		code := s.writeAdapterErrorCode(req.Mode, err)
		return PublishReleaseResult{}, ErrWriteFailure{Code: code, RepoID: route.RepoID, IdempotencyKey: key, PayloadSource: failureSource(err), Cause: writeFailureCause(code, err)}
	}
	if !result.Confirmed || strings.TrimSpace(result.RemoteID) == "" {
		return PublishReleaseResult{}, ErrWriteFailure{Code: "write_unconfirmed_remote", RepoID: route.RepoID, IdempotencyKey: key}
	}
	base.Status = "succeeded"
	base.RemoteID = result.RemoteID
	base.Evidence = "adapter-confirmed release publish"
	base.GeneratedAt = firstNonZeroTime(result.ConfirmedAt, s.now().UTC())
	return base, nil
}

func (s *Service) requireCachedPRParent(ctx context.Context, repoID string, number int) (string, error) {
	stableID := pullRequestStableID(number)
	identity, err := s.store.ResolveAliasScoped(ctx, repoID, cache.RemoteAlias{Type: "pull_request", ID: strconv.Itoa(number)})
	if err == nil && strings.TrimSpace(identity.SourceID) != "" {
		stableID = identity.SourceID
	} else if err != nil && !isCacheNotFound(err) {
		return "", err
	}
	record, err := s.store.GetRecord(ctx, repoID, stableID)
	if err != nil {
		if isCacheNotFound(err) {
			return "", ErrParentPRNotCached{RepoID: repoID, Number: number}
		}
		return "", err
	}
	if record.Type != "pull_request" {
		return "", ErrParentPRNotCached{RepoID: repoID, Number: number}
	}
	return stableID, nil
}

func (s *Service) operationResult(ctx context.Context, command string, req OperationRequest) (OperationResult, error) {
	if err := ctx.Err(); err != nil {
		return OperationResult{}, err
	}
	sources, err := s.store.ListSources(ctx, cache.SourceFilter{})
	if err != nil && !isCacheNotFound(err) {
		return OperationResult{}, normalizeError(err, "sources", "")
	}
	return OperationResult{Command: command, Status: "ok", ProcessedCount: len(sources), Evidence: operationMode(req.Mode), GeneratedAt: s.now().UTC()}, nil
}

func operationMode(mode string) string {
	mode = strings.TrimSpace(mode)
	if mode == "" {
		return "default"
	}
	return mode
}

func (s *Service) seedMinimumCorpus(ctx context.Context) error {
	now := s.now().UTC()
	taskBody := "# Task\n\nTASK-001 keeps the offline export walkthrough cache-first.\n"
	taskHash := index.ContentHash(taskBody)
	if err := s.store.UpsertSourceGraph(ctx, cache.SourceGraph{
		Source:     cache.Source{ID: "TASK-001", Kind: "task", Path: "project/tasks/day7.md", Title: "Offline Export Walkthrough", Body: taskBody, Status: "ready", Labels: []string{"task"}, ContentHash: taskHash, CreatedAt: now, UpdatedAt: now},
		Identities: []cache.Identity{{SourceID: "TASK-001", AliasType: "id", Alias: "TASK-001"}},
		SyncStatus: &cache.SyncStatus{SourceID: "TASK-001", RemoteType: "fixture", RemoteID: "task-001", RemoteRevision: taskHash, Status: "fresh", LastFetchedAt: now},
	}); err != nil {
		return normalizeError(err, "source", "TASK-001")
	}
	body := "---\nstatus: ready\nlabels: backlog,design\n---\n# Backlog\n\nDOC-123 describes the cache-first backlog.\n\nSee [task](TASK-001).\n"
	hash := index.ContentHash(body)
	graph := cache.SourceGraph{
		Source:     cache.Source{ID: "DOC-123", Kind: "doc", Path: "docs/day7-offline.md", Title: "Day 7 Offline Backlog", Body: body, Status: "ready", Labels: []string{"backlog", "design"}, ContentHash: hash, CreatedAt: now, UpdatedAt: now},
		Identities: []cache.Identity{{SourceID: "DOC-123", AliasType: "remote", Alias: "wiki/day7-offline", Remote: cache.RemoteAlias{Type: "wiki", ID: "day7-offline"}}},
		Links:      []cache.Link{{SourceID: "DOC-123", TargetID: "TASK-001", Kind: "markdown", Text: "task"}},
		SyncStatus: &cache.SyncStatus{SourceID: "DOC-123", RemoteType: "fixture", RemoteID: "day7-offline", RemoteRevision: hash, Status: "fresh", LastFetchedAt: now},
	}
	if err := s.store.UpsertSourceGraph(ctx, graph); err != nil {
		return normalizeError(err, "source", graph.Source.ID)
	}
	return nil
}

func chunksForSource(source cache.Source) []cache.Chunk {
	idxSource := indexSourceRecord(source)
	parsed := index.ParseSource(idxSource)
	chunks := index.ChunkSource(idxSource, parsed)
	out := make([]cache.Chunk, 0, len(chunks))
	for _, chunk := range chunks {
		out = append(out, cacheChunk(chunk))
	}
	return out
}

func cacheChunk(chunk index.Chunk) cache.Chunk {
	return cache.Chunk{RepoID: chunk.RepoID, ID: chunk.ID, SourceID: chunk.SourceID, RecordID: chunk.RecordID, SnapshotID: chunk.SnapshotID, ContentHash: chunk.ContentHash, ByteStart: chunk.ByteStart, ByteEnd: chunk.ByteEnd, LineStart: chunk.LineStart, LineEnd: chunk.LineEnd, HeadingPath: append([]string(nil), chunk.HeadingPath...), Text: chunk.Text, NormalizedText: chunk.NormalizedText, InheritedMetadata: copyStringMap(chunk.InheritedMetadata), OutboundLinks: sortedStrings(chunk.OutboundLinks), ResolvedAliases: copyStringMap(chunk.ResolvedAliases), Embedding: append([]byte(nil), chunk.Embedding...), Policy: string(chunk.Policy)}
}

func indexChunks(chunks []cache.Chunk) []index.Chunk {
	out := make([]index.Chunk, 0, len(chunks))
	for _, chunk := range chunks {
		out = append(out, index.Chunk{RepoID: chunk.RepoID, ID: chunk.ID, SourceID: chunk.SourceID, RecordID: chunk.RecordID, SnapshotID: chunk.SnapshotID, ContentHash: chunk.ContentHash, ByteStart: chunk.ByteStart, ByteEnd: chunk.ByteEnd, LineStart: chunk.LineStart, LineEnd: chunk.LineEnd, HeadingPath: append([]string(nil), chunk.HeadingPath...), Text: chunk.Text, NormalizedText: chunk.NormalizedText, InheritedMetadata: copyStringMap(chunk.InheritedMetadata), OutboundLinks: sortedStrings(chunk.OutboundLinks), ResolvedAliases: copyStringMap(chunk.ResolvedAliases), Embedding: append([]byte(nil), chunk.Embedding...), Policy: index.ChunkPolicy(chunk.Policy)})
	}
	return out
}

func indexSourceRecord(source cache.Source) index.SourceRecord {
	aliases := make([]index.Alias, 0, len(source.Aliases))
	remoteAliases := make([]index.Alias, 0, len(source.Aliases))
	for _, alias := range source.Aliases {
		if alias.AliasType != "" && alias.Alias != "" {
			aliases = append(aliases, index.Alias{Type: alias.AliasType, ID: alias.Alias})
		}
		if alias.Remote.Type != "" && alias.Remote.ID != "" {
			remoteAliases = append(remoteAliases, index.Alias{Type: alias.Remote.Type, ID: alias.Remote.ID})
		}
	}
	return index.SourceRecord{RepoID: source.RepoID, ID: source.ID, RecordID: source.ID, Kind: source.Kind, Path: source.Path, Title: source.Title, Body: source.Body, Metadata: map[string]string{"content_hash": source.ContentHash, "source_updated_at": source.UpdatedAt.UTC().Format(time.RFC3339Nano)}, Status: source.Status, UpdatedAt: source.UpdatedAt.UTC(), Aliases: aliases, RemoteAliases: remoteAliases}
}

type stagedRemote struct {
	source       cache.Source
	identity     cache.Identity
	syncStatus   cache.SyncStatus
	remoteType   string
	remoteID     string
	revision     string
	contentBytes int64
}

func (s *Service) syncIdempotencyKey(req SyncRequest) string {
	payload := strings.Join([]string{"sync", req.Source, req.TrackerID, req.StableID, req.RemoteAlias, req.AliasType, req.AliasID, s.now().UTC().Format(time.RFC3339Nano)}, "|")
	sum := sha256.Sum256([]byte(payload))
	return hex.EncodeToString(sum[:])[:32]
}

func syncEventID(key string) string {
	sum := sha256.Sum256([]byte("sync-event|" + key))
	return hex.EncodeToString(sum[:])[:32]
}

func (s *Service) syncEventSourceID(ctx context.Context, req SyncRequest, remoteType, remoteID string) string {
	if req.StableID != "" {
		return req.StableID
	}
	identity, err := s.store.ResolveAliasScoped(ctx, req.RepoID, cache.RemoteAlias{Type: remoteType, ID: remoteID})
	if err == nil && identity.SourceID != "" {
		return identity.SourceID
	}
	return fallbackSourceID(remoteType, remoteID)
}

func (s *Service) syncTarget(ctx context.Context, req SyncRequest) (string, string, error) {
	if req.AliasType != "" || req.AliasID != "" {
		if req.AliasType == "" || req.AliasID == "" {
			return "", "", ErrInvalidQuery{Field: "alias", Message: "alias type and id are required together"}
		}
		return req.AliasType, req.AliasID, nil
	}
	if req.RemoteAlias != "" {
		parts := strings.SplitN(req.RemoteAlias, ":", 2)
		if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
			return "", "", ErrInvalidQuery{Field: "remote_alias", Message: "remote alias must be type:id"}
		}
		return parts[0], parts[1], nil
	}
	if req.StableID != "" {
		source, err := s.store.GetSourceScoped(ctx, req.RepoID, req.StableID)
		if err != nil {
			return "", "", normalizeError(err, "source", req.StableID)
		}
		for _, identity := range source.Aliases {
			if identity.Remote.Type != "" && identity.Remote.ID != "" {
				return identity.Remote.Type, identity.Remote.ID, nil
			}
		}
		status, err := s.store.GetSyncStatus(ctx, req.StableID)
		if err == nil && status.RemoteType != "" && status.RemoteID != "" {
			return status.RemoteType, status.RemoteID, nil
		}
		return "", "", ErrSyncNoRemoteAlias{Target: req.StableID}
	}
	return "", "", ErrInvalidQuery{Field: "sync target", Message: "stable id or remote alias is required"}
}

func (s *Service) fetchAndStage(ctx context.Context, req SyncRequest, remoteType, remoteID string) (cache.SourceGraph, SyncCounts, error) {
	if s.client == nil {
		return cache.SourceGraph{}, SyncCounts{}, ErrInvalidQuery{Field: "client", Message: "sync requires a GitCode client"}
	}
	attempts := req.MaxAttempts
	if attempts <= 0 {
		attempts = 1
	}
	var graph cache.SourceGraph
	var counts SyncCounts
	var err error
	for attempt := 0; attempt < attempts; attempt++ {
		graph, counts, err = s.fetchOnce(ctx, req, remoteType, remoteID)
		if err == nil {
			return graph, counts, nil
		}
		if attempt == attempts-1 || !isRetryableSyncError(err) {
			return cache.SourceGraph{}, SyncCounts{}, err
		}
		if wait := retryDelay(err); wait > 0 {
			if deadline, ok := ctx.Deadline(); ok && time.Now().Add(wait).After(deadline) {
				return cache.SourceGraph{}, SyncCounts{}, err
			}
			timer := time.NewTimer(wait)
			select {
			case <-ctx.Done():
				timer.Stop()
				return cache.SourceGraph{}, SyncCounts{}, ctx.Err()
			case <-timer.C:
			}
		}
	}
	return cache.SourceGraph{}, SyncCounts{}, err
}

func (s *Service) fetchOnce(ctx context.Context, req SyncRequest, remoteType, remoteID string) (cache.SourceGraph, SyncCounts, error) {
	switch remoteType {
	case "issue", "issues":
		route, err := s.BuildAdapterRoute(ctx, req.RepoID, RepositoryScopeIssues)
		if err != nil {
			return cache.SourceGraph{}, SyncCounts{}, err
		}
		number, err := strconv.Atoi(remoteID)
		if err != nil {
			return cache.SourceGraph{}, SyncCounts{}, ErrInvalidQuery{Field: "remote_id", Message: "issue remote id must be numeric"}
		}
		issue, err := s.client.GetIssue(ctx, gitcode.IssueRequest{Owner: route.Owner, Repo: route.Name, Number: number, KnownRemoteAlias: true, RemoteAlias: remoteID})
		if err != nil {
			return cache.SourceGraph{}, SyncCounts{}, err
		}
		if issue.Number != number {
			return cache.SourceGraph{}, SyncCounts{}, ErrSyncFailure{
				Mode:           "remote_identity_mismatch",
				Target:         remoteType + ":" + remoteID,
				ExpectedID:     strconv.Itoa(number),
				ActualID:       strconv.Itoa(issue.Number),
				RecoveryAction: "inspect the GitCode single-issue response contract before retrying",
			}
		}
		return s.stageIssue(ctx, req, remoteType, remoteID, issue)
	case "pull_request", "pull", "pulls", "pr":
		route, err := s.BuildAdapterRoute(ctx, req.RepoID, RepositoryScopeIssues)
		if err != nil {
			return cache.SourceGraph{}, SyncCounts{}, err
		}
		number, err := strconv.Atoi(remoteID)
		if err != nil {
			return cache.SourceGraph{}, SyncCounts{}, ErrInvalidQuery{Field: "remote_id", Message: "pull request remote id must be numeric"}
		}
		pr, err := s.client.GetPR(ctx, gitcode.PRRequest{Owner: route.Owner, Repo: route.Name, Number: number})
		if err != nil {
			return cache.SourceGraph{}, SyncCounts{}, err
		}
		return s.stagePullRequest(ctx, req, "pull_request", remoteID, pr)
	case "wiki", "page", "remote":
		route, err := s.BuildAdapterRoute(ctx, req.RepoID, RepositoryScopeWiki)
		if err != nil {
			return cache.SourceGraph{}, SyncCounts{}, err
		}
		page, err := s.client.GetWikiPage(ctx, gitcode.WikiPageRequest{Owner: route.Owner, Repo: route.Name, Slug: remoteID})
		if err != nil {
			return cache.SourceGraph{}, SyncCounts{}, err
		}
		return s.stageWiki(ctx, req, remoteType, remoteID, page)
	default:
		return cache.SourceGraph{}, SyncCounts{}, ErrInvalidQuery{Field: "remote_type", Message: "unsupported remote type " + remoteType}
	}
}

func (s *Service) syncGraphFromSourceGraph(repoID string, graph cache.SourceGraph) cache.SyncGraph {
	revision := ""
	if graph.SyncStatus != nil {
		revision = graph.SyncStatus.RemoteRevision
	}
	record := cache.Record{RepoID: repoID, ID: graph.Source.ID, Type: graph.Source.Kind, Path: graph.Source.Path, Title: graph.Source.Title, Body: graph.Source.Body, Status: graph.Source.Status, Labels: graph.Source.Labels, ContentHash: graph.Source.ContentHash, CreatedAt: graph.Source.CreatedAt, UpdatedAt: graph.Source.UpdatedAt, RemoteRevision: revision}
	if graph.SyncStatus != nil {
		record.RemoteType = graph.SyncStatus.RemoteType
		record.RemoteID = graph.SyncStatus.RemoteID
	}
	revisions := []cache.RemoteRevision{}
	if graph.SyncStatus != nil {
		revisions = append(revisions, cache.RemoteRevision{RepoID: repoID, RecordID: graph.Source.ID, RemoteType: graph.SyncStatus.RemoteType, RemoteID: graph.SyncStatus.RemoteID, RemoteRevision: graph.SyncStatus.RemoteRevision, Status: graph.SyncStatus.Status, LastFetchedAt: graph.SyncStatus.LastFetchedAt})
	}
	return cache.SyncGraph{RepoID: repoID, Provenance: s.syncOriginProvenance(), Record: record, Comments: graph.Comments, PRReviewComments: graph.PRReviewComments, PRReviewDiscussions: graph.PRReviewDiscussions, PRReviewPositions: graph.PRReviewPositions, Identities: graph.Identities, Links: graph.Links, Chunks: graph.Chunks, ReplaceChunks: graph.ReplaceChunks, RemoteRevisions: revisions, SyncEvents: graph.SyncEvents}
}

func (s *Service) stageIssue(ctx context.Context, req SyncRequest, remoteType, remoteID string, issue gitcode.Issue) (cache.SourceGraph, SyncCounts, error) {
	graph, counts, err := s.stageIssueParent(ctx, req, remoteType, remoteID, issue)
	if err != nil {
		return graph, counts, err
	}
	if counts.SkippedByRevision > 0 {
		queued, ok, queueErr := s.store.GetIssueCommentSync(ctx, req.RepoID, graph.Source.ID)
		if queueErr != nil {
			return cache.SourceGraph{}, SyncCounts{}, queueErr
		}
		if !ok || queued.Status == "complete" {
			return graph, counts, nil
		}
	}
	route, err := s.BuildAdapterRoute(ctx, req.RepoID, RepositoryScopeIssues)
	if err != nil {
		return cache.SourceGraph{}, SyncCounts{}, err
	}
	counts.FetchedDetail = 1
	comments, err := s.client.ListIssueComments(ctx, gitcode.IssueRequest{Owner: route.Owner, Repo: route.Name, Number: issue.Number, KnownRemoteAlias: true, RemoteAlias: remoteID})
	if err != nil {
		if !isDeferredIssueCommentsRead(err) {
			return cache.SourceGraph{}, SyncCounts{}, err
		}
		counts.Deferred = 1
		comments = gitcode.Page[gitcode.Comment]{}
	}
	if err := appendIssueCommentsToGraph(s, &graph, comments.Items, remoteID, issue.ID); err != nil {
		return cache.SourceGraph{}, SyncCounts{}, err
	}
	return graph, counts, nil
}

func (s *Service) recordTargetedIssueCommentCoverage(ctx context.Context, remoteType string, graph cache.SourceGraph, counts SyncCounts) error {
	if remoteType != "issue" || graph.SyncStatus == nil || counts.FetchedDetail == 0 {
		return nil
	}
	now := s.now().UTC()
	item, ok, err := s.store.GetIssueCommentSync(ctx, graph.Source.RepoID, graph.Source.ID)
	if err != nil {
		return err
	}
	if !ok {
		number, err := strconv.Atoi(graph.SyncStatus.RemoteID)
		if err != nil {
			return err
		}
		item = cache.IssueCommentSync{RepoID: graph.Source.RepoID, SourceID: graph.Source.ID, IssueNumber: number, RemoteID: graph.SyncStatus.RemoteID, ExpectedCount: len(graph.Comments)}
		for _, identity := range graph.Identities {
			if identity.AliasType == "gitcode_issue_id" {
				item.ProviderID = identity.Alias
				break
			}
		}
	}
	item.RemoteRevision = graph.SyncStatus.RemoteRevision
	item.Attempts++
	item.LastAttemptAt = now
	item.UpdatedAt = now
	if counts.Deferred > 0 {
		item.Status = "deferred"
		item.LastErrorClass = "comments_read"
	} else {
		if err := s.projectIssueComments(ctx, item, graph.Comments); err != nil {
			return err
		}
		item.Status = "complete"
		item.ExpectedCount = len(graph.Comments)
		item.LastErrorClass = ""
		item.RetryAfter = ""
	}
	return s.store.UpsertIssueCommentSync(ctx, item)
}

func (s *Service) stageIssueParent(ctx context.Context, req SyncRequest, remoteType, remoteID string, issue gitcode.Issue) (cache.SourceGraph, SyncCounts, error) {
	body := issue.Body
	if req.MaxSize > 0 && int64(len(body)+len(issue.Title)) > req.MaxSize {
		return cache.SourceGraph{}, SyncCounts{}, gitcode.ErrPayloadTooLarge{Endpoint: remoteID, Limit: req.MaxSize, Size: int64(len(body) + len(issue.Title))}
	}
	providerID := strings.TrimSpace(issue.ID)
	if s.syncProviderMode() == gitcode.ProviderModeLive && providerID == "" {
		return cache.SourceGraph{}, SyncCounts{}, s.liveGraphError("issue missing provider id")
	}
	stableID := req.StableID
	if stableID == "" {
		stableID = s.resolveOrFallback(ctx, req.RepoID, remoteType, remoteID, liveFallbackSourceID(s.syncProviderMode(), remoteType, remoteID, providerID))
	}
	if err := s.guardRemoteAlias(ctx, req.RepoID, remoteType, remoteID, stableID); err != nil {
		return cache.SourceGraph{}, SyncCounts{}, err
	}
	now := s.now().UTC()
	updated := issue.UpdatedAt.UTC()
	if updated.IsZero() {
		updated = now
	}
	created := issue.CreatedAt.UTC()
	if created.IsZero() {
		created = updated
	}
	hash := contentHash(issue.Title, body, issue.State, issue.Labels)
	revision := issueRemoteRevision(issue, hash)
	existing, err := s.store.GetSourceScoped(ctx, req.RepoID, stableID)
	counts := SyncCounts{Fetched: 1}
	if err == nil && existing.ContentHash == hash {
		counts.Skipped = 1
	} else if err == nil {
		counts.Updated = 1
	} else if isCacheNotFound(err) {
		counts.Inserted = 1
	} else {
		return cache.SourceGraph{}, SyncCounts{}, err
	}
	status := issue.State
	if status == "" {
		status = issue.Status
	}
	if status == "" {
		status = "open"
	}
	graph := cache.SourceGraph{Source: cache.Source{RepoID: req.RepoID, ID: stableID, Kind: "issue", Path: "issues/" + remoteID + ".md", Title: issue.Title, Body: body, Status: status, Labels: issue.Labels, ContentHash: hash, CreatedAt: created, UpdatedAt: updated}, Identities: []cache.Identity{{RepoID: req.RepoID, SourceID: stableID, AliasType: remoteType, Alias: remoteID, Remote: cache.RemoteAlias{Type: remoteType, ID: remoteID}}}, SyncStatus: &cache.SyncStatus{RepoID: req.RepoID, SourceID: stableID, RemoteType: remoteType, RemoteID: remoteID, RemoteRevision: revision, Status: "fresh", LastFetchedAt: now}}
	if providerID != "" && providerID != remoteID {
		graph.Identities = append(graph.Identities, cache.Identity{RepoID: req.RepoID, SourceID: stableID, AliasType: "gitcode_issue_id", Alias: providerID, Remote: cache.RemoteAlias{Type: "gitcode_issue_id", ID: providerID}})
	}
	if err == nil {
		status, statusErr := s.store.GetSyncStatusScoped(ctx, req.RepoID, stableID)
		if statusErr != nil && !isCacheNotFound(statusErr) {
			return cache.SourceGraph{}, SyncCounts{}, statusErr
		}
		if statusErr == nil && status.RemoteType == remoteType && status.RemoteID == remoteID && status.RemoteRevision == revision && status.Status == "fresh" {
			counts.SkippedByRevision = 1
			return graph, counts, nil
		}
	}
	graph.Chunks = chunksForSource(graph.Source)
	graph.ReplaceChunks = true
	return graph, counts, nil
}

func appendIssueCommentsToGraph(s *Service, graph *cache.SourceGraph, comments []gitcode.Comment, remoteID, providerID string) error {
	if graph == nil {
		return errors.New("issue comment graph is nil")
	}
	now := s.now().UTC()
	for _, comment := range comments {
		commentID := strings.TrimSpace(comment.ID)
		if s.syncProviderMode() == gitcode.ProviderModeLive {
			if commentID == "" {
				return s.liveGraphError("comment missing provider id")
			}
			if !s.liveCommentParentReconciles(comment, remoteID, providerID) {
				return s.liveGraphError("comment parent issue id is unreconciled")
			}
		}
		if commentID == "" {
			commentID = contentHash(remoteID, comment.Author, comment.Body, comment.CreatedAt)
		}
		commentUpdated := comment.UpdatedAt.UTC()
		if commentUpdated.IsZero() {
			commentUpdated = now
		}
		commentCreated := comment.CreatedAt.UTC()
		if commentCreated.IsZero() {
			commentCreated = commentUpdated
		}
		graph.Comments = append(graph.Comments, cache.RecordComment{RepoID: graph.Source.RepoID, RecordID: graph.Source.ID, CommentID: commentID, Author: comment.Author, Body: comment.Body, ContentHash: contentHash(commentID, comment.Author, comment.Body), RemoteRevision: contentHash(commentUpdated), CreatedAt: commentCreated, UpdatedAt: commentUpdated})
	}
	return nil
}

func isDeferredIssueCommentsRead(err error) bool {
	var capability gitcode.ErrUnsupportedCapability
	if errors.As(err, &capability) && capability.CapabilityKey == "comments_read" {
		return true
	}
	var rate gitcode.ErrRateLimited
	return errors.As(err, &rate)
}

func deferredIssueCommentStopReason(err error) string {
	var capability gitcode.ErrUnsupportedCapability
	if errors.As(err, &capability) && capability.CapabilityKey == "comments_read" {
		return "comments_read_unsupported"
	}
	return "rate_limited"
}

func (s *Service) stagePullRequest(ctx context.Context, req SyncRequest, remoteType, remoteID string, pr gitcode.PullRequest) (cache.SourceGraph, SyncCounts, error) {
	body := pr.Body
	if req.MaxSize > 0 && int64(len(body)+len(pr.Title)) > req.MaxSize {
		return cache.SourceGraph{}, SyncCounts{}, gitcode.ErrPayloadTooLarge{Endpoint: remoteID, Limit: req.MaxSize, Size: int64(len(body) + len(pr.Title))}
	}
	if pr.Number <= 0 {
		return cache.SourceGraph{}, SyncCounts{}, s.liveGraphError("pull request number is required")
	}
	stableID := req.StableID
	if stableID == "" {
		stableID = s.resolveOrFallback(ctx, req.RepoID, remoteType, remoteID, pullRequestStableID(pr.Number))
	}
	if err := s.guardRemoteAlias(ctx, req.RepoID, remoteType, remoteID, stableID); err != nil {
		return cache.SourceGraph{}, SyncCounts{}, err
	}
	now := s.now().UTC()
	updated := pr.UpdatedAt.UTC()
	if updated.IsZero() {
		updated = now
	}
	created := pr.CreatedAt.UTC()
	if created.IsZero() {
		created = updated
	}
	status := strings.TrimSpace(pr.State)
	if status == "" {
		status = "open"
	}
	hash := contentHash(pr.ID, pr.Number, pr.Title, body, status, pr.Labels, pr.Base, pr.Head, pr.HTMLURL)
	existing, err := s.store.GetSourceScoped(ctx, req.RepoID, stableID)
	counts := SyncCounts{Fetched: 1}
	if err == nil && existing.ContentHash == hash {
		counts.Skipped = 1
	} else if err == nil {
		counts.Updated = 1
	} else if isCacheNotFound(err) {
		counts.Inserted = 1
	} else {
		return cache.SourceGraph{}, SyncCounts{}, err
	}
	revision := pullRequestRemoteRevision(pr, hash)
	graph := cache.SourceGraph{Source: cache.Source{RepoID: req.RepoID, ID: stableID, Kind: "pull_request", Path: "pulls/" + remoteID + ".md", Title: pr.Title, Body: body, Status: status, Labels: pr.Labels, ContentHash: hash, CreatedAt: created, UpdatedAt: updated}, Identities: []cache.Identity{{RepoID: req.RepoID, SourceID: stableID, AliasType: remoteType, Alias: remoteID, Remote: cache.RemoteAlias{Type: remoteType, ID: remoteID}}}, SyncStatus: &cache.SyncStatus{RepoID: req.RepoID, SourceID: stableID, RemoteType: remoteType, RemoteID: remoteID, RemoteRevision: revision, Status: "fresh", LastFetchedAt: now}}
	if pr.ID != "" && pr.ID != remoteID {
		graph.Identities = append(graph.Identities, cache.Identity{RepoID: req.RepoID, SourceID: stableID, AliasType: "gitcode_pr_id", Alias: pr.ID, Remote: cache.RemoteAlias{Type: "gitcode_pr_id", ID: pr.ID}})
	}
	graph.Chunks = chunksForSource(graph.Source)
	graph.ReplaceChunks = true
	return graph, counts, nil
}

func (s *Service) stagePRComment(ctx context.Context, req SyncRequest, remoteType, remoteID string, prNumber int, comment gitcode.PRComment) (cache.SourceGraph, SyncCounts, error) {
	body := comment.Body
	title := "PR " + strconv.Itoa(prNumber) + " comment"
	if comment.ID != "" {
		title += " " + comment.ID
	}
	if req.MaxSize > 0 && int64(len(body)+len(title)) > req.MaxSize {
		return cache.SourceGraph{}, SyncCounts{}, gitcode.ErrPayloadTooLarge{Endpoint: remoteID, Limit: req.MaxSize, Size: int64(len(body) + len(title))}
	}
	commentID := strings.TrimSpace(comment.ID)
	if commentID == "" {
		commentID = contentHash(prNumber, comment.Author, comment.Body, comment.CreatedAt)
		remoteID = prCommentRemoteID(prNumber, commentID)
	}
	stableID := req.StableID
	if stableID == "" {
		stableID = s.resolveOrFallback(ctx, req.RepoID, remoteType, remoteID, prCommentStableID(prNumber, commentID))
	}
	if err := s.guardRemoteAlias(ctx, req.RepoID, remoteType, remoteID, stableID); err != nil {
		return cache.SourceGraph{}, SyncCounts{}, err
	}
	now := s.now().UTC()
	updated := comment.UpdatedAt.UTC()
	if updated.IsZero() {
		updated = now
	}
	created := comment.CreatedAt.UTC()
	if created.IsZero() {
		created = updated
	}
	hash := contentHash(prNumber, commentID, comment.DiscussionID, comment.ReviewKind, comment.Path, comment.Line, comment.StartLine, comment.EndLine, comment.Position, comment.OriginalPosition, comment.Positions, nullableBoolHash(comment.Resolved), nullableBoolHash(comment.Resolvable), comment.ParentID, comment.Author, body, updated)
	revision := prCommentRemoteRevision(comment, hash)
	existing, err := s.store.GetSourceScoped(ctx, req.RepoID, stableID)
	counts := SyncCounts{Fetched: 1}
	if err == nil && existing.ContentHash == hash {
		counts.Skipped = 1
	} else if err == nil {
		counts.Updated = 1
	} else if isCacheNotFound(err) {
		counts.Inserted = 1
	} else {
		return cache.SourceGraph{}, SyncCounts{}, err
	}
	parentID := firstNonEmptyString(req.ParentSourceID, pullRequestStableID(prNumber))
	graph := cache.SourceGraph{Source: cache.Source{RepoID: req.RepoID, ID: stableID, Kind: "pr_comment", Path: fmt.Sprintf("pulls/%d/comments/%s.md", prNumber, safeIDPart(commentID)), Title: title, Body: body, Status: "current", ContentHash: hash, CreatedAt: created, UpdatedAt: updated}, Identities: []cache.Identity{{RepoID: req.RepoID, SourceID: stableID, AliasType: remoteType, Alias: remoteID, Remote: cache.RemoteAlias{Type: remoteType, ID: remoteID}}}, Links: []cache.Link{{RepoID: req.RepoID, SourceID: stableID, TargetID: parentID, Kind: "parent", Text: "pull_request"}}, SyncStatus: &cache.SyncStatus{RepoID: req.RepoID, SourceID: stableID, RemoteType: remoteType, RemoteID: remoteID, RemoteRevision: revision, Status: "fresh", LastFetchedAt: now}}
	graph.PRReviewComments = []cache.PRReviewComment{{
		RepoID:           req.RepoID,
		SourceID:         stableID,
		PRNumber:         prNumber,
		CommentID:        commentID,
		DiscussionID:     comment.DiscussionID,
		ReviewKind:       firstNonEmptyString(comment.ReviewKind, "general"),
		Author:           comment.Author,
		Path:             comment.Path,
		Line:             comment.Line,
		StartLine:        comment.StartLine,
		EndLine:          comment.EndLine,
		Position:         comment.Position,
		OriginalPosition: comment.OriginalPosition,
		Resolved:         comment.Resolved,
		Resolvable:       comment.Resolvable,
		ParentID:         comment.ParentID,
		CreatedAt:        created,
		UpdatedAt:        updated,
	}}
	if comment.DiscussionID != "" {
		graph.PRReviewDiscussions = []cache.PRReviewDiscussion{{
			RepoID:         req.RepoID,
			PRNumber:       prNumber,
			DiscussionID:   comment.DiscussionID,
			Kind:           firstNonEmptyString(comment.ReviewKind, "general"),
			Resolved:       comment.Resolved,
			Resolvable:     comment.Resolvable,
			FirstCommentID: commentID,
			CreatedAt:      created,
			UpdatedAt:      updated,
		}}
	}
	for _, position := range comment.Positions {
		graph.PRReviewPositions = append(graph.PRReviewPositions, cache.PRReviewPosition{
			RepoID:        req.RepoID,
			PRNumber:      prNumber,
			CommentID:     commentID,
			PositionKind:  firstNonEmptyString(position.PositionKind, "current"),
			DiscussionID:  comment.DiscussionID,
			PositionType:  position.PositionType,
			BaseSHA:       position.BaseSHA,
			StartSHA:      position.StartSHA,
			HeadSHA:       position.HeadSHA,
			OldPath:       position.OldPath,
			NewPath:       position.NewPath,
			OldLine:       position.OldLine,
			NewLine:       position.NewLine,
			StartOldLine:  position.StartOldLine,
			StartNewLine:  position.StartNewLine,
			LineCode:      position.LineCode,
			StartLineCode: position.StartLineCode,
			PatchsetIID:   position.PatchsetIID,
			DiffID:        position.DiffID,
			VersionSHA:    position.VersionSHA,
			Side:          position.Side,
			IsOutdated:    position.IsOutdated,
			CreatedAt:     created,
			UpdatedAt:     updated,
		})
	}
	if comment.DiscussionID != "" {
		graph.Identities = append(graph.Identities, cache.Identity{RepoID: req.RepoID, SourceID: stableID, AliasType: "gitcode_pr_discussion", Alias: comment.DiscussionID, Remote: cache.RemoteAlias{Type: "gitcode_pr_discussion", ID: comment.DiscussionID}})
	}
	graph.Chunks = chunksForSource(graph.Source)
	graph.ReplaceChunks = true
	return graph, counts, nil
}

func (s *Service) stageWiki(ctx context.Context, req SyncRequest, remoteType, remoteID string, page gitcode.WikiPage) (cache.SourceGraph, SyncCounts, error) {
	body := page.Body
	if req.MaxSize > 0 && int64(len(body)+len(page.Title)) > req.MaxSize {
		return cache.SourceGraph{}, SyncCounts{}, gitcode.ErrPayloadTooLarge{Endpoint: remoteID, Limit: req.MaxSize, Size: int64(len(body) + len(page.Title))}
	}
	providerID := strings.TrimSpace(page.ID)
	if s.syncProviderMode() == gitcode.ProviderModeLive && providerID == "" {
		return cache.SourceGraph{}, SyncCounts{}, s.liveGraphError("wiki missing provider id")
	}
	stableID := req.StableID
	if stableID == "" {
		stableID = s.resolveOrFallback(ctx, req.RepoID, remoteType, remoteID, liveFallbackSourceID(s.syncProviderMode(), remoteType, remoteID, providerID))
	}
	if err := s.guardRemoteAlias(ctx, req.RepoID, remoteType, remoteID, stableID); err != nil {
		return cache.SourceGraph{}, SyncCounts{}, err
	}
	now := s.now().UTC()
	updated := page.UpdatedAt.UTC()
	if updated.IsZero() {
		updated = now
	}
	created := page.CreatedAt.UTC()
	if created.IsZero() {
		created = updated
	}
	revision := page.Revision
	if revision == "" {
		revision = contentHash(page.Title, body)
	}
	hash := contentHash(page.Title, body, revision)
	existing, err := s.store.GetSourceScoped(ctx, req.RepoID, stableID)
	counts := SyncCounts{Fetched: 1}
	if err == nil && existing.ContentHash == hash {
		counts.Skipped = 1
	} else if err == nil {
		counts.Updated = 1
	} else if isCacheNotFound(err) {
		counts.Inserted = 1
	} else {
		return cache.SourceGraph{}, SyncCounts{}, err
	}
	graph := cache.SourceGraph{Source: cache.Source{RepoID: req.RepoID, ID: stableID, Kind: "wiki", Path: normalizeWikiCachePath(remoteID), Title: page.Title, Body: body, Status: "fresh", ContentHash: hash, CreatedAt: created, UpdatedAt: updated}, Identities: []cache.Identity{{RepoID: req.RepoID, SourceID: stableID, AliasType: remoteType, Alias: remoteID, Remote: cache.RemoteAlias{Type: remoteType, ID: remoteID}}}, SyncStatus: &cache.SyncStatus{RepoID: req.RepoID, SourceID: stableID, RemoteType: remoteType, RemoteID: remoteID, RemoteRevision: revision, Status: "fresh", LastFetchedAt: now}}
	if providerID != "" && providerID != remoteID {
		graph.Identities = append(graph.Identities, cache.Identity{RepoID: req.RepoID, SourceID: stableID, AliasType: "gitcode_wiki_id", Alias: providerID, Remote: cache.RemoteAlias{Type: "gitcode_wiki_id", ID: providerID}})
	}
	graph.Chunks = chunksForSource(graph.Source)
	graph.ReplaceChunks = true
	return graph, counts, nil
}

func normalizeWikiCachePath(remoteID string) string {
	remoteID = strings.TrimSpace(remoteID)
	remoteID = strings.TrimPrefix(remoteID, "/")
	remoteID = strings.TrimPrefix(remoteID, "wiki/")
	if remoteID == "" || remoteID == "." {
		remoteID = "Home"
	}
	base := path.Base(remoteID)
	ext := strings.ToLower(path.Ext(base))
	switch ext {
	case ".md", ".markdown", ".mdown", ".mkd":
		dir := path.Dir(remoteID)
		withoutExt := strings.TrimSuffix(base, ext)
		if dir != "." {
			return "wiki/" + dir + "/" + withoutExt + ".md"
		}
		return "wiki/" + withoutExt + ".md"
	default:
		return "wiki/" + remoteID + ".md"
	}
}

func (s *Service) BuildAdapterRoute(ctx context.Context, repoID string, requestedScope RepositoryScope) (RepositoryRoute, error) {
	repoID, err := s.requireRepo(ctx, repoID, "route")
	if err != nil {
		return RepositoryRoute{}, err
	}
	repo, err := s.repositoryWithScope(ctx, repoID, requestedScope)
	if err != nil {
		return RepositoryRoute{}, err
	}
	route := RepositoryRoute{RepoID: repo.RepoID, Owner: repo.Owner, Name: repo.Name, APIBaseURL: repo.APIBaseURL}
	for _, configured := range repo.Scopes {
		route.Scopes = append(route.Scopes, RepositoryScope(configured))
	}
	return route, nil
}

func (s *Service) ResolveLiveRepositoryBinding(ctx context.Context, req LiveRepositoryBindingRequest) (LiveRepositoryBinding, error) {
	repoID, err := s.requireRepo(ctx, req.RepoID, "live repository binding")
	if err != nil {
		return LiveRepositoryBinding{}, err
	}
	repo, err := s.repositoryWithScope(ctx, repoID, req.RequestedScope)
	if err != nil {
		return LiveRepositoryBinding{}, err
	}
	selected := strings.TrimSpace(repo.APIBaseURL)
	if selected == "" {
		return LiveRepositoryBinding{}, ErrInvalidQuery{Field: "api_base_url", Message: "live repository binding requires api_base_url"}
	}
	baseURL, err := normalizeLiveAPIBaseURL(selected)
	if err != nil {
		return LiveRepositoryBinding{}, err
	}
	binding := LiveRepositoryBinding{RepoID: repo.RepoID, Owner: repo.Owner, Name: repo.Name, APIBaseURL: baseURL, CachePath: strings.TrimSpace(req.CachePath), AuditPath: strings.TrimSpace(req.AuditPath), BaseURLSource: "repository_binding"}
	for _, configured := range repo.Scopes {
		binding.Scopes = append(binding.Scopes, RepositoryScope(configured))
	}
	return binding, nil
}

func (s *Service) repositoryWithScope(ctx context.Context, repoID string, requestedScope RepositoryScope) (cache.RepositoryBinding, error) {
	repo, err := s.store.GetRepository(ctx, repoID)
	if err != nil {
		return cache.RepositoryBinding{}, normalizeError(err, "repository", repoID)
	}
	for _, scope := range repo.Scopes {
		if RepositoryScope(scope) == requestedScope {
			return repo, nil
		}
	}
	return cache.RepositoryBinding{}, ErrInvalidQuery{Field: "scope", Message: string(requestedScope) + " scope is not enabled for repo " + repoID}
}

func normalizeLiveAPIBaseURL(raw string) (string, error) {
	parsed, err := url.Parse(strings.TrimSpace(raw))
	if err != nil || parsed.Scheme == "" || parsed.Host == "" {
		return "", ErrInvalidQuery{Field: "api_base_url", Message: "valid absolute http(s) api_base_url is required for live mode"}
	}
	if parsed.Scheme != "http" && parsed.Scheme != "https" {
		return "", ErrInvalidQuery{Field: "api_base_url", Message: "api_base_url must use http or https for live mode"}
	}
	if parsed.User != nil {
		return "", ErrInvalidQuery{Field: "api_base_url", Message: "api_base_url must not contain credentials"}
	}
	return sanitizeAPIBaseURL(parsed.String()), nil
}

func (s *Service) validateRepoScope(ctx context.Context, repoID, remoteType string) error {
	want := RepositoryScopeIssues
	if remoteType == "wiki" || remoteType == "page" || remoteType == "remote" {
		want = RepositoryScopeWiki
	}
	_, err := s.BuildAdapterRoute(ctx, repoID, want)
	return err
}

func (s *Service) syncProviderMode() gitcode.ProviderMode {
	return s.ProviderMode()
}

func (s *Service) syncOriginProvenance() cache.Provenance {
	if s.syncProviderMode() == gitcode.ProviderModeLive {
		return cache.ProvenanceLive
	}
	return cache.ProvenanceFixture
}

func (s *Service) guardRemoteAlias(ctx context.Context, repoID, remoteType, remoteID, stableID string) error {
	identity, err := s.store.ResolveAliasScoped(ctx, repoID, cache.RemoteAlias{Type: remoteType, ID: remoteID})
	if err == nil && identity.SourceID != "" && identity.SourceID != stableID {
		return gitcode.ErrRemoteCollision{Alias: remoteType + ":" + remoteID, ExistingID: identity.SourceID, NewID: stableID}
	}
	if err != nil && !isCacheNotFound(err) {
		return err
	}
	return nil
}

func (s *Service) resolveOrFallback(ctx context.Context, repoID, remoteType, remoteID, fallback string) string {
	identity, err := s.store.ResolveAliasScoped(ctx, repoID, cache.RemoteAlias{Type: remoteType, ID: remoteID})
	if err == nil && identity.SourceID != "" {
		return identity.SourceID
	}
	return fallback
}

func liveFallbackSourceID(mode gitcode.ProviderMode, remoteType, remoteID, providerID string) string {
	providerID = strings.TrimSpace(providerID)
	if mode == gitcode.ProviderModeLive && providerID != "" {
		if remoteType != "issue" && remoteType != "issues" && remoteType != "pull_request" {
			return fallbackSourceID(remoteType, providerID)
		}
		if _, err := strconv.ParseInt(providerID, 10, 64); err != nil {
			return fallbackSourceID(remoteType, providerID)
		}
	}
	return fallbackSourceID(remoteType, remoteID)
}

func fallbackSourceID(remoteType, remoteID string) string {
	clean := strings.NewReplacer("/", "-", " ", "-", ":", "-").Replace(strings.ToUpper(remoteID))
	switch remoteType {
	case "issue", "issues":
		return "ISSUE-" + clean
	case "pull_request", "pull", "pulls", "pr":
		return "PR-" + clean
	case "pr_comment":
		return "PRCOMMENT-" + clean
	case "milestone", "milestones":
		return "MILESTONE-" + clean
	case "push_mirror", "push_mirrors", "pushmirror", "push_remote_mirror":
		return "PUSHMIRROR-" + clean
	case "wiki", "page", "remote":
		return "WIKI-" + clean
	default:
		return "REMOTE-" + clean
	}
}

func pullRequestStableID(number int) string {
	return "PR-" + strconv.Itoa(number)
}

func prCommentStableID(prNumber int, commentID string) string {
	return "PRCOMMENT-" + strconv.Itoa(prNumber) + "-" + safeIDPart(commentID)
}

func prCommentRemoteID(prNumber int, commentID string) string {
	return strconv.Itoa(prNumber) + ":" + strings.TrimSpace(commentID)
}

func issueCommentStableID(issueNumber int, commentID string) string {
	return "ISSUECOMMENT-" + strconv.Itoa(issueNumber) + "-" + safeIDPart(commentID)
}

func issueCommentRemoteID(issueNumber int, commentID string) string {
	return strconv.Itoa(issueNumber) + ":" + strings.TrimSpace(commentID)
}

func safeIDPart(value string) string {
	clean := strings.TrimSpace(value)
	if clean == "" {
		return "unknown"
	}
	return strings.NewReplacer("/", "-", " ", "-", ":", "-", "\\", "-", "#", "-", "?", "-").Replace(clean)
}

func pullRequestNumberFromSource(source cache.Source) (int, bool) {
	for _, alias := range source.Aliases {
		if alias.Remote.Type == "pull_request" || alias.AliasType == "pull_request" {
			id := strings.TrimSpace(alias.Remote.ID)
			if id == "" {
				id = strings.TrimSpace(alias.Alias)
			}
			if n, err := strconv.Atoi(id); err == nil && n > 0 {
				return n, true
			}
		}
	}
	id := strings.TrimPrefix(source.ID, "PR-")
	if n, err := strconv.Atoi(id); err == nil && n > 0 {
		return n, true
	}
	return 0, false
}

func (s *Service) validateLiveSourceGraph(graph cache.SourceGraph) error {
	if s.syncProviderMode() != gitcode.ProviderModeLive {
		return nil
	}
	if gitcode.IsFixtureBoundary(s.client) {
		return s.liveGraphError("fixture provider is forbidden in live graph")
	}
	// Stable IDs and aliases are not provenance: ISSUE-42 is also the normal
	// identity of a real repository-local issue 42. Reject explicit fixture
	// origin, never a coincidentally matching source/parent/alias string.
	if graph.Source.Provenance == cache.ProvenanceFixture {
		return s.liveGraphError("fixture provenance is forbidden in live graph")
	}
	if graph.Source.ID == "" {
		return s.liveGraphError("source id is required")
	}
	if graph.SyncStatus == nil || strings.TrimSpace(graph.SyncStatus.RemoteID) == "" {
		return s.liveGraphError("remote id is required")
	}
	for _, comment := range graph.Comments {
		if strings.TrimSpace(comment.CommentID) == "" {
			return s.liveGraphError("comment provider id is required")
		}
		if comment.RecordID != graph.Source.ID {
			return s.liveGraphError("comment parent record is unreconciled")
		}
	}
	return nil
}

func (s *Service) liveCommentParentReconciles(comment gitcode.Comment, remoteID, providerID string) bool {
	parent := strings.TrimSpace(comment.IssueID)
	return parent == "" || parent == strings.TrimSpace(remoteID) || parent == strings.TrimSpace(providerID)
}

func (s *Service) liveGraphError(message string) error {
	return ErrSyncFailure{Mode: "live_graph_invalid", Cause: ErrInvalidQuery{Field: "live_graph", Message: message}}
}

func contentHash(parts ...any) string {
	b, _ := json.Marshal(parts)
	sum := sha256.Sum256(b)
	return hex.EncodeToString(sum[:])
}

func nullableBoolHash(value *bool) string {
	if value == nil {
		return ""
	}
	if *value {
		return "true"
	}
	return "false"
}

func prReviewCommentFromCache(meta cache.PRReviewComment, source cache.Source) PRReviewComment {
	kind := firstNonEmptyString(meta.ReviewKind, "general")
	return PRReviewComment{
		ID:               meta.CommentID,
		SourceID:         meta.SourceID,
		DiscussionID:     meta.DiscussionID,
		Kind:             kind,
		Body:             source.Body,
		Author:           meta.Author,
		Path:             meta.Path,
		Line:             meta.Line,
		StartLine:        meta.StartLine,
		EndLine:          meta.EndLine,
		Position:         meta.Position,
		OriginalPosition: meta.OriginalPosition,
		Resolved:         meta.Resolved,
		Resolvable:       meta.Resolvable,
		ParentID:         meta.ParentID,
		CreatedAt:        firstNonZeroTime(meta.CreatedAt, source.CreatedAt),
		UpdatedAt:        firstNonZeroTime(meta.UpdatedAt, source.UpdatedAt),
	}
}

func prReviewPositionFromCache(position cache.PRReviewPosition) PRReviewPosition {
	return PRReviewPosition{
		Kind:          firstNonEmptyString(position.PositionKind, "current"),
		PositionType:  position.PositionType,
		BaseSHA:       position.BaseSHA,
		StartSHA:      position.StartSHA,
		HeadSHA:       position.HeadSHA,
		OldPath:       position.OldPath,
		NewPath:       position.NewPath,
		OldLine:       position.OldLine,
		NewLine:       position.NewLine,
		StartOldLine:  position.StartOldLine,
		StartNewLine:  position.StartNewLine,
		LineCode:      position.LineCode,
		StartLineCode: position.StartLineCode,
		PatchsetIID:   position.PatchsetIID,
		DiffID:        position.DiffID,
		VersionSHA:    position.VersionSHA,
		Side:          position.Side,
		IsOutdated:    position.IsOutdated,
	}
}

func firstCurrentPosition(positions []PRReviewPosition) *PRReviewPosition {
	if len(positions) == 0 {
		return nil
	}
	for i := range positions {
		if positions[i].Kind == "current" {
			return &positions[i]
		}
	}
	return &positions[0]
}

func discussionKind(comment PRReviewComment) string {
	if comment.Kind == "inline" || comment.Path != "" || comment.Line > 0 || comment.Position > 0 {
		return "inline"
	}
	return "general"
}

func mergeDiscussionResolved(current, next *bool) *bool {
	if next == nil {
		return current
	}
	if !*next {
		return next
	}
	if current == nil {
		return next
	}
	if !*current {
		return current
	}
	return next
}

func issueRemoteRevision(issue gitcode.Issue, contentRevision string) string {
	return contentHash("issue", issue.ID, issue.Number, issue.UpdatedAt.UTC(), issue.Comments, contentRevision)
}

func pullRequestRemoteRevision(pr gitcode.PullRequest, contentRevision string) string {
	return contentHash("pull_request", pr.ID, pr.Number, pr.UpdatedAt.UTC(), pr.Base, pr.Head, contentRevision)
}

func prCommentRemoteRevision(comment gitcode.PRComment, contentRevision string) string {
	return contentHash("pr_comment", comment.ID, comment.DiscussionID, comment.UpdatedAt.UTC(), contentRevision)
}

func syncEventMessage(counts SyncCounts) string {
	b, _ := json.Marshal(counts)
	return string(b)
}

func syncResultFromEvent(event cache.SyncEvent, generated time.Time) SyncResult {
	var counts SyncCounts
	_ = json.Unmarshal([]byte(event.Message), &counts)
	freshness := string(FreshnessFresh)
	if event.Status != "succeeded" {
		freshness = string(FreshnessUnknown)
	}
	return SyncResult{IdempotencyKey: event.IdempotencyKey, Status: event.Status, Counts: counts, SyncEventID: event.ID, Freshness: freshness, GeneratedAt: generated, StartedAt: event.StartedAt, CompletedAt: event.CompletedAt, ZeroDelta: event.ZeroDelta}
}

func failedSyncEvent(event cache.SyncEvent, cause error, at time.Time) cache.SyncEvent {
	event.Status = "failed"
	event.Message = cause.Error()
	event.CreatedAt = at
	event.CompletedAt = at
	return event
}

func (s *Service) normalizeSyncFailure(err error, req SyncRequest, remoteType, remoteID string) error {
	target := syncFailureTarget(req, remoteType, remoteID)
	var network gitcode.ErrNetworkUnavailable
	if errors.As(err, &network) {
		return ErrSyncFailure{Mode: "network_timeout", Target: target, Endpoint: network.Endpoint, RecoveryAction: "retry with --timeout to increase deadline or check connectivity", Cause: err}
	}
	var rate gitcode.ErrRateLimited
	if errors.As(err, &rate) {
		return ErrSyncFailure{Mode: "rate_limited", Target: target, Endpoint: rate.Endpoint, RetryAfter: rate.RetryAfter, RecoveryAction: fmt.Sprintf("wait %s before retrying sync", rate.RetryAfter), Cause: err}
	}
	var partial gitcode.ErrPartialResponse
	if errors.As(err, &partial) {
		return ErrSyncFailure{Mode: "partial_response", Target: target, Endpoint: partial.Endpoint, ExpectedBytes: partial.Expected, GotBytes: partial.Got, ResponseBytes: partial.Got, ContentType: partial.ContentType, DecodeOffset: partial.Offset, Attempts: partial.Attempts, RecoveryAction: "response remained truncated after bounded retries; reduce per_page or inspect provider transport health", Cause: err}
	}
	var malformed gitcode.ErrMalformedJSON
	if errors.As(err, &malformed) {
		return ErrSyncFailure{Mode: "malformed_response", Target: target, Endpoint: malformed.Endpoint, ResponseBytes: malformed.ResponseSize, DecodeOffset: malformed.Offset, Attempts: malformed.Attempts, RecoveryAction: "provider returned malformed JSON after bounded retries; inspect provider status or update the captured adapter contract", Cause: err}
	}
	var contentType gitcode.ErrUnexpectedContentType
	if errors.As(err, &contentType) {
		return ErrSyncFailure{Mode: "unexpected_content_type", Target: target, Endpoint: contentType.Endpoint, ResponseBytes: contentType.ResponseSize, ContentType: contentType.ContentType, Attempts: contentType.Attempts, RecoveryAction: "provider returned a non-JSON success response after bounded retries; inspect provider gateway status", Cause: err}
	}
	var schema *gitcode.ErrSchemaDecode
	if errors.As(err, &schema) {
		return ErrSyncFailure{Mode: "schema_decode", Target: target, Endpoint: schema.Endpoint, RecoveryAction: "changing sync bounds will not recover; update the captured GitCode response fixture and adapter schema", Cause: err}
	}
	var auth gitcode.ErrAuthExpired
	if errors.As(err, &auth) {
		mode := "auth_expired"
		if s.syncProviderMode() == gitcode.ProviderModeLive && (auth.Status == 401 || auth.Status == 403) {
			mode = "live_auth_failure"
		}
		return ErrSyncFailure{Mode: mode, Target: target, Endpoint: auth.Endpoint, RecoveryAction: "renew GITCODE_TOKEN and retry sync", Cause: err}
	}
	var forbidden gitcode.ErrForbidden
	if errors.As(err, &forbidden) {
		if s.syncProviderMode() == gitcode.ProviderModeLive && (forbidden.Status == 401 || forbidden.Status == 403) {
			return ErrSyncFailure{Mode: "live_auth_failure", Target: target, Endpoint: forbidden.Endpoint, RecoveryAction: "renew GITCODE_TOKEN and retry sync", Cause: err}
		}
	}
	var alreadySync ErrSyncFailure
	if errors.As(err, &alreadySync) {
		return err
	}
	var collision gitcode.ErrRemoteCollision
	if errors.As(err, &collision) {
		return ErrSyncFailure{Mode: "remote_collision", Target: target, Endpoint: collision.Endpoint, Alias: collision.Alias, ExistingID: collision.ExistingID, NewID: collision.NewID, RecoveryAction: "run link-check for guidance", Cause: err}
	}
	var corruption cache.ErrCacheCorruption
	if errors.As(err, &corruption) {
		return ErrSyncFailure{Mode: "cache_corruption", Target: target, Endpoint: corruption.Path, RecoveryAction: "recover from backup or re-ingest with gitcode-mcp sync --full", Cause: err}
	}
	var missing gitcode.ErrRemoteNotFound
	if errors.As(err, &missing) {
		alias := missing.Alias
		if alias == "" {
			alias = remoteType + ":" + remoteID
		}
		return ErrSyncFailure{Mode: "remote_not_found", Target: target, Endpoint: missing.Endpoint, Alias: alias, RecoveryAction: "run link-check to find affected references", Cause: err}
	}
	var tooLarge gitcode.ErrPayloadTooLarge
	if errors.As(err, &tooLarge) {
		return ErrSyncFailure{Mode: "payload_too_large", Target: target, Endpoint: tooLarge.Endpoint, LimitBytes: tooLarge.Limit, SizeBytes: tooLarge.Size, PayloadSource: tooLarge.Source, RecoveryAction: "increase --max-size or skip with --skip-large", Cause: err}
	}
	var conflict gitcode.ErrConflict
	if errors.As(err, &conflict) {
		return ErrSyncFailure{Mode: "conflict", Target: target, Endpoint: conflict.Endpoint, LocalPayload: append([]byte(nil), conflict.LocalPayload...), RemotePayload: append([]byte(nil), conflict.RemotePayload...), RecoveryAction: "resolve local and remote payloads manually", Cause: err}
	}
	if errorHasDiagnosticCode(err, "empty_wiki") {
		return ErrSyncFailure{Mode: "empty_wiki", Target: target, RecoveryAction: "run `gitcode-mcp wiki init --repo ...` or create a page via the GitCode UI", Cause: err}
	}
	return err
}

func errorHasDiagnosticCode(err error, want string) bool {
	for err != nil {
		if coded, ok := err.(interface{ DiagnosticCode() string }); ok {
			if coded.DiagnosticCode() == want {
				return true
			}
		}
		if unwrapped := errors.Unwrap(err); unwrapped != nil {
			err = unwrapped
		} else {
			break
		}
	}
	return false
}

func syncFailureTarget(req SyncRequest, remoteType, remoteID string) string {
	if req.StableID != "" {
		return req.StableID
	}
	if req.RemoteAlias != "" {
		return req.RemoteAlias
	}
	if remoteType != "" || remoteID != "" {
		return remoteType + ":" + remoteID
	}
	return req.Source
}

func (s *Service) markMissingRemote(ctx context.Context, event cache.SyncEvent, failure error, remoteType, remoteID string) error {
	var syncFailure ErrSyncFailure
	if !errors.As(failure, &syncFailure) || syncFailure.Mode != "remote_not_found" || event.SourceID == "" {
		return nil
	}
	source, err := s.store.GetSourceScoped(ctx, event.RepoID, event.SourceID)
	if err != nil {
		return failure
	}
	graph := cache.SourceGraph{Source: source, SyncStatus: &cache.SyncStatus{RepoID: event.RepoID, SourceID: event.SourceID, RemoteType: remoteType, RemoteID: remoteID, RemoteRevision: event.RemoteRevision, Status: "not_found", LastFetchedAt: s.now().UTC()}}
	if err := s.store.UpsertSourceGraph(ctx, graph); err != nil {
		return err
	}
	return nil
}

func isRetryableSyncError(err error) bool {
	var network gitcode.ErrNetworkUnavailable
	if errors.As(err, &network) {
		return true
	}
	var rate gitcode.ErrRateLimited
	return errors.As(err, &rate)
}

func retryDelay(err error) time.Duration {
	var rate gitcode.ErrRateLimited
	if errors.As(err, &rate) {
		return rate.RetryAfter
	}
	return 0
}

func freshnessFor(source cache.Source, status cache.SyncStatus) string {
	if status.Status == "missing_remote" || status.Status == "not_found" {
		return string(FreshnessMissingRemote)
	}
	if status.LastFetchedAt.IsZero() || source.UpdatedAt.IsZero() {
		return string(FreshnessUnknown)
	}
	if source.UpdatedAt.After(status.LastFetchedAt) {
		return string(FreshnessStale)
	}
	return string(FreshnessFresh)
}

func (s *Service) executeWrite(ctx context.Context, command string, req WriteCommandRequest, scope RepositoryScope) (WriteCommandResult, error) {
	if err := ctx.Err(); err != nil {
		return WriteCommandResult{}, err
	}
	repoID := firstNonEmptyString(req.RepoID, req.Repo)
	route, err := s.BuildAdapterRoute(ctx, repoID, scope)
	if err != nil {
		return WriteCommandResult{}, err
	}
	req.RepoID = route.RepoID
	req.Repo = route.RepoID
	if err := s.canonicalizeIssueWriteTarget(ctx, command, route.RepoID, &req); err != nil {
		return WriteCommandResult{}, err
	}
	if req.Mode != WriteModeDryRun && req.Mode != WriteModeLive {
		return WriteCommandResult{}, ErrInvalidQuery{Field: "write_mode", Message: "exactly one of dry_run or live is required"}
	}
	key, fingerprint := writeIdempotency(command, req)
	base := WriteCommandResult{Command: command, RepoID: route.RepoID, Status: "dry_run_valid", ID: writeTargetID(req), IdempotencyKey: key, SourceFingerprint: fingerprint, Evidence: "validated write command", GeneratedAt: s.now().UTC()}
	applyWriteIssueIdentity(&base, command, req, req.IssueID, 0)
	applyWriteCommentTarget(&base, command, req, route)
	if req.Mode == WriteModeDryRun {
		return base, nil
	}
	if command == "add-pr-review-comment" {
		lease, err := s.store.AcquireWriter(ctx, cache.WriterRequest{Operation: "write", RepoID: route.RepoID, LockPath: s.lockPath})
		if err != nil {
			if lookup, lookupErr := audit.LookupIdempotency(ctx, s.store, route.RepoID, key, fingerprint); lookupErr == nil && lookup.InProgress {
				return WriteCommandResult{}, ErrWriteFailure{Code: "write_idempotency_in_progress", RepoID: route.RepoID, IdempotencyKey: key}
			}
			return WriteCommandResult{}, err
		}
		defer s.store.ReleaseWriter(context.Background(), lease)
		parentSourceID, err := s.requireCachedPRParent(ctx, route.RepoID, req.Number)
		if err != nil {
			return WriteCommandResult{}, err
		}
		req.parentSourceID = parentSourceID
	}
	if !s.hasWriteCredential() {
		return WriteCommandResult{}, ErrWriteFailure{Code: "write_missing_credential", RepoID: route.RepoID, IdempotencyKey: key}
	}
	if command == "create-repo-label" {
		return s.executeRepositoryLabelWrite(ctx, route, req, base)
	}
	if command == "create-page" || command == "update-page" {
		return s.executeWikiContentWrite(ctx, command, route, req, base)
	}
	if command == "create-milestone" || command == "update-milestone" {
		return s.executeMilestoneWrite(ctx, command, route, req, base)
	}
	lookup, err := audit.LookupIdempotency(ctx, s.store, route.RepoID, key, fingerprint)
	if err != nil {
		return WriteCommandResult{}, err
	}
	var priorEntry *cache.AuditTrailEntry
	if lookup.Entry != nil {
		prior := *lookup.Entry
		priorEntry = &prior
		if lookup.Conflict {
			return WriteCommandResult{}, ErrWriteFailure{Code: "write_idempotency_conflict", RepoID: route.RepoID, RemoteID: prior.RemoteID, IdempotencyKey: key}
		}
		if lookup.Replay {
			result := replayWriteResult(command, req, prior, fingerprint, s.now().UTC())
			applyWriteCommentTarget(&result, command, req, route)
			return result, nil
		}
		if lookup.Partial {
			if command == "merge-pr" {
				if recovered, ok, recoverErr := s.recoverAmbiguousMergePR(ctx, route, req, prior, fingerprint); recoverErr != nil {
					return WriteCommandResult{}, recoverErr
				} else if ok {
					return recovered, nil
				}
				return WriteCommandResult{}, ErrWriteFailure{Code: "write_ambiguous_remote", RepoID: route.RepoID, RemoteID: prior.RemoteID, IdempotencyKey: key}
			}
			graph, err := s.replayWriteGraph(ctx, command, route.RepoID, req, prior)
			if err != nil {
				return WriteCommandResult{}, err
			}
			if isPrimaryCreation(command) {
				completed := audit.WithRequestMetadata(audit.Success(route.RepoID, key, command, graph.Record.ID, graph.Record.RemoteType, prior.RemoteID, fingerprint, "canonical creation cache repair confirmed", s.now().UTC()), prior.RequestMetadata)
				return s.settlePrimaryCreation(ctx, command, req, prior, graph, completed, true)
			}
			if err := s.store.UpsertRecordGraph(ctx, graph); err != nil {
				partial := audit.WithRequestMetadata(audit.RemoteConfirmedCacheRefreshFailed(route.RepoID, key, command, prior.RecordID, prior.RemoteType, prior.RemoteID, fingerprint, err.Error(), s.now().UTC()), prior.RequestMetadata)
				_ = s.store.RecordAuditEvent(ctx, partial)
				return WriteCommandResult{}, ErrWriteFailure{Code: "write_partial_cache_refresh_failed", RepoID: route.RepoID, RemoteID: prior.RemoteID, IdempotencyKey: key, Cause: err}
			}
			if err := s.refreshIssueCommentWriteCache(ctx, command, graph); err != nil {
				return WriteCommandResult{}, ErrWriteFailure{Code: "write_partial_cache_refresh_failed", RepoID: route.RepoID, RemoteID: prior.RemoteID, IdempotencyKey: key, Cause: err}
			}
			if err := s.recordCacheConfirmation(ctx, command, route.RepoID, key, fingerprint, graph, prior.RemoteID, "succeeded", s.now().UTC()); err != nil {
				return WriteCommandResult{}, ErrWriteFailure{Code: "write_partial_cache_refresh_failed", RepoID: route.RepoID, RemoteID: prior.RemoteID, IdempotencyKey: key, Cause: err}
			}
			completed := audit.WithRequestMetadata(audit.Success(route.RepoID, key, command, graph.Record.ID, graph.Record.RemoteType, prior.RemoteID, fingerprint, "cache refresh replay completed", s.now().UTC()), prior.RequestMetadata)
			if err := s.store.RecordAuditEvent(ctx, completed); err != nil {
				return WriteCommandResult{}, ErrWriteFailure{Code: "write_partial_remote_confirmed_audit_failed", RepoID: route.RepoID, RemoteID: prior.RemoteID, IdempotencyKey: key, Cause: err}
			}
			result := replayWriteResult(command, req, completed, fingerprint, s.now().UTC())
			applyWriteCommentTarget(&result, command, req, route)
			result.Status = "succeeded"
			return result, nil
		}
		if prior.Status == audit.StatusRemoteConfirmedAuditFailed {
			return WriteCommandResult{}, ErrWriteFailure{Code: "write_partial_remote_confirmed_audit_failed", RepoID: route.RepoID, RemoteID: prior.RemoteID, IdempotencyKey: key}
		}
		if lookup.InProgress {
			switch command {
			case "update-issue":
				if recovered, ok, recoverErr := s.recoverAmbiguousIssueUpdate(ctx, route, req, prior, fingerprint); recoverErr != nil {
					return WriteCommandResult{}, recoverErr
				} else if ok {
					return recovered, nil
				}
				return WriteCommandResult{}, ErrWriteFailure{Code: "write_ambiguous_remote", RepoID: route.RepoID, RemoteID: prior.RemoteID, IdempotencyKey: key}
			case "update-pr":
				if recovered, ok, recoverErr := s.recoverAmbiguousPRUpdate(ctx, route, req, prior, fingerprint); recoverErr != nil {
					return WriteCommandResult{}, recoverErr
				} else if ok {
					return recovered, nil
				}
				return WriteCommandResult{}, ErrWriteFailure{Code: "write_ambiguous_remote", RepoID: route.RepoID, RemoteID: prior.RemoteID, IdempotencyKey: key}
			case "merge-pr":
				phase := strings.TrimSpace(prior.RequestMetadata["write_phase"])
				if recovered, ok, recoverErr := s.recoverAmbiguousMergePR(ctx, route, req, prior, fingerprint); recoverErr != nil {
					return WriteCommandResult{}, recoverErr
				} else if ok {
					return recovered, nil
				}
				if !strings.HasSuffix(phase, "_ambiguous") && phase != "put_unconfirmed" {
					return WriteCommandResult{}, ErrWriteFailure{Code: "write_idempotency_in_progress", RepoID: route.RepoID, RemoteID: prior.RemoteID, IdempotencyKey: key}
				}
				return WriteCommandResult{}, ErrWriteFailure{Code: "write_ambiguous_remote", RepoID: route.RepoID, RemoteID: prior.RemoteID, IdempotencyKey: key}
			}
			return WriteCommandResult{}, ErrWriteFailure{Code: "write_idempotency_in_progress", RepoID: route.RepoID, RemoteID: prior.RemoteID, IdempotencyKey: key}
		}
		if lookup.Unsafe {
			return WriteCommandResult{}, ErrWriteFailure{Code: firstNonEmptyString(prior.Message, "write_ambiguous_remote"), RepoID: route.RepoID, RemoteID: prior.RemoteID, IdempotencyKey: key}
		}
		if prior.Status == audit.StatusFailed && prior.Message == "write_conflict" {
			return WriteCommandResult{}, ErrWriteFailure{Code: "write_conflict", RepoID: route.RepoID, RemoteID: prior.RemoteID, IdempotencyKey: key}
		}
	}
	var issueUpdateClaimMetadata map[string]string
	var prUpdateClaimMetadata map[string]string
	prUpdateClaimed := false
	var prUpdateClaimCreatedAt time.Time
	var prUpdateAuditStore auditGenerationClaimStore
	var mergePRClaimMetadata map[string]string
	mergePRClaimed := false
	var mergePRClaimCreatedAt time.Time
	var mergePRAuditStore auditGenerationClaimStore
	var topLevelCommentClaimMetadata map[string]string
	var topLevelCommentClaimRecordID string
	var topLevelCommentClaimRemoteType string
	if command == "update-issue" {
		req.beforeIssueUpdateMutation = func(preimage gitcode.Issue) error {
			metadata := issueUpdatePreimageMetadata(preimage)
			metadata["method"] = "PATCH"
			metadata["idempotency_key"] = key
			metadata["remote_number"] = strconv.Itoa(req.Number)
			metadata["remote_type"] = "issue"
			metadata["provider"] = "gitcode-http"
			metadata["provider_mode"] = string(gitcode.ProviderModeLive)
			metadata["source_fingerprint"] = fingerprint
			metadata["write_phase"] = "preimage_captured"
			issueUpdateClaimMetadata = metadata
			if priorEntry != nil {
				expected := strings.TrimSpace(priorEntry.RequestMetadata["issue_preimage_fingerprint"])
				actual := metadata["issue_preimage_fingerprint"]
				if expected != "" && expected != actual {
					return gitcode.ErrWritePreconditionConflict{Endpoint: "issues/" + strconv.Itoa(req.Number), ExpectedFingerprint: expected, ActualFingerprint: actual}
				}
			}
			entry := audit.WithRequestMetadata(audit.InProgress(route.RepoID, key, command, req.IssueID, "issue", strconv.Itoa(req.Number), fingerprint, "issue update claimed before PATCH", s.now().UTC()), metadata)
			if claimer, ok := s.store.(auditClaimStore); ok {
				claimed, claimErr := claimer.ClaimAuditEvent(ctx, entry)
				if claimErr != nil {
					return claimErr
				}
				if !claimed {
					return ErrWriteFailure{Code: "write_idempotency_in_progress", RepoID: route.RepoID, RemoteID: strconv.Itoa(req.Number), IdempotencyKey: key}
				}
				return nil
			}
			return s.store.RecordAuditEvent(ctx, entry)
		}
	}
	if command == "update-pr" {
		claimer, ok := s.store.(auditGenerationClaimStore)
		if !ok {
			return WriteCommandResult{}, ErrWriteFailure{Code: "write_audit_start_failed", RepoID: route.RepoID, RemoteID: strconv.Itoa(req.Number), IdempotencyKey: key, Cause: errors.New("atomic audit generation transition support is unavailable")}
		}
		prUpdateAuditStore = claimer
		req.beforePRUpdateMutation = func(preimage gitcode.PullRequest) error {
			metadata := prUpdatePreimageMetadata(preimage)
			metadata["method"] = "PATCH"
			metadata["idempotency_key"] = key
			metadata["remote_number"] = strconv.Itoa(req.Number)
			metadata["remote_type"] = "pull_request"
			metadata["provider"] = "gitcode-http"
			metadata["provider_mode"] = string(gitcode.ProviderModeLive)
			metadata["source_fingerprint"] = fingerprint
			metadata["write_phase"] = "preimage_captured"
			prUpdateClaimMetadata = metadata
			if priorEntry != nil {
				expected := strings.TrimSpace(priorEntry.RequestMetadata["pr_update_preimage_fingerprint"])
				actual := metadata["pr_update_preimage_fingerprint"]
				if expected != "" && expected != actual {
					return gitcode.ErrWritePreconditionConflict{Endpoint: "pulls/" + strconv.Itoa(req.Number), ExpectedFingerprint: expected, ActualFingerprint: actual}
				}
			}
			entry := audit.WithRequestMetadata(audit.InProgress(route.RepoID, key, command, fallbackSourceID("pull_request", strconv.Itoa(req.Number)), "pull_request", strconv.Itoa(req.Number), fingerprint, "pull request update claimed before PATCH", s.now().UTC()), metadata)
			var expectedFailedAt *time.Time
			if priorEntry != nil && priorEntry.Status == audit.StatusFailed {
				expected := priorEntry.CreatedAt
				expectedFailedAt = &expected
			}
			claimed, claimErr := claimer.ClaimAuditEventGeneration(ctx, entry, expectedFailedAt)
			if claimErr != nil {
				return ErrWriteFailure{Code: "write_audit_start_failed", RepoID: route.RepoID, RemoteID: strconv.Itoa(req.Number), IdempotencyKey: key, Cause: claimErr}
			}
			if !claimed {
				return ErrWriteFailure{Code: "write_idempotency_in_progress", RepoID: route.RepoID, RemoteID: strconv.Itoa(req.Number), IdempotencyKey: key}
			}
			prUpdateClaimed = true
			prUpdateClaimCreatedAt = entry.CreatedAt
			return nil
		}
	}
	if command == "merge-pr" {
		claimer, ok := s.store.(auditGenerationClaimStore)
		if !ok {
			return WriteCommandResult{}, ErrWriteFailure{Code: "write_audit_start_failed", RepoID: route.RepoID, RemoteID: strconv.Itoa(req.Number), IdempotencyKey: key, Cause: errors.New("atomic audit claim support is unavailable")}
		}
		mergePRAuditStore = claimer
		req.beforeMergePRMutation = func(preimage gitcode.PullRequest) error {
			metadata := mergePRPreimageMetadata(preimage)
			metadata["method"] = "PUT"
			metadata["idempotency_key"] = key
			metadata["remote_number"] = strconv.Itoa(req.Number)
			metadata["remote_type"] = "pull_request"
			metadata["provider"] = "gitcode-http"
			metadata["provider_mode"] = string(gitcode.ProviderModeLive)
			metadata["source_fingerprint"] = fingerprint
			metadata["write_phase"] = "preimage_captured"
			mergePRClaimMetadata = metadata
			if priorEntry != nil {
				expected := strings.TrimSpace(priorEntry.RequestMetadata["merge_preimage_head_sha_hash"])
				actual := metadata["merge_preimage_head_sha_hash"]
				if expected != "" && expected != actual {
					return gitcode.ErrWritePreconditionConflict{Endpoint: "pulls/" + strconv.Itoa(req.Number) + "/merge", ExpectedFingerprint: expected, ActualFingerprint: actual}
				}
			}
			entry := audit.WithRequestMetadata(audit.InProgress(route.RepoID, key, command, fallbackSourceID("pull_request", strconv.Itoa(req.Number)), "pull_request", strconv.Itoa(req.Number), fingerprint, "pull request merge claimed before PUT", s.now().UTC()), metadata)
			var expectedFailedAt *time.Time
			if priorEntry != nil && priorEntry.Status == audit.StatusFailed {
				expected := priorEntry.CreatedAt
				expectedFailedAt = &expected
			}
			claimed, claimErr := claimer.ClaimAuditEventGeneration(ctx, entry, expectedFailedAt)
			if claimErr != nil {
				return ErrWriteFailure{Code: "write_audit_start_failed", RepoID: route.RepoID, RemoteID: strconv.Itoa(req.Number), IdempotencyKey: key, Cause: claimErr}
			}
			if !claimed {
				return ErrWriteFailure{Code: "write_idempotency_in_progress", RepoID: route.RepoID, RemoteID: strconv.Itoa(req.Number), IdempotencyKey: key}
			}
			mergePRClaimed = true
			mergePRClaimCreatedAt = entry.CreatedAt
			return nil
		}
	}
	var primaryCreationClaim cache.AuditTrailEntry
	if command == "create-issue" || command == "create-pr" || command == "add-comment" || command == "add-pr-comment" || command == "add-pr-review-comment" {
		remoteType := "issue"
		recordID := fallbackSourceID("issue", fingerprint)
		message := "remote issue creation pending confirmation"
		switch command {
		case "create-pr":
			remoteType = "pull_request"
			recordID = fallbackSourceID("pull_request", fingerprint)
			message = "remote pull request creation pending confirmation"
		case "add-comment":
			remoteType = "issue_comment"
			recordID = fallbackSourceID("issue_comment", fingerprint)
			message = "remote issue comment pending confirmation"
		case "add-pr-comment":
			remoteType = "pr_comment"
			recordID = fallbackSourceID("pr_comment", fingerprint)
			message = "remote pull request comment pending confirmation"
		case "add-pr-review-comment":
			remoteType = "pr_comment"
			recordID = fallbackSourceID("pr_comment", fingerprint)
			message = "remote inline review comment pending confirmation"
		}
		metadata := map[string]string{
			"method":             "POST",
			"idempotency_key":    key,
			"remote_type":        remoteType,
			"provider":           "gitcode-http",
			"provider_mode":      string(gitcode.ProviderModeLive),
			"source_fingerprint": fingerprint,
		}
		if req.Number > 0 {
			metadata["remote_number"] = strconv.Itoa(req.Number)
		}
		if isTopLevelCommentCommand(command) {
			topLevelCommentClaimMetadata = cloneStringMap(metadata)
			topLevelCommentClaimRecordID = recordID
			topLevelCommentClaimRemoteType = remoteType
		}
		entry := audit.WithRequestMetadata(
			audit.InProgress(route.RepoID, key, command, recordID, remoteType, "", fingerprint, message, s.now().UTC()),
			metadata,
		)
		if isPrimaryCreation(command) {
			claimer, claimOK := s.store.(auditGenerationClaimStore)
			_, settleOK := s.store.(wikiWriteSettlementStore)
			if !claimOK || !settleOK {
				return WriteCommandResult{}, ErrWriteFailure{Code: "write_audit_start_failed", RepoID: route.RepoID, IdempotencyKey: key, Cause: errors.New("atomic creation claim and settlement support is unavailable")}
			}
			var expectedFailedAt *time.Time
			if priorEntry != nil && priorEntry.Status == audit.StatusFailed {
				expectedFailedAt = &priorEntry.CreatedAt
			}
			claimed, err := claimer.ClaimAuditEventGeneration(ctx, entry, expectedFailedAt)
			if err != nil {
				return WriteCommandResult{}, ErrWriteFailure{Code: "write_audit_start_failed", RepoID: route.RepoID, IdempotencyKey: key, Cause: err}
			}
			if !claimed {
				return WriteCommandResult{}, ErrWriteFailure{Code: "write_idempotency_in_progress", RepoID: route.RepoID, IdempotencyKey: key}
			}
			primaryCreationClaim = entry
		} else if claimer, ok := s.store.(auditClaimStore); ok {
			claimed, err := claimer.ClaimAuditEvent(ctx, entry)
			if err != nil {
				return WriteCommandResult{}, ErrWriteFailure{Code: "write_audit_start_failed", RepoID: route.RepoID, IdempotencyKey: key, Cause: err}
			}
			if !claimed {
				return WriteCommandResult{}, ErrWriteFailure{Code: "write_idempotency_in_progress", RepoID: route.RepoID, IdempotencyKey: key}
			}
		} else if command == "create-pr" || command == "add-comment" || command == "add-pr-comment" {
			return WriteCommandResult{}, ErrWriteFailure{Code: "write_audit_start_failed", RepoID: route.RepoID, IdempotencyKey: key, Cause: errors.New("atomic audit claim support is unavailable")}
		} else if err := s.store.RecordAuditEvent(ctx, entry); err != nil {
			return WriteCommandResult{}, ErrWriteFailure{Code: "write_audit_start_failed", RepoID: route.RepoID, IdempotencyKey: key, Cause: err}
		}
	}
	if command == "trigger-push-mirror" {
		entry := audit.WithRequestMetadata(
			audit.InProgress(route.RepoID, key, command, fallbackSourceID("pushmirror", req.ID), "push_remote_mirror", req.ID, fingerprint, "remote trigger pending confirmation", s.now().UTC()),
			map[string]string{
				"method":             "POST",
				"idempotency_key":    key,
				"remote_alias":       req.ID,
				"remote_type":        "push_remote_mirror",
				"provider":           "gitcode-http",
				"provider_mode":      string(gitcode.ProviderModeLive),
				"source_fingerprint": fingerprint,
			},
		)
		if claimer, ok := s.store.(auditClaimStore); ok {
			claimed, err := claimer.ClaimAuditEvent(ctx, entry)
			if err != nil {
				return WriteCommandResult{}, ErrWriteFailure{Code: "write_audit_start_failed", RepoID: route.RepoID, RemoteID: req.ID, IdempotencyKey: key, Cause: err}
			}
			if !claimed {
				return s.executeWrite(ctx, command, req, scope)
			}
		} else if err := s.store.RecordAuditEvent(ctx, entry); err != nil {
			return WriteCommandResult{}, ErrWriteFailure{Code: "write_audit_start_failed", RepoID: route.RepoID, RemoteID: req.ID, IdempotencyKey: key, Cause: err}
		}
	}
	confirmed, graph, err := s.callWriteAdapter(ctx, command, route, req, key)
	if err != nil {
		code := s.writeAdapterErrorCode(req.Mode, err)
		remoteID := remoteWriteID(err)
		if command == "create-pr" && remoteID == "" {
			phase, attempted := writeMutationPhase(err)
			if attempted && !safeMergePRMutationFailure(phase, err) {
				// An identity-less POST outcome cannot be reconciled safely.
				// Keep the pre-POST claim instead of enabling a blind creation.
				return WriteCommandResult{}, ErrWriteFailure{Code: "write_ambiguous_remote", RepoID: route.RepoID, IdempotencyKey: key, Cause: err}
			}
		}
		if isTopLevelCommentCommand(command) && remoteID == "" && !safeTopLevelCommentFailure(err) {
			metadata := cloneStringMap(topLevelCommentClaimMetadata)
			metadata["write_phase"] = "post_ambiguous"
			entry := audit.WithRequestMetadata(audit.InProgress(route.RepoID, key, command, topLevelCommentClaimRecordID, topLevelCommentClaimRemoteType, "", fingerprint, "write_ambiguous_remote", s.now().UTC()), metadata)
			_ = s.store.RecordAuditEvent(ctx, entry)
			return WriteCommandResult{}, ErrWriteFailure{Code: "write_ambiguous_remote", RepoID: route.RepoID, IdempotencyKey: key, PayloadSource: failureSource(err), Cause: err}
		}
		if command == "update-issue" {
			var claimFailure ErrWriteFailure
			if errors.As(err, &claimFailure) && claimFailure.Code == "write_idempotency_in_progress" {
				return WriteCommandResult{}, claimFailure
			}
			var precondition gitcode.ErrWritePreconditionConflict
			if errors.As(err, &precondition) {
				metadata := issueUpdateClaimMetadata
				if priorEntry != nil && len(priorEntry.RequestMetadata) > 0 {
					metadata = priorEntry.RequestMetadata
				}
				entry := audit.WithRequestMetadata(audit.Failure(route.RepoID, key, command, fingerprint, "write_conflict", s.now().UTC()), metadata)
				_ = s.store.RecordAuditEvent(ctx, entry)
				return WriteCommandResult{}, ErrWriteFailure{Code: "write_conflict", RepoID: route.RepoID, RemoteID: strconv.Itoa(req.Number), IdempotencyKey: key, Cause: err}
			}
			if phase, attempted := writeMutationPhase(err); attempted && !safeIssueUpdateMutationFailure(phase, err) {
				metadata := cloneStringMap(issueUpdateClaimMetadata)
				metadata["write_phase"] = phase + "_ambiguous"
				entry := audit.WithRequestMetadata(audit.InProgress(route.RepoID, key, command, req.IssueID, "issue", strconv.Itoa(req.Number), fingerprint, "write_ambiguous_remote", s.now().UTC()), metadata)
				_ = s.store.RecordAuditEvent(ctx, entry)
				return WriteCommandResult{}, ErrWriteFailure{Code: "write_ambiguous_remote", RepoID: route.RepoID, RemoteID: strconv.Itoa(req.Number), IdempotencyKey: key, PayloadSource: failureSource(err), Cause: err}
			}
			if len(issueUpdateClaimMetadata) > 0 {
				metadata := cloneStringMap(issueUpdateClaimMetadata)
				metadata["write_phase"] = "failed_before_or_rejected_patch"
				_ = s.store.RecordAuditEvent(ctx, audit.WithRequestMetadata(audit.Failure(route.RepoID, key, command, fingerprint, code, s.now().UTC()), metadata))
				return WriteCommandResult{}, ErrWriteFailure{Code: code, RepoID: route.RepoID, IdempotencyKey: key, PayloadSource: failureSource(err), Cause: writeFailureCause(code, err)}
			}
		}
		if command == "update-pr" {
			var claimFailure ErrWriteFailure
			if errors.As(err, &claimFailure) && (claimFailure.Code == "write_idempotency_in_progress" || claimFailure.Code == "write_audit_start_failed") {
				return WriteCommandResult{}, claimFailure
			}
			var precondition gitcode.ErrWritePreconditionConflict
			if errors.As(err, &precondition) {
				return WriteCommandResult{}, ErrWriteFailure{Code: "write_conflict", RepoID: route.RepoID, RemoteID: strconv.Itoa(req.Number), IdempotencyKey: key, Cause: err}
			}
			if !prUpdateClaimed {
				// Provider preflight failed before this caller acquired the
				// generation-aware claim. It does not own the audit row and must
				// not overwrite a concurrent claimant's in-progress fence.
				return WriteCommandResult{}, ErrWriteFailure{Code: code, RepoID: route.RepoID, RemoteID: strconv.Itoa(req.Number), IdempotencyKey: key, PayloadSource: failureSource(err), Cause: writeFailureCause(code, err)}
			}
			if prUpdateClaimed {
				if phase, attempted := writeMutationPhase(err); attempted && !safePRUpdateMutationFailure(phase, err) {
					metadata := cloneStringMap(prUpdateClaimMetadata)
					metadata["write_phase"] = phase + "_ambiguous"
					entry := audit.WithRequestMetadata(audit.InProgress(route.RepoID, key, command, fallbackSourceID("pull_request", strconv.Itoa(req.Number)), "pull_request", strconv.Itoa(req.Number), fingerprint, "write_ambiguous_remote", s.now().UTC()), metadata)
					transitioned, current, transitionErr := s.transitionAuditGeneration(ctx, prUpdateAuditStore, entry, prUpdateClaimCreatedAt, audit.StatusInProgress)
					if transitionErr != nil {
						return WriteCommandResult{}, ErrWriteFailure{Code: "write_audit_start_failed", RepoID: route.RepoID, RemoteID: strconv.Itoa(req.Number), IdempotencyKey: key, Cause: transitionErr}
					}
					if !transitioned && auditGenerationSucceeded(current, prUpdateClaimCreatedAt, fingerprint) {
						return replayWriteResult(command, req, *current, fingerprint, s.now().UTC()), nil
					}
					return WriteCommandResult{}, ErrWriteFailure{Code: "write_ambiguous_remote", RepoID: route.RepoID, RemoteID: strconv.Itoa(req.Number), IdempotencyKey: key, PayloadSource: failureSource(err), Cause: err}
				}
				metadata := cloneStringMap(prUpdateClaimMetadata)
				metadata["write_phase"] = "failed_before_or_rejected_patch"
				entry := audit.WithRequestMetadata(audit.Failure(route.RepoID, key, command, fingerprint, code, s.now().UTC()), metadata)
				transitioned, current, transitionErr := s.transitionAuditGeneration(ctx, prUpdateAuditStore, entry, prUpdateClaimCreatedAt, audit.StatusInProgress)
				if transitionErr != nil {
					return WriteCommandResult{}, ErrWriteFailure{Code: "write_audit_start_failed", RepoID: route.RepoID, RemoteID: strconv.Itoa(req.Number), IdempotencyKey: key, Cause: transitionErr}
				}
				if !transitioned && auditGenerationSucceeded(current, prUpdateClaimCreatedAt, fingerprint) {
					return replayWriteResult(command, req, *current, fingerprint, s.now().UTC()), nil
				}
				return WriteCommandResult{}, ErrWriteFailure{Code: code, RepoID: route.RepoID, IdempotencyKey: key, PayloadSource: failureSource(err), Cause: writeFailureCause(code, err)}
			}
		}
		if command == "merge-pr" {
			var claimFailure ErrWriteFailure
			if errors.As(err, &claimFailure) && (claimFailure.Code == "write_idempotency_in_progress" || claimFailure.Code == "write_audit_start_failed") {
				return WriteCommandResult{}, claimFailure
			}
			var precondition gitcode.ErrWritePreconditionConflict
			if errors.As(err, &precondition) {
				if mergePRClaimed {
					entry := audit.WithRequestMetadata(audit.Failure(route.RepoID, key, command, fingerprint, "write_conflict", s.now().UTC()), mergePRClaimMetadata)
					_, _ = mergePRAuditStore.TransitionAuditEventGeneration(ctx, entry, mergePRClaimCreatedAt, audit.StatusInProgress)
				}
				return WriteCommandResult{}, ErrWriteFailure{Code: "write_conflict", RepoID: route.RepoID, RemoteID: strconv.Itoa(req.Number), IdempotencyKey: key, Cause: err}
			}
			if !mergePRClaimed {
				// Provider preflight failed before this caller acquired the
				// generation-aware claim. It does not own the audit row and must
				// not overwrite a concurrent claimant's in-progress fence.
				return WriteCommandResult{}, ErrWriteFailure{Code: code, RepoID: route.RepoID, RemoteID: strconv.Itoa(req.Number), IdempotencyKey: key, PayloadSource: failureSource(err), Cause: writeFailureCause(code, err)}
			}
			if phase, attempted := writeMutationPhase(err); attempted && !safeMergePRMutationFailure(phase, err) {
				metadata := cloneStringMap(mergePRClaimMetadata)
				metadata["write_phase"] = phase + "_ambiguous"
				entry := audit.WithRequestMetadata(audit.InProgress(route.RepoID, key, command, fallbackSourceID("pull_request", strconv.Itoa(req.Number)), "pull_request", strconv.Itoa(req.Number), fingerprint, "write_ambiguous_remote", s.now().UTC()), metadata)
				_, _ = mergePRAuditStore.TransitionAuditEventGeneration(ctx, entry, mergePRClaimCreatedAt, audit.StatusInProgress)
				return WriteCommandResult{}, ErrWriteFailure{Code: "write_ambiguous_remote", RepoID: route.RepoID, RemoteID: strconv.Itoa(req.Number), IdempotencyKey: key, PayloadSource: failureSource(err), Cause: err}
			}
			if len(mergePRClaimMetadata) > 0 {
				metadata := cloneStringMap(mergePRClaimMetadata)
				metadata["write_phase"] = "failed_before_or_rejected_put"
				entry := audit.WithRequestMetadata(audit.Failure(route.RepoID, key, command, fingerprint, code, s.now().UTC()), metadata)
				_, _ = mergePRAuditStore.TransitionAuditEventGeneration(ctx, entry, mergePRClaimCreatedAt, audit.StatusInProgress)
				return WriteCommandResult{}, ErrWriteFailure{Code: code, RepoID: route.RepoID, RemoteID: strconv.Itoa(req.Number), IdempotencyKey: key, PayloadSource: failureSource(err), Cause: writeFailureCause(code, err)}
			}
		}
		if remoteID != "" {
			remoteType := writeCommandRemoteType(command)
			entry := audit.RemoteConfirmedUnsafe(route.RepoID, key, command, fallbackSourceID(remoteType, remoteID), remoteType, remoteID, fingerprint, code, s.now().UTC())
			if auditErr := s.store.RecordAuditEvent(ctx, entry); auditErr != nil {
				partial := audit.RemoteConfirmedAuditFailed(route.RepoID, key, command, entry.RecordID, remoteType, remoteID, fingerprint, auditErr.Error(), s.now().UTC())
				_ = s.store.RecordAuditEvent(ctx, partial)
				return WriteCommandResult{}, ErrWriteFailure{Code: "write_partial_remote_confirmed_audit_failed", RepoID: route.RepoID, RemoteID: remoteID, IdempotencyKey: key, Cause: auditErr}
			}
			return WriteCommandResult{}, ErrWriteFailure{Code: code, RepoID: route.RepoID, RemoteID: remoteID, IdempotencyKey: key, PayloadSource: failureSource(err), Cause: writeFailureCause(code, err)}
		}
		if command == "trigger-push-mirror" && !safePushMirrorTriggerFailure(err) {
			return WriteCommandResult{}, ErrWriteFailure{Code: "write_ambiguous_remote", RepoID: route.RepoID, RemoteID: req.ID, IdempotencyKey: key, PayloadSource: failureSource(err), Cause: err}
		}
		_ = s.store.RecordAuditEvent(ctx, audit.Failure(route.RepoID, key, command, fingerprint, code, s.now().UTC()))
		return WriteCommandResult{}, ErrWriteFailure{Code: code, RepoID: route.RepoID, IdempotencyKey: key, PayloadSource: failureSource(err), Cause: writeFailureCause(code, err)}
	}
	if !confirmed.confirmed || confirmed.remoteID == "" {
		if isTopLevelCommentCommand(command) {
			metadata := cloneStringMap(topLevelCommentClaimMetadata)
			metadata["write_phase"] = "confirmation_ambiguous"
			entry := audit.WithRequestMetadata(audit.InProgress(route.RepoID, key, command, topLevelCommentClaimRecordID, topLevelCommentClaimRemoteType, confirmed.remoteID, fingerprint, "write_ambiguous_remote", s.now().UTC()), metadata)
			_ = s.store.RecordAuditEvent(ctx, entry)
			return WriteCommandResult{}, ErrWriteFailure{Code: "write_ambiguous_remote", RepoID: route.RepoID, RemoteID: confirmed.remoteID, IdempotencyKey: key}
		}
		if command == "update-issue" && len(issueUpdateClaimMetadata) > 0 {
			metadata := cloneStringMap(issueUpdateClaimMetadata)
			metadata["write_phase"] = "patch_unconfirmed"
			_ = s.store.RecordAuditEvent(ctx, audit.WithRequestMetadata(audit.InProgress(route.RepoID, key, command, req.IssueID, "issue", strconv.Itoa(req.Number), fingerprint, "write_ambiguous_remote", s.now().UTC()), metadata))
			return WriteCommandResult{}, ErrWriteFailure{Code: "write_ambiguous_remote", RepoID: route.RepoID, RemoteID: strconv.Itoa(req.Number), IdempotencyKey: key}
		}
		if command == "merge-pr" && len(mergePRClaimMetadata) > 0 {
			metadata := cloneStringMap(mergePRClaimMetadata)
			metadata["write_phase"] = "put_unconfirmed"
			entry := audit.WithRequestMetadata(audit.InProgress(route.RepoID, key, command, fallbackSourceID("pull_request", strconv.Itoa(req.Number)), "pull_request", strconv.Itoa(req.Number), fingerprint, "write_ambiguous_remote", s.now().UTC()), metadata)
			_, _ = mergePRAuditStore.TransitionAuditEventGeneration(ctx, entry, mergePRClaimCreatedAt, audit.StatusInProgress)
			return WriteCommandResult{}, ErrWriteFailure{Code: "write_ambiguous_remote", RepoID: route.RepoID, RemoteID: strconv.Itoa(req.Number), IdempotencyKey: key}
		}
		_ = s.store.RecordAuditEvent(ctx, audit.Failure(route.RepoID, key, command, fingerprint, "write_unconfirmed_remote", s.now().UTC()))
		return WriteCommandResult{}, ErrWriteFailure{Code: "write_unconfirmed_remote", RepoID: route.RepoID, IdempotencyKey: key}
	}
	auditEntry := audit.Success(route.RepoID, key, command, graph.Record.ID, graph.Record.RemoteType, confirmed.remoteID, fingerprint, confirmed.message, confirmed.completedAt)
	if command == "create-issue" {
		entry, err := audit.LiveCreateIssueConfirmation(audit.ConfirmationInput{RepoID: route.RepoID, Key: key, Command: command, Mode: string(req.Mode), RecordID: graph.Record.ID, RemoteType: graph.Record.RemoteType, RemoteID: confirmed.remoteID, PayloadHash: fingerprint, Message: confirmed.message, RequestMetadata: writeAuditMetadata(command, key, fingerprint, graph.Record.RemoteType, confirmed), CreatedAt: confirmed.completedAt})
		if err != nil {
			_ = s.store.RecordAuditEvent(ctx, audit.Failure(route.RepoID, key, command, fingerprint, "write_unconfirmed_remote", s.now().UTC()))
			return WriteCommandResult{}, ErrWriteFailure{Code: "write_unconfirmed_remote", RepoID: route.RepoID, IdempotencyKey: key}
		}
		auditEntry = entry
	}
	auditEntry = withWriteAuditMetadata(auditEntry, command, key, fingerprint, graph.Record.RemoteType, confirmed)
	if isPrimaryCreation(command) {
		return s.settlePrimaryCreation(ctx, command, req, primaryCreationClaim, graph, auditEntry, false)
	}
	if command == "update-issue" && len(issueUpdateClaimMetadata) > 0 {
		metadata := cloneStringMap(issueUpdateClaimMetadata)
		for metadataKey, metadataValue := range auditEntry.RequestMetadata {
			metadata[metadataKey] = metadataValue
		}
		metadata["write_phase"] = "canonical_readback_confirmed"
		auditEntry = audit.WithRequestMetadata(auditEntry, metadata)
	}
	if command == "update-pr" && len(prUpdateClaimMetadata) > 0 {
		metadata := cloneStringMap(prUpdateClaimMetadata)
		for metadataKey, metadataValue := range auditEntry.RequestMetadata {
			metadata[metadataKey] = metadataValue
		}
		metadata["write_phase"] = "canonical_readback_confirmed"
		auditEntry = audit.WithRequestMetadata(auditEntry, metadata)
	}
	if command == "merge-pr" && len(mergePRClaimMetadata) > 0 {
		metadata := cloneStringMap(mergePRClaimMetadata)
		for metadataKey, metadataValue := range auditEntry.RequestMetadata {
			metadata[metadataKey] = metadataValue
		}
		metadata["write_phase"] = "canonical_readback_confirmed"
		auditEntry = audit.WithRequestMetadata(auditEntry, metadata)
	}
	initialAuditEntry := auditEntry
	if command == "update-pr" {
		initialAuditEntry = audit.WithRequestMetadata(audit.RemoteConfirmedCacheRefreshPending(route.RepoID, key, command, graph.Record.ID, graph.Record.RemoteType, confirmed.remoteID, fingerprint, "remote pull request update confirmed; cache refresh pending", confirmed.completedAt), auditEntry.RequestMetadata)
	}
	if command == "merge-pr" {
		initialAuditEntry = audit.WithRequestMetadata(audit.RemoteConfirmedCacheRefreshPending(route.RepoID, key, command, graph.Record.ID, graph.Record.RemoteType, confirmed.remoteID, fingerprint, "remote merge confirmed; cache refresh pending", confirmed.completedAt), auditEntry.RequestMetadata)
	}
	var initialAuditErr error
	if command == "update-pr" {
		var settled bool
		var current *cache.AuditTrailEntry
		settled, current, initialAuditErr = s.transitionAuditGeneration(ctx, prUpdateAuditStore, initialAuditEntry, prUpdateClaimCreatedAt, audit.StatusInProgress)
		if initialAuditErr == nil && !settled {
			if auditGenerationSucceeded(current, prUpdateClaimCreatedAt, fingerprint) {
				return replayWriteResult(command, req, *current, fingerprint, s.now().UTC()), nil
			}
			initialAuditErr = errors.New("pull request update audit generation advanced by another caller")
		}
	} else if command == "merge-pr" {
		var settled bool
		var current *cache.AuditTrailEntry
		settled, current, initialAuditErr = s.transitionAuditGeneration(ctx, mergePRAuditStore, initialAuditEntry, mergePRClaimCreatedAt, audit.StatusInProgress, audit.StatusRemoteConfirmedCacheRefreshFailed)
		if initialAuditErr == nil && !settled {
			if auditGenerationSucceeded(current, mergePRClaimCreatedAt, fingerprint) {
				return replayWriteResult(command, req, *current, fingerprint, s.now().UTC()), nil
			}
			initialAuditErr = errors.New("merge audit generation advanced by another caller")
		}
	} else {
		initialAuditErr = s.store.RecordAuditEvent(ctx, initialAuditEntry)
	}
	if initialAuditErr != nil {
		partial := withWriteAuditMetadata(audit.RemoteConfirmedAuditFailed(route.RepoID, key, command, graph.Record.ID, graph.Record.RemoteType, confirmed.remoteID, fingerprint, initialAuditErr.Error(), s.now().UTC()), command, key, fingerprint, graph.Record.RemoteType, confirmed)
		if command == "update-issue" || command == "update-pr" || command == "merge-pr" {
			partial = audit.WithRequestMetadata(partial, auditEntry.RequestMetadata)
		}
		if command != "update-pr" && command != "merge-pr" {
			_ = s.store.RecordAuditEvent(ctx, partial)
		} else if command == "update-pr" {
			_, _, _ = s.transitionAuditGeneration(ctx, prUpdateAuditStore, partial, prUpdateClaimCreatedAt, audit.StatusInProgress, audit.StatusRemoteConfirmedCacheRefreshPending)
		}
		return WriteCommandResult{}, ErrWriteFailure{Code: "write_partial_remote_confirmed_audit_failed", RepoID: route.RepoID, RemoteID: confirmed.remoteID, IdempotencyKey: key, Cause: initialAuditErr}
	}
	if err := s.store.UpsertRecordGraph(ctx, graph); err != nil {
		partial := withWriteAuditMetadata(audit.RemoteConfirmedCacheRefreshFailed(route.RepoID, key, command, graph.Record.ID, graph.Record.RemoteType, confirmed.remoteID, fingerprint, err.Error(), s.now().UTC()), command, key, fingerprint, graph.Record.RemoteType, confirmed)
		if command == "update-issue" || command == "update-pr" || command == "merge-pr" {
			partial = audit.WithRequestMetadata(partial, auditEntry.RequestMetadata)
		}
		if command == "update-pr" {
			_, current, _ := s.transitionAuditGeneration(ctx, prUpdateAuditStore, partial, prUpdateClaimCreatedAt, audit.StatusRemoteConfirmedCacheRefreshPending)
			if auditGenerationSucceeded(current, prUpdateClaimCreatedAt, fingerprint) {
				return replayWriteResult(command, req, *current, fingerprint, s.now().UTC()), nil
			}
		} else if command == "merge-pr" {
			_, current, _ := s.transitionAuditGeneration(ctx, mergePRAuditStore, partial, mergePRClaimCreatedAt, audit.StatusRemoteConfirmedCacheRefreshPending)
			if auditGenerationSucceeded(current, mergePRClaimCreatedAt, fingerprint) {
				return replayWriteResult(command, req, *current, fingerprint, s.now().UTC()), nil
			}
		} else {
			_ = s.store.RecordAuditEvent(ctx, partial)
		}
		return WriteCommandResult{}, ErrWriteFailure{Code: "write_partial_cache_refresh_failed", RepoID: route.RepoID, RemoteID: confirmed.remoteID, IdempotencyKey: key, Cause: err}
	}
	if err := s.refreshIssueCommentWriteCache(ctx, command, graph); err != nil {
		partial := withWriteAuditMetadata(audit.RemoteConfirmedCacheRefreshFailed(route.RepoID, key, command, graph.Record.ID, graph.Record.RemoteType, confirmed.remoteID, fingerprint, err.Error(), s.now().UTC()), command, key, fingerprint, graph.Record.RemoteType, confirmed)
		if command == "update-issue" || command == "update-pr" || command == "merge-pr" {
			partial = audit.WithRequestMetadata(partial, auditEntry.RequestMetadata)
		}
		if command == "update-pr" {
			_, current, _ := s.transitionAuditGeneration(ctx, prUpdateAuditStore, partial, prUpdateClaimCreatedAt, audit.StatusRemoteConfirmedCacheRefreshPending)
			if auditGenerationSucceeded(current, prUpdateClaimCreatedAt, fingerprint) {
				return replayWriteResult(command, req, *current, fingerprint, s.now().UTC()), nil
			}
		} else if command == "merge-pr" {
			_, current, _ := s.transitionAuditGeneration(ctx, mergePRAuditStore, partial, mergePRClaimCreatedAt, audit.StatusRemoteConfirmedCacheRefreshPending)
			if auditGenerationSucceeded(current, mergePRClaimCreatedAt, fingerprint) {
				return replayWriteResult(command, req, *current, fingerprint, s.now().UTC()), nil
			}
		} else {
			_ = s.store.RecordAuditEvent(ctx, partial)
		}
		return WriteCommandResult{}, ErrWriteFailure{Code: "write_partial_cache_refresh_failed", RepoID: route.RepoID, RemoteID: confirmed.remoteID, IdempotencyKey: key, Cause: err}
	}
	if err := s.recordCacheConfirmation(ctx, command, route.RepoID, key, fingerprint, graph, confirmed.remoteID, "succeeded", confirmed.completedAt); err != nil {
		partial := withWriteAuditMetadata(audit.RemoteConfirmedCacheRefreshFailed(route.RepoID, key, command, graph.Record.ID, graph.Record.RemoteType, confirmed.remoteID, fingerprint, err.Error(), s.now().UTC()), command, key, fingerprint, graph.Record.RemoteType, confirmed)
		if command == "update-issue" || command == "update-pr" || command == "merge-pr" {
			partial = audit.WithRequestMetadata(partial, auditEntry.RequestMetadata)
		}
		if command == "update-pr" {
			_, current, _ := s.transitionAuditGeneration(ctx, prUpdateAuditStore, partial, prUpdateClaimCreatedAt, audit.StatusRemoteConfirmedCacheRefreshPending)
			if auditGenerationSucceeded(current, prUpdateClaimCreatedAt, fingerprint) {
				return replayWriteResult(command, req, *current, fingerprint, s.now().UTC()), nil
			}
		} else if command == "merge-pr" {
			_, current, _ := s.transitionAuditGeneration(ctx, mergePRAuditStore, partial, mergePRClaimCreatedAt, audit.StatusRemoteConfirmedCacheRefreshPending)
			if auditGenerationSucceeded(current, mergePRClaimCreatedAt, fingerprint) {
				return replayWriteResult(command, req, *current, fingerprint, s.now().UTC()), nil
			}
		} else {
			_ = s.store.RecordAuditEvent(ctx, partial)
		}
		return WriteCommandResult{}, ErrWriteFailure{Code: "write_partial_cache_refresh_failed", RepoID: route.RepoID, RemoteID: confirmed.remoteID, IdempotencyKey: key, Cause: err}
	}
	if command == "update-pr" {
		settled, _, err := s.transitionAuditGeneration(ctx, prUpdateAuditStore, auditEntry, prUpdateClaimCreatedAt, audit.StatusRemoteConfirmedCacheRefreshPending, audit.StatusRemoteConfirmedCacheRefreshFailed)
		if err != nil || !settled {
			if err == nil {
				err = errors.New("pull request update success could not settle the owned audit generation")
			}
			partial := audit.WithRequestMetadata(audit.RemoteConfirmedAuditFailed(route.RepoID, key, command, graph.Record.ID, graph.Record.RemoteType, confirmed.remoteID, fingerprint, err.Error(), s.now().UTC()), auditEntry.RequestMetadata)
			_, _, _ = s.transitionAuditGeneration(ctx, prUpdateAuditStore, partial, prUpdateClaimCreatedAt, audit.StatusRemoteConfirmedCacheRefreshPending)
			return WriteCommandResult{}, ErrWriteFailure{Code: "write_partial_remote_confirmed_audit_failed", RepoID: route.RepoID, RemoteID: confirmed.remoteID, IdempotencyKey: key, Cause: err}
		}
	}
	if command == "merge-pr" {
		settled, _, err := s.transitionAuditGeneration(ctx, mergePRAuditStore, auditEntry, mergePRClaimCreatedAt, audit.StatusRemoteConfirmedCacheRefreshPending, audit.StatusRemoteConfirmedCacheRefreshFailed)
		if err != nil || !settled {
			if err == nil {
				err = errors.New("merge success could not settle the owned audit generation")
			}
			partial := audit.WithRequestMetadata(audit.RemoteConfirmedAuditFailed(route.RepoID, key, command, graph.Record.ID, graph.Record.RemoteType, confirmed.remoteID, fingerprint, err.Error(), s.now().UTC()), auditEntry.RequestMetadata)
			_, _, _ = s.transitionAuditGeneration(ctx, mergePRAuditStore, partial, mergePRClaimCreatedAt, audit.StatusRemoteConfirmedCacheRefreshPending)
			return WriteCommandResult{}, ErrWriteFailure{Code: "write_partial_remote_confirmed_audit_failed", RepoID: route.RepoID, RemoteID: confirmed.remoteID, IdempotencyKey: key, Cause: err}
		}
	}
	base.Status = "succeeded"
	base.ID = graph.Record.ID
	applyWriteIssueIdentity(&base, command, req, graph.Record.ID, confirmed.remoteNumber)
	base.RemoteID = confirmed.remoteID
	base.RemoteNumber = confirmed.remoteNumber
	base.RemoteSlug = confirmed.remoteSlug
	base.RemoteRevision = confirmed.remoteRevision
	base.APIPath = confirmed.apiPath
	base.CachePath = confirmed.cachePath
	base.BrowserURL = firstNonEmptyString(confirmed.browserURL, base.BrowserURL)
	base.Milestone = confirmed.milestone
	base.PushMirror = confirmed.pushMirror
	base.Evidence = "adapter-confirmed write with audit and cache refresh"
	return base, nil
}

type writeConfirmation struct {
	confirmed      bool
	remoteID       string
	providerID     string
	remoteNumber   int
	remoteSlug     string
	remoteRevision string
	apiPath        string
	cachePath      string
	browserURL     string
	message        string
	completedAt    time.Time
	milestone      *WriteMilestoneReceipt
	pushMirror     *WritePushMirrorReceipt
	parentRemoteID string
}

func writeAuditMetadata(command, key, fingerprint, remoteType string, confirmed writeConfirmation) map[string]string {
	method := "POST"
	switch command {
	case "update-issue", "update-pr", "set-issue-milestone", "clear-issue-milestone":
		method = "PATCH"
	case "merge-pr", "update-page":
		method = "PUT"
	}
	metadata := map[string]string{
		"method":             method,
		"idempotency_key":    key,
		"remote_type":        remoteType,
		"provider_mode":      string(gitcode.ProviderModeLive),
		"source_fingerprint": fingerprint,
	}
	if confirmed.remoteID != "" {
		metadata["remote_alias"] = confirmed.remoteID
	}
	if confirmed.providerID != "" {
		metadata["provider_id"] = confirmed.providerID
	}
	if confirmed.remoteNumber > 0 {
		metadata["remote_number"] = strconv.Itoa(confirmed.remoteNumber)
	}
	if confirmed.parentRemoteID != "" {
		metadata["pr_discussion_id"] = confirmed.parentRemoteID
	}
	if command != "" {
		metadata["provider"] = "gitcode-http"
	}
	if confirmed.milestone != nil {
		if confirmed.milestone.ID != "" {
			metadata["milestone_id"] = confirmed.milestone.ID
		}
		if confirmed.milestone.RemoteID != "" {
			metadata["milestone_remote_id"] = confirmed.milestone.RemoteID
		}
		if confirmed.milestone.Title != "" {
			metadata["milestone_title"] = confirmed.milestone.Title
		}
		if confirmed.milestone.Cleared {
			metadata["milestone_cleared"] = "true"
		}
	}
	if confirmed.pushMirror != nil {
		metadata["push_mirror_id"] = confirmed.pushMirror.MirrorID
		metadata["push_mirror_status"] = confirmed.pushMirror.Status
		metadata["push_mirror_previous_status"] = confirmed.pushMirror.PreviousStatus
		metadata["push_mirror_triggered_at"] = confirmed.pushMirror.TriggeredAt.UTC().Format(time.RFC3339Nano)
	}
	return metadata
}

func withWriteAuditMetadata(entry cache.AuditTrailEntry, command, key, fingerprint, remoteType string, confirmed writeConfirmation) cache.AuditTrailEntry {
	if command != "create-issue" && command != "create-pr" && command != "merge-pr" && command != "add-comment" && command != "add-pr-comment" && command != "reply-pr-review-comment" && confirmed.milestone == nil && confirmed.pushMirror == nil {
		return entry
	}
	return audit.WithRequestMetadata(entry, writeAuditMetadata(command, key, fingerprint, remoteType, confirmed))
}

func (s *Service) recordCacheConfirmation(ctx context.Context, command, repoID, key, fingerprint string, graph cache.RecordGraph, remoteID, status string, createdAt time.Time) error {
	if command != "create-issue" {
		return nil
	}
	return s.store.RecordCacheConfirmation(ctx, cache.CacheConfirmationRecord{RepoID: repoID, Command: command, RecordID: graph.Record.ID, RecordType: graph.Record.Type, RemoteType: graph.Record.RemoteType, RemoteID: firstNonEmptyString(remoteID, graph.Record.RemoteID), IdempotencyKey: key, Status: status, SourceFingerprint: fingerprint, CreatedAt: createdAt})
}

func (s *Service) hasWriteCredential() bool {
	if s.providerMode == gitcode.ProviderModeLive {
		return s.writeCredentialPresent
	}
	return strings.TrimSpace(os.Getenv("GITCODE_TOKEN")) != ""
}

func (s *Service) callWriteAdapter(ctx context.Context, command string, route RepositoryRoute, req WriteCommandRequest, key string) (writeConfirmation, cache.RecordGraph, error) {
	opts := gitcode.WriteOptions{IdempotencyKey: key, BeforeIssueUpdateMutation: req.beforeIssueUpdateMutation, BeforePRUpdateMutation: req.beforePRUpdateMutation, BeforeMergePRMutation: req.beforeMergePRMutation}
	now := s.now().UTC()
	switch command {
	case "create-issue":
		var resolved *gitcode.Milestone
		var encoded json.RawMessage
		if strings.TrimSpace(req.Milestone) != "" {
			milestone, err := s.resolveMilestone(ctx, route, req.Milestone)
			if err != nil {
				return writeConfirmation{}, cache.RecordGraph{}, err
			}
			resolved = &milestone
			encoded = gitcode.EncodeIssueMilestone(milestone.RemoteID)
		}
		result, err := s.client.CreateIssue(ctx, gitcode.CreateIssueRequest{Owner: route.Owner, Repo: route.Name, Title: strings.TrimSpace(req.Title), Body: req.Body, Labels: gitcode.EncodeIssueLabels(req.Labels), Milestone: encoded}, opts)
		if err != nil {
			return writeConfirmation{}, cache.RecordGraph{}, err
		}
		var receipt *WriteMilestoneReceipt
		if resolved != nil {
			result, receipt, err = s.confirmIssueMilestone(ctx, route, result, resolved, false)
			if err != nil {
				return writeConfirmation{}, cache.RecordGraph{}, err
			}
		}
		confirmation, graph := s.issueWriteGraph(route.RepoID, result.Record, result, now)
		confirmation.milestone = receipt
		return confirmation, graph, nil
	case "update-issue":
		var resolved *gitcode.Milestone
		var encoded json.RawMessage
		if strings.TrimSpace(req.Milestone) != "" {
			milestone, err := s.resolveMilestone(ctx, route, req.Milestone)
			if err != nil {
				return writeConfirmation{}, cache.RecordGraph{}, err
			}
			resolved = &milestone
			encoded = gitcode.EncodeIssueMilestone(milestone.RemoteID)
		} else if req.ClearMilestone {
			encoded = gitcode.EncodeIssueMilestone("null")
		}
		result, err := s.client.UpdateIssue(ctx, gitcode.UpdateIssueRequest{Owner: route.Owner, Repo: route.Name, Number: req.Number, Title: req.Title, Body: req.Body, State: req.State, Labels: gitcode.EncodeIssueLabels(req.Labels), Milestone: encoded}, opts)
		if err != nil {
			return writeConfirmation{}, cache.RecordGraph{}, err
		}
		var receipt *WriteMilestoneReceipt
		if resolved != nil || req.ClearMilestone {
			result, receipt, err = s.confirmIssueMilestone(ctx, route, result, resolved, req.ClearMilestone)
			if err != nil {
				return writeConfirmation{}, cache.RecordGraph{}, err
			}
		}
		confirmation, graph := s.issueWriteGraph(route.RepoID, result.Record, result, now)
		confirmation.milestone = receipt
		return confirmation, graph, nil
	case "add-comment":
		result, err := s.client.CreateIssueComment(ctx, gitcode.CreateIssueCommentRequest{Owner: route.Owner, Repo: route.Name, Number: req.Number, Body: req.Body}, opts)
		if err != nil {
			return writeConfirmation{}, cache.RecordGraph{}, err
		}
		return s.commentWriteGraph(ctx, route.RepoID, req.Number, req.IssueID, result.Record, result, now)
	case "update-comment":
		commentID := strings.TrimSpace(firstNonEmptyString(req.CommentID, req.ID))
		result, err := s.client.UpdateIssueComment(ctx, gitcode.UpdateIssueCommentRequest{Owner: route.Owner, Repo: route.Name, Number: req.Number, CommentID: commentID, Body: req.Body}, opts)
		if err != nil {
			return writeConfirmation{}, cache.RecordGraph{}, err
		}
		return s.commentWriteGraph(ctx, route.RepoID, req.Number, req.IssueID, result.Record, result, now)
	case "create-pr":
		result, err := s.client.CreatePR(ctx, gitcode.CreatePRRequest{Owner: route.Owner, Repo: route.Name, Title: strings.TrimSpace(req.Title), Body: req.Body, Head: strings.TrimSpace(req.Head), Base: strings.TrimSpace(req.Base)}, opts)
		if err != nil {
			return writeConfirmation{}, cache.RecordGraph{}, err
		}
		return s.pullRequestWriteGraph(ctx, route.RepoID, result.Record, result, now)
	case "update-pr":
		result, err := s.client.UpdatePR(ctx, gitcode.UpdatePRRequest{Owner: route.Owner, Repo: route.Name, Number: req.Number, Title: req.Title, Body: req.Body, State: req.State}, opts)
		if err != nil {
			return writeConfirmation{}, cache.RecordGraph{}, err
		}
		return s.pullRequestWriteGraph(ctx, route.RepoID, result.Record, result, now)
	case "merge-pr":
		result, err := s.client.MergePR(ctx, gitcode.MergePRRequest{Owner: route.Owner, Repo: route.Name, Number: req.Number, HeadSHA: strings.TrimSpace(req.Sha), MergeMethod: req.Strategy}, opts)
		if err != nil {
			return writeConfirmation{}, cache.RecordGraph{}, err
		}
		return s.pullRequestWriteGraph(ctx, route.RepoID, result.Record, result, now)
	case "create-milestone":
		result, err := s.client.CreateMilestone(ctx, gitcode.MilestoneWriteRequest{Owner: route.Owner, Repo: route.Name, Title: strings.TrimSpace(req.Title), Description: req.Description, DueOn: req.DueOn, State: req.State}, opts)
		if err != nil {
			return writeConfirmation{}, cache.RecordGraph{}, err
		}
		confirmation, graph := s.milestoneWriteGraph(route.RepoID, result.Record, result, now)
		return confirmation, graph, nil
	case "update-milestone":
		id, err := s.resolveMilestoneID(ctx, route, firstNonEmptyString(req.Milestone, req.ID))
		if err != nil {
			return writeConfirmation{}, cache.RecordGraph{}, err
		}
		result, err := s.client.UpdateMilestone(ctx, gitcode.MilestoneWriteRequest{Owner: route.Owner, Repo: route.Name, ID: id, Title: req.Title, Description: req.Description, DueOn: req.DueOn, State: req.State}, opts)
		if err != nil {
			return writeConfirmation{}, cache.RecordGraph{}, err
		}
		confirmation, graph := s.milestoneWriteGraph(route.RepoID, result.Record, result, now)
		return confirmation, graph, nil
	case "trigger-push-mirror":
		result, err := s.client.TriggerPushRemoteMirror(ctx, gitcode.PushMirrorTriggerRequest{Owner: route.Owner, Repo: route.Name, MirrorID: req.ID}, opts)
		if err != nil {
			return writeConfirmation{}, cache.RecordGraph{}, err
		}
		_, graph := pushMirrorRecordGraph(route.RepoID, result.Record, now)
		confirmation := writeConfirmation{
			confirmed:      result.Confirmed,
			remoteID:       result.RemoteID,
			remoteRevision: result.RemoteRevision,
			apiPath:        triggerPushMirrorAPIPath(route.Owner, route.Name, result.RemoteID),
			cachePath:      graph.Record.Path,
			message:        result.ProviderStatus,
			completedAt:    firstNonZeroTime(result.ConfirmedAt, now),
			pushMirror: &WritePushMirrorReceipt{
				MirrorID:       result.RemoteID,
				Status:         "triggered",
				PreviousStatus: req.pushMirrorPreviousStatus,
				ReadbackStatus: result.Record.UpdateStatus,
				TriggeredAt:    firstNonZeroTime(result.ConfirmedAt, now),
			},
		}
		return confirmation, graph, nil
	case "set-issue-milestone":
		milestone, err := s.resolveMilestone(ctx, route, firstNonEmptyString(req.Milestone, req.ID))
		if err != nil {
			return writeConfirmation{}, cache.RecordGraph{}, err
		}
		return s.updateIssueMilestone(ctx, route, req.Number, &milestone, false, opts, now)
	case "clear-issue-milestone":
		return s.updateIssueMilestone(ctx, route, req.Number, nil, true, opts, now)
	case "add-pr-comment":
		result, err := s.client.CreatePRComment(ctx, gitcode.CreatePRCommentRequest{Owner: route.Owner, Repo: route.Name, Number: req.Number, Body: req.Body}, opts)
		if err != nil {
			return writeConfirmation{}, cache.RecordGraph{}, err
		}
		return s.prCommentWriteGraph(ctx, route.RepoID, req.Number, result.Record, result, now)
	case "add-pr-review-comment":
		result, err := s.client.CreatePRReviewComment(ctx, gitcode.CreatePRReviewCommentRequest{Owner: route.Owner, Repo: route.Name, Number: req.Number, Body: req.Body, Path: req.Path, Line: req.Line, StartLine: req.StartLine, EndLine: req.EndLine, Position: req.Position}, opts)
		if err != nil {
			return writeConfirmation{}, cache.RecordGraph{}, err
		}
		return s.prCommentWriteGraphWithParent(ctx, route.RepoID, req.Number, req.parentSourceID, result.Record, result, now)
	case "reply-pr-review-comment":
		result, err := s.client.ReplyPRReviewComment(ctx, gitcode.ReplyPRReviewCommentRequest{Owner: route.Owner, Repo: route.Name, Number: req.Number, DiscussionID: req.DiscussionID, ParentCommentID: req.ParentID, Body: req.Body}, opts)
		if err != nil {
			return writeConfirmation{}, cache.RecordGraph{}, err
		}
		return s.prCommentThreadWriteGraph(ctx, route.RepoID, req.Number, result, now)
	case "link-pr-issue":
		if strings.TrimSpace(req.Strategy) != "description_fallback" {
			result, err := s.client.LinkPRIssue(ctx, gitcode.LinkPRIssueRequest{Owner: route.Owner, Repo: route.Name, Number: req.Number, IssueNumber: req.IssueNumber}, opts)
			if err == nil {
				return s.prIssueLinkWriteGraph(ctx, route.RepoID, req.Number, req.IssueNumber, result, now)
			}
			if !isPRIssueRelationUnsupported(err) {
				return writeConfirmation{}, cache.RecordGraph{}, err
			}
		}
		confirmation, graph, err := s.linkPRIssueDescriptionFallback(ctx, route, req, opts, now)
		if err != nil {
			return writeConfirmation{}, cache.RecordGraph{}, err
		}
		return confirmation, graph, nil
	case "add-label":
		return s.addLabelViaUpdateIssue(ctx, route, req, opts, now)
	case "create-page":
		result, err := s.client.CreateWikiPage(ctx, gitcode.CreateWikiPageRequest{Owner: route.Owner, Repo: route.Name, Path: firstNonEmptyString(req.Path, req.Slug, req.Title), Slug: req.Slug, Title: strings.TrimSpace(req.Title), Body: req.Body}, opts)
		if err != nil {
			return writeConfirmation{}, cache.RecordGraph{}, err
		}
		confirmation, graph := s.wikiWriteGraph(route.RepoID, result.Record, result, now)
		return confirmation, graph, nil
	case "update-page":
		result, err := s.client.UpdateWikiPage(ctx, gitcode.UpdateWikiPageRequest{Owner: route.Owner, Repo: route.Name, Path: firstNonEmptyString(req.Path, req.Slug, req.ID), Slug: firstNonEmptyString(req.Slug, req.ID), Title: req.Title, Body: req.Body, Sha: req.Sha}, opts)
		if err != nil {
			return writeConfirmation{}, cache.RecordGraph{}, err
		}
		confirmation, graph := s.wikiWriteGraph(route.RepoID, result.Record, result, now)
		return confirmation, graph, nil
	case "delete-page":
		result, err := s.client.DeleteWikiPage(ctx, gitcode.DeleteWikiPageRequest{Owner: route.Owner, Repo: route.Name, Path: firstNonEmptyString(req.Path, req.Slug, req.ID), Slug: firstNonEmptyString(req.Slug, req.ID), Sha: req.Sha}, opts)
		if err != nil {
			return writeConfirmation{}, cache.RecordGraph{}, err
		}
		confirmation, graph := s.wikiWriteGraph(route.RepoID, result.Record, result, now)
		return confirmation, graph, nil
	default:
		return writeConfirmation{}, cache.RecordGraph{}, ErrWriteFailure{Code: "write_unsupported_deferred", RepoID: route.RepoID}
	}
}

func (s *Service) addLabelViaUpdateIssue(ctx context.Context, route RepositoryRoute, req WriteCommandRequest, opts gitcode.WriteOptions, now time.Time) (writeConfirmation, cache.RecordGraph, error) {
	label := strings.TrimSpace(req.Label)
	issue, err := s.client.GetIssue(ctx, gitcode.IssueRequest{Owner: route.Owner, Repo: route.Name, Number: req.Number})
	if err != nil {
		return writeConfirmation{}, cache.RecordGraph{}, err
	}
	labels, changed := mergeIssueLabel(issue.Labels, label)
	if !changed {
		result := gitcode.WriteResult[gitcode.Issue]{
			Record:       issue,
			Confirmed:    true,
			Operation:    "AddLabelNoop",
			RemoteID:     issue.ID,
			RemoteNumber: issue.Number,
			ConfirmedAt:  now,
		}
		confirmation, graph := s.issueWriteGraph(route.RepoID, issue, result, now)
		return confirmation, graph, nil
	}
	result, err := s.client.UpdateIssue(ctx, gitcode.UpdateIssueRequest{Owner: route.Owner, Repo: route.Name, Number: req.Number, Labels: gitcode.EncodeIssueLabels(labels)}, opts)
	if err != nil {
		return writeConfirmation{}, cache.RecordGraph{}, err
	}
	confirmation, graph := s.issueWriteGraph(route.RepoID, result.Record, result, now)
	return confirmation, graph, nil
}

func mergeIssueLabel(existing []string, label string) ([]string, bool) {
	label = strings.TrimSpace(label)
	labels := make([]string, 0, len(existing)+1)
	found := false
	for _, current := range existing {
		trimmed := strings.TrimSpace(current)
		if trimmed == "" {
			continue
		}
		if trimmed == label {
			found = true
		}
		labels = append(labels, trimmed)
	}
	if found {
		return labels, false
	}
	labels = append(labels, label)
	return labels, true
}

func (s *Service) resolveMilestone(ctx context.Context, route RepositoryRoute, milestone string) (gitcode.Milestone, error) {
	trimmed := strings.TrimSpace(milestone)
	if trimmed == "" {
		return gitcode.Milestone{}, ErrInvalidQuery{Field: "milestone", Message: "milestone id or title is required"}
	}
	normalized := strings.TrimPrefix(strings.ToUpper(trimmed), "MILESTONE-")
	if id, err := strconv.Atoi(normalized); err == nil && id > 0 {
		resolved, err := s.client.GetMilestone(ctx, gitcode.MilestoneRequest{Owner: route.Owner, Repo: route.Name, ID: id})
		if err != nil {
			return gitcode.Milestone{}, err
		}
		if resolved.RemoteID != strconv.Itoa(id) {
			return gitcode.Milestone{}, ErrInvalidQuery{Field: "milestone", Message: "milestone readback id does not match requested repository milestone"}
		}
		return resolved, nil
	}
	page, err := s.client.ListMilestones(ctx, gitcode.MilestoneListRequest{Owner: route.Owner, Repo: route.Name, PerPage: 100})
	if err != nil {
		return gitcode.Milestone{}, err
	}
	var match *gitcode.Milestone
	for idx := range page.Items {
		if strings.EqualFold(strings.TrimSpace(page.Items[idx].Title), trimmed) {
			if match != nil {
				return gitcode.Milestone{}, ErrInvalidQuery{Field: "milestone", Message: "milestone title is ambiguous; use numeric id"}
			}
			match = &page.Items[idx]
		}
	}
	if match == nil {
		return gitcode.Milestone{}, ErrInvalidQuery{Field: "milestone", Message: "milestone not found by id or title"}
	}
	id, err := strconv.Atoi(match.RemoteID)
	if err != nil || id <= 0 {
		return gitcode.Milestone{}, ErrInvalidQuery{Field: "milestone", Message: "milestone id is invalid"}
	}
	return *match, nil
}

func (s *Service) resolveMilestoneID(ctx context.Context, route RepositoryRoute, milestone string) (int, error) {
	resolved, err := s.resolveMilestone(ctx, route, milestone)
	if err != nil {
		return 0, err
	}
	id, err := strconv.Atoi(resolved.RemoteID)
	if err != nil || id <= 0 {
		return 0, ErrInvalidQuery{Field: "milestone", Message: "milestone id is invalid"}
	}
	return id, nil
}

func (s *Service) confirmIssueMilestone(ctx context.Context, route RepositoryRoute, result gitcode.WriteResult[gitcode.Issue], milestone *gitcode.Milestone, clear bool) (gitcode.WriteResult[gitcode.Issue], *WriteMilestoneReceipt, error) {
	number := firstNonZeroInt(result.Record.Number, result.RemoteNumber)
	if number <= 0 {
		return gitcode.WriteResult[gitcode.Issue]{}, nil, ErrWriteFailure{Code: "write_readback_mismatch", RepoID: route.RepoID, RemoteID: result.RemoteID}
	}
	readback, err := s.client.GetIssue(ctx, gitcode.IssueRequest{Owner: route.Owner, Repo: route.Name, Number: number})
	if err != nil {
		return gitcode.WriteResult[gitcode.Issue]{}, nil, err
	}
	if clear {
		if readback.Milestone != nil {
			return gitcode.WriteResult[gitcode.Issue]{}, nil, ErrWriteFailure{Code: "write_readback_mismatch", RepoID: route.RepoID, RemoteID: strconv.Itoa(number)}
		}
		result.Record = readback
		result.RemoteID = firstNonEmptyString(result.RemoteID, readback.ID, strconv.Itoa(readback.Number))
		result.RemoteNumber = firstNonZeroInt(result.RemoteNumber, readback.Number)
		result.RemoteRevision = firstNonEmptyString(result.RemoteRevision, result.ResponseHash, readback.UpdatedAt.UTC().Format(time.RFC3339Nano))
		return result, &WriteMilestoneReceipt{Cleared: true}, nil
	}
	if milestone == nil || readback.Milestone == nil || readback.Milestone.RemoteID != milestone.RemoteID {
		return gitcode.WriteResult[gitcode.Issue]{}, nil, ErrWriteFailure{Code: "write_readback_mismatch", RepoID: route.RepoID, RemoteID: strconv.Itoa(number)}
	}
	resolved := readback.Milestone
	if strings.TrimSpace(resolved.Title) == "" {
		resolved = milestone
	}
	result.Record = readback
	result.RemoteID = firstNonEmptyString(result.RemoteID, readback.ID, strconv.Itoa(readback.Number))
	result.RemoteNumber = firstNonZeroInt(result.RemoteNumber, readback.Number)
	result.RemoteRevision = firstNonEmptyString(result.RemoteRevision, result.ResponseHash, readback.UpdatedAt.UTC().Format(time.RFC3339Nano))
	return result, milestoneWriteReceipt(resolved, false), nil
}

func (s *Service) updateIssueMilestone(ctx context.Context, route RepositoryRoute, number int, milestone *gitcode.Milestone, clear bool, opts gitcode.WriteOptions, now time.Time) (writeConfirmation, cache.RecordGraph, error) {
	raw := "null"
	if milestone != nil {
		raw = milestone.RemoteID
	}
	encoded := gitcode.EncodeIssueMilestone(raw)
	if len(encoded) == 0 {
		return writeConfirmation{}, cache.RecordGraph{}, ErrInvalidQuery{Field: "milestone", Message: "milestone id must be positive or null"}
	}
	result, err := s.client.UpdateIssue(ctx, gitcode.UpdateIssueRequest{Owner: route.Owner, Repo: route.Name, Number: number, Milestone: encoded}, opts)
	if err != nil {
		return writeConfirmation{}, cache.RecordGraph{}, err
	}
	result, receipt, err := s.confirmIssueMilestone(ctx, route, result, milestone, clear)
	if err != nil {
		return writeConfirmation{}, cache.RecordGraph{}, err
	}
	if clear {
		result.Operation = "ClearIssueMilestone"
	} else {
		result.Operation = "SetIssueMilestone"
	}
	confirmation, graph := s.issueWriteGraph(route.RepoID, result.Record, result, now)
	confirmation.milestone = receipt
	return confirmation, graph, nil
}

func milestoneWriteReceipt(milestone *gitcode.Milestone, cleared bool) *WriteMilestoneReceipt {
	if cleared {
		return &WriteMilestoneReceipt{Cleared: true}
	}
	if milestone == nil {
		return nil
	}
	return &WriteMilestoneReceipt{
		ID:       firstNonEmptyString(milestone.SourceID, fallbackSourceID("milestone", milestone.RemoteID)),
		RemoteID: milestone.RemoteID,
		Title:    milestone.Title,
	}
}

func (s *Service) canonicalizeIssueWriteTarget(ctx context.Context, command, repoID string, req *WriteCommandRequest) error {
	var number int
	var selector string
	consumedLegacyID := false
	switch command {
	case "update-issue", "add-comment", "add-label":
		number, selector = req.Number, firstNonEmptyString(req.IssueID, req.ID)
		consumedLegacyID = strings.TrimSpace(req.IssueID) == "" && strings.TrimSpace(req.ID) != ""
	case "update-comment":
		if req.Number == 0 && strings.TrimSpace(req.IssueID) == "" {
			return nil
		}
		number, selector = req.Number, req.IssueID
	case "set-issue-milestone":
		number, selector = req.Number, req.IssueID
	case "clear-issue-milestone":
		number, selector = req.Number, firstNonEmptyString(req.IssueID, req.ID)
		consumedLegacyID = strings.TrimSpace(req.IssueID) == "" && strings.TrimSpace(req.ID) != ""
	case "link-pr-issue":
		number, selector = req.IssueNumber, req.IssueID
	default:
		return nil
	}
	resolvedNumber, stableID, err := s.resolveIssueWriteTarget(ctx, repoID, number, selector)
	if err != nil {
		return err
	}
	if command == "link-pr-issue" {
		req.IssueNumber = resolvedNumber
	} else {
		req.Number = resolvedNumber
	}
	req.IssueID = stableID
	if consumedLegacyID {
		// The deprecated --id selector is semantically identical to issue_id.
		// Clear it after canonicalization so both forms share one fingerprint.
		req.ID = ""
	}
	return nil
}

func (s *Service) resolveIssueWriteTarget(ctx context.Context, repoID string, number int, selector string) (int, string, error) {
	selector = strings.TrimSpace(selector)
	if selector != "" {
		stableID, err := s.resolveScopedStableID(ctx, repoID, selector, "", "")
		if err != nil {
			if IsNotFound(err) {
				return 0, "", ErrInvalidQuery{Field: "issue_id", Message: fmt.Sprintf("issue_id %q is not cached in repo %q; sync the issue first with sync_live or CLI sync --issues --input issue:N, then retry the same idempotency key", selector, repoID)}
			}
			return 0, "", err
		}
		source, err := s.store.GetSourceScoped(ctx, repoID, stableID)
		if err != nil {
			return 0, "", normalizeError(err, "source", stableID)
		}
		if source.Kind != "issue" && source.Kind != "issues" {
			return 0, "", ErrInvalidQuery{Field: "issue_id", Message: fmt.Sprintf("%q resolves to %s %q, not an issue", selector, source.Kind, source.ID)}
		}
		_, resolvedNumber := sourceIssueIdentity(source)
		if resolvedNumber == 0 {
			return 0, "", ErrInvalidQuery{Field: "issue_id", Message: fmt.Sprintf("%q resolves to %q but the cache has no repository-local issue number; sync issue details before writing", selector, source.ID)}
		}
		if number > 0 && number != resolvedNumber {
			return 0, "", ErrInvalidQuery{Field: "issue", Message: fmt.Sprintf("number %d and issue_id %q resolve to different issues; issue_id maps to issue_number %d and stable_source_id %q", number, selector, resolvedNumber, source.ID)}
		}
		return resolvedNumber, source.ID, nil
	}
	if number <= 0 {
		return 0, "", ErrInvalidQuery{Field: "issue", Message: "positive issue number or issue_id is required"}
	}
	if source, ok, err := s.issueSourceForNumber(ctx, repoID, number); err != nil {
		return 0, "", err
	} else if ok {
		return number, source.ID, nil
	}
	if source, ok, err := s.issueSourceForProviderID(ctx, repoID, strconv.Itoa(number)); err != nil {
		return 0, "", err
	} else if ok {
		_, actual := sourceIssueIdentity(source)
		if actual > 0 && actual != number {
			return 0, "", ErrInvalidQuery{Field: "number", Message: fmt.Sprintf("%d is a cached GitCode provider id, not the repository-local issue number; use number %d or issue_id %q", number, actual, source.ID)}
		}
	}
	if source, err := s.store.GetSourceScoped(ctx, repoID, "ISSUE-"+strconv.Itoa(number)); err == nil {
		_, actual := sourceIssueIdentity(source)
		if actual > 0 && actual != number {
			return 0, "", ErrInvalidQuery{Field: "number", Message: fmt.Sprintf("%d is the numeric part of stable_source_id %q, not the repository-local issue number; use number %d or issue_id %q", number, source.ID, actual, source.ID)}
		}
	} else if !isCacheNotFound(err) {
		return 0, "", normalizeError(err, "source", "ISSUE-"+strconv.Itoa(number))
	}
	return number, "", nil
}

func (s *Service) issueSourceForProviderID(ctx context.Context, repoID, providerID string) (cache.Source, bool, error) {
	identity, err := s.store.ResolveAliasScoped(ctx, repoID, cache.RemoteAlias{Type: "gitcode_issue_id", ID: providerID})
	if err != nil {
		if isCacheNotFound(err) {
			return cache.Source{}, false, nil
		}
		return cache.Source{}, false, err
	}
	source, err := s.store.GetSourceScoped(ctx, repoID, identity.SourceID)
	if err != nil {
		return cache.Source{}, false, normalizeError(err, "source", identity.SourceID)
	}
	return source, source.Kind == "issue" || source.Kind == "issues", nil
}

func (s *Service) issueSourceForNumber(ctx context.Context, repoID string, number int) (cache.Source, bool, error) {
	identity, err := s.store.ResolveAliasScoped(ctx, repoID, cache.RemoteAlias{Type: "issue", ID: strconv.Itoa(number)})
	if err != nil {
		if isCacheNotFound(err) {
			return cache.Source{}, false, nil
		}
		return cache.Source{}, false, err
	}
	source, err := s.store.GetSourceScoped(ctx, repoID, identity.SourceID)
	if err != nil {
		return cache.Source{}, false, normalizeError(err, "source", identity.SourceID)
	}
	return source, source.Kind == "issue" || source.Kind == "issues", nil
}

func (s *Service) replayWriteGraph(ctx context.Context, command string, repoID string, req WriteCommandRequest, prior cache.AuditTrailEntry) (cache.RecordGraph, error) {
	now := s.now().UTC()
	switch command {
	case "create-issue", "update-issue":
		route, err := s.BuildAdapterRoute(ctx, repoID, RepositoryScopeIssues)
		if err != nil {
			return cache.RecordGraph{}, err
		}
		number := req.Number
		if command == "create-issue" {
			number, _ = strconv.Atoi(prior.RequestMetadata["remote_number"])
			if number <= 0 || prior.RequestMetadata["provider_id"] == "" {
				return cache.RecordGraph{}, ErrWriteFailure{Code: "write_partial_cache_refresh_failed", RepoID: repoID, RemoteID: prior.RemoteID, IdempotencyKey: prior.IdempotencyKey, Cause: errors.New("creation receipt lacks confirmed local number or provider identity")}
			}
		}
		if number == 0 {
			number, _ = strconv.Atoi(prior.RemoteID)
		}
		issue, err := s.client.GetIssue(ctx, gitcode.IssueRequest{Owner: route.Owner, Repo: route.Name, Number: number})
		if err != nil {
			return cache.RecordGraph{}, ErrWriteFailure{Code: "write_partial_cache_refresh_failed", RepoID: repoID, RemoteID: prior.RemoteID, IdempotencyKey: prior.IdempotencyKey, Cause: err}
		}
		if command == "create-issue" && (issue.Number != number || prior.RequestMetadata["provider_id"] == "" || issue.ID != prior.RequestMetadata["provider_id"]) {
			return cache.RecordGraph{}, ErrWriteFailure{Code: "write_partial_cache_refresh_failed", RepoID: repoID, RemoteID: prior.RemoteID, IdempotencyKey: prior.IdempotencyKey, Cause: errors.New("created issue readback identity mismatch")}
		}
		remoteID := firstNonEmptyString(issue.ID, prior.RemoteID, strconv.Itoa(issue.Number))
		result := gitcode.WriteResult[gitcode.Issue]{Record: issue, Confirmed: true, Operation: "UpdateIssuePartialReplayReadback", RemoteID: remoteID, RemoteNumber: issue.Number, ConfirmedAt: now}
		_, graph := s.issueWriteGraph(repoID, issue, result, now)
		return graph, nil
	case "add-label", "set-issue-milestone", "clear-issue-milestone":
		number, _ := strconv.Atoi(prior.RemoteID)
		issue := gitcode.Issue{ID: prior.RemoteID, Number: number, Title: strings.TrimSpace(req.Title), Body: req.Body, State: firstNonEmptyString(req.State, "open"), CreatedAt: now, UpdatedAt: now}
		if receipt := milestoneReceiptFromAudit(prior); receipt != nil && !receipt.Cleared {
			issue.Milestone = &gitcode.Milestone{RemoteID: receipt.RemoteID, SourceID: receipt.ID, Title: receipt.Title}
		}
		if issue.Title == "" {
			issue.Title = "Issue " + prior.RemoteID
		}
		if command == "add-label" && strings.TrimSpace(req.Label) != "" {
			issue.Labels = append(issue.Labels, strings.TrimSpace(req.Label))
		}
		result := gitcode.WriteResult[gitcode.Issue]{Record: issue, Confirmed: true, RemoteID: prior.RemoteID, RemoteNumber: number, RemoteRevision: firstNonEmptyString(prior.Message, prior.PayloadHash), ConfirmedAt: now}
		_, graph := s.issueWriteGraph(repoID, issue, result, now)
		return graph, nil
	case "create-milestone", "update-milestone":
		milestone := gitcode.Milestone{RemoteID: prior.RemoteID, SourceID: fallbackSourceID("milestone", prior.RemoteID), Title: req.Title, Body: req.Description, Status: firstNonEmptyString(req.State, "open"), DueOn: req.DueOn, UpdatedAt: now.Format(time.RFC3339)}
		if milestone.Title == "" {
			milestone.Title = firstNonEmptyString(req.Milestone, req.ID, "Milestone "+prior.RemoteID)
		}
		result := gitcode.WriteResult[gitcode.Milestone]{Record: milestone, Confirmed: true, RemoteID: prior.RemoteID, RemoteRevision: firstNonEmptyString(prior.Message, prior.PayloadHash), ConfirmedAt: now}
		_, graph := s.milestoneWriteGraph(repoID, milestone, result, now)
		return graph, nil
	case "create-pr", "update-pr":
		route, err := s.BuildAdapterRoute(ctx, repoID, RepositoryScopeIssues)
		if err != nil {
			return cache.RecordGraph{}, err
		}
		number := req.Number
		if command == "create-pr" {
			number, _ = strconv.Atoi(prior.RequestMetadata["remote_number"])
			if number <= 0 || prior.RequestMetadata["provider_id"] == "" {
				return cache.RecordGraph{}, ErrWriteFailure{Code: "write_partial_cache_refresh_failed", RepoID: repoID, RemoteID: prior.RemoteID, IdempotencyKey: prior.IdempotencyKey, Cause: errors.New("creation receipt lacks confirmed local number or provider identity")}
			}
		}
		if number == 0 {
			number, _ = strconv.Atoi(prior.RemoteID)
		}
		pr, err := s.client.GetPR(ctx, gitcode.PRRequest{Owner: route.Owner, Repo: route.Name, Number: number})
		if err != nil {
			return cache.RecordGraph{}, ErrWriteFailure{Code: "write_partial_cache_refresh_failed", RepoID: repoID, RemoteID: prior.RemoteID, IdempotencyKey: prior.IdempotencyKey, Cause: err}
		}
		if command == "create-pr" && (pr.Number != number || prior.RequestMetadata["provider_id"] == "" || pr.ID != prior.RequestMetadata["provider_id"]) {
			return cache.RecordGraph{}, ErrWriteFailure{Code: "write_partial_cache_refresh_failed", RepoID: repoID, RemoteID: prior.RemoteID, IdempotencyKey: prior.IdempotencyKey, Cause: errors.New("created pull request readback identity mismatch")}
		}
		result := gitcode.WriteResult[gitcode.PullRequest]{Record: pr, Confirmed: true, Operation: "UpdatePRPartialReplayReadback", RemoteID: pr.ID, RemoteNumber: pr.Number, ConfirmedAt: now}
		_, graph, err := s.pullRequestWriteGraph(ctx, repoID, pr, result, now)
		return graph, err
	case "trigger-push-mirror":
		route, err := s.BuildAdapterRoute(ctx, repoID, RepositoryScopeIssues)
		if err != nil {
			return cache.RecordGraph{}, err
		}
		mirrors, err := s.client.ListPushRemoteMirrors(ctx, gitcode.PushMirrorListRequest{Owner: route.Owner, Repo: route.Name})
		if err != nil {
			return cache.RecordGraph{}, err
		}
		mirror, err := resolvePushMirror(mirrors, prior.RemoteID)
		if err != nil {
			return cache.RecordGraph{}, err
		}
		_, graph := pushMirrorRecordGraph(repoID, mirror, now)
		return graph, nil
	case "add-comment", "update-comment":
		number := req.Number
		if number == 0 {
			number, _ = strconv.Atoi(prior.RecordID)
		}
		commentID := firstNonEmptyString(req.CommentID, req.ID, prior.RemoteID)
		comment := gitcode.Comment{ID: commentID, Body: req.Body, CreatedAt: now, UpdatedAt: now}
		result := gitcode.WriteResult[gitcode.Comment]{Record: comment, Confirmed: true, RemoteID: commentID, ParentIssueNumber: number, RemoteRevision: firstNonEmptyString(prior.Message, prior.PayloadHash), ConfirmedAt: now}
		_, graph, err := s.commentWriteGraph(ctx, repoID, number, req.IssueID, comment, result, now)
		return graph, err
	case "merge-pr", "link-pr-issue":
		number := req.Number
		if number == 0 {
			number, _ = strconv.Atoi(prior.RemoteID)
		}
		state := firstNonEmptyString(req.State, "open")
		if command == "merge-pr" {
			state = "merged"
		}
		pr := gitcode.PullRequest{ID: prior.RemoteID, Number: number, Title: req.Title, Body: req.Body, State: state, Base: req.Base, Head: req.Head, CreatedAt: now, UpdatedAt: now}
		if pr.Title == "" {
			pr.Title = "Pull request " + strconv.Itoa(number)
		}
		result := gitcode.WriteResult[gitcode.PullRequest]{Record: pr, Confirmed: true, RemoteID: prior.RemoteID, RemoteNumber: number, RemoteRevision: firstNonEmptyString(prior.Message, prior.PayloadHash), ConfirmedAt: now}
		_, graph, err := s.pullRequestWriteGraph(ctx, repoID, pr, result, now)
		return graph, err
	case "add-pr-comment":
		number := req.Number
		comment := gitcode.PRComment{ID: prior.RemoteID, Body: req.Body, PRNumber: number, CreatedAt: now, UpdatedAt: now}
		result := gitcode.WriteResult[gitcode.PRComment]{Record: comment, Confirmed: true, RemoteID: prior.RemoteID, ParentIssueNumber: number, ParentIssueID: strconv.Itoa(number), RemoteRevision: firstNonEmptyString(prior.Message, prior.PayloadHash), ConfirmedAt: now}
		_, graph, err := s.prCommentWriteGraph(ctx, repoID, number, comment, result, now)
		return graph, err
	case "add-pr-review-comment":
		number := req.Number
		comment := gitcode.PRComment{ID: prior.RemoteID, Body: req.Body, PRNumber: number, ReviewKind: "inline", Path: req.Path, Line: req.Line, StartLine: req.StartLine, EndLine: req.EndLine, Position: req.Position, CreatedAt: now, UpdatedAt: now}
		result := gitcode.WriteResult[gitcode.PRComment]{Record: comment, Confirmed: true, RemoteID: prior.RemoteID, ParentIssueNumber: number, ParentIssueID: strconv.Itoa(number), RemoteRevision: firstNonEmptyString(prior.Message, prior.PayloadHash), ConfirmedAt: now}
		_, graph, err := s.prCommentWriteGraphWithParent(ctx, repoID, number, req.parentSourceID, comment, result, now)
		return graph, err
	case "reply-pr-review-comment":
		number := req.Number
		discussionID := firstNonEmptyString(prior.RequestMetadata["pr_discussion_id"], req.DiscussionID)
		comment := gitcode.PRComment{ID: prior.RemoteID, Body: req.Body, PRNumber: number, DiscussionID: discussionID, ParentID: req.ParentID, ReviewKind: "inline", CreatedAt: now, UpdatedAt: now}
		result := gitcode.WriteResult[gitcode.PRComment]{Record: comment, Confirmed: true, RemoteID: prior.RemoteID, ParentIssueNumber: number, ParentIssueID: discussionID, RemoteRevision: firstNonEmptyString(prior.Message, prior.PayloadHash), ConfirmedAt: now}
		_, graph, err := s.prCommentWriteGraph(ctx, repoID, number, comment, result, now)
		return graph, err
	case "create-page", "update-page", "delete-page":
		page := gitcode.WikiPage{ID: prior.RemoteID, Slug: firstNonEmptyString(req.Path, req.Slug, req.ID, prior.RemoteID), Title: req.Title, Body: req.Body, Revision: firstNonEmptyString(prior.Message, prior.PayloadHash), CreatedAt: now, UpdatedAt: now}
		if page.Title == "" {
			page.Title = page.Slug
		}
		result := gitcode.WriteResult[gitcode.WikiPage]{Record: page, Confirmed: true, RemoteID: prior.RemoteID, RemoteSlug: page.Slug, RemoteRevision: page.Revision, ConfirmedAt: now}
		_, graph := s.wikiWriteGraph(repoID, page, result, now)
		return graph, nil
	default:
		return cache.RecordGraph{}, ErrWriteFailure{Code: "write_unsupported_deferred", RepoID: repoID, RemoteID: prior.RemoteID, IdempotencyKey: prior.IdempotencyKey}
	}
}

func issueUpdatePreimageMetadata(issue gitcode.Issue) map[string]string {
	labels := append([]string(nil), issue.Labels...)
	sort.Strings(labels)
	milestone := ""
	if issue.Milestone != nil {
		milestone = firstNonEmptyString(issue.Milestone.RemoteID, issue.Milestone.SourceID, issue.Milestone.Title)
	}
	metadata := map[string]string{
		"issue_preimage_title_hash":     hashWriteInvariant(issue.Title),
		"issue_preimage_body_hash":      hashWriteInvariant(issue.Body),
		"issue_preimage_state_hash":     hashWriteInvariant(strings.TrimSpace(issue.State)),
		"issue_preimage_labels_hash":    hashWriteInvariant(strings.Join(labels, "\x00")),
		"issue_preimage_milestone_hash": hashWriteInvariant(milestone),
	}
	metadata["issue_preimage_fingerprint"] = hashWriteInvariant(strings.Join([]string{
		metadata["issue_preimage_title_hash"],
		metadata["issue_preimage_body_hash"],
		metadata["issue_preimage_state_hash"],
		metadata["issue_preimage_labels_hash"],
		metadata["issue_preimage_milestone_hash"],
	}, "\x00"))
	return metadata
}

func prUpdatePreimageMetadata(pr gitcode.PullRequest) map[string]string {
	metadata := map[string]string{
		"pr_update_preimage_title_hash": hashWriteInvariant(pr.Title),
		"pr_update_preimage_body_hash":  hashWriteInvariant(pr.Body),
		"pr_update_preimage_state_hash": hashWriteInvariant(strings.TrimSpace(pr.State)),
	}
	metadata["pr_update_preimage_fingerprint"] = hashWriteInvariant(strings.Join([]string{
		metadata["pr_update_preimage_title_hash"],
		metadata["pr_update_preimage_body_hash"],
		metadata["pr_update_preimage_state_hash"],
	}, "\x00"))
	return metadata
}

func hashWriteInvariant(value string) string {
	sum := sha256.Sum256([]byte(value))
	return hex.EncodeToString(sum[:])
}

func cloneStringMap(in map[string]string) map[string]string {
	out := make(map[string]string, len(in)+1)
	for key, value := range in {
		out[key] = value
	}
	return out
}

func (s *Service) transitionAuditGeneration(ctx context.Context, transitioner auditGenerationClaimStore, entry cache.AuditTrailEntry, generation time.Time, expectedStatuses ...string) (bool, *cache.AuditTrailEntry, error) {
	for _, expectedStatus := range expectedStatuses {
		transitioned, err := transitioner.TransitionAuditEventGeneration(ctx, entry, generation, expectedStatus)
		if err != nil {
			return false, nil, err
		}
		if transitioned {
			current := entry
			current.CreatedAt = generation
			return true, &current, nil
		}
	}
	current, err := s.store.GetAuditEventByKey(ctx, entry.RepoID, entry.IdempotencyKey)
	if err != nil {
		return false, nil, err
	}
	if current != nil && current.CreatedAt.Equal(generation) && current.PayloadHash == entry.PayloadHash && current.Status == entry.Status {
		return true, current, nil
	}
	return false, current, nil
}

func auditGenerationSucceeded(current *cache.AuditTrailEntry, generation time.Time, payloadHash string) bool {
	return current != nil && current.CreatedAt.Equal(generation) && current.PayloadHash == payloadHash && current.Status == audit.StatusSucceeded
}

func mergePRPreimageMetadata(pr gitcode.PullRequest) map[string]string {
	return map[string]string{
		"merge_preimage_head_sha_hash": hashWriteInvariant(strings.ToLower(strings.TrimSpace(pr.HeadSHA))),
		"merge_preimage_state_hash":    hashWriteInvariant(strings.ToLower(strings.TrimSpace(pr.State))),
	}
}

func (s *Service) recoverAmbiguousMergePR(ctx context.Context, route RepositoryRoute, req WriteCommandRequest, prior cache.AuditTrailEntry, fingerprint string) (WriteCommandResult, bool, error) {
	pr, err := s.client.GetPR(ctx, gitcode.PRRequest{Owner: route.Owner, Repo: route.Name, Number: req.Number})
	if err != nil {
		return WriteCommandResult{}, false, ErrWriteFailure{Code: "write_ambiguous_readback_failed", RepoID: route.RepoID, RemoteID: prior.RemoteID, IdempotencyKey: prior.IdempotencyKey, Cause: err}
	}
	if strings.TrimSpace(pr.ID) == "" || pr.Number != req.Number || !strings.EqualFold(strings.TrimSpace(pr.State), "merged") {
		return WriteCommandResult{}, false, nil
	}
	requestedHead := strings.TrimSpace(req.Sha)
	if requestedHead != "" && !strings.EqualFold(requestedHead, strings.TrimSpace(pr.HeadSHA)) {
		return WriteCommandResult{}, false, nil
	}
	expectedHeadHash := strings.TrimSpace(prior.RequestMetadata["merge_preimage_head_sha_hash"])
	actualHeadHash := mergePRPreimageMetadata(pr)["merge_preimage_head_sha_hash"]
	if expectedHeadHash == "" || expectedHeadHash != actualHeadHash {
		return WriteCommandResult{}, false, nil
	}

	now := s.now().UTC()
	providerResult := gitcode.WriteResult[gitcode.PullRequest]{Record: pr, Confirmed: true, Operation: "MergePRRecoveryReadback", RemoteID: pr.ID, RemoteNumber: pr.Number, ProviderStatus: "readback-recovered", IdempotencyKey: prior.IdempotencyKey, ConfirmedAt: now}
	confirmation, graph, err := s.pullRequestWriteGraph(ctx, route.RepoID, pr, providerResult, now)
	if err != nil {
		return WriteCommandResult{}, false, err
	}
	metadata := cloneStringMap(prior.RequestMetadata)
	metadata["write_phase"] = "recovered_by_canonical_readback"
	completed := audit.WithRequestMetadata(audit.Success(route.RepoID, prior.IdempotencyKey, "merge-pr", graph.Record.ID, graph.Record.RemoteType, confirmation.remoteID, fingerprint, "recovered_by_canonical_readback", now), metadata)
	pending := audit.WithRequestMetadata(audit.RemoteConfirmedCacheRefreshPending(route.RepoID, prior.IdempotencyKey, "merge-pr", graph.Record.ID, graph.Record.RemoteType, confirmation.remoteID, fingerprint, "canonical merge readback confirmed; cache refresh pending", now), metadata)
	transitioner, ok := s.store.(auditGenerationClaimStore)
	if !ok {
		return WriteCommandResult{}, false, ErrWriteFailure{Code: "write_audit_start_failed", RepoID: route.RepoID, RemoteID: confirmation.remoteID, IdempotencyKey: prior.IdempotencyKey, Cause: errors.New("atomic audit generation transition support is unavailable")}
	}
	settling, current, err := s.transitionAuditGeneration(ctx, transitioner, pending, prior.CreatedAt, prior.Status, audit.StatusRemoteConfirmedCacheRefreshFailed)
	if err != nil {
		return WriteCommandResult{}, false, ErrWriteFailure{Code: "write_partial_remote_confirmed_audit_failed", RepoID: route.RepoID, RemoteID: confirmation.remoteID, IdempotencyKey: prior.IdempotencyKey, Cause: err}
	}
	if !settling {
		if auditGenerationSucceeded(current, prior.CreatedAt, fingerprint) {
			return replayWriteResult("merge-pr", req, *current, fingerprint, now), true, nil
		}
		return WriteCommandResult{}, false, ErrWriteFailure{Code: "write_idempotency_in_progress", RepoID: route.RepoID, RemoteID: confirmation.remoteID, IdempotencyKey: prior.IdempotencyKey}
	}
	if err := s.store.UpsertRecordGraph(ctx, graph); err != nil {
		partial := audit.WithRequestMetadata(audit.RemoteConfirmedCacheRefreshFailed(route.RepoID, prior.IdempotencyKey, "merge-pr", graph.Record.ID, graph.Record.RemoteType, confirmation.remoteID, fingerprint, err.Error(), now), metadata)
		_, current, _ := s.transitionAuditGeneration(ctx, transitioner, partial, prior.CreatedAt, audit.StatusRemoteConfirmedCacheRefreshPending)
		if auditGenerationSucceeded(current, prior.CreatedAt, fingerprint) {
			return replayWriteResult("merge-pr", req, *current, fingerprint, now), true, nil
		}
		return WriteCommandResult{}, false, ErrWriteFailure{Code: "write_partial_cache_refresh_failed", RepoID: route.RepoID, RemoteID: confirmation.remoteID, IdempotencyKey: prior.IdempotencyKey, Cause: err}
	}
	if err := s.recordCacheConfirmation(ctx, "merge-pr", route.RepoID, prior.IdempotencyKey, fingerprint, graph, confirmation.remoteID, "succeeded", now); err != nil {
		partial := audit.WithRequestMetadata(audit.RemoteConfirmedCacheRefreshFailed(route.RepoID, prior.IdempotencyKey, "merge-pr", graph.Record.ID, graph.Record.RemoteType, confirmation.remoteID, fingerprint, err.Error(), now), metadata)
		_, current, _ := s.transitionAuditGeneration(ctx, transitioner, partial, prior.CreatedAt, audit.StatusRemoteConfirmedCacheRefreshPending)
		if auditGenerationSucceeded(current, prior.CreatedAt, fingerprint) {
			return replayWriteResult("merge-pr", req, *current, fingerprint, now), true, nil
		}
		return WriteCommandResult{}, false, ErrWriteFailure{Code: "write_partial_cache_refresh_failed", RepoID: route.RepoID, RemoteID: confirmation.remoteID, IdempotencyKey: prior.IdempotencyKey, Cause: err}
	}
	settled, _, err := s.transitionAuditGeneration(ctx, transitioner, completed, prior.CreatedAt, audit.StatusRemoteConfirmedCacheRefreshPending, audit.StatusRemoteConfirmedCacheRefreshFailed)
	if err != nil || !settled {
		if err == nil {
			err = errors.New("merge recovery could not settle the audit generation")
		}
		partial := audit.WithRequestMetadata(audit.RemoteConfirmedAuditFailed(route.RepoID, prior.IdempotencyKey, "merge-pr", graph.Record.ID, graph.Record.RemoteType, confirmation.remoteID, fingerprint, err.Error(), now), metadata)
		_, _, _ = s.transitionAuditGeneration(ctx, transitioner, partial, prior.CreatedAt, audit.StatusRemoteConfirmedCacheRefreshPending)
		return WriteCommandResult{}, false, ErrWriteFailure{Code: "write_partial_remote_confirmed_audit_failed", RepoID: route.RepoID, RemoteID: confirmation.remoteID, IdempotencyKey: prior.IdempotencyKey, Cause: err}
	}
	result := replayWriteResult("merge-pr", req, completed, fingerprint, now)
	if prior.Status == audit.StatusInProgress {
		result.Status = "recovered_after_ambiguous_write"
	} else {
		result.Status = "succeeded"
	}
	result.ID = graph.Record.ID
	result.RemoteID = confirmation.remoteID
	result.RemoteNumber = confirmation.remoteNumber
	result.RemoteRevision = confirmation.remoteRevision
	result.Replayed = true
	result.Evidence = "canonical merged-state readback matched the claimed head; no second PUT issued"
	return result, true, nil
}

func (s *Service) recoverAmbiguousPRUpdate(ctx context.Context, route RepositoryRoute, req WriteCommandRequest, prior cache.AuditTrailEntry, fingerprint string) (WriteCommandResult, bool, error) {
	pr, err := s.client.GetPR(ctx, gitcode.PRRequest{Owner: route.Owner, Repo: route.Name, Number: req.Number})
	if err != nil {
		return WriteCommandResult{}, false, ErrWriteFailure{Code: "write_ambiguous_readback_failed", RepoID: route.RepoID, RemoteID: prior.RemoteID, IdempotencyKey: prior.IdempotencyKey, Cause: err}
	}
	if strings.TrimSpace(pr.ID) == "" || pr.Number != req.Number || !prUpdateRecoveryMatches(req, pr, prior.RequestMetadata) {
		return WriteCommandResult{}, false, nil
	}
	now := s.now().UTC()
	providerResult := gitcode.WriteResult[gitcode.PullRequest]{Record: pr, Confirmed: true, Operation: "UpdatePRRecoveryReadback", RemoteID: pr.ID, RemoteNumber: pr.Number, ProviderStatus: "readback-recovered", IdempotencyKey: prior.IdempotencyKey, ConfirmedAt: now}
	confirmation, graph, err := s.pullRequestWriteGraph(ctx, route.RepoID, pr, providerResult, now)
	if err != nil {
		return WriteCommandResult{}, false, err
	}
	metadata := cloneStringMap(prior.RequestMetadata)
	metadata["write_phase"] = "recovered_by_canonical_readback"
	completed := audit.WithRequestMetadata(audit.Success(route.RepoID, prior.IdempotencyKey, "update-pr", graph.Record.ID, graph.Record.RemoteType, confirmation.remoteID, fingerprint, "recovered_by_canonical_readback", now), metadata)
	pending := audit.WithRequestMetadata(audit.RemoteConfirmedCacheRefreshPending(route.RepoID, prior.IdempotencyKey, "update-pr", graph.Record.ID, graph.Record.RemoteType, confirmation.remoteID, fingerprint, "canonical pull request readback confirmed; cache refresh pending", now), metadata)
	transitioner, ok := s.store.(auditGenerationClaimStore)
	if !ok {
		return WriteCommandResult{}, false, ErrWriteFailure{Code: "write_audit_start_failed", RepoID: route.RepoID, RemoteID: confirmation.remoteID, IdempotencyKey: prior.IdempotencyKey, Cause: errors.New("atomic audit generation transition support is unavailable")}
	}
	settling, current, err := s.transitionAuditGeneration(ctx, transitioner, pending, prior.CreatedAt, prior.Status, audit.StatusRemoteConfirmedCacheRefreshFailed)
	if err != nil {
		return WriteCommandResult{}, false, ErrWriteFailure{Code: "write_partial_remote_confirmed_audit_failed", RepoID: route.RepoID, RemoteID: confirmation.remoteID, IdempotencyKey: prior.IdempotencyKey, Cause: err}
	}
	if !settling {
		if auditGenerationSucceeded(current, prior.CreatedAt, fingerprint) {
			return replayWriteResult("update-pr", req, *current, fingerprint, now), true, nil
		}
		return WriteCommandResult{}, false, ErrWriteFailure{Code: "write_idempotency_in_progress", RepoID: route.RepoID, RemoteID: confirmation.remoteID, IdempotencyKey: prior.IdempotencyKey}
	}
	if err := s.store.UpsertRecordGraph(ctx, graph); err != nil {
		partial := audit.WithRequestMetadata(audit.RemoteConfirmedCacheRefreshFailed(route.RepoID, prior.IdempotencyKey, "update-pr", graph.Record.ID, graph.Record.RemoteType, confirmation.remoteID, fingerprint, err.Error(), now), metadata)
		_, current, _ := s.transitionAuditGeneration(ctx, transitioner, partial, prior.CreatedAt, audit.StatusRemoteConfirmedCacheRefreshPending)
		if auditGenerationSucceeded(current, prior.CreatedAt, fingerprint) {
			return replayWriteResult("update-pr", req, *current, fingerprint, now), true, nil
		}
		return WriteCommandResult{}, false, ErrWriteFailure{Code: "write_partial_cache_refresh_failed", RepoID: route.RepoID, RemoteID: confirmation.remoteID, IdempotencyKey: prior.IdempotencyKey, Cause: err}
	}
	settled, _, err := s.transitionAuditGeneration(ctx, transitioner, completed, prior.CreatedAt, audit.StatusRemoteConfirmedCacheRefreshPending, audit.StatusRemoteConfirmedCacheRefreshFailed)
	if err != nil || !settled {
		if err == nil {
			err = errors.New("pull request update recovery could not settle the audit generation")
		}
		partial := audit.WithRequestMetadata(audit.RemoteConfirmedAuditFailed(route.RepoID, prior.IdempotencyKey, "update-pr", graph.Record.ID, graph.Record.RemoteType, confirmation.remoteID, fingerprint, err.Error(), now), metadata)
		_, _, _ = s.transitionAuditGeneration(ctx, transitioner, partial, prior.CreatedAt, audit.StatusRemoteConfirmedCacheRefreshPending)
		return WriteCommandResult{}, false, ErrWriteFailure{Code: "write_partial_remote_confirmed_audit_failed", RepoID: route.RepoID, RemoteID: confirmation.remoteID, IdempotencyKey: prior.IdempotencyKey, Cause: err}
	}
	result := replayWriteResult("update-pr", req, completed, fingerprint, now)
	result.Status = "recovered_after_ambiguous_write"
	result.ID = graph.Record.ID
	result.RemoteID = confirmation.remoteID
	result.RemoteNumber = confirmation.remoteNumber
	result.RemoteRevision = confirmation.remoteRevision
	result.Replayed = true
	result.Evidence = "canonical readback matched requested PR state; no second PATCH issued"
	return result, true, nil
}

func prUpdateRecoveryMatches(req WriteCommandRequest, pr gitcode.PullRequest, metadata map[string]string) bool {
	checks := []struct {
		requested string
		actual    string
		preimage  string
	}{
		{requested: req.Title, actual: pr.Title, preimage: "pr_update_preimage_title_hash"},
		{requested: req.Body, actual: pr.Body, preimage: "pr_update_preimage_body_hash"},
		{requested: strings.TrimSpace(req.State), actual: strings.TrimSpace(pr.State), preimage: "pr_update_preimage_state_hash"},
	}
	for _, check := range checks {
		if check.requested != "" {
			if check.actual != check.requested {
				return false
			}
			continue
		}
		if metadata[check.preimage] == "" || hashWriteInvariant(check.actual) != metadata[check.preimage] {
			return false
		}
	}
	return true
}

func (s *Service) recoverAmbiguousIssueUpdate(ctx context.Context, route RepositoryRoute, req WriteCommandRequest, prior cache.AuditTrailEntry, fingerprint string) (WriteCommandResult, bool, error) {
	issue, err := s.client.GetIssue(ctx, gitcode.IssueRequest{Owner: route.Owner, Repo: route.Name, Number: req.Number})
	if err != nil {
		return WriteCommandResult{}, false, ErrWriteFailure{Code: "write_ambiguous_readback_failed", RepoID: route.RepoID, RemoteID: prior.RemoteID, IdempotencyKey: prior.IdempotencyKey, Cause: err}
	}
	matched, err := s.issueUpdateRecoveryMatches(ctx, route, req, issue, prior.RequestMetadata)
	if err != nil {
		return WriteCommandResult{}, false, err
	}
	if !matched {
		return WriteCommandResult{}, false, nil
	}
	now := s.now().UTC()
	remoteID := firstNonEmptyString(issue.ID, strconv.Itoa(issue.Number))
	providerResult := gitcode.WriteResult[gitcode.Issue]{Record: issue, Confirmed: true, Operation: "UpdateIssueRecoveryReadback", RemoteID: remoteID, RemoteNumber: issue.Number, ProviderStatus: "readback-recovered", IdempotencyKey: prior.IdempotencyKey, ConfirmedAt: now}
	confirmation, graph := s.issueWriteGraph(route.RepoID, issue, providerResult, now)
	metadata := cloneStringMap(prior.RequestMetadata)
	metadata["write_phase"] = "recovered_by_canonical_readback"
	completed := audit.WithRequestMetadata(audit.Success(route.RepoID, prior.IdempotencyKey, "update-issue", graph.Record.ID, graph.Record.RemoteType, remoteID, fingerprint, "recovered_by_canonical_readback", now), metadata)
	if err := s.store.RecordAuditEvent(ctx, completed); err != nil {
		return WriteCommandResult{}, false, ErrWriteFailure{Code: "write_partial_remote_confirmed_audit_failed", RepoID: route.RepoID, RemoteID: remoteID, IdempotencyKey: prior.IdempotencyKey, Cause: err}
	}
	if err := s.store.UpsertRecordGraph(ctx, graph); err != nil {
		partial := audit.WithRequestMetadata(audit.RemoteConfirmedCacheRefreshFailed(route.RepoID, prior.IdempotencyKey, "update-issue", graph.Record.ID, graph.Record.RemoteType, remoteID, fingerprint, err.Error(), now), metadata)
		_ = s.store.RecordAuditEvent(ctx, partial)
		return WriteCommandResult{}, false, ErrWriteFailure{Code: "write_partial_cache_refresh_failed", RepoID: route.RepoID, RemoteID: remoteID, IdempotencyKey: prior.IdempotencyKey, Cause: err}
	}
	if err := s.recordCacheConfirmation(ctx, "update-issue", route.RepoID, prior.IdempotencyKey, fingerprint, graph, remoteID, "succeeded", now); err != nil {
		return WriteCommandResult{}, false, ErrWriteFailure{Code: "write_partial_cache_refresh_failed", RepoID: route.RepoID, RemoteID: remoteID, IdempotencyKey: prior.IdempotencyKey, Cause: err}
	}
	result := replayWriteResult("update-issue", req, completed, fingerprint, now)
	result.Status = "recovered_after_ambiguous_write"
	result.ID = graph.Record.ID
	result.RemoteID = remoteID
	result.RemoteNumber = confirmation.remoteNumber
	result.Replayed = true
	result.Evidence = "canonical readback matched requested state; no second PATCH issued"
	return result, true, nil
}

func (s *Service) issueUpdateRecoveryMatches(ctx context.Context, route RepositoryRoute, req WriteCommandRequest, issue gitcode.Issue, metadata map[string]string) (bool, error) {
	current := issueUpdatePreimageMetadata(issue)
	checks := []struct {
		requested bool
		actual    string
		expected  string
	}{
		{req.Title != "", issue.Title, req.Title},
		{req.Body != "", issue.Body, req.Body},
		{req.State != "", strings.TrimSpace(issue.State), strings.TrimSpace(req.State)},
	}
	for _, check := range checks {
		if check.requested && check.actual != check.expected {
			return false, nil
		}
	}
	preserved := []struct {
		omitted bool
		key     string
	}{
		{req.Title == "", "issue_preimage_title_hash"},
		{req.Body == "", "issue_preimage_body_hash"},
		{req.State == "", "issue_preimage_state_hash"},
		{len(req.Labels) == 0, "issue_preimage_labels_hash"},
		{strings.TrimSpace(req.Milestone) == "" && !req.ClearMilestone, "issue_preimage_milestone_hash"},
	}
	for _, check := range preserved {
		if check.omitted && (metadata[check.key] == "" || current[check.key] != metadata[check.key]) {
			return false, nil
		}
	}
	if len(req.Labels) > 0 {
		want := append([]string(nil), req.Labels...)
		got := append([]string(nil), issue.Labels...)
		sort.Strings(want)
		sort.Strings(got)
		if strings.Join(want, "\x00") != strings.Join(got, "\x00") {
			return false, nil
		}
	}
	if req.ClearMilestone {
		if issue.Milestone != nil && firstNonEmptyString(issue.Milestone.RemoteID, issue.Milestone.SourceID, issue.Milestone.Title) != "" {
			return false, nil
		}
	} else if strings.TrimSpace(req.Milestone) != "" {
		milestone, err := s.resolveMilestone(ctx, route, req.Milestone)
		if err != nil {
			return false, err
		}
		if issue.Milestone == nil || firstNonEmptyString(issue.Milestone.RemoteID, issue.Milestone.SourceID) != firstNonEmptyString(milestone.RemoteID, milestone.SourceID) {
			return false, nil
		}
	}
	return true, nil
}

func (s *Service) issueWriteGraph(repoID string, issue gitcode.Issue, result gitcode.WriteResult[gitcode.Issue], now time.Time) (writeConfirmation, cache.RecordGraph) {
	remoteID := firstNonEmptyString(result.RemoteID, issue.ID, strconv.Itoa(firstNonZeroInt(result.RemoteNumber, issue.Number)))
	issue.Number = firstNonZeroInt(issue.Number, result.RemoteNumber)
	remoteAlias := remoteID
	if issue.Number > 0 {
		remoteAlias = strconv.Itoa(issue.Number)
	}
	stableID := fallbackSourceID("issue", remoteAlias)
	status := firstNonEmptyString(issue.State, issue.Status, "open")
	updated := issue.UpdatedAt.UTC()
	if updated.IsZero() {
		updated = now
	}
	created := issue.CreatedAt.UTC()
	if created.IsZero() {
		created = updated
	}
	milestoneID := ""
	if issue.Milestone != nil {
		milestoneID = issue.Milestone.RemoteID
	}
	hash := contentHash(issue.Title, issue.Body, status, issue.Labels)
	if milestoneID != "" {
		hash = contentHash(issue.Title, issue.Body, status, issue.Labels, milestoneID)
	}
	revision := firstNonEmptyString(result.RemoteRevision, result.ResponseHash, hash)
	record := cache.Record{RepoID: repoID, ID: stableID, Type: "issue", Path: "issues/" + remoteAlias + ".md", Title: issue.Title, Body: issue.Body, Status: status, Labels: issue.Labels, ContentHash: hash, Provenance: cache.ProvenanceRemote, RemoteType: "issue", RemoteID: remoteAlias, RemoteRevision: revision, CreatedAt: created, UpdatedAt: updated}
	graph := cache.RecordGraph{Record: record, SourceProvenance: s.syncOriginProvenance(), Identities: []cache.Identity{{RepoID: repoID, SourceID: stableID, AliasType: "issue", Alias: remoteAlias, Remote: cache.RemoteAlias{Type: "issue", ID: remoteAlias}}}, ReplaceLinkKinds: []string{"milestone"}, RemoteRevisions: []cache.RemoteRevision{{RepoID: repoID, RecordID: stableID, RemoteType: "issue", RemoteID: remoteAlias, RemoteRevision: revision, Status: "fresh", LastFetchedAt: now}}}
	if remoteID != remoteAlias {
		graph.Identities = append(graph.Identities, cache.Identity{RepoID: repoID, SourceID: stableID, AliasType: "gitcode_issue_id", Alias: remoteID, Remote: cache.RemoteAlias{Type: "gitcode_issue_id", ID: remoteID}})
	}
	if issue.Milestone != nil {
		milestoneResult := gitcode.WriteResult[gitcode.Milestone]{Record: *issue.Milestone, Confirmed: true, RemoteID: issue.Milestone.RemoteID, RemoteRevision: issue.Milestone.UpdatedAt, BrowserURL: issue.Milestone.HTMLURL, ConfirmedAt: now}
		_, milestoneGraph := s.milestoneWriteGraph(repoID, *issue.Milestone, milestoneResult, now)
		graph.RelatedRecords = append(graph.RelatedRecords, milestoneGraph.Record)
		graph.Identities = append(graph.Identities, milestoneGraph.Identities...)
		graph.RemoteRevisions = append(graph.RemoteRevisions, milestoneGraph.RemoteRevisions...)
		graph.Links = append(graph.Links, cache.Link{RepoID: repoID, SourceID: stableID, TargetID: milestoneGraph.Record.ID, Kind: "milestone", Text: issue.Milestone.Title})
	}
	return writeConfirmation{confirmed: result.Confirmed, remoteID: remoteID, providerID: issue.ID, remoteNumber: issue.Number, remoteRevision: revision, message: result.Operation, completedAt: firstNonZeroTime(result.ConfirmedAt, now)}, graph
}

func (s *Service) milestoneWriteGraph(repoID string, milestone gitcode.Milestone, result gitcode.WriteResult[gitcode.Milestone], now time.Time) (writeConfirmation, cache.RecordGraph) {
	remoteID := firstNonEmptyString(result.RemoteID, milestone.RemoteID)
	stableID := firstNonEmptyString(milestone.SourceID, fallbackSourceID("milestone", remoteID))
	updated := parseMilestoneTime(milestone.UpdatedAt)
	if updated.IsZero() {
		updated = now
	}
	created := parseMilestoneTime(milestone.CreatedAt)
	if created.IsZero() {
		created = updated
	}
	status := firstNonEmptyString(milestone.Status, "open")
	revision := firstNonEmptyString(result.RemoteRevision, milestone.UpdatedAt, result.ResponseHash, contentHash(milestone.Title, milestone.Body, status, milestone.DueOn))
	record := cache.Record{RepoID: repoID, ID: stableID, Type: "milestone", Path: "milestones/" + remoteID + ".md", Title: milestone.Title, Body: milestone.Body, Status: status, ContentHash: contentHash(milestone.Title, milestone.Body, status, milestone.DueOn), Provenance: cache.ProvenanceRemote, RemoteType: "milestone", RemoteID: remoteID, RemoteRevision: revision, CreatedAt: created, UpdatedAt: updated}
	graph := cache.RecordGraph{Record: record, Identities: []cache.Identity{{RepoID: repoID, SourceID: stableID, AliasType: "milestone", Alias: remoteID, Remote: cache.RemoteAlias{Type: "milestone", ID: remoteID}}}, RemoteRevisions: []cache.RemoteRevision{{RepoID: repoID, RecordID: stableID, RemoteType: "milestone", RemoteID: remoteID, RemoteRevision: revision, Status: "fresh", LastFetchedAt: now}}}
	if milestone.IID != "" {
		graph.Identities = append(graph.Identities, cache.Identity{RepoID: repoID, SourceID: stableID, AliasType: "milestone_iid", Alias: "milestone_iid:" + milestone.IID, Remote: cache.RemoteAlias{Type: "milestone", ID: remoteID}})
	}
	if milestone.HTMLURL != "" {
		graph.Identities = append(graph.Identities, cache.Identity{RepoID: repoID, SourceID: stableID, AliasType: "url", Alias: milestone.HTMLURL, Remote: cache.RemoteAlias{Type: "milestone", ID: remoteID}})
	}
	remoteNumber, _ := strconv.Atoi(remoteID)
	return writeConfirmation{confirmed: result.Confirmed, remoteID: remoteID, remoteNumber: remoteNumber, remoteRevision: revision, browserURL: firstNonEmptyString(milestone.HTMLURL, result.BrowserURL), message: result.Operation, completedAt: firstNonZeroTime(result.ConfirmedAt, now)}, graph
}

func milestoneRecord(m gitcode.Milestone) MilestoneRecord {
	return MilestoneRecord{ID: firstNonEmptyString(m.SourceID, fallbackSourceID("milestone", m.RemoteID)), RemoteID: m.RemoteID, IID: m.IID, Title: m.Title, Description: m.Body, State: firstNonEmptyString(m.Status, "open"), DueOn: m.DueOn, BrowserURL: m.HTMLURL, CreatedAt: parseMilestoneTime(m.CreatedAt), UpdatedAt: parseMilestoneTime(m.UpdatedAt)}
}

func parseMilestoneTime(raw string) time.Time {
	if strings.TrimSpace(raw) == "" {
		return time.Time{}
	}
	t, err := time.Parse(time.RFC3339, strings.TrimSpace(raw))
	if err != nil {
		return time.Time{}
	}
	return t.UTC()
}

func pushMirrorRecordGraph(repoID string, mirror gitcode.PushMirror, now time.Time) (PushMirrorRecord, cache.RecordGraph) {
	stableID := fallbackSourceID("pushmirror", mirror.RemoteID)
	created := parseMilestoneTime(mirror.CreatedAt)
	updated := parseMilestoneTime(mirror.LastUpdateAt)
	if updated.IsZero() {
		updated = parseMilestoneTime(mirror.LastSuccessfulUpdateAt)
	}
	if updated.IsZero() {
		updated = created
	}
	if updated.IsZero() {
		updated = now
	}
	if created.IsZero() {
		created = updated
	}
	status := firstNonEmptyString(mirror.UpdateStatus, "configured")
	body := fmt.Sprintf(
		"destination: %s\nforce: %t\nprivate: %t\nupdate_status: %s\nnumber_of_failures: %d\nmessage: %s\nlast_update_at: %s\nlast_successful_update_at: %s\n",
		mirror.URL,
		mirror.Force,
		mirror.Private,
		status,
		mirror.NumberOfFailures,
		mirror.Message,
		mirror.LastUpdateAt,
		mirror.LastSuccessfulUpdateAt,
	)
	hash := contentHash(mirror.RemoteID, mirror.ProjectID, mirror.URL, mirror.Force, mirror.Private, status, mirror.NumberOfFailures, mirror.Message, mirror.CreatedAt, mirror.LastUpdateAt, mirror.LastSuccessfulUpdateAt)
	record := cache.Record{
		RepoID:         repoID,
		ID:             stableID,
		Type:           "push_remote_mirror",
		Path:           "push-mirrors/" + mirror.RemoteID + ".md",
		Title:          "Push mirror " + mirror.RemoteID,
		Body:           body,
		Status:         status,
		ContentHash:    hash,
		Provenance:     cache.ProvenanceRemote,
		RemoteType:     "push_remote_mirror",
		RemoteID:       mirror.RemoteID,
		RemoteRevision: hash,
		CreatedAt:      created,
		UpdatedAt:      updated,
	}
	graph := cache.RecordGraph{
		Record: record,
		Identities: []cache.Identity{{
			RepoID:    repoID,
			SourceID:  stableID,
			AliasType: "push_remote_mirror",
			Alias:     mirror.RemoteID,
			Remote:    cache.RemoteAlias{Type: "push_remote_mirror", ID: mirror.RemoteID},
		}},
		RemoteRevisions: []cache.RemoteRevision{{
			RepoID:         repoID,
			RecordID:       stableID,
			RemoteType:     "push_remote_mirror",
			RemoteID:       mirror.RemoteID,
			RemoteRevision: hash,
			Status:         "fresh",
			LastFetchedAt:  now,
		}},
	}
	return PushMirrorRecord{
		ID:                     stableID,
		RemoteID:               mirror.RemoteID,
		ProjectID:              mirror.ProjectID,
		Destination:            mirror.URL,
		Force:                  mirror.Force,
		Private:                mirror.Private,
		UpdateStatus:           mirror.UpdateStatus,
		NumberOfFailures:       mirror.NumberOfFailures,
		Message:                mirror.Message,
		CreatedAt:              parseMilestoneTime(mirror.CreatedAt),
		LastUpdateAt:           parseMilestoneTime(mirror.LastUpdateAt),
		LastSuccessfulUpdateAt: parseMilestoneTime(mirror.LastSuccessfulUpdateAt),
	}, graph
}

func triggerPushMirrorAPIPath(owner, repo, mirrorID string) string {
	return fmt.Sprintf("/api/v5/repos/%s/%s/push_remote_mirrors/%s", url.PathEscape(owner), url.PathEscape(repo), url.PathEscape(mirrorID))
}

func (s *Service) wikiWriteGraph(repoID string, page gitcode.WikiPage, result gitcode.WriteResult[gitcode.WikiPage], now time.Time) (writeConfirmation, cache.RecordGraph) {
	remoteID := firstNonEmptyString(result.RemoteSlug, page.Slug, result.RemoteID, page.ID)
	stableID := fallbackSourceID("wiki", remoteID)
	updated := page.UpdatedAt.UTC()
	if updated.IsZero() {
		updated = now
	}
	created := page.CreatedAt.UTC()
	if created.IsZero() {
		created = updated
	}
	revision := firstNonEmptyString(result.RemoteRevision, page.Revision, result.ResponseHash, contentHash(page.Title, page.Body))
	record := cache.Record{RepoID: repoID, ID: stableID, Type: "wiki", Path: normalizeWikiCachePath(remoteID), Title: page.Title, Body: page.Body, Status: "fresh", ContentHash: contentHash(page.Title, page.Body, revision), Provenance: cache.ProvenanceRemote, RemoteType: "wiki", RemoteID: remoteID, RemoteRevision: revision, CreatedAt: created, UpdatedAt: updated}
	graph := cache.RecordGraph{Record: record, Identities: []cache.Identity{{RepoID: repoID, SourceID: stableID, AliasType: "wiki", Alias: remoteID, Remote: cache.RemoteAlias{Type: "wiki", ID: remoteID}}}, RemoteRevisions: []cache.RemoteRevision{{RepoID: repoID, RecordID: stableID, RemoteType: "wiki", RemoteID: remoteID, RemoteRevision: revision, Status: "fresh", LastFetchedAt: now}}}
	return writeConfirmation{confirmed: result.Confirmed, remoteID: remoteID, remoteSlug: remoteID, remoteRevision: revision, apiPath: result.APIPath, cachePath: firstNonEmptyString(result.CachePath, record.Path), browserURL: result.BrowserURL, message: result.Operation, completedAt: firstNonZeroTime(result.ConfirmedAt, now)}, graph
}

func (s *Service) commentWriteGraph(ctx context.Context, repoID string, number int, parentSourceID string, comment gitcode.Comment, result gitcode.WriteResult[gitcode.Comment], now time.Time) (writeConfirmation, cache.RecordGraph, error) {
	issueNumber := firstNonZeroInt(result.ParentIssueNumber, number)
	providerID := firstNonEmptyString(result.ParentIssueID, comment.IssueID)
	stableID := strings.TrimSpace(parentSourceID)
	if stableID == "" && issueNumber > 0 {
		if source, ok, err := s.issueSourceForNumber(ctx, repoID, issueNumber); err != nil {
			return writeConfirmation{}, cache.RecordGraph{}, err
		} else if ok {
			stableID = source.ID
		}
	}
	if stableID == "" && providerID != "" {
		if source, ok, err := s.issueSourceForProviderID(ctx, repoID, providerID); err != nil {
			return writeConfirmation{}, cache.RecordGraph{}, err
		} else if ok {
			stableID = source.ID
		}
	}
	if stableID == "" && issueNumber > 0 {
		stableID = fallbackSourceID("issue", strconv.Itoa(issueNumber))
	}
	if stableID == "" {
		return writeConfirmation{}, cache.RecordGraph{}, errors.New("issue comment parent cannot be resolved to a repository-local issue identity")
	}
	record, err := s.store.GetRecord(ctx, repoID, stableID)
	if err != nil {
		if !isCacheNotFound(err) {
			return writeConfirmation{}, cache.RecordGraph{}, err
		}
		if issueNumber <= 0 {
			return writeConfirmation{}, cache.RecordGraph{}, err
		}
		remoteID := strconv.Itoa(issueNumber)
		record = cache.Record{RepoID: repoID, ID: stableID, Type: "issue", Path: "issues/" + remoteID + ".md", Title: "Issue " + remoteID, Status: "open", ContentHash: contentHash(remoteID), Provenance: cache.ProvenanceRemote, RemoteType: "issue", RemoteID: remoteID, CreatedAt: now, UpdatedAt: now}
	}
	commentID := firstNonEmptyString(result.RemoteID, comment.ID, contentHash(stableID, comment.Body, now))
	created := firstNonZeroTime(comment.CreatedAt.UTC(), now)
	updated := firstNonZeroTime(comment.UpdatedAt.UTC(), created)
	graph := cache.RecordGraph{Record: record, Comments: []cache.RecordComment{{RepoID: repoID, RecordID: stableID, CommentID: commentID, Author: comment.Author, Body: comment.Body, ContentHash: contentHash(commentID, comment.Body), RemoteRevision: firstNonEmptyString(result.RemoteRevision, result.ResponseHash), CreatedAt: created, UpdatedAt: updated}}}
	if issueNumber > 0 {
		numberID := strconv.Itoa(issueNumber)
		graph.Identities = append(graph.Identities, cache.Identity{RepoID: repoID, SourceID: stableID, AliasType: "issue", Alias: numberID, Remote: cache.RemoteAlias{Type: "issue", ID: numberID}})
	}
	if providerID != "" {
		graph.Identities = append(graph.Identities, cache.Identity{RepoID: repoID, SourceID: stableID, AliasType: "gitcode_issue_id", Alias: providerID, Remote: cache.RemoteAlias{Type: "gitcode_issue_id", ID: providerID}})
	}
	return writeConfirmation{confirmed: result.Confirmed, remoteID: commentID, remoteNumber: issueNumber, remoteRevision: result.RemoteRevision, message: result.Operation, completedAt: firstNonZeroTime(result.ConfirmedAt, now)}, graph, nil
}

func (s *Service) refreshIssueCommentWriteCache(ctx context.Context, command string, graph cache.RecordGraph) error {
	if command != "add-comment" && command != "update-comment" {
		return nil
	}
	issueNumber, err := strconv.Atoi(strings.TrimSpace(graph.Record.RemoteID))
	if err != nil || issueNumber <= 0 {
		return errors.New("issue comment cache refresh is missing the canonical repository-local issue number")
	}
	providerID := ""
	for _, identity := range graph.Identities {
		if identity.AliasType == "gitcode_issue_id" {
			providerID = identity.Alias
			break
		}
	}
	item, ok, err := s.store.GetIssueCommentSync(ctx, graph.Record.RepoID, graph.Record.ID)
	if err != nil {
		return err
	}
	if !ok {
		item = cache.IssueCommentSync{
			RepoID:        graph.Record.RepoID,
			SourceID:      graph.Record.ID,
			RemoteID:      graph.Record.RemoteID,
			ExpectedCount: -1,
			Status:        "pending",
		}
	}
	item.IssueNumber = issueNumber
	if item.RemoteID == "" {
		item.RemoteID = graph.Record.RemoteID
	}
	if providerID != "" {
		item.ProviderID = providerID
	}
	item.UpdatedAt = s.now().UTC()
	record, err := s.store.GetRecord(ctx, graph.Record.RepoID, graph.Record.ID)
	if err != nil {
		return err
	}
	if item.ExpectedCount >= 0 && item.ExpectedCount < len(record.Comments) {
		item.ExpectedCount = len(record.Comments)
	}
	if err := s.projectIssueComments(ctx, item, record.Comments); err != nil {
		return err
	}
	return s.store.UpsertIssueCommentSync(ctx, item)
}

func (s *Service) pullRequestWriteGraph(ctx context.Context, repoID string, pr gitcode.PullRequest, result gitcode.WriteResult[gitcode.PullRequest], now time.Time) (writeConfirmation, cache.RecordGraph, error) {
	pr.Number = firstNonZeroInt(pr.Number, result.RemoteNumber)
	remoteID := strconv.Itoa(pr.Number)
	if remoteID == "0" {
		remoteID = firstNonEmptyString(result.RemoteID, pr.ID)
	}
	sourceGraph, _, err := s.stagePullRequest(ctx, SyncRequest{RepoID: repoID}, "pull_request", remoteID, pr)
	if err != nil {
		return writeConfirmation{}, cache.RecordGraph{}, err
	}
	graph := recordGraphFromSourceGraph(sourceGraph)
	graph.SourceProvenance = s.syncOriginProvenance()
	revision := firstNonEmptyString(result.RemoteRevision, sourceGraph.SyncStatus.RemoteRevision, result.ResponseHash)
	if len(graph.RemoteRevisions) > 0 {
		graph.RemoteRevisions[0].RemoteRevision = revision
	}
	return writeConfirmation{confirmed: result.Confirmed, remoteID: remoteID, providerID: pr.ID, remoteNumber: pr.Number, remoteRevision: revision, message: result.Operation, completedAt: firstNonZeroTime(result.ConfirmedAt, now)}, graph, nil
}

func (s *Service) prCommentWriteGraph(ctx context.Context, repoID string, number int, comment gitcode.PRComment, result gitcode.WriteResult[gitcode.PRComment], now time.Time) (writeConfirmation, cache.RecordGraph, error) {
	return s.prCommentWriteGraphWithParent(ctx, repoID, number, "", comment, result, now)
}

func (s *Service) prCommentWriteGraphWithParent(ctx context.Context, repoID string, number int, parentSourceID string, comment gitcode.PRComment, result gitcode.WriteResult[gitcode.PRComment], now time.Time) (writeConfirmation, cache.RecordGraph, error) {
	comment.PRNumber = firstNonZeroInt(comment.PRNumber, number, result.ParentIssueNumber)
	remoteCommentID := firstNonEmptyString(result.RemoteID, comment.ID)
	remoteID := prCommentRemoteID(comment.PRNumber, remoteCommentID)
	sourceGraph, _, err := s.stagePRComment(ctx, SyncRequest{RepoID: repoID, ParentSourceID: parentSourceID}, "pr_comment", remoteID, comment.PRNumber, comment)
	if err != nil {
		return writeConfirmation{}, cache.RecordGraph{}, err
	}
	graph := recordGraphFromSourceGraph(sourceGraph)
	graph.SourceProvenance = s.syncOriginProvenance()
	revision := firstNonEmptyString(result.RemoteRevision, sourceGraph.SyncStatus.RemoteRevision, result.ResponseHash)
	if len(graph.RemoteRevisions) > 0 {
		graph.RemoteRevisions[0].RemoteRevision = revision
	}
	return writeConfirmation{confirmed: result.Confirmed, remoteID: remoteCommentID, remoteNumber: comment.PRNumber, remoteRevision: revision, message: result.Operation, completedAt: firstNonZeroTime(result.ConfirmedAt, now), parentRemoteID: firstNonEmptyString(result.ParentIssueID, comment.DiscussionID)}, graph, nil
}

func (s *Service) prCommentThreadWriteGraph(ctx context.Context, repoID string, number int, result gitcode.WriteResult[gitcode.PRComment], now time.Time) (writeConfirmation, cache.RecordGraph, error) {
	confirmation, graph, err := s.prCommentWriteGraph(ctx, repoID, number, result.Record, result, now)
	if err != nil {
		return writeConfirmation{}, cache.RecordGraph{}, err
	}
	seen := map[string]bool{result.Record.ID: true}
	for _, comment := range result.Record.Thread {
		if comment.ID == "" || seen[comment.ID] {
			continue
		}
		seen[comment.ID] = true
		threadResult := gitcode.WriteResult[gitcode.PRComment]{Record: comment, Confirmed: true, Operation: "ReplyPRReviewCommentReadback", RemoteID: comment.ID, ParentIssueNumber: number, ParentIssueID: comment.DiscussionID, ConfirmedAt: result.ConfirmedAt}
		_, related, err := s.prCommentWriteGraph(ctx, repoID, number, comment, threadResult, now)
		if err != nil {
			return writeConfirmation{}, cache.RecordGraph{}, err
		}
		graph.RelatedRecords = append(graph.RelatedRecords, related.Record)
		graph.Comments = append(graph.Comments, related.Comments...)
		graph.PRReviewComments = append(graph.PRReviewComments, related.PRReviewComments...)
		graph.PRReviewDiscussions = append(graph.PRReviewDiscussions, related.PRReviewDiscussions...)
		graph.PRReviewPositions = append(graph.PRReviewPositions, related.PRReviewPositions...)
		graph.Identities = append(graph.Identities, related.Identities...)
		graph.Links = append(graph.Links, related.Links...)
		graph.RemoteRevisions = append(graph.RemoteRevisions, related.RemoteRevisions...)
		graph.SyncEvents = append(graph.SyncEvents, related.SyncEvents...)
	}
	return confirmation, graph, nil
}

func (s *Service) prIssueLinkWriteGraph(ctx context.Context, repoID string, prNumber int, issueNumber int, result gitcode.WriteResult[[]gitcode.Issue], now time.Time) (writeConfirmation, cache.RecordGraph, error) {
	remoteID := strconv.Itoa(firstNonZeroInt(result.RemoteNumber, prNumber))
	stableID := s.resolveOrFallback(ctx, repoID, "pull_request", remoteID, fallbackSourceID("pull_request", remoteID))
	record, err := s.store.GetRecord(ctx, repoID, stableID)
	if err != nil {
		record = cache.Record{RepoID: repoID, ID: stableID, Type: "pull_request", Path: "pulls/" + remoteID + ".md", Title: "Pull request " + remoteID, Status: "open", ContentHash: contentHash(remoteID), Provenance: cache.ProvenanceRemote, RemoteType: "pull_request", RemoteID: remoteID, CreatedAt: now, UpdatedAt: now}
	}
	revision := firstNonEmptyString(result.RemoteRevision, result.ResponseHash, contentHash(remoteID, strconv.Itoa(issueNumber), result.Record))
	record.RemoteType = "pull_request"
	record.RemoteID = remoteID
	record.RemoteRevision = revision
	record.UpdatedAt = now
	graph := cache.RecordGraph{Record: record, Identities: []cache.Identity{{RepoID: repoID, SourceID: stableID, AliasType: "pull_request", Alias: remoteID, Remote: cache.RemoteAlias{Type: "pull_request", ID: remoteID}}}, RemoteRevisions: []cache.RemoteRevision{{RepoID: repoID, RecordID: stableID, RemoteType: "pull_request", RemoteID: remoteID, RemoteRevision: revision, Status: "fresh", LastFetchedAt: now}}}
	return writeConfirmation{confirmed: result.Confirmed, remoteID: remoteID, remoteNumber: prNumber, remoteRevision: revision, message: result.Operation, completedAt: firstNonZeroTime(result.ConfirmedAt, now)}, graph, nil
}

func (s *Service) linkPRIssueDescriptionFallback(ctx context.Context, route RepositoryRoute, req WriteCommandRequest, opts gitcode.WriteOptions, now time.Time) (writeConfirmation, cache.RecordGraph, error) {
	pr, err := s.client.GetPR(ctx, gitcode.PRRequest{Owner: route.Owner, Repo: route.Name, Number: req.Number})
	if err != nil {
		return writeConfirmation{}, cache.RecordGraph{}, err
	}
	body := linkPRIssueBody(pr.Body, req.IssueNumber)
	result, err := s.client.UpdatePR(ctx, gitcode.UpdatePRRequest{Owner: route.Owner, Repo: route.Name, Number: req.Number, Body: body}, opts)
	if err != nil {
		return writeConfirmation{}, cache.RecordGraph{}, err
	}
	return s.pullRequestWriteGraph(ctx, route.RepoID, result.Record, result, now)
}

func isPRIssueRelationUnsupported(err error) bool {
	var unsupported gitcode.ErrUnsupportedCapability
	return errors.As(err, &unsupported) && unsupported.CapabilityKey == "pr_issue_relation"
}

func recordGraphFromSourceGraph(graph cache.SourceGraph) cache.RecordGraph {
	record := cache.Record{RepoID: graph.Source.RepoID, ID: graph.Source.ID, Type: graph.Source.Kind, Path: graph.Source.Path, Title: graph.Source.Title, Body: graph.Source.Body, Status: graph.Source.Status, Labels: graph.Source.Labels, ContentHash: graph.Source.ContentHash, Provenance: cache.ProvenanceRemote, CreatedAt: graph.Source.CreatedAt, UpdatedAt: graph.Source.UpdatedAt}
	out := cache.RecordGraph{Record: record, Comments: graph.Comments, PRReviewComments: graph.PRReviewComments, PRReviewDiscussions: graph.PRReviewDiscussions, PRReviewPositions: graph.PRReviewPositions, Identities: graph.Identities, Links: graph.Links, SyncEvents: graph.SyncEvents}
	if graph.SyncStatus != nil {
		record.RemoteType = graph.SyncStatus.RemoteType
		record.RemoteID = graph.SyncStatus.RemoteID
		record.RemoteRevision = graph.SyncStatus.RemoteRevision
		out.Record = record
		out.RemoteRevisions = []cache.RemoteRevision{{RepoID: graph.SyncStatus.RepoID, RecordID: graph.Source.ID, RemoteType: graph.SyncStatus.RemoteType, RemoteID: graph.SyncStatus.RemoteID, RemoteRevision: graph.SyncStatus.RemoteRevision, Status: graph.SyncStatus.Status, LastFetchedAt: graph.SyncStatus.LastFetchedAt}}
	}
	return out
}

func linkPRIssueBody(body string, issueNumber int) string {
	marker := fmt.Sprintf("<!-- gitcode-mcp-link:issue:%d -->", issueNumber)
	if strings.Contains(body, marker) {
		return body
	}
	line := fmt.Sprintf("%s\nFixes #%d", marker, issueNumber)
	trimmed := strings.TrimRight(body, "\n")
	if strings.TrimSpace(trimmed) == "" {
		return line
	}
	return trimmed + "\n\n" + line
}

func isReleaseNotFound(err error) bool {
	var notFound gitcode.ErrNotFound
	return errors.As(err, &notFound)
}

func releaseStatus(value string) (int, error) {
	switch strings.ToLower(strings.TrimSpace(value)) {
	case "", "latest":
		return gitcode.ReleaseStatusLatest, nil
	case "unset", "default":
		return gitcode.ReleaseStatusUnset, nil
	case "pre", "prerelease", "pre-release":
		return gitcode.ReleaseStatusPreRelease, nil
	default:
		return 0, ErrInvalidQuery{Field: "status", Message: "release status must be latest, prerelease, or unset"}
	}
}

func releaseBodyWithAssets(body string, assets []PublishAssetLink) string {
	body = strings.TrimRight(body, "\n")
	if len(assets) == 0 {
		return body
	}
	var b strings.Builder
	b.WriteString(body)
	if !strings.Contains(body, "## Assets") {
		b.WriteString("\n\n## Assets")
	}
	for _, asset := range assets {
		name := strings.TrimSpace(asset.Name)
		url := strings.TrimSpace(asset.URL)
		if name == "" || url == "" || strings.Contains(body, url) {
			continue
		}
		b.WriteString("\n- [")
		b.WriteString(name)
		b.WriteString("](")
		b.WriteString(url)
		b.WriteString(")")
	}
	return b.String()
}

func releaseIdempotency(req PublishReleaseRequest, status int) (string, string) {
	payload, _ := json.Marshal(struct {
		Command string
		RepoID  string
		Tag     string
		Ref     string
		Title   string
		Body    string
		Status  int
		Assets  []PublishAssetLink
	}{
		Command: "publish-release",
		RepoID:  firstNonEmptyString(req.RepoID, req.Repo),
		Tag:     strings.TrimSpace(req.Tag),
		Ref:     strings.TrimSpace(req.Ref),
		Title:   strings.TrimSpace(req.Title),
		Body:    req.Body,
		Status:  status,
		Assets:  req.Assets,
	})
	sum := sha256.Sum256(payload)
	fingerprint := hex.EncodeToString(sum[:])
	key := strings.TrimSpace(req.IdempotencyKey)
	if key == "" {
		key = "publish-release-" + fingerprint
		if len(key) > 64 {
			key = key[:64]
		}
	}
	return key, fingerprint
}

func writeIdempotency(command string, req WriteCommandRequest) (string, string) {
	payload, _ := json.Marshal(struct {
		Command        string
		RepoID         string
		ID             string
		Number         int
		IssueNumber    int
		DiscussionID   string
		ParentID       string
		Slug           string
		Path           string
		Sha            string
		Line           int
		Position       int
		StartLine      int
		EndLine        int
		Title          string
		Body           string
		Description    string
		DueOn          string
		Milestone      string
		ClearMilestone bool
		Head           string
		Base           string
		State          string
		Label          string
		Labels         []string
		Strategy       string
	}{command, req.RepoID, req.ID, req.Number, req.IssueNumber, req.DiscussionID, req.ParentID, req.Slug, req.Path, req.Sha, req.Line, req.Position, req.StartLine, req.EndLine, strings.TrimSpace(req.Title), req.Body, req.Description, strings.TrimSpace(req.DueOn), strings.TrimSpace(req.Milestone), req.ClearMilestone, strings.TrimSpace(req.Head), strings.TrimSpace(req.Base), req.State, strings.TrimSpace(req.Label), req.Labels, strings.TrimSpace(req.Strategy)})
	sum := sha256.Sum256(payload)
	fingerprint := hex.EncodeToString(sum[:])
	if override := strings.TrimSpace(req.idempotencyFingerprint); override != "" {
		fingerprint = override
	}
	if strings.TrimSpace(req.IdempotencyKey) != "" {
		return strings.TrimSpace(req.IdempotencyKey), fingerprint
	}
	return fingerprint[:32], fingerprint
}

func writeTargetID(req WriteCommandRequest) string {
	if req.IssueID != "" {
		return req.IssueID
	}
	if req.ID != "" {
		return req.ID
	}
	if req.Number != 0 {
		return strconv.Itoa(req.Number)
	}
	return firstNonEmptyString(req.Milestone, req.Path, req.Slug)
}

func replayWriteResult(command string, req WriteCommandRequest, entry cache.AuditTrailEntry, fingerprint string, now time.Time) WriteCommandResult {
	result := WriteCommandResult{Command: command, Status: "already_applied", RepoID: entry.RepoID, ID: entry.RecordID, RemoteID: entry.RemoteID, IdempotencyKey: entry.IdempotencyKey, SourceFingerprint: fingerprint, Replayed: true, Milestone: milestoneReceiptFromAudit(entry), PushMirror: pushMirrorReceiptFromAudit(entry), Evidence: "replayed from audit_trail", GeneratedAt: now}
	remoteNumber, _ := strconv.Atoi(entry.RequestMetadata["remote_number"])
	if remoteNumber == 0 {
		remoteNumber, _ = strconv.Atoi(entry.RemoteID)
	}
	applyWriteIssueIdentity(&result, command, req, entry.RecordID, remoteNumber)
	return result
}

func applyWriteIssueIdentity(result *WriteCommandResult, command string, req WriteCommandRequest, recordID string, remoteNumber int) {
	if result == nil {
		return
	}
	switch command {
	case "create-pr":
		result.RemoteNumber = remoteNumber
	case "create-issue", "update-issue", "add-label", "set-issue-milestone", "clear-issue-milestone":
		result.StableSourceID = firstNonEmptyString(req.IssueID, recordID)
		result.IssueNumber = firstNonZeroInt(req.Number, remoteNumber)
	case "add-comment", "update-comment":
		result.StableSourceID = req.IssueID
		result.IssueNumber = req.Number
	case "link-pr-issue":
		result.StableSourceID = req.IssueID
		result.IssueNumber = req.IssueNumber
	}
}

func applyWriteCommentTarget(result *WriteCommandResult, command string, req WriteCommandRequest, route RepositoryRoute) {
	if result == nil {
		return
	}
	segment := ""
	switch command {
	case "add-comment":
		result.TargetKind = "issue"
		result.IssueNumber = firstNonZeroInt(result.IssueNumber, req.Number)
		segment = "issues"
	case "add-pr-comment":
		result.TargetKind = "pull_request"
		result.RemoteNumber = firstNonZeroInt(result.RemoteNumber, req.Number)
		segment = "merge_requests"
	default:
		return
	}
	if result.BrowserURL == "" {
		result.BrowserURL = writeTargetBrowserURL(route, segment, req.Number)
	}
}

func writeTargetBrowserURL(route RepositoryRoute, segment string, number int) string {
	if number <= 0 || strings.TrimSpace(route.Owner) == "" || strings.TrimSpace(route.Name) == "" {
		return ""
	}
	rawBaseURL := strings.TrimSpace(route.APIBaseURL)
	if rawBaseURL == "" {
		rawBaseURL = "https://api.gitcode.com/api/v5"
	}
	u, err := url.Parse(rawBaseURL)
	if err != nil || u.Scheme == "" || u.Host == "" {
		return ""
	}
	u.User = nil
	u.RawQuery = ""
	u.Fragment = ""
	u.RawPath = ""
	if strings.EqualFold(u.Hostname(), "api.gitcode.com") {
		u.Host = "gitcode.com"
	}
	basePath := strings.TrimSuffix(u.Path, "/")
	for _, suffix := range []string{"/api/v5", "/api/v4", "/api"} {
		if strings.HasSuffix(basePath, suffix) {
			basePath = strings.TrimSuffix(basePath, suffix)
			break
		}
	}
	u.Path = path.Join(basePath, route.Owner, route.Name, segment, strconv.Itoa(number))
	return u.String()
}

func milestoneReceiptFromAudit(entry cache.AuditTrailEntry) *WriteMilestoneReceipt {
	metadata := entry.RequestMetadata
	if len(metadata) == 0 {
		return nil
	}
	receipt := &WriteMilestoneReceipt{
		ID:       metadata["milestone_id"],
		RemoteID: metadata["milestone_remote_id"],
		Title:    metadata["milestone_title"],
		Cleared:  metadata["milestone_cleared"] == "true",
	}
	if receipt.ID == "" && receipt.RemoteID == "" && receipt.Title == "" && !receipt.Cleared {
		return nil
	}
	return receipt
}

func pushMirrorReceiptFromAudit(entry cache.AuditTrailEntry) *WritePushMirrorReceipt {
	metadata := entry.RequestMetadata
	if len(metadata) == 0 || strings.TrimSpace(metadata["push_mirror_id"]) == "" {
		return nil
	}
	triggeredAt, _ := time.Parse(time.RFC3339Nano, metadata["push_mirror_triggered_at"])
	return &WritePushMirrorReceipt{
		MirrorID:       metadata["push_mirror_id"],
		Status:         firstNonEmptyString(metadata["push_mirror_status"], "triggered"),
		PreviousStatus: metadata["push_mirror_previous_status"],
		TriggeredAt:    triggeredAt,
	}
}

func (s *Service) writeAdapterErrorCode(mode WriteMode, err error) string {
	if mode == WriteModeLive && gitcode.IsFixtureReadOnly(err) {
		return "write_fixture_fallback_detected"
	}
	return writeErrorCode(err)
}

func writeMutationPhase(err error) (string, bool) {
	var phase gitcode.ErrWriteMutationPhase
	if !errors.As(err, &phase) || !phase.MutationAttempted {
		return "", false
	}
	return firstNonEmptyString(phase.Phase, "mutation"), true
}

func isTopLevelCommentCommand(command string) bool {
	return command == "add-comment" || command == "add-pr-comment"
}

func safeTopLevelCommentFailure(err error) bool {
	var auth gitcode.ErrAuthExpired
	if errors.As(err, &auth) {
		return true
	}
	var forbidden gitcode.ErrForbidden
	if errors.As(err, &forbidden) {
		return true
	}
	var conflict gitcode.ErrConflict
	if errors.As(err, &conflict) {
		return true
	}
	var validation gitcode.ErrAPIValidation
	if errors.As(err, &validation) {
		return true
	}
	var limited gitcode.ErrRateLimited
	return errors.As(err, &limited)
}

func safeMergePRMutationFailure(phase string, err error) bool {
	if phase == "readback" {
		return false
	}
	var auth gitcode.ErrAuthExpired
	if errors.As(err, &auth) {
		return true
	}
	var forbidden gitcode.ErrForbidden
	if errors.As(err, &forbidden) {
		return true
	}
	var conflict gitcode.ErrConflict
	if errors.As(err, &conflict) {
		return true
	}
	var validation gitcode.ErrAPIValidation
	return errors.As(err, &validation)
}

func safeIssueUpdateMutationFailure(phase string, err error) bool {
	if phase == "readback" {
		return false
	}
	var auth gitcode.ErrAuthExpired
	if errors.As(err, &auth) {
		return true
	}
	var forbidden gitcode.ErrForbidden
	if errors.As(err, &forbidden) {
		return true
	}
	var conflict gitcode.ErrConflict
	if errors.As(err, &conflict) {
		return true
	}
	var validation gitcode.ErrAPIValidation
	if errors.As(err, &validation) {
		return true
	}
	var limited gitcode.ErrRateLimited
	return errors.As(err, &limited)
}

func safePRUpdateMutationFailure(phase string, err error) bool {
	if phase == "readback" {
		return false
	}
	var auth gitcode.ErrAuthExpired
	if errors.As(err, &auth) {
		return true
	}
	var forbidden gitcode.ErrForbidden
	if errors.As(err, &forbidden) {
		return true
	}
	var conflict gitcode.ErrConflict
	if errors.As(err, &conflict) {
		return true
	}
	var validation gitcode.ErrAPIValidation
	if errors.As(err, &validation) {
		return true
	}
	var limited gitcode.ErrRateLimited
	return errors.As(err, &limited)
}

func writeErrorCode(err error) string {
	var validation gitcode.ErrAPIValidation
	if errors.As(err, &validation) {
		return "api_validation"
	}
	var emptyWiki gitcode.ErrEmptyWiki
	if errors.As(err, &emptyWiki) {
		return "empty_wiki"
	}
	var unavailableWiki gitcode.ErrWikiUnavailable
	if errors.As(err, &unavailableWiki) {
		return unavailableWiki.DiagnosticCode()
	}
	var precondition gitcode.ErrWritePreconditionConflict
	if errors.As(err, &precondition) {
		return "write_conflict"
	}
	var replyUnavailable gitcode.ErrDiscussionReplyUnavailable
	if errors.As(err, &replyUnavailable) {
		return "discussion_reply_unavailable"
	}
	var anchorMismatch gitcode.ErrPRReviewAnchorMismatch
	if errors.As(err, &anchorMismatch) {
		return "pr_review_anchor_mismatch"
	}
	var incomplete gitcode.ErrWriteConfirmationIncomplete
	if errors.As(err, &incomplete) {
		return "write_confirmation_incomplete"
	}
	var schema *gitcode.ErrSchemaDecode
	if errors.As(err, &schema) {
		return "schema_decode"
	}
	var conflict gitcode.ErrConflict
	if errors.As(err, &conflict) {
		return "write_conflict"
	}
	var auth gitcode.ErrAuthExpired
	if errors.As(err, &auth) {
		return "write_unauthorized"
	}
	var limited gitcode.ErrRateLimited
	if errors.As(err, &limited) {
		return "write_rate_limited"
	}
	var mirrorInProgress gitcode.ErrPushMirrorSyncInProgress
	if errors.As(err, &mirrorInProgress) {
		return "push_mirror_sync_in_progress"
	}
	var forbidden gitcode.ErrForbidden
	if errors.As(err, &forbidden) {
		return "write_forbidden"
	}
	var notFound gitcode.ErrRemoteNotFound
	if errors.As(err, &notFound) {
		return "push_mirror_not_found"
	}
	var network gitcode.ErrNetworkUnavailable
	if errors.As(err, &network) {
		return "write_network_unavailable"
	}
	return "write_provider_error"
}

func safePushMirrorTriggerFailure(err error) bool {
	var inProgress gitcode.ErrPushMirrorSyncInProgress
	if errors.As(err, &inProgress) {
		return true
	}
	var auth gitcode.ErrAuthExpired
	if errors.As(err, &auth) {
		return true
	}
	var forbidden gitcode.ErrForbidden
	if errors.As(err, &forbidden) {
		return true
	}
	var notFound gitcode.ErrRemoteNotFound
	if errors.As(err, &notFound) {
		return true
	}
	var validation gitcode.ErrAPIValidation
	if errors.As(err, &validation) {
		return true
	}
	var limited gitcode.ErrRateLimited
	return errors.As(err, &limited)
}

func writeFailureCause(code string, err error) error {
	if code == "write_fixture_fallback_detected" {
		return nil
	}
	return err
}

func failureSource(err error) string {
	var tooLarge gitcode.ErrPayloadTooLarge
	if errors.As(err, &tooLarge) {
		return tooLarge.Source
	}
	var schema *gitcode.ErrSchemaDecode
	if errors.As(err, &schema) {
		return "schema_decode"
	}
	var partial gitcode.ErrPartialResponse
	if errors.As(err, &partial) {
		return "partial_response"
	}
	return ""
}

func remoteWriteID(err error) string {
	var remote interface{ RemoteWriteID() string }
	if errors.As(err, &remote) {
		return strings.TrimSpace(remote.RemoteWriteID())
	}
	return ""
}

func writeCommandRemoteType(command string) string {
	switch command {
	case "add-pr-comment", "add-pr-review-comment", "reply-pr-review-comment":
		return "pr_comment"
	case "add-comment", "update-comment":
		return "issue_comment"
	case "create-issue", "update-issue":
		return "issue"
	case "create-page", "update-page", "delete-page":
		return "wiki"
	case "create-pr", "update-pr", "merge-pr", "link-pr-issue":
		return "pull_request"
	default:
		return "remote"
	}
}

func firstNonEmptyString(values ...string) string {
	for _, value := range values {
		if strings.TrimSpace(value) != "" {
			return strings.TrimSpace(value)
		}
	}
	return ""
}

func firstNonZeroInt(values ...int) int {
	for _, value := range values {
		if value != 0 {
			return value
		}
	}
	return 0
}

func firstNonZeroTime(values ...time.Time) time.Time {
	for _, value := range values {
		if !value.IsZero() {
			return value
		}
	}
	return time.Time{}
}

func (s *Service) requireRepo(ctx context.Context, repoID, operation string) (string, error) {
	repoID = strings.TrimSpace(repoID)
	if repoID == "" {
		return "", ErrRepoRequired{Operation: operation}
	}
	if _, err := s.store.GetRepository(ctx, repoID); err != nil {
		return "", normalizeError(err, "repository", repoID)
	}
	return repoID, nil
}

func (s *Service) resolveScopedStableID(ctx context.Context, repoID, id, aliasType, aliasID string) (string, error) {
	if id != "" {
		if aliasType == "" && aliasID == "" {
			if parsedType, parsedID, ok := parseRecordRef(id); ok {
				aliasType, aliasID = parsedType, parsedID
			} else if _, err := s.store.GetSourceScoped(ctx, repoID, id); err == nil {
				return id, nil
			} else if !isCacheNotFound(err) {
				return "", normalizeError(err, "source", id)
			}
		}
	}
	if aliasType != "" || aliasID != "" {
		if aliasType == "" || aliasID == "" {
			return "", ErrInvalidQuery{Field: "alias", Message: "alias type and id are required together"}
		}
		identity, err := s.store.ResolveAliasScoped(ctx, repoID, cache.RemoteAlias{Type: aliasType, ID: aliasID})
		if err != nil {
			return "", normalizeError(err, "alias", aliasType+":"+aliasID)
		}
		return identity.SourceID, nil
	}
	if id == "" {
		return "", ErrInvalidQuery{Field: "id", Message: "id is required"}
	}
	return "", ErrNotFound{Kind: "source", ID: id}
}

func parseRecordRef(ref string) (string, string, bool) {
	parts := strings.SplitN(strings.TrimSpace(ref), ":", 2)
	if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
		return "", "", false
	}
	return parts[0], parts[1], true
}

func (s *Service) DiagnoseUnscopedAlias(ctx context.Context, aliasType, aliasID string) error {
	identities, err := s.store.DiagnoseAlias(ctx, cache.RemoteAlias{Type: aliasType, ID: aliasID})
	if err != nil {
		return normalizeError(err, "alias", aliasType+":"+aliasID)
	}
	if len(identities) == 0 {
		return ErrNotFound{Kind: "alias", ID: aliasType + ":" + aliasID}
	}
	repos := map[string]struct{}{}
	for _, identity := range identities {
		repos[identity.RepoID] = struct{}{}
	}
	if len(repos) > 1 {
		return ErrAmbiguousAlias{Alias: aliasType + ":" + aliasID, Repos: sortedKeys(repos)}
	}
	return ErrRepoRequired{Operation: "alias lookup"}
}

func (s *Service) snippetFromLines(ctx context.Context, repoID, id string, start, end int) (SnippetResult, error) {
	if start <= 0 || end <= 0 || end < start {
		return SnippetResult{}, ErrInvalidQuery{Field: "line range", Message: "line_start and line_end must be positive and ordered"}
	}
	freshness, err := s.freshnessReport(ctx, repoID, index.ChunkQuery{RepoID: repoID, SourceID: id})
	if err != nil {
		return SnippetResult{}, err
	}
	source, err := s.store.GetSourceScoped(ctx, repoID, id)
	if err != nil {
		return SnippetResult{}, normalizeError(err, "source", id)
	}
	lines := strings.Split(source.Body, "\n")
	if start > len(lines) {
		return SnippetResult{}, ErrInvalidQuery{Field: "line_start", Message: "line_start is beyond source body"}
	}
	warnings := warningCodes(freshness.Warnings)
	actualEnd := end
	if actualEnd > len(lines) {
		actualEnd = len(lines)
		warnings = append(warnings, ErrRangeClamped{RequestedStart: start, RequestedEnd: end, ActualStart: start, ActualEnd: actualEnd}.Error())
	}
	return SnippetResult{RepoID: source.RepoID, ID: source.ID, Path: source.Path, Text: strings.Join(lines[start-1:actualEnd], "\n"), LineStart: start, LineEnd: actualEnd, Warnings: warnings}, nil
}

func (s *Service) freshnessReport(ctx context.Context, repoID string, query index.ChunkQuery) (index.IndexFreshnessReport, error) {
	sources, err := s.indexSources(ctx, repoID)
	if err != nil {
		return index.IndexFreshnessReport{}, err
	}
	chunks, err := s.store.ListChunks(ctx, cache.ChunkFilter{RepoID: query.RepoID, SourceID: query.SourceID, RecordID: query.RecordID, SnapshotID: query.SnapshotID, Policy: string(query.Policy)})
	if err != nil {
		return index.IndexFreshnessReport{}, normalizeError(err, "chunks", query.SourceID)
	}
	links, err := s.store.ListLinks(ctx, cache.LinkFilter{RepoID: repoID})
	if err != nil {
		return index.IndexFreshnessReport{}, normalizeError(err, "links", "")
	}
	linkReport := linkStaleReport(sources, links)
	return index.BuildFreshnessReport(ctx, sources, nil, indexChunks(chunks), nil, linkReport, query), nil
}

func (s *Service) indexSources(ctx context.Context, repoID string) ([]index.SourceRecord, error) {
	sources, err := s.store.ListSources(ctx, cache.SourceFilter{RepoID: repoID})
	if err != nil {
		return nil, normalizeError(err, "sources", "")
	}
	out := make([]index.SourceRecord, 0, len(sources))
	for _, source := range sources {
		record := indexSourceRecord(source)
		if status, err := s.store.GetSyncStatusScoped(ctx, repoID, source.ID); err == nil {
			record.RemoteRevision = status.RemoteRevision
			record.SyncRevision = status.RemoteRevision
		}
		out = append(out, record)
	}
	return out, nil
}

func linkStaleReport(sources []index.SourceRecord, links []cache.Link) index.StaleReport {
	sourceIDs := map[string]struct{}{}
	for _, source := range sources {
		sourceIDs[source.ID] = struct{}{}
	}
	affected := map[string]bool{}
	missing := map[string]bool{}
	for _, link := range links {
		if _, ok := sourceIDs[link.TargetID]; !ok {
			affected[link.SourceID] = true
			missing[link.TargetID] = true
		}
	}
	return index.StaleReport{TotalStaleBacklinks: len(missing), AffectedSourceIDs: indexSortedKeys(affected), UnresolvedTargets: indexSortedKeys(missing)}
}

func warningCodes(warnings []IndexWarning) []string {
	out := make([]string, 0, len(warnings))
	seen := map[string]bool{}
	for _, warning := range warnings {
		if warning.Code == "" || seen[warning.Code] {
			continue
		}
		seen[warning.Code] = true
		out = append(out, warning.Code)
	}
	return out
}

func indexSortedKeys(values map[string]bool) []string {
	keys := []string{}
	for key := range values {
		if key != "" {
			keys = append(keys, key)
		}
	}
	sort.Strings(keys)
	return keys
}

func filterWarningsForSources(warnings []IndexWarning, include map[string]struct{}) []IndexWarning {
	if len(include) == 0 {
		return append([]IndexWarning(nil), warnings...)
	}
	out := make([]IndexWarning, 0, len(warnings))
	for _, warning := range warnings {
		if _, ok := include[warning.SourceID]; ok {
			out = append(out, warning)
		}
	}
	return out
}

func (s *Service) snippetFromChunk(ctx context.Context, repoID, id, chunkID string) (SnippetResult, error) {
	freshness, err := s.freshnessReport(ctx, repoID, index.ChunkQuery{RepoID: repoID, SourceID: id})
	if err != nil {
		return SnippetResult{}, err
	}
	chunks, err := s.store.GetChunksScoped(ctx, repoID, id)
	if err != nil {
		return SnippetResult{}, normalizeError(err, "chunks", id)
	}
	for _, chunk := range chunks {
		if chunk.ID == chunkID {
			source, err := s.store.GetSourceScoped(ctx, repoID, id)
			if err != nil {
				return SnippetResult{}, normalizeError(err, "source", id)
			}
			return SnippetResult{RepoID: source.RepoID, ID: id, Path: source.Path, Text: chunk.Text, LineStart: chunk.LineStart, LineEnd: chunk.LineEnd, ChunkID: chunk.ID, Warnings: warningCodes(freshness.Warnings)}, nil
		}
	}
	if len(freshness.Warnings) > 0 {
		source, err := s.store.GetSourceScoped(ctx, repoID, id)
		if err != nil {
			return SnippetResult{}, normalizeError(err, "source", id)
		}
		return SnippetResult{RepoID: source.RepoID, ID: id, Path: source.Path, Warnings: warningCodes(freshness.Warnings)}, nil
	}
	return SnippetResult{}, ErrNotFound{Kind: "chunk", ID: chunkID}
}

func (s *Service) storedSnapshot(ctx context.Context, req ExportSnapshotRequest) (Snapshot, error) {
	if strings.TrimSpace(req.SnapshotID) == "" {
		stored, err := s.createStoredSnapshot(ctx, req.RepoID, req)
		if err != nil {
			return Snapshot{}, err
		}
		req.SnapshotID = stored.ID
	}
	repoID, err := s.requireRepo(ctx, req.RepoID, "export-snapshot")
	if err != nil {
		return Snapshot{}, err
	}
	stored, err := s.store.GetSnapshot(ctx, repoID, req.SnapshotID)
	if err != nil {
		return Snapshot{}, normalizeError(err, "snapshot", req.SnapshotID)
	}
	chunks, err := s.store.ListSnapshotChunks(ctx, repoID, req.SnapshotID)
	if err != nil {
		return Snapshot{}, err
	}
	if stored.ChunkCount != len(chunks) {
		return Snapshot{}, ErrSnapshotConsistency{RepoID: repoID, SnapshotID: req.SnapshotID, Expectation: "chunk_count"}
	}
	if stored.ChunkSetHash != "" {
		recomputed, err := snapshotHash(snapshotChunkHashRows(chunks))
		if err != nil {
			return Snapshot{}, err
		}
		if recomputed != stored.ChunkSetHash {
			return Snapshot{}, ErrSnapshotConsistency{RepoID: repoID, SnapshotID: req.SnapshotID, Expectation: "chunk_set_hash"}
		}
	}
	var snapshot Snapshot
	if err := json.Unmarshal([]byte(stored.ManifestJSON), &snapshot); err != nil {
		return Snapshot{}, err
	}
	if stored.ManifestHash != "" {
		recomputedManifest, err := snapshotHash(snapshotManifest(snapshot))
		if err != nil {
			return Snapshot{}, err
		}
		if recomputedManifest != stored.ManifestHash {
			return Snapshot{}, ErrSnapshotConsistency{RepoID: repoID, SnapshotID: req.SnapshotID, Expectation: "manifest_hash"}
		}
	}
	if stored.WarningsJSON != "" {
		_ = json.Unmarshal([]byte(stored.WarningsJSON), &snapshot.Warnings)
	}
	snapshot.Chunks = snapshotChunksFromCache(chunks)
	snapshot.ManifestHash = stored.ManifestHash
	snapshot.ChunkSetHash = stored.ChunkSetHash
	sortSnapshot(&snapshot)
	return snapshot, nil
}

func (s *Service) createStoredSnapshot(ctx context.Context, repoID string, req ExportSnapshotRequest) (cache.Snapshot, error) {
	snapshot, err := s.buildSnapshot(ctx, req)
	if err != nil {
		return cache.Snapshot{}, err
	}
	manifestHash, err := snapshotHash(snapshotManifest(snapshot))
	if err != nil {
		return cache.Snapshot{}, err
	}
	snapshotID := manifestHash
	if len(snapshotID) > 32 {
		snapshotID = snapshotID[:32]
	}
	chunks := cacheSnapshotChunks(snapshot, snapshotID)
	chunkSetHash, err := snapshotHash(snapshotChunkHashRows(chunks))
	if err != nil {
		return cache.Snapshot{}, err
	}
	manifestJSON, err := json.Marshal(snapshot)
	if err != nil {
		return cache.Snapshot{}, err
	}
	warningsJSON, err := json.Marshal(snapshot.Warnings)
	if err != nil {
		return cache.Snapshot{}, err
	}
	stored := cache.Snapshot{RepoID: snapshot.RepoID, ID: snapshotID, Format: normalizeSnapshotFormat(req.Format), ContentHash: manifestHash, RecordCount: len(snapshot.Sources), CreatedAt: snapshot.CreatedAt, SchemaVersion: snapshot.SchemaVersion, ManifestHash: manifestHash, ChunkSetHash: chunkSetHash, ChunkCount: len(chunks), ManifestJSON: string(manifestJSON), WarningsJSON: string(warningsJSON), Metadata: map[string]string{"schema_version": snapshot.SchemaVersion}, Chunks: chunks}
	if err := s.store.UpsertSnapshot(ctx, stored); err != nil {
		return cache.Snapshot{}, err
	}
	return stored, nil
}

func (s *Service) buildSnapshot(ctx context.Context, req ExportSnapshotRequest) (Snapshot, error) {
	repoID, err := s.requireRepo(ctx, req.RepoID, "export")
	if err != nil {
		return Snapshot{}, err
	}
	sources, err := s.store.ListSources(ctx, cache.SourceFilter{RepoID: repoID})
	if err != nil {
		return Snapshot{}, normalizeError(err, "sources", "")
	}
	if len(sources) == 0 {
		return Snapshot{}, ErrCacheEmpty{Message: "cache has no sources"}
	}
	include := map[string]struct{}{}
	for _, id := range req.SourceIDs {
		include[id] = struct{}{}
	}
	freshnessQuery := index.ChunkQuery{RepoID: repoID}
	if len(include) == 1 {
		for id := range include {
			freshnessQuery.SourceID = id
		}
	}
	freshness, err := s.freshnessReport(ctx, repoID, freshnessQuery)
	if err != nil {
		return Snapshot{}, err
	}
	snapshot := Snapshot{SchemaVersion: "gitcode-mcp.snapshot.v1", RepoID: repoID, Warnings: filterWarningsForSources(freshness.Warnings, include)}
	for _, source := range sources {
		if len(include) > 0 {
			if _, ok := include[source.ID]; !ok {
				continue
			}
		}
		labels := append([]string(nil), source.Labels...)
		sort.Strings(labels)
		body := ""
		if req.IncludeBody {
			body = source.Body
		}
		snapshot.Sources = append(snapshot.Sources, SnapshotSource{RepoID: source.RepoID, ID: source.ID, Kind: source.Kind, Path: source.Path, Title: source.Title, Body: body, Status: source.Status, Labels: labels, ContentHash: source.ContentHash, CreatedAt: source.CreatedAt.UTC(), UpdatedAt: source.UpdatedAt.UTC()})
		aliases, err := s.store.GetIdentityMapScoped(ctx, repoID, source.ID)
		if err != nil {
			return Snapshot{}, normalizeError(err, "aliases", source.ID)
		}
		for _, alias := range aliases {
			snapshot.Aliases = append(snapshot.Aliases, SnapshotAlias{RepoID: alias.RepoID, SourceID: alias.SourceID, AliasKind: alias.AliasType, AliasValue: alias.Alias, RemoteKind: alias.Remote.Type, RemoteID: alias.Remote.ID})
		}
		links, err := s.store.ListLinks(ctx, cache.LinkFilter{RepoID: repoID, SourceID: source.ID})
		if err != nil {
			return Snapshot{}, normalizeError(err, "links", source.ID)
		}
		for _, link := range links {
			snapshot.Links = append(snapshot.Links, SnapshotLink{RepoID: link.RepoID, SourceID: link.SourceID, TargetID: link.TargetID, LinkType: link.Kind, Text: link.Text})
		}
		backlinks, err := s.store.ListLinks(ctx, cache.LinkFilter{RepoID: repoID, TargetID: source.ID})
		if err != nil {
			return Snapshot{}, normalizeError(err, "backlinks", source.ID)
		}
		for _, link := range backlinks {
			snapshot.Backlinks = append(snapshot.Backlinks, SnapshotLink{RepoID: link.RepoID, SourceID: link.SourceID, TargetID: link.TargetID, LinkType: link.Kind, Text: link.Text})
		}
		status, err := s.store.GetSyncStatusScoped(ctx, repoID, source.ID)
		if err != nil {
			var notFound ErrNotFound
			if !errors.As(normalizeError(err, "sync status", source.ID), &notFound) {
				return Snapshot{}, normalizeError(err, "sync status", source.ID)
			}
			snapshot.SyncStatus = append(snapshot.SyncStatus, SnapshotSyncStatus{RepoID: source.RepoID, SourceID: source.ID, Status: "unknown", Freshness: "unknown"})
		} else {
			snapshot.SyncStatus = append(snapshot.SyncStatus, SnapshotSyncStatus{RepoID: status.RepoID, SourceID: source.ID, RemoteType: status.RemoteType, RemoteID: status.RemoteID, RemoteRevision: status.RemoteRevision, Status: status.Status, Freshness: freshnessFor(source, status), LastFetchedAt: status.LastFetchedAt.UTC()})
		}
		chunks, err := s.store.GetChunksScoped(ctx, repoID, source.ID)
		if err != nil {
			return Snapshot{}, normalizeError(err, "chunks", source.ID)
		}
		if len(chunks) == 0 {
			snapshot.Warnings = append(snapshot.Warnings, IndexWarning{RepoID: repoID, SourceID: source.ID, RecordID: source.ID, Code: "missing_index", State: "missing_index", Message: "source has no indexed chunks"})
		}
		for _, chunk := range chunks {
			if chunk.ContentHash != source.ContentHash {
				snapshot.Warnings = append(snapshot.Warnings, IndexWarning{RepoID: repoID, SourceID: source.ID, RecordID: source.ID, Code: "stale_index", State: "stale_index", Message: "chunk content hash differs from source content hash"})
			}
			if chunk.LineStart <= 0 || chunk.LineEnd <= 0 || chunk.ByteEnd <= chunk.ByteStart {
				snapshot.Warnings = append(snapshot.Warnings, IndexWarning{RepoID: repoID, SourceID: source.ID, RecordID: source.ID, Code: "missing_citation", State: "missing_citation", Message: "chunk citation range is unavailable"})
			}
			snapshot.Chunks = append(snapshot.Chunks, SnapshotChunk{RepoID: chunk.RepoID, ID: chunk.ID, SourceID: chunk.SourceID, RecordID: chunk.RecordID, ContentHash: chunk.ContentHash, ByteStart: chunk.ByteStart, ByteEnd: chunk.ByteEnd, LineStart: chunk.LineStart, LineEnd: chunk.LineEnd, HeadingPath: append([]string(nil), chunk.HeadingPath...), Text: chunk.Text, NormalizedText: chunk.NormalizedText, InheritedMetadata: copyStringMap(chunk.InheritedMetadata), OutboundLinks: sortedStrings(chunk.OutboundLinks), ResolvedAliases: copyStringMap(chunk.ResolvedAliases), SourceRevisionHash: source.ContentHash, IndexBuildID: chunk.SnapshotID})
		}
	}
	if len(snapshot.Sources) == 0 {
		return Snapshot{}, ErrCacheEmpty{Message: "cache has no matching sources"}
	}
	snapshot.CreatedAt = snapshotCreatedAt(snapshot)
	sortSnapshot(&snapshot)
	return snapshot, nil
}

func (s *Service) loadSnapshotRef(ctx context.Context, repoID string, ref SnapshotRef, format string) (Snapshot, error) {
	switch strings.ToLower(ref.Kind) {
	case "", "current":
		return s.buildSnapshot(ctx, ExportSnapshotRequest{RepoID: repoID, Format: format, IncludeBody: true})
	case "path":
		b, err := os.ReadFile(ref.Path)
		if err != nil {
			return Snapshot{}, err
		}
		return parseSnapshotBytes(b, ref.Format)
	case "bytes":
		return parseSnapshotBytes(ref.Bytes, ref.Format)
	default:
		return Snapshot{}, ErrInvalidQuery{Field: "snapshot_ref", Message: "kind must be current, path, or bytes"}
	}
}