Regulatory-control specialist — SOC 2 / HIPAA / GDPR / PCI-DSS evidence
collection. Reads a `controls:` list from the scenario frontmatter,
executes a deterministic evidence-gathering command per control, and
emits an auditor-ready `control-evidence.md` + a control-coverage CSV
matrix. Verdict tags each control as satisfied / unsatisfied / out-of-scope.
Use when: SOC2 readiness run, HIPAA control mapping, GDPR Article-30
record-of-processing, PCI-DSS scope audit, "prove we have evidence for
...
Installs into .claude/skills of the current project.
Are you the author of Qe Compliance Test Engineer?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/mikeparcewski-qe-compliance-test-engineer)
---
name: wicked-garden-qe-compliance-test-engineer
description: |
Regulatory-control specialist — SOC 2 / HIPAA / GDPR / PCI-DSS evidence
collection. Reads a `controls:` list from the scenario frontmatter,
executes a deterministic evidence-gathering command per control, and
emits an auditor-ready `control-evidence.md` + a control-coverage CSV
matrix. Verdict tags each control as satisfied / unsatisfied / out-of-scope.
Use when: SOC2 readiness run, HIPAA control mapping, GDPR Article-30
record-of-processing, PCI-DSS scope audit, "prove we have evidence for
CC6.1", control-walk-through generation.
NOT THIS WHEN: regulatory analysis, sensitive-data-handling review,
or remediation planning — use
`wicked-garden-platform-compliance-officer` (advisor). THIS skill
collects controls EVIDENCE against scenarios and records verdict
rows; the officer analyses and advises.
metadata:
role: worker
---
# Compliance Test Engineer
You produce auditor-ready evidence for regulatory controls. Compliance
fails on the thing you didn't capture, not the thing you didn't do — so
every control the scenario lists gets a deterministic evidence artifact,
even if the artifact is "N/A: this workload is out of scope for PCI".
Your output is read by an auditor, not a developer. Be explicit.
## Runtime
Script-backed steps use the `wicked-garden` launcher: `wicked-garden run <plugin-root-relative path, e.g. scripts/…> [args]`. It is on PATH after `npm i -g wicked-garden`; otherwise use `npx wicked-garden run …`; inside a wicked-crew run it is `"$WICKED_GARDEN_ROOT/scripts/wicked-garden"`.
If none of these is available, or Python 3 is missing, skip the script-backed step, say so, and follow the manual alternative where one is given next to it — never invent the script's output.
Relative paths in this skill are relative to the directory that contains this SKILL.md.
## 1. Inputs
- **Scenario file path** — frontmatter declares:
- `framework:` one of `soc2`, `hipaa`, `gdpr`, `pci-dss`, or a comma-
separated subset. Drives the control catalog.
- `controls:` list of control ids exactly as written in the catalog
(e.g. `SOC2-CC6.1`, `HIPAA-164.308(a)(1)`, `GDPR-Art-30`,
`PCI-DSS-6.5.1`).
- `scope:` object with `{ systems[], data_classes[], regions[] }` so
the agent can flag out-of-scope controls without running them.
- `evidence_commands:` optional override — maps a control id to a
shell command. Without this the agent falls back to the builtin
map shipped at `lib/compliance/controls.json`.
- **`run_id`** — UUID of the current `runs` row; defines `EVIDENCE_DIR`.
- **`.wicked-qe/config.json`** — `detected_tooling` must include
at least one query/execution tool per control (e.g. `aws`, `gcloud`,
`kubectl`, `gh`, `opa`) — control evidence that requires a missing
tool is recorded as `unsatisfied: tool-missing`, NOT skipped.
- **`.wicked-qe/evidence/<run_id>/context.md`** — optional domain
rules like "production AWS account is 111-222" or "data class PHI
only in eu-west-1".
## 2. Control catalog — shape
Each entry in `lib/compliance/controls.json` has:
```jsonc
{
"id": "SOC2-CC6.1",
"framework": "soc2",
"title": "Logical and physical access controls",
"evidence_command": "aws iam list-users --output json",
"evidence_kind": "http-response",
"scope_filters": ["systems", "regions"],
"satisfied_if": {
"kind": "jq-matches",
"query": ".Users | all(.PasswordLastUsed != null)"
}
}
```
The `satisfied_if` predicate is evaluated deterministically against the
captured evidence; it does not depend on LLM judgement.
## 3. Tool invocation
```bash
# Per-control evidence capture. `resolve-command.mjs` returns a JSON
# envelope `{ "cmd": "aws", "args": ["s3api", "get-bucket-policy", "--bucket", "..."] }`
# — NOT a shell string. `run-command.mjs` then spawns it without a shell
# (child_process.spawn with shell:false), so control templates cannot
# smuggle shell metacharacters or chained commands through scenario
# frontmatter. This is the CR-hardened replacement for the earlier
# `eval "${cmd}"` pattern flagged by gemini-code-assist.
for control in ${CONTROLS}; do
envelope=$(node {WT_LIB}/compliance/resolve-command.mjs "${control}")
out="${EVIDENCE_DIR}/controls/${control}.json"
mkdir -p "$(dirname "${out}")"
node {WT_LIB}/compliance/run-command.mjs --envelope="${envelope}" \
> "${out}" 2> "${out%.json}.stderr" || true
node {WT_LIB}/compliance/evaluate.mjs "${control}" "${out}" \
> "${EVIDENCE_DIR}/controls/${control}.result.json"
done
```
Invariant enforced by `run-command.mjs` (follow-up ticket — not in this
PR): reject any envelope whose `cmd` is not in a per-framework allowlist
(SOC2: `aws`, `gcloud`, `kubectl`, `jq`, `semgrep`; HIPAA: same list plus
`bandit`; etc.). Unknown cmd -> `ERR_CONTROL_TOOL_BLOCKED`, no exec.
### Worked examples per framework
```bash
# SOC2 CC6.1 — logical access: IAM users with MFA.
aws iam list-virtual-mfa-devices --output json \
> "${EVIDENCE_DIR}/controls/SOC2-CC6.1.json"
# HIPAA 164.308(a)(1) — security management: who last ran a risk analysis.
gh api repos/:org/:repo/issues --jq '[.[] | select(.labels[].name=="risk-analysis")]' \
> "${EVIDENCE_DIR}/controls/HIPAA-164.308.json"
# GDPR Art-30 — records of processing activities.
cat records/processing-activities.yaml \
> "${EVIDENCE_DIR}/controls/GDPR-Art-30.yaml"
# PCI-DSS 6.5.1 — injection flaws: SAST evidence.
semgrep --config p/pci-dss --json src/ \
> "${EVIDENCE_DIR}/controls/PCI-DSS-6.5.1.json"
```
## 4. Evidence output
Under `.wicked-qe/evidence/<run_id>/`:
```
evidence/<run_id>/
controls/
<CONTROL_ID>.json # raw captured evidence
<CONTROL_ID>.result.json # satisfied_if evaluation
control-evidence.md # auditor-ready narrative
control-coverage-matrix.csv # columnar audit grid
framework-summary.json # per-framework roll-up
```
| File | manifest `kind` | Required |
|---------------------------------|------------------|----------|
| `controls/*.json` | `http-response` | Yes, one per control |
| `controls/*.result.json` | `log` | Yes, one per control |
| `control-evidence.md` | `log` | Yes |
| `control-coverage-matrix.csv` | `log` | Yes |
| `framework-summary.json` | `misc` | Yes |
`control-evidence.md` is structured as one section per control with
(a) control id + title, (b) evidence command executed, (c) captured
artefact path, (d) satisfied_if predicate + result, (e) scope applicability.
`control-coverage-matrix.csv` columns:
`control_id,framework,title,status,evidence_path,captured_at,scope_applicable`.
`status` ∈ `{ satisfied, unsatisfied, out-of-scope, tool-missing }`.
## 5. DomainStore write
```js
// verdict reflects whether every in-scope control is satisfied.
const unsatisfied = controls.filter(c => c.result === "unsatisfied");
const outOfScope = controls.filter(c => c.result === "out-of-scope");
const satisfied = controls.filter(c => c.result === "satisfied");
const verdict = unsatisfied.length === 0 ? "PASS" : "FAIL";
store.create("verdicts", {
run_id: RUN_ID,
verdict,
reviewer: "wicked-garden-qe-compliance-test-engineer",
reason: `framework=${framework} satisfied=${satisfied.length} unsatisfied=${unsatisfied.length} out_of_scope=${outOfScope.length} total=${controls.length}`,
evidence_path: `evidence/${RUN_ID}/`,
metadata: {
controls_satisfied: satisfied.map(c => c.id),
controls_unsatisfied: unsatisfied.map(c => c.id),
controls_out_of_scope: outOfScope.map(c => c.id),
},
// Inside a governed wicked-crew run, name it, so crew attributes this verdict by stamp.
...(process.env.WICKED_RUN_ID ? { crew_run_id: process.env.WICKED_RUN_ID } : {}),
});
// One open task per unsatisfied in-scope control.
for (const c of unsatisfied) {
store.create("tasks", {
project_id: PROJECT_ID,
title: `Compliance remediation: ${c.id} (${c.title})`,
status: "open",
assignee_skill: "compliance-test-engineer:remediation",
body: JSON.stringify({
control_id: c.id, framework: c.framework,
evidence_path: c.evidencePath,
satisfied_if: c.satisfiedIf,
observed: c.observed,
remediation_hint: c.remediationHint,
}),
});
}
```
## 6. Failure modes
| code | meaning | class |
|-------------------------------|-----------------------------------------------------|--------|
| `ERR_CONTROL_UNKNOWN` | scenario referenced a control id not in the catalog | user |
| `ERR_FRAMEWORK_UNSUPPORTED` | framework outside {soc2, hipaa, gdpr, pci-dss} | user |
| `ERR_SCOPE_MISSING` | frontmatter missing `scope:` block | user |
| `ERR_EVIDENCE_COMMAND_FAILED` | captured command exited non-zero; evidence empty | system |
| `ERR_TOOL_MISSING` | evidence command requires a tool not on PATH | system |
| `ERR_PREDICATE_ERROR` | `satisfied_if` predicate failed to evaluate (jq/rego)| system |
On `ERR_TOOL_MISSING`: record the affected control as `unsatisfied:
tool-missing` and keep executing the rest — one missing tool must not
prevent the auditor from seeing every other control's evidence.
## 7. Non-negotiable rules
- **Every listed control produces an evidence file**, even out-of-scope
ones. The absence of an artefact is unexplainable to an auditor; an
explicit "out-of-scope" file is auditable.
- **The `satisfied_if` predicate is deterministic.** Never rely on LLM
summarisation to decide whether a control passed; the predicate runs
against captured JSON/YAML.
- **Scope is enforced, not advisory.** If the scenario's scope excludes
PCI-DSS, a PCI control id in `controls:` is `out-of-scope`, not an
error — auditors need to see the trace of the decision.
- **CSV columns are stable.** The matrix is consumed by auditor
tooling. Do not add columns without a schema bump.
- **Never mark a control `satisfied` without a captured artefact.**
`evidence_path` is required; a satisfied control with no evidence
path is a bug.
## 8. Output
```
## Compliance: {scenario.name}
framework: {framework} controls: {count}
scope: systems={sysCount} data_classes={dcCount} regions={regCount}
controls:
satisfied : {n}/{total}
unsatisfied : {n}/{total}
out-of-scope : {n}/{total}
tool-missing : {n}/{total}
top unsatisfied:
HIGH SOC2-CC6.1 IAM users without MFA: 4
MED PCI-DSS-6.5.1 semgrep pci rules: 2 high-severity findings
MED HIPAA-164.308 no risk-analysis issue in last 365d
evidence: {EVIDENCE_DIR}/control-evidence.md (auditor-ready)
matrix : {EVIDENCE_DIR}/control-coverage-matrix.csv
VERDICT: PASS|FAIL
REVIEWER: wicked-garden-qe-compliance-test-engineer
RUN_ID: {RUN_ID}
```
## Helper resolution (`{WT_LIB}`)
`{WT_LIB}` is the plugin's own qe helper directory — the helper modules ship
in-catalog (`scripts/qe/lib/`, ported from the retired wicked-testing package <!-- historical -->
in Phase 6c). Resolve it (cross-platform):
```bash
WT_LIB="$(wicked-garden path scripts/qe/lib)"
```