#2088

ENG-622: Load the alcatraz NER model in the sidecar through pii.ner

OpenEmanuelJrwants to merge intofrom

📝 Description

The sidecar loads the alcatraz NER model through a pii.ner config block, so a mask or pii guardrail rule can name PERSON, LOCATION, ORGANIZATION and NRP. ONNX Runtime runs it in -tags ORT builds (Linux hoop, hoop-inspect ica build); the pure-Go backend elsewhere. Without the block those names stay refused, with a message that says to set pii.ner. New -alcatraz and -distroless-alcatraz sidecar images carry the model and ONNX Runtime at the block's default paths.

📣 User-facing impact

Sidecar rules can now mask or block people's names, places and organizations in query results and HTTP bodies, by setting pii.ner and running the -alcatraz sidecar image.

🔗 Related Issue

ENG-622

🚀 Type of Change

  • ✨ New feature (non-breaking change which adds functionality)
  • 🔧 Build configuration change

📋 Changes Made

  • sidecar/pii/alcatraz: pii.ner (models_dir, backend, ort_library, threads) loads alcatraz/ner and attaches it to the engine (SetNlpEngine + Recognizer). One engine per distinct block for the process life; Offline, sha256-verified model; backend defaults by build tag, ort on a non-ORT build is a startup error.
  • sidecar/daemon: pii_ner value capability; CheckServable refuses pii.ner to a build without it; the JSON logger is installed before the detector builds.
  • Dockerfile.sidecar: alcatraz and distroless-alcatraz flavours from shared models/ort stages (ica reuses ort); release.yml and pullrequest-release.yml publish and verify them; pullrequest.yml adds sidecar-pii-ner, which runs TestNERRealModel on ONNX Runtime with the real model.
  • webapp_v2: ORGANIZATION and NRP in the sidecar rule entity picker.
  • Docs: sidecar/README.md NER section, sidecar/CLAUDE.md, package docs.

🧪 Testing

Result: a pii.ner block loads the model, and rules naming PERSON/LOCATION/ORGANIZATION/NRP mask and deny; without the block the sidecar refuses those names as before. Independent QA: PASS (round 2) on 1f9356b88 against origin/main 4dfa5aecd.

Left: origin/main refuses the PERSON rule at startup. Right: the branch redacts the name column only; the audit event names PERSON and no value.

A statement carrying a name is denied; a clean one passes.

What it solves: operators masking free-text columns and HTTP bodies with the sidecar get the four statistical entities the webapp already offered.

Definition of done

  • alcatraz/ner linked and attached to the same engine; the four entities accepted with NER
  • pii.ner opt-in; refusal without it names pii.ner
  • backend default by build tag; ort on a non-ORT build is a startup error
  • Missing or tampered model is a startup / --validate error naming the file; Offline
  • Default paths match both Dockerfiles (/opt/alcatraz/models, /opt/alcatraz/ort/releases/1.28.0/lib/libonnxruntime.so)
  • Postgres lane: PERSON redacted, audit names the class only; pii guardrail denies
  • Hot reload: add/change/remove pii.ner without restart, whole-document accept/refuse
  • pii_ner capability; CheckServable; make test-sidecar-compat passes
  • -alcatraz images: stages and publish jobs in place; docker run … backend ort proven by this PR's docker-publish-hoopsidecar + verify-alcatraz-image.sh and sidecar-pii-ner jobs
  • Tests: fake NlpEngine unit tests; env-gated real-model test; CI job
  • Webapp picker lists ORGANIZATION and NRP
  • Docs

Tests that guard it

sidecar/pii/alcatraz: TestNEREntitiesRefusedWithoutTheBlock, TestNERBlockAddsTheFourEntities, TestNERMasksAndScansNames, TestNERGuardrailDeniesAStatementCarryingAName, TestNEREngineIsLoadedOncePerBlock, TestNERLoadFailureRefusesTheDetector, TestNERResolveDefaults, TestPluginFromConfigDecodesTheNERBlock, TestNERRealModel (CI job sidecar-pii-ner). sidecar/daemon: TestPIINERNeedsTheCapability.

Re-run

export GOPRIVATE=github.com/hoophq/libhoop,github.com/hoophq/libhoop-ica
(cd sidecar/pii/alcatraz && CGO_ENABLED=0 go test ./...)
(cd sidecar/daemon && CGO_ENABLED=0 go test ./...) && make test-sidecar-compat
# real model, pure-Go backend
alcatraz models download --dest /tmp/models   # or the curl loop of the `models` stage in Dockerfile.sidecar
(cd sidecar/pii/alcatraz && HOOP_ALCATRAZ_MODELS_DIR=/tmp/models CGO_ENABLED=0 go test -run TestNERRealModel -v .)
# a lane: pii: {ner: {backend: go, models_dir: /tmp/models}}, listener postgres with mask rule {entities: [PERSON], strategy: redact}
(cd sidecar/cmd && CGO_ENABLED=0 go run . -validate -config config.yaml && go run . -config config.yaml)
psql -h 127.0.0.1 -p <listen> -c 'select name, city from customers'

Test Configuration:

  • Browser(s): Chromium (headless) for the picker
  • OS: macOS 15 (arm64), CGO_ENABLED=0; ORT paths run in CI only

Tests performed:

  • Unit tests pass
  • Integration tests pass (sidecar lane against Postgres, hot reload, compat)
  • Manual testing completed

✅ Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • My changes generate no new warnings
  • New and existing unit tests pass locally with my changes
  • I have checked my code and corrected any misspellings

📄 Additional Notes

  • Gateway↔agent contract untouched; no migration, env var or analytics change.
  • Sidecar compatibility: an older build ignores pii.ner and refuses the PERSON rule by name; the plane now refuses to serve the block to a build without pii_ner.
  • The pure-Go backend costs ~100 ms per masked value; ort is the production path (documented).
  • TestNERRealModel skips under -race on the go backend: gomlx/compute v0.1.2 trips checkptr (upstream).
  • A bare docker build -f Dockerfile.sidecar now builds alcatraz, the superset flavour, since a stage derives only from one declared before it.

Activity

12
sandromello2d ago

✅ Build Completed with Success, Version=2088.0.0-g1f9356b

matheusfranciscoreviewed2d ago
matheusfrancisco2d ago

Runtime conflict with ICA: yes, one real one. Both the ICA engine and pii.ner reach alcatrazner.New → hugot.NewORTSession, and hugot allows one ORT session per process:

  • hugot@v0.7.7/hugot_ort.go:18 — if ort.IsInitialized() { return errors.New("another session is currently active, and only one session can be active at one time") }
  • libhoop-ica@v0.7.1/internal/policy/ner.go:275 — ICA calls alcatrazner.New with BackendORT.
  • PR ner.go newNLPEngine → ner.New with Backend defaulting to ort under -tags ORT (ort_compiled.go).

So in the -ica image, a config with both an ICA listener and pii.ner (the README diff explicitly suggests this: "The -ica image holds the same model under /opt/libhoop-ica/releases/image/models; point models_dir at it") fails at whichever engine loads second. Two follow-on hazards in the same process:

  1. ICA teardown kills the shared ORT env. sidecar/ica/ica.go:408 e.runtime.Close() → libhoop-ica engine.Close() → hugot.Session.Destroy → ort.DestroyEnvironment() (hugot.go:352). The PR's nerEngines cache keeps its engine for the process life, so after an ICA listener release the cached pii.ner engine points at a destroyed environment.
  2. Hot reload claim is wrong on ORT. README: "one that changes ner loads a second engine beside the first". On the ort backend the second NewORTSession is refused by hugot_ort.go:18, so changing pii.ner on a reload fails until restart. The PR's tests use a fake newNLPEngine, so this is not covered.
matheusfrancisco2d ago

pii.ner beside an ICA lane: one ORT session per process

Both the ICA engine and pii.ner go through alcatrazner.New → hugot.NewORTSession, and hugot allows one ORT session per process (hugot@v0.7.7/hugot_ort.go:18: if ort.IsInitialized() { return errors.New("another session is currently active ...") }). ICA always loads its own NER (sidecar/ica/ica.go:48,710: the model is a required file and readiness waits on it), so the question is whether pii.ner also wants ORT.

Same process: ICA lane +pii.nerORT sessionsResult
postgres/mysql/http lane masking the 52 pattern entities (EMAIL, SSN, CREDIT_CARD, …)not set1 (ICA)works today and after the PR; unaffected
lane masking PERSON/LOCATION/ORGANIZATION/NRPbackend: ort (default in the -ica ORT build)2second load fails: hugot_ort.go:18
samebackend: go1 (ICA)works; pii.ner is ~100 ms/value, pure Go, and ICA keeps ORT
sameort, with ica.ner_backend: go (ica.go:126)1 (pii.ner)works; ICA's on-screen NER slows down instead

So "can't run ICA and name masking on other protocols in one process on ORT" is the accurate statement. The pattern-entity masking you run today beside ICA is not touched.

Two follow-on hazards in the same process:

  1. ICA teardown destroys the shared ORT environment: sidecar/ica/ica.go:408 e.runtime.Close() → libhoop-ica engine.Close() → hugot.Session.Destroy → ort.DestroyEnvironment() (hugot.go:352). nerEngines keeps the pii.ner engine for the process life, so after an ICA lane is released it runs on a destroyed environment.
  2. The README says a reload that changes ner "loads a second engine beside the first". On ort the second NewORTSession is refused by hugot_ort.go:18, so that reload fails until restart. Tests use the fake newNLPEngine, so this is not covered.

The production answer is two sidecars: -ica image for ICA lanes, -alcatraz image for DB/HTTP lanes with pii.ner. Both get ORT, hazard 1 disappears, and the config split is one listeners: block per process. The README line telling you to point models_dir at /opt/libhoop-ica/releases/image/models inside the -ica image is the trap and should be reworded in the PR.

matheusfranciscoreviewed2d ago
matheusfrancisco2d ago

add an envoy stack with this configuration and config.yml

pii:                      # one per process: WHAT the detector can recognise
  entities: [...]         # narrows the 52 built-in types (optional)
  threshold: 0.4
  allow_list: [...]
  ner:                    # PR #2088: adds PERSON/LOCATION/ORGANIZATION/NRP
    models_dir: ...
    backend: ort

mask:                     # top-level default, inherited; per listener override
  rules:                  # WHAT TO DO on this lane
    - entities: [EMAIL, PERSON]
      strategy: partial
      keep_last: 4
    - columns: [ssn]
      strategy: redact

guardrails:
  rules:
    - type: pii           # DENY the statement when the detector finds these
      entities: [PERSON]

and add documentation about this

So pii: {ner: {...}} alone changes nothing on the wire. Add mask.rules: [{entities: [PERSON], strategy: redact}] on the lane and names get masked; add a type: pii guardrail and statements carrying names get denied. The PR's QA images show exactly these two combinations.

Mental model: pii = vocabulary; mask / guardrails / analyzer = verbs.

EmanuelJr2d ago

Confirmed against hugot v0.7.7 (hugot_ort.go:18, hugot.go:352): one ORT session per process, and the ICA engine's Close destroys the environment. 9a5040337:

  • daemon.Config.validate refuses pii.ner beside an ica listener unless one side is go (pii.ner.backend: go or ica.options.ner_backend: go). An explicit go, not the build default, so the plane's Validate decides the same as the host. Cases in TestICAConfig.
  • pii/alcatraz.loadNER refuses a changed ner block on ort with "ONNX Runtime is held by the previous pii.ner block; restart" instead of hugot's message (ortSessionHeld, tested). Hazard 2 is now a named refusal, not a failed reload. Hazard 1 cannot happen: the pairing that would share the environment is refused at load.
  • README: the -ica models_dir line is gone; the section states the one-session rule and the two-sidecar shape (-ica for ICA lanes, -alcatraz for DB/HTTP lanes).
sandromello2d ago

✅ Build Completed with Success, Version=2088.0.0-g9a50403

qodo-code-review[bot]2d ago

PR Summary by Qodo

Load alcatraz NER in the sidecar through pii.ner

✨ Enhancement ⚙️ Configuration changes 🧪 Tests 📝 Documentation 🕐 40+ Minutes

AI Description

• Enable PERSON, LOCATION, ORGANIZATION and NRP detection for sidecar masking and PII guardrails
 through pii.ner.
• Ship model-backed sidecar images with ONNX Runtime while retaining a pure-Go backend for other
 builds.
• Add capability checks, model tests and deployment guidance to make NER configuration safer.
Diagram

graph TD
  CFG["pii.ner config"] --> VALID["Daemon validation"] --> LOAD["NER loader"] --> ENGINE["Alcatraz engine"] --> DET["PII detector"] --> MASK["Mask rules"]
  ASSETS[("Model assets")] --> LOAD
  DET --> GUARD["PII guardrail"]
High-Level Assessment

The following are alternative approaches to this PR:

1. Run NER in a separate inference service
  • ➕ Scales inference independently and avoids sharing an ONNX Runtime session with ICA.
  • ➖ Adds operational complexity, latency and a network boundary for potentially sensitive text.

Recommendation: Keep the opt-in, in-process approach: it reuses the existing detector and rule paths without sending PII to another service. Separate sidecars remain the simpler deployment answer when ICA and PII NER both need ONNX Runtime.

Files changed (23) +1316 / -140

Enhancement (9) +369 / -27
capabilities.goAdvertise and require pii_ner support +8/-1

Advertise and require pii_ner support

• Adds the pii_ner value capability to the handshake and requires it when a served configuration sets pii.ner.

sidecar/daemon/capabilities.go

config.goReject competing ONNX Runtime users +43/-0

Reject competing ONNX Runtime users

• Detects an active pii.ner block and refuses configurations where it and an ICA listener would both request the process's single ORT session.

sidecar/daemon/config.go

daemon.goInitialize logging before detector construction +3/-0

Initialize logging before detector construction

• Installs the configured logger before model loading so startup messages use the normal log format.

sidecar/daemon/daemon.go

alcatraz.goAttach NER to the Alcatraz detector +54/-17

Attach NER to the Alcatraz detector

• Optionally registers the model recognizer and NLP engine, making four statistical entities available only when NER is configured. Missing-model errors now direct operators to pii.ner.

sidecar/pii/alcatraz/alcatraz.go

config.goDecode pii.ner into detector options +25/-7

Decode pii.ner into detector options

• Accepts model directory, backend, ORT library and thread settings from the PII configuration and passes them to detector construction.

sidecar/pii/alcatraz/config.go

ner.goLoad, warm and reuse configured NER engines +211/-0

Load, warm and reuse configured NER engines

• Selects the build-appropriate Go or ORT backend, loads the offline model, enables organization detection and caches engines across detector rebuilds. It limits ORT to one distinct configuration per process.

sidecar/pii/alcatraz/ner.go

ort_absent.goDefault to Go without compiled ORT +10/-0

Default to Go without compiled ORT

• Marks builds lacking the cgo ORT backend and selects pure-Go inference by default.

sidecar/pii/alcatraz/ort_absent.go

ort_compiled.goDefault to ORT when compiled +12/-0

Default to ORT when compiled

• Marks cgo ORT-enabled builds and selects ONNX Runtime as their default NER backend.

sidecar/pii/alcatraz/ort_compiled.go

sidecarRuleVocabulary.jsOffer organization and NRP in sidecar rules +3/-2

Offer organization and NRP in sidecar rules

• Adds ORGANIZATION and NRP to the mask and PII guardrail entity picker and notes that NER entities require pii.ner.

webapp_v2/src/pages/sidecarRuleVocabulary.js

Tests (6) +479 / -0
pullrequest.ymlExercise the real NER model in PR CI +87/-0

Exercise the real NER model in PR CI

• Adds an ORT-tagged sidecar test job that downloads the pinned tokenizer and runtime, verifies cached model files against their manifest, and fails if the real-model test skips.

.github/workflows/pullrequest.yml

address_test.goTest pii.ner capability gating +21/-0

Test pii.ner capability gating

• Verifies that an older sidecar cannot be served an active NER block, while configurations without one remain servable.

sidecar/daemon/address_test.go

ica_test.goCover ICA and PII backend combinations +7/-0

Cover ICA and PII backend combinations

• Tests refusal when both features use ORT and acceptance when either uses Go or no ICA listener exists.

sidecar/daemon/ica_test.go

ner_test.goTest NER detection, masking and lifecycle +354/-0

Test NER detection, masking and lifecycle

• Covers entity availability, masking, guardrail denial, configuration errors, cache behavior and ORT session limits with a fake engine. An environment-gated integration test exercises masking and scanning with the real model.

sidecar/pii/alcatraz/ner_test.go

norace_test.goIdentify non-race test builds +5/-0

Identify non-race test builds

• Defines the build-specific flag used by the real-model test when the race detector is absent.

sidecar/pii/alcatraz/norace_test.go

race_test.goIdentify race-enabled test builds +5/-0

Identify race-enabled test builds

• Defines the flag that lets the real-model test skip the affected pure-Go backend under the race detector.

sidecar/pii/alcatraz/race_test.go

Documentation (3) +68 / -15
verify-alcatraz-image.shDocument image verification for both repositories +8/-7

Document image verification for both repositories

• Clarifies that the existing filesystem-export check applies to sidecar as well as agent alcatraz images.

scripts/ci/verify-alcatraz-image.sh

CLAUDE.mdDocument NER module and test requirements +4/-1

Document NER module and test requirements

• Records the new NER dependency, backend build distinction and environment needed to run the real-model ORT test.

sidecar/CLAUDE.md

README.mdExplain sidecar NER configuration and deployment +56/-7

Explain sidecar NER configuration and deployment

• Documents supported entities, backend defaults, model paths, performance considerations and the one-session constraint when ICA shares a process.

sidecar/README.md

Other (5) +400 / -98
pullrequest-release.ymlPublish and verify preview NER sidecar images +38/-4

Publish and verify preview NER sidecar images

• Builds PR-numbered alcatraz and distroless-alcatraz sidecar images for both supported architectures, then verifies their published model and runtime artifacts.

.github/workflows/pullrequest-release.yml

release.ymlRelease model-backed sidecar image variants +50/-10

Release model-backed sidecar image variants

• Publishes and verifies versioned alcatraz and distroless-alcatraz sidecar images without changing the unsuffixed or latest tags.

.github/workflows/release.yml

Dockerfile.sidecarBuild sidecar images with model and ONNX Runtime assets +144/-82

Build sidecar images with model and ONNX Runtime assets

• Adds two model-backed flavours derived from the existing images. Shared, verified model and runtime stages also supply the ICA image and assets bundle.

Dockerfile.sidecar

go.modDeclare NER and inference dependencies +30/-0

Declare NER and inference dependencies

• Adds the Alcatraz NER module, hugot and their transitive dependencies to the sidecar detector module.

sidecar/pii/alcatraz/go.mod

go.sumRecord dependency checksums for NER +138/-2

Record dependency checksums for NER

• Adds checksums for the model runtime and its dependency graph and removes stale Alcatraz version entries.

sidecar/pii/alcatraz/go.sum

qodo-code-review[bot]2d ago

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (2) 📎 Requirement gaps (1) 🔗 Cross-repo conflicts (2) 📜 Skill insights (0)


1. ORT NER changes require restart 📎 Requirement gap ≡ Correctness
Description
ortSessionHeld rejects a new ONNX Runtime engine whenever another configuration is already cached
for the process. Changing models_dir, ort_library, or threads therefore fails instead of
taking effect during a configuration reload, reaching every detector using the updated pii.ner
block.
Code

sidecar/pii/alcatraz/ner.go[R205-207]

+	for prev := range nerEngines.byConfig {
+		if prev.Backend == ner.BackendORT {
+			return fmt.Errorf("ner: ONNX Runtime is held by the previous pii.ner block; restart the sidecar to change models_dir, ort_library or threads")
Relevance

●● Moderate

The restart requirement is intentional and documented, but conflicts with the ticket's hot-reload
objective.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The process-wide nerEngines cache retains engines by configuration, while ortSessionHeld scans
that cache and returns a restart-required error when any prior ORT configuration exists. The
implementation and accompanying tests therefore explicitly reject changed ORT settings rather than
applying them atomically during reload.

Hot-reload NER configuration atomically sidecar/pii/alcatraz/ner.go[163-207] sidecar/pii/alcatraz/ner_test.go[227-243]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Changing an existing ONNX Runtime `pii.ner` configuration is rejected because the process-wide cache refuses a second ORT engine, so the hot-reload requirement is not met.

## Fix Focus Areas
- sidecar/pii/alcatraz/ner.go[163-207]
- sidecar/daemon/config.go[266-282]

## Recommended Fix
Implement a safe atomic replacement strategy for the active ORT NER engine, or coordinate the reload lifecycle so the previous session is released only after in-flight users finish and the new session is ready. Preserve the existing detector and configuration when loading or swapping the replacement fails, and add tests covering changed `models_dir`, `ort_library`, and `threads` without restarting the sidecar.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Dismiss ↗ | View ↗


2. Nationality rules miss every match 🔗 Cross-repo conflict ≡ Correctness
Description
NEREntities admits NRP rules, but the pinned Alcatraz model is a CoNLL-2003 fine-tune while
Alcatraz maps only the OntoNotes NORP label to NRP. With the shipped model, nationality,
religion and political-group text produces no NRP detection, so those mask and guardrail rules
cannot fire.
Code

sidecar/pii/alcatraz/ner.go[42]

+	return []string{entities.Location, entities.NRP, Organization, entities.Person}
Relevance

●● Moderate

Potential model-label incompatibility is a substantive cross-repository semantic concern without a
close repository precedent.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The PR advertises NRP and loads Alcatraz's default configuration. Alcatraz identifies its pinned
weights as a CoNLL-2003 fine-tune, maps NRP only from NORP, and derives supported entities from
configured mappings rather than the model's output labels.

hoop -> alcatraz sidecar/pii/alcatraz/ner.go[39-43] sidecar/pii/alcatraz/ner.go[133-138] External repo: hoophq/alcatraz, docs/cli.md [209-216] External repo: hoophq/alcatraz, ner/config.go [203-218] External repo: hoophq/alcatraz, ner/config.go [231-249] External repo: hoophq/alcatraz, ner/ner.go [335-350]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The sidecar accepts NRP policies, but the pinned CoNLL-trained model does not emit the NORP label that Alcatraz maps to NRP.
## Fix Focus Areas
- sidecar/pii/alcatraz/ner.go[39-43]
- sidecar/pii/alcatraz/ner_test.go[318-354]
- /cross_repos/alcatraz/ner/config.go[203-218]
## Recommended Fix
Either use and package a verified model that emits NORP, or stop offering NRP as a detectable entity with this model. Add a real-model inference assertion for NRP rather than checking only that the detector accepts its name.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Dismiss ↗ | View ↗



3. NER tests lack sample environment keys 📘 Rule violation ⚙ Maintainability
Description
TestNERRealModel reads HOOP_ALCATRAZ_MODELS_DIR and HOOP_ALCATRAZ_ORT_LIBRARY with
os.Getenv. These newly introduced variables are absent from the root .env.sample, so developers
cannot discover the environment needed to run the real-model test locally.
Code

sidecar/pii/alcatraz/ner_test.go[R311-313]

+	dir := os.Getenv("HOOP_ALCATRAZ_MODELS_DIR")
+	if dir == "" {
+		t.Skip("HOOP_ALCATRAZ_MODELS_DIR is unset")
Relevance

●●● Strong

The active repository rule explicitly requires new environment variables to appear in .env.sample.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The changed test directly reads both new environment variables, while the root sample contains no
matching HOOP_ALCATRAZ_* keys.

Rule 1800927: New env vars must be mirrored in .env.sample sidecar/pii/alcatraz/ner_test.go[307-324] .env.sample[1-35] .env.sample[65-85]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

Issue description
The new real-model test reads `HOOP_ALCATRAZ_MODELS_DIR` and `HOOP_ALCATRAZ_ORT_LIBRARY`, but neither key is listed in the root `.env.sample`.

Fix Focus Areas
- sidecar/pii/alcatraz/ner_test.go[311-324]
- .env.sample[1-120]

Recommended Fix
Add `HOOP_ALCATRAZ_MODELS_DIR=` and `HOOP_ALCATRAZ_ORT_LIBRARY=` as separate entries in `.env.sample`, with comments explaining that they are required only for the real ONNX Runtime model test.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Dismiss ↗ | View ↗


4. Build guidance hides a supported tag 📘 Rule violation ✧ Quality
Description
The README calls ORT the only build tag, but the new ort_compiled.go build constraint also
enables the backend for ALL. Readers using the documented tag guidance can miss a supported build
configuration, so the README's backend-selection instructions do not match the source.
Code

sidecar/README.md[R197-198]

+The one build tag is `ORT`, and it decides only which backend runs the NER
+model (see "Names, places and organizations" under Masking and PII). The
Relevance

●●● Strong

Recent history accepts README corrections for misleading operational claims and unsupported
behavior.

PR-#2058 PR-#1847

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The changed README states that ORT is the one build tag, while the new build-constraint file
explicitly enables ORTCompiled for both ORT and ALL.

Rule 3060202: README/CLAUDE docs must avoid unverified or misleading product claims sidecar/README.md[197-203] sidecar/pii/alcatraz/ort_compiled.go[1-10]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

Issue description
The README says `ORT` is the only build tag, but `sidecar/pii/alcatraz/ort_compiled.go` also enables the ONNX Runtime backend when the `ALL` build tag is set.

Fix Focus Areas
- sidecar/README.md[197-198]
- sidecar/pii/alcatraz/ort_compiled.go[1-10]

Recommended Fix
Update the README to accurately describe every supported build-tag combination, or change the build constraints if `ALL` is not intended to enable the backend. Keep the backend-selection and startup-error documentation consistent with the final build constraints.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Dismiss ↗ | View ↗


5. Rules naming PERSON never tell operators to set pii.ner 🐞 Bug ◔ Observability
Description
The new hint is added only to NewDetector's refusal, and that refusal fires only when
pii.entities or pii.ignored lists an NER entity. The usual setup is a mask rule (`entities:
[PERSON]) or a pii guardrail rule with no pii` section. Those rules are checked elsewhere: the
masker fails with "entity \"PERSON\" is not in the detector's set", and the guardrail check in
daemon.go says "drop it from pii.ignored, or fix the spelling". Neither message mentions pii.ner,
even though the README added in this PR says both rule kinds get a message naming it.
Code

sidecar/pii/alcatraz/alcatraz.go[R277-280]

+		hint := ""
+		if o.NER == nil && namesNEREntity(o.Entities, o.Ignored) {
+			hint = " (PERSON, LOCATION, ORGANIZATION and NRP need the NER model: set the pii.ner block)"
+		}
Relevance

●●● Strong

The documented error guidance omits actual mask and guardrail validation paths; this is a concrete
user-facing mismatch.

PR-#2058

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The hint is built only inside NewDetector, from the pii section's own lists. Mask rules are
validated in masker.go and pii guardrail rules in daemon.go, and both use their own generic messages
with no NER hint. The README this PR adds says a mask rule or pii guardrail rule naming one of the
four is refused "and the message names pii.ner", which is not true for either path.

sidecar/pii/alcatraz/masker.go[300-305] sidecar/daemon/daemon.go[1634-1641] sidecar/README.md[4717-4720] sidecar/pii/alcatraz/alcatraz.go[276-283]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Without a `pii.ner` block, a mask rule or a `pii` guardrail rule that names PERSON, LOCATION, ORGANIZATION or NRP is refused with a generic message that never mentions `pii.ner`. The guardrail message even suggests the wrong fix ("drop it from pii.ignored"). The pii.ner hint only appears when the `pii.entities`/`pii.ignored` lists name one of these entities.

## Fix Focus Areas
- sidecar/pii/alcatraz/masker.go[300-305]
- sidecar/daemon/daemon.go[1634-1641]
- sidecar/pii/alcatraz/alcatraz.go[352-364]

## Recommended Fix
In masker.go, when an unclaimed entity is in `NEREntities()` and the detector was built without NER, add " (PERSON, LOCATION, ORGANIZATION and NRP need the NER model: set the pii.ner block)" to the problem. Record on the Detector whether NER was loaded so this can be checked. In daemon.go's pii-rule check, add the same hint for NER entity names. One way is to expose a small helper on the plugin interface; another is to match against a shared list of NER entity names. Add tests that cover both rule paths.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Dismiss ↗ | View ↗


View medium (1)
6. Sidecar guide misleads model deployments 🔗 Cross-repo conflict ≡ Correctness
Description
NER.resolve now requires model files and an ORT-capable build for backend: ort, but
hoophq/documentation's Sidecar guide still says there are no build tags and PII configuration never
requires swapping the binary. An operator following that guidance with the unsuffixed image and
pii.ner encounters a startup failure rather than name masking.
Code

sidecar/pii/alcatraz/ner.go[R94-102]

+	case ner.BackendORT:
+		if !ORTCompiled {
+			return NER{}, fmt.Errorf("backend ort is not compiled into this build; build with CGO_ENABLED=1 -tags ORT, or set backend: go")
+		}
+		if n.ORTLibrary == "" {
+			n.ORTLibrary = DefaultORTLibrary
+		}
+		if _, err := os.Stat(n.ORTLibrary); err != nil {
+			return NER{}, fmt.Errorf("ort_library: %w", err)
Relevance

●●● Strong

Deployment documentation contradicts the new required model and ORT artifacts, matching recent
accepted documentation-correction patterns.

PR-#2058

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The external Sidecar guide explicitly promises configuration-only PII and no build tags. The new
loader rejects an unavailable ORT backend or library, and the PR adds separate model-bearing image
targets.

hoop -> documentation sidecar/pii/alcatraz/ner.go[82-103] Dockerfile.sidecar[525-529] External repo: hoophq/documentation, setup/configuration/hoop-sidecar/components.mdx [315-320]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The published Sidecar guidance contradicts the new model and build requirements for pii.ner.
## Fix Focus Areas
- /cross_repos/documentation/setup/configuration/hoop-sidecar/components.mdx[315-320]
- sidecar/pii/alcatraz/ner.go[82-103]
- Dockerfile.sidecar[525-529]
## Recommended Fix
Update the documentation repository's Sidecar guide to explain the pii.ner block, model-enabled image tags, required model files, and when the ORT build tag is necessary.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Dismiss ↗ | View ↗


Context sources
✅ Compliance rules (platform): 160 rules
✅ Skills: build-ticket
✅ Web pages:
  +2 more
✅ Cross-repo context — repo relationships
  Explored: repo: hoophq/documentation (sha: 9c5363c3) — View relationship
  Explored: repo: hoophq/alcatraz (sha: a1d14656) — View relationship
  Explored: repo: hoophq/internal-runtime-control (sha: f9e2bc6f) — View relationship
  Explored: repo: hoophq/hoop-benchmarks (sha: ec40a61e) — View relationship
  Explored: repo: hoophq/voight-kampff (sha: c6c71dc5) — View relationship
Review mode: Auto: 🧠 Deep: Broad, bug-dense feature spans runtime logic, configuration, CI, images, and compatibility.
Tip of the day
💡 Did you know, you can route each severity your way: inline, summary, both, or drop

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

qodo-code-review[bot]reviewed2d ago