PHI Desensitize
SkillCommunicationUse this skill before any prompt, log message, error trace, test fixture, or external API call that may include patient health information (PHI) or personal identifying information (PII) in a medical-data SaaS context. Replaces sensitive tokens with reversible synthetic substitutes, emits a desensitize map (encrypted), and produces a residual-risk report. Mandatory pre-stage before any L3/L4 data ever enters a model context window. Chinese trigger examples: "脱敏", "PHI 脱敏", "数据脱敏", "去标识化", "patient_id 替换", "把这段日志脱敏", "把样例匿名化". Do NOT use as a one-way redactor when the workflow needs round-trip mapping; do NOT use for fully synthetic data that contains no real source values (use test-data-generation instead). Success = every L3/L4 token in input is replaced, output passes mcp-phi-detector with zero hits, reversal map is encrypted with KMS-managed key, and residual-risk report is empty or explicitly approved.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the PHI Desensitize skill
What this skill tells your AI
The instructions your AI receives, as published by charliehzm/medharness in .claude/skills/phi-desensitize/SKILL.md and read by ahel’s review.
The single most important runtime gate in the entire AI Coding system: no L3/L4 token should ever enter a model context window without first passing through this skill.
Core mental model
Desensitization is not redaction. Redaction destroys information; desensitization replaces it with reversible placeholders so the downstream LLM can still reason about structure and relationships, while the operator can reverse the mapping on a vetted output in a controlled environment.
"Patient 张三 (ID 110101199001011234) seen 2026-03-12"
↓ desensitize
"Patient {{PT_A1}} (ID {{ID_B7}}) seen {{DATE_C3}}"
↓ LLM reasons, produces analysis referencing {{PT_A1}}
↓ controlled reversal in approved environment
"Patient 张三 (ID 110101199001011234) seen 2026-03-12 — diagnosis: ..."
What this skill produces
For each invocation:
desensitized_payload— sanitized text / JSON / fixture<source>.desensitize_map.json.enc— encrypted reversal map (AES-256-GCM, key from KMS)residual_risk_report.md— listing any tokens the classifier was uncertain about
When NOT to use this skill
Skip for:
- Already fully synthetic data (use
test-data-generationinstead) - Purely L1 / public content (no PHI possible)
- One-way logging where reversal is never needed (use simple redaction)
- Encryption / hashing at storage layer (that's data-at-rest concern, different domain)
Active context bundle
Always load first
- This
SKILL.md reference/hipaa-18-identifiers.md— the canonical PHI taxonomyreference/cn-personal-info-catalog.md— 公安部 / 健康医疗数据安全指南 PII categoriesreference/detection-rules.md— regex + ML classifier combination strategy
Load on demand
reference/free-text-strategy.md— for unstructured notes / discharge summariesreference/structured-data-strategy.md— for JSON / CSV / Parquet payloadsreference/reversal-protocol.md— when reversal is needed in approved environment
Detection strategy (dual-pass)
Pass 1 — Rule layer (deterministic, high recall)
- CN-ID regex:
\d{17}[\dXx]after Luhn-like check digit - CN-phone regex:
1[3-9]\d{9} - CN-name heuristic: 2-4 CJK chars in subject position of sentence
- Birth date:
\d{4}[-/年]\d{1,2}[-/月]\d{1,2}— combined with name proximity bumps tier - Medical record number: pattern depends on hospital; load org-specific patterns from
reference/org-mrn-patterns.md - 18 HIPAA identifiers: see reference
Pass 2 — Classifier layer (high precision)
- Local fine-tuned BERT or Qwen-1.8B running CPU
- Confirms / rejects Pass 1 candidates
- Catches free-text PHI missed by regex (e.g. "the man whose wife works at Tongji")
Pass 3 — Disagreement resolution
- If rule says PHI but classifier disagrees: still desensitize (false positive is cheap)
- If classifier says PHI but rule didn't: desensitize + log to
residual_risk_report(for rule library update)
Substitution scheme
Each detected token → {{TYPE_SHORTID}} where:
TYPE∈ {PT (patient), ID (identifier), DR (doctor), HS (hospital), DT (date), AD (address), PH (phone), MR (medical record), DX (diagnosis sometimes), TX (treatment sometimes)}SHORTIDis a 2-char base32 from a per-session HMAC of the original value, so the same input within a session gets a stable placeholder (preserves relationships)
The map stores {placeholder: original} and gets encrypted before being written to disk.
Reversal protocol (controlled)
Reversal is opt-in and audited:
- Reversal can only happen inside the project's
.controlled/directory (mounted read-only outside dev environment) - Reversal requires:
- Active developer session with Data Steward signoff
- The
.desensitize_map.json.encfrom the same change - KMS access (logged centrally)
- Reversed output is treated as L4 immediately upon reversal — no copying to other locations
Hard gate checklist
- Every L3/L4 token in the source has been replaced
- Output passes
mcp-phi-detectorwith zero hits (hooked at UserPromptSubmit, double-check) - Desensitize map encrypted with KMS-managed key (no plaintext map on disk)
- Residual risk report empty OR every entry has owner ack
- If used inside a Skill chain, the desensitized payload is what flows downstream — never the original
Common failure modes
- Half-desensitized fixtures: dev manually replaced names but forgot phone numbers. Mitigation: always run both passes, never trust manual cleanup.
- Map leakage: developer puts the unencrypted map in repo. Mitigation: hook
PreToolUseto denyWrite(*.desensitize_map.json)(no.encsuffix). - Cross-change collisions: same patient appears in two changes, placeholders differ — that's fine and intentional (no cross-change linkability). Don't try to "harmonize" across changes.
- Free-text re-identification: "the 38-year-old male diabetic seen in Shanghai United on 2026-03-12" — even with name removed, this is identifying. Detection must consider co-occurrence. The classifier layer is your defense; rule layer alone is insufficient.
- Date shifting confusion: for time-series analysis, simply masking dates breaks reasoning. Use date shifting: add a per-patient random offset that preserves intervals. Document the shift in the map.
Integration points
- At Hook layer:
UserPromptSubmitrunsmcp-phi-detectoras a tripwire; if it fires AFTER this skill, something is wrong (regression or new vector) - At MCP layer:
mcp-desensitizeexposes this skill's logic as a callable tool so non-conversation flows (CI, batch jobs, MCP-using sub-agents) can use it programmatically - At SOP layer: invoked before any prompt in Steps 5 (mock data), 6 (apply), 8 (review), 9 (mocking test) that touches L3/L4
Output handoff
Returns:
- Desensitized payload (use as-is downstream)
- Path to encrypted map (for controlled reversal later)
- Residual risk report (must be empty or signed off)
Signals
- GitHub stars
- 86
- Forks
- 8
- Last commit
- Jun 2026
Advanced
- Catalog kind
- skill
- Gateway key
phi-desensitize- Source
- github.com/charliehzm/medharness