| name | policystack-instrument |
| description | Find un-annotated data collection and data egress in a codebase and add PolicyStack `collecting()` / `sharing()` / `thirdParty()` / `defineCookie()` call sites so the generated policy reflects what the code actually does. |
policystack-instrument
Make the policy reflect reality: annotate real data flows at their call sites
so the @policystack/vite build scans them into the config. The markers are
identity functions — they return their value unchanged and have no runtime
effect; the build reads them statically.
The markers (from @policystack/sdk)
collecting(category, value, labels) — wrap a value at the point it is
stored. labels maps each field to a human label, or Ignore to
exclude it (e.g. password: Ignore).
sharing(key, recipient, value) — wrap an outbound payload at a data
egress edge (the CCPA sell/share signal).
thirdParty(name, purpose, policyUrl?) — declare a vendor exists; place
next to its initialisation.
defineCookie(category) — declare a consent category at its use site.
Constraint: category, key, recipient, name and the label
map must be string / object literals. Non-literal arguments are skipped by
the static scanner (it emits a build diagnostic — they are not silently lost,
but no annotation is produced). Never pass a variable.
What counts as an un-annotated flow
The scanner already recognises common cookie/vendor surfaces (document.cookie,
js-cookie, cookies-next, react-cookie, server set-cookie, and
~30 known vendor imports) and flags any that are not gated by
<ConsentGate>, a consent.has(...) check, or an accept/set helper. Look
for, in priority order:
- Persisted user input / DB writes with no nearby
collecting().
- Outbound requests carrying user data (analytics, CRM, webhooks) with no
sharing() and no thirdParty().
- Cookie /
localStorage writes and tracker <script>/SDK init with no
defineCookie() and not consent-gated.
Procedure
- Grep the data layer and network calls; list candidate flows.
- Add the smallest correct marker at each call site, reusing presets where
they fit so labels stay consistent:
- Data categories:
DataCategories.AccountInfo, DataCategories.Communications, DataCategories.DeviceInfo, DataCategories.LocationData, DataCategories.PaymentInfo, DataCategories.SessionData, DataCategories.UsageData
- Lawful bases:
LegalBases.Consent, LegalBases.Contract, LegalBases.LegalObligation, LegalBases.LegitimateInterests, LegalBases.PublicTask, LegalBases.VitalInterests
- Retention:
Retention.AsRequiredByLaw, Retention.NinetyDays, Retention.OneYear, Retention.ThirtyDays, Retention.ThreeYears, Retention.UntilAccountDeletion, Retention.UntilSessionExpiry
- Vendors:
Providers.AWS, Providers.Auth0, Providers.Clerk, Providers.Cloudflare, Providers.Datadog, Providers.GoogleAnalytics, Providers.LemonSqueezy, Providers.Loops, Providers.Mixpanel, Providers.Paddle, Providers.PayPal, Providers.Plausible, Providers.PostHog, Providers.Postmark, Providers.Resend, Providers.SendGrid, Providers.Sentry, Providers.Stripe, Providers.Vercel
- Compliance bundles:
Compliance.CCPA, Compliance.GDPR, Compliance.UK_GDPR
- Ensure every newly scanned category has a matching
data.context /
cookies.context entry in policystack.ts (the typed config enforces
this).
- Run
policystack-audit to confirm the config still validates.