Files
regorus/docs/knowledge/value-semantics.md
copilot-swe-agent[bot] 524aab5528 Add knowledge docs, agent definitions, and skill files
Add comprehensive documentation and GitHub Copilot configuration:

- docs/knowledge/: 17 deep-dive knowledge files covering value semantics,
  RVM architecture, builtins, FFI boundary, feature composition, error
  handling migration, policy evaluation security, Rego semantics,
  interpreter/compiler architecture, Azure Policy/RBAC, engine API,
  time builtins, language extension guide, tooling architecture,
  causality/partial eval, Rego compiler, Azure Policy aliases, and
  telemetry/diagnostics

- .github/agents/: 16 role-specific AI agent definitions (red-teamer,
  semantics-expert, architect, performance-engineer, test-engineer,
  verification-engineer, security-auditor, reliability-engineer,
  support-engineer, ci-engineer, refactorer, api-steward, program-manager,
  demo-engineer, dx-engineer, tech-lead)

- .github/skills/: 6 workflow skill definitions (thorough-review,
  design-alternatives, add-builtin, opa-conformance, security-review,
  verification)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Co-authored-by: anakrish <35780660+anakrish@users.noreply.github.com>
2026-04-25 21:20:10 +00:00

5.6 KiB

Knowledge: Value Semantics

Deep knowledge about regorus's Value type, Undefined propagation, and three-valued logic. Read this before modifying src/value.rs, src/number.rs, or any evaluation code.

The Value Enum

pub enum Value {
    Null,                              // JSON null
    Bool(bool),                        // JSON boolean
    Number(Number),                    // u64 | i64 | f64 | BigInt — at least 100-digit precision
    String(Rc<str>),                   // Shared, cheap to clone
    Array(Rc<Vec<Value>>),             // Ordered collection
    Set(Rc<BTreeSet<Value>>),          // Ordered set (no JSON equivalent)
    Object(Rc<BTreeMap<Value, Value>>),// Keys can be any Value, not just strings
    Undefined,                         // Absence of value — NOT the same as Null or false
}

All collection variants use Rc (or Arc with the arc feature). Cloning a Value is a refcount bump. Use Rc::make_mut() for copy-on-write mutation.

Implementation note: Rego does NOT require ordered sets or objects. The current use of BTreeSet and BTreeMap provides deterministic ordering but this is an implementation detail, not a semantic requirement. The Value representation may change in the future (e.g., to hash-based collections for performance). Do not write code that depends on iteration order of Sets or Objects — treat them as unordered collections.

The Number Type

src/number.rs represents numbers as one of four internal representations:

Variant Range Use case
UInt(u64) 0 to 2^64-1 Non-negative integers
Int(i64) -2^63 to 2^63-1 Negative integers
Float(f64) IEEE 754 Fractional values
BigInt(Rc<BigInt>) Arbitrary Overflow from u64/i64

Invariants:

  • from_bigint_owned() normalizes: if a BigInt fits in i64/u64, it stores the smaller representation.
  • Float comparison uses the Number type's methods, never raw == on f64 (denied by clippy::float_cmp).
  • F64_SAFE_INTEGER = 2^53 — beyond this, float loses integer precision.
  • Arithmetic between variants promotes correctly (e.g., UInt + Int → Int or BigInt).

Never do raw arithmetic on Number internals. Use the type's methods — they handle precision, overflow, and type promotion.

Undefined: The Critical Concept

Undefined is NOT false. Undefined is NOT Null. Rego has three-valued logic where expressions can be true, false, or undefined (absent).

This is the single richest source of subtle bugs in regorus.

Propagation Rules

Boolean and comparison operations (src/interpreter.rs:618-676):

Undefined <op> anything  →  Undefined
anything <op> Undefined  →  Undefined

Both operands must be defined for the operation to produce a result.

Negation (not):

not true      →  false
not false     →  true
not Undefined →  true    ← THIS IS THE TRAP

not Undefined evaluates to true because negating "absence" means "the condition wasn't met" which is truthy in Rego. This is correct OPA semantics but extremely subtle.

Reference chains (a.b.c): If any intermediate key is missing or Undefined, the entire chain returns Undefined. The interpreter navigates the path and returns Undefined at the first missing component.

Collection construction (Array, Set, Object literals):

[1, Undefined, 3]  →  Undefined    (entire collection is Undefined!)

If ANY element in a collection literal is Undefined, the entire collection becomes Undefined. This is NOT intuitive — it doesn't skip the undefined element, it poisons the whole result.

Builtin function arguments:

builtin(x, Undefined, z)  →  Undefined

If any argument to a builtin function is Undefined, the result is Undefined. The function is never called.

Rule bodies: When a statement in a rule body evaluates to Undefined, the rule body fails (the rule doesn't produce a value for that input). This is Rego's core evaluation model — rules are "queries" that succeed or fail.

Default Rules and Undefined

Default rules only fire when:

  1. No complete rule for the path produced a defined value, AND
  2. The path is Undefined in the data

Precedence: initial data > evaluated rules > default rules

Testing Undefined

Every code path that handles Values must consider:

  1. What if this Value is Undefined?
  2. What if an intermediate value in a chain is Undefined?
  3. What does not <this expression> mean when the expression is Undefined?
  4. Does collection construction with an Undefined element behave correctly?

Value Ordering

Values implement Ord with a total order:

Null < Bool < Number < String < Array < Set < Object < Undefined

Within each variant, natural ordering applies (false < true, numeric order, lexicographic for strings, element-wise for collections).

This ordering matters for Set and Object (which use BTreeSet/BTreeMap).

Memory Limits

Value construction respects memory limits. The function enforce_limit_anyhow() is called during deserialization and construction to check the global memory limit (see src/utils/limits/memory.rs). This prevents adversarial JSON payloads from exhausting memory.

Serialization

  • Set serializes as JSON array (no JSON equivalent for sets)
  • Object keys that aren't strings are serialized as {"__regorus_key": key, "__regorus_value": value}
  • Undefined should never appear in serialized output (it represents absence)
  • Number serialization preserves precision (BigInt as string when needed)