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.
Install
npx skillscat add mikeparcewski/wicked-garden/wicked-garden-qe-compliance-test-engineer Install via the SkillsCat registry.
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 ofsoc2,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 atlib/compliance/controls.json.
run_id— UUID of the currentrunsrow; definesEVIDENCE_DIR..wicked-qe/config.json—detected_toolingmust 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 asunsatisfied: 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:
{
"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
# 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"
doneInvariant 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 plusbandit; etc.). Unknown cmd -> ERR_CONTROL_TOOL_BLOCKED, no exec.
Worked examples per framework
# 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
// 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),
},
});
// 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_ifpredicate 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 incontrols:isout-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
satisfiedwithout a captured artefact.evidence_pathis 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
in Phase 6c). Resolve it (cross-platform):
WT_LIB="$(wicked-garden path scripts/qe/lib)"