Conversation
|
Please update your branch to the latest After updating, please verify:
Thanks. |
…ervice
Add install_windows.go that provides a native 'metronous install' command
on Windows:
1. Initializes ~/.metronous directory structure (via existing runInit)
2. Registers Metronous as a Windows service via kardianos/service
3. Starts the service immediately
4. Patches opencode.json (checks %APPDATA%\opencode first, then
falls back to ~/.config/opencode)
Update install_other.go build tag from '!linux' to '!linux && !windows'
so macOS and other platforms still get the stub, but Windows gets the
real implementation.
Includes 3 tests for patchOpencodeJSON: basic patching, APPDATA priority
over .config fallback, and missing file error handling.
Port the Linux MCP shim to Windows:
- Replace unix.Flock with windows.LockFileEx/UnlockFileEx for
serializing concurrent shim processes
- Replace syscall.SysProcAttr{Setsid: true} with CREATE_NEW_PROCESS_GROUP
and DETACHED_PROCESS flags for daemon detachment on Windows
- All JSON-RPC protocol handling, health checks, and tool forwarding
remain identical to the Linux implementation
Update mcp_shim_other.go build tag from '!linux' to '!linux && !windows'
so the stub only applies to macOS and other unsupported platforms.
- Add Windows installation section with PowerShell commands - Document elevated terminal requirement for service registration - Add manual service control commands (start/stop/status/uninstall) - Update architecture diagram to mention Windows SCM alongside systemd
Adds a normalized 0-1 composite score that combines accuracy (40%), latency (20%), tool success rate (20%), and ROI (20%) into a single comparable metric. Weights are configurable via thresholds.json. - internal/benchmark/score.go: ComputeCompositeScore pure function - internal/config/score_weights.go: ScoreWeights type with validation - internal/decision/engine.go: ScoreWeights accessor method - configs/thresholds.json: score_weights section added
Pure function CompareModels() produces side-by-side metric deltas between two benchmark runs with auto-generated recommendation text. Includes tie detection (delta < 0.01) and per-metric better/worse. - comparison.go: CompareModels, MetricDelta, ModelComparison types - comparison_test.go: table-driven tests for all comparison paths
OpenCode sometimes emits model names without provider prefix (e.g. "claude-opus-4-6" instead of "anthropic/claude-opus-4-6"). NormalizeModelName() infers the provider from known prefixes and GroupEventsByModel() applies normalization before grouping. Supported providers: anthropic, openai, google, mistral.
Extends BenchmarkStore with compound (agent_id, model) queries: - ListAgentModels: distinct agent+model pairs - GetLatestRunByAgentModel: most recent run per model - GetVerdictTrendByModel: verdict history per model - composite_score column added to benchmark_runs table - Compound index idx_benchmark_agent_model for query performance
Changes processAgent() to group events by model via GroupEventsByModel() before aggregation. Each (agent_id, model) pair gets independent metrics, evaluation, and composite score. Resolves the v1 limitation where all models were mixed into a single metric set per agent.
Rewrites benchmark tab to show one row per (agent, model) with: - Score column with color coding (green/yellow/red) - Model column with shortened names (opus-4-6 vs full path) - Verdict colors: KEEP=green, SWITCH=red, INSUFFICIENT=yellow - Ranked comparison panel (press 'c') with visual bars - Toggle NO DATA rows with 'h' key (hidden by default) The comparison panel shows all models for an agent ranked by composite score with proportional bars, BEST/KEEP/SWITCH/INSUFFICIENT labels, cost deltas, and a recommendation sentence from pairwise comparison.
Adds 'metronous web' command serving a dashboard at localhost:9100. Built with Go's embed FS, net/http, and a single HTML file using Tailwind CSS + Chart.js (CDN, no build step). API endpoints: - GET /api/overview: all latest runs per (agent, model) - GET /api/compare?agent=X: ranked model comparison with deltas - GET /api/trend?agent=X&model=Y: verdict history Dashboard features: - Dark mode, auto-refresh every 30s - Agent overview table grouped by type - Click agent → model ranking with bar charts - Verdict trend visualization - Only shows agents with actual benchmark data
- Tracking tab: session list with expandable events, 5s auto-refresh - i18n: EN/ES language selector with localStorage persistence - Backend string translations for context, recommendation, trend - Detail panel bugfix: compound key (agent+model) for row selection - Responsive: horizontal scroll on narrow viewports - Visibility API: pause/resume refresh when tab is hidden - Tracking API: /api/sessions and /api/sessions/events endpoints - EventStore passed alongside BenchmarkStore to web server
Documents the browser-based dashboard (metronous web) as an alternative to the TUI. Includes usage, flags, and architecture diagram update showing both dashboard options.
The Refresh button now executes a real benchmark before refreshing data, so samples update immediately instead of waiting for the daily scheduled run. Protected with mutex to prevent concurrent runs. - POST /api/benchmark/run endpoint triggers runner.RunWeekly - Runner instance created in web CLI with thresholds + decision engine - Frontend shows progress: "Running benchmark..." → "Done!" - i18n: benchmark status messages translated (EN/ES)
Configures sc failure recovery after service installation so the daemon restarts automatically if it crashes or the binary is replaced during an update (go install). Three restart attempts: 5s, 10s, 30s delays. Also documents the update flow in README: always run metronous install after updating the binary to ensure clean service state.
The web dashboard is now served directly by the daemon on port 9100. No need to run 'metronous web' separately — the browser dashboard is available as soon as the service starts. One service, one process: - MCP server (dynamic port for OpenCode shims) - Web dashboard (fixed port 9100 for browser) - Benchmark runner (on-demand from dashboard button) Architecture: metronous daemon (single process) ├── MCP ingest (dynamic port, shim→daemon) ├── Web dashboard (localhost:9100) └── Benchmark scheduler + on-demand runner The 'metronous web' command still works as a standalone fallback.
dea8d33 to
1979819
Compare
Alignment review — based on current state of `main`Hi! Reviewed this PR against what was merged into `main` over the last few days. Here's a summary of what needs to be reconciled before this can merge cleanly. What this PR adds (great work overall)
Conflicts and drift with `main`1. `BenchmarkRun` struct — field removals that broke `main`This PR removes `RunKind`, `WindowStart`, and `WindowEnd` from the top of the struct and re-adds them at the bottom. In `main` these fields are actively written by the runner and read by the TUI (Benchmark Detailed tab cycles by `WindowStart`). The reordering is fine but make sure the SQLite column mapping in `benchmark_store.go` is consistent — the store uses positional scan order in some queries. 2. `NormalizeModelName` — duplicate, different implementations`main` already has `store.NormalizeModelName()` (in `internal/store/`). This PR introduces `benchmark.NormalizeModelName()` in `internal/benchmark/normalize.go` with a different implementation (prefix inference table vs. the existing one). Before merging, decide on one canonical location and remove the other. Prefer keeping it in `store` since both runner and TUI already import it from there. 3. `score_weights` in `configs/thresholds.json` — conflicts with active config fields`main` removed `max_p95_latency_ms` and `min_tool_success_rate` from the active Config tab (they were noisy/always-1.0). This PR re-introduces `tool_success_rate` as a score weight (0.20). That's a different concept (a weight, not a threshold), but `MaxLatencyP95Ms` being used for `latency_norm` calculation needs to be validated — in `main` this field is present in `DefaultThresholds` but is not an active SWITCH trigger. Confirm the normalization fallback when `MaxLatencyP95Ms == 0`. 4. `ROI formula` mismatchIn `main`, ROI = `accuracy / cost_per_session` (tool_success_rate was dropped because it is always 1.0). This PR's `score_weights` still includes `tool_success_rate: 0.20` as a separate dimension in the composite score. That's a valid design choice, but it diverges from the current verdict logic. Make sure the decision engine verdict (SWITCH/URGENT_SWITCH) and the composite score use a consistent definition — or document the intentional divergence. 5. Tab order — README is outdated in this PRThe README diff in this PR still shows the old 4-tab layout and old tab order (Tracking first). `main` already has the correct 5-tab order: `[1] Benchmark Summary → [2] Benchmark Detailed → [3] Tracking → [4] Charts → [5] Config`. When you rebase, the README will conflict — use the version from `main` as the base and layer the web dashboard section on top. 6. `docs/` — new files on `main` not present here`main` now has `docs/ARCHITECTURE.md` and `docs/BENCHMARKS.md`. This PR still references the old `docs/architecture.md` and `docs/how-it-works.md`. After rebasing, update any cross-references. Suggested rebase stepsgit fetch origin
git rebase origin/main
# resolve conflicts in: README.md, internal/store/interface.go, internal/benchmark/fetcher.go
# consolidate NormalizeModelName → keep store.NormalizeModelName, delete benchmark/normalize.go
# verify benchmark_store.go column scan order matches updated BenchmarkRun structMinor notes
Let me know if any of the above needs clarification. Happy to review again after the rebase. |
|
Hi! Thanks for the comprehensive work on this PR. I've reviewed it against the current state of Current SituationSince this PR was opened on Mar 31,
In Git, What Needs to HappenYour PR is still valuable, but we need to rebase it cleanly against the current main. Here's the suggested workflow: git fetch origin
git rebase origin/main
# Resolve any conflicts (favor main when in doubt)
# Run tests to ensure everything still works
go test ./...
git push -f origin feat/benchmark-v2-web-dashboardKey Reconciliations to WatchWhen you rebase, you'll want to carefully review:
Next Steps
Looking forward to getting this merged once everything aligns. Great work so far! |
Updated Status — Apr 5, 2026Hi! Wanted to give you a comprehensive update since What landed on
|
| File | Expected conflict | Resolution |
|---|---|---|
internal/store/interface.go |
BenchmarkRun struct fields |
Keep main's struct, add CompositeScore float64 from your PR |
internal/benchmark/normalize.go |
Duplicate NormalizeModelName |
Delete your file, use store.NormalizeModelName() everywhere |
internal/runner/runner.go |
Per-model pipeline + active model lookup | Keep main's NewRunnerWithModelLookup wiring, integrate your GroupEventsByModel approach |
internal/store/sqlite/benchmark_store.go |
Column scan order | Keep main's scan order (positional), add composite_score column at the end |
README.md |
Tab order + dashboard section | Use main's README as base, add your web dashboard section after Tab 5 |
docs/ |
All doc files | Use main's versions, add web dashboard references where appropriate |
What from your PR is NOT yet in main (still valuable):
internal/benchmark/score.go— composite score (0-1 weighted normalization)internal/benchmark/comparison.go— pairwise model comparisoninternal/web/— browser-based dashboard atlocalhost:9100internal/cli/web.go—metronous webcommandconfig/score_weights.go— configurable weights- Windows service improvements
One design note on tool_success_rate: in main, this metric is excluded from both verdict triggers and ROI because it is always 1.0 in practice. If you include it as a composite score weight (0.20), it effectively becomes a dead dimension. Consider replacing it with a more meaningful signal or redistributing that weight. Happy to discuss.
After rebasing and resolving conflicts, run:
go test ./...Let me know if anything is unclear or if you want to discuss any of the design decisions. Looking forward to getting this merged!
|
Auditoría técnica (hoy): este PR no es mergeable en su estado actual.\n\nBlockers:\n1) Tiene conflictos de merge con (merge local Auto-merging README.md |
|
Corrección de formato del comentario anterior: Dictamen: CHANGES_REQUESTED (no merge hoy). Blockers concretos:
Para destrabar:
Con eso listo, se puede re-auditar para merge. |
…oard Reemplaza #4 con conflictos resueltos contra main, priorizando comportamiento vigente en main donde hubo choques.
|
Se destrabó este trabajo mediante PR reemplazo en rama de origin: #21 (mergeado).\n\nMotivo: la rama head de este PR es cross-repo (yeerliin) y no quedó mergeable contra main.\nAcción aplicada: se resolvieron conflictos contra main priorizando comportamiento vigente en choques, se abrió PR #21 y se mergeó por squash.\n\nCommit final en main: 388e8e9 |
Summary
This PR enhances the benchmark system to evaluate each (agent, model) combination independently instead of mixing all models into one metric set per agent. It also adds a browser-based web dashboard embedded directly in the daemon — one service, one process, everything unified.
Problem
sdd-orchestratorran with opus, sonnet, and gpt, all events were aggregated together. If one model degraded, another compensated statistically, masking the problem.Architecture
What changed
Per-model evaluation pipeline:
GroupEventsByModel()partitions events by model before aggregation(agent_id, model)pair gets independent metrics, thresholds evaluation, and verdictNormalizeModelName()prevents duplicates from inconsistent provider prefixesComposite score (0-1):
ComputeCompositeScore()= accuracy x 0.40 + (1-latency_norm) x 0.20 + tool_rate x 0.20 + roi_clamped x 0.20score_weightsin thresholds.jsonPairwise model comparison:
CompareModels()pure function with per-metric deltas and auto-generated recommendationStore extensions:
ListAgentModels(),GetLatestRunByAgentModel(),GetVerdictTrendByModel()composite_scorecolumn + compound indexUnified daemon with embedded web dashboard:
metronous webprocess neededmetronous webstill works as standalone fallbackWeb Dashboard features:
Windows service stability:
Commits (16)
feat(install): Windows service via kardianos/servicefeat(mcp): Windows MCP stdio shimdocs: Windows installation instructionsfeat(benchmark): per-model evaluationfeat(benchmark): composite scorefeat(benchmark): pairwise model comparisonfix(benchmark): normalize model namesfeat(store): per-model queriesfeat(runner): per-model pipelinefeat(tui): ranked comparison panelfeat(web): browser-based benchmark dashboardfeat(web): tracking tab, i18n, detail paneldocs: add web dashboard section to READMEfeat(web): on-demand benchmark runfix(install): SCM auto-recovery on failurefeat(daemon): embed web dashboard into daemonTest results (41/41 PASS)
Installation (Windows)
Updating