quota: z.ai credit-bucket back-pressure + resource gate + usage accounting (Redmine 490+491)

The loop now consults quota and host state before every dispatch and
DEFERS gated work with a logged reason instead of letting turns die at
the provider (the 2026-08-28 19:00 quota-wall failure mode, replayed as
a test). Adds internal/quota: 5h/weekly credit buckets (provider poll
when z.ai ships an endpoint - fake-server tested - else locally
estimated from the documented credit formula), TZ-aware peak window
(default 01:00-05:00 America/Chicago weekdays, matching the documented
z.ai peak Mon-Fri 14:00-18:00 Singapore), block/defer thresholds, a
read-only load/mem/disk/IO-PSI monitor, an optional redis shared-state
hop (stdlib RESP2 mini-client) so all instances of an account
coordinate, per-class token+credit accounting in loop.jsonl, and
`harness quota status|probe|gate`. Config: [quota] + [resources]
sections; README runbook covers the redis container and deploy-time
cgroup enforcement.

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
This commit is contained in:
2026-08-29 05:37:15 -05:00
parent 9dbb20489c
commit fc518c475e
21 changed files with 2904 additions and 20 deletions
+13 -1
View File
@@ -17,6 +17,7 @@ import (
"ukrrs.com/mopac/harness/internal/intake"
"ukrrs.com/mopac/harness/internal/llm"
"ukrrs.com/mopac/harness/internal/models"
"ukrrs.com/mopac/harness/internal/quota"
"ukrrs.com/mopac/harness/internal/task"
"ukrrs.com/mopac/harness/internal/tools"
"ukrrs.com/mopac/harness/internal/writeback"
@@ -35,6 +36,9 @@ type Conductor struct {
bash *tools.BashTool
keys *config.KeyResolver
out io.Writer
gate *quota.Gate
// deferLogged dedups defer log lines per (task, updated_on, reason).
deferLogged map[string]bool
}
// New validates routing config and prepares the tool palette.
@@ -57,9 +61,17 @@ func New(cfg *config.Config, out io.Writer) (*Conductor, error) {
return nil, err
}
}
return &Conductor{cfg: cfg, router: router, bash: bash, keys: config.NewKeyResolver(cfg), out: out}, nil
keys := config.NewKeyResolver(cfg)
gate, err := buildGates(cfg, keys)
if err != nil {
return nil, err
}
return &Conductor{cfg: cfg, router: router, bash: bash, keys: keys, out: out, gate: gate, deferLogged: map[string]bool{}}, nil
}
// Gate exposes the quota gate (nil when off) for `harness quota status`.
func (c *Conductor) Gate() *quota.Gate { return c.gate }
// OnceOpts controls a single iteration.
type OnceOpts struct {
DryRun bool // intake + plan only, no LLM call