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
207 lines
7.5 KiB
TOML
207 lines
7.5 KiB
TOML
# MOPAC harness configuration, v0 (2026-08-28).
|
|
#
|
|
# Copy to harness.toml and adjust. harness.toml is gitignored: secrets never
|
|
# live in this file, only refs (env:NAME | file:PATH | literal:VALUE |
|
|
# mpk:PLACEHOLDER via [keyproxy]; the bw: bitwarden ref lands with the key
|
|
# wrapper in build phase 3).
|
|
|
|
# Vertical / stack identity for this harness instance.
|
|
vertical = "demo"
|
|
|
|
# Where the bash tool executes and REPORTs land (relative to the CWD the
|
|
# harness is started from).
|
|
work_root = "."
|
|
report_dir = "reports"
|
|
|
|
[loop]
|
|
# Bounded turn: max LLM round trips per task (tool calls included).
|
|
max_rounds = 8
|
|
# `harness loop` daemon: Redmine scan interval and state location
|
|
# (append-only loop.jsonl, dedup by issue id + updated_on).
|
|
poll_interval_secs = 120
|
|
state_dir = "state/loop"
|
|
|
|
[redmine]
|
|
url = "https://rm.example.org"
|
|
key_ref = "env:HARNESS_REDMINE_KEY"
|
|
# Released scope: raw /issues.json filter params...
|
|
scope_query = "project=mopac&status_id=released&limit=25"
|
|
# ...or a saved custom query id (scope_query wins when both are set).
|
|
# scope_query_id = 42
|
|
# Custom field carrying the TASK class; issues without it get default_class.
|
|
class_field = "Class"
|
|
default_class = "primary"
|
|
|
|
# `harness loop` status transitions: after a REPORT is noted back on the
|
|
# issue, an issue whose CURRENT status matches a key here is moved to the
|
|
# value. Statuses are names (resolved via /issue_statuses.json); an empty
|
|
# map leaves status alone.
|
|
[redmine.status_map]
|
|
"In Progress" = "Done"
|
|
|
|
[litellm]
|
|
base_url = "http://192.168.3.78:4001"
|
|
key_ref = "env:HARNESS_LITELLM_KEY"
|
|
timeout_secs = 120
|
|
max_retries = 2
|
|
|
|
# KEYPROXY (optional): resolve `mpk:` key refs through the ukrrs/mopac-keyproxy
|
|
# hop (POST /v1/resolve, bearer auth). Without this section, env:/file:/
|
|
# literal: refs keep working as-is — the loop runs with or without keyproxy up.
|
|
# [keyproxy]
|
|
# url = "http://127.0.0.1:8082"
|
|
# token_ref = "env:HARNESS_KEYPROXY_TOKEN" # local ref only (no mpk: recursion)
|
|
# cache_ttl_secs = 60
|
|
|
|
# GITEA (optional, off by default): `harness loop` commits each REPORT file
|
|
# to this repo right after writing it (contents API, create-or-update).
|
|
# [gitea]
|
|
# url = "https://git.example.org"
|
|
# key_ref = "env:HARNESS_GITEA_KEY"
|
|
# owner = "ukrrs"
|
|
# repo = "MOPAC-reports"
|
|
# branch = "main" # empty = repo default branch
|
|
# commit_reports = false
|
|
|
|
# MODEL ROUTING v0 (static, config-only, no heuristics): [models] is the
|
|
# tier map (tier alias -> concrete proxy model); [models.classes] maps task
|
|
# classes to tiers. Requests go out with the CONCRETE model name resolved
|
|
# here. Swapping models (e.g. glm-4.7-flash -> glm-5.3-flash once it is
|
|
# configured on the proxy) is a one-line edit in this file.
|
|
[models]
|
|
mopac-study = "glm-4.7-flash" # flash tier
|
|
mopac-code = "glm-5.2" # flagship
|
|
mopac-review = "glm-5-turbo" # mid
|
|
mopac-primary = "glm-5.3" # default / flagship+
|
|
mopac-vision = "glm-4.6v" # vision when needed
|
|
default_tier = "mopac-primary"
|
|
|
|
[models.classes]
|
|
study = "mopac-study"
|
|
read = "mopac-study"
|
|
code = "mopac-code"
|
|
architecture = "mopac-code"
|
|
review = "mopac-review"
|
|
summarize = "mopac-review"
|
|
writeback = "mopac-review"
|
|
vision = "mopac-vision"
|
|
primary = "mopac-primary"
|
|
|
|
# Exec tool: allow-listed bash. Org preset: deny-first, read-leaning allow
|
|
# list, no sudo/ssh/network exfil. Compound commands are checked segment by
|
|
# segment; command substitution and subshells are always denied.
|
|
[tools.bash]
|
|
enabled = true
|
|
timeout_secs = 60
|
|
max_output_bytes = 100000
|
|
default = "deny"
|
|
deny = [
|
|
"sudo *",
|
|
"ssh *",
|
|
"scp *",
|
|
"nc *",
|
|
"curl *",
|
|
"wget *",
|
|
"rm -rf *",
|
|
]
|
|
allow = [
|
|
"pwd",
|
|
"ls *",
|
|
"cat *",
|
|
"head *",
|
|
"tail *",
|
|
"grep *",
|
|
"find *",
|
|
"wc *",
|
|
"echo *",
|
|
"env",
|
|
"git status",
|
|
"git diff *",
|
|
"git log *",
|
|
"git show *",
|
|
"go version",
|
|
"go build *",
|
|
"go vet *",
|
|
"go test *",
|
|
]
|
|
|
|
# MVP demo (Charles, 2026-08-28 ~19:00): `harness once --demo` runs this
|
|
# issue through LiteLLM; the GLM self-description lands as the REPORT.
|
|
[demo]
|
|
id = "demo-1"
|
|
subject = "MVP demo: GLM self-description"
|
|
prompt = "tell me about yourself"
|
|
class = "primary"
|
|
|
|
# EVENTS: the `harness events` webhook receiver (Redmine/Discourse/Gitea
|
|
# punch the harness; DESIGN "Events are V1 scope"). Secrets are refs only,
|
|
# resolved at startup, never logged. At least one secret_ref must resolve
|
|
# or the receiver refuses to start.
|
|
[events]
|
|
listen = ":4100" # bind address; publish on the LAN via docker -p
|
|
state_dir = "state/events" # append-only events.jsonl + dedup index
|
|
|
|
[events.redmine]
|
|
secret_ref = "env:HARNESS_REDMINE_WEBHOOK_SECRET"
|
|
# secret_header = "X-Redmine-Webhook-Secret" # default; match your plugin
|
|
|
|
[events.discourse]
|
|
secret_ref = "env:HARNESS_DISCOURSE_WEBHOOK_SECRET"
|
|
# secret_header = "X-Discourse-Webhook-Secret" # default
|
|
|
|
[events.gitea]
|
|
secret_ref = "env:HARNESS_GITEA_WEBHOOK_SECRET"
|
|
# Gitea always verifies via HMAC-SHA256 in X-Gitea-Signature; the
|
|
# secret_header override does not apply to it.
|
|
|
|
# SERVE (optional): the `harness serve` OpenAI-compatible front door for
|
|
# OpenWebUI (DESIGN "OWUI front door"). Each [models.classes] class is
|
|
# exposed as a servable model named mopac-<class> (mopac-study,
|
|
# mopac-code, ...); POST /v1/chat/completions runs ONE bounded stateless
|
|
# conductor turn over the conversation history OWUI sends (tools off,
|
|
# v0; non-streaming). Bearer vkey auth — the same value goes into the
|
|
# OWUI connection config. Own port: coexists with [events].
|
|
[serve]
|
|
listen = ":8090" # publish on the LAN via docker -p
|
|
vkey_ref = "env:HARNESS_SERVE_VKEY"
|
|
# enabled_models = ["mopac-primary", "mopac-study"] # optional subset
|
|
|
|
# QUOTA GATE (Redmine 490, off by default until verified live): z.ai coding
|
|
# plan credit buckets (5h + weekly), back-pressure thresholds, TZ-aware peak
|
|
# window, and the redis shared-state hop so all harness instances of one
|
|
# account coordinate. The loop DEFERS gated work with a logged reason; it
|
|
# never hard-fails. With no usage_url it runs on locally estimated
|
|
# consumption against the configured plan limits (see README: z.ai has no
|
|
# public usage endpoint yet — LIVE VERIFICATION open).
|
|
# [quota]
|
|
# enabled = true
|
|
# account = "zai-max-1" # plan label shared across instances
|
|
# plan_5h_credits = 28000 # Max plan; Lite 2000/10000, Pro 12000/60000
|
|
# plan_weekly_credits = 140000
|
|
# usage_url = "" # set when z.ai ships the endpoint
|
|
# key_ref = "env:HARNESS_ZAI_KEY" # bearer for usage_url; never logged
|
|
# poll_interval_secs = 300
|
|
# defer_at_pct = 85 # heavy classes defer, flash tier continues
|
|
# block_at_pct = 95 # everything defers until reset (the wall)
|
|
# # Peak = z.ai Mon-Fri 14:00-18:00 Singapore == 01:00-05:00 America/Chicago
|
|
# # in winter (00:00-04:00 during US DST — adjust in March/November).
|
|
# peak_start = "01:00"
|
|
# peak_end = "05:00"
|
|
# timezone = "America/Chicago"
|
|
# peak_weekdays_only = true
|
|
# peak_classes = ["study", "read"] # flash/LLM-lite classes allowed in peak
|
|
# # Shared state for the 9 instances across 2 hosts (redis docker container
|
|
# # on 192.168.3.78; see README runbook). Empty = local-only estimates.
|
|
# redis_url = "redis://192.168.3.78:6390/0"
|
|
|
|
# RESOURCE GATE (Redmine 491, off by default): read-only host monitor —
|
|
# loadavg, mem available, work_root disk free, IO pressure (/proc/pressure
|
|
# io, skipped when PSI is absent). The loop defers dispatch while busy.
|
|
# cgroup enforcement is deploy-time (README runbook).
|
|
# [resources]
|
|
# enabled = true
|
|
# max_load_avg = 6.0
|
|
# min_mem_available_mb = 2048
|
|
# min_disk_free_mb = 5120
|
|
# max_io_delay_pct = 90.0
|