---
phase: 02-scanners
plan: 01
subsystem: scanners
tags: [pii, scanner, enum, guardline, bundle-expansion, action-overrides]

requires:
  - phase: 01-foundation
    provides: BaseScanner, _StrValueMixin, Action, Condition enums, Guard class

provides:
  - PIILabel enum with 53 members (47 canonical + 5 bundle shortcuts + ALL sentinel)
  - PIIScanner class with SDK-side bundle expansion and per-label action override support
  - Guard.scan_input() updated to handle multi-spec scanners via to_guardline_specs()

affects:
  - 02-scanners (remaining scanner plans may follow same bundle-expansion pattern)
  - 03-server (guardline spec format with bundles field for forward compat)
  - 04-integration (PIIScanner is primary user-facing scanner)

tech-stack:
  added: []
  patterns:
    - "SDK-side bundle expansion: bundles expanded to canonical labels in required.ner/regex since server does not expand bundles"
    - "Multi-spec scanners: to_guardline_specs() splits per-label overrides into separate guardline entries per action group"
    - "Fallback pattern: Guard checks for to_guardline_specs() before falling back to to_guardline_spec()"

key-files:
  created:
    - src/meshulash_guard/scanners/pii.py
  modified:
    - src/meshulash_guard/guard.py

key-decisions:
  - "Bundle expansion happens SDK-side: server's process_sdk() reads only required.ner and required.regex, does not expand bundle names"
  - "Per-label overrides require multiple guardline specs since server supports one action per guardline"
  - "Multi-spec key format: sdk_{class}_{i}_{speckey} for scanners with to_guardline_specs(); sdk_{class}_{i} for simple scanners"
  - "bundles field retained in spec for forward compatibility but expanded labels drive server behavior"
  - "ALL sentinel expands all 5 bundles (45 unique canonical labels after deduplication)"

patterns-established:
  - "Bundle shortcuts pattern: PIILabel.PII/PHI/PCI/SECRETS/TECH expand to their canonical labels via _BUNDLE_LABELS constant"
  - "Action override grouping: labels grouped by effective action, each group becomes a separate guardline spec"
  - "to_guardline_specs() always returns 'main' key; additional groups use 'override_0', 'override_1', etc."

duration: 2min
completed: 2026-02-26
---

# Plan 1: PIIScanner Summary

**PIIScanner with 53-label PIILabel enum, SDK-side bundle expansion, and per-label action override support via multiple guardline specs.**

## Performance

- **Duration:** 2min
- **Started:** 2026-02-26T18:14:13Z
- **Completed:** 2026-02-26T18:16:11Z
- **Tasks:** 2 completed
- **Files modified:** 2

## Accomplishments

- Created `PIIScanner` and `PIILabel` enum with 53 members covering identity, contact, financial, credentials, technical, temporal, and organizational categories plus 5 bundle shortcuts and the ALL wildcard
- Implemented SDK-side bundle expansion: the server's `process_sdk()` only reads `required.ner` and `required.regex`, so all bundle shortcuts must be expanded to canonical labels by the SDK before sending
- Added per-label action override support: `to_guardline_specs()` groups labels by their effective action and returns one guardline spec per action group, enabling labels like `NATIONAL_ID` to use `Action.BLOCK` while `EMAIL_ADDRESS` uses `Action.REPLACE`
- Updated `Guard.scan_input()` to detect scanners with `to_guardline_specs()` and expand them into multiple guardline entries, while preserving full backward compatibility for simple scanners

## Task Commits

Each task was committed atomically:

1. **Task 1: Create PIIScanner with PIILabel enum, bundle expansion, and to_guardline_specs** - `6d02a00` (feat)
2. **Task 2: Update Guard.scan_input() to support multi-spec scanners** - `2e26555` (feat)

**Plan metadata:** (docs commit below)

## Files Created/Modified

- `src/meshulash_guard/scanners/pii.py` - PIILabel enum (53 members), _BUNDLE_LABELS constant, _expand_labels() helper, _build_spec() helper, PIIScanner class with to_guardline_spec() and to_guardline_specs()
- `src/meshulash_guard/guard.py` - scan_input() updated to check for to_guardline_specs() and expand multi-spec scanners

## Decisions Made

1. **SDK-side bundle expansion**: The server's `gating.py _build_label_guardline_index` reads only `required.ner` and `required.regex` from guardline specs — it does NOT expand bundle names. Therefore the SDK must expand `PIILabel.PII`, `PIILabel.SECRETS`, etc. into their constituent canonical labels before sending the request. The `bundles` field is still populated for forward compatibility but is not used by the current server.

2. **Multi-spec key naming**: Multi-spec scanner keys follow `sdk_{class}_{i}_{speckey}` format (e.g., `sdk_piiscanner_0_main`, `sdk_piiscanner_0_override_0`). Simple scanners keep the original `sdk_{class}_{i}` format for backward compatibility.

3. **ALL expands to 45 labels**: After deduplication across all 5 bundles, `PIILabel.ALL` expands to 45 unique canonical labels (PII bundle already contains most PCI labels, SECRETS has 20 credential labels, TECH has 2).

## Deviations from Plan

None - plan executed exactly as written.

## Issues Encountered

None.

## User Setup Required

None - no external service configuration required.

## Next Phase Readiness

- PIIScanner is importable from `meshulash_guard.scanners.pii`
- Guard.scan_input() handles both simple scanners (to_guardline_spec) and multi-spec scanners (to_guardline_specs)
- Ready for Phase 2 Plan 02 (TopicScanner or next scanner implementation)
- The guardline spec format is now established: name, action, condition, types, required.{ner,regex,tc}, bundles, threshold, allowlist

---
*Phase: 02-scanners*
*Completed: 2026-02-26*
