UAT: agents invariant add CLI missing --non-overridable flag — cannot create non-overridable global invariants via CLI #5111

Closed
opened 2026-04-09 01:03:14 +00:00 by HAL9000 · 1 comment
Owner

Bug Report

Feature Area: Validation and Invariants — Non-Overridable Global Invariants
Tested By: UAT worker (uat-pool-1), feature area: Validation and Invariants
Severity: High — non-overridable global invariants cannot be created via the CLI


What Was Tested

Code-level analysis of the agents invariant add CLI command in:

  • src/cleveragents/cli/commands/invariant.py
  • src/cleveragents/application/services/invariant_service.py

Expected Behavior (from spec)

The spec (§Invariant System, line 19749) explicitly states:

Non-overridable global invariants: A global invariant may be marked non_overridable: true. When set, this invariant takes precedence over all lower-scope invariants regardless of the normal precedence chain — even plan-level invariants cannot override it. This is intended for system-wide safety constraints that must never be relaxed (e.g., "Never commit secrets to version control"). Non-overridable invariants are set via agents invariant add --global --non-overridable "<constraint>".

The Invariant domain model (invariant.py, line 86) already has the non_overridable: bool field. The InvariantReconciliationActor (reconciliation.py, lines 138–157) correctly handles non_overridable invariants during reconciliation.

Actual Behavior

CLI command (invariant.py, line 112)

The agents invariant add command does not have a --non-overridable flag:

@app.command()
def add(
    text: Annotated[str, typer.Argument(help="The invariant constraint text")],
    is_global: Annotated[bool, typer.Option("--global", ...)] = False,
    project: Annotated[str | None, typer.Option("--project", ...)] = None,
    plan: Annotated[str | None, typer.Option("--plan", ...)] = None,
    action: Annotated[str | None, typer.Option("--action", ...)] = None,
    fmt: Annotated[str, typer.Option("--format", ...)] = "rich",
    # ← --non-overridable flag is MISSING
) -> None:

Service method (invariant_service.py, line 63)

The InvariantService.add_invariant method does not accept a non_overridable parameter:

def add_invariant(
    self,
    text: str,
    scope: InvariantScope,
    source_name: str,
    # ← non_overridable parameter is MISSING
) -> Invariant:
    # ...
    invariant = Invariant(
        id=str(ULID()),
        text=text,
        scope=scope,
        source_name=source_name.strip(),
        # non_overridable always defaults to False — cannot be set
    )

Workaround in tests

The Behave test steps (features/steps/invariant_reconciliation_actor_steps.py, line 60) note this limitation:

@given('a non_overridable global invariant "{text}" from source "{source}"')
def step_add_non_overridable_global(context, text, source):
    """Add a non_overridable global-scope invariant."""
    # add_invariant does not expose non_overridable; create directly and store

The tests bypass the service entirely and create Invariant objects directly, confirming the service API gap.

Impact

  1. Non-overridable invariants cannot be created via CLI — users cannot set system-wide safety constraints that cannot be relaxed.
  2. Non-overridable invariants cannot be created via the service API — programmatic creation is also blocked.
  3. The non_overridable field on the Invariant model is effectively dead code from the user's perspective — it can only be set in tests by bypassing the service.
  4. Spec compliance: The spec explicitly documents --non-overridable as a supported CLI flag.

Code Locations

  • src/cleveragents/cli/commands/invariant.py:
    • Line 112: add() function — missing --non-overridable flag
  • src/cleveragents/application/services/invariant_service.py:
    • Line 63: add_invariant() — missing non_overridable parameter

Steps to Reproduce

$ agents invariant add --global --non-overridable "Never commit secrets to version control"
# Error: No such option: --non-overridable

Fix Required

  1. Add non_overridable: bool = False parameter to InvariantService.add_invariant() and pass it to the Invariant constructor.
  2. Add --non-overridable flag to the agents invariant add CLI command (only meaningful with --global; should warn or error if used with other scope flags).
  3. Update the add_invariant call in the CLI to pass non_overridable=True when the flag is set.

Automated by CleverAgents Bot
Supervisor: UAT Testing | Agent: uat-tester

## Bug Report **Feature Area**: Validation and Invariants — Non-Overridable Global Invariants **Tested By**: UAT worker (uat-pool-1), feature area: Validation and Invariants **Severity**: High — non-overridable global invariants cannot be created via the CLI --- ## What Was Tested Code-level analysis of the `agents invariant add` CLI command in: - `src/cleveragents/cli/commands/invariant.py` - `src/cleveragents/application/services/invariant_service.py` ## Expected Behavior (from spec) The spec (§Invariant System, line 19749) explicitly states: > **Non-overridable global invariants**: A global invariant may be marked `non_overridable: true`. When set, this invariant takes precedence over all lower-scope invariants regardless of the normal precedence chain — even plan-level invariants cannot override it. This is intended for system-wide safety constraints that must never be relaxed (e.g., "Never commit secrets to version control"). Non-overridable invariants are set via **`agents invariant add --global --non-overridable "<constraint>"`**. The `Invariant` domain model (`invariant.py`, line 86) already has the `non_overridable: bool` field. The `InvariantReconciliationActor` (`reconciliation.py`, lines 138–157) correctly handles `non_overridable` invariants during reconciliation. ## Actual Behavior ### CLI command (`invariant.py`, line 112) The `agents invariant add` command does **not** have a `--non-overridable` flag: ```python @app.command() def add( text: Annotated[str, typer.Argument(help="The invariant constraint text")], is_global: Annotated[bool, typer.Option("--global", ...)] = False, project: Annotated[str | None, typer.Option("--project", ...)] = None, plan: Annotated[str | None, typer.Option("--plan", ...)] = None, action: Annotated[str | None, typer.Option("--action", ...)] = None, fmt: Annotated[str, typer.Option("--format", ...)] = "rich", # ← --non-overridable flag is MISSING ) -> None: ``` ### Service method (`invariant_service.py`, line 63) The `InvariantService.add_invariant` method does **not** accept a `non_overridable` parameter: ```python def add_invariant( self, text: str, scope: InvariantScope, source_name: str, # ← non_overridable parameter is MISSING ) -> Invariant: # ... invariant = Invariant( id=str(ULID()), text=text, scope=scope, source_name=source_name.strip(), # non_overridable always defaults to False — cannot be set ) ``` ### Workaround in tests The Behave test steps (`features/steps/invariant_reconciliation_actor_steps.py`, line 60) note this limitation: ```python @given('a non_overridable global invariant "{text}" from source "{source}"') def step_add_non_overridable_global(context, text, source): """Add a non_overridable global-scope invariant.""" # add_invariant does not expose non_overridable; create directly and store ``` The tests bypass the service entirely and create `Invariant` objects directly, confirming the service API gap. ## Impact 1. **Non-overridable invariants cannot be created via CLI** — users cannot set system-wide safety constraints that cannot be relaxed. 2. **Non-overridable invariants cannot be created via the service API** — programmatic creation is also blocked. 3. **The `non_overridable` field on the `Invariant` model is effectively dead code** from the user's perspective — it can only be set in tests by bypassing the service. 4. **Spec compliance**: The spec explicitly documents `--non-overridable` as a supported CLI flag. ## Code Locations - `src/cleveragents/cli/commands/invariant.py`: - Line 112: `add()` function — missing `--non-overridable` flag - `src/cleveragents/application/services/invariant_service.py`: - Line 63: `add_invariant()` — missing `non_overridable` parameter ## Steps to Reproduce ```bash $ agents invariant add --global --non-overridable "Never commit secrets to version control" # Error: No such option: --non-overridable ``` ## Fix Required 1. Add `non_overridable: bool = False` parameter to `InvariantService.add_invariant()` and pass it to the `Invariant` constructor. 2. Add `--non-overridable` flag to the `agents invariant add` CLI command (only meaningful with `--global`; should warn or error if used with other scope flags). 3. Update the `add_invariant` call in the CLI to pass `non_overridable=True` when the flag is set. --- **Automated by CleverAgents Bot** Supervisor: UAT Testing | Agent: uat-tester
Author
Owner

Closing as duplicate of #4826 — both issues report the same problem: agents invariant add missing --non-overridable flag.


Automated by CleverAgents Bot
Supervisor: Backlog Grooming | Agent: backlog-groomer

Closing as duplicate of #4826 — both issues report the same problem: `agents invariant add` missing `--non-overridable` flag. --- **Automated by CleverAgents Bot** Supervisor: Backlog Grooming | Agent: backlog-groomer
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
cleveragents/cleveragents-core#5111
No description provided.