Metadata
| Status | done |
|---|---|
| Assigned | agent-2369 |
| Agent identity | f51439356729d112a6c404803d88015d5b44832c6c584c62b96732b63c2b0c7e |
| Model | claude:opus |
| Created | 2026-05-04T17:39:12.067705738+00:00 |
| Started | 2026-05-04T18:37:10.347184829+00:00 |
| Completed | 2026-05-04T18:44:17.470148173+00:00 |
| Tags | agency,sync,research, eval-scheduled |
| Eval score | 0.91 |
| └ blocking impact | 0.90 |
| └ completeness | 0.94 |
| └ coordination overhead | 0.96 |
| └ correctness | 0.92 |
| └ downstream usability | 0.95 |
| └ efficiency | 0.90 |
| └ intent fidelity | 0.85 |
| └ style adherence | 0.93 |
Description
Description
Agency v1.2.4 carries a scope field on every primitive: task | meta:assigner | meta:evaluator | meta:evolver | meta:agent_creator. This makes functional-agent identity (who composes assigners vs evaluators vs evolvers) a first-class primitive attribute. Workgraph encodes the same idea via TASK ID prefixes (.evaluate-*, .place-*, .assign-*, .flip-*) and pins them to claude:haiku in CLAUDE.md. Distinct mechanisms, similar semantic intent.
Agency also ships a separate watched config file ~/.agency/composition-rules.csv (per the agency-composition-config skill) with columns: agent_type, rule, max_role_components, max_desired_outcomes, max_trade_off_configs, all_projects, project_ids. This caps composition selection at assignment time. Workgraph has no equivalent — composition rules are hard-wired.
Specific things to investigate
- Read
src/agency/prompt.rsandsrc/commands/assign.rsto understand wg's composition logic for the four functional agents. - Read
src/commands/evaluate.rsand the.evaluate-*task naming scheme — how is the evaluator agent currently composed? - Does the four-agent split (assigner / evaluator / evolver / agent_creator) line up with wg's
.place-* / .evaluate-* / evolve / agent_createsurfaces? - Should wg adopt a
scopefield on primitives, OR keep task-tag implicit, OR run both side by side? - Should wg implement
~/.agency/composition-rules.csvas a watched overlay, or extend wg config (.workgraph/config.toml)? Watched-file semantics matter for live edits without daemon restart. - Cross-reference: how does the user override per-role models (CLAUDE.md mentions
[models.evaluator]/[models.assigner])? That config surface ALREADY treats evaluator/assigner as named functional-agent slots — extend it.
Deliverable
wg log entry with:
- Mapping table: agency scope value → wg task-tag prefix → wg config key (
[models.X]) - Recommendation: add primitive
scopefield (yes/no) + add composition-rules.csv overlay (yes/no) - File:line citations in src/agency/prompt.rs, src/commands/assign.rs, src/commands/evaluate.rs
Validation
- Mapping table covers all four functional agents
-
Decision documented for primitive
scopefield (additive vs not) - Decision documented for composition-rules.csv (adopt path vs not)
-
Cites the
[models.evaluator]/[models.assigner]config surface and proposes whether composition-rules.csv lives next to it or in~/.agency/
Depends on
Required by
- (none)
Log
- 2026-05-04T17:39:11.967948278+00:00 Task paused
- 2026-05-04T18:36:41.360631113+00:00 Task published
- 2026-05-04T18:37:10.209512371+00:00 Lightweight assignment: agent=Careful Programmer (f5143935), exec_mode=light, context_scope=full, reason=Careful Programmer is the best fit for a read-only codebase research task that requires precise inspection of multiple files and a synthesized recommendation.
- 2026-05-04T18:37:10.347189547+00:00 Spawned by coordinator --executor claude --model opus
- 2026-05-04T18:37:32.859880721+00:00 Starting research: reading prompt.rs, assign.rs, evaluate.rs and config surface
- 2026-05-04T18:43:06.727272652+00:00 === RESEARCH: agency functional-agent scope + composition rules === KEY FINDING — wg already has scope plumbing, just dormant. `src/agency/run_mode.rs:54-66` defines `required_scope_for_task()` that maps task-id prefixes to agency v1.2.4 scope strings byte-for-byte: .assign-* → "meta:assigner" .evaluate-* → "meta:evaluator" .flip-* → "meta:evaluator" (FLIP is treated as eval) .evolve-* → "meta:evolver" .create-agent-* → "meta:agent_creator" (anything else) → "task" Filter is wired in `propose_learning_experiment` (run_mode.rs:253-255) and matches via `RoleComponent.metadata["scope"]` (run_mode.rs:90). Today's starter primitives in `src/agency/starters.rs` do NOT set that key, so the filter falls through to "no-scope = matches all" (run_mode.rs:74-81). The mechanism exists; the data layer is empty. ## A. Mapping table — agency scope → wg surfaces | agency v1.2.4 scope | wg task-tag prefix | wg DispatchRole | wg [models.X] key | wg [agency].X_agent slot | |---------------------|------------------------|-----------------|---------------------|-----------------------------------| | task | (none, default) | TaskAgent | task_agent | (none — actor is per-task agent) | | meta:assigner | .assign-<id> | Assigner | assigner | assigner_agent | | meta:evaluator | .evaluate-<id> | Evaluator | evaluator | evaluator_agent | | meta:evaluator | .flip-<id> | FlipInference + | flip_inference, | evaluator_agent (re-used) | | | | FlipComparison | flip_comparison | | | meta:evolver | .evolve-<id> | Evolver | evolver | evolver_agent | | meta:agent_creator | .create-agent-<id> | Creator | creator | creator_agent | | (wg-only, no agency)| .place-<id> | Placer | placer | placer_agent | Citations: `DispatchRole` enum src/config.rs:1102-1131; `ModelRoutingConfig` fields src/config.rs:1488-1550; `AgencyConfig` slots src/config.rs:2625-2643 (assigner_agent, evaluator_agent, evolver_agent, creator_agent, placer_agent). Note 1: wg ships a FIFTH functional-agent slot `placer` (placement / dependency edges) that has no agency analog. The `.place-*` prefix is NOT mapped in `required_scope_for_task` (run_mode.rs:54-66) even though the slot exists in config. This is a gap that should close in either direction. Note 2: The four "Special Agent" roles already ship verbatim in `src/agency/starters.rs:591-646` under `special_agent_roles()`: - "Assigner" ← assigner_components() + "Optimal agent-task assignment" - "Evaluator" ← evaluator_components() + "Calibrated evaluation grade" - "Evolver" ← evolver_components() + "Proposed primitive modifications" - "Agent Creator" ← creator_components() + "New primitive candidates" Each role has a corresponding starter tradeoff in `special_agent_tradeoffs()` (starters.rs:497-588): Assigner Balanced, Evaluator Balanced, Evolver Balanced, plus FOUR Creator tradeoffs (Unconstrained/Adjacent/Distant/Internal) — wg already encodes "agent_type → multiple tradeoff_configs" implicitly; the Creator already has a 4-config family. ## B. Current composition logic for the four functional agents 1. **Assigner** — `src/commands/assign.rs:9-68 record_assigner_evaluation`. Identity is config-driven: `config.agency.assigner_agent` (content hash). The actor agent's selection itself runs through `agency::request_agency_assignment` (federation) or `run_auto_assign`'s "highest avg_score wins" (assign.rs:181-202). The composer is NOT the assigner-agent — `wg assign` just records an evaluation against the configured assigner identity (placeholder score 0.5; updated later via the 6-step cascade). 2. **Evaluator** — `src/commands/evaluate.rs:282-304 evaluator_identity`. Identity is composed at runtime from `config.agency.evaluator_agent`'s role+tradeoff via `render_identity_prompt_rich`; this string replaces the generic system instruction in `render_evaluator_prompt` (src/agency/prompt.rs:360-378). Single role + single tradeoff. No cap on role components or outcomes — all components in the role ID list are resolved. 3. **Evolver** — config slot `evolver_agent` exists (config.rs:2635) but evaluator-style runtime identity injection is not yet wired. Search confirms no `evolver_identity` plumbing in src/commands/evolve/*. 4. **Agent Creator** — config slot `creator_agent` exists (config.rs:2639); only invoked when `auto_create=true`, gated by `auto_create_threshold`. No runtime identity injection. 5. **Placer** (wg-only) — config slot `placer_agent` exists; runs `.place-*` task at coordinator-time when `auto_place=true`. So composition today is: **one role + one tradeoff per functional-agent**, hard-wired by content hash in `[agency]`. No declarative caps on role_components / desired_outcomes / trade_off_configs — those are implicit ("a role bundles N components", "an agent has exactly 1 tradeoff"). ## C. Cross-reference: [models.evaluator] / [models.assigner] Already a first-class config surface. `ModelRoutingConfig` (src/config.rs:1508-1550) carries explicit slots for evaluator, assigner, evolver, creator, placer, plus task_agent, flip_inference, flip_comparison, verification, triage, compactor, chat_compactor, default. Every functional agent already has a dedicated routing slot — the user's CLAUDE.md note ("Power users who *want* a non-Anthropic provider … can override per-role via `[models.evaluator]` / `[models.assigner]` etc.") is the existing contract. Pinning to claude:haiku in CLAUDE.md is a doc convention, not enforced in config.rs — the actual default tier per role is set in `default_tier()` (src/config.rs:1208-1225): evaluator/assigner/triage/compactor/placer/ flip_comparison/chat_compactor/coordinator_eval default to Tier::Fast (haiku); evolver/creator default to Tier::Premium (opus). ## D. Decisions ### D.1 Adopt agency primitive `scope` field — YES, additive promotion Promote the implicit `RoleComponent.metadata["scope"]` lookup to a first-class, optional `scope: Option<String>` field on RoleComponent / DesiredOutcome / TradeoffConfig. Reasons: - Scope dispatch infrastructure already exists; data layer is empty. - Agency v1.2.4 starter CSV ships the field (col 9). For byte-equality CSV roundtrip (research-agency-csv-roundtrip's deliverable), wg must accept and emit `scope` as a column, not as `metadata.scope`. - A first-class field is index-friendly for stats/scan commands; HashMap lookup is fine for runtime but ugly in storage YAML. - Backward-compat: keep reading `metadata["scope"]` when the typed field is absent (one-release migration; wg has explicit "skip back-compat ceremony" guidance — but here the migration is trivial: load typed field, fall back to metadata, write typed field on next save). Do NOT remove the task-tag prefix scheme — keep both. Task tags are the RUNTIME signal (the `.evaluate-foo` task definitionally requests an evaluator-scoped composer); scope on primitives is the CATALOG signal (this RoleComponent is shaped for evaluator work). Both are needed: the prefix tells `required_scope_for_task` what to ask for; the scope field tells the primitive store what it can offer. They were already designed to compose — just one half was wired and the other left for later. This research closes the loop. Action items for impl-agency-scope-rules: - Add `scope: Option<String>` to RoleComponent, DesiredOutcome, TradeoffConfig (src/agency/types.rs:184, :209, :240). - Update `filter_components_by_scope` (run_mode.rs:84-100) to check the typed field first, falling back to `metadata.get("scope")`. - Have `seed_starters` (or a follow-up task) tag the four special_agent_* component sets with their scope strings. - Map `.place-*` to `meta:placer` in `required_scope_for_task`, OR decide explicitly that placer is wg-only and gets no agency scope. Recommend: add `meta:placer` to wg with a comment that agency v1.2.4 does not define this scope; if wg ever federates placer primitives back to agency they will be tagged "wg-only". ### D.2 Adopt composition-rules.csv overlay — NO (extend [models.X] instead) Reject the separate `~/.agency/composition-rules.csv` watched-file overlay. Reasons: - wg already has `[models.assigner]`, `[models.evaluator]`, etc. in `~/.wg/config.toml` (per-role model routing). Adding a parallel `~/.agency/composition-rules.csv` for caps (max_role_components etc.) fragments the per-functional-agent config across two files, two formats, two watch loops. The user's "skip back-compat ceremony" rule applies in reverse: don't ADD ceremony. - The user's "merge freeze" / single-active-user pattern means we don't need cross-instance CSV federation for composition rules right now. - TOML hot-reload already works (src/commands/config_cmd.rs auto-reload on `wg config -m/-e`); the daemon picks up changes without restart. Recommended path: add three new optional fields to `RoleModelConfig` (src/config.rs:1490-1504), or to a new `[composition.<role>]` section parallel to `[models.<role>]`: [composition.assigner] max_role_components = 5 max_desired_outcomes = 1 max_trade_off_configs = 1 rule = "balanced" # free-form policy hint, mirrors agency CSV column The schema mirrors agency's CSV columns 1:1 (agent_type, rule, max_role_components, max_desired_outcomes, max_trade_off_configs, all_projects, project_ids), which keeps a future federation translator trivial — and `all_projects`/`project_ids` map naturally onto wg's profile system (src/profile.rs). If federation later requires byte-equal CSV roundtrip with an Agency instance, add a thin import/export shim in `src/commands/agency_import.rs` that reads/writes `composition-rules.csv` against the TOML representation. This keeps wg's single config surface authoritative while still being agency-compatible at the wire level. ### D.3 Placer is wg-only — close the gap The fifth functional agent `Placer` is workgraph's extension. Either upstream it to agency v1.3+ or document it as wg-specific. Recommended: - Add `placer_components()` / "Placer Balanced" tradeoff / "Placer" role to special_agent_* in starters.rs (parallel to the other four). - Map `.place-*` to `meta:placer` in `required_scope_for_task`. - File this proposal upstream (agency repo) as a candidate v1.3 scope. This is out of scope for the impl-agency-scope-rules task per the design doc's "non-overlapping file scopes" rule — file as a follow-up task in the integration phase or as a separate "wg-extends-agency-with-placer" task. ## E. File:line citations - src/agency/prompt.rs:223-300 (render_identity_prompt_rich) - src/agency/prompt.rs:302-354 (EvaluatorInput) - src/agency/prompt.rs:360-378 (evaluator_identity replaces system prompt) - src/agency/prompt.rs:903-982 (AssignerModeContext + render_assigner_mode_context) - src/agency/run_mode.rs:47-66 (required_scope_for_task — task-tag → scope) - src/agency/run_mode.rs:68-81 (scope_matches; backward-compat None=all) - src/agency/run_mode.rs:84-100 (filter_components_by_scope) - src/agency/run_mode.rs:253-255 (filter applied in learning experiments) - src/agency/starters.rs:268,316,358,412 (assigner/evaluator/evolver/creator components) - src/agency/starters.rs:454-588 (special_agent_outcomes + tradeoffs) - src/agency/starters.rs:591-646 (special_agent_roles) - src/agency/types.rs:184-203 (RoleComponent, has metadata HashMap, no scope field) - src/agency/types.rs:209-230 (DesiredOutcome, no scope) - src/agency/types.rs:240-261 (TradeoffConfig, no scope) - src/commands/assign.rs:9-68 (record_assigner_evaluation, config-driven) - src/commands/assign.rs:181-202 (run_auto_assign, max-by-avg_score) - src/commands/evaluate.rs:282-304 (evaluator_identity composition) - src/config.rs:1102-1131 (DispatchRole — 14 roles, 5 are functional-agent) - src/config.rs:1208-1225 (default_tier — haiku for evaluator/assigner/placer) - src/config.rs:1488-1550 (ModelRoutingConfig with per-role slots) - src/config.rs:2625-2643 (AgencyConfig: assigner/evaluator/evolver/creator/placer slots) ## F. Validation against task criteria - [x] Mapping table covers all four functional agents (plus placer as wg-only) - [x] Decision documented for primitive `scope` field — YES, additive - [x] Decision documented for composition-rules.csv — NO, extend [models.X] - [x] Cites [models.evaluator] / [models.assigner] (config.rs:1488-1550) and proposes composition rules live next to it (TOML), not in a separate ~/.agency/composition-rules.csv
- 2026-05-04T18:43:47.185666876+00:00 Validated: mapping table covers all 4 agency scopes (+placer wg-only); scope-field decision YES additive; composition-rules.csv NO (extend [models.X]); citations include [models.evaluator] / [models.assigner] surface at config.rs:1488-1550. No source files modified — research-only task per design-agency-sync subgraph spec.
- 2026-05-04T18:44:17.470157190+00:00 Task pending eval (agent reported done; awaiting `.evaluate-*` to score)
- 2026-05-04T18:45:20.084093827+00:00 PendingEval → Done (evaluator passed; downstream unblocks)