# Workshop Air Pollution Monitoring

> Production-grade skill registry & harness for Industrial Workshop Indoor Air Quality & Safety — registers, resolves, executes, and validates skills/tools/hooks with typed JSON-Schema contracts, a chain-of-thought router, lifecycle hooks, and a self-improving knowledge pipeline. Trigger whenever a user wants workshop air-pollution monitoring, exposure-limit assessment, ventilation/LEV design, IAQ…

- **Type:** Skill
- **Install:** `agentstack add skill-dungnotnull-workshop-air-pollution-monitoring-agent-skill-workshop-air-pollution-monitoring-agent-skill`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [dungnotnull](https://agentstack.voostack.com/s/dungnotnull)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [dungnotnull](https://github.com/dungnotnull)
- **Source:** https://github.com/dungnotnull/workshop-air-pollution-monitoring-agent-skill

## Install

```sh
agentstack add skill-dungnotnull-workshop-air-pollution-monitoring-agent-skill-workshop-air-pollution-monitoring-agent-skill
```

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

## About

# SKILL.md — Skill Registry & Execution Contract

This document is the **canonical registry documentation** for the
`workshop-air-pollution-monitoring` skill. It explains how skills, tools, and
hooks are **registered, resolved, executed, and validated**, including the
input/output JSON-Schema contracts that bind every component.

It is read by:
* contributors who add or modify a skill/tool/hook;
* the runtime (`tools/skill_registry.py`, `tools/tool_registry.py`,
  `tools/hooks.py`) which loads and validates the registry;
* the orchestrator (`skills/main.md`) which dispatches sub-skills by name.

The machine-readable companion is `assets/registry.json`; every entry there
validates against `assets/schemas/registry-entry.schema.json`.

---

## 1. Architecture at a glance

```
USER QUERY
   │
   ▼
[pre_flight hooks]  language · context budget · registry validate
   │
   ▼
[skills/main.md]  orchestrator + quality gate
   │
   ▼
[skills/sub-router.md]  chain-of-thought → plan
   │
   ▼
[skills/sub-*.md]  intake → evidence → core-analysis → knowledge → advisor
   │   (each powered by tools/* via the tool registry, grounded by references/*)
   ▼
[pre_delivery hook]  enforce U1–U6 + G1–G4
   │
   ▼
[final report]  conforms to assets/schemas/report.schema.json
   │
   ▼
[post_delivery hook]  queue gaps · metrics · structured log
```

See `assets/diagrams/system-architecture.md` for the full diagram and
`assets/registry.json` for the manifest.

---

## 2. Skill registry

### 2.1 Registration

A **skill** is a Markdown file in `skills/` with YAML frontmatter and the
required sections. To register a new skill:

1. Create `skills/sub-.md` with frontmatter `name` + `description`.
2. Include the required sections: `Role & Persona`, `Workflow`, `Output Format`,
   `Quality Gates` (sub-skills) — and additionally `Sub-skills Available`,
   `Quality Gates` table, `Graceful Degradation`, `Output Format` (orchestrator).
3. Add an entry to `assets/registry.json` under `skills[]` with `type` = `sub_skill`
   (or `orchestrator` / `router`), the `path`, `phase`, `depends_on`, `inputs`,
   `outputs`, and `output_schema` (a URI to a schema in `assets/schemas/`).
4. If the skill emits structured data, author the schema in `assets/schemas/` and
   reference it from `output_schema`.
5. Re-run `python scripts/validate_project.py` — it asserts the file exists, the
   frontmatter is valid, the required sections are present, and the
   `registry.json` entry validates.

#### Frontmatter contract

```yaml
---
name: sub-            # required; matches filename stem
description:   # required
---
```

#### Required sections (sub-skill)

| Section | Required content |
|---------|------------------|
| `Role & Persona` | Who the sub-skill is and its discipline. |
| `Workflow` | Numbered steps: Receive Inputs → Execute Core Task → Emit Outputs. |
| `Tools` | Tool names from the tool registry (or "conversation only"). |
| `Output Format` | The exact block template the sub-skill emits. |
| `Quality Gates` | Checkbox list of pass criteria. |

The orchestrator (`main.md`) additionally requires `Harness Execution Protocol`,
`Quality Gates` (table), `Graceful Degradation & Error Handling`,
`Sub-skills Available`, `Tools`, `Output Format`.

### 2.2 Resolution

`tools/skill_registry.SkillRegistry.load()` scans `skills/`, parses frontmatter,
and builds an index keyed by `name`. Resolution by name is O(1):

```python
from tools.skill_registry import SkillRegistry
reg = SkillRegistry.load()
sub = reg.get("sub-core-analysis")   # -> SkillDefinition
```

The orchestrator resolves each sub-skill named in the router's plan through this
index; an unknown name is a hard error (the `pre_flight_registry_validate` hook
catches this at startup).

### 2.3 Execution

Sub-skills are dispatched by `SkillRegistry.invoke(name, inputs, context)`,
which:

1. Resolves the name to a `SkillDefinition`.
2. Runs the `pre_step` hook (input validation against the skill's declared
   inputs and the referenced input schema, if any).
3. Hands the prompt + inputs to the model (or, for offline runs, to the
   deterministic tool handlers).
4. Captures the structured output.
5. Runs the `post_step` hook — validates the output against `output_schema`
   (`tools/validate_schema`), and on failure either re-invokes (up to
   `skill_registry.max_retries_per_gate`) or raises a `ValidationError` that
   triggers the graceful-degradation protocol.

### 2.4 Validation

Three layers:

* **Static** — `scripts/validate_project.py` validates frontmatter, sections,
  and `registry.json` against `registry-entry.schema.json`.
* **Load-time** — `pre_flight_registry_validate` hook validates every skill at
  harness startup.
* **Runtime** — `post_step` hook validates each structured output against its
  JSON Schema (`assets/schemas/*.schema.json`).

---

## 3. Tool registry

### 3.1 Registration

A **tool** is a deterministic Python callable with a JSON input schema. Register
it by:

1. Implementing the handler in `tools/` (e.g. `tools/exposure_calc.py`).
2. Adding an entry to `assets/registry.json` under `tools[]` with `name`,
   `category`, `handler` (dotted path), optional `input_schema`/`output_schema`,
   `timeout_seconds`, `idempotent`, and `requires_feature_flag`.
3. The handler is auto-discovered by `tools/tool_registry.ToolRegistry.load()`.

### 3.2 Tool definition shape

```jsonc
// assets/schemas/tool.schema.json
{
  "name": "exposure_assess",
  "description": "Compute C_i/T_i ratios and the additive mixture index.",
  "category": "computation",
  "input_schema": { /* JSON Schema for the tool's args */ },
  "output_schema": { /* JSON Schema for the tool's result */ },
  "handler": "tools.exposure_calc.assess",
  "timeout_seconds": 10,
  "idempotent": true,
  "requires_feature_flag": "enable_mixture_formula"
}
```

### 3.3 Resolution & execution

```python
from tools.tool_registry import ToolRegistry
reg = ToolRegistry.load()
result = reg.invoke("exposure_assess", {
    "pollutants": [
        {"name": "CO", "concentration_ppm": 30, "limit_ppm": 50},
        {"name": "NO2", "concentration_ppm": 3, "limit_ppm": 5}
    ],
    "apply_mixture": True
})
```

`ToolRegistry.invoke`:
1. Resolves the handler by dotted path.
2. Checks `requires_feature_flag` against `config.FeatureFlags`; if disabled,
   returns a structured `{"disabled": true, "reason": ...}` result (never
   raises — graceful degradation).
3. Validates `inputs` against `input_schema` (when `enforce_schema` is on).
4. Calls the handler with a timeout; catches all exceptions and returns a
   structured error envelope `{"error": ..., "type": ...}` so the orchestrator
   can route to a fallback instead of crashing.

### 3.4 Built-in tools

| Tool | Handler | Category | Schema |
|------|---------|----------|--------|
| `web_search` | `tools.tool_registry.web_search` | retrieval | tool schema |
| `web_fetch` | `tools.tool_registry.web_fetch` | retrieval | tool schema |
| `knowledge_query` | `tools.tool_registry.knowledge_query` | knowledge | — |
| `knowledge_append` | `tools.tool_registry.knowledge_append` | knowledge | — |
| `exposure_assess` | `tools.exposure_calc.assess` | computation | core-analysis |
| `ventilation_design` | `tools.ventilation_calc.design` | computation | core-analysis |
| `context_budget` | `tools.context_manager.ContextManager` | orchestration | — |
| `skill_invoke` | `tools.skill_registry.SkillRegistry.invoke` | orchestration | — |

---

## 4. Hooks

### 4.1 Lifecycle phases

| Phase | Fires | Typical use |
|-------|-------|-------------|
| `pre_flight` | once, before Step 1 | language detect, context budget, registry validate |
| `pre_step` | before each sub-skill | input validation, token-budget check |
| `post_step` | after each sub-skill | output schema validation, gate check, metrics |
| `pre_delivery` | before final report | enforce U1–U6 + G1–G4, auto-fix |
| `post_delivery` | after final report | queue knowledge gaps, log, metrics |

### 4.2 Registration

Add an entry to `assets/registry.json` under `hooks[]`:

```jsonc
{
  "name": "pre_flight_language",
  "phase": "pre_flight",
  "handler": "tools.hooks.pre_flight_language",
  "priority": 10,
  "fail_closed": false,
  "requires_feature_flag": "enable_language_detection"
}
```

Hooks within a phase run in ascending `priority` order. If `fail_closed` is true
and the handler raises, the harness halts; otherwise it logs and continues.

### 4.3 Execution contract

Every hook handler has the signature
`handler(context: dict, config: AppConfig) -> dict` and returns a possibly-mutated
`context` (plus optional `metrics`). See `hooks/README.md` and `tools/hooks.py`.

---

## 5. Input / Output JSON-Schema contracts

Every structured hand-off in the pipeline is bound to a JSON Schema
(Draft 2020-12) in `assets/schemas/`. Validation is performed by
`tools/validate_schema.validate()` (a stdlib-only validator; `jsonschema` is
used when available for full Draft 2020-12 compliance).

| Artifact | Schema | Emitted by |
|----------|--------|------------|
| Requirements object | `requirements.schema.json` | `sub-gather-requirements` |
| Evidence bundle | `evidence-bundle.schema.json` | `sub-evidence-collector` |
| Core analysis scorecard | `core-analysis.schema.json` | `sub-core-analysis` |
| Knowledge evidence | `knowledge-evidence.schema.json` | `sub-knowledge-updater` |
| Advisor conclusion | `advisor-conclusion.schema.json` | `sub-advisor` |
| Final report | `report.schema.json` | orchestrator |
| Skill definition | `skill.schema.json` | `skills/*.md` (parsed) |
| Tool definition | `tool.schema.json` | `assets/registry.json` `tools[]` |
| Hook definition | `hook.schema.json` | `assets/registry.json` `hooks[]` |
| Registry entry | `registry-entry.schema.json` | `assets/registry.json` |

### Example — core-analysis output (excerpt)

```json
{
  "pollutants": [{"name": "Welding fume", "sources": ["MIG welding on mild steel"]}],
  "exposure_assessment": [
    {"pollutant": "CO", "concentration": 30, "unit": "ppm",
     "limit_type": "OSHA PEL", "limit_value": 50, "ratio": 0.6, "status": "conditional"}
  ],
  "mixture_index": 0.6,
  "ventilation_lev": {"type": "LEV + dilution", "capture_velocity_m_s": 0.6,
                      "air_changes_per_hour": 6, "source": "ACGIH IV manual"},
  "sensors_alerts": [{"parameter": "CO", "threshold": 25, "unit": "ppm",
                      "action": "warn-investigate-increase-LEV"}],
  "control_hierarchy": [{"level": "engineering_control", "measure": "Portable LEV hood at weld point"}],
  "scenarios": {"best": "...", "base": "...", "worst": "..."}
}
```

---

## 6. Context window & token management

`tools/context_manager.ContextManager` enforces the budgets in
`config.context`:

* **soft_budget_tokens** / **hard_budget_tokens** — never exceed the hard budget.
* **reserve_tokens_for_output** — always kept free for the final report.
* **compaction_threshold_tokens** — when running usage crosses this, compact the
  evidence bundle and reference chunks (keep highest-tier items, drop low-tier
  news) and log the compaction.
* Per-section caps: `evidence_bundle_max_items`, `knowledge_citations_max`,
  `reference_chunk_max_chars`.

Every sub-skill checks `context.budget` before fetching; the router downgrades a
`comparison` plan to `standard` if the remaining budget cannot afford two
core-analysis passes.

---

## 7. Error handling & graceful fallback

* **Tools** return structured error envelopes (never raise to the orchestrator).
* **Sub-skills** that fail schema validation are re-invoked up to
  `max_retries_per_gate`, then degraded.
* **LLM calls** fall back to `llm.fallback_model` after `llm.max_retries`.
* **Sources** follow the fallback chain live → secondary → knowledge base →
  `DATA UNAVAILABLE`, raising `degradation_level`.
* **Hooks** fail-closed or fail-open per their `fail_closed` flag.
* The orchestrator **never fabricates** a missing value; it emits
  `DATA UNAVAILABLE` and, if the missing input is decisive, routes the verdict
  to `Inconclusive`.

---

## 8. Self-improving knowledge pipeline

`tools/knowledge_updater.py` (driven by `config.knowledge`) crawls ArXiv,
Semantic Scholar, and RSS feeds, dedups by SHA-256 of DOI/URL, scores by
recency + keyword relevance + citation count, and appends to
`SECOND-KNOWLEDGE-BRAIN.md` Section 7. Schedule (documented in `CLAUDE.md`):
weekly academic (Mondays 08:00) + daily news (07:00). Gaps flagged by
`sub-knowledge-updater` become the next crawl's queries.

---

## 9. Validation & testing

| Command | What it checks |
|---------|----------------|
| `python scripts/validate_project.py` | 8-File Contract + new modular dirs, registry.json vs schema, all skill frontmatter/sections, schemas are valid JSON Schema. |
| `python scripts/run_pipeline.py --dry-run` | End-to-end harness dry run with the typed tools + hooks (no network). |
| `python tools/test_knowledge_updater.py` | Hash dedup, scoring, formatting. |
| `python tools/run_test_scenarios.py` | Structural & content validator + scenario gate coverage. |
| `python -m pytest tools/tests/` | Unit tests for exposure/ventilation/config/schema tooling. |

---

## 10. Contributing checklist

- [ ] New skill → `skills/sub-*.md` + `assets/registry.json` entry + schema.
- [ ] New tool → handler in `tools/` + `assets/registry.json` entry + input/output schema.
- [ ] New hook → handler in `tools/hooks.py` + `assets/registry.json` entry.
- [ ] `python scripts/validate_project.py` passes.
- [ ] `python tools/run_test_scenarios.py` passes.
- [ ] No placeholders, TODOs, stubs, or empty handlers.
- [ ] Structured logging via `config.get_logger`; no bare `print` in library code.

## Source & license

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

- **Author:** [dungnotnull](https://github.com/dungnotnull)
- **Source:** [dungnotnull/workshop-air-pollution-monitoring-agent-skill](https://github.com/dungnotnull/workshop-air-pollution-monitoring-agent-skill)
- **License:** MIT

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-dungnotnull-workshop-air-pollution-monitoring-agent-skill-workshop-air-pollution-monitoring-agent-skill
- Seller: https://agentstack.voostack.com/s/dungnotnull
- 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%.
