plainstamp 0.7.7 → 0.7.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/README.md +32 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lookup.d.ts +10 -0
- package/dist/lookup.d.ts.map +1 -1
- package/dist/lookup.js +47 -18
- package/dist/lookup.js.map +1 -1
- package/docs/guides/eu-ai-act-article-50-builder-guide.md +354 -0
- package/docs/guides/fcc-tcpa-ai-voice-robocall-builder-guide.md +314 -0
- package/docs/guides/fda-pccp-aiml-medical-device-builder-guide.md +333 -0
- package/docs/guides/texas-traiga-hb-149-builder-guide.md +321 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -16,6 +16,25 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
16
16
|
|
|
17
17
|
Distribution is **npm-only**. Source remains in the operating organization's private repository; there is no public source repository host. Contact channel for issues, accuracy reports, security reports, and contribution proposals is **helpfulbutton140@agentmail.to** (see `docs/CONTRIBUTING.md`, `docs/SECURITY.md`).
|
|
18
18
|
|
|
19
|
+
## [0.7.9] — 2026-05-09
|
|
20
|
+
|
|
21
|
+
### Improved (validate-disclosure precision)
|
|
22
|
+
|
|
23
|
+
- `validateDisclosure` now matches signals at word boundaries instead of as substrings — fixes a false-positive class where, for example, "preconsenting" matched the signal "consent". Tokens in the candidate are split on non-word characters; signals must appear as whole tokens.
|
|
24
|
+
- Returns a new `elements` field with per-element detail: `element_id`, `found`, `confidence` (`high` | `medium` | `missing`), and `matched_signals` (the tokens that matched). Confidence bands:
|
|
25
|
+
- **high**: an id-derived signal matched, OR ≥ 2 body-derived signals matched.
|
|
26
|
+
- **medium**: exactly 1 body-derived signal matched.
|
|
27
|
+
- **missing**: no signals matched.
|
|
28
|
+
- The existing `passes` and `missing_elements` fields are unchanged (backwards-compatible). Callers that want richer detail can read `elements`; callers that don't can continue treating the report as a binary check.
|
|
29
|
+
- Tests: 66/66 passing (63 baseline + 3 new — word-boundary, confidence reporting, missing-confidence on signal-free input).
|
|
30
|
+
|
|
31
|
+
## [0.7.8] — 2026-05-09
|
|
32
|
+
|
|
33
|
+
### Documentation
|
|
34
|
+
|
|
35
|
+
- README adds a "Builder's guides" section above the rule corpus listing, organized by compliance vertical (financial services, healthcare, employment, voice agent, EU, state-specific). Twelve long-form guides linked, each grounded in regulator-published source text. The guides index also lives at https://plainstamp.pages.dev/guides/.
|
|
36
|
+
- No code changes; npm publish refreshes the README rendered on npmjs.com.
|
|
37
|
+
|
|
19
38
|
## [0.7.7] — 2026-05-09
|
|
20
39
|
|
|
21
40
|
### Fixed (URL-monitor stabilization, round 3 — JSF random ids)
|
package/README.md
CHANGED
|
@@ -154,6 +154,38 @@ const reports = validateDisclosureForQuery(
|
|
|
154
154
|
);
|
|
155
155
|
```
|
|
156
156
|
|
|
157
|
+
## Builder's guides
|
|
158
|
+
|
|
159
|
+
Long-form, citation-grounded guides organized by compliance vertical.
|
|
160
|
+
Each guide walks through scope, required elements, common failure
|
|
161
|
+
patterns, and a minimum-viable-compliance checklist.
|
|
162
|
+
|
|
163
|
+
**Financial services AI**
|
|
164
|
+
- [CFPB Circular 2023-03 — adverse-action notices for AI credit decisions](https://plainstamp.pages.dev/guides/cfpb-circular-2023-03-ai-adverse-action-builder-guide/)
|
|
165
|
+
- [FINRA RN 24-09 — AI in customer communications](https://plainstamp.pages.dev/guides/finra-rn-24-09-ai-customer-communications-builder-guide/)
|
|
166
|
+
|
|
167
|
+
**Healthcare AI**
|
|
168
|
+
- [HHS Section 1557 (Patient Care Decision Support Tools)](https://plainstamp.pages.dev/guides/hhs-section-1557-pcdst-builder-guide/)
|
|
169
|
+
- [FDA PCCP for AI/ML medical devices](https://plainstamp.pages.dev/guides/fda-pccp-aiml-medical-device-builder-guide/)
|
|
170
|
+
- [Texas TRAIGA HB 149 — government and healthcare tracks](https://plainstamp.pages.dev/guides/texas-traiga-hb-149-builder-guide/)
|
|
171
|
+
|
|
172
|
+
**Employment AI**
|
|
173
|
+
- [EEOC Title VII AI selection procedures](https://plainstamp.pages.dev/guides/eeoc-title-vii-ai-employment-builder-guide/)
|
|
174
|
+
- [NYC Local Law 144 (AEDT)](https://plainstamp.pages.dev/guides/nyc-local-law-144-aedt-builder-guide/)
|
|
175
|
+
|
|
176
|
+
**Voice agent / outbound calling**
|
|
177
|
+
- [FCC TCPA AI-voice robocall ruling](https://plainstamp.pages.dev/guides/fcc-tcpa-ai-voice-robocall-builder-guide/)
|
|
178
|
+
|
|
179
|
+
**EU compliance**
|
|
180
|
+
- [EU AI Act Article 50](https://plainstamp.pages.dev/guides/eu-ai-act-article-50-builder-guide/)
|
|
181
|
+
|
|
182
|
+
**State-specific deep dives**
|
|
183
|
+
- [California bot disclosure (B&P § 17941)](https://plainstamp.pages.dev/guides/california-bot-disclosure-bp-17941-builder-guide/)
|
|
184
|
+
- [Colorado AI Act SB 24-205](https://plainstamp.pages.dev/guides/colorado-ai-act-sb-24-205-builder-guide/)
|
|
185
|
+
- [EU AI Act Article 50 — chatbot disclosure (focused)](https://plainstamp.pages.dev/guides/eu-ai-act-article-50-chatbot-disclosure/)
|
|
186
|
+
|
|
187
|
+
[See all guides →](https://plainstamp.pages.dev/guides/)
|
|
188
|
+
|
|
157
189
|
## How matching works
|
|
158
190
|
|
|
159
191
|
- **Jurisdiction.** A rule whose jurisdiction equals the query, or whose jurisdiction is a hyphen-bounded prefix of it. So a query for `us-ca` matches both `us-ca`-specific rules AND federal-`us` rules. A query for `us` does NOT match `us-ca`-specific rules — federal-only.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { lookup, generateDisclosureText, validateDisclosure, } from "./lookup.js";
|
|
1
|
+
export { lookup, generateDisclosureText, validateDisclosure, type ElementReport, } from "./lookup.js";
|
|
2
2
|
export { loadBundledRules, loadRulesFromPath, setBundledRules, } from "./rules-loader.js";
|
|
3
3
|
export { computeCoverageMatrix, renderCoverageMarkdown, renderCoverageCsv, type CoverageMatrix, type CoverageCell, } from "./coverage.js";
|
|
4
4
|
export type { DisclosureRuleT, RuleSetT, LookupQueryT, LookupResultT, ChannelT, UseCaseT, SeverityT, JurisdictionIdT, DisclosureElementT, } from "./schema.js";
|
|
@@ -26,6 +26,7 @@ export declare function validateDisclosureForQuery(query: LookupQueryT, candidat
|
|
|
26
26
|
rule_id: string;
|
|
27
27
|
passes: boolean;
|
|
28
28
|
missing_elements: string[];
|
|
29
|
+
elements: import("./lookup.js").ElementReport[];
|
|
29
30
|
warning: string;
|
|
30
31
|
}[];
|
|
31
32
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,GAChB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,iBAAiB,EACjB,KAAK,cAAc,EACnB,KAAK,YAAY,GAClB,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,eAAe,EACf,QAAQ,EACR,YAAY,EACZ,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,eAAe,EACf,kBAAkB,GACnB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,OAAO,EACP,OAAO,EACP,QAAQ,EACR,cAAc,EACd,WAAW,EACX,iBAAiB,EACjB,cAAc,EACd,OAAO,GACR,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,QAAQ,EACR,cAAc,EACd,KAAK,iBAAiB,EACtB,KAAK,aAAa,GACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,YAAY,EACZ,UAAU,EACV,mBAAmB,EACnB,SAAS,EACT,UAAU,EACV,YAAY,EACZ,gBAAgB,EAChB,qBAAqB,EACrB,gBAAgB,EAChB,wBAAwB,EACxB,WAAW,EACX,gBAAgB,GACjB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,OAAO,EACP,MAAM,EACN,SAAS,EACT,eAAe,EACf,UAAU,EACV,YAAY,GACb,MAAM,oBAAoB,CAAC;AAI5B,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEhF;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,YAAY,GAAG,aAAa,EAAE,CAEnE;AAED,2EAA2E;AAC3E,wBAAgB,iBAAiB,IAAI,MAAM,EAAE,CAK5C;AAED,uDAAuD;AACvD,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAGnE;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,YAAY,EACnB,aAAa,EAAE,MAAM
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,sBAAsB,EACtB,kBAAkB,EAClB,KAAK,aAAa,GACnB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,GAChB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,iBAAiB,EACjB,KAAK,cAAc,EACnB,KAAK,YAAY,GAClB,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,eAAe,EACf,QAAQ,EACR,YAAY,EACZ,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,eAAe,EACf,kBAAkB,GACnB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,OAAO,EACP,OAAO,EACP,QAAQ,EACR,cAAc,EACd,WAAW,EACX,iBAAiB,EACjB,cAAc,EACd,OAAO,GACR,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,QAAQ,EACR,cAAc,EACd,KAAK,iBAAiB,EACtB,KAAK,aAAa,GACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,YAAY,EACZ,UAAU,EACV,mBAAmB,EACnB,SAAS,EACT,UAAU,EACV,YAAY,EACZ,gBAAgB,EAChB,qBAAqB,EACrB,gBAAgB,EAChB,wBAAwB,EACxB,WAAW,EACX,gBAAgB,GACjB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,OAAO,EACP,MAAM,EACN,SAAS,EACT,eAAe,EACf,UAAU,EACV,YAAY,GACb,MAAM,oBAAoB,CAAC;AAI5B,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEhF;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,YAAY,GAAG,aAAa,EAAE,CAEnE;AAED,2EAA2E;AAC3E,wBAAgB,iBAAiB,IAAI,MAAM,EAAE,CAK5C;AAED,uDAAuD;AACvD,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAGnE;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,YAAY,EACnB,aAAa,EAAE,MAAM;;;;;;IAKtB"}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,sBAAsB,EACtB,kBAAkB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,sBAAsB,EACtB,kBAAkB,GAEnB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,GAChB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,iBAAiB,GAGlB,MAAM,eAAe,CAAC;AAYvB,OAAO,EACL,OAAO,EACP,OAAO,EACP,QAAQ,EACR,cAAc,EACd,WAAW,EACX,iBAAiB,EACjB,cAAc,EACd,OAAO,GACR,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,QAAQ,EACR,cAAc,GAGf,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,YAAY,EACZ,UAAU,EACV,mBAAmB,EACnB,SAAS,EACT,UAAU,EACV,YAAY,EACZ,gBAAgB,EAChB,qBAAqB,EACrB,gBAAgB,EAChB,wBAAwB,EACxB,WAAW,EACX,gBAAgB,GACjB,MAAM,oBAAoB,CAAC;AAU5B,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,MAAM,IAAI,QAAQ,EAAE,kBAAkB,IAAI,UAAU,EAAE,MAAM,aAAa,CAAC;AAGnF;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,KAAmB;IAChD,OAAO,QAAQ,CAAC,gBAAgB,EAAE,EAAE,KAAK,CAAC,CAAC;AAC7C,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,iBAAiB;IAC/B,MAAM,KAAK,GAAG,gBAAgB,EAAE,CAAC;IACjC,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,KAAK;QAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;IACrD,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;AACzB,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,WAAW,CAAC,EAAU;IACpC,MAAM,KAAK,GAAG,gBAAgB,EAAE,CAAC;IACjC,OAAO,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;AAC9C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CACxC,KAAmB,EACnB,aAAqB;IAErB,MAAM,KAAK,GAAG,gBAAgB,EAAE,CAAC;IACjC,MAAM,UAAU,GAAG,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC1C,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC;AAClE,CAAC"}
|
package/dist/lookup.d.ts
CHANGED
|
@@ -33,10 +33,20 @@ export declare function generateDisclosureText(rule: DisclosureRuleT): {
|
|
|
33
33
|
* (accepting non-compliant text) are possible. For high-stakes disclosures,
|
|
34
34
|
* verify against the cited regulator-published text and consult counsel.
|
|
35
35
|
*/
|
|
36
|
+
export interface ElementReport {
|
|
37
|
+
element_id: string;
|
|
38
|
+
found: boolean;
|
|
39
|
+
/** Confidence band based on how many signals matched. */
|
|
40
|
+
confidence: "high" | "medium" | "missing";
|
|
41
|
+
/** Signals that matched the candidate (whole-word). */
|
|
42
|
+
matched_signals: string[];
|
|
43
|
+
}
|
|
36
44
|
export declare function validateDisclosure(rule: DisclosureRuleT, candidate: string): {
|
|
37
45
|
rule_id: string;
|
|
38
46
|
passes: boolean;
|
|
39
47
|
missing_elements: string[];
|
|
48
|
+
/** Per-element detail: which signals matched and confidence band. */
|
|
49
|
+
elements: ElementReport[];
|
|
40
50
|
warning: string;
|
|
41
51
|
};
|
|
42
52
|
//# sourceMappingURL=lookup.d.ts.map
|
package/dist/lookup.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lookup.d.ts","sourceRoot":"","sources":["../src/lookup.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,QAAQ,EACd,MAAM,aAAa,CAAC;AAErB;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,YAAY,GAAG,aAAa,EAAE,CAuB5E;AAoCD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,eAAe,GAAG;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B,CAOA;
|
|
1
|
+
{"version":3,"file":"lookup.d.ts","sourceRoot":"","sources":["../src/lookup.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,QAAQ,EACd,MAAM,aAAa,CAAC;AAErB;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,YAAY,GAAG,aAAa,EAAE,CAuB5E;AAoCD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,eAAe,GAAG;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B,CAOA;AA2FD;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;IACf,yDAAyD;IACzD,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;IAC1C,uDAAuD;IACvD,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,eAAe,EACrB,SAAS,EAAE,MAAM,GAChB;IACD,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,qEAAqE;IACrE,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;CACjB,CA4CA"}
|
package/dist/lookup.js
CHANGED
|
@@ -120,43 +120,71 @@ const STOPWORDS = new Set([
|
|
|
120
120
|
"your",
|
|
121
121
|
]);
|
|
122
122
|
function signalsFor(element) {
|
|
123
|
-
const
|
|
123
|
+
const id = [];
|
|
124
124
|
for (const token of element.id.split("-")) {
|
|
125
125
|
if (token.length > 3)
|
|
126
|
-
|
|
126
|
+
id.push(token.toLowerCase());
|
|
127
127
|
}
|
|
128
|
+
const body = [];
|
|
128
129
|
const collect = (text) => {
|
|
129
130
|
for (const word of text.toLowerCase().split(/[^a-z0-9]+/)) {
|
|
130
131
|
if (word.length > 4 && !STOPWORDS.has(word))
|
|
131
|
-
|
|
132
|
+
body.push(word);
|
|
132
133
|
}
|
|
133
134
|
};
|
|
134
135
|
collect(element.description);
|
|
135
136
|
if (element.example !== undefined)
|
|
136
137
|
collect(element.example);
|
|
137
|
-
return
|
|
138
|
+
return {
|
|
139
|
+
id: [...new Set(id)],
|
|
140
|
+
body: [...new Set(body)],
|
|
141
|
+
};
|
|
138
142
|
}
|
|
139
143
|
/**
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
* keyword signals (id tokens, significant words from the element description
|
|
144
|
-
* and example) and checks whether any appear in the candidate (case-insensitive).
|
|
145
|
-
* An element with no signals found is reported as missing.
|
|
146
|
-
*
|
|
147
|
-
* This is a sanity check, NOT a legal-sufficiency determination. False
|
|
148
|
-
* negatives (rejecting compliant text) are possible and false positives
|
|
149
|
-
* (accepting non-compliant text) are possible. For high-stakes disclosures,
|
|
150
|
-
* verify against the cited regulator-published text and consult counsel.
|
|
144
|
+
* Tokenize candidate text into a Set of lowercase word tokens for
|
|
145
|
+
* word-boundary matching. Substrings within longer tokens do NOT
|
|
146
|
+
* match — fixes false positives like "preconsent" matching "consent".
|
|
151
147
|
*/
|
|
148
|
+
function tokenize(text) {
|
|
149
|
+
const out = new Set();
|
|
150
|
+
for (const word of text.toLowerCase().split(/[^a-z0-9]+/)) {
|
|
151
|
+
if (word.length > 0)
|
|
152
|
+
out.add(word);
|
|
153
|
+
}
|
|
154
|
+
return out;
|
|
155
|
+
}
|
|
152
156
|
export function validateDisclosure(rule, candidate) {
|
|
153
|
-
const
|
|
157
|
+
const tokens = tokenize(candidate);
|
|
158
|
+
const elementReports = [];
|
|
154
159
|
const missing = [];
|
|
155
160
|
for (const el of rule.required_elements) {
|
|
156
161
|
if (!el.required)
|
|
157
162
|
continue;
|
|
158
163
|
const signals = signalsFor(el);
|
|
159
|
-
const
|
|
164
|
+
const idMatches = signals.id.filter((s) => tokens.has(s));
|
|
165
|
+
const bodyMatches = signals.body.filter((s) => tokens.has(s));
|
|
166
|
+
const allMatches = [...new Set([...idMatches, ...bodyMatches])];
|
|
167
|
+
// Confidence rule:
|
|
168
|
+
// - high = an id-token matched, OR ≥ 2 body tokens matched
|
|
169
|
+
// - medium = exactly 1 body token matched (signal present but weak)
|
|
170
|
+
// - missing = no signals matched
|
|
171
|
+
let confidence;
|
|
172
|
+
if (idMatches.length > 0 || bodyMatches.length >= 2) {
|
|
173
|
+
confidence = "high";
|
|
174
|
+
}
|
|
175
|
+
else if (bodyMatches.length === 1) {
|
|
176
|
+
confidence = "medium";
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
confidence = "missing";
|
|
180
|
+
}
|
|
181
|
+
const found = confidence !== "missing";
|
|
182
|
+
elementReports.push({
|
|
183
|
+
element_id: el.id,
|
|
184
|
+
found,
|
|
185
|
+
confidence,
|
|
186
|
+
matched_signals: allMatches,
|
|
187
|
+
});
|
|
160
188
|
if (!found)
|
|
161
189
|
missing.push(el.id);
|
|
162
190
|
}
|
|
@@ -164,7 +192,8 @@ export function validateDisclosure(rule, candidate) {
|
|
|
164
192
|
rule_id: rule.id,
|
|
165
193
|
passes: missing.length === 0,
|
|
166
194
|
missing_elements: missing,
|
|
167
|
-
|
|
195
|
+
elements: elementReports,
|
|
196
|
+
warning: "Heuristic word-boundary check with confidence bands. NOT a legal-sufficiency determination. Even high-confidence elements may not satisfy the rule's substantive requirements; medium-confidence elements warrant manual review. For high-stakes disclosures, verify against the cited regulator-published text and consult counsel.",
|
|
168
197
|
};
|
|
169
198
|
}
|
|
170
199
|
//# sourceMappingURL=lookup.js.map
|
package/dist/lookup.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lookup.js","sourceRoot":"","sources":["../src/lookup.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"lookup.js","sourceRoot":"","sources":["../src/lookup.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
# EU AI Act Article 50: a builder's guide
|
|
2
|
+
|
|
3
|
+
> **Informational only — not legal advice.** Verify against the cited
|
|
4
|
+
> regulator-published text and consult counsel for production deployments.
|
|
5
|
+
> See `AI-DISCLOSURE.md` in this package.
|
|
6
|
+
|
|
7
|
+
If your AI product is delivered to anyone in the European Union — by
|
|
8
|
+
a provider established in the EU, or by a provider outside the EU
|
|
9
|
+
whose system's output is used inside the EU — **Article 50 of the
|
|
10
|
+
EU AI Act** is the disclosure framework you need to ship before
|
|
11
|
+
**August 2, 2026**. Article 50 has two distinct obligations that
|
|
12
|
+
people often conflate but that target different actors and different
|
|
13
|
+
artifacts: 50(1) covers AI *systems that interact with humans*
|
|
14
|
+
(chatbots, voice agents, AI assistants), and 50(2) covers *AI-
|
|
15
|
+
generated synthetic content* (images, audio, video, text — including
|
|
16
|
+
output from general-purpose models). This guide separates the two,
|
|
17
|
+
spells out who has to do what, what counts as a sufficient
|
|
18
|
+
disclosure under each, the deepfake / public-interest text overlay
|
|
19
|
+
in 50(4), and what extraterritorial reach actually means in practice.
|
|
20
|
+
|
|
21
|
+
## What Article 50 actually says
|
|
22
|
+
|
|
23
|
+
The EU AI Act ([Regulation (EU) 2024/1689](https://eur-lex.europa.eu/eli/reg/2024/1689/oj))
|
|
24
|
+
was published in the Official Journal on July 12, 2024 and entered
|
|
25
|
+
into force on August 1, 2024. The substantive obligations under
|
|
26
|
+
Article 50 (titled *Transparency obligations for providers and
|
|
27
|
+
deployers of certain AI systems*) **apply from August 2, 2026** —
|
|
28
|
+
two years after entry into force.
|
|
29
|
+
|
|
30
|
+
Article 50 has four operative paragraphs that matter for builders:
|
|
31
|
+
|
|
32
|
+
| Paragraph | Who | What | Notes |
|
|
33
|
+
|---|---|---|---|
|
|
34
|
+
| 50(1) | Providers of AI systems intended to interact directly with natural persons | Inform persons they are interacting with an AI system | "Unless obvious to a reasonably well-informed person under the circumstances." |
|
|
35
|
+
| 50(2) | Providers of AI systems generating synthetic audio, image, video, or text content (including general-purpose AI) | Mark output in machine-readable format detectable as artificially generated | "As far as technically feasible." |
|
|
36
|
+
| 50(3) | Deployers of emotion-recognition or biometric-categorisation systems | Inform the natural persons exposed to the system | Separate, narrower obligation. |
|
|
37
|
+
| 50(4) | Deployers of AI generating "deep fakes" (image/audio/video) or AI-generated text published to inform the public on matters of public interest | Disclose that the content has been artificially generated or manipulated | Two different sub-obligations bundled together. |
|
|
38
|
+
|
|
39
|
+
Article 50 is enforced as a **transparency obligation** under Title
|
|
40
|
+
IV of the Act. Penalties for non-compliance are in Article 99: up
|
|
41
|
+
to **€15M or 3% of total worldwide annual turnover**, whichever is
|
|
42
|
+
higher (for non-compliance with obligations on AI systems other than
|
|
43
|
+
prohibited and high-risk). Member states implement enforcement; the
|
|
44
|
+
EU AI Office coordinates.
|
|
45
|
+
|
|
46
|
+
## Article 50(1): chatbot / voice-agent disclosure
|
|
47
|
+
|
|
48
|
+
The 50(1) obligation falls on **the provider** of an AI system that
|
|
49
|
+
is "intended to interact directly with natural persons." That
|
|
50
|
+
includes:
|
|
51
|
+
|
|
52
|
+
- Customer-facing chatbots (B2C support, marketing, sales bots).
|
|
53
|
+
- AI voice agents (outbound calling, IVR).
|
|
54
|
+
- AI assistants (productivity assistants, sales-rep assistants
|
|
55
|
+
whose interactions touch end-users).
|
|
56
|
+
- AI tutors (in education products).
|
|
57
|
+
- AI companions (NY Companion Models law has a parallel state-level
|
|
58
|
+
rule).
|
|
59
|
+
- Any conversational AI feature embedded in a larger product.
|
|
60
|
+
|
|
61
|
+
The obligation is on the **provider** — the entity that develops
|
|
62
|
+
the AI system or has it developed. If you ship a customer-facing
|
|
63
|
+
AI product into the EU, you are the provider for purposes of 50(1).
|
|
64
|
+
If you embed someone else's AI (e.g., OpenAI's API) inside your
|
|
65
|
+
product, you may be both a deployer (of OpenAI's general-purpose
|
|
66
|
+
model) and a provider (of your derived AI system) — the provider
|
|
67
|
+
hat is the one that triggers 50(1).
|
|
68
|
+
|
|
69
|
+
The "unless obvious" carve-out is significant in practice but
|
|
70
|
+
narrow in interpretation. Examples where the AI nature is "obvious":
|
|
71
|
+
|
|
72
|
+
- A clearly-branded chatbot with an explicit "AI assistant" label and
|
|
73
|
+
a robot icon.
|
|
74
|
+
- A page introducing a conversational interface with a banner
|
|
75
|
+
announcing "Talk to our AI assistant."
|
|
76
|
+
|
|
77
|
+
Examples where the AI nature is **not** obvious (disclosure
|
|
78
|
+
required):
|
|
79
|
+
|
|
80
|
+
- A live-chat window that doesn't distinguish between human and AI
|
|
81
|
+
responses.
|
|
82
|
+
- A voice agent that uses a human-sounding voice without any audio
|
|
83
|
+
cue.
|
|
84
|
+
- Email correspondence that appears handwritten / personal but is
|
|
85
|
+
AI-generated.
|
|
86
|
+
- An AI persona that uses a human-presenting name and avatar.
|
|
87
|
+
|
|
88
|
+
The disclosure must be made "in a clear and distinguishable manner
|
|
89
|
+
at the latest at the time of the first interaction or exposure" —
|
|
90
|
+
i.e., upfront, not buried in a Terms of Service. The exact text
|
|
91
|
+
isn't prescribed; clarity and prominence are.
|
|
92
|
+
|
|
93
|
+
Plain-language template that satisfies 50(1) and most US state-level
|
|
94
|
+
rules layered on top:
|
|
95
|
+
|
|
96
|
+
> *"You are interacting with an AI system, not a human. Some
|
|
97
|
+
> responses may be generated using artificial intelligence."*
|
|
98
|
+
|
|
99
|
+
## Article 50(2): synthetic-content labeling
|
|
100
|
+
|
|
101
|
+
The 50(2) obligation falls on **providers of AI systems generating
|
|
102
|
+
synthetic audio, image, video, or text content, including general-
|
|
103
|
+
purpose AI systems**. The required output is:
|
|
104
|
+
|
|
105
|
+
- **Marked in a machine-readable format** as artificially generated
|
|
106
|
+
or manipulated.
|
|
107
|
+
- **Detectable as artificially generated or manipulated** by tools
|
|
108
|
+
designed to read those marks.
|
|
109
|
+
|
|
110
|
+
This is fundamentally different from the 50(1) obligation. 50(1) is
|
|
111
|
+
*human-readable* disclosure to users. 50(2) is *machine-readable*
|
|
112
|
+
provenance metadata baked into the output itself. The two
|
|
113
|
+
obligations stack — a generative AI assistant that produces an image
|
|
114
|
+
needs both a user-facing chatbot disclosure (50(1)) AND machine-
|
|
115
|
+
readable image marking (50(2)).
|
|
116
|
+
|
|
117
|
+
Acceptable techniques (per Recital 133):
|
|
118
|
+
|
|
119
|
+
- **C2PA / Content Credentials** (Coalition for Content Provenance
|
|
120
|
+
and Authenticity) — widely-adopted standard for image, audio, and
|
|
121
|
+
video provenance metadata. Adobe, Microsoft, and others ship
|
|
122
|
+
C2PA-marking tools.
|
|
123
|
+
- **Watermarking** — perceptible or imperceptible signal embedded in
|
|
124
|
+
the output. Imperceptible watermarking (frequency-domain markers,
|
|
125
|
+
steganographic encoding) is preferred for non-deepfake use cases
|
|
126
|
+
to preserve user experience.
|
|
127
|
+
- **Cryptographic methods** — signed metadata that survives
|
|
128
|
+
compression and transformation.
|
|
129
|
+
- **Logging metadata in the file format** — Exif fields, ID3 tags,
|
|
130
|
+
PDF metadata, MP4 metadata — though these are easier to strip.
|
|
131
|
+
|
|
132
|
+
The "as far as technically feasible" qualifier is real. Pure-text
|
|
133
|
+
output is the hardest case: text watermarking is an active research
|
|
134
|
+
area without a settled standard. For text, the Recital 133 expectation
|
|
135
|
+
is that providers make a good-faith effort with current state-of-the-
|
|
136
|
+
art techniques (e.g., Anthropic's Constitutional Classifier-style
|
|
137
|
+
output watermarks, OpenAI's hidden token-frequency biases).
|
|
138
|
+
|
|
139
|
+
## Article 50(4): the deepfake and public-interest-text overlay
|
|
140
|
+
|
|
141
|
+
Article 50(4) is two sub-obligations bundled together; they apply to
|
|
142
|
+
**deployers**, not providers, of AI systems:
|
|
143
|
+
|
|
144
|
+
### 50(4) first sentence: deepfakes
|
|
145
|
+
|
|
146
|
+
Deployers of AI systems that generate or manipulate image, audio, or
|
|
147
|
+
video content constituting **a deep fake** must disclose that the
|
|
148
|
+
content has been artificially generated or manipulated.
|
|
149
|
+
|
|
150
|
+
A "deep fake" under Article 3(60) means AI-generated or -manipulated
|
|
151
|
+
content that resembles existing persons, objects, places, entities or
|
|
152
|
+
events and would falsely appear to a person to be authentic.
|
|
153
|
+
|
|
154
|
+
Carve-outs:
|
|
155
|
+
|
|
156
|
+
- **Artistic, creative, satirical, fictional, or analogous works**:
|
|
157
|
+
the disclosure is satisfied "in an appropriate manner that does
|
|
158
|
+
not hamper the display or enjoyment of the work" — i.e., the
|
|
159
|
+
disclosure can be in the credits, in a sidebar, in metadata —
|
|
160
|
+
rather than on the work itself.
|
|
161
|
+
- **For deepfakes used in policing of crime**: separate exemption
|
|
162
|
+
if authorized by law.
|
|
163
|
+
|
|
164
|
+
Practical disclosure examples: a watermark on a deepfake image, a
|
|
165
|
+
banner on video, a credit at the end of audio content.
|
|
166
|
+
|
|
167
|
+
### 50(4) second sentence: AI-generated text on matters of public interest
|
|
168
|
+
|
|
169
|
+
Deployers of AI systems generating or manipulating text published to
|
|
170
|
+
**inform the public on matters of public interest** must disclose
|
|
171
|
+
that the text has been artificially generated or manipulated.
|
|
172
|
+
|
|
173
|
+
This targets:
|
|
174
|
+
|
|
175
|
+
- AI-generated news articles published to a public audience.
|
|
176
|
+
- AI-generated political commentary intended for public consumption.
|
|
177
|
+
- AI-generated content on matters of public health, safety, or
|
|
178
|
+
policy published to the public.
|
|
179
|
+
|
|
180
|
+
Carve-outs:
|
|
181
|
+
|
|
182
|
+
- The text is subject to **human review or editorial control** AND
|
|
183
|
+
a natural or legal person holds editorial responsibility.
|
|
184
|
+
- The publication is for purposes other than informing the public on
|
|
185
|
+
matters of public interest.
|
|
186
|
+
|
|
187
|
+
Practical implication: AI-drafted news content with no human
|
|
188
|
+
editorial review must carry an "AI-generated" disclosure. Same
|
|
189
|
+
content with documented editorial human review by a named editor
|
|
190
|
+
satisfies the carve-out.
|
|
191
|
+
|
|
192
|
+
## Provider vs deployer: who bears each obligation
|
|
193
|
+
|
|
194
|
+
Article 50 distributes obligations precisely; getting this wrong is
|
|
195
|
+
a common compliance failure pattern. The Act's definitions
|
|
196
|
+
(Article 3) draw the line:
|
|
197
|
+
|
|
198
|
+
- **Provider** (Art 3(3)): natural or legal person that *develops* an
|
|
199
|
+
AI system or has it developed and places it on the market or puts
|
|
200
|
+
it into service under its own name or trademark, whether for
|
|
201
|
+
payment or free of charge.
|
|
202
|
+
- **Deployer** (Art 3(4)): natural or legal person *using* an AI
|
|
203
|
+
system under its authority, except where the AI system is used in
|
|
204
|
+
the course of a personal non-professional activity.
|
|
205
|
+
|
|
206
|
+
Mapping to Article 50:
|
|
207
|
+
|
|
208
|
+
| Obligation | Who | Common production owner |
|
|
209
|
+
|---|---|---|
|
|
210
|
+
| 50(1) — chatbot disclosure | Provider | The team that builds and ships the chatbot |
|
|
211
|
+
| 50(2) — synthetic-content marking | Provider | The team that builds and ships the gen-AI feature |
|
|
212
|
+
| 50(3) — emotion-recognition / biometric notice | Deployer | The customer / business using the system |
|
|
213
|
+
| 50(4) — deepfake / public-interest-text disclosure | Deployer | The publisher / company posting the content |
|
|
214
|
+
|
|
215
|
+
A SaaS image-generation product: the SaaS company is a **provider**
|
|
216
|
+
under 50(2) and must mark outputs. The SaaS *customer* using the
|
|
217
|
+
output to publish a deepfake is a **deployer** under 50(4) and must
|
|
218
|
+
add the deepfake disclosure on top.
|
|
219
|
+
|
|
220
|
+
## Extraterritorial reach: when does this apply to non-EU companies
|
|
221
|
+
|
|
222
|
+
Article 2 of the Act states the territorial scope. The Act applies
|
|
223
|
+
to:
|
|
224
|
+
|
|
225
|
+
- **Providers placing AI systems on the EU market or putting them
|
|
226
|
+
into service in the EU**, regardless of whether the provider is
|
|
227
|
+
established in the EU or a third country.
|
|
228
|
+
- **Deployers of AI systems located within the EU**.
|
|
229
|
+
- **Providers and deployers of AI systems located in a third
|
|
230
|
+
country, where the output produced by the AI system is used in
|
|
231
|
+
the EU**.
|
|
232
|
+
|
|
233
|
+
The third bullet is the controversial one. A US-based AI provider
|
|
234
|
+
whose system has even one EU end-user is in scope. A US news website
|
|
235
|
+
publishing AI-generated articles read by EU citizens may be in scope.
|
|
236
|
+
The "used in the EU" interpretation is being clarified by the EU AI
|
|
237
|
+
Office through guidance documents; conservative interpretation
|
|
238
|
+
treats any EU-accessible AI deployment as in scope.
|
|
239
|
+
|
|
240
|
+
For US-based companies serving global audiences, the practical
|
|
241
|
+
floor is: **assume Article 50 applies if you ship AI features that
|
|
242
|
+
EU citizens can access**. The cost of compliance (a disclosure line
|
|
243
|
+
in the chatbot, C2PA marking on images) is small compared to the
|
|
244
|
+
penalty exposure.
|
|
245
|
+
|
|
246
|
+
## How Article 50 stacks with other rules
|
|
247
|
+
|
|
248
|
+
| Other rule | How it stacks |
|
|
249
|
+
|---|---|
|
|
250
|
+
| **GDPR** (especially Art 22 on automated decisions) | GDPR is separate. Art 50 disclosure does not replace GDPR consent or right to explanation. Both apply when both apply. |
|
|
251
|
+
| **California B&P § 17941** (bot disclosure) | Substantively similar to 50(1) but applies to incentivizing-sale or influencing-vote contexts. A 50(1)-compliant disclosure typically satisfies 17941; converse not always true. |
|
|
252
|
+
| **California SB 942** (AI Transparency Act) | Provides for AI image/audio/video provenance + watermarking — partly aligned with 50(2). Templates need to satisfy both. |
|
|
253
|
+
| **NY Companion Models law** (NY GBL Art 47, A6767) | Stricter than 50(1) for "AI companion" subset; 50(1) is a floor. |
|
|
254
|
+
| **EU member-state implementations** | Member states implement enforcement and may impose additional obligations within the AI Act framework. Track Germany / France / Spain / Italy / Netherlands first. |
|
|
255
|
+
| **National laws on deepfakes** (DE Stoltenberg AI law in development; FR loi visant à sécuriser et réguler) | Layer on top of 50(4); strictest applies. |
|
|
256
|
+
|
|
257
|
+
## Common compliance failure patterns
|
|
258
|
+
|
|
259
|
+
- **50(1) treated as "chatbot disclosure" only.** Voice agents, AI
|
|
260
|
+
email-drafting tools, and AI persona accounts on social platforms
|
|
261
|
+
are also "AI systems that interact directly with natural persons"
|
|
262
|
+
and require 50(1) disclosure.
|
|
263
|
+
- **50(2) treated as optional because text is "technically
|
|
264
|
+
infeasible."** The "as far as technically feasible" qualifier is
|
|
265
|
+
not a blanket exemption. Providers are expected to use current
|
|
266
|
+
state-of-the-art text-marking techniques (e.g., output watermarks)
|
|
267
|
+
even if not perfect.
|
|
268
|
+
- **50(4) deepfake disclosure missing on commercial deepfake video.**
|
|
269
|
+
Marketing content that uses AI-generated likenesses of real people
|
|
270
|
+
needs a 50(4) deployer disclosure even when the provider-side 50(2)
|
|
271
|
+
marking is in place.
|
|
272
|
+
- **Non-EU provider assumes territorial exclusion.** Cloud-based AI
|
|
273
|
+
service with EU end-users is in scope under Art 2(1)(c).
|
|
274
|
+
- **Provider-deployer obligations conflated.** SaaS company assumes
|
|
275
|
+
the customer is responsible for 50(2) marking; in fact 50(2) is the
|
|
276
|
+
provider's obligation that the SaaS company must build into its
|
|
277
|
+
product before customers ever interact with it.
|
|
278
|
+
- **Disclosure buried in Terms of Service.** 50(1) requires a clear
|
|
279
|
+
and distinguishable disclosure at the time of first interaction;
|
|
280
|
+
ToS-only disclosure is not compliant.
|
|
281
|
+
- **No editorial-review documentation for AI-generated public-
|
|
282
|
+
interest text.** Publisher relies on the carve-out without having
|
|
283
|
+
documented evidence of human editorial review. Defense fails on
|
|
284
|
+
inspection.
|
|
285
|
+
|
|
286
|
+
## How plainstamp helps
|
|
287
|
+
|
|
288
|
+
`plainstamp` ships two EU AI Act Article 50 rules:
|
|
289
|
+
`eu-ai-act-art50-chatbot` (50(1) chatbot/voice/agent disclosure) and
|
|
290
|
+
`eu-ai-act-art50-genai-content` (50(2) synthetic content marking).
|
|
291
|
+
Each returns the disclosure-element checklist, plain-language and
|
|
292
|
+
formal-language templates, citation back to Regulation (EU)
|
|
293
|
+
2024/1689, and a `last_verified` date. Lookup:
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
# Chatbot / voice agent
|
|
297
|
+
npx plainstamp lookup --jurisdiction eu --channel live-chat --use-case b2c-customer-support
|
|
298
|
+
npx plainstamp lookup --jurisdiction eu --channel voice --use-case b2c-marketing
|
|
299
|
+
|
|
300
|
+
# Generative AI content
|
|
301
|
+
npx plainstamp lookup --jurisdiction eu --channel ai-generated-image --use-case b2c-marketing
|
|
302
|
+
npx plainstamp lookup --jurisdiction eu --channel ai-generated-content --use-case b2c-marketing
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
For US companies serving EU audiences, layer the EU queries on top
|
|
306
|
+
of the US-jurisdiction queries — the disclosure copy needs to satisfy
|
|
307
|
+
each applicable rule.
|
|
308
|
+
|
|
309
|
+
## The minimum viable compliance posture
|
|
310
|
+
|
|
311
|
+
If your AI deployment is starting from zero on Article 50 and August
|
|
312
|
+
2, 2026 is approaching, ship these six artifacts in order:
|
|
313
|
+
|
|
314
|
+
1. **50(1) chatbot disclosure.** Clear, prominent disclosure on
|
|
315
|
+
first interaction with any AI system that engages natural
|
|
316
|
+
persons. Plain-language template above is sufficient.
|
|
317
|
+
2. **50(2) machine-readable marking** for image / audio / video
|
|
318
|
+
outputs. Adopt C2PA Content Credentials as the default; for
|
|
319
|
+
non-C2PA-aware tooling, use cryptographic signatures embedded in
|
|
320
|
+
format-level metadata.
|
|
321
|
+
3. **50(2) text-output marking** to the extent technically feasible.
|
|
322
|
+
Document the technique chosen and the rationale (this is the
|
|
323
|
+
"good-faith effort" record).
|
|
324
|
+
4. **50(4) deepfake disclosure pipeline** for any deployer use of
|
|
325
|
+
deepfake outputs. Watermark + visible disclosure on the
|
|
326
|
+
published deepfake.
|
|
327
|
+
5. **50(4) AI-generated public-interest text governance.** Documented
|
|
328
|
+
editorial-review process if relying on the carve-out, OR
|
|
329
|
+
AI-generated disclosure on each piece of public-interest content.
|
|
330
|
+
6. **Provider-deployer mapping.** A documented mapping of which
|
|
331
|
+
Article 50 obligations apply to your team as provider and which
|
|
332
|
+
apply to your customers as deployers; communicate the deployer
|
|
333
|
+
obligations to customers.
|
|
334
|
+
|
|
335
|
+
Then layer the higher-fidelity work — member-state implementation
|
|
336
|
+
specifics, sector overlays (healthcare AI under MDR, financial AI
|
|
337
|
+
under DORA), GDPR Art 22 stacking — onto the higher-risk use cases
|
|
338
|
+
first.
|
|
339
|
+
|
|
340
|
+
## Source-of-truth links
|
|
341
|
+
|
|
342
|
+
- **Regulation (EU) 2024/1689 (AI Act) — full text** ([eur-lex.europa.eu](https://eur-lex.europa.eu/eli/reg/2024/1689/oj))
|
|
343
|
+
- **EU AI Office** ([digital-strategy.ec.europa.eu](https://digital-strategy.ec.europa.eu/en/policies/ai-office))
|
|
344
|
+
- **C2PA — Coalition for Content Provenance and Authenticity** ([c2pa.org](https://c2pa.org))
|
|
345
|
+
- **Recital 133 (synthetic content marking)** — see EUR-Lex full text above.
|
|
346
|
+
- **Recitals 132 and 134 (Article 50 transparency framework)** — see EUR-Lex full text above.
|
|
347
|
+
|
|
348
|
+
`plainstamp` is maintained by an autonomous AI agent operating under
|
|
349
|
+
KS Elevated Solutions LLC. Accuracy reports, rule-update suggestions,
|
|
350
|
+
and security disclosures: [helpfulbutton140@agentmail.to](mailto:helpfulbutton140@agentmail.to).
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
354
|
+
[`← Back to plainstamp`](https://plainstamp.pages.dev/)
|