UAT: _invariant_dict() omits non_overridable field — JSON/YAML/plain output for invariant add and invariant list never shows the non_overridable flag #6336

Open
opened 2026-04-09 20:11:27 +00:00 by HAL9000 · 0 comments
Owner

Summary

The _invariant_dict() helper in src/cleveragents/cli/commands/invariant.py serializes Invariant objects for non-rich output formats (JSON, YAML, plain, table). It omits the non_overridable boolean field entirely, so any structured output from agents invariant add or agents invariant list never reveals whether a global invariant is non-overridable. This makes it impossible for downstream tooling to distinguish overridable from non-overridable global invariants.

Spec Reference

  • docs/specification.md line 19749: "Non-overridable invariants are set via agents invariant add --global --non-overridable <constraint>. The non_overridable flag is only meaningful on GLOBAL-scoped invariants."
  • The spec's JSON output for invariant add (line 17994) shows an invariant object structure — non_overridable is implied as part of the full invariant record.
  • domain/models/core/invariant.py line 86: Invariant.non_overridable: bool is a first-class model field.

Code Location

File: src/cleveragents/cli/commands/invariant.py, lines 100–109

def _invariant_dict(inv: Invariant) -> dict[str, object]:
    """Serialise an Invariant for non-rich output formats."""
    return {
        "id": inv.id,
        "text": inv.text,
        "scope": inv.scope.value,
        "source_name": inv.source_name,
        "active": inv.active,
        "created_at": inv.created_at.isoformat(),
        # BUG: inv.non_overridable is NOT included
    }

Steps to Reproduce

# (After #6253 is fixed and --non-overridable flag exists)
agents invariant add --global --non-overridable "Never commit secrets to version control" --format json

Expected JSON output (illustrative):

{
  "id": "01HXM9A1B...",
  "text": "Never commit secrets to version control",
  "scope": "global",
  "source_name": "system",
  "non_overridable": true,
  "active": true,
  "created_at": "2026-04-09T20:00:00Z"
}

Actual JSON output:

{
  "id": "01HXM9A1B...",
  "text": "Never commit secrets to version control",
  "scope": "global",
  "source_name": "system",
  "active": true,
  "created_at": "2026-04-09T20:00:00Z"
}

non_overridable is absent.

Impact

  • JSON/YAML/plain consumers of agents invariant list cannot determine which global invariants are non-overridable without re-querying a different endpoint.
  • The rich table output (lines 223–238) also does not display a "Non-overridable" column, making the flag invisible in all output formats.
  • Automation tooling (e.g., CI pipelines that audit invariants) cannot enforce or verify non-overridable constraints from structured output.

Fix

Add "non_overridable": inv.non_overridable to _invariant_dict():

def _invariant_dict(inv: Invariant) -> dict[str, object]:
    return {
        "id": inv.id,
        "text": inv.text,
        "scope": inv.scope.value,
        "source_name": inv.source_name,
        "non_overridable": inv.non_overridable,
        "active": inv.active,
        "created_at": inv.created_at.isoformat(),
    }

Also add a "Non-Overridable" column to the rich Table in list_invariants() (lines 223–238).


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

## Summary The `_invariant_dict()` helper in `src/cleveragents/cli/commands/invariant.py` serializes `Invariant` objects for non-rich output formats (JSON, YAML, plain, table). It omits the `non_overridable` boolean field entirely, so any structured output from `agents invariant add` or `agents invariant list` never reveals whether a global invariant is non-overridable. This makes it impossible for downstream tooling to distinguish overridable from non-overridable global invariants. ## Spec Reference - `docs/specification.md` line 19749: "Non-overridable invariants are set via `agents invariant add --global --non-overridable <constraint>`. The `non_overridable` flag is only meaningful on `GLOBAL`-scoped invariants." - The spec's JSON output for `invariant add` (line 17994) shows an `invariant` object structure — `non_overridable` is implied as part of the full invariant record. - `domain/models/core/invariant.py` line 86: `Invariant.non_overridable: bool` is a first-class model field. ## Code Location **File**: `src/cleveragents/cli/commands/invariant.py`, lines 100–109 ```python def _invariant_dict(inv: Invariant) -> dict[str, object]: """Serialise an Invariant for non-rich output formats.""" return { "id": inv.id, "text": inv.text, "scope": inv.scope.value, "source_name": inv.source_name, "active": inv.active, "created_at": inv.created_at.isoformat(), # BUG: inv.non_overridable is NOT included } ``` ## Steps to Reproduce ```bash # (After #6253 is fixed and --non-overridable flag exists) agents invariant add --global --non-overridable "Never commit secrets to version control" --format json ``` **Expected JSON output** (illustrative): ```json { "id": "01HXM9A1B...", "text": "Never commit secrets to version control", "scope": "global", "source_name": "system", "non_overridable": true, "active": true, "created_at": "2026-04-09T20:00:00Z" } ``` **Actual JSON output**: ```json { "id": "01HXM9A1B...", "text": "Never commit secrets to version control", "scope": "global", "source_name": "system", "active": true, "created_at": "2026-04-09T20:00:00Z" } ``` `non_overridable` is absent. ## Impact - JSON/YAML/plain consumers of `agents invariant list` cannot determine which global invariants are non-overridable without re-querying a different endpoint. - The rich table output (lines 223–238) also does not display a "Non-overridable" column, making the flag invisible in all output formats. - Automation tooling (e.g., CI pipelines that audit invariants) cannot enforce or verify non-overridable constraints from structured output. ## Fix Add `"non_overridable": inv.non_overridable` to `_invariant_dict()`: ```python def _invariant_dict(inv: Invariant) -> dict[str, object]: return { "id": inv.id, "text": inv.text, "scope": inv.scope.value, "source_name": inv.source_name, "non_overridable": inv.non_overridable, "active": inv.active, "created_at": inv.created_at.isoformat(), } ``` Also add a "Non-Overridable" column to the rich `Table` in `list_invariants()` (lines 223–238). --- **Automated by CleverAgents Bot** Supervisor: UAT Testing | Agent: uat-tester
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#6336
No description provided.