Skip to content
Back to skills

2540 Wm 003 A232ddc5

ASecurity

Add new methods to StateManager for recording timestamps and storing/retrieving metrics.

  • 9 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 11, 2026
testingpythonbash

Security analysis

A100/100

Scanned October 11, 2026

npx -y skills add tools-only/X-Skills --skill 2540-wm-003_a232ddc5 --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of 2540 Wm 003 A232ddc5?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for 2540 Wm 003 A232ddc5
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tools-only-2540-wm-003-a232ddc5/badge)](https://www.skillsdirectory.com/skills/tools-only-2540-wm-003-a232ddc5)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

SKILL.md
# WM-003: Extend StateManager with metrics methods

## Overview
Add new methods to StateManager for recording timestamps and storing/retrieving metrics.

## Files to Modify
- `zerg/state.py`

## Requirements

### New Imports
At the top of the file, add to the imports from zerg.types:
```python
from zerg.types import ExecutionEvent, WorkerState, FeatureMetrics
```

### New Methods

Add these methods to the StateManager class:

```python
def record_task_claimed(self, task_id: str, worker_id: int) -> None:
    """Record when a task was claimed by a worker.

    Sets the claimed_at timestamp for task metrics tracking.

    Args:
        task_id: Task identifier
        worker_id: Worker that claimed the task
    """
    with self._lock:
        if "tasks" not in self._state:
            self._state["tasks"] = {}

        if task_id not in self._state["tasks"]:
            self._state["tasks"][task_id] = {}

        self._state["tasks"][task_id]["claimed_at"] = datetime.now().isoformat()
        self._state["tasks"][task_id]["worker_id"] = worker_id

    self.save()
    logger.debug(f"Task {task_id} claimed by worker {worker_id}")


def record_task_duration(self, task_id: str, duration_ms: int) -> None:
    """Record task execution duration.

    Args:
        task_id: Task identifier
        duration_ms: Execution duration in milliseconds
    """
    with self._lock:
        if task_id in self._state.get("tasks", {}):
            self._state["tasks"][task_id]["duration_ms"] = duration_ms

    self.save()
    logger.debug(f"Task {task_id} duration: {duration_ms}ms")


def store_metrics(self, metrics: "FeatureMetrics") -> None:
    """Store computed metrics to state.

    Args:
        metrics: FeatureMetrics to persist
    """
    with self._lock:
        self._state["metrics"] = metrics.to_dict()

    self.save()
    logger.debug("Stored feature metrics")


def get_metrics(self) -> "FeatureMetrics | None":
    """Retrieve stored metrics.

    Returns:
        FeatureMetrics if available, None otherwise
    """
    with self._lock:
        metrics_data = self._state.get("metrics")
        if not metrics_data:
            return None

        # Import here to avoid circular import
        from zerg.types import FeatureMetrics
        return FeatureMetrics.from_dict(metrics_data)
```

### Update set_worker_ready

The existing `set_worker_ready` method already sets `ready_at`. Verify it looks like this:

```python
def set_worker_ready(self, worker_id: int) -> None:
    """Mark a worker as ready to receive tasks.

    Args:
        worker_id: Worker identifier
    """
    with self._lock:
        worker_data = self._state.get("workers", {}).get(str(worker_id), {})
        if worker_data:
            worker_data["status"] = WorkerStatus.READY.value
            worker_data["ready_at"] = datetime.now().isoformat()

    self.save()
    logger.debug(f"Worker {worker_id} marked ready")
```

### Update claim_task

Modify the existing `claim_task` method to also record claimed_at:

```python
def claim_task(self, task_id: str, worker_id: int) -> bool:
    """Attempt to claim a task for a worker.

    Uses file-based locking to prevent race conditions.

    Args:
        task_id: Task to claim
        worker_id: Worker claiming the task

    Returns:
        True if claim succeeded
    """
    with self._lock:
        # Reload state to get latest
        self.load()

        task_state = self._state.get("tasks", {}).get(task_id, {})
        current_status = task_state.get("status", TaskStatus.PENDING.value)

        # Can only claim pending tasks
        if current_status not in (TaskStatus.TODO.value, TaskStatus.PENDING.value):
            return False

        # Check if already claimed by a different worker
        existing_worker = task_state.get("worker_id")
        if existing_worker is not None and existing_worker != worker_id:
            return False

        # Claim it - now also records claimed_at
        self.set_task_status(task_id, TaskStatus.CLAIMED, worker_id=worker_id)
        self.record_task_claimed(task_id, worker_id)  # Add this line
        logger.info(f"Worker {worker_id} claimed task {task_id}")
        return True
```

### Update _create_initial_state

Add metrics to initial state:

```python
def _create_initial_state(self) -> dict[str, Any]:
    """Create initial state structure.

    Returns:
        Initial state dictionary
    """
    return {
        "feature": self.feature,
        "started_at": datetime.now().isoformat(),
        "current_level": 0,
        "tasks": {},
        "workers": {},
        "levels": {},
        "execution_log": [],
        "metrics": None,  # Add this line
        "paused": False,
        "error": None,
    }
```

## Verification

```bash
python -c "from zerg.state import StateManager; s = StateManager('test-metrics-check'); print('StateManager OK')"
```

## Acceptance Criteria
- [ ] record_task_claimed method sets claimed_at timestamp
- [ ] record_task_duration method sets duration_ms field
- [ ] store_metrics method persists FeatureMetrics to state
- [ ] get_metrics method returns FeatureMetrics or None
- [ ] claim_task now records claimed_at timestamp
- [ ] Initial state includes metrics field
- [ ] All methods are thread-safe (use self._lock)

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.