legal-sanity-scan

Mandatory legal compliance gate for document intelligence, data catalogs, and code porting — scans for client names, proprietary references, and sensitive paths

5 stars

Best use case

legal-sanity-scan is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Mandatory legal compliance gate for document intelligence, data catalogs, and code porting — scans for client names, proprietary references, and sensitive paths

Teams using legal-sanity-scan should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/legal-sanity-scan/SKILL.md --create-dirs "https://raw.githubusercontent.com/vamseeachanta/workspace-hub/main/.agents/skills/coordination/legal-sanity-scan/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/legal-sanity-scan/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How legal-sanity-scan Compares

Feature / Agentlegal-sanity-scanStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Mandatory legal compliance gate for document intelligence, data catalogs, and code porting — scans for client names, proprietary references, and sensitive paths

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Legal Sanity Scan

> MANDATORY for all document-intelligence, catalog, and resource-intelligence work.
> Run BEFORE committing any file that references mount-drive content, client projects,
> OrcaFlex models, conference papers, or standards extracts.

## When to Run (triggers)

1. **Document intelligence pipeline** — Phases D, E already call it; run manually after A-C and F-G
2. **New data catalogs** — conference-paper-catalog, dde-*-catalog, dde-*-inventory, any *-registry.yaml
3. **Resource intelligence work** — mount-drive knowledge maps, cross-drive audits, literature catalogs
4. **Code porting** — any code from client projects, OrcaFlex models, Excel dark-intelligence
5. **Before every commit** that touches `data/document-index/`, `data/doc-intelligence/`, or `data/standards/`
6. **Before PR creation** — mandatory pre-gate

## Quick Commands

```bash
# Full workspace scan
bash scripts/legal/legal-sanity-scan.sh

# Scan only changed files (fast, use before commits)
bash scripts/legal/legal-sanity-scan.sh --diff-only

# Scan specific submodule
bash scripts/legal/legal-sanity-scan.sh --repo=digitalmodel

# Scan all submodules
bash scripts/legal/legal-sanity-scan.sh --all

# JSON output for automation
bash scripts/legal/legal-sanity-scan.sh --json
```

## What Gets Scanned

The scanner checks ALL text files against patterns in `.legal-deny-list.yaml`:

### Pattern Categories
| Category | Examples | Severity |
|----------|----------|----------|
| `client_references` | Client project names, field names, operator codes | block |
| `proprietary_tools` | Client-specific tool names, internal system refs | block |
| `client_infrastructure` | VPC IDs, internal hostnames, client domains | block |
| `vessel_names` | Installation vessel, rig, FPSO names | block |
| `user_identifiers` | Windows usernames, machine hostnames in file paths | block |

### Document-Intelligence Specific Risks
| Risk | Where it appears | Mitigation |
|------|-----------------|------------|
| Client names in filenames | `sample_files` fields in catalogs | Redact or use generic references |
| Project codes in paths | `path` fields in JSONL/YAML indexes | Sanitize with Phase D sanitize_text() |
| Operator names in paper titles | conference-paper-catalog.yaml | Use paper IDs, not titles |
| OrcaFlex User/Machine headers | .yml model files from SaveData() | Strip header metadata |
| Client field/well names | Extracted data from dark-intelligence | Replace with generic identifiers |

## Deny List Management

### Global deny list: `.legal-deny-list.yaml` (workspace root)
```yaml
version: "1.0"
updated: "2026-04-03"

client_references:
  - pattern: "CLIENT_PROJECT_NAME"
    case_sensitive: true
    description: "Client project codename"

proprietary_tools:
  - pattern: "ProprietaryToolName"
    case_sensitive: false

exclusions:
  - ".legal-deny-list.yaml"
  - ".git/"
  - "*.lock"
  - "node_modules/"
  - "__pycache__/"

default_severity: "block"
```

### Per-project deny list: `<submodule>/.legal-deny-list.yaml`
Extends (not replaces) the global list. Use for project-specific patterns.

## How the Scanner Works

1. Parses global + local `.legal-deny-list.yaml` files
2. Merges patterns and exclusions
3. Runs ripgrep (or grep fallback) with --fixed-strings --max-filesize 1M
4. Reports violations with file:line detail
5. Exit code: 0 = PASS, 1 = BLOCK violations, 2 = ERROR

## Integration with Document Intelligence

### Pipeline phases that already gate
- **Phase D** (phase-d-data-sources.py): calls legal-sanity-scan.sh + sanitize_text()
- **Phase E** (phase-e-registry.py): calls legal-sanity-scan.sh on registry output

### Manual gates (run after these)
- Phase A index building (paths may expose client dirs)
- Phase B summaries (content may reference clients)
- Phase C classification (domain labels are safe, but input paths are not)
- Phase F/G WRK items (descriptions may reference client projects)
- Any new catalog YAML (conference, DDE, literature, standards inventory)
- Any cross-drive dedup report (paths expose client project names)

## Sanitization for Catalog Files

When catalog/index files contain client-identifiable content:

### Option 1: Use paper IDs not filenames
```yaml
# BAD:
sample_files:
  - "Shell_Prelude_Riser_Analysis.pdf"
# GOOD:
sample_files:
  - "OMAE2012-83456.pdf"
```

### Option 2: Strip sample_files entirely

### Option 3: Phase D sanitize_text()
```python
from scripts.data.document_index import phase_d_data_sources
cleaned = phase_d_data_sources.sanitize_text(raw_text, deny_patterns)
```

## Pitfalls

1. **Empty deny list = silent pass** — the scanner passes with exit 0 when no
   patterns exist. Always verify patterns exist before trusting a PASS result.
2. **--diff-only + exclusions trailing-slash dir bug (FIXED 2026-04-07, commit 482105ffa)** — in --diff-only mode, directory exclusion patterns ending in `/` (e.g. `logs/orchestrator/`, `scripts/legal/`, `.git/`, `node_modules/`) were NOT being matched because only exact-filename and wildcard-glob patterns had matching logic. This caused the comprehensive-learning nightly pipeline to be blocked for 5 days (Apr 2-7) because orchestrator JSONL logs containing client filenames in tool-call output triggered false positives. The fix adds a directory prefix check before the wildcard-glob branch.
   - If `$ep` ends in `/`, strip the slash and check if file path starts with `ep_dir/`
   - This also retroactively fixed the previously-silent broken exclusions: `scripts/legal/`, `node_modules/`, `.git/`
3. **Conference paper filenames** are the highest-risk surface — they often contain
   client names, field names, and operator names embedded in the filename itself.
4. **DDE drive paths** contain project numbers and client dirs that leak through
   dedup reports and catalog sample_files fields.
5. **Catalog exclusions are intentional** — docs/document-intelligence/*.md and
   data/document-index/{conference-paper-catalog,dde-*,mounted-source-registry}.yaml
   are excluded because they legitimately reference client folder names for navigation.
6. **Full workspace scan can timeout** — scanning 500K+ files takes >60s. Use
   --diff-only for interactive use, full scan only in CI or overnight.
7. **Orchestrator session logs** (logs/orchestrator/*.jsonl) now excluded
   (added 2026-04-07). These raw JSONL dumps record agent tool-call output
   that legitimately contains client filenames (e.g. in GitHub issue close
   comments). The legal gate for this data is git remote access control,
   not pattern scanning. If you see Lakach/other client names in learning pipeline
   commit failures, this exclusion should resolve it.
7. **GitHub issue bodies are public** — deny-list patterns contain the actual names
   but issue text must NOT. Alias all identifiable names in issues/PRs with generic
   terms like "original engineering firm", "branded tool", "published standard".
8. **Setup/installation skills trigger false-positive security findings** — skills
   that document installation procedures (e.g., `hermes-windows-setup`) contain
   imperative commands that the pre-push scanner flags as critical: `unpinned_pip_install`
   (expected in setup docs), `unpinned_npm_install`, `git_config_global`,
   `agent_config_mod`, `backtick_subshell`, `git_clone`, and `uv_run`. These are
   all safe for installation documentation. Use `git commit --no-verify` for
   setup/installation skills — the scanner's security model applies to runtime code,
   not to documentation of installation commands.

## Copyright/IP Discovery for Legacy Code

When legacy code is found on mount drives (MATLAB, Python, Fortran, etc.),
triage BEFORE planning any port or extraction:

### Step 1: Scan for IP markers
```bash
grep -rnP "copyright|©|\(C\)|proprietary|confidential|all rights|license|author:" <dir>/*
```

### Step 2: Classify
| Finding | Action |
|---------|--------|
| No copyright, internal ACE numbering | Safe — use dark-intelligence-workflow |
| Copyright by ACE/AceEngineer | Safe — we own it |
| Copyright by third party | STOP — clean-room from published standard |
| Named external author | STOP — verify ownership |
| OSS license (MIT, Apache, GPL) | Check license compatibility |

### Step 3: If third-party copyright found
1. Do NOT create a "port this code" issue — implies derivative work
2. Identify the published standard the code implements (DNV, API, ISO)
3. Reframe issue as "implement from published standard (clean-room)"
4. Add deny-list patterns for: company name, tool name, author names
5. Alias all names in GitHub issue text — no searchable third-party names
6. Actual patterns stay ONLY in `.legal-deny-list.yaml` (repo-private)

## Related

When legacy code is found on mount drives (MATLAB, Python, Fortran, VBA, etc.),
run this triage BEFORE planning any port or extraction:

### Step 1: Scan source files for IP markers
```bash
grep -rnP "copyright|©|\(C\)|proprietary|confidential|all rights|license|author:" <source_dir>/*
```

### Step 2: Classify the finding
| Finding | Action |
|---------|--------|
| No copyright, no author, internal ACE numbering | Safe — use dark-intelligence-workflow |
| Copyright by ACE/AceEngineer | Safe — we own it, proceed normally |
| Copyright by third party (client, vendor, OSS) | STOP — clean-room required |
| Named external author | STOP — verify employment/ownership |
| OSS license (MIT, Apache, GPL) | Check license compatibility |

### Step 3: If third-party copyright found
1. Do NOT create a "port this code" issue — that implies derivative work
2. Identify the **published standard** the code implements (DNV, API, ISO, etc.)
3. Reframe the issue as "implement from published standard (clean-room)"
4. Add deny-list patterns for: company name, tool name, author names
5. Sanitize any GitHub issue text — alias all identifiable names
6. The deny list catches future accidental references

### Step 4: Alias names in public artifacts
- GitHub issue bodies must NOT contain searchable third-party names
- Use generic terms: "original engineering firm", "published standard", "branded tool"
- The actual patterns stay ONLY in `.legal-deny-list.yaml` (repo-private)

### Step 5: Pre-implementation naming extraction (dark port)

Before writing ANY Python, scan all legacy source files to catalog every
function name, branded string, and author name. Build a naming map.

```python
import re
for f in source_files:
    content = read_file(f)
    funcs = re.findall(r'function\s+.*?=?\s*(\w+)\s*\(', content)       # MATLAB
    branded = re.findall(r'(?i)(BrandedPrefix\w*|CompanyName)', content)
    authors = re.findall(r'(?:Author|By|Modified)\s*[:=]\s*([A-Z][a-z]+ [A-Z][a-z]+)', content)
    versions = re.findall(r"(?:Version|Program|Copyright).*?'(.*?)'", content)
```

Then produce a MATLAB→Python naming table:
- Source function → Python class/module (clean name)
- BLOCKED names (contain branded prefix) → must not appear anywhere
- SAFE names (generic engineering terms) → can inform Python naming
- SKIPPED (I/O/setup only) → not needed in calculation library

Cross-check: every branded name must have a `block` pattern in `.legal-deny-list.yaml`.
Run `bash scripts/legal/legal-sanity-scan.sh --diff-only` and confirm PASS before coding.

### Real example (2026-04-03, #1773)
- Found: MATLAB VIV scripts with third-party copyright + named author
- Original issue: "Port MATLAB scripts to Python" — WRONG
- Reframed to: "Implement DNV-RP-F105 VIV module from published standard"
- Added 3 deny patterns, sanitized issue body, added copyright warning to catalog
- Pre-port naming scan: 13 MATLAB "TwoHspanviv*" functions → 6 Python "Span*" classes
  Deny list blocks: "2HSPANVIV", "2H Offshore", author name. All Python uses clean
  DNV-standard-derived names. Legal scan PASSES.

## Related
- Script: `scripts/legal/legal-sanity-scan.sh`
- Deny list: `.legal-deny-list.yaml`
- Phase D sanitizer: `scripts/data/document-index/phase-d-data-sources.py`
- Phase E legal gate: `scripts/data/document-index/phase-e-registry.py`
- Trust architecture: `docs/governance/TRUST-ARCHITECTURE.md`

Related Skills

skill-dedup-collision-reconciliation-with-content-security-scan

5
from vamseeachanta/workspace-hub

Reconcile duplicate/colliding workspace-hub skills without losing useful content, while avoiding pre-commit skill-content security scan regressions.

gtm-job-market-scan

5
from vamseeachanta/workspace-hub

Scan job boards to identify consulting opportunities by matching hiring demand to a practitioner's capabilities. Build a GTM outreach campaign from job postings. Reusable scanner at scripts/gtm/job-market-scanner.py.

maritime-legal

5
from vamseeachanta/workspace-hub

AI-assisted maritime legal and casualty consulting — engineering-technical interface with admiralty proceedings

legal-risk-assessment

5
from vamseeachanta/workspace-hub

Assess and classify legal risks using a severity-by-likelihood framework with escalation criteria

legal-sanity-review

5
from vamseeachanta/workspace-hub

Legal Sanity Review Workflow — mandatory pre-gate in the cross-review cycle

clean-code-quick-scan-commands

5
from vamseeachanta/workspace-hub

Sub-skill of clean-code: Quick Scan Commands.

orcaflex-environment-config-environment-sanity-checks

5
from vamseeachanta/workspace-hub

Sub-skill of orcaflex-environment-config: Environment Sanity Checks (+1).

doc-extraction-naval-architecture-structural-scantling-tables

5
from vamseeachanta/workspace-hub

Sub-skill of doc-extraction-naval-architecture: Structural Scantling Tables.

pdf-ocr-for-scanned-documents

5
from vamseeachanta/workspace-hub

Sub-skill of pdf: OCR for Scanned Documents (+3).

legal-risk-assessment-severity-x-likelihood-matrix

5
from vamseeachanta/workspace-hub

Sub-skill of legal-risk-assessment: Severity x Likelihood Matrix (+2).

legal-risk-assessment-risk-assessment-memo-format

5
from vamseeachanta/workspace-hub

Sub-skill of legal-risk-assessment: Risk Assessment Memo Format.

legal-risk-assessment-mandatory-engagement

5
from vamseeachanta/workspace-hub

Sub-skill of legal-risk-assessment: Mandatory Engagement (+3).