ops(redmine): re-track tickets (features/studies out of Bug), CommonMark bodies

Bug tracker now reserved for actual defects: builds/designs -> Feature,
studies/selftests -> Support. All major ticket bodies rewritten in
CommonMark (# headers, fenced refs) replacing Textile syntax.
This commit is contained in:
2026-08-29 05:29:52 -05:00
parent d13559f9a2
commit 23ca361025
4 changed files with 201 additions and 2 deletions
+198
View File
@@ -0,0 +1,198 @@
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)