import os, json, urllib.request URL = os.environ["REDMINE_URL"] KEY = os.environ["REDMINE_API_KEY"] def api(method, path, body=None): data = json.dumps(body).encode() if body is not None else None req = urllib.request.Request(URL + path, data=data, headers={"X-Redmine-API-Key": KEY, "Content-Type": "application/json"}, method=method) resp = urllib.request.urlopen(req, timeout=15) raw = resp.read().decode() return json.loads(raw) if raw.strip() else {} # 1. Re-track: builds/designs -> Feature (2), studies/selftests -> Support (3) for iid in (490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 483, 484, 486): api("PUT", "/issues/%d.json" % iid, {"issue": {"tracker_id": 2}}) for iid in (480, 481, 482, 485, 487): api("PUT", "/issues/%d.json" % iid, {"issue": {"tracker_id": 3}}) print("re-tracked: majors->Feature, studies->Support") # 2. Rewrite major bodies in CommonMark MD = {} MD[490] = """# Quota monitoring + back-pressure + usage management ## Objective Never again lose turns to a blind quota wall (8/28 19:00 incident killed two dispatched turns). Maximize 24x7 utilization of the z.ai MAX plan, with automatic tier-down before exhaustion and full visibility into bucket state. ## Background Spec of record: `docs/SPEC-20260829-charles-brief.md` (section "It's a marathon"). Charles upgraded to the MAX plan 8/29 (all buckets zeroed). Legacy-plan economics end 9/22 ($200/mo budget after: 2x z.ai + OpenRouter). Confirmed by build turn: peak hours = Mon-Fri 14:00-18:00 SGT (01:00-05:00 CST); buckets = 5-hour + weekly credits. ## Scope - Poll the z.ai coding-plan usage/limits endpoint; parse per-bucket used/limit/reset into a `QuotaSnapshot` (auth via key_ref, never logged). - Central quota state shared by 9 harness instances on 2 hosts: decide Redis container vs LiteLLM-native (postgres spend DB already live at 192.168.3.78:4001) with documented rationale. - Back-pressure in the loop: peak window = flash-tier classes only, heavy classes deferred with logged reason; quota-low = defer + surface, never hard-fail. - All thresholds/schedule/TZ in the `[quota]` harness.toml section (CST default). - Per vertical/class token+model accounting into the JSONL state (feeds future per-instance Discourse usage reports). ## Acceptance criteria - The 8/28 wall scenario replayed as a test: loop defers instead of dying. - MAX-plan bucket shapes parse correctly (upgraded plan). - Config surface documented in README per docs standard. ## References / Status TASK-20260829-0500-quota.md (mopac-pmo repo) — **IN PROGRESS** (work-q9).""" MD[491] = """# System resource monitoring + cgroup control ## Objective The loop must not dispatch builds into an overloaded host; wall-clock waste from resource contention is a real cost per the spec. ## Scope - Read-only monitor: load avg, mem available, disk free, IO delay (from /proc + /sys), polled alongside quota. - Busy thresholds configurable (quota/resources config section); loop defers dispatch when busy, reason logged in JSONL. - cgroup enforcement documented as deploy-time runbook section (deploy ticket #494 consumes it). ## Acceptance criteria - Injected fake /proc fixtures drive defer/pass decisions in tests. - README documents thresholds + how to tune. ## Status IN PROGRESS (bundled with #490 in work-q9 turn).""" MD[492] = """# Phase-aware model selection ## Objective Route work *phases* to tiers: planning on a heavy model, chunk work on flash — per spec Roadmap 2. Quota-optimal without sacrificing plan quality. ## Scope - Phase source precedence: (1) Redmine custom field (configurable name, values plan/implement/review/grind), (2) `models.phases` config map phase-to-class, (3) fallback existing class map (behavior unchanged). - REPORT header records model + phase + class used (telemetry for the future auto-classifier, v2, out of scope). - Unknown phase value = hard config error naming valid phases. ## Acceptance criteria - Table-driven tests: custom-field phase beats fallback; unknown phase errors loudly. - README config tables updated. ## Status QUEUED (TASK-20260829-0700-dispatcher.md; runs after deploy+briefing). Blocked by #490.""" MD[493] = """# Queue selection: priority / due-date / relations / quota / resources ## Objective The loop picks the *right* next task, not just the next task (spec Roadmap 3). ## Scope - Intake sorted priority desc, due_date asc (`redmine.selection` config). - Redmine relations: skip issues blocked by open related issues, log reason. - Quota + resource gates consulted before dispatch (builds on #490/#491). - Every selection/deferral decision appended to loop JSONL (auditable). ## Acceptance criteria - Test proves a priority-5 due-today ticket beats priority-3 no-due. - Blocked-relation skip covered by fake-Redmine test. ## Status QUEUED (same turn as #492). Blocked by #490.""" MD[494] = """# Multi-account harness deployment: 9 accounts, 2 hosts ## Objective MOPAC runs one Go daemon per Linux account across both VMs. PMO runtime cannot ssh/sudo, so this ticket ships everything needed for a ~10-minute Charles execution window. ## Accounts - ultix-streaming: reachableceo, TSGBOD, TSGCOO, TSGCTO, TSGCCO - ultix-offstage (via ssh reachableceo-offstage): reachableceo-offstage, COSRCEO-Personal, COSRCEO-Biz, COSWFO ## Scope - `deploy/accounts.tsv` (account, host, vertical, redmine scope, port scheme). - `deploy/install-account.sh` — idempotent, runs as target user, `~/.mopac` layout, template-substituted harness.toml, refuses to overwrite existing config/secrets. - Static release binary via docker builder (Makefile `release` target, CGO=0). - Distinct state dirs + ports per account (collision-free co-hosting). - `deploy/runbook.md` — exact Charles sequence per host, verify steps (healthz + first `loop --once`), rollback. ## Acceptance criteria - Install script idempotence tested in a fake HOME. - Port-derivation + template substitution tested. ## Status QUEUED next after quota turn (TASK-20260829-0900-deploy.md).""" MD[495] = """# COS morning briefing pipeline (0630 CST daily) ## Objective Charles gets a daily 0630 CST briefing from all three COS instances via Discourse (Redmine fallback). Beta 8/31 0600; first live briefing 9/1 0630. ## Scope - Part A: `ukrrs/mopac-discourse-go` client (categories/topics/posts, key via key_ref, fake-server tests; note current key 403s on category creation — admin key on the 1900 list). - Part B: `harness brief` subcommand: digest window since last marker; sources = Redmine scope activity + Gitea open PRs + quota snapshot; clean markdown (per-vertical shipped/moved/incoming/PRs/quota); post to Discourse category, fallback = journal note on configured Redmine issue, never silent drop; idempotent per window. ## Acceptance criteria - Sample briefing rendered with fake data in the REPORT. - Delivery verified both paths (discourse + fallback). ## Status QUEUED after deploy (TASK-20260829-1100-briefing.md).""" MD[496] = """# Review chain agents + kick-back protocol ## Objective Work ascends: IC builder > IC reviewer > manager > director > VP > SVP > COO/CTO/CCO review; any level can kick back down/across. Foundation of the morning briefing quality. ## Scope DESIGN FIRST (post-beta iteration): state machine for review levels, kick-back semantics, how it maps to Redmine workflows/statuses, agent-per-level prompts. Implementation tickets spawn from the design. ## Status BACKLOG (design doc ticket to be filed post-beta).""" MD[497] = """# keyproxy: live Bitwarden/Vault backends + MCP/CLI frontend ## Objective Real credential material only ever lives in Bitwarden (Vaultwarden) + Hashicorp Vault. keyproxy resolves `mpk_` placeholders at the wire; harness/LLM see placeholders only. Secrets never on disk. ## Scope - Integrate `ukrrs/mopac-bitwarden-go` v0 client (shipped 8/28, fake-tested) into the keyproxy `bitwarden:` backend (currently 501 stub). - Vault KV v2 + AppRole backend via official MPL-2.0 Go client. - Bitwarden-held SSH keys via ssh-agent/BW socket for Linux connections. - MCP/API/CLI frontend surface for the harness. ## Acceptance criteria - keyproxy resolves a BW-backed ref end-to-end with live creds. - Redaction tests prove material never appears in logs/errors. ## Status BLOCKED on Charles connecting BW machine creds (1900 tonight).""" MD[498] = """# MCP/CLI outbound integration fleet (umbrella) ## Objective MOPAC acts on the whole TSYS software estate via API (Cloudron accounts): google suite, IMAP, Linkwarden, Google Chat, Signal, Discord, Redmine, Discourse, Gitea, browsers, Linux ssh/MCP (Kali/OpenVAS for the security queue), Cloudron, HomeAssistant, Kubernetes. ## Scope One integration = one standalone ukrrs repo (loose coupling, config-driven, exec+JSON contracts). OAuth one-time browser flow preferred; QR for Signal. Children #500-#514 carry per-integration scope. Language tiers apply (node = last resort + consult Charles first). ## Status BACKLOG umbrella — children #500-#514.""" MD[499] = """# Beautiful PDF generation pipeline ## Objective Client-grade PDFs from markdown: budgets, travel itineraries, project plans, proposals, consulting reports, multi-year financials, engineering docs, charts/diagrams. Feeds COS briefings + TSYS deliverables. ## Scope - New repo `ukrrs/mopac-pdf`: Go CLI/library, markdown + front-matter input. - Engine decision typst vs pandoc+latex (dockerized, digest-pinned; prebuilt engines are supply chain — our code stays Go). - Templates v0: "report" (title page, TOC, headers/footers) + "brief" (1-3 page exec summary). - Charts: pure-Go rendering embedded from fenced data blocks (one type proven v0). - Golden tests: fixture .md -> PDF (non-empty, page count). ## Acceptance criteria - Sample outputs committed; briefing pipeline call-path documented. ## Status IN PROGRESS (work-q13, TASK-20260829-1300-pdf.md).""" for iid, desc in MD.items(): api("PUT", "/issues/%d.json" % iid, {"issue": {"description": desc}}) print("markdown:", iid)