Files
Agent-System/internal/projectinstance/policy.go
T
weichenandCursor a2aa3d41bd feat(projectinstance): support per-doc author_only duty policy
Add ResolveDocTaskDuty, require the system policy on save/preview,
lock the policy row during activation, and derive A/A/A assignments.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-17 09:39:59 +00:00

96 lines
2.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package projectinstance
import (
"context"
"fmt"
"github.com/jackc/pgx/v5"
)
// AuthorOnlyPolicySnapshot 系统级「允许编制人兼任审核批准」总授权快照。
// 字段语义对齐 sysadmin.AuthorOnlyApprovalPolicy;本包不导入 sysadmin
// (module-boundaries.yaml 未授权),由组合根提供适配实现。
type AuthorOnlyPolicySnapshot struct {
Allowed bool
Revision int64
}
// AuthorOnlyPolicySource 组合根注入的窄只读/事务锁接口。
type AuthorOnlyPolicySource interface {
GetAuthorOnlyApprovalPolicy(ctx context.Context) (AuthorOnlyPolicySnapshot, error)
LockAuthorOnlyApprovalPolicy(ctx context.Context, tx pgx.Tx) (AuthorOnlyPolicySnapshot, error)
}
func (s *Service) SetAuthorOnlyPolicySource(src AuthorOnlyPolicySource) {
if s == nil {
return
}
s.authorOnlyPolicy = src
}
func planUsesAuthorOnly(plan WorkflowPlan) bool {
for _, st := range plan.Stages {
if !st.Enabled {
continue
}
for _, dt := range st.DocTasks {
if dt.Enabled && dt.DutyPolicy == DutyPolicyAuthorOnly {
return true
}
}
}
return false
}
func (s *Service) requireAuthorOnlyAllowed(ctx context.Context) (AuthorOnlyPolicySnapshot, error) {
if s == nil || s.authorOnlyPolicy == nil {
return AuthorOnlyPolicySnapshot{}, ErrAuthorOnlyPolicyUnavailable
}
snap, err := s.authorOnlyPolicy.GetAuthorOnlyApprovalPolicy(ctx)
if err != nil {
return AuthorOnlyPolicySnapshot{}, fmt.Errorf("%w: %v", ErrAuthorOnlyPolicyUnavailable, err)
}
if !snap.Allowed {
return snap, ErrAuthorOnlyPolicyDisabled
}
return snap, nil
}
func (s *Service) loadAuthorOnlyCapability(ctx context.Context) GovernancePolicyCapability {
cap := GovernancePolicyCapability{Allowed: false, Revision: 0}
if s == nil || s.authorOnlyPolicy == nil {
cap.BlockedReason = "unavailable"
return cap
}
snap, err := s.authorOnlyPolicy.GetAuthorOnlyApprovalPolicy(ctx)
if err != nil {
cap.BlockedReason = "unavailable"
return cap
}
cap.Allowed = snap.Allowed
cap.Revision = snap.Revision
if !snap.Allowed {
cap.BlockedReason = "policy_disabled"
}
return cap
}
type activateAuthorOnlyLockKey struct{}
func withActivateAuthorOnlyLock(ctx context.Context, fn func(context.Context, pgx.Tx) (AuthorOnlyPolicySnapshot, error)) context.Context {
if fn == nil {
return ctx
}
return context.WithValue(ctx, activateAuthorOnlyLockKey{}, fn)
}
// runActivateAuthorOnlyLock 执行激活事务内策略行锁;第三个返回值表示是否注册过闭包。
func runActivateAuthorOnlyLock(ctx context.Context, tx pgx.Tx) (AuthorOnlyPolicySnapshot, error, bool) {
fn, _ := ctx.Value(activateAuthorOnlyLockKey{}).(func(context.Context, pgx.Tx) (AuthorOnlyPolicySnapshot, error))
if fn == nil {
return AuthorOnlyPolicySnapshot{}, nil, false
}
snap, err := fn(ctx, tx)
return snap, err, true
}