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
Turn machinery refactored so the upcoming serve front door reuses it
instead of copying it: the round loop is now runTurn over arbitrary
message history with an optional tool palette and forwarded max_tokens/
temperature. The serve path (ServeTurn) is stateless chat over client
history with tools hard-off: a tool call the model produces anyway is
refused as a tool result, never executed. Also exposes the model router
(so serve can build its catalog) and a sorted class list; llm requests
can now carry temperature.
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
`harness loop` polls the /issues.json intake on an interval (default
120s, --once for cron-style single scans), runs one bounded turn per
new/updated issue — deduped by issue id + updated_on in an append-only
loop.jsonl — then writes the REPORT back as a Redmine journal note,
transitions status per [redmine.status_map] (In Progress -> Done by
name, resolved via /issue_statuses.json), and refreshes the dedup marker
to the post-writeback updated_on so its own notes never re-trigger it.
Turns are sequential (v0); failed turns are recorded, not retried, so a
down proxy cannot hot-loop the poll. The optional Gitea REPORT commit
([gitea] commit_reports, off) rides the same flow. No slot files, no
doorbell screens, no queue scripts — the bash middle layer is replaced,
not wrapped.
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
The old path carried the reachableceo org prefix while the repo lives at
git.knownelement.com/ukrrs/MOPAC; the drift was cited as doc rot. go.mod
and every internal import updated; build/vet/test clean in the builder.
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
`harness events` serves POST /hooks/{redmine,discourse,gitea} and
GET /healthz on stdlib net/http until SIGINT/SIGTERM (graceful
shutdown). Flow per delivery: verify (401, generic body) -> normalize
(400) -> append-only store with dedup (200 stored/duplicate + id +
action) -> hand stored actionable events to the conductor via the
Dispatcher interface. Conductor.DispatchEvent is the wiring point and
prints what it will do once phase 3 lands the real event-to-turn
dispatch. Body cap 1 MiB (413); audit log carries normalized fields +
digest only, never headers, secrets or payload. Server refuses to start
without at least one resolvable webhook secret.
`harness once` runs one bounded iteration (intake -> routing -> bounded turn
-> REPORT) then exits, so callers chain it without a daemon. The bounded
turn loops request -> tool calls -> results up to max_rounds; gate denials
feed back to the model and are counted instead of failing the turn.
--dry-run prints the plan (task, resolved model, tool bounds) and makes zero
LLM calls; --demo runs the [demo] issue ("tell me about yourself" through
LiteLLM -> GLM self-description as the REPORT), the MVP acceptance bar.
Distinct exit codes for config/usage, intake, and llm failures. Ships
harness.toml.example (scope query, tier map, allow-lists); real configs are
gitignored. Loop tests run end-to-end against a scripted fake OpenAI server:
demo turn, dry-run zero-call, tool round-trip, denial counting, round limit,
and error-class mapping.
💘 Generated with Crush
Assisted-by: Crush:glm-5.2