pixellint

Fixtures in CI, not a weekly HAR

Short answer

The HTTP 200 from the vendor will not fail the build. A linter that knows timestamp units will. That is the point of Pixellint as a CLI.

Validate a pixel Open the rulepack

What to store

1. A golden Purchase body per ads vendor you actually POST to. 2. A golden pixel URL per browser tag. 3. The cases that must fail: unhashed email, hashed IP, 13-digit Meta event_time, 13-digit GA4 timestamp_micros, missing event name, empty PostHog distinct_id, Segment track with no event, Mixpanel array with no properties.token. 4. Template fixtures marked as template so macros are legal. 5. Example.com addresses and documented sample hashes. Never a live HAR.

One fixture per bug you already shipped is how the suite grows. A single happy-path Purchase will not catch the refund path that omits transaction_id.

Commands

Run the binary in CI like you run any other linter. The pixellint repository ships a composite GitHub Action: uses aleksUIX/pixellint at a v* tag, with path (a fixture file) and kind (url, json or request). Pin the released version so the fixture suite and destination contracts stay reproducible.

pixellint validate url on fired or golden URLs. pixellint validate json on CAPI, MP, capture, and track bodies. --rulepack scopes the vendor. --state template is for unexpanded macros. Exit 0 when there is no error-severity finding. Warnings do not fail the process. Exit 1 when any error finding is present. Exit 2 is a usage problem. Same engine as the playground.

pixellint validate url @fixtures/meta-pixel.txt --rulepack vendor/meta
pixellint validate json @fixtures/meta-capi.json --rulepack vendor/meta-conversions-api
pixellint validate json @fixtures/ga4-mp.json --rulepack vendor/google-analytics
pixellint validate url @fixtures/g-collect.txt --rulepack vendor/google-analytics-collect

Complete captures preserve the transport context.

Use kind request with a JSON capture containing url, method, headers and an optional raw string body. The envelope makes method, MIME, credential placement and linked payload checks observable. A bare JSON body cannot establish which endpoint, method or headers carried it. A bare URL retains its existing query contracts.

Meta's http_url_presence_overrides permits its explicit HTTP authentication alternatives to satisfy missing query access_token presence on matching captures. The override does not disable checks on a submitted blank or malformed query/form value. Unrelated endpoints retain their route mismatch and ordinary URL behavior.

Multipart UTF-8 text fields can reuse the existing body rules. File parts, unsupported charsets, ambiguous headers, compressed binary bodies and captures above the local limits remain informative. The engine does not fabricate missing values when it could not observe the representation.

pixellint validate request @fixtures/meta-request.json --rulepack vendor/meta-conversions-api
pixellint validate request @fixtures/amplitude-identify-multipart.json --rulepack vendor/amplitude-identify

Encoded fixtures retain their source representation.

FTrack's percent_encoded_json decodes one inner encodeURIComponent layer after the outer transport decode. Percent escapes and UTF-8 are strict, and a literal plus remains plus. HubSpot's query_params_json reads a nested form query: plus becomes a space, repeated literal names become ordered arrays, and bracketed names remain literal keys. These codecs do not share interchangeable plus semantics.

Matomo bulk captures opt into native_map matomo_php8 for the pinned server reader profile. check_chronological_order compares explicit parseable cdt timestamps and warns on backward order. Missing or unresolved timestamps receive no inferred clock. Unsupported reader and precision behavior remains informative; this profile is not a complete PHP runtime.

Keep a source-valid fixture beside each negative boundary. SDK-derived expectations can be warnings even when a malformed payload is useful to diagnose. Passing a producer warning oracle does not establish backend acceptance or account authorization.

Pin the action, or call the binary

Any CI that can run a binary can run this. The composite action downloads the release tarball for the runner OS and architecture, then runs pixellint validate on the path you named. A Makefile or a bash step that calls the same binary is the same check. A step that only curls the vendor and asserts 200 is the incident the CLI exists to prevent.

Keep the commands next to the fixtures so a PR that changes the hasher shows a red job. Weekly HAR archaeology does not catch the merge that shipped Date.now() to Meta.

#!/bin/sh
set -e
pixellint validate url @fixtures/pixel.txt --rulepack vendor/meta
pixellint validate json @fixtures/capi.json --rulepack vendor/meta-conversions-api

PII in the repo

1. Fixtures use example.com and documented sample SHA-256 digests. 2. Never commit a live HAR from production. 3. Never commit access_token, api_secret, or writeKey that work. 4. Redact cookies. 5. Template fixtures may contain ${macro} or [CACHEBUSTER]; mark --state template so core privacy rules stay quiet on unfilled slots.

A golden body that still contains a real buyer email is a leak in git history. Rotate if it happened. See HAR files.

What CI will not catch

A GTM trigger that only fails on one locale. A published container that does not match the preview you stared at. A destination mapping that hashes the wrong field after Segment 200s. Pair fixtures with a sampled export and with a post-publish Network pass on a clean browser.

Pixellint is not affiliated with Meta or Google. The CLI checks the artifact you give it. It does not log into Ads Manager. That is a feature.

Check the artifact

Paste the pixel URL or JSON body into the playground. Same engine as pixellint validate. Artifacts you test may be stored; see privacy.