Repository navigation
feat(rewrite): classify entity labels and enforce sensitivity policy - #333
asteier2026 wants to merge 27 commits into
Conversation
[High impact] The PR appears safe to merge; the skill-test response mismatch is fixed. SummaryThe PR separates entity classification from sensitivity decisions and reconstructs entity identities from stable IDs.
Reviews (18) · Last reviewed commit: "fix(tests): return compact dispositions ..." · Reviewed by Greptile |
|
The DataDesigner constraint matches current main (>=0.9.4,<0.10). This PR does not intend to change the supported DataDesigner versions. It is mentioned above in the bot summary because I did actually accidentally change it at one point and then reversed the change. |
|
Some recorded-run evidence for Short count. On one input row (27 detected entities plus latent ones), the disposition model left out a detected Two questions:
The compact format cuts about 55% of tokens per entry on the recorded dispositions (106 to 48 mean), which matches your figure. The recorded omissions, though, happened well below the output ceiling (30 entries at 16384, 5-18 at 4096), so the format alone may not remove them, as the plan already notes. If useful, I can add a parametrized test to |
|
Thanks—this is useful regression material. The intended behavior for incomplete coverage is currently to fail the record rather than continue with partial protection decisions. Returning a row with needs_human_review would require a defined fallback, which this change doesn’t add. I agree that compact output cannot detect a decision assigned to the wrong otherwise-valid ID; I’ll document that limitation. The recorded short-count fixture would be useful, and the equal-count fixture can document acceptance by ID without implying semantic correctness. I added all the necessary tests. |
Breaks after merging with main
Design question
Behavior worth recording
Lower priority
Earlier threads The Unicode line-split issue, the import suppressions (now covered by #357), the duplicate and missing custom-label classifications, and the rewrite-docs definition of sensitivity are all addressed on this head. The question about drop-or-flag on incomplete coverage is answered: the record is failed, the tradeoff is documented in the plan, and failed-record propagation is tested through the real adapter. The plan-retention question remains open. |
041a260 to
6347707
Compare
Resolved: 1 (the disposition columns now use JSON storage), 2 (the unused finalizer requirement is removed), 3 ( New (124d3c8, e6bac99), non-blocking
Note for the record: fixed leakage-mass thresholds no longer separate the presets
So with this PR on main, either proposal would make the Unchanged from the previous review
The DCO check fails because commit fd78a76 has no |
|
|
Code changes made Nov 11: Updated the branch with main and fixed compatibility issues.
Made inconsistent retention decisions recoverable. Instead of failing the record:
Separated sensitivity from the requirement to protect an entity.
Made low-sensitivity explanations optional for validation. The prompt still requests low_sensitivity_reason for every low rating, but a missing, null, or blank reason no longer fails the record. Confirmed privacy questions follow the protection decision.
Updated documentation, plans, and regression tests to reflect those policies. Broadened the sensitivity prompt’s opening to cover linkage to people, organizations, records, systems, and other protected assets, plus disclosure of credentials, access details, and proprietary information. Fixed model-selection defaults: entity_classifier follows disposition_analyzer only when the user explicitly overrides the disposition analyzer. Unrelated model overrides preserve the bundled classifier default, and an explicit classifier selection takes priority. Moved custom-label classification to once per dataset: both rewrite paths classify custom labels together and reuse their categories across records. Default labels remain classified locally, and sensitivity is still assessed per record. Commit 37e743b makes retention corrections consistent and visible:
|
…sposition Adds EntityClassificationWorkflow: a per-document step that resolves each non-latent entity label's direct/quasi-identifier category from a manually curated default table, falling back to an LLM call only for custom labels outside that table. Wires the result into the sensitivity disposition prompt as input so it stops re-deriving category itself, removing a source of prompt overload and sensitivity/combined_risk_level conflation. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: asteier2026 <asteier@nvidia.com>
Signed-off-by: asteier2026 <asteier@nvidia.com>
Signed-off-by: asteier2026 <asteier@nvidia.com>
Signed-off-by: asteier2026 <asteier@nvidia.com>
Restore the meaning-unit extraction prompt and block builder in
qa_generation.py to their main versions, and drop the redundant
disposition-coverage gates on the privacy QA and rewrite block
generators (the verified _sensitivity_disposition column is already
produced after the coverage check, so consumers cannot run earlier).
Remove rewrite rule 6 ("Change supporting context as needed so protected
latent entity values cannot be reliably inferred") from the rewrite
prompt. The protection_reason removal in rewrite_generation.py is kept
because the schema no longer carries that field.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Signed-off-by: asteier2026 <asteier@nvidia.com>
Signed-off-by: asteier2026 <asteier@nvidia.com>
Signed-off-by: asteier2026 <asteier@nvidia.com>
Signed-off-by: asteier2026 <asteier@nvidia.com>
…sions Signed-off-by: asteier2026 <asteier@nvidia.com>
Signed-off-by: asteier2026 <asteier@nvidia.com>
Signed-off-by: asteier2026 <asteier@nvidia.com>
Signed-off-by: asteier2026 <asteier@nvidia.com>
…empt Signed-off-by: asteier2026 <asteier@nvidia.com>
Signed-off-by: asteier2026 <asteier@nvidia.com>
Signed-off-by: asteier2026 <asteier@nvidia.com>
Signed-off-by: asteier2026 <asteier@nvidia.com>
Signed-off-by: asteier2026 <asteier@nvidia.com>
Signed-off-by: asteier2026 <asteier@nvidia.com>
Signed-off-by: asteier2026 <asteier@nvidia.com>
Signed-off-by: asteier2026 <asteier@nvidia.com>
Signed-off-by: asteier2026 <asteier@nvidia.com>
Signed-off-by: asteier2026 <asteier@nvidia.com>
Signed-off-by: asteier2026 <asteier@nvidia.com>
Signed-off-by: asteier2026 <asteier@nvidia.com>
Signed-off-by: asteier2026 <asteier@nvidia.com>
52b2a8f to
ef5dedd
Compare
| """ | ||
|
|
||
| model_config = ConfigDict(use_enum_values=True) | ||
| model_config = ConfigDict(use_enum_values=True, extra="forbid") |
There was a problem hiding this comment.
The new compact schema rejects extra fields, but tests/skills/stub_endpoint.py::_disposition still returns the old full response, including source, category, entity_label, entity_value, protection_reason, and combined_risk_level. The skill tests send rewrite requests through this endpoint, so every disposition is rejected and the tests cannot produce their expected rows.
Update the stub to read the numbered inventory and return only the four compact fields.
Signed-off-by: asteier2026 <asteier@nvidia.com>
Signed-off-by: asteier2026 <asteier@nvidia.com>
binaryaaron
left a comment
There was a problem hiding this comment.
agent (
review-pr): Re-check at head dde3645. The findings from the previous comment are fixed. Nothing here blocks merge.
Optional: log the low override in normal mode
_verify_disposition_coverage (sensitivity_disposition.py:416-419) sets every low decision to leave_as_is, including one where the model picked a protective method. That matches the documented normal-mode policy. Unlike the other corrections, though, it logs nothing. A warning here, plus a test for a low + protective decision without a reason, would make the case visible if it happens.
For the record, not merge conditions
- Thresholds.
low=0.3now applies only understrict_entity_protection. The sensitivity policy changed from "materially helps an adversary link the document to, or recognize, the subject" to "could identify or enable linkage to a person, organization, record, system, or other protected asset, or expose confidential information". That is a lower bar over a wider scope. It will probably raise ratings beyond the earlier comparison, which was measured on the older prompt. The threshold re-derivation in #379 should use this head's prompt. - Classification failures. A classification response that is still invalid after its correction attempt now fails the whole run (
classify_datasetraisesAnonymizerWorkflowError), where before it failed only the affected records. The docs say so; please confirm it is intended.
Related Documentation
More information about the problem, solution and testing that was done can be found here.
Summary
Sensitivity disposition previously asked one model call to classify labels, assess risk, and echo entity identities without enforcing complete inventory coverage. This change resolves default labels locally, classifies custom explicit labels separately, and requires exactly one decision for each supplied explicit or latent entity.
entity_classifierrole. Skip the classifier call when no custom labels are present; omitted role selections reuse the effective disposition analyzer. Local categories cannot be overridden by the model.leave_as_isand an explanation; protected latent entities usesuppress_inference. Remove the separate combined-risk field.(value, label)pair and latent entry. Request compact decisions containing only ID, sensitivity, method, and low-sensitivity reason; reconstruct identity and category from the supplied inventory. Reject incomplete, duplicate, or reordered IDs.>=0.9.4,<0.10).Type of Change
Contributor Checklist
Validation
make check: passed (format, lint, type checking, lockfile, copyright).make docs-build: passed (strict mode).Documentation and Artifacts