feat(sense): add reconciliation safety controls [T-012]
Harness governance / validate (push) Has been cancelled
Harness governance / validate (pull_request) Has been cancelled

This commit is contained in:
QiuSW
2026-08-07 23:00:03 +08:00
parent 677ed732f7
commit 12857fdf32
35 changed files with 2817 additions and 124 deletions
+17
View File
@@ -21,6 +21,8 @@ var (
ErrAreaPolicyUnavailable = errors.New("area policy unavailable")
ErrAreaPolicyInvalid = errors.New("area policy invalid")
ErrAreaPolicyDenied = errors.New("area policy denies imaging device")
ErrReconcileLeaseLost = errors.New("reconcile lease lost")
ErrOperationalLeaseLost = errors.New("operational lease lost")
)
// Repository is the storage boundary used by the Sense process. SQLite stays
@@ -31,6 +33,10 @@ type Repository interface {
SetDesiredState(context.Context, string, device.DesiredState) error
GetDevice(context.Context, string) (device.Device, error)
ListDueReconcile(context.Context, time.Time, int) ([]ReconcileCandidate, error)
ClaimDueReconcile(context.Context, ReconcileClaim) ([]ReconcileCandidate, error)
RenewReconcileLease(context.Context, string, string, string, time.Time, time.Duration) (bool, error)
CompleteReconcile(context.Context, string, int64, string, string, time.Time) error
FailReconcile(context.Context, string, int, time.Time, string, string, string, time.Time) error
ListEnabledVideoDevices(context.Context, int) ([]device.Device, error)
MarkReconciled(context.Context, string, int64, time.Time) error
MarkReconcileFailure(context.Context, string, int, time.Time, string, time.Time) error
@@ -39,6 +45,17 @@ type Repository interface {
ConvergenceSnapshot(context.Context) (ConvergenceSnapshot, error)
}
// ReconcileClaim identifies one short-lived batch claim. Token is unique per
// run and fences a worker whose lease expired and was acquired by another
// process. SQLite accepts the shape but remains explicitly single-process.
type ReconcileClaim struct {
Owner string
Token string
Now time.Time
LeaseDuration time.Duration
Limit int
}
func OpenRepository(ctx context.Context, driver, dsn string) (Repository, error) {
switch strings.ToLower(strings.TrimSpace(driver)) {
case "", DriverSQLite: