fix(cli): route 'agents actor add' through ActorRegistry.add() YAML-first path #3462
Labels
Clear labels
auto/needs-reevaluation
controller-managed
overdue
auto/blocked-by-deps
auto/ci-timeout
auto/claimed-implementer
auto/claimed-merge
auto/claimed-reviewer
auto/driver-down
auto/invariant-violation
auto/last-attempt-tier-0
auto/last-attempt-tier-1
auto/last-attempt-tier-2
auto/last-attempt-tier-min
Automation Tracking
auto/needs-conflict-resolution
auto/needs-implementer
auto/postmortem
auto/ready-to-merge
auto/restart-throttled
auto/revert
auto/sentinel
auto/stale-inactivity
auto/unstable
Blocked
Needs Feedback
Signed-off: Owner
Signed-off: Scrum Master
Signed-off: Tech Lead
Spike
Controller deferred this PR; awaiting Phase 6+ scope-evaluator or operator re-enablement.
Auto-agents controller manages this PR/issue (see tools/controller/deploy/RUNBOOK.md). Remove this label to abandon controller management.
PR blocked by an open issue dependency. Operator must close the dep (or remove the dependency link) before the merge driver can act. Auto-cleared by merge_drive when no open deps remain.
Most recent merge cycle hit CI timeout. Driver excludes this PR while last merge_cycle row is < 30 min old; label persists thereafter as visible history.
Currently being processed by an implementer worker.
Currently being processed by the merge driver.
Currently being processed by a reviewer worker.
Merge driver heartbeat stale; pipeline halted. Closed automatically on next clean tick.
Detected master commit violating the strict merge invariant. Tracked as an issue (not a PR label); kept here for label completeness.
In-cycle escalation: most recent attempt ran at the Tier 0 slot (`tier-0`). Slot's model defined in .opencode/models/tiers.yaml.
In-cycle escalation: most recent attempt ran at the Tier 1 slot (`tier-1`). Slot's model defined in .opencode/models/tiers.yaml.
In-cycle escalation: most recent attempt ran at the Tier 2 slot (`tier-2`). Slot's model defined in .opencode/models/tiers.yaml. Gated behind IMPLEMENTER_ESCALATION_TIER2_ENABLED.
In-cycle escalation: most recent attempt ran at the Tier -1 slot (`tier-min`). Slot's model defined in .opencode/models/tiers.yaml. Suffix is ``-min`` (not ``--1``) so the Forgejo UI reads naturally.
Tracking issues used by the AI Automation system for agents to communicate and report.
Rebase conflict needs LLM conflict-resolver.
Failing CI needs implementer attention.
Documenting a driver incident or rollback.
Reviewer has APPROVED this PR and no later REQUEST_CHANGES is outstanding. The merge driver requires this label to even consider a PR for merging. Set by the reviewer worker on APPROVE; cleared on REQUEST_CHANGES.
Train repeatedly lost master-tempo races. Driver excludes via merge_cycle until cooldown elapses; label persists as visible history.
Revert PR backing out an invariant violation. Fast-tracked through the merge driver.
Sentinel PR duplicated from upstream into a personal fork by tools/duplicate_prs_to_fork.py for pipeline testing. Lives only in the fork; the canonical pipeline never sees it.
No implementer activity for N days. Flagged for human review. Auto-cleared on next push to head branch.
Repeatedly fails on current master (>= 3 ci-fail-on-rebased-sha releases in 12 h). Excluded from driver until human triage.
A ticket in a blocked state and unable to complete until some other task is completed first.
Bounty
$100
A bounty of $100 for any open-source contributor who provides a MR that solves this issue
Bounty
$1000
A bounty of $1000 for any open-source contributor who provides a MR that solves this issue
Bounty
$10000
A bounty of $10000 for any open-source contributor who provides a MR that solves this issue
Bounty
$20
A bounty of $20 for any open-source contributor who provides a MR that solves this issue
Bounty
$2000
A bounty of $2000 for any open-source contributor who provides a MR that solves this issue
Bounty
$250
A bounty of $250 for any open-source contributor who provides a MR that solves this issue
Bounty
$50
A bounty of $50 for any open-source contributor who provides a MR that solves this issue
Bounty
$500
A bounty of $500 for any open-source contributor who provides a MR that solves this issue
Bounty
$5000
A bounty of $5000 for any open-source contributor who provides a MR that solves this issue
Bounty
$750
A bounty of $750 for any open-source contributor who provides a MR that solves this issue
MoSCoW
Could have
Could have feature in order to satisfy the epic/legendary.
MoSCoW
Must have
Must have feature in order to satisfy the epic/legendary.
MoSCoW
Should have
Should have feature in order to satisfy the epic/legendary.
There are questions in the ticket that can not be completed until the project owner provides clarity.
Points
1
1 man-hours worth of work for an expert with no learning curve.
Points
13
13 man-hours worth of work for an expert with no learning curve.
Points
2
2 man-hours worth of work for an expert with no learning curve.
Points
21
21 man-hours worth of work for an expert with no learning curve.
Points
3
3 man-hours worth of work for an expert with no learning curve.
Points
34
34 man-hours worth of work for an expert with no learning curve.
Points
5
5 man-hours worth of work for an expert with no learning curve.
Points
55
55 man-hours worth of work for an expert with no learning curve.
Points
8
8 man-hours worth of work for an expert with no learning curve.
Points
88
88 man-hours worth of work for an expert with no learning curve.
Priority
Backlog
This ticket has backlogged priority and is not to be worked on yet
Priority
CI Blocker
Critical priority issue that blocks CI/CD pipeline and prevents PR merges
Priority
Critical
The priority is critical
Priority
High
The priority is high
Priority
Low
The priority is low
Priority
Medium
The priority is medium
When an epic or legendary is in review it must be signed off by owner, tech lead, and scrum master before being marked as completed.
When an epic or legendary is in review it must be signed off by owner, tech lead, and scrum master before being marked as completed.
When an epic or legendary is in review it must be signed off by owner, tech lead, and scrum master before being marked as completed.
A ticket for learning a tool or technology that is needed to be able to do future planning and design.
State
Completed
The ticket has been fully implemented, completed, and merged with the source code. This label should only be applied once a ticket is closed.
State
Duplicate
A ticket that represents the same content as an existing ticket.
State
In Progress
A ticket that is actively being developed.
State
In Review
A ticket that has had some code completed to implement but is waiting to pass peer review and is not yet merged in.
State
Paused
This ticket's work started but wasn't finished. It's on hold (likely in a feature branch) and will be resumed later, either due to a blocker or a delay.
State
Unverified
All new tickets start in this state. A developer may set it to show the ticket is unverified. This means we haven't agreed to work on it. It will either move to a verified state or be closed as wontdo.
State
Verified
The issue has been verified by a developer as legitimate. It will be worked on and verified tickets are now considered part of the backlog.
State
Wont Do
This ticket has been decided it wont be done. This may mean the bug has been determined to not be real (cant verify) or the feature is one we have decided we dont want to adopt.
Type
Automation
Any edits or discussion about the AI automated coding system.
Type
Bug
Something that doesnt work as intended.
Type
Discussion
Anytime a ticket represents a discussion about a subject and doesnt fall into one of the other categories.
Type
Documentation
An error or improvement needed in the documentation.
Type
Epic
Any first tier epic. That is, an epic which contains only issues as children and will not have sub-epics.
Type
Feature
Some new functionality not present.
Type
Legendary
A type of Epic which will contain other Epics.
Type
Refactor
A code change that restructures existing code without changing its external behavior.
Type
Support
Someone needs help using the project.
Type
Task
A generic task that doesnt fit into the other type categories.
Type
Testing
Work exclusively focusing on fixing or expanding testing.
No Label
Milestone
No items
No Milestone
Projects
Clear projects
No project
Assignees
aditya (Aditya Chhabra)
aleenaumair (Aleena Umair)
brent.edwards (Brent Edwards)
CoreRasurae (Luis Mendes)
drew (Drew Morris)
eugen.thaci (Eugen Thaci)
freemo (Jeffrey Phillips Freeman)
HAL9000 (HAL 9000)
HAL9001 (HAL9001)
hamza.khyari (Hamza Khyari)
hurui200320 (Rui Hu)
justin.morris
khird (Kyle Hird)
org.cleveragents
Clear assignees
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: cleveragents/cleveragents-core#3462
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Delete Branch "fix/actor-add-cli-yaml-first-path"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
agents actor addthroughActorRegistry.add()instead of the legacyregistry.upsert_actor()path_load_config_text()helper to read both raw YAML text and parsed dict from config filesyaml_text,schema_version, andcompiled_metadatain the databaseregistry.add()instead ofregistry.upsert_actor()Problem
The
agents actor addCLI command was callingregistry.upsert_actor()directly, bypassing the YAML-first persistence path. This meant:yaml_text=None)schema_versionwas not set from the file (defaulted to"1.0")compiled_metadatawas not storedSolution
Refactored
add()insrc/cleveragents/cli/commands/actor.pyto:_load_config_text()helperregistry.add(yaml_text, update=update_existing)when a registry is availableThe
--unsafeflag still works correctly — it prevents therequires_confirmationcheck from blocking the call, andregistry.add()reads theunsafefield from the YAML blob.Tests
registry.add()instead ofregistry.upsert_actor()features/actor_add_yaml_first_path.featurewith 5 scenarios verifying the YAML-first pathrobot/actor_add_yaml_first_path.robotwith 3 integration testsCloses #3426
Automated by CleverAgents Bot
Supervisor: Implementation | Agent: ca-issue-worker
Code Review — PR #3462
Review Focus: architecture-alignment, module-boundaries, specification-compliance
Reviewed the full diff across 9 files: the core CLI change in
actor.py, the new_load_config_text()helper, theActorRegistry.add()integration, 5 new Behave scenarios, 3 new Robot integration tests, and all updated mock paths in existing test step files.❌ Required Changes
1. [REGRESSION]
--set-defaultflag silently ignored when registry is availablesrc/cleveragents/cli/commands/actor.py—add()function, registry path (~line 290)set_default=set_defaulttoregistry.upsert_actor(). The new code callsregistry.add(yaml_text, update=update_existing)which does not accept aset_defaultparameter. Looking atActorRegistry.add()insrc/cleveragents/actor/registry.py, it hardcodesset_default=Falsein its internal call to_actor_service.upsert_actor().agents actor add --config actor.yaml --set-defaultwill silently ignore the--set-defaultflag. The actor will be added but NOT set as default. This is a user-facing behavioral regression.set_defaultparameter toActorRegistry.add()and thread it through, or (b) callregistry.set_default_actor(name)afterregistry.add()whenset_defaultis True.2. [REGRESSION]
--optionoverrides silently ignored when registry is availablesrc/cleveragents/cli/commands/actor.py—add()function, registry path (~line 288)option_overrides=option_overridestoregistry.upsert_actor(), which merged them into the canonical blob. The new code callsregistry.add(yaml_text, ...)which only receives the raw YAML text.ActorRegistry.add()parses the YAML fresh and has no knowledge of CLI-provided option overrides. The_canonicalize_actor_config()call earlier in the function computescanonical_blobwith overrides applied, but this result is never used in the registry path.agents actor add --config actor.yaml --option temperature=0.5will silently ignore the--optionflag. The option override will not be applied. This is a user-facing behavioral regression.option_overridesparameter toActorRegistry.add(), or (b) modify the YAML text to include the overrides before passing it toregistry.add(), or (c) apply overrides afterregistry.add()returns via a separate update call.3. [TEST] Missing test coverage for regressed flags
features/actor_add_yaml_first_path.feature,features/steps/actor_add_yaml_first_path_steps.pyregistry.add()is called withyaml_textandupdate=True, but there are no tests verifying that--set-defaultand--optionflags are properly handled through the new YAML-first path. These are the exact flags that are now broken.agents actor add --config ... --set-defaultresults in the actor being set as defaultagents actor add --config ... --option key=valueresults in the option override being applied⚠️ Observations (Non-blocking)
4. Code duplication between
_load_config()and_load_config_text()src/cleveragents/cli/commands/actor.py— both helper functions_load_config_text()is nearly identical to_load_config(). The only difference is the return type:dict | Nonevstuple[str, dict] | None. This is a DRY violation._load_config()to call_load_config_text()internally and discard the text, or extract the shared parsing logic into a common helper.5. PR metadata incomplete
Type/label (required by CONTRIBUTING.md — every PR must have exactly oneType/label). Based on the linked issue #3426 which hasType/Bug, this PR should haveType/Bugas well.✅ Good Aspects
ActorRegistry.add()properly preservesyaml_text,schema_version, andcompiled_metadatain the database, which aligns with the v3 YAML-first persistence model.ISSUES CLOSEDfooter._load_config_text()helper properly validates inputs and raisestyper.BadParameterfor invalid configs, following fail-fast principles.ActorRegistry.add()method itself is well-designed with proper validation, namespace enforcement, and duplicate detection.Summary
The PR correctly identifies and addresses the core problem (CLI bypassing YAML-first persistence), but the refactoring introduces two silent behavioral regressions where the
--set-defaultand--optionCLI flags are dropped when routing throughregistry.add(). These must be fixed before merge, as they would cause user-facing bugs where documented CLI options silently stop working.Decision: REQUEST CHANGES 🔄
Automated by CleverAgents Bot
Reviewer: Code Quality | Agent: ca-pr-self-reviewer
[REGRESSION]
option_overridescomputed earlier in this function are never used in the registry path. The_canonicalize_actor_config()call above computescanonical_blobwith overrides applied, butregistry.add()only receives the rawyaml_text— the overrides are lost.Users running
agents actor add --config f.yaml --option temperature=0.5will have their--optionflag silently ignored.[REGRESSION]
set_defaultis not passed toregistry.add(). TheActorRegistry.add()method hardcodesset_default=False. This means--set-defaultis silently ignored.The old code was:
The new code drops
set_defaultentirely. Either add it as a parameter toActorRegistry.add()or callregistry.set_default_actor()after the add.🔍 Code Review — REQUEST CHANGES
Reviewed PR #3462 with focus on api-consistency, naming-conventions, and code-patterns.
The intent of this PR is correct and well-motivated: routing
agents actor addthroughActorRegistry.add()to preserve the original YAML text, schema version, and compiled metadata. However, the implementation introduces silent behavioral regressions where several CLI flags are now silently ignored, and violates DRY principles.Required Changes
1. 🚨 [API-CONSISTENCY / CRITICAL] CLI flags
--option,--set-defaultsilently dropped on registry pathLocation:
src/cleveragents/cli/commands/actor.py, lines ~531-534 (the newregistry.add()call)Issue: The
ActorRegistry.add()method signature is:It does not accept
option_overrides,set_default, orallow_unsafeparameters. The new code:This means:
--set-defaultflag is parsed and validated but silently ignored — the actor is never set as default--option key=valueoverrides are parsed, canonicalized intooption_overrides, but never applied — the YAML blob is used as-is_canonicalize_actor_config()call still runs (computingcanonical_blob,resolved,requires_confirmation) but its output is discarded on the registry pathThis is a user-facing behavioral regression. A user running
agents actor add --config actor.yaml --set-default --option temperature=0.5would see no error but the actor would not be set as default and the temperature override would be lost.Required: Either:
(a) Extend
ActorRegistry.add()to acceptset_default,option_overrides, andallow_unsafeparameters, or(b) After calling
registry.add(), applyset_defaultviaregistry.set_default_actor()and handle option overrides by modifying the YAML text before passing it, or(c) Continue using
registry.upsert_actor()but passyaml_text=yaml_textto it (the legacy method already acceptsyaml_text,schema_version, andcompiled_metadatakwargs — seeregistry.pylines 259-274)Option (c) is the simplest and most correct approach — it preserves all existing CLI flag behavior while also threading through the YAML text.
2. ⚠️ [API-CONSISTENCY]
schema_versionandcompiled_metadatanot threaded throughsrc/cleveragents/cli/commands/actor.py, lines ~531-534schema_versionandcompiled_metadataare correctly threaded through from the CLI toActorRegistry.add()". The new code does not pass these parameters. Whileregistry.add()has defaults, the CLI should at minimum extractschema_versionfrom the parsed config blob if present.schema_versionfrom the parsed config blob if present and pass it toregistry.add().3. ⚠️ [CODE-PATTERNS / DRY]
_load_config_text()duplicates_load_config()src/cleveragents/cli/commands/actor.py, lines ~238-268_load_config_text()is a near-exact copy of_load_config()with the only difference being it returns(text, dict)instead of justdict. This violates DRY and creates a maintenance burden._load_config()delegate to_load_config_text()and return only the dict portion.4. ⚠️ [CODE-PATTERNS] Dead code / redundant null checks
src/cleveragents/cli/commands/actor.py, lines ~496-503config is Nonethen immediately calls_load_config_text(config)and checksloaded is None. The second check is dead code —_load_config_text()only returnsNonewhenconfig_path is None, which was already handled.5. ⚠️ [API-CONSISTENCY]
_canonicalize_actor_config()output discarded on registry pathsrc/cleveragents/cli/commands/actor.py, lines ~510-530_canonicalize_actor_config()which computesresolved,canonical_blob, andrequires_confirmation. Onlyrequires_confirmationis used;canonical_blobandresolvedare completely discarded on the registry path. Theregistry.add()method does its own parsing viaActorConfiguration.load_yaml_text(), which may produce different results thanActorConfiguration.from_blob(). This creates an inconsistency between what the CLI validates and what gets persisted._canonicalize_actor_config()call on the registry path, or ensure the results are used consistently.6. ⚠️ [TEST] Missing tests for
--optionand--set-defaultwith new pathfeatures/steps/actor_cli_steps.py(multiple step definitions)context.expected_config_blobandcontext.expected_allow_unsafewere removed from multiple step definitions, but no replacement assertions verify that--optionoverrides and--set-defaultflag behavior still work correctly. These flags are now silently dropped.Minor Issues (Non-blocking)
_load_config_textis slightly misleading — it returns both text and parsed dict. Consider_load_config_with_raw_textor_load_config_raw.Type/label on the PR (required by CONTRIBUTING.md).Good Aspects
ISSUES CLOSED: #3426Recommendation
The simplest fix that preserves all existing behavior while achieving the YAML-first goal would be to keep using
registry.upsert_actor()but passyaml_text=yaml_textto it. The legacyupsert_actor()method already acceptsyaml_text,schema_version, andcompiled_metadataparameters (seeregistry.pylines 272-274) and threads them through to_actor_service.upsert_actor(). This approach:--option,--set-default,--unsafe)_load_config_text()function — just read the raw text separatelyDecision: REQUEST CHANGES 🔄
Automated by CleverAgents Bot
Supervisor: PR Review | Agent: ca-pr-self-reviewer
0ebc5158beto8a0154d9baReview Feedback Addressed
Implemented all required changes from the two review comments. Here's what was fixed:
✅ Required Changes Implemented
1.
--set-defaultflag now honoured (was silently ignored)registry.add()doesn't acceptset_defaultparameterregistry.upsert_actor()withyaml_text=yaml_textthreaded through (as suggested in review option (c))set_default=set_defaultis now correctly passed toregistry.upsert_actor()2.
--optionoverrides now honoured (were silently ignored)registry.add()doesn't acceptoption_overridesparameterregistry.upsert_actor()withoption_overrides=option_overridespassed through_canonicalize_actor_config()output (canonical_blob) is now used correctly3.
schema_versionthreaded throughschema_version=schema_versiontoregistry.upsert_actor()4. DRY violation fixed —
_load_config()now delegates to_load_config_text()_load_config()is now a thin wrapper that calls_load_config_text()and discards the raw text5. Dead code / redundant null check removed
loaded is Nonecheck +type: ignore[misc]with a cleanassert loaded is not None(unreachable guard)6. Tests updated for
--set-defaultand--optionflagsset_default_flagandoption_override_flagtest casesregistry.upsert_actor()instead ofregistry.add()Verification
nox -s lint→ ✅ All checks passednox -s typecheck→ ✅ 0 errors, 0 warningsyaml_text_preserved,set_default_flag,option_override_flag,update_flagAutomated by CleverAgents Bot
Supervisor: Implementation | Agent: ca-issue-worker
8a0154d9bato62ded31c24Rebased onto Latest Master + Additional Fixes
Rebased onto latest master (
c3b2f597) and resolved merge conflicts. Additional fixes applied during rebase:Conflict Resolution
src/cleveragents/cli/commands/actor.py: Master added_compute_actor_impact()function and a--updateflag enforcement block (checks if actor already exists before adding). Both are preserved in the rebased commit.--updateenforcement block callsregistry.get_actor(name)— updated all test mocks to raiseNotFoundErrorso the "actor not found → proceed with add" path is exercised correctly.Additional Fixes Applied
whenstep definitions inactor_add_yaml_first_path_steps.pyto passlocal/test-actoras the required positionalNAMEargument (master added this as a required positional arg)registry.get_actor.side_effect = NotFoundError("not found")to all relevant test mocksyaml_text_preserved,set_default_flag,option_override_flag,update_flagVerification
nox -s lint→ ✅ All checks passednox -s typecheck→ ✅ 0 errors, 0 warningsAutomated by CleverAgents Bot
Supervisor: Implementation | Agent: ca-issue-worker
✅ Re-Review — PR #3462: All blocking issues addressed
VERDICT: APPROVE
The implementor has addressed all blocking issues:
--set-defaultflag now honoured — switched back toregistry.upsert_actor()withyaml_textthreaded through--optionoverrides now honoured —option_overridescorrectly passed toregistry.upsert_actor()schema_versionthreaded through — extracted from parsed config blob_load_config()now delegates to_load_config_text()assert--set-defaultand--optionflagsThis PR is ready to merge.
Automated by CleverAgents Bot
Supervisor: PR Review | Agent: ca-continuous-pr-reviewer