Bug: HITL Approve click never reaches server — approved stays false, overrides stay empty (affects both anonymize_file and anonymize_text sessions)
Summary
After calling start_review(session_id) and clicking Approve in the in-chat review panel, the panel displays a "Review approved" confirmation dialog client-side. However, the server never records the approval: subsequent calls to start_review still return approved: false and overrides: { remove: [], add: [] }, and anonymize_file(..., review_session_id=...) returns status: "waiting_for_approval" indefinitely.
This blocks the entire HITL pipeline for both file-based and text-based sessions.
Environment
| Field |
Value |
| PII Shield version |
2.0.2 |
| Runtime |
Node.js v24.15.0 |
| Host |
Claude Desktop → Cowork mode (local agent session) |
| OS |
macOS (Apple Silicon, Darwin) |
| Model |
gliner-pii-base-v1.0 (~634 MB), installed via in-chat setup panel |
| Data dir |
~/.pii_shield (fallback) |
| NER status |
ner_ready: true, NER detection working correctly |
Reproducer
Minimal sequence (reproduced multiple times in one session, in two different modes):
Mode A — file input
1. anonymize_file(file_path: "/path/to/contract.docx")
→ returns status: "success", session_id: "S1"
2. start_review(session_id: "S1")
→ returns status: "review_ready", approved: false, overrides empty
→ review panel renders in chat with entity highlights
3. User clicks Approve in the panel
→ panel shows "Review approved" dialog client-side
4. anonymize_file(file_path: "/path/to/contract.docx", review_session_id: "S1")
→ ACTUAL: status: "waiting_for_approval" (loops indefinitely)
→ EXPECTED: status: "success" or "approved_no_changes"
Mode B — direct text input
1. anonymize_text(text: "Dear HR, I am writing to report ... Sophie Vandenberghe ...")
→ returns status: "success", session_id: "S2", entity_count: 6
2. start_review(session_id: "S2")
→ returns status: "review_ready", approved: false, source_filename: "inline_text"
3. User clicks Approve in the panel
→ panel shows "Review approved" client-side
4. start_review(session_id: "S2") (re-call to inspect server state)
→ ACTUAL: approved: false, overrides: { remove: [], add: [] }
→ EXPECTED: approved: true, overrides reflecting user's edits (or empty if no edits)
Observations
- The bug affects both session types created by
anonymize_file and anonymize_text, so it is not specific to the file-handling path.
- The panel UI is rendered (entities highlighted, Approve button visible, post-click dialog shown), so the iframe is loading correctly.
- The server-side state for the session never transitions to
approved: true, which is the gating condition the skill's pipeline relies on.
- The "Review approved" dialog the user sees client-side appears to be optimistic — the panel announces success before (or instead of) confirming the server actually persisted the override.
- Re-opening the panel via
start_review(session_id) on an already-"approved" session still returns approved: false — i.e., it's not a stale-cache issue in the panel; the server itself never saw the apply.
Likely cause (informed guesses, please verify)
The panel UI presumably calls apply_review_overrides(session_id, overrides) on the MCP server when the user clicks Approve. Most plausible failure modes:
apply_review_overrides is called via a JS-bridge path that this host doesn't expose to MCP Apps iframes. The panel may be using postMessage to a parent frame that, in Cowork, isn't routing the call back to the MCP server process. The skill's own documentation flags an analogous limitation: "that tool call is invisible to Claude on some hosts (known limitation)" — so a known-bad signal path may also be the cause of the server not seeing it.
- The tool call is being made but to the wrong endpoint (e.g., a stdio MCP connection vs. an HTTP transport, or to a different
session_id than the one in the panel state).
- The call is being made but failing silently. If
apply_review_overrides returns an error and the panel doesn't surface it, the dialog might still show "approved" client-side.
Suggested instrumentation to verify
- Add a server-side log line at the top of
apply_review_overrides handler — does the request ever arrive? Run with DEBUG=* or equivalent and check stdout/stderr captured by Claude Desktop's MCP logs.
- In the panel's Approve-click handler, log the result of the
apply_review_overrides call (success/error) to the panel iframe's console. The user can then open dev tools on the panel iframe to capture it.
- Add a
apply_review_overrides_attempted counter to list_entities output so users can see "the panel tried to apply N times" vs. "server received N applies", making it obvious where the signal is being lost.
Workaround used in this session
The skill defines a PII_SKIP_REVIEW=true environment variable that bypasses the HITL gate. Setting that lets the pipeline proceed without the review step, but obviously sacrifices the human-correction step that makes PII Shield trustworthy on real PII (the NER itself missed three person names and one mis-tagged date in our synthetic test text — exactly the kind of error the review step is designed to catch).
What's working well
So this isn't a "PII Shield is broken" report — it's specifically the panel↔server signal step:
- File auto-resolution (BFS of Downloads/Documents/Desktop) — worked first try on a non-standard Claude Desktop session path.
- GLiNER inference quality — caught 6 of 7 entity types correctly on a Belgian-context HR test message (email, location, IBAN, phone, ID number, plus one mis-fire on a date that the HITL step would catch).
- Session model + placeholder coalescing —
<EMAIL_1> appeared consistently for the same entity across multiple positions in the doc.
start_review panel rendering — the iframe loads, entities highlight correctly, buttons are interactive.
- Model setup panel — clean one-click Download → one-click Install flow worked end-to-end.
Bug: HITL Approve click never reaches server —
approvedstaysfalse,overridesstay empty (affects bothanonymize_fileandanonymize_textsessions)Summary
After calling
start_review(session_id)and clicking Approve in the in-chat review panel, the panel displays a "Review approved" confirmation dialog client-side. However, the server never records the approval: subsequent calls tostart_reviewstill returnapproved: falseandoverrides: { remove: [], add: [] }, andanonymize_file(..., review_session_id=...)returnsstatus: "waiting_for_approval"indefinitely.This blocks the entire HITL pipeline for both file-based and text-based sessions.
Environment
~/.pii_shield(fallback)ner_ready: true, NER detection working correctlyReproducer
Minimal sequence (reproduced multiple times in one session, in two different modes):
Mode A — file input
Mode B — direct text input
Observations
anonymize_fileandanonymize_text, so it is not specific to the file-handling path.approved: true, which is the gating condition the skill's pipeline relies on.start_review(session_id)on an already-"approved" session still returnsapproved: false— i.e., it's not a stale-cache issue in the panel; the server itself never saw the apply.Likely cause (informed guesses, please verify)
The panel UI presumably calls
apply_review_overrides(session_id, overrides)on the MCP server when the user clicks Approve. Most plausible failure modes:apply_review_overridesis called via a JS-bridge path that this host doesn't expose to MCP Apps iframes. The panel may be usingpostMessageto a parent frame that, in Cowork, isn't routing the call back to the MCP server process. The skill's own documentation flags an analogous limitation: "that tool call is invisible to Claude on some hosts (known limitation)" — so a known-bad signal path may also be the cause of the server not seeing it.session_idthan the one in the panel state).apply_review_overridesreturns an error and the panel doesn't surface it, the dialog might still show "approved" client-side.Suggested instrumentation to verify
apply_review_overrideshandler — does the request ever arrive? Run withDEBUG=*or equivalent and check stdout/stderr captured by Claude Desktop's MCP logs.apply_review_overridescall (success/error) to the panel iframe'sconsole. The user can then open dev tools on the panel iframe to capture it.apply_review_overrides_attemptedcounter tolist_entitiesoutput so users can see "the panel tried to apply N times" vs. "server received N applies", making it obvious where the signal is being lost.Workaround used in this session
The skill defines a
PII_SKIP_REVIEW=trueenvironment variable that bypasses the HITL gate. Setting that lets the pipeline proceed without the review step, but obviously sacrifices the human-correction step that makes PII Shield trustworthy on real PII (the NER itself missed three person names and one mis-tagged date in our synthetic test text — exactly the kind of error the review step is designed to catch).What's working well
So this isn't a "PII Shield is broken" report — it's specifically the panel↔server signal step:
<EMAIL_1>appeared consistently for the same entity across multiple positions in the doc.start_reviewpanel rendering — the iframe loads, entities highlight correctly, buttons are interactive.