From agent-almanac
Redacts sensitive identifiers from Mermaid diagrams, SVGs, and HTML dashboards using a mapping table. Used to publish investigation visualizations publicly without leaking internal names.
How this skill is triggered — by the user, by Claude, or both
Slash command
/agent-almanac:redact-visualization-for-disclosureThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
A diagram leaks differently than prose: the sensitive names sit in node labels, tooltip text, and embedded `<text>` nodes, while the *shape* of the graph is exactly the generalizable insight worth publishing. This skill redacts the labels and keeps the structure — replacing each internal identifier with a descriptive stand-in through an ordered mapping table, re-rendering the image from the red...
A diagram leaks differently than prose: the sensitive names sit in node labels, tooltip text, and embedded <text> nodes, while the shape of the graph is exactly the generalizable insight worth publishing. This skill redacts the labels and keeps the structure — replacing each internal identifier with a descriptive stand-in through an ordered mapping table, re-rendering the image from the redacted source, and then verifying the output through enforce-redaction-gate so nothing slips through.
.mmd, rendered .svg) built from internal findings.mmd, .svg, .html) in the private repoenforce-redaction-gate Step 1)Decide, per artifact, what carries the leak and what carries the insight. In every diagram format the structure (which node connects to which) is the shareable pattern; the labels are the sensitive part.
| Format | Sensitive (redact) | Structural (preserve) |
|---|---|---|
| Mermaid | node labels, edge labels, subgraph titles | node ids, edge directions, layout |
| SVG | <text>, <title>, id/data-* attrs | path geometry, viewBox, styles |
| HTML | cell text, tooltips, captions | table shape, headings, layout |
The rule that prevents the most damage: redact labels, preserve ids. A Mermaid A1[acme_widget_gate] becomes A1[widget-gate] — the id A1 is structural and carries nothing; the label is the leak.
Expected: A per-format list of which token positions get rewritten and which are left untouched.
On failure: If a node id itself encodes a sensitive name (rare), treat it as a label and remap it consistently across every edge that references it.
Map each sensitive identifier to a descriptive stand-in. Order matters: longest/most-specific first, so a precise label wins before a namespace label before the catch-all. Otherwise a generic prefix rewrite eats the specific case.
# Ordered tiers. Within each dict, apply longest-key-first.
SPECIFIC = { # exact internal name -> descriptive stand-in
"acme_widget_autoinstall_gate": "[autoinstall-gate]",
"acme_relay_chain_v1": "[relay-chain-gate]",
}
NAMESPACE = { # family prefix -> namespace label
"acme_widget_": "[widget-namespace]",
}
FUNCTIONS = { # minified identifier (whole-word) -> role name
r"\bQ3\(\)": "resolveProvider()",
r"\bAb\b": "flagResolver",
}
def redact(text):
for src in sorted(SPECIFIC, key=len, reverse=True): text = text.replace(src, SPECIFIC[src])
for src in sorted(NAMESPACE, key=len, reverse=True): text = text.replace(src, NAMESPACE[src])
for pat, repl in FUNCTIONS.items(): text = re.sub(pat, repl, text)
text = re.sub(r"acme_[A-Za-z0-9_]+", "[feature-flag]", text) # catch-all, last
text = re.sub(r"\b(\w+(?:\(\))?) \1\b", r"\1", text) # collapse "name name" pairs
return text
Two non-obvious rules baked in above: whole-word boundaries for minified identifiers (so Ab does not match inside Abstract), and a collapse pass that removes adjacent duplicate stand-ins left when an original label paired a name with its minified twin (scheduleWakeup Y8z → both map to scheduleWakeup → collapse to one).
Expected: Every sensitive identifier in the source maps to a descriptive stand-in; the catch-all only ever fires on tokens the specific/namespace tiers missed.
On failure: If the catch-all fires often, a real identifier family is unmapped — add a specific or namespace entry rather than relying on the generic placeholder, which erases teaching value.
Run the mapping over the source text and write the redacted artifact to the public-mirror path. Never edit the public copy by hand — it must be a pure function of the private source so re-runs are deterministic.
python3 tools/redact-visualization.py docs/flow.mmd publish/docs/flow.mmd
For SVG/HTML, apply the same mapping but scope replacements to text-bearing nodes (parse the DOM rather than regexing the whole file) so you do not rewrite an attribute that happens to contain a matching substring.
Expected: The redacted artifact has identical structure to the source and descriptive stand-ins in every label position.
On failure: If a diff shows a structural change (a dropped edge, a renamed id), the mapping over-matched — scope it to label positions and re-run.
If the artifact has a rendered form (an .svg/.png generated from the .mmd), regenerate it from the redacted source. A stale rendered image is the classic leak: the source got redacted, the image did not.
mmdc -i publish/docs/flow.mmd -o publish/docs/flow.svg
Expected: The rendered image is regenerated from the redacted source and shows only stand-in labels.
On failure: If the render tool is unavailable, do not ship the old image — remove it from the publish set until it can be regenerated, and note the gap.
Run enforce-redaction-gate over the redacted artifact and its rendered image. The transform is not done until the gate exits 0 — including the structure-aware tier, which catches a sensitive token hiding in an SVG <text> node that a label-position rewrite missed.
bash tools/enforce-redaction-gate.sh publish/docs/ || {
echo "visualization still leaks; extend the mapping table"; exit 1; }
Expected: The gate exits 0 on both the source and the rendered image.
On failure: A gate hit means the mapping table is incomplete — add the missing shape to the mapping (Step 2) and the deny-list, then re-run from Step 3. Do not hand-edit the public file to silence the gate.
enforce-redaction-gate exits 0 on both the source and the rendered image.mmd but shipping the old .svg publishes everything you just removed. Always regenerate.[feature-flag] strips the teaching value that justified publishing the diagram. Map families to descriptive namespaces.enforce-redaction-gate — the verification step every redaction transform ends on; also defines the shape deny-list the mapping table derives fromredact-for-public-disclosure — the methodology umbrella that decides what is publishable before this skill decides how to scrub the diagramredact-wire-capture — the sibling transform for network/MITM captures rather than rendered artifactsgenerate-workflow-diagram — produces the kind of Mermaid diagram this skill redacts for publicationannotate-source-files — upstream annotation step whose output diagrams may carry internal identifiers into the visualnpx claudepluginhub pjt222/agent-almanacGenerates GitHub-renderable Mermaid flowcharts from PRDs, docs, or codebases with evidence maps tracing nodes to sources. Useful for creating user flow diagrams.
Create SVG-based technical diagrams inside HTML — flowcharts, sequence diagrams, state machines, data-flow diagrams, dependency graphs, request/response timelines. Use whenever the user wants to visualize, illustrate, diagram, or sketch a technical concept, system, or process. Strongly prefer SVG over ASCII art, mermaid blocks, or markdown text for anything spatial or relational. Reach for this whenever an explanation involves arrows, boxes, layers, or sequencing — even when the user doesn't say "diagram".
Enables data-driven conditional formatting, live data binding, and dynamic diagram updates in draw.io via custom XML object properties, %placeholders%, and file-level variables. For status dashboards with metrics like status, latency, and uptime.