Skip to content

fix(agent): add post-load semantic validation for contracts/constants.json values #219

@nizar-lahlali

Description

@nizar-lahlali

Summary

Follow-up from PR #204 code review comment. The shared constants loaded from contracts/constants.json in agent/src/policy.py are parsed as integers but never validated for semantic correctness (invariants like min > 0, default >= min, max >= default). If constants.json is corrupted (e.g., bad merge sets "min": 0), the floor clamp becomes ineffective — a security-relevant silent misbehavior since approval_timeout_s.min governs how long humans have to review approval prompts.

Problem

policy.py currently does:

_ATS = _SHARED_CONSTANTS["approval_timeout_s"]
FLOOR_TIMEOUT_S: int = int(_ATS["min"])
DEFAULT_TASK_TIMEOUT_S: int = int(_ATS["default"])

No assertions enforce that these values are logically consistent. The same weakness exists for approval_gate_cap.

Proposed change

Add post-load assertions in agent/src/policy.py immediately after parsing each constant group from contracts/constants.json:

For approval_timeout_s:

if FLOOR_TIMEOUT_S <= 0:
    raise ValueError(f"contracts/constants.json: approval_timeout_s.min must be > 0, got {FLOOR_TIMEOUT_S}")
if DEFAULT_TASK_TIMEOUT_S < FLOOR_TIMEOUT_S:
    raise ValueError(f"contracts/constants.json: approval_timeout_s.default ({DEFAULT_TASK_TIMEOUT_S}) must be >= min ({FLOOR_TIMEOUT_S})")
if APPROVAL_TIMEOUT_S_MAX < DEFAULT_TASK_TIMEOUT_S:
    raise ValueError(f"contracts/constants.json: approval_timeout_s.max ({APPROVAL_TIMEOUT_S_MAX}) must be >= default ({DEFAULT_TASK_TIMEOUT_S})")

For approval_gate_cap:

if APPROVAL_GATE_CAP_MIN <= 0:
    raise ValueError(f"contracts/constants.json: approval_gate_cap.min must be > 0, got {APPROVAL_GATE_CAP_MIN}")
if DEFAULT_APPROVAL_GATE_CAP < APPROVAL_GATE_CAP_MIN:
    raise ValueError(f"contracts/constants.json: approval_gate_cap.default ({DEFAULT_APPROVAL_GATE_CAP}) must be >= min ({APPROVAL_GATE_CAP_MIN})")
if APPROVAL_GATE_CAP_MAX < DEFAULT_APPROVAL_GATE_CAP:
    raise ValueError(f"contracts/constants.json: approval_gate_cap.max ({APPROVAL_GATE_CAP_MAX}) must be >= default ({DEFAULT_APPROVAL_GATE_CAP})")

Consider also mirroring equivalent validation in the TypeScript side (cdk/src/handlers/shared/types.ts) where sharedConstants is consumed, to fail at compile/synth time rather than runtime.

Acceptance criteria

  • agent/src/policy.py raises ValueError at module load if approval_timeout_s invariants are violated (min > 0, default >= min, max >= default)
  • agent/src/policy.py raises ValueError at module load if approval_gate_cap invariants are violated (same pattern)
  • Unit tests in agent/tests/ cover each invalid-constant scenario (corrupted JSON triggers the expected error)
  • scripts/check-constants-sync.ts optionally extended to validate invariants at CI time (belt-and-suspenders)
  • mise //agent:quality passes

Context

References

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions