add conservative quota availability filtering
This commit is contained in:
@@ -155,6 +155,17 @@ func ValidatePayload(typ string, p map[string]any) error {
|
|||||||
if err := requiredString("subject_ref"); err != nil {
|
if err := requiredString("subject_ref"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
case "QuotaReported":
|
||||||
|
if err := requiredString("harness_id"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if v, ok := p["consumed"].(float64); !ok || v < 0 {
|
||||||
|
return fmt.Errorf("%w: consumed required", ErrInvalid)
|
||||||
|
}
|
||||||
|
case "StandupAdvisory":
|
||||||
|
if _, ok := p["items"]; !ok {
|
||||||
|
return fmt.Errorf("%w: items required", ErrInvalid)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,47 @@ type AlwaysAvailable struct{}
|
|||||||
|
|
||||||
func (AlwaysAvailable) Available(registry.Herdr) bool { return true }
|
func (AlwaysAvailable) Available(registry.Herdr) bool { return true }
|
||||||
|
|
||||||
|
// QuotaAvailability applies the conservative 80% rule to the most recent
|
||||||
|
// native quota report in the configured rolling window.
|
||||||
|
type QuotaAvailability struct {
|
||||||
|
Store *store.Store
|
||||||
|
Limits map[string]float64
|
||||||
|
Window time.Duration
|
||||||
|
Now func() time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q QuotaAvailability) Available(h registry.Herdr) bool {
|
||||||
|
if q.Store == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
limit, bounded := q.Limits[h.ID]
|
||||||
|
if !bounded || limit <= 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
now := time.Now()
|
||||||
|
if q.Now != nil {
|
||||||
|
now = q.Now()
|
||||||
|
}
|
||||||
|
window := q.Window
|
||||||
|
if window <= 0 {
|
||||||
|
window = 7 * 24 * time.Hour
|
||||||
|
}
|
||||||
|
var consumed float64
|
||||||
|
for _, e := range q.Store.Events(0) {
|
||||||
|
if e.Type != "QuotaReported" || e.At.Before(now.Add(-window)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var p struct {
|
||||||
|
HarnessID string `json:"harness_id"`
|
||||||
|
Consumed float64 `json:"consumed"`
|
||||||
|
}
|
||||||
|
if json.Unmarshal(e.Payload, &p) == nil && p.HarnessID == h.ID && p.Consumed > consumed {
|
||||||
|
consumed = p.Consumed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return consumed < limit*0.8
|
||||||
|
}
|
||||||
|
|
||||||
type RetryPolicy struct {
|
type RetryPolicy struct {
|
||||||
MaxAttempts int
|
MaxAttempts int
|
||||||
Backoff time.Duration
|
Backoff time.Duration
|
||||||
|
|||||||
@@ -63,6 +63,8 @@ The coordinator now persists active task→herdr session mappings in an atomic r
|
|||||||
|
|
||||||
Provider supervision/reflection is now wired: Gitea webhook and polling use append-first task reflection, JSONL and Gitea loops restart with bounded backoff, and `/v1/providers/health` exposes running/error state. Provider lifecycle cancellation remains tied to process shutdown until the server gains a root cancellation context.
|
Provider supervision/reflection is now wired: Gitea webhook and polling use append-first task reflection, JSONL and Gitea loops restart with bounded backoff, and `/v1/providers/health` exposes running/error state. Provider lifecycle cancellation remains tied to process shutdown until the server gains a root cancellation context.
|
||||||
|
|
||||||
|
Quota reporting now has strict payload validation, and `router.QuotaAvailability` implements rolling-window conservative headroom filtering: a harness is considered full at 80% of its configured limit. Standup event payloads also require an items field; scheduled advisory production and approval application remain open.
|
||||||
|
|
||||||
Recommended order:
|
Recommended order:
|
||||||
|
|
||||||
1. Add the orchestration coordinator: lease → worktree → harness session → bootstrap → lifecycle events.
|
1. Add the orchestration coordinator: lease → worktree → harness session → bootstrap → lifecycle events.
|
||||||
|
|||||||
Reference in New Issue
Block a user