From 3e242e6d360cb70d57702097106d5ea9e14e1aa6 Mon Sep 17 00:00:00 2001 From: weichen Date: Sun, 13 Sep 2026 03:32:20 +0000 Subject: [PATCH] fix(docgov): reclaim orphan squad leases and align run_id Use task run_id as doc lease holder so release tracks execution, and reclaim squad locks when the holder run is missing or no longer running. Co-authored-by: Cursor --- internal/bizdev/service.go | 5 ++++- internal/docgov/lease.go | 29 ++++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/internal/bizdev/service.go b/internal/bizdev/service.go index 462ef648..0efafc07 100644 --- a/internal/bizdev/service.go +++ b/internal/bizdev/service.go @@ -538,7 +538,10 @@ func (s *Service) ExecuteTask(ctx context.Context, req TaskRunRequest) (*TaskRun } // 通过权限检查后获取小队编辑租约(提案拦截路径不持锁) - leaseHolderID = fmt.Sprintf("run_%d", time.Now().UnixNano()) + leaseHolderID = strings.TrimSpace(req.RunID) + if leaseHolderID == "" { + leaseHolderID = fmt.Sprintf("run_%d", time.Now().UnixNano()) + } leaseTTL := req.Timeout if leaseTTL <= 0 && s.cfg != nil { leaseTTL = s.cfg.AgentTimeout diff --git a/internal/docgov/lease.go b/internal/docgov/lease.go index 8bc5df60..d1f777c7 100644 --- a/internal/docgov/lease.go +++ b/internal/docgov/lease.go @@ -67,7 +67,14 @@ func (e *GovernanceEngine) AcquireLease(ctx context.Context, req LeaseAcquireReq if doc.HasActiveLease(now) { sameHolder := doc.LeaseHolderType == req.HolderType && doc.LeaseHolderID == req.HolderID - if !sameHolder { + if !sameHolder && req.HolderType == LeaseHolderSquad && e.shouldReclaimSquadLease(ctx, doc) { + e.appendAuditLocked(doc, "lease_reclaim_orphan", 0, string(req.HolderType), RoleSystem, map[string]any{ + "previous_holder_type": doc.LeaseHolderType, + "previous_holder_id": doc.LeaseHolderID, + }) + doc.ClearLease() + e.persistLeaseLocked(ctx, doc) + } else if !sameHolder { e.appendAuditLocked(doc, "lease_deny", 0, string(req.HolderType), RoleSystem, map[string]any{ "requester_type": req.HolderType, "requester_id": req.HolderID, @@ -173,6 +180,26 @@ func (e *GovernanceEngine) HeartbeatLease(ctx context.Context, docID, holderType return doc, nil } +// shouldReclaimSquadLease 当 squad 租约 holder 无对应 running task_run 时视为孤儿锁并回收。 +func (e *GovernanceEngine) shouldReclaimSquadLease(ctx context.Context, doc *DocumentRecord) bool { + if doc == nil || doc.LeaseHolderType != LeaseHolderSquad { + return false + } + holderID := strings.TrimSpace(doc.LeaseHolderID) + if holderID == "" || !strings.HasPrefix(holderID, "run_") { + return false + } + if e.pool == nil { + return false + } + var status string + err := e.pool.QueryRow(ctx, `SELECT status FROM task_runs WHERE run_id = $1`, holderID).Scan(&status) + if err != nil { + return true + } + return status != "running" +} + // AssertWritableForComment 批注前检查:有效 edit lease 期间禁止批注 func (e *GovernanceEngine) AssertWritableForComment(ctx context.Context, docID string) error { e.mu.Lock()