Skip to content

fix(#727): mask ISO-8601 dates as dates, not phone numbers - #744

Merged
Weegy merged 4 commits into
byte5ai:mainfrom
sneumannb5:feat/727-csv-phone-date
Aug 19, 2026
Merged

fix(#727): mask ISO-8601 dates as dates, not phone numbers#744
Weegy merged 4 commits into
byte5ai:mainfrom
sneumannb5:feat/727-csv-phone-date

Conversation

@sneumannb5

@sneumannb5 sneumannb5 commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator

What

Closes #727 : ISO-8601 date (2026-07-02) masked as a phone surrogate. dedupSpans now breaks exact overlap ties by a documented, order-independent rule; CSV import skips date columns so stored data matches its declared type.

Why

Phone pattern grabbed the date's -07-02 tail, extension grew it to the full token, tying the date span — and the tie fell through to C0_PATTERNS insertion order, so phone won. On the dataset path that's permanent corruption at rest. Tiebreak is now native match length, then a fixed lexical order for determinism only.

Test plan

  • node --import tsx --test test/privacyPromptMask.test.ts test/datasetImport.test.ts — 60/60
  • tsc --noEmit on harness-plugin-privacy-guard — clean
  • C0 eval re-run — byte-for-byte unchanged (de/en 100%, es/fr/it 99.1% PASS, nl 89.0% FAIL pre-existing, precision 32/32)

Risk / blast radius

Shared path (chat masking + dataset import). Change affects only which surrogate type replaces a masked span, never whether it's masked — eval recall/precision unmoved. Documented residual: a bare-PII date column (birth_date) persists un-redacted; reversible date masking is a #430 follow-up.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

sneumannb5 and others added 4 commits August 19, 2026 09:37
…eck ratchet passes

The new dedupSpans lexical-fallback test destructured its pairs from an
inferred string[][], so under noUncheckedIndexedAccess both elements were
string | undefined and mk() rejected them (4x TS2345). tsx strips types
without checking, so a local test run stayed green while the byte5ai#573
test/scripts typecheck ratchet failed in CI.

Widen nothing: the pairs are declared as a readonly tuple array, the
assertions are unchanged.
… and dotted

30-06-2027 (the dashed form byte5ai#482 added for nl/fr) was broken by the exact
mechanism the issue reported for the ISO shape: the phone pattern's \b0
branch grabs its 06-2027 tail and word-boundary extension grows that span
over the whole token, tying the date span. Verified against the raw
detector: 30-06-2027 -> [[phone,06-2027],[date,30-06-2027]], the same
arrangement as 2026-07-02.

The issue only named the ISO form and the C0 eval cannot surface either,
because it scores span coverage rather than surrogate type. Pinning all
three separator styles in one loop keeps them from drifting apart.
@Weegy

Weegy commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Pushed two commits to this branch (maintainer edit) while prepping it for merge, plus a branch update from main.

1. fix(#727) — the required middleware (lint + typecheck + test) check was red.

Not the test run: the #573 test/scripts typecheck ratchet. test/privacyPromptMask.test.ts destructured its lexical-fallback pairs from an inferred string[][], so under noUncheckedIndexedAccess both elements were string | undefined and mk() rejected them — 4x TS2345, file not in baseline. The pairs are now a readonly (readonly [string, string])[]; assertions unchanged, baseline left at 406.

Worth flagging for next time: node --test via tsx strips types without checking them, so 60/60 locally and this failure are fully compatible. npm run typecheck:test is the one that catches it.

2. test(#727) — the bug was wider than the issue described.

The issue named only the ISO shape, but the dashed dd-mm-yyyy form that #482 added for nl/fr hits the identical mechanism. Straight from the raw detector:

2026-07-02  raw=[["phone","07-02"],["date","2026-07-02"]]
30-06-2027  raw=[["phone","06-2027"],["date","30-06-2027"]]   <- same arrangement
02.07.2026  raw=[["date","02.07.2026"]]                        <- never affected
2026-12-25  raw=[["date","2026-12-25"]]                        <- never affected

The trigger class is any date whose segment after a - starts with 0 (the phone pattern's \b0 branch). So 30-06-2027 was persisting as +49 30 55590000 too. I added it to the existing regression loop and confirmed it is not vacuous: narrowing that loop to only 30-06-2027 and restoring the pre-fix comparator still turns it red.

This also sharpens why RESULTS.md is right that the eval reproduces byte-for-byte — the eval scores span coverage, not surrogate type, so it is structurally blind to this entire class. Re-ran it independently and confirm your numbers: de/en 100%, es/fr/it 99.1% PASS, nl 89.0% FAIL (address-only, pre-existing since Run 2), precision 32/32 in every locale.

Verification on the rebased head

Gate Result
npm test (full middleware) 6756 tests, 6744 pass, 0 fail, 0 cancelled, exit 0
npm run typecheck / :test / :golden exit 0 (gated on exit code, not grep)
npm run lint exit 0
C0 eval re-run matches Run 2
Mutation checks nativeLen, lexical key, and the dataset date-skip each kill exactly their own test

One note on the mutation results: removing nativeLen kills only the "native match length is the decider" test — the first ISO test stays green because for date vs phone the lexical fallback also picks date. That second test is doing real work; it is what keeps the comparator honest.

Not changed, flagging instead: the module doc says the date-column residual is "tracked as a follow-up" to #430, but there is no such issue open. The decision itself is fine and AC #4 explicitly allows skip — it just isn't tracked anywhere yet. Worth opening one, since a birth_date column now persists in the clear where it previously (wrongly, but effectively) got redacted.

@Weegy
Weegy merged commit bb99b37 into byte5ai:main Aug 19, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Privacy Shield masks ISO-8601 dates as phone numbers — destroys date columns in CSV datasets at rest

2 participants