Skip to content

Missing Weekly Position Skill Growth, Split Skill Keys, and Non-Idempotent Weekly Settlement on Resume #3

Description

@Hobo12

Problem Summary

We have identified three interrelated issues in the core simulation and data management flow that violate the defined skills and economy data contracts:

  1. Missing Weekly Position Skill Settlement: The weekly settlement process (_before_week_start) successfully processes weekly income, but entirely misses applying the weekly_delta_skills configured for the agent's current position.
  2. Split Skill Keys (No Normalization): Skill gains from activity outcomes are appended directly to the state using raw keys returned by the model/environment. If an agent has a Chinese initial skill (e.g., 观察与共情) but an activity returns an English counterpart (e.g., observation_and_empathy), they are persisted as two separate keys in the agent's state instead of being mapped and aggregated.
  3. Non-Idempotent Re-execution on Last-Week Resume: When the simulation resumes from a checkpoint at the final week of a year (e.g., Y2020-W10), the resume logic resolves the starting point to that same week, triggering a re-run. Because the weekly settlement stage (_before_week_start) is not idempotent, the agents receive duplicate weekly income and decay calculations.

Implications on Research & Essay Findings

These issues directly impact some of the key analytical findings presented in the essay:

  • In Striving vs. Leisurely Agent Profiles (essay/Sections/appendix.tex:1420-1468), a striving score is defined based on extra_earning_count and skill_advance_count to support the conclusion that "Striving agents accumulate more wealth but do not lead to greater well-being."
  • Because:
    1. Position-based skill growth is never added to the agent's state weekly, and
    2. Activity-based skill gains suffer from split keys (preventing proper accumulation under a unified skill canonical name),
  • The underlying skill metrics (total_skills, skill_improvement_count) used for these calculations may be distorted. The striving-to-leisurely grouping and its corresponding correlations might require a recalculation once these data contract issues are resolved.

As for what the simulation results will look like after fixing these bugs—we believe it will be highly intriguing! Unfortunately, our server's compute budget is quite limited, so we'll leave that exciting discovery to the maintainers QwQ.


Minimal Isolated Reproduction

The following Python script reproduces all three issues locally using memory-only mocks (no external API calls or LLM dependency). Run it from the repository root:

from types import SimpleNamespace
from src.config import load_config

# Load default config template
load_config("config.example.json")

from src.agents.data_manager import DataManager
from src.world.world import World

class Logger:
    def info(self, *args, **kwargs): pass
    def warning(self, *args, **kwargs): pass

# --- Case 1: Missing Weekly Position Skill Settlement ---
dm = SimpleNamespace()
dm.state = {
    "vitality": 70,
    "fulfillment": {"mood": 50, "material": 50, "social": 50, "esteem": 50},
    "skills": {"观察与共情": 10},
    "assets": {"deposit": 100, "possessions": []}
}
dm.read_profile = lambda: {
    "position": {
        "weekly_income": 20,
        "weekly_delta_skills": {"观察与共情": 3}
    },
    "extra_income": 0
}
dm.get_fulfillment = lambda: dm.state["fulfillment"]
dm.apply_fulfillment_decay = lambda decays: None
dm.get_deposit = lambda: dm.state["assets"]["deposit"]
dm.update_deposit = lambda val: dm.state["assets"].update(deposit=val)

world = World.__new__(World)
world.config = {"fulfillment_decay_min_ratio": {k: 0 for k in dm.state["fulfillment"]}}
world.agents = [SimpleNamespace(name="TestAgent", dm=dm)]
world.logger = Logger()

# Run the weekly pre-start settlement
world._before_week_start()

print("--- Case 1 ---")
print(f"Expected Deposit: 120 | Actual: {dm.state['assets']['deposit']}")
print(f"Expected Skills: {{'观察与共情': 13}} | Actual: {dm.state['skills']}")


# --- Case 2: Split Skill Keys (No Normalization) ---
state = {
    "vitality": 70,
    "fulfillment": {},
    "skills": {"观察与共情": 10},
    "assets": {"deposit": 0, "possessions": []}
}
manager = DataManager.__new__(DataManager)
manager.read_state = lambda **kwargs: state
manager.save_state = lambda value: None

outcome = SimpleNamespace(
    delta_vitality=0,
    delta_fulfillment={},
    delta_skills={"observation_and_empathy": 2},
    delta_money=0,
    gain_items=[]
)

# Apply activity outcome
manager.apply_activity_outcome(outcome)

print("\n--- Case 2 ---")
print("Persisted Skills in State:")
print(state["skills"])
print(f"Is same skill split into two keys? {'Yes' if len(state['skills']) == 2 else 'No'}")


# --- Case 3: Non-Idempotent Last-Week Resume ---
resume_world = World.__new__(World)
resume_world.config = {"time": {"start_year": 2020, "n_week": 10}}
resume_world._read_checkpoint = lambda: {"year": 2020, "week": 10}

print("\n--- Case 3 ---")
print(f"Checkpoint at Y2020-W10 resolves to starting week: {resume_world._resolve_resume_point(None)}")

Output of the Repro:

--- Case 1 ---
Expected Deposit: 120 | Actual: 120
Expected Skills: {'观察与共情': 13} | Actual: {'观察与共情': 10}   # Skill growth is missing!

--- Case 2 ---
Persisted Skills in State:
{'观察与共情': 10, 'observation_and_empathy': 2}                   # Key split!
Is same skill split into two keys? Yes

--- Case 3 ---
Checkpoint at Y2020-W10 resolves to starting week: (2020, 10)       # Week 10 will re-run settlements!

Technical Root Cause Analysis

1. Position Skills Omitted in Weekly Settlement

  • Location: src/world/world.py:448-451
  • Analysis: _before_week_start() only triggers _apply_fulfillment_decay() and _settle_weekly_income(). There is no execution path that reads weekly_delta_skills from the agent's current-year profile position data and adds them to skills in state.jsonl.

2. No Skill Name Normalization / Mapping

  • Location: src/agents/data_manager.py:2548-2550
  • Analysis: In apply_activity_outcome(), skill increments are merged directly using outcome.delta_skills.items() without passing them through a canonical dictionary translation or alias-merging function.

3. Checkpoint Resume Resolves to Non-Idempotent Week

  • Location: src/world/world.py:259-282
  • Analysis: When week == n_week, the resume logic returns (year, week) (e.g., (2020, 10)), which forces a complete re-execution of the final week of that year. During step() (src/world/world.py:662-664), _before_week_start() is called unconditionally, causing another round of deposits and decay to accumulate.

Suggested Fixes

  1. Implement Position Skill Settlement: Extend _before_week_start (or introduce a sister method in world.py) to fetch weekly_delta_skills from each agent's active profile position, apply them to the current state, and persist via save_state().
  2. Canonical Skill Mapping: Implement a mapping dictionary (supporting English/Chinese aliases) inside DataManager. Ensure any skill key written to the state (during profile initialization, weekly position settlement, and activity outcomes) goes through a normalization step to merge synonym keys.
  3. Idempotence or Checkpoint Refinement: Ensure weekly settlements check if they have already run for the current (year, week) before modifying states. Alternatively, adjust _resolve_resume_point so that resuming from week == n_week resumes directly to the year-end transition stage without re-running the last week's non-idempotent pre-week settlement.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions