[0.391] - 2026-05-07

Features

Other

Tagged for release. v0.390

[0.390] - 2026-05-07

Features

Other

Tagged for release. v0.380

[0.380] - 2026-05-07

Features

Other

Tagged for release. v0.370

Performance

[0.370] - 2026-05-06

Bug Fixes

Other

Three must-fix changes plus expanded test coverage that came out of the deep review on PR #35:

Test coverage added for previously-uncovered paths:

MANIFEST: add lib/GDPR/IAB/TCFv2/Validator.pm, lib/GDPR/IAB/TCFv2/Validator/Result.pm, and t/06-validator.t (missing from the original Phase 2 commits).

t/06-validator.t now has 11 subtests (was 4); 8287 tests pass overall (was 8280). perlcritic + perltidy clean.

flexible_purpose_ids is now a flat ArrayRef[Int]. The default legal basis for each flexible purpose is derived structurally:

This mirrors the IAB GVL vendor-entry schema 1:1 (purposes / legIntPurposes / flexiblePurposes) and removes the mixed-shape parameter that accepted both scalars and {purpose_id, default_is_li} hashrefs. Constructor builds a private _flexible_set hash for O(1) lookup; _check_consent_purposes and _check_li_purposes dispatch to is_vendor_allowed_for_flexible_purpose when the purpose is flexible.

The standalone _check_flexible_purposes helper is removed and its call site in _run_validation deleted. Failure reasons now read "(consent)" or "(legitimate interest)" -- the basis used for the check -- regardless of whether the purpose was flexible.

Tests in t/06-validator.t updated for the new shape:

Two configurations are now caught at construction time rather than silently producing strange validation outcomes:

  1. A purpose listed in both consent_purpose_ids and legitimate_interest_purpose_ids (GVL semantics treat those as mutually exclusive vendor declarations).
  2. A purpose listed in flexible_purpose_ids but neither of the other two lists (no default basis can be derived).

Both croak with explicit messages naming the offending purpose ID.

Maps a parsed IAB GVL vendor entry hashref to the constructor arguments Validator->new expects. Field aliases:

id -> vendor_id purposes -> consent_purpose_ids legIntPurposes -> legitimate_interest_purpose_ids flexiblePurposes -> flexible_purpose_ids

Returns a list (key-value pairs) so callers can splat into the constructor and add extras:

my $v = GDPR::IAB::TCFv2::Validator->new( GDPR::IAB::TCFv2::Validator::from_gvl_vendor_entry($entry), strict => 1, );

Croaks only on missing 'id'. Missing list fields default to empty arrayrefs per the GVL schema (a vendor may legitimately have no LI or flexible purposes).

Document the structural derivation of default basis (membership in consent_purpose_ids vs legitimate_interest_purpose_ids), the construction-time coherence checks that croak on incoherent inputs, and the new from_gvl_vendor_entry public function in a new =head1 FUNCTIONS section. SYNOPSIS updated to use the flat-int flexible_purpose_ids shape and shows the from_gvl_vendor_entry splat idiom.

Optional min_policy_version constructor arg (positive int; undef = no check) gates validation by the parser's policy_version(). The check runs FIRST in the rule order -- before disclosed-vendor and the purpose checks -- because it's the most fundamental reason to reject a TC string. In fail-fast mode it short-circuits the rest; in validate_all mode the other rules still run and accumulate.

Failure reason: 'TC string policy version X is below required minimum Y'. Per-call override via validate($tc, min_policy_version => N) is supported, mirroring the other override-able knobs.

Path D for the dump subcommand:

Tests in t/09-predicates.t and t/10-cli-iabtcfv2.t are updated for the new defaults; POD for the dump subcommand documents the new flag and the new --quiet semantics.

Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

Implement the previously-stubbed iabtcfv2 validate subcommand. It wraps GDPR::IAB::TCFv2::Validator with a CLI surface that mirrors the validator's constructor:

-v|--vendor-id ID (required) -C|--consent-purposes 1,2,3 -L|--legitimate-interest-purposes 1,2,3 -F|--flexible-purposes 1,2,3 -d|--check-disclosed-vendors -s|--strict -m|--min-policy-version N -a|--all (validate_all instead of fail-fast)

Output is one JSON object per TC string by default, or human-readable lines with -t|--text. Failure shape uses a singular reason in fail-fast mode and a plural reasons array in --all mode.

Pipeline ergonomics match the dump subcommand: --json-array, --ignore-errors (parse only), --fail-fast (parse OR validation), --errors-to-stderr, --enable-warnings (off by default), --quiet (suppresses stdout, preserves exit code).

Exit codes: 0 = all valid; 1 = at least one parse or validation failure; 2 = bad CLI usage (missing --vendor-id, incoherent purpose lists, --text with --json-array).

Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

Three new phases tracked after the CMP validator (Phase 5):

Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

The from_gvl_vendor_entry helper was added speculatively in this branch but its only useful caller — feeding parsed GVL JSON into the validator — depends on the GVL infrastructure that lands in Phase 6 (see TODO.md). Removing it now keeps Phase 2's API surface focused on what is actually exercised by the validate subcommand.

The helper, its POD, and its dedicated subtest will be reintroduced together with the rest of the GVL surface in Phase 6.

Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

xt/tidy.t (Test::PerlTidy under .perltidyrc) was failing on the 'Perl latest on ubuntu' CI job for PR #54. Cosmetic-only reflow: column alignment between consecutive my() lines, splitting long arg-lists across multiple lines, breaking some closing parens onto their own line. No assertions changed.

Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

The validate subcommand's "Exit codes" section uses em-dashes in its prose (B<0> — every input TC string was parsed...). Without an explicit =encoding directive, Test::Pod warns "Non-ASCII character seen before =encoding in '—'. Assuming UTF-8" and t/99-pod.t fails on smokers running Test::Pod with strict POD validation.

Add =encoding utf8 right after END so the entire POD block is declared as UTF-8. No prose changes.

Co-authored-by: Claude Opus 4.7 noreply@anthropic.com

Tagged for release. v0.360

[0.360] - 2026-05-06

Features

Other

Tagged for release. v0.352

[0.352] - 2026-05-06

Bug Fixes

Other

Tagged for release. v0.351

[0.351] - 2026-05-06

Bug Fixes

Documentation

Other

Tagged for release. v0.350

[0.350] - 2026-05-06

Bug Fixes

Features

Other

Both _parse_bitfield and _parse_range_section previously passed the full core_data bit-length as data_size while passing a sliced $data. This made the BitField/RangeSection size validation lenient for truncated cores: malformed inputs would stumble forward and croak deep in _parse_publisher_section with a misleading "missing 'core_data'" error instead of failing fast at the bitfield boundary.

Align with the pattern already used in _parse_vendor_bitfield_or_range so data_size faithfully describes what the callee receives.

_parse_vendor_bitfield_or_range now accepts an expected_segment_type argument and croaks if the payload header disagrees. Brings parity with PublisherTC->Parse and protects against future refactors that might bypass _decode_tc_string_segments routing.

Per IAB TCF v2 spec, MaxVendorId=0 means the field is unused. Previously, a segment with max_id=0 and IsRangeEncoding=1 would fall into RangeSection->Parse and either parse spurious range entries or croak from the 31-byte minimum-size guard.

Now we return an empty BitField immediately, preserving has_vendor_disclosure() semantics while making contains() return falsey for any vendor id.

Phase 1 added several public methods (is_v22_plus, is_v23, disclosed_vendor, has_vendor_disclosure, allowed_vendor, has_publisher_restrictions) and two Parse options (TCF v2.3 strict-mode behaviour, vendor_id JSON filter), all documented in the .pm POD but missing from README.md. Regenerated README.md via the documented workflow (pod2markdown lib/GDPR/IAB/TCFv2.pm > README.md).

Also removed two leaked lines from a prior merge commit message (# Conflicts: / #\tCHANGELOG.md) that git-cliff had swept into the v0.350 "Other" section.

The previous "FOR RELEASE MANAGER" section was a 12-line stub. Replaced with a comprehensive guide covering:

Added =encoding utf8 so podchecker accepts the em-dashes used throughout the new section.

Adds a "Pre-release review" subsection that calls out two reasons the draft GitHub Release pattern doesn't actually buy us anything on this project:

  1. PAUSE upload is irreversible, so reviewing the GitHub Release before it ships only protects the rendered notes page, not the CPAN-distributed artifact.
  2. release.yml hard-codes draft: false in the softprops step and listens only on push.tags, so naively adding a draft step would either close the review window in 3 minutes or skip release.yml entirely (API-created tags don't trigger push events).

Points readers at the existing PR-based vanilla-git path in step 8 as the actual review gate, and notes the workflow tweak required if someone later wants to support drafts properly.

[0.340] - 2026-05-05

Other

[0.330] - 2026-05-05

Bug Fixes

Other

[0.320] - 2026-05-05

Bug Fixes

Documentation

Other

[0.310] - 2026-05-05

Features

[0.300] - 2026-05-05

Bug Fixes

Documentation

Other

[0.203] - 2025-04-21

Bug Fixes

Other

try fix windows

install git and curl

force install linux in older versions

try fix images

[0.202] - 2025-04-21

Other

fix branch again

fix branch again

Tagged for release. v0.201

[0.201] - 2023-12-20

Other

Tagged for release. v0.200

[0.200] - 2023-12-17

Bug Fixes

Other

This reverts commit ae7274e49ad0b767b158beadd9ce73e07a5eb836.

Remove char

Remove char

Tagged for release. v0.100

[0.100] - 2023-12-15

Bug Fixes

Other

Tagged for release. v0.084

Refactor

[0.084] - 2023-12-14

Other

Tagged for release. vv0.083

[0.083] - 2023-12-13

Bug Fixes

Other

This reverts commit e5fb435f3d78beb07ef44b434282724c4f0270ec.

This reverts commit dca6f2f25344bbd226f8ef79780414f57b378f1a.

Try fix link to method

performance improvement on range objects

[0.082] - 2023-12-12

Bug Fixes

Other

This reverts commit af62cf3873f673bcc0f790a2523a036042ec34d6.

Refactor

[0.081] - 2023-12-11

Bug Fixes

Other

[0.08] - 2023-12-10

Bug Fixes

Other

add coc

add badges

add new bagdes

retry perlcritic

try different approach

try again

update badges

remove appveyor

update badges

rename file

fix 2

fix linux

improve linux tests

add tests on macos

add tests on windows

add perldity

fix typo in badges

fix pod

fix typo

try fix git config

add coveralls repo token on secret

update action

[0.07] - 2023-12-07

Bug Fixes

Other

This reverts commit c56400f41b1f71f516d999786ec88f1144945f48.

Fix pod

Fix pod

[0.06] - 2023-12-06

Other

add coveralls

rename

[0.051] - 2023-12-05

Bug Fixes

Other

[0.05] - 2023-12-05

Other

[0.0.5] - 2023-12-05

Bug Fixes

Other

[0.0.4] - 2023-12-04

Bug Fixes

Other

[0.0.3] - 2023-12-04

Other

[0.0.2] - 2023-12-03

Other

[0.0.1] - 2023-12-02

Other