Skip to content

Fragile keyword matching in _position_action_friction() for outcome friction estimation #99

@DeveshParagiri

Description

@DeveshParagiri

Problem

_position_action_friction() in extropy/simulation/engine.py:1350-1385 uses hardcoded keyword substring matching to estimate behavioral friction from position labels:

token = position.lower()
low = ("maintain", "continue", "keep", "stay", "deny", "remove_shared_access", "no_change")
medium = ("reduce", "abandon", "pause")
high = ("switch", "migrate", "cancel", "subscribe", "purchase", "buy", "upgrade")

if any(k in token for k in low):
    return 0.2
if any(k in token for k in medium):
    return 0.4
if any(k in token for k in high):
    return 0.75
return 0.5  # default fallback

Silent Failure Cases

Position Label Expected Friction Actual Why
keep_subscription 0.2 (low) 0.2 ✅ "keep" matches
hold 0.2 (low) 0.5 ❌ Not in keywords
retain 0.2 (low) 0.5 ❌ Not in keywords
stop 0.75 (high) 0.5 ❌ Not in keywords
quit 0.75 (high) 0.5 ❌ Not in keywords
sign_up 0.75 (high) 0.5 ❌ Not in keywords
join 0.75 (high) 0.5 ❌ Not in keywords
leave 0.75 (high) 0.5 ❌ Not in keywords
drop 0.4 (medium) 0.5 ❌ Not in keywords

The LLM-generated scenario outcomes can use any phrasing. When they don't match the hardcoded keywords, friction defaults to 0.5, which affects:

  • _infer_private_anchor_position() - picks wrong default
  • Flip resistance calculations

Suggested Fix

Add explicit option_friction to outcome definitions in scenario spec:

# scenario.v1.yaml
outcomes:
  - name: position
    type: categorical
    options:
      - value: keep_current_plan
        friction: 0.2
      - value: switch_to_competitor  
        friction: 0.75
      - value: reduce_usage
        friction: 0.4

Then in engine:

def _position_action_friction(self, position: str | None) -> float:
    if not position:
        return 0.5
    # First check explicit friction from scenario spec
    if position in self._primary_option_friction:
        return self._primary_option_friction[position]
    # Fallback to heuristic with warning
    logger.warning(f"No explicit friction for '{position}', using heuristic")
    return self._heuristic_friction(position)

Impact

Medium - affects behavioral realism but has a reasonable default fallback (0.5).

References

  • extropy/simulation/engine.py:1350-1385
  • Used by _infer_private_anchor_position() at line 1387

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions