ai-consultants 2.20.0 → 2.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/CLAUDE.md +25 -8
  2. package/README.md +9 -2
  3. package/SKILL.md +10 -4
  4. package/bin/ai-consultants +5 -0
  5. package/docs/COST_RATES.md +1 -1
  6. package/docs/SETUP.md +9 -5
  7. package/docs/cost_rates.json +1 -1
  8. package/docs/releases/v2.21.0.md +65 -0
  9. package/package.json +1 -1
  10. package/references/configuration.md +35 -10
  11. package/scripts/config.sh +58 -12
  12. package/scripts/consult_all.sh +31 -0
  13. package/scripts/doctor.sh +6 -5
  14. package/scripts/lib/api_query.sh +9 -0
  15. package/scripts/lib/common.sh +58 -5
  16. package/scripts/lib/orchestration.sh +33 -5
  17. package/scripts/lib/personas.sh +10 -3
  18. package/scripts/lib/reflection.sh +4 -1
  19. package/scripts/lib/reliability.sh +132 -0
  20. package/scripts/lib/stance.sh +70 -0
  21. package/scripts/lib/voting.sh +73 -20
  22. package/scripts/peer_review.sh +18 -9
  23. package/scripts/query_gemini.sh +12 -7
  24. package/scripts/query_kilo.sh +7 -0
  25. package/scripts/query_kimi.sh +24 -4
  26. package/scripts/query_minimax.sh +72 -7
  27. package/scripts/release.sh +220 -0
  28. package/scripts/roster_calibrate.sh +8 -2
  29. package/scripts/synthesize.sh +12 -5
  30. package/scripts/test_e2e.sh +84 -0
  31. package/scripts/test_fixtures/stub_cli.sh +45 -0
  32. package/scripts/test_release.sh +220 -0
  33. package/scripts/test_reliability.sh +83 -0
  34. package/scripts/test_roster_calibrate.sh +22 -7
  35. package/scripts/test_stance.sh +67 -0
  36. package/scripts/test_suite.sh +11 -0
  37. package/scripts/test_update_clis.sh +79 -0
  38. package/scripts/test_user_config.sh +45 -1
  39. package/scripts/test_voting_metadata.sh +69 -0
  40. package/scripts/update_clis.sh +238 -0
package/CLAUDE.md CHANGED
@@ -6,7 +6,7 @@ AI Consultants is a multi-model AI deliberation system that queries up to 15 AI
6
6
 
7
7
  **Self-Exclusion**: The invoking agent is automatically excluded from the panel to prevent self-consultation. Claude Code won't query Claude, Codex CLI won't query Codex, etc.
8
8
 
9
- **Version**: 2.20.0
9
+ **Version**: 2.21.0
10
10
 
11
11
  ## Distribution
12
12
 
@@ -38,6 +38,7 @@ ai-consultants/
38
38
  │ ├── consult_all.sh # Main orchestrator - entry point
39
39
  │ ├── config.sh # Centralized configuration
40
40
  │ ├── doctor.sh # Diagnostic and auto-fix tool (v2.2)
41
+ │ ├── update_clis.sh # Check/update installed consultant CLIs (v2.21)
41
42
  │ ├── peer_review.sh # Anonymous peer review (v2.2)
42
43
  │ ├── roster_audit.sh # Roster uncorrelated-value audit (v2.20)
43
44
  │ ├── roster_calibrate.sh # Measured capability calibration, Tier A (v2.20)
@@ -59,6 +60,7 @@ ai-consultants/
59
60
  │ ├── voting.sh # Voting/consensus + confidence intervals
60
61
  │ ├── routing.sh # Smart routing + cost-aware routing
61
62
  │ ├── orchestration.sh # Dynamic orchestration planner + shapes (v2.16)
63
+ │ ├── stance.sh # Stance-based semantic consensus (v2.21, opt-in)
62
64
  │ ├── session.sh # Session management
63
65
  │ ├── costs.sh # Cost tracking + response limits
64
66
  │ ├── cache.sh # Semantic caching (v2.3)
@@ -285,7 +287,7 @@ curl -fsSL https://ampcode.com/install.sh | bash
285
287
  ```
286
288
 
287
289
  **Environment Variables:**
288
- - `ENABLE_AMP` - Enable Amp consultant (default: false)
290
+ - `ENABLE_AMP` - Enable Amp consultant (default: true)
289
291
  - `AMP_CMD` - CLI command (default: amp)
290
292
  - `AMP_TIMEOUT` - Timeout in seconds (default: 180)
291
293
  - `AMP_API_KEY` - API key for authentication
@@ -326,7 +328,7 @@ Qwen3 now supports CLI/API mode switching using the qwen-code CLI.
326
328
  export QWEN3_USE_API=false
327
329
  ./scripts/consult_all.sh "question"
328
330
 
329
- # API mode (default, preserves v2.6 behavior)
331
+ # API mode (opt-in)
330
332
  export QWEN3_USE_API=true
331
333
  export QWEN3_API_KEY="your-dashscope-key"
332
334
  ./scripts/consult_all.sh "question"
@@ -342,7 +344,7 @@ npm install -g @qwen-code/qwen-code@latest
342
344
  ## v2.6 Features
343
345
 
344
346
  ### CLI/API Mode Switching
345
- Five consultants can now switch between CLI and API mode: **Gemini, Codex, Claude, Mistral, Qwen3**.
347
+ Six consultants can now switch between CLI and API mode: **Gemini, Codex, Claude, Mistral, Qwen3, MiniMax**.
346
348
 
347
349
  When API mode is enabled for an agent, CLI mode is disabled (mutual exclusivity).
348
350
 
@@ -379,6 +381,7 @@ New environment variables in `config.sh`:
379
381
  | `CLAUDE_USE_API` | false | Use Anthropic API instead of claude CLI |
380
382
  | `MISTRAL_USE_API` | false | Use Mistral API instead of vibe CLI |
381
383
  | `QWEN3_USE_API` | false | Use DashScope API instead of qwen CLI (v2.7) |
384
+ | `MINIMAX_USE_API` | false | Use MiniMax API instead of the mmx CLI (v2.21) |
382
385
  | `GEMINI_API_URL` | https://generativelanguage.googleapis.com/v1beta/models | Google AI endpoint |
383
386
  | `CODEX_API_URL` | https://api.openai.com/v1/chat/completions | OpenAI endpoint |
384
387
  | `CLAUDE_API_URL` | https://api.anthropic.com/v1/messages | Anthropic endpoint |
@@ -649,7 +652,7 @@ for f in scripts/*.sh scripts/lib/*.sh; do bash -n "$f" && echo "OK: $f"; done
649
652
  | Variable | Default | Description |
650
653
  |----------|---------|-------------|
651
654
  | `INVOKING_AGENT` | unknown | Agent invoking the skill (for self-exclusion) |
652
- | `ENABLE_CLAUDE` | false | Enable Claude consultant |
655
+ | `ENABLE_CLAUDE` | true | Enable Claude consultant (auto-excluded under Claude Code) |
653
656
  | `ENABLE_DEBATE` | false | Enable Multi-Agent Debate |
654
657
  | `DEBATE_ROUNDS` | 1 | Number of debate rounds |
655
658
  | `ENABLE_SYNTHESIS` | true | Auto-synthesis of responses |
@@ -676,15 +679,19 @@ for f in scripts/*.sh scripts/lib/*.sh; do bash -n "$f" && echo "OK: $f"; done
676
679
  | `ENABLE_CAPABILITY_ROUTING` | false | Capability-aware panel composition (v2.20, opt-in) |
677
680
  | `CAPABILITY_WEIGHT_STRENGTH` | 10 | Vote-weight modulation: conf*(S+cap)/S (v2.20) |
678
681
  | `CAPABILITY_DEFAULT` | 5 | Fallback capability for a missing consultant/axis (v2.20) |
682
+ | `ENABLE_STANCE_CONSENSUS` | false | Enumerated-stance exact-match consensus (v2.21, opt-in; +1 LLM call/run) |
683
+ | `STANCE_MAX_OPTIONS` | 5 | Max stance options generated per question (v2.21) |
679
684
  | `ENABLE_BUDGET_LIMIT` | false | Budget enforcement (v2.4, opt-in) |
680
685
  | `BUDGET_ACTION` | warn | Action on budget exceeded: warn/stop (v2.4) |
681
686
  | `QWEN3_USE_API` | false | Use DashScope API instead of qwen CLI (v2.7) |
682
687
  | `QWEN3_CMD` | qwen | Qwen CLI command (v2.7) |
683
- | `ENABLE_KIMI` | false | Enable Kimi consultant (v2.9) |
688
+ | `ENABLE_KIMI` | true | Enable Kimi consultant (v2.9) |
684
689
  | `KIMI_CMD` | kimi | Kimi CLI command (v2.9) |
685
690
  | `KIMI_MODEL` | kimi-code/kimi-for-coding | Kimi model (v2.9) |
686
- | `ENABLE_MINIMAX` | false | Enable MiniMax consultant (v2.10) |
687
- | `MINIMAX_API_KEY` | - | MiniMax API key (v2.10) |
691
+ | `ENABLE_MINIMAX` | true | Enable MiniMax consultant (v2.10; CLI via mmx v2.21) |
692
+ | `MINIMAX_USE_API` | false | Use MiniMax API instead of the mmx CLI (v2.21) |
693
+ | `MINIMAX_CMD` | mmx | MiniMax CLI command (v2.21) |
694
+ | `MINIMAX_API_KEY` | - | MiniMax API key (API mode only) (v2.10) |
688
695
  | `MINIMAX_MODEL` | MiniMax-M2.7 | MiniMax model (v2.10) |
689
696
 
690
697
  ## External Dependencies
@@ -699,6 +706,7 @@ for f in scripts/*.sh scripts/lib/*.sh; do bash -n "$f" && echo "OK: $f"; done
699
706
  - `kimi` CLI - Kimi Code (v2.9)
700
707
  - `claude` CLI - Claude (v2.2, also used for synthesis)
701
708
  - `qwen` CLI - Qwen via qwen-code (v2.7, optional)
709
+ - `mmx` CLI - MiniMax via mmx-cli (v2.21, optional; `npm i -g mmx-cli`, auth `mmx auth login`)
702
710
  - `ollama` CLI - Local models (v2.2)
703
711
  - `jq` - JSON parsing
704
712
 
@@ -834,6 +842,15 @@ curl -fsSL https://raw.githubusercontent.com/matteoscurati/ai-consultants/main/s
834
842
 
835
843
  ## Changelog
836
844
 
845
+ ### v2.21.0
846
+ - **CLI-first transport (principle).** Every `*_USE_API` defaults `false`; a consultant with a CLI always uses it, API is opt-in (CLI-less model or explicit choice). `ENABLE_AMP`/`ENABLE_CLAUDE` default true.
847
+ - **MiniMax via `mmx` CLI** (was API-only). `query_minimax.sh` runs `mmx text chat --non-interactive …` in CLI mode; `config.sh` adds `MINIMAX_CMD` + auto-resolves `MINIMAX_USE_API` (API iff `MINIMAX_API_KEY` set — back-compat), drops MiniMax from `API_CONSULTANTS`; `doctor.sh` treats it as switchable.
848
+ - **`scripts/update_clis.sh`** (+ `ai-consultants update-clis`): per CLI-backed consultant, `detect_method` (brew formula/cask, npm, uv, pipx, pip, self-update, curl installer) → update. `--dry-run`, `--only`.
849
+ - **Stance consensus** (opt-in, `ENABLE_STANCE_CONSENSUS`): new `lib/stance.sh` (`generate_stance_options` — one synthesizer call wrapped in `run_with_timeout`; `_stance_clean`; `build_stance_prompt`). `consult_all.sh` generates the shared set → `stance_options.json` + exports `STANCE_OPTIONS_PROMPT`; injected via `personas.sh` (CLI) + `api_query.sh` (API). `voting.sh::calculate_consensus_score` scores the plurality stance over the PANEL size (not the count of emitted stances). `STANCE_MAX_OPTIONS`, `STANCE_TIMEOUT`.
850
+ - **Smoke-test reliability**: `query_gemini.sh` (`agy -p "$QUERY"`), `query_kimi.sh` (`stream-json` + `_kimi_extract_content`), `query_kilo.sh` (de-fence). Shared `_is_consultant_response_file` metadata filter in `common.sh`, applied across `voting.sh`/`synthesize.sh`/`peer_review.sh`/`reflection.sh`. `orchestration.sh` stalled→stable relabel (set -e-safe) + debate-round promote validation.
851
+ - **Persona fix**: `_AGENT_DEFAULT_PERSONAS` mapped `GLM|17`/`DEEPSEEK|7` (each got the other's persona) → `GLM|7`/`DEEPSEEK|17`.
852
+ - **Dev/reliability**: reliability tracking + Amp/Claude default-on; offline e2e test; `scripts/release.sh` version-bump automation; calibration fixes (diagnosable peer-review failures + cost-only path); `test_user_config.sh` XDG test made hermetic (unset the exported `_AI_CONSULTANTS_XDG_*` intermediates). `npm test` = 18 suites. Hardened by a `/code-review max` pass.
853
+
837
854
  ### v2.20.0
838
855
  - **Capability axes (borrowed from the delegation-kit cost/intelligence/taste table).** `references/affinity.json` → v1.1: new `capabilities` (per-consultant {intelligence, taste, cost}, 1-10), `category_axis` (category → the quality axis it stresses: taste for API_DESIGN/ARCHITECTURE/CODE_REVIEW/GENERAL, intelligence otherwise), `capability_default`. New `lib/routing.sh::get_capability` / `get_category_axis` (cached like `get_affinity`).
839
856
  - **Capability-weighted voting** (`ENABLE_CAPABILITY_WEIGHTING`, opt-in): `lib/voting.sh::_effective_vote_weight` modulates a vote by the consultant's capability on the run's axis — `confidence × (S + cap) / S` (`S = CAPABILITY_WEIGHT_STRENGTH`, default 10). Applied in `calculate_weighted_recommendation` and `calculate_final_score` (normalizer uses the same weight → 1-10 scale preserved). Axis from `QUESTION_CATEGORY`. `cost` never weights a vote (tie-break intelligence > taste > cost).
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
- # AI Consultants v2.20.0
1
+ # AI Consultants v2.21.0
2
2
 
3
3
  > **A harness for every question.** A panel of up to 15 frontier models that writes its own playbook per question — fan out, debate to convergence, cross-examine under adversarial review, or run a tournament — and checks its work before it reaches you.
4
4
 
5
- [![Version](https://img.shields.io/badge/version-2.20.0-blue.svg)](https://github.com/matteoscurati/ai-consultants)
5
+ [![Version](https://img.shields.io/badge/version-2.21.0-blue.svg)](https://github.com/matteoscurati/ai-consultants)
6
6
  [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
7
7
  [![Claude Code Skill](https://img.shields.io/badge/Claude%20Code-Skill-orange.svg)](https://docs.anthropic.com/en/docs/claude-code/skills)
8
8
  [![GitHub stars](https://img.shields.io/github/stars/matteoscurati/ai-consultants?style=social)](https://github.com/matteoscurati/ai-consultants)
@@ -580,6 +580,8 @@ ai-consultants doctor --json # JSON for automa
580
580
  ai-consultants doctor --live # Real ping per consultant — catches installed-but-unauthenticated CLIs
581
581
  ai-consultants doctor --suggest-config # Print recommended ENABLE_* based on detected CLIs
582
582
  ai-consultants doctor --suggest-preset --question "..." # Recommend preset + strategy for a question
583
+ ai-consultants update-clis # Check & update every installed consultant CLI
584
+ ai-consultants update-clis --dry-run # Preview: each CLI's install method + update command
583
585
  ```
584
586
 
585
587
  > When a consultant fails mid-consultation, the run surfaces the captured reason (e.g. `CLI not found`, `401 Unauthorized`) instead of a bare "Failed", so you can tell *not installed* from *not authenticated* from *transient*.
@@ -609,6 +611,11 @@ The **shape** is chosen per question (or pinned via `ORCHESTRATION_MODE`):
609
611
 
610
612
  Set `ORCHESTRATION_MODE=fixed` for the classic fixed-round pipeline.
611
613
 
614
+ Consensus is measured lexically by default; set `ENABLE_STANCE_CONSENSUS=true`
615
+ (opt-in, v2.21) to have the panel pick from a shared set of enumerated stance
616
+ options and score agreement by exact match instead — see
617
+ [references/configuration.md](references/configuration.md#semantic-consensus-v221-opt-in).
618
+
612
619
  With peer review:
613
620
  ```
614
621
  Responses -> Anonymize -> Peer Ranking -> De-anonymize -> Peer Scores
package/SKILL.md CHANGED
@@ -5,10 +5,10 @@ license: MIT
5
5
  compatibility: Requires bash, jq, and at least 2 AI CLI tools (agy, codex, vibe, etc.). macOS and Linux.
6
6
  metadata:
7
7
  author: matteoscurati
8
- version: 2.20.0
8
+ version: 2.21.0
9
9
  ---
10
10
 
11
- # AI Consultants v2.20.0 - AI Expert Panel
11
+ # AI Consultants v2.21.0 - AI Expert Panel
12
12
 
13
13
  **A harness for every question.** Convene a panel of AI "consultants" for coding questions — and let the engine pick *how* they deliberate: a quick read, a convergence loop, an adversarial refutation gate, a tournament, or an exhaustive sweep. Each consultant has a **configurable persona** that shapes its analysis.
14
14
 
@@ -42,11 +42,12 @@ Configuration (presets, strategies, features, personas, API keys) can be managed
42
42
  | **Kimi** | `kimi` | The Eastern Sage | Holistic, balanced perspectives |
43
43
  | **Claude** | `claude` | The Synthesizer | Big picture, synthesis |
44
44
  | **Qwen** | `qwen` | The Analyst | Data-driven, metrics |
45
+ | **MiniMax** | `mmx` | The Pragmatic Optimizer | Performance, efficiency, pragmatism |
45
46
  | **Ollama** | `ollama` | The Local Expert | Privacy-first, zero cost |
46
47
 
47
- **API-only consultants**: GLM (The Methodologist), Grok (The Provocateur), DeepSeek (The Code Specialist), MiniMax (The Pragmatic Optimizer)
48
+ **API-only consultants**: GLM (The Methodologist), Grok (The Provocateur), DeepSeek (The Code Specialist)
48
49
 
49
- **CLI/API Mode**: Gemini, Codex, Claude, Mistral, and Qwen can switch between CLI and API mode via `*_USE_API` environment variables. Gemini auto-selects API mode when `GEMINI_API_KEY` is set (no `agy` install needed) and the CLI otherwise.
50
+ **CLI/API Mode**: Gemini, Codex, Claude, Mistral, Qwen, and MiniMax can switch between CLI and API mode via `*_USE_API` environment variables. Gemini auto-selects API mode when `GEMINI_API_KEY` is set (no `agy` install needed) and the CLI otherwise.
50
51
 
51
52
  **Self-Exclusion**: The invoking agent is automatically excluded from the panel. When invoked from Claude Code, Claude is excluded; when invoked from Codex CLI, Codex is excluded, etc.
52
53
 
@@ -96,6 +97,7 @@ Query is classified, then sent to consultants in parallel. Responses are scored,
96
97
  | **Smart Routing** | Auto-select best consultants per question type | `ENABLE_SMART_ROUTING` |
97
98
  | **Cost Tracking** | Track API usage costs | `ENABLE_COST_TRACKING` |
98
99
  | **Panic Mode** | Auto-add rigor when uncertainty detected | `ENABLE_PANIC_MODE` |
100
+ | **Stance Consensus** | Panel picks from enumerated stances; agreement by exact match (opt-in, v2.21) | `ENABLE_STANCE_CONSENSUS` |
99
101
 
100
102
  ## Configuration
101
103
 
@@ -136,6 +138,10 @@ Diagnose and fix issues:
136
138
  ./scripts/doctor.sh --live # Real ping per consultant — catches installed-but-unauthenticated CLIs
137
139
  ```
138
140
 
141
+ Keep the consultant CLIs current with `ai-consultants update-clis` (`--dry-run` to
142
+ preview, `--only <cli>` to target one) — it detects how each CLI was installed
143
+ (brew/npm/uv/pipx/curl/self-update) and updates it.
144
+
139
145
  When a consultant fails during a consultation, the run prints the captured reason (e.g. "CLI not found", "401 Unauthorized"). The run is also **graded** MET/DEGRADED/FAILED by how many responded (`QUORUM_MIN`), with a "Diagnosed Failures" section in the report. Set `ENABLE_HEALTH_GATE=true` to ping consultants and drop the dead ones *before* the run.
140
146
 
141
147
  ## Interpreting Results
@@ -55,6 +55,7 @@ Usage:
55
55
  ai-consultants --preset <name> "question" Use a configuration preset
56
56
  ai-consultants --strategy <name> "question" Use a synthesis strategy
57
57
  ai-consultants doctor [--fix] [--json] Run diagnostics
58
+ ai-consultants update-clis [--dry-run] Check & update installed consultant CLIs
58
59
  ai-consultants init [--force] Scaffold ~/.config/ai-consultants/{config.sh,.env}
59
60
  ai-consultants install Install slash commands to ~/.claude/commands/
60
61
  ai-consultants version Show version
@@ -78,6 +79,10 @@ EOF
78
79
  shift
79
80
  exec "$SCRIPTS_DIR/doctor.sh" "$@"
80
81
  ;;
82
+ update-clis|update)
83
+ shift
84
+ exec "$SCRIPTS_DIR/update_clis.sh" "$@"
85
+ ;;
81
86
  init)
82
87
  shift
83
88
  FORCE=false
@@ -1,4 +1,4 @@
1
- # Cost Rates - AI Consultants v2.20.0
1
+ # Cost Rates - AI Consultants v2.21.0
2
2
 
3
3
  This page documents the per-token rates used by the cost tracking system.
4
4
 
package/docs/SETUP.md CHANGED
@@ -358,15 +358,19 @@ curl -L code.kimi.com/install.sh | bash
358
358
 
359
359
  Verify: `kimi --version`
360
360
 
361
- ### MiniMax (API-only) - v2.10
361
+ ### MiniMax (CLI via mmx) - v2.21
362
362
 
363
- MiniMax requires an API key — no CLI installation needed.
363
+ MiniMax runs via the `mmx` CLI by default (CLI-first, OAuth). Install and sign in:
364
364
 
365
365
  ```bash
366
- export MINIMAX_API_KEY="your-key"
367
- export ENABLE_MINIMAX=true
366
+ npm install -g mmx-cli
367
+ mmx auth login # OAuth (browser); or: mmx auth login --api-key <key>
368
368
  ```
369
369
 
370
+ API mode is opt-in: set `MINIMAX_USE_API=true` with `MINIMAX_API_KEY`.
371
+
372
+ Verify: `mmx --version`
373
+
370
374
  ---
371
375
 
372
376
  ## Ollama (Local Models) - v2.2
@@ -521,7 +525,7 @@ INVOKING_AGENT=codex ./scripts/consult_all.sh "Question"
521
525
 
522
526
  ## CLI/API Mode Switching (v2.6+)
523
527
 
524
- Five consultants can switch between CLI mode (using local CLI tools) and API mode (direct API calls): **Gemini, Codex, Claude, Mistral, and Qwen3**.
528
+ Six consultants can switch between CLI mode (using local CLI tools) and API mode (direct API calls): **Gemini, Codex, Claude, Mistral, Qwen3, and MiniMax**.
525
529
 
526
530
  ### Why Use API Mode?
527
531
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.20.0",
2
+ "version": "2.21.0",
3
3
  "updated": "2026-06-24",
4
4
  "models": {
5
5
  "_comment_units": "All rates are USD per 1,000 tokens (per-1K). lib/costs.sh computes cost as (token_count / 1000) * rate, so every value here MUST be a per-1K rate = (provider $/1M price) / 1000. Do NOT paste $/MTok figures directly.",
@@ -0,0 +1,65 @@
1
+ # Release v2.21.0
2
+
3
+ **Date:** 2026-07-14
4
+ **Type:** Minor — CLI-first transport, MiniMax on the mmx CLI, `update-clis`, and opt-in stance consensus
5
+ **Previous:** v2.20.0
6
+
7
+ ## Highlights
8
+
9
+ - **CLI-first is now the rule.** Any consultant that ships a CLI uses it by default; API mode is opt-in — only for CLI-less models or an explicit `*_USE_API=true`. Amp and Claude are on by default.
10
+ - **MiniMax runs on the `mmx` CLI** (it was API-only). Existing API-only setups keep working: `MINIMAX_USE_API` auto-resolves to API mode only when `MINIMAX_API_KEY` is set.
11
+ - **`ai-consultants update-clis`** keeps every consultant CLI current, detecting how each was installed.
12
+ - **Stance-based semantic consensus** (opt-in): the panel picks from a shared set of enumerated stance options, so agreement is scored by exact match instead of lexical similarity.
13
+ - A batch of smoke-test-surfaced reliability fixes across the Gemini/Kimi/Kilo adapters and the voting/synthesis pipeline, plus a runtime persona fix. Hardened by a multi-agent `/code-review max` pass.
14
+
15
+ ## What's New
16
+
17
+ ### CLI-first transport
18
+
19
+ Every `*_USE_API` flag now defaults to `false`. A consultant with a CLI always uses it; API mode is reserved for models without a CLI or an explicit opt-in. `ENABLE_AMP` and `ENABLE_CLAUDE` default on.
20
+
21
+ ### MiniMax via the `mmx` CLI
22
+
23
+ `query_minimax.sh` runs `mmx text chat --non-interactive --quiet --no-color --output text …` by default. `MINIMAX_CMD` (default `mmx`) sets the binary. To preserve pre-2.21 behavior, `MINIMAX_USE_API` auto-resolves:
24
+
25
+ - `MINIMAX_API_KEY` set → API mode (the only transport before 2.21 — nothing breaks on upgrade).
26
+ - otherwise → the `mmx` CLI.
27
+
28
+ An explicit `MINIMAX_USE_API=true|false` always wins. `doctor` validates the CLI in CLI mode and the key in API mode.
29
+
30
+ ### `update-clis`
31
+
32
+ ```bash
33
+ ai-consultants update-clis # update every installed consultant CLI
34
+ ai-consultants update-clis --dry-run # show each CLI's install method + the exact update command
35
+ ai-consultants update-clis --only mmx # limit to one CLI (by consultant name or binary)
36
+ ```
37
+
38
+ For each CLI-backed consultant it detects the install method — brew formula/cask, npm, uv, pipx, pip, a self-updating binary, or a curl installer — and updates it via the matching command, best-effort.
39
+
40
+ ### Stance-based semantic consensus (opt-in)
41
+
42
+ | Flag (default) | Effect |
43
+ |---|---|
44
+ | `ENABLE_STANCE_CONSENSUS` (false) | One LLM call enumerates 2–`STANCE_MAX_OPTIONS` mutually-exclusive stance options for the question; each consultant picks one verbatim; consensus = the plurality stance's share of the **panel**. |
45
+ | `STANCE_MAX_OPTIONS` (5) | Cap on the generated option set. |
46
+ | `STANCE_TIMEOUT` (60) | Seconds for the stance-generation call (guards a blocking/unauthenticated synthesizer at startup). |
47
+
48
+ The lexical cluster (Jaccard over the free-text `approach`) can't tell that "Commit the lockfile" and "Always keep package-lock in git" agree; the stance signal makes agreement exact-matchable. Every path degrades to the lexical cluster if fewer than two stances are emitted or generation fails, so it is always safe to enable. The generated options are written to `stance_options.json` in the output dir.
49
+
50
+ ### Reliability fixes
51
+
52
+ - **CLI adapters** (from a full-panel smoke test): Gemini (`agy -p "$QUERY"`, not the broken `-p -`), Kimi (`--output-format stream-json` + a hardened extractor), and Kilo (strip the ```json fence) return structured responses instead of the unstructured fallback.
53
+ - **Metadata pollution**: a shared `_is_consultant_response_file` filter keeps pipeline files (`voting.json`, `orchestration.json`, `stance_options.json`, …) out of every voting/synthesis/peer-review glob, so they can't become phantom votes, reviewees, or synthesis inputs. Consensus is the largest agreeing cluster (single-linkage over Jaccard).
54
+ - **Orchestration**: a lexical "stalled" is relabeled "stable" when the panel's own per-round signal says positions stopped moving (now `set -e`-safe against a malformed round summary); a debate round's refined stance is validated before it is grafted.
55
+ - **Personas**: GLM and DeepSeek were transposed at runtime — each consultant ran under the other's persona name and prompt. Fixed.
56
+
57
+ ### Under the hood
58
+
59
+ Reliability tracking with Amp/Claude default-on; an offline end-to-end integration test; `scripts/release.sh` version-bump automation; calibration fixes (diagnosable peer-review failures + a cost-only path). `npm test` now runs 18 suites.
60
+
61
+ ## Upgrade notes
62
+
63
+ - **Fully back-compat.** With the new flags off, behavior matches v2.20.0.
64
+ - **MiniMax users**: if you ran MiniMax via `MINIMAX_API_KEY`, nothing changes — it stays on the API. To switch to the CLI, `npm install -g mmx-cli` and `mmx auth login`, then unset the key (or set `MINIMAX_USE_API=false`).
65
+ - **New opt-in flags**: `ENABLE_STANCE_CONSENSUS`, `STANCE_MAX_OPTIONS`, `STANCE_TIMEOUT`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-consultants",
3
- "version": "2.20.0",
3
+ "version": "2.21.0",
4
4
  "description": "A harness for every question - a panel of up to 15 AI models that picks its own deliberation strategy per question (convergence, adversarial verification, tournament) and checks its work before answering",
5
5
  "bin": {
6
6
  "ai-consultants": "bin/ai-consultants"
@@ -141,9 +141,32 @@ agrees on the first pass.
141
141
  The convergence trajectory and stop reason are recorded in `orchestration.json` /
142
142
  `optimization_metrics.json`. Every round still respects `MAX_SESSION_COST` / budget limits.
143
143
 
144
+ ## Semantic Consensus (v2.21+, opt-in)
145
+
146
+ By default the consensus score (which drives `CONVERGENCE_TARGET_CONSENSUS`) is a
147
+ **lexical cluster**: the largest group of consultants whose free-text `approach`
148
+ fields are similar (single-linkage over Jaccard). That can't tell that two
149
+ differently-phrased answers actually AGREE ("Commit the lockfile" vs "Always keep
150
+ package-lock in git").
151
+
152
+ `ENABLE_STANCE_CONSENSUS=true` adds an exact-matchable signal: one extra LLM call
153
+ enumerates a small set of mutually-exclusive **stance options** for the question,
154
+ each consultant is asked to pick exactly one verbatim, and consensus becomes the
155
+ plurality stance's share of the **panel** (consultants that answered but emitted no
156
+ stance count against agreement). It degrades to the lexical cluster whenever fewer
157
+ than two stances are emitted or generation fails, so it is always safe to enable.
158
+
159
+ ```bash
160
+ ENABLE_STANCE_CONSENSUS=false # opt-in; adds ~1 LLM call per run
161
+ STANCE_MAX_OPTIONS=5 # max stance options generated per question
162
+ STANCE_TIMEOUT=60 # seconds for the stance-generation call (guards a hang)
163
+ ```
164
+
165
+ The generated options are written to `stance_options.json` in the output dir.
166
+
144
167
  ## CLI/API Mode Switching (v2.6+)
145
168
 
146
- Five consultants support switching between CLI and API mode. When API mode is enabled, the CLI is not used.
169
+ Six consultants support switching between CLI and API mode. **The default is the CLI** for every consultant that has one — API mode is opt-in (for CLI-less models or an explicit choice). When API mode is enabled, the CLI is not used.
147
170
 
148
171
  **Gemini auto-resolution (v2.15.1):** leave `GEMINI_USE_API` unset and the mode is chosen for you — API mode when `GEMINI_API_KEY` is present (no `agy` install or OAuth needed; ideal for `npx`), CLI mode (`agy`) otherwise. Set `GEMINI_USE_API` explicitly only to force a mode (an explicit value disables auto-detection).
149
172
 
@@ -154,6 +177,7 @@ CODEX_USE_API=false # Use OpenAI API instead of codex CLI
154
177
  CLAUDE_USE_API=false # Use Anthropic API instead of claude CLI
155
178
  MISTRAL_USE_API=false # Use Mistral API instead of vibe CLI
156
179
  QWEN3_USE_API=false # Use qwen CLI (default) or DashScope API
180
+ MINIMAX_USE_API=false # Use mmx CLI (default) or MiniMax API
157
181
  ```
158
182
 
159
183
  ### API Keys
@@ -165,6 +189,7 @@ QWEN3_USE_API=false # Use qwen CLI (default) or DashScope API
165
189
  | Claude | `ANTHROPIC_API_KEY` | Anthropic API key |
166
190
  | Mistral | `MISTRAL_API_KEY` | Mistral API key |
167
191
  | Qwen3 | `QWEN3_API_KEY` | DashScope API key |
192
+ | MiniMax | `MINIMAX_API_KEY` | MiniMax API key (API mode only; the mmx CLI uses OAuth) |
168
193
 
169
194
  ## Consultant Toggles and Models
170
195
 
@@ -175,17 +200,17 @@ ENABLE_CODEX=true
175
200
  ENABLE_MISTRAL=true
176
201
  ENABLE_KILO=true
177
202
  ENABLE_CURSOR=true
178
- ENABLE_AIDER=true
179
-
180
- # Opt-in consultants
181
- ENABLE_AMP=false # Amp CLI - The Systems Thinker
182
- ENABLE_KIMI=false # Kimi CLI - The Eastern Sage
183
- ENABLE_QWEN3=false # Qwen CLI/API - The Analyst
184
- ENABLE_CLAUDE=false # Claude CLI - The Synthesizer
203
+ ENABLE_AMP=true # Amp CLI - The Systems Thinker
204
+ ENABLE_KIMI=true # Kimi CLI - The Eastern Sage
205
+ ENABLE_QWEN3=true # Qwen CLI/API - The Analyst
206
+ ENABLE_MINIMAX=true # MiniMax CLI/API (mmx) - The Pragmatic Optimizer
207
+ ENABLE_CLAUDE=true # Claude CLI - The Synthesizer (auto-excluded under Claude Code)
208
+
209
+ # Off by default
210
+ ENABLE_AIDER=false # Aider CLI - The Pair Programmer
185
211
  ENABLE_OLLAMA=false # Ollama - The Local Expert
186
- ENABLE_MINIMAX=false # MiniMax API - The Pragmatic Optimizer
187
212
 
188
- # API-only consultants
213
+ # API-only consultants (off by default - require API keys)
189
214
  ENABLE_GLM=false
190
215
  ENABLE_GROK=false
191
216
  ENABLE_DEEPSEEK=false
package/scripts/config.sh CHANGED
@@ -64,9 +64,13 @@ DEFAULT_OUTPUT_DIR_BASE="${DEFAULT_OUTPUT_DIR_BASE:-${_AI_CONSULTANTS_XDG_CACHE}
64
64
  # =============================================================================
65
65
  # For agents that support both CLI and API mode, set USE_API=true to use API mode.
66
66
  # When API mode is enabled, CLI mode is automatically disabled (mutual exclusivity).
67
- # 5 agents support switching: Gemini, Codex, Claude, Mistral, Qwen3
67
+ # 6 agents support switching: Gemini, Codex, Claude, Mistral, Qwen3, MiniMax
68
68
 
69
- # Mode switching (true = use API, false = use CLI)
69
+ # Mode switching (true = API, false = CLI). DEFAULT IS CLI — when a consultant
70
+ # has a CLI, the tool uses it; the CLIs are the primary transport (OAuth /
71
+ # subscription, no API key needed). API mode is opt-in only: for CLI-less models
72
+ # (GLM/Grok/DeepSeek are API-only anyway) or an explicit per-run user
73
+ # choice. Set a switch to true (with its API key) to force API for that agent.
70
74
  # GEMINI_USE_API is intentionally NOT defaulted here: it is auto-resolved in the
71
75
  # Gemini configuration section below (needs GEMINI_API_KEY / GEMINI_CMD), so an
72
76
  # explicit unset must remain distinguishable from an explicit "false".
@@ -74,6 +78,10 @@ CODEX_USE_API="${CODEX_USE_API:-false}"
74
78
  CLAUDE_USE_API="${CLAUDE_USE_API:-false}"
75
79
  MISTRAL_USE_API="${MISTRAL_USE_API:-false}"
76
80
  QWEN3_USE_API="${QWEN3_USE_API:-false}" # Default false to use qwen CLI
81
+ # MINIMAX_USE_API is intentionally NOT defaulted here: it is auto-resolved in the
82
+ # MiniMax configuration section below (needs MINIMAX_API_KEY), so an explicit
83
+ # unset stays distinguishable from an explicit "false" (back-compat for pre-v2.21
84
+ # API-only MiniMax users -- see the rationale at the resolve block).
77
85
 
78
86
  # API endpoints for CLI-switchable agents
79
87
  GEMINI_API_URL="${GEMINI_API_URL:-https://generativelanguage.googleapis.com/v1beta/models}"
@@ -215,14 +223,36 @@ DEEPSEEK_FORMAT="${DEEPSEEK_FORMAT:-openai}"
215
223
  # API key: Set DEEPSEEK_API_KEY environment variable
216
224
 
217
225
  # =============================================================================
218
- # MINIMAX CONFIGURATION - The Pragmatic Optimizer (API-based, v2.10)
226
+ # MINIMAX CONFIGURATION - The Pragmatic Optimizer (CLI/API, v2.10; CLI via mmx v2.21)
219
227
  # =============================================================================
220
228
 
229
+ MINIMAX_CMD="${MINIMAX_CMD:-mmx}"
221
230
  MINIMAX_MODEL="${MINIMAX_MODEL:-MiniMax-M2.7}"
222
231
  MINIMAX_TIMEOUT_SECONDS="${MINIMAX_TIMEOUT:-180}"
223
232
  MINIMAX_API_URL="${MINIMAX_API_URL:-https://api.minimax.io/v1/chat/completions}"
224
233
  MINIMAX_FORMAT="${MINIMAX_FORMAT:-openai}"
225
- # API key: Set MINIMAX_API_KEY environment variable
234
+ # CLI mode (default) uses the mmx CLI (npm i -g mmx-cli; auth: mmx auth login).
235
+ # API mode (MINIMAX_USE_API=true) uses MINIMAX_API_KEY against MINIMAX_API_URL.
236
+ #
237
+ # Auto-resolve the MiniMax transport when the user hasn't pinned MINIMAX_USE_API.
238
+ # Unlike Gemini, the mmx CLI IS npm-installable, so CLI is the genuine default
239
+ # (per the CLI-first principle). This block exists purely for BACK-COMPAT: before
240
+ # v2.21 MiniMax was API-only, so a working pre-v2.21 config necessarily had
241
+ # MINIMAX_API_KEY and no mmx. Defaulting such a user to CLI would break MiniMax on
242
+ # upgrade (mmx not found). So when the mode is unset:
243
+ # - MINIMAX_API_KEY present -> API mode (preserves the pre-v2.21 API-only user;
244
+ # a set key is itself the "explicit user choice")
245
+ # - otherwise -> CLI mode (mmx; the new default)
246
+ # An explicit MINIMAX_USE_API=true/false is always honored. Idempotent across
247
+ # re-sourcing via the "${MINIMAX_USE_API+x}" set-vs-unset guard.
248
+ if [[ -z "${MINIMAX_USE_API+x}" ]]; then
249
+ if [[ -n "${MINIMAX_API_KEY:-}" ]]; then
250
+ MINIMAX_USE_API=true
251
+ else
252
+ MINIMAX_USE_API=false
253
+ fi
254
+ fi
255
+ export MINIMAX_USE_API
226
256
 
227
257
  # =============================================================================
228
258
  # AMP CONFIGURATION - The Systems Thinker (v2.8)
@@ -285,7 +315,7 @@ ALL_CONSULTANTS=("Gemini" "Codex" "Mistral" "Kilo" "Cursor" "Aider" "Amp" "Kimi"
285
315
  CLI_CONSULTANTS=("Gemini" "Codex" "Mistral" "Kilo" "Cursor" "Aider" "Amp" "Kimi" "Claude" "Qwen3" "Ollama")
286
316
 
287
317
  # API-only consultants (use HTTP API directly, no CLI available)
288
- API_CONSULTANTS=("GLM" "Grok" "DeepSeek" "MiniMax")
318
+ API_CONSULTANTS=("GLM" "Grok" "DeepSeek")
289
319
 
290
320
  # =============================================================================
291
321
  # ENABLED CONSULTANTS
@@ -298,17 +328,17 @@ ENABLE_CODEX="${ENABLE_CODEX:-true}"
298
328
  ENABLE_MISTRAL="${ENABLE_MISTRAL:-true}"
299
329
  ENABLE_KILO="${ENABLE_KILO:-true}"
300
330
  ENABLE_CURSOR="${ENABLE_CURSOR:-true}"
301
- ENABLE_AIDER="${ENABLE_AIDER:-false}"
302
- ENABLE_AMP="${ENABLE_AMP:-false}" # Amp Code (v2.8)
331
+ ENABLE_AMP="${ENABLE_AMP:-true}" # Amp Code (v2.8)
303
332
  ENABLE_KIMI="${ENABLE_KIMI:-true}" # Kimi Code (v2.9)
304
- ENABLE_CLAUDE="${ENABLE_CLAUDE:-false}" # Auto-disabled when invoked by Claude Code
333
+ ENABLE_CLAUDE="${ENABLE_CLAUDE:-true}" # Auto-disabled when invoked by Claude Code
334
+ ENABLE_QWEN3="${ENABLE_QWEN3:-true}" # qwen-code CLI (v2.7); API opt-in
335
+ ENABLE_MINIMAX="${ENABLE_MINIMAX:-true}" # mmx CLI (v2.21); API opt-in
336
+ ENABLE_AIDER="${ENABLE_AIDER:-false}" # Aider CLI (off by default)
305
337
 
306
- # API-based consultants (disabled by default - require API keys)
307
- ENABLE_QWEN3="${ENABLE_QWEN3:-true}"
338
+ # API-only consultants (disabled by default - require API keys)
308
339
  ENABLE_GLM="${ENABLE_GLM:-false}"
309
340
  ENABLE_GROK="${ENABLE_GROK:-false}"
310
341
  ENABLE_DEEPSEEK="${ENABLE_DEEPSEEK:-false}"
311
- ENABLE_MINIMAX="${ENABLE_MINIMAX:-false}"
312
342
 
313
343
  # Local model support via Ollama (disabled by default)
314
344
  ENABLE_OLLAMA="${ENABLE_OLLAMA:-false}"
@@ -440,6 +470,13 @@ WARN_AT_COST="${WARN_AT_COST:-0.50}"
440
470
  # (persistent across reboots; this is user data, not cache).
441
471
  COST_TRACKING_FILE="${COST_TRACKING_FILE:-${_AI_CONSULTANTS_XDG_DATA}/costs.json}"
442
472
 
473
+ # --- Reliability Tracking (foundation for a future self-tuning roster) ---
474
+ # Enable per-consultant success/failure recording (opt-in default: ON)
475
+ ENABLE_RELIABILITY_TRACKING="${ENABLE_RELIABILITY_TRACKING:-true}"
476
+
477
+ # File for cumulative per-consultant reliability tracking (same XDG data dir as costs)
478
+ RELIABILITY_FILE="${RELIABILITY_FILE:-${_AI_CONSULTANTS_XDG_DATA}/reliability.json}"
479
+
443
480
  # --- Budget Enforcement (v2.4) ---
444
481
  # Enable budget limit enforcement (opt-in, default OFF)
445
482
  ENABLE_BUDGET_LIMIT="${ENABLE_BUDGET_LIMIT:-false}"
@@ -586,6 +623,15 @@ CONVERGENCE_STALL_EPSILON="${CONVERGENCE_STALL_EPSILON:-5}"
586
623
  # and runs anonymous peer review as a refutation gate before synthesis.
587
624
  ENABLE_ADVERSARIAL_VERIFY="${ENABLE_ADVERSARIAL_VERIFY:-true}"
588
625
 
626
+ # Stance-based semantic consensus (v2.21, opt-in). When true, one LLM call
627
+ # enumerates 2..STANCE_MAX_OPTIONS mutually-exclusive stance options per question
628
+ # and each consultant picks one verbatim, so consensus is an exact-match plurality
629
+ # (robust to paraphrasing) instead of lexical clustering. Adds one serial LLM call
630
+ # per run; degrades gracefully to the cluster consensus if generation fails.
631
+ ENABLE_STANCE_CONSENSUS="${ENABLE_STANCE_CONSENSUS:-false}"
632
+ STANCE_MAX_OPTIONS="${STANCE_MAX_OPTIONS:-5}"
633
+ STANCE_TIMEOUT="${STANCE_TIMEOUT:-60}" # seconds for the stance-generation call (guards a hang)
634
+
589
635
  # =============================================================================
590
636
  # QUORUM GRADING (v2.19.0)
591
637
  # =============================================================================
@@ -899,4 +945,4 @@ EOF
899
945
  # VERSION
900
946
  # =============================================================================
901
947
 
902
- AI_CONSULTANTS_VERSION="2.20.0"
948
+ AI_CONSULTANTS_VERSION="2.21.0"
@@ -36,6 +36,7 @@ source "$SCRIPT_DIR/lib/common.sh"
36
36
  source "$SCRIPT_DIR/lib/session.sh"
37
37
  source "$SCRIPT_DIR/lib/progress.sh"
38
38
  source "$SCRIPT_DIR/lib/costs.sh"
39
+ source "$SCRIPT_DIR/lib/reliability.sh"
39
40
  source "$SCRIPT_DIR/lib/voting.sh"
40
41
  source "$SCRIPT_DIR/lib/routing.sh"
41
42
  source "$SCRIPT_DIR/lib/cache.sh"
@@ -298,6 +299,21 @@ chmod 700 "$DEFAULT_OUTPUT_DIR_BASE"
298
299
  mkdir -p "$OUTPUT_DIR"
299
300
  chmod 700 "$OUTPUT_DIR"
300
301
 
302
+ # --- Stance options (v2.21, opt-in): generate a shared enumerated stance set so
303
+ # every consultant picks the same one -> exact-match consensus. Fully graceful:
304
+ # on any failure STANCE_OPTIONS_PROMPT stays empty and consensus uses the cluster.
305
+ if [[ "${ENABLE_STANCE_CONSENSUS:-false}" == "true" ]]; then
306
+ source "$SCRIPT_DIR/lib/stance.sh"
307
+ STANCE_OPTIONS=$(generate_stance_options "$QUERY" 2>/dev/null || echo "[]")
308
+ echo "$STANCE_OPTIONS" > "$OUTPUT_DIR/stance_options.json" 2>/dev/null || true
309
+ if STANCE_OPTIONS_PROMPT=$(build_stance_prompt "$STANCE_OPTIONS" 2>/dev/null); then
310
+ export STANCE_OPTIONS_PROMPT
311
+ log_info "Stance consensus: $(echo "$STANCE_OPTIONS" | jq -r 'length' 2>/dev/null || echo '?') options generated"
312
+ else
313
+ log_warn "Stance consensus: no usable options — falling back to lexical cluster consensus"
314
+ fi
315
+ fi
316
+
301
317
  log_info "Output: $OUTPUT_DIR"
302
318
  log_info "Question: ${QUERY:0:80}$([ ${#QUERY} -gt 80 ] && echo '...')"
303
319
  if [[ ${#FILES[@]} -gt 0 ]]; then
@@ -557,9 +573,15 @@ for i in "${!PIDS[@]}"; do
557
573
  log_success " $name: OK (cached)"
558
574
  RESULTS+=("$name:OK")
559
575
  SUCCESS_COUNT=$((SUCCESS_COUNT + 1))
576
+ if [[ "${ENABLE_RELIABILITY_TRACKING:-true}" == "true" ]]; then
577
+ record_consultant_outcome "$name" success 2>/dev/null || true
578
+ fi
560
579
  else
561
580
  log_warn " $name: Cache miss"
562
581
  RESULTS+=("$name:EMPTY")
582
+ if [[ "${ENABLE_RELIABILITY_TRACKING:-true}" == "true" ]]; then
583
+ record_consultant_outcome "$name" fail 2>/dev/null || true
584
+ fi
563
585
  fi
564
586
  continue
565
587
  fi
@@ -570,6 +592,9 @@ for i in "${!PIDS[@]}"; do
570
592
  RESULTS+=("$name:OK")
571
593
  SUCCESS_COUNT=$((SUCCESS_COUNT + 1))
572
594
  update_progress "$name" 100 "success"
595
+ if [[ "${ENABLE_RELIABILITY_TRACKING:-true}" == "true" ]]; then
596
+ record_consultant_outcome "$name" success 2>/dev/null || true
597
+ fi
573
598
 
574
599
  # Store in cache (v2.3)
575
600
  if is_cache_enabled; then
@@ -581,12 +606,18 @@ for i in "${!PIDS[@]}"; do
581
606
  _surface_consultant_error "$name" "$output_file"
582
607
  RESULTS+=("$name:EMPTY")
583
608
  update_progress "$name" 100 "failed"
609
+ if [[ "${ENABLE_RELIABILITY_TRACKING:-true}" == "true" ]]; then
610
+ record_consultant_outcome "$name" fail 2>/dev/null || true
611
+ fi
584
612
  fi
585
613
  else
586
614
  log_error " $name: Failed"
587
615
  _surface_consultant_error "$name" "$output_file"
588
616
  RESULTS+=("$name:FAILED")
589
617
  update_progress "$name" 100 "failed"
618
+ if [[ "${ENABLE_RELIABILITY_TRACKING:-true}" == "true" ]]; then
619
+ record_consultant_outcome "$name" fail 2>/dev/null || true
620
+ fi
590
621
  fi
591
622
  done
592
623