Private
Public Access
0
0

docs(mma): add persona application, expanded tier_usage schema, Beads and Workspace profile roadmap sections

This commit is contained in:
2026-06-02 19:07:28 -04:00
parent f81f1f5eaa
commit a9cb8cae11
+99 -5
View File
@@ -35,6 +35,9 @@ class Ticket:
depends_on: List[str] = field() # Ticket IDs that must complete first
blocked_reason: Optional[str] = None # Why this ticket is blocked
step_mode: bool = False # If True, requires manual approval before execution
persona_id: Optional[str] = None # Per-ticket persona override; see Persona Application
retry_count: int = 0 # Increments on failure; drives model escalation
model_override: Optional[str] = None # If set, bypasses persona/model_list selection
def mark_blocked(self, reason: str) -> None # Sets status="blocked", stores reason
def mark_complete(self) -> None # Sets status="completed"
@@ -75,6 +78,8 @@ class WorkerContext:
ticket_id: str # Which ticket this worker is processing
model_name: str # LLM model to use (e.g., "gemini-2.5-flash-lite")
messages: List[dict] # Conversation history for this worker
persona_id: Optional[str] = None # Per-worker persona (set in run_worker_lifecycle)
tool_preset: Optional[str] = None # Fallback tool preset if persona has none
```
---
@@ -173,10 +178,10 @@ class ConductorEngine:
self.track = track
self.event_queue = event_queue
self.tier_usage = {
"Tier 1": {"input": 0, "output": 0, "model": "gemini-3.1-pro-preview"},
"Tier 2": {"input": 0, "output": 0, "model": "gemini-3-flash-preview"},
"Tier 3": {"input": 0, "output": 0, "model": "gemini-2.5-flash-lite"},
"Tier 4": {"input": 0, "output": 0, "model": "gemini-2.5-flash-lite"},
"Tier 1": {"input": 0, "output": 0, "model": "gemini-3.1-pro-preview", "tool_preset": None, "persona": None},
"Tier 2": {"input": 0, "output": 0, "model": "gemini-3-flash-preview", "tool_preset": None, "persona": None},
"Tier 3": {"input": 0, "output": 0, "model": "gemini-2.5-flash-lite", "tool_preset": None, "persona": None},
"Tier 4": {"input": 0, "output": 0, "model": "gemini-2.5-flash-lite", "tool_preset": None, "persona": None},
}
self.dag = TrackDAG(self.track.tickets)
self.engine = ExecutionEngine(self.dag, auto_queue=auto_queue)
@@ -185,6 +190,16 @@ class ConductorEngine:
self._pause_event: threading.Event = threading.Event()
```
**Per-tier `tier_usage` schema** (each tier entry):
| Key | Type | Purpose |
|---|---|---|
| `input` | `int` | Cumulative input tokens for this tier |
| `output` | `int` | Cumulative output tokens for this tier |
| `model` | `str` | Default model name (overridable per ticket via `model_override` or persona) |
| `tool_preset` | `Optional[str]` | Active tool preset name (set via `set_tool_preset` or persona) |
| `persona` | `Optional[str]` | Active persona name (set when a ticket's persona is applied) |
### State Broadcast (`_push_state`)
On every state change, the engine pushes the full orchestration state to the GUI via `AsyncEventQueue`:
@@ -333,6 +348,48 @@ The `confirm_spawn` function uses the `dialog_container` pattern:
4. When the GUI fills in the dialog, call `.wait()` to get the result.
5. Returns `(approved, modified_prompt, modified_context)`.
### Persona Application
When a ticket has `persona_id` set (or a tier-level persona is active), `run_worker_lifecycle` resolves the persona from `PersonaManager` and applies it before the AI call:
```python
# Apply Persona if specified
persona = None
if context.persona_id:
pm = PersonaManager(...)
personas = pm.load_all()
if context.persona_id in personas:
persona = personas[context.persona_id]
if persona.system_prompt:
ai_client.set_custom_system_prompt(persona.system_prompt)
if persona.bias_profile:
ai_client.set_bias_profile(persona.bias_profile)
if persona.preferred_models:
preferred_models = persona.preferred_models
if persona.tool_preset:
persona_tool_preset = persona.tool_preset
# Apply tool preset: use persona's tool_preset if available, otherwise fall back to context.tool_preset
effective_tool_preset = persona_tool_preset or context.tool_preset
```
A single persona may override:
- **`system_prompt`** — replaces the default system prompt for the worker
- **`bias_profile`** — influences tool selection via semantic nudging
- **`preferred_models`** — list used for model escalation (replaces the default `models_list`)
- **`tool_preset`** — applied via `set_tool_preset()`; takes precedence over the ticket's `context.tool_preset`
- **`aggregation_strategy`** — sets the file aggregation strategy (`auto`/`full`/`summarize`/`skeleton`) for the worker's context
**Resolution order at model selection time** (in `run_worker_lifecycle`):
1. `ticket.model_override` (if set) — used unconditionally
2. `persona.preferred_models` (if persona applied) — first item is the initial model
3. `ticket.retry_count`-indexed entry in the resolved `models_list` — escalates on retries
If the persona fails to load (file not found, parse error), the worker logs a warning and falls back to the default model list. The persona is **not** a hard failure point.
See [guide_personas.md](guide_personas.md) (placeholder; written in Task 10) for the full persona schema, scope inheritance rules, and editor modal.
---
## Tier 4: QA Error Analysis
@@ -373,8 +430,9 @@ Each tier operates within its own token budget:
- **Tier 3 workers** use lightweight models (default: `gemini-2.5-flash-lite`) and receive only the files listed in `context_requirements`.
- **Context Amnesia** ensures no accumulated history bleeds between tickets.
- **Tier 2** tracks cumulative `tier_usage` per tier: `{"input": N, "output": N}` for token cost monitoring.
- **Tier 2** tracks cumulative `tier_usage` per tier: `{"input": N, "output": N, "model": ..., "tool_preset": ..., "persona": ...}` for token cost monitoring and persona attribution.
- **First file vs subsequent files**: The first `context_requirements` file gets a curated view (preserving hot paths); subsequent files get only skeletons.
- **RAG augmentation is caller-injected**: The ConductorEngine does not own a RAG engine. The caller (typically `AppController` for the main discussion, or the GUI's RAG panel for project-wide queries) is responsible for instantiating an `RAGEngine` and passing it through to `ai_client.send(rag_engine=...)` for each worker call. See [guide_architecture.md](guide_architecture.md#rag-integration) for the dispatch flow.
---
@@ -468,3 +526,39 @@ conductor/tracks/<track_id>/
1. `state.toml` (structured `TrackState`) — counts tasks with `status == "completed"`.
2. `metadata.json` (legacy) — gets id/title/status only.
3. `plan.md` (regex) — counts `- [x]` vs `- [ ]` checkboxes for progress.
---
## Beads Integration (Roadmap)
[Beads](https://github.com/steveyegge/beads) is a Dolt-backed issue tracking system. The `src/beads_client.py` module provides a Python client for `bd` CLI calls (`bd_create`, `bd_list`, `bd_ready`, `bd_update`). The client is functional but not yet integrated into the `ConductorEngine` execution loop.
**Current state (as of 2026-06-02):**
- `BeadsClient` is instantiable; it detects whether a project's `.beads/` directory exists and falls back to no-op if not.
- Tools `bd_create`, `bd_list`, `bd_ready`, `bd_update` are exposed via the MCP bridge (see [guide_tools.md](guide_tools.md)).
- The ConductorEngine still writes track state to `conductor/tracks/<id>/` (markdown-based), not to a Beads repo.
- A project's TOML may specify a conductor directory override (`[conductor].dir`) but does not yet support a Beads repository path.
**Planned integration:**
- The ConductorEngine's `parse_json_tickets` would optionally forward ingested tickets to `BeadsClient.bd_create` when Beads mode is active.
- `save_track_state` would write to `.beads/` instead of `conductor/tracks/<id>/state.toml` when Beads is active.
- The Visual DAG would query `bd_list` for real-time ticket status instead of the in-memory `TrackDAG`.
See [guide_beads.md](guide_beads.md) (placeholder; written in Task 10) for the full Beads client API and the toolset exposed to agents.
---
## Workspace Profile Auto-Switching (Roadmap)
The `WorkspaceManager` (`src/workspace_manager.py`) supports binding workspace profiles to MMA tier context. Currently, profiles can be saved and loaded manually; the auto-switch hook is implemented but not yet wired into `ConductorEngine`.
**Current state:**
- `WorkspaceProfile` (named docking + window state) can be saved/loaded via the GUI.
- Scope inheritance (Global vs Project) is supported.
- A `bind_to_context(context_id: str, profile_name: str)` method exists on `WorkspaceManager`.
**Planned integration:**
- On Tier transition (`tier1 → tier2 → tier3`), `ConductorEngine` would call `WorkspaceManager.bind_to_context("tier3", active_profile)` to reshape the UI for the current cognitive load.
- This is opt-in via `[conductor].auto_switch_profiles = true` in `config.toml`.
See [guide_workspace_profiles.md](guide_workspace_profiles.md) (placeholder; written in Task 10) for the full profile schema.