Files
Agent-System/internal/dataset/types.go
T
weichenandCursor be0c1e843f feat(dataset): registry, pause/resume backfill, destroy gates, eight tracks
批准 dataset 分类入库;SKIP LOCKED 分批;销毁默认 fail closed。restore 演练拒绝共享 env DSN。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-15 02:40:12 +00:00

181 lines
5.2 KiB
Go

package dataset
import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"sort"
"strings"
"time"
"chat-agent-system/internal/artifactvault"
)
const (
ClassPinned = "pinned"
ClassDemo = "demo"
ClassRegression = "regression"
ClassMigrationBaseline = "migration_baseline"
ClassEphemeral = "ephemeral"
ClassQuarantine = "quarantine"
JobRunning = "running"
JobPaused = "paused"
JobCompleted = "completed"
JobFailed = "failed"
OldestSupportedSchema = artifactvault.OldestSupportedSchema
MinQuarantineWindow = 7 * 24 * time.Hour
)
type Dataset struct {
ID string `json:"dataset_id"`
Class string `json:"class"`
DisplayName string `json:"display_name"`
StorageIdentity string `json:"storage_identity"`
SourceSchema int `json:"source_schema"`
SourceCommit string `json:"source_commit"`
Status string `json:"status"`
ProjectCount int `json:"project_count"`
}
func ValidateClass(class string) error {
switch class {
case ClassPinned, ClassDemo, ClassRegression, ClassMigrationBaseline, ClassEphemeral, ClassQuarantine:
return nil
default:
return fmt.Errorf("dataset: invalid class %q", class)
}
}
type Checkpoint struct {
JobID string
Shard int
SourceKind string
LastPK int64
Scanned int
Succeeded int
Quarantined int
RetryWatermark int
}
type JobStats struct {
Scanned int
Succeeded int
Quarantined int
}
func Recalc(cps []Checkpoint) JobStats {
var s JobStats
for _, c := range cps {
s.Scanned += c.Scanned
s.Succeeded += c.Succeeded
s.Quarantined += c.Quarantined
}
return s
}
type DeleteManifest struct {
DatasetIDs []string `json:"dataset_ids"`
StorageIdentity string `json:"storage_identity"`
Reason string `json:"reason"`
}
func CanonicalDestroyBytes(m DeleteManifest) []byte {
ids := append([]string(nil), m.DatasetIDs...)
sort.Strings(ids)
raw, _ := json.Marshal(struct {
DatasetIDs []string `json:"dataset_ids"`
StorageIdentity string `json:"storage_identity"`
Reason string `json:"reason"`
}{ids, m.StorageIdentity, m.Reason})
return raw
}
func DestroyManifestSHA(m DeleteManifest) string {
sum := sha256.Sum256(CanonicalDestroyBytes(m))
return hex.EncodeToString(sum[:])
}
type DestroyRequest struct {
TargetIdentity string
TargetDSN string
Manifest DeleteManifest
ExpectedManifestSHA string
RecoverableLocation string
CreatedAt time.Time
QuarantineUntil time.Time
Approval1 string
Approval2 string
Coverage artifactvault.Coverage
BackupOK bool
CorpusOK bool
VaultReady bool
Now time.Time
}
func RefuseOperationalDSN(dsn, identity string) error {
low := strings.ToLower(dsn + " " + identity)
for _, ban := range []string{"agent_system_dev", "agent_system_test", "agent_system_main"} {
if strings.Contains(low, ban) && !strings.Contains(low, "scratch") {
return fmt.Errorf("dataset: refuse operational database %s", ban)
}
}
if strings.Contains(low, ":5433/") || strings.Contains(low, ":5435/") {
if !strings.Contains(low, "scratch") {
return fmt.Errorf("dataset: refuse shared env port")
}
}
if strings.Contains(low, ":5434/") && strings.Contains(low, "agent_system_test") {
return fmt.Errorf("dataset: refuse shared test database")
}
return nil
}
func EvaluateDestroy(req DestroyRequest) error {
if err := RefuseOperationalDSN(req.TargetDSN, req.TargetIdentity); err != nil {
return err
}
if !strings.Contains(strings.ToLower(req.RecoverableLocation), "scratch") {
return fmt.Errorf("dataset: recoverable location must be scratch")
}
if req.QuarantineUntil.Sub(req.CreatedAt) < MinQuarantineWindow {
return fmt.Errorf("dataset: quarantine observation window must be at least 7 days")
}
now := req.Now
if now.IsZero() {
now = time.Now().UTC()
}
if now.Before(req.QuarantineUntil) {
return fmt.Errorf("dataset: quarantine window still open")
}
if strings.TrimSpace(req.Approval1) == "" || strings.TrimSpace(req.Approval2) == "" || req.Approval1 == req.Approval2 {
return fmt.Errorf("dataset: two distinct user approvals required")
}
got := DestroyManifestSHA(req.Manifest)
if req.ExpectedManifestSHA != "" && req.ExpectedManifestSHA != got {
return fmt.Errorf("dataset: destroy manifest sha mismatch")
}
if len(req.Manifest.DatasetIDs) == 0 || strings.TrimSpace(req.Manifest.StorageIdentity) == "" {
return fmt.Errorf("dataset: destroy manifest incomplete")
}
return artifactvault.PreflightDestroy(req.Coverage, req.BackupOK, req.CorpusOK, req.VaultReady)
}
func RequireScratchRestore(dsn, storageIdentity, manifestSHA, expectedSHA string, hasAdmin bool) error {
if err := RefuseOperationalDSN(dsn, storageIdentity); err != nil {
return err
}
if !strings.Contains(strings.ToLower(dsn+" "+storageIdentity), "scratch") {
return fmt.Errorf("dataset: restore target must be scratch")
}
if !hasAdmin {
return fmt.Errorf("dataset: restore requires role-admin")
}
if manifestSHA == "" || expectedSHA == "" || manifestSHA != expectedSHA {
return fmt.Errorf("dataset: restore manifest mismatch or missing")
}
return nil
}