# Metrics Collector

> >-

- **Type:** Skill
- **Install:** `agentstack add skill-knoxops-open-devops-skills-metrics-collector`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [KnoxOps](https://agentstack.voostack.com/s/knoxops)
- **Installs:** 0
- **Category:** [Cloud & Infrastructure](https://agentstack.voostack.com/c/cloud-infrastructure)
- **Latest version:** 0.1.0
- **License:** Apache-2.0
- **Upstream author:** [KnoxOps](https://github.com/KnoxOps)
- **Source:** https://github.com/KnoxOps/open-devops-skills/tree/master/plugins/ico/skills/metrics-collector
- **Website:** https://knoxops.app?invite_token=GITHUB26

## Install

```sh
agentstack add skill-knoxops-open-devops-skills-metrics-collector
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

## Input Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| run_dir | string | Yes | Working directory absolute path for input/output files |
| ssh_key_path | string | No | SSH key path for cloud tags queries and reachability checks |
| task_id | string | Yes | Task ID for progress tracking in scan_episodes.json |

## Execution Flow

### Task Context

Before starting execution, initialize `task_context.json`:

```json
{
  "task_id": "",
  "current_step": 0,
  "current_step_id": null,
  "status": "running",
  "steps": {
    "layer_a1_discovery": "pending",
    "layer_a2_exclusion": "pending",
    "layer_b1_metrics": "pending",
    "layer_b2_exclusion": "pending",
    "finalize_output": "pending",
    "review_signals": "pending"
  },
  "updated_at": ""
}
```

Update this file after each step completes. On error, set step status to `"failed"` and overall `status` to `"failed"`.

### Step 1: layer_a1_discovery

**Type:** inline
**Description:** Layer A.1: Discover all resources via cloud CLI and API tools per entity_type

## Execution
Follow these instructions:

Read {run_dir}/intent_detection.json and {run_dir}/connection_config.json.
Discover all cloud resources in scope. For each resource, extract:
resource_id, resource_name, entity_type, resource_type, environment,
tags, provisioned_at, private_ip, status, spec, cost.
For compute resources, also detect virt_type (lxc/kvm/bare-metal/unknown).
Estimate monthly cost from spec: vCPU × $20 + RAM_GB × $3 + disk_GB × $0.10.

Write {run_dir}/layer_a_raw_resources.json.
Update {run_dir}/scan_episodes.json with scan_status="phase1_layer_a1_completed".

### Progress Tracking

After completing this step, update `task_context.json`:
- Set `current_step_id` to `"layer_a1_discovery"`
- Set `steps.layer_a1_discovery` to `"completed"`
### Step 2: layer_a2_exclusion

**Type:** inline
**Description:** Layer A.2: Apply coarse exclusion rules to Layer A candidates

## Execution
Follow these instructions:

Apply coarse exclusion rules to filter out obviously non-candidate resources.

**Input**: {run_dir}/layer_a_raw_resources.json

**Exclusion rules** (deterministic):
1. New resources: provisioned_at within N days -> exclude, reason="new_resource".
   N depends on environment: dev=1 day, staging=3 days, prod=7 days.
   If environment is unknown, default to 7 days.
2. Non-ready status: status in [terminated, deleting, creating] -> exclude, reason="not_ready"
3. DR/backup tags: tags contain purpose=disaster-recovery or purpose=backup -> exclude, reason="dr_backup_tag"
4. Recent CI/CD deployment: if the workload has been deployed via the user's CI/CD tool within N days -> exclude, reason="active_cicd". N depends on environment: dev=7, staging=30, prod=90. If no CI/CD tool is configured, skip this rule. For K8s workloads without CI/CD access, check ReplicaSet creation timestamps as a proxy.

**LLM semantic check** (supplemental, for resources not already excluded):
- Check if resource name contains dr-, backup-, standby- prefix
- Check if tags contain role=standby, usage=backup
- If matched, mark as "potential_dr_backup" but still include in candidates

Write the output to the specified output file.

## Output
- **Schema:** schemas/layer-a-candidates.schema.json
  - **File:** layer_a_candidates.json

### Progress Tracking

After completing this step, update `task_context.json`:
- Set `current_step_id` to `"layer_a2_exclusion"`
- Set `steps.layer_a2_exclusion` to `"completed"`
### Step 3: layer_b1_metrics

**Type:** inline
**Description:** Layer B.1: Collect idle/zombie detection signals per resource type

## Execution
Follow these instructions:

Collect idle/zombie detection signals for each resource in {run_dir}/layer_a_candidates.json.

For signal definitions and per-resource-type thresholds, read prompts/signals-*.md.

Collect idle detection signals for each Layer A candidate based on its resource_type.

**Input**: {run_dir}/layer_a_candidates.json

**Signal rules by resource type** (read the reference file for detailed thresholds and edge cases):
| resource_type | Reference file |
|--------------|---------------|
| compute | `$PLUGINS/ico/skills/metrics-collector/prompts/signals-compute.md` |
| database, cache | `$PLUGINS/ico/skills/metrics-collector/prompts/signals-database.md` |
| storage | `$PLUGINS/ico/skills/metrics-collector/prompts/signals-storage.md` |
| network | `$PLUGINS/ico/skills/metrics-collector/prompts/signals-network.md` |
| k8s_workload | `$PLUGINS/ico/skills/metrics-collector/prompts/signals-k8s-workload.md` |
| k8s_service, k8s_orphan | `$PLUGINS/ico/skills/metrics-collector/prompts/signals-k8s-service.md` |
| object_storage | `$PLUGINS/ico/skills/metrics-collector/prompts/signals-object-storage.md` |
| domain | no signals available; mark unmeasurable, skip to manual review |

**CRITICAL — Threshold source**: All signal thresholds, units, and collection methods come ONLY from the `$PLUGINS/ico/skills/metrics-collector/prompts/signals-{type}.md` reference files. DO NOT use thresholds from scan_plan.json, intent_detection.json, or any other file. DO NOT add extra signals (memory, disk_io, connections) beyond what the reference file defines. Collect ONLY the signals listed in the table above.

**Execution**:
1. For each candidate, determine resource_type
2. Read the corresponding reference file for signal definitions, thresholds, and data sources
3. Collect signals using available cloud CLI tools (cloud monitoring API preferred over in-VM /proc)
4. For each signal, record: value, threshold, threshold_unit, is_active (value exceeds threshold → true), reliability, data_source
5. Downstream consumers read `is_active` and `threshold` directly — do NOT make them guess or derive from signal values

**Rules**:
- Data collection fails → reliability=0.0, data_source="failed"
- ALL signals fail → "unmeasurable", do NOT classify as zombie
- Batch timeout → retry once; still failing → reliability=0.0 for that signal

Write the output to the specified output file.

## Output
- **Schema:** schemas/layer-b-candidates.schema.json
  - **File:** layer_b_raw_signals.json

### Progress Tracking

After completing this step, update `task_context.json`:
- Set `current_step_id` to `"layer_b1_metrics"`
- Set `steps.layer_b1_metrics` to `"completed"`
### Step 4: layer_b2_exclusion

**Type:** inline
**Description:** Layer B.2: Apply per-type signal rules to identify zombie candidates

## Execution
Follow these instructions:

Apply idle detection rules per resource_type to determine zombie candidates.

**Input**: {run_dir}/layer_b_raw_signals.json

**The Rule** (same for all resource types):
- ANY signal exceeds its threshold (defined in prompts/signals-{type}.md) → ACTIVE → exclude
- ALL signals below threshold → CANDIDATE (potential zombie)
- ALL signals reliability=0.0 → UNMEASURABLE → separate array, do NOT classify

**Edge cases**:
- Signal reliability=0.0 for some but not all signals: treat failed ones as "below threshold". Mark low_confidence_signals=true
- Borderline values: check threshold range in the reference file. Mark low_confidence_signals=true
- resource_type with no reference file (e.g. domain): mark unmeasurable, skip

Write the output to the specified output file.

## Output
- **Schema:** schemas/layer-b-candidates.schema.json
  - **File:** layer_b_candidates.json

### Progress Tracking

After completing this step, update `task_context.json`:
- Set `current_step_id` to `"layer_b2_exclusion"`
- Set `steps.layer_b2_exclusion` to `"completed"`
### Step 5: finalize_output

**Type:** inline
**Description:** Finalize output, collect candidate context info, and update progress

## Execution
Follow these instructions:

Finalize output, collect candidate context, and update tracking.

**Verification**: Verify {run_dir}/layer_a_candidates.json and {run_dir}/layer_b_candidates.json exist and are valid JSON.

**Candidate context**: For each candidate, collect and append as `context_info` block:
1. Owner: from resource tags / cloud audit logs / IaC git blame / cloud tags
2. Blast radius: what depends on this resource, what would break if deleted
3. Reachability: for compute, check SSH/API access and record reachable status

**Update**: Set scan_episodes.json → scan_status: "phase1_discovery_complete", completed_at.

**Log summary**: Layer A/B counts, filter rates, excluded_reasons, tool failures, skipped types.

Edge case: 0 candidates → scan_status "phase1_discovery_complete_zero_candidates" or "_all_active".

### Progress Tracking

After completing this step, update `task_context.json`:
- Set `current_step_id` to `"finalize_output"`
- Set `steps.finalize_output` to `"completed"`
### Step 6: review_signals

**Type:** inline
**Description:** Validate Phase A output before passing to screener (BLOCKING)

## Execution
Follow these instructions:

Read these output files and validate them before completing this skill:

layer_b_raw_signals.json — every signal MUST have: value, threshold, threshold_unit, is_active, reliability, data_source
layer_b_candidates.json — all required schema fields present: candidates[], total counts, filter_rate

If any field is missing or any value is a placeholder ("N/A", "NONE", "0.00 MB" without real data), mark that resource as unmeasurable.
Fix any issues before completing. BLOCKING.

### Progress Tracking

After completing this step, update `task_context.json`:
- Set `current_step_id` to `"review_signals"`
- Set `steps.review_signals` to `"completed"`

## Error Handling

### zero_candidates_after_layer_a

candidates_count == 0 after Layer A.2 → log warning, write empty candidates, skip Layer B, scan_status=phase1_discovery_complete_zero_candidates

### zero_candidates_after_layer_b

layer_b_candidates_count == 0 after Layer B.2 → normal for healthy infra, write empty candidates, scan_status=phase1_discovery_complete_all_active

### tool_unavailable

cloud CLI tool fails for one entity_type → skip it, record in skipped_entity_types[], continue. All fail → abort.

### single_signal_collection_failure

1-3 signals fail (at least 1 has real data) → reliability=0.0 + data_source=failed. During B.2 exclusion: treat as below threshold, mark low_confidence_signals=true.

### all_signals_failed

ALL signals placeholder or failed → UNMEASURABLE. Write to unmeasurable_hosts[], do NOT classify as zombie. > 30% unmeasurable → abort (collection method broken).

### batch_timeout_in_layer_b

Batch query timeout → retry once with smaller batch. Still failing → all affected resources reliability=0.0 for that signal, continue.

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [KnoxOps](https://github.com/KnoxOps)
- **Source:** [KnoxOps/open-devops-skills](https://github.com/KnoxOps/open-devops-skills)
- **License:** Apache-2.0
- **Homepage:** https://knoxops.app?invite_token=GITHUB26

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-knoxops-open-devops-skills-metrics-collector
- Seller: https://agentstack.voostack.com/s/knoxops
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
