boarddata 6.8.2__tar.gz → 6.8.4__tar.gz

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.
Files changed (90) hide show
  1. {boarddata-6.8.2 → boarddata-6.8.4}/PKG-INFO +1 -1
  2. {boarddata-6.8.2 → boarddata-6.8.4}/__init__.py +1 -1
  3. {boarddata-6.8.2 → boarddata-6.8.4}/_comex.py +12 -0
  4. {boarddata-6.8.2 → boarddata-6.8.4}/boarddata.egg-info/PKG-INFO +1 -1
  5. {boarddata-6.8.2 → boarddata-6.8.4}/pyproject.toml +1 -1
  6. {boarddata-6.8.2 → boarddata-6.8.4}/tests/test_comex.py +40 -0
  7. {boarddata-6.8.2 → boarddata-6.8.4}/tests/test_types_politic.py +67 -0
  8. {boarddata-6.8.2 → boarddata-6.8.4}/types/comex.py +57 -0
  9. {boarddata-6.8.2 → boarddata-6.8.4}/types/core.py +14 -0
  10. {boarddata-6.8.2 → boarddata-6.8.4}/CLAUDE.md +0 -0
  11. {boarddata-6.8.2 → boarddata-6.8.4}/README.md +0 -0
  12. {boarddata-6.8.2 → boarddata-6.8.4}/_assemblies.py +0 -0
  13. {boarddata-6.8.2 → boarddata-6.8.4}/_auditors.py +0 -0
  14. {boarddata-6.8.2 → boarddata-6.8.4}/_base.py +0 -0
  15. {boarddata-6.8.2 → boarddata-6.8.4}/_committees.py +0 -0
  16. {boarddata-6.8.2 → boarddata-6.8.4}/_companies.py +0 -0
  17. {boarddata-6.8.2 → boarddata-6.8.4}/_criteria.py +0 -0
  18. {boarddata-6.8.2 → boarddata-6.8.4}/_directors.py +0 -0
  19. {boarddata-6.8.2 → boarddata-6.8.4}/_documents.py +0 -0
  20. {boarddata-6.8.2 → boarddata-6.8.4}/_esg.py +0 -0
  21. {boarddata-6.8.2 → boarddata-6.8.4}/_indexes.py +0 -0
  22. {boarddata-6.8.2 → boarddata-6.8.4}/_persons.py +0 -0
  23. {boarddata-6.8.2 → boarddata-6.8.4}/_risks.py +0 -0
  24. {boarddata-6.8.2 → boarddata-6.8.4}/_sentinel.py +0 -0
  25. {boarddata-6.8.2 → boarddata-6.8.4}/_utilities.py +0 -0
  26. {boarddata-6.8.2 → boarddata-6.8.4}/boarddata.egg-info/SOURCES.txt +0 -0
  27. {boarddata-6.8.2 → boarddata-6.8.4}/boarddata.egg-info/dependency_links.txt +0 -0
  28. {boarddata-6.8.2 → boarddata-6.8.4}/boarddata.egg-info/requires.txt +0 -0
  29. {boarddata-6.8.2 → boarddata-6.8.4}/boarddata.egg-info/top_level.txt +0 -0
  30. {boarddata-6.8.2 → boarddata-6.8.4}/cache.py +0 -0
  31. {boarddata-6.8.2 → boarddata-6.8.4}/client.py +0 -0
  32. {boarddata-6.8.2 → boarddata-6.8.4}/dq/__init__.py +0 -0
  33. {boarddata-6.8.2 → boarddata-6.8.4}/dq/adjudicate.py +0 -0
  34. {boarddata-6.8.2 → boarddata-6.8.4}/dq/apply.py +0 -0
  35. {boarddata-6.8.2 → boarddata-6.8.4}/dq/context.py +0 -0
  36. {boarddata-6.8.2 → boarddata-6.8.4}/dq/db.py +0 -0
  37. {boarddata-6.8.2 → boarddata-6.8.4}/dq/domains.py +0 -0
  38. {boarddata-6.8.2 → boarddata-6.8.4}/dq/models.py +0 -0
  39. {boarddata-6.8.2 → boarddata-6.8.4}/dq/oracle.py +0 -0
  40. {boarddata-6.8.2 → boarddata-6.8.4}/dq/registry.py +0 -0
  41. {boarddata-6.8.2 → boarddata-6.8.4}/dq/report.py +0 -0
  42. {boarddata-6.8.2 → boarddata-6.8.4}/dq/rules/__init__.py +0 -0
  43. {boarddata-6.8.2 → boarddata-6.8.4}/dq/rules/cross_company.py +0 -0
  44. {boarddata-6.8.2 → boarddata-6.8.4}/dq/rules/lexical.py +0 -0
  45. {boarddata-6.8.2 → boarddata-6.8.4}/dq/s3.py +0 -0
  46. {boarddata-6.8.2 → boarddata-6.8.4}/dq/verify_s3.py +0 -0
  47. {boarddata-6.8.2 → boarddata-6.8.4}/errors.py +0 -0
  48. {boarddata-6.8.2 → boarddata-6.8.4}/py.typed +0 -0
  49. {boarddata-6.8.2 → boarddata-6.8.4}/scripts/__init__.py +0 -0
  50. {boarddata-6.8.2 → boarddata-6.8.4}/scripts/dq_apply.py +0 -0
  51. {boarddata-6.8.2 → boarddata-6.8.4}/scripts/dq_batch.py +0 -0
  52. {boarddata-6.8.2 → boarddata-6.8.4}/scripts/dq_plan.py +0 -0
  53. {boarddata-6.8.2 → boarddata-6.8.4}/scripts/dq_revert.py +0 -0
  54. {boarddata-6.8.2 → boarddata-6.8.4}/scripts/dq_scan.py +0 -0
  55. {boarddata-6.8.2 → boarddata-6.8.4}/scripts/dq_verify_s3.py +0 -0
  56. {boarddata-6.8.2 → boarddata-6.8.4}/setup.cfg +0 -0
  57. {boarddata-6.8.2 → boarddata-6.8.4}/tests/__init__.py +0 -0
  58. {boarddata-6.8.2 → boarddata-6.8.4}/tests/conftest.py +0 -0
  59. {boarddata-6.8.2 → boarddata-6.8.4}/tests/test_assemblies.py +0 -0
  60. {boarddata-6.8.2 → boarddata-6.8.4}/tests/test_auditors.py +0 -0
  61. {boarddata-6.8.2 → boarddata-6.8.4}/tests/test_base.py +0 -0
  62. {boarddata-6.8.2 → boarddata-6.8.4}/tests/test_build_payload.py +0 -0
  63. {boarddata-6.8.2 → boarddata-6.8.4}/tests/test_cache.py +0 -0
  64. {boarddata-6.8.2 → boarddata-6.8.4}/tests/test_committees.py +0 -0
  65. {boarddata-6.8.2 → boarddata-6.8.4}/tests/test_companies.py +0 -0
  66. {boarddata-6.8.2 → boarddata-6.8.4}/tests/test_config.py +0 -0
  67. {boarddata-6.8.2 → boarddata-6.8.4}/tests/test_criteria.py +0 -0
  68. {boarddata-6.8.2 → boarddata-6.8.4}/tests/test_directors.py +0 -0
  69. {boarddata-6.8.2 → boarddata-6.8.4}/tests/test_documents.py +0 -0
  70. {boarddata-6.8.2 → boarddata-6.8.4}/tests/test_esg.py +0 -0
  71. {boarddata-6.8.2 → boarddata-6.8.4}/tests/test_exports.py +0 -0
  72. {boarddata-6.8.2 → boarddata-6.8.4}/tests/test_indexes.py +0 -0
  73. {boarddata-6.8.2 → boarddata-6.8.4}/tests/test_persons.py +0 -0
  74. {boarddata-6.8.2 → boarddata-6.8.4}/tests/test_politic_fixture_conformance.py +0 -0
  75. {boarddata-6.8.2 → boarddata-6.8.4}/tests/test_risks.py +0 -0
  76. {boarddata-6.8.2 → boarddata-6.8.4}/tests/test_sentinel.py +0 -0
  77. {boarddata-6.8.2 → boarddata-6.8.4}/tests/test_utilities.py +0 -0
  78. {boarddata-6.8.2 → boarddata-6.8.4}/types/__init__.py +0 -0
  79. {boarddata-6.8.2 → boarddata-6.8.4}/types/assemblies.py +0 -0
  80. {boarddata-6.8.2 → boarddata-6.8.4}/types/auditors.py +0 -0
  81. {boarddata-6.8.2 → boarddata-6.8.4}/types/committees.py +0 -0
  82. {boarddata-6.8.2 → boarddata-6.8.4}/types/companies.py +0 -0
  83. {boarddata-6.8.2 → boarddata-6.8.4}/types/criteria.py +0 -0
  84. {boarddata-6.8.2 → boarddata-6.8.4}/types/directors.py +0 -0
  85. {boarddata-6.8.2 → boarddata-6.8.4}/types/documents.py +0 -0
  86. {boarddata-6.8.2 → boarddata-6.8.4}/types/esg.py +0 -0
  87. {boarddata-6.8.2 → boarddata-6.8.4}/types/indexes.py +0 -0
  88. {boarddata-6.8.2 → boarddata-6.8.4}/types/persons.py +0 -0
  89. {boarddata-6.8.2 → boarddata-6.8.4}/types/risks.py +0 -0
  90. {boarddata-6.8.2 → boarddata-6.8.4}/types/sentinel.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: boarddata
3
- Version: 6.8.2
3
+ Version: 6.8.4
4
4
  Summary: Python SDK for the BoardData V2 REST API
5
5
  License: MIT
6
6
  Classifier: Development Status :: 4 - Beta
@@ -6,7 +6,7 @@ from .cache import FileTokenCache, ScalewaySecretTokenCache
6
6
  from .client import BoardDataClient
7
7
  from .errors import BoardDataError, ExportTooLargeError
8
8
 
9
- __version__ = "6.8.2"
9
+ __version__ = "6.8.4"
10
10
  __all__ = [
11
11
  "Base",
12
12
  "BoardDataClient",
@@ -368,6 +368,7 @@ class ComexMixin:
368
368
  lti_grant_target_basis: str | None = None,
369
369
  lti_grant_target_value: str | None = None,
370
370
  lti_grant_cap_kind: str | None = None,
371
+ lti_max: str | None = None,
371
372
  performance_shares_number_level: str | None = None,
372
373
  performance_shares_level: str | None = None,
373
374
  policy_changed_midyear: bool | None = None,
@@ -442,6 +443,10 @@ class ComexMixin:
442
443
  lti_grant_target_value: LTI grant at TARGET in the DISCLOSED basis (decimal string).
443
444
  lti_grant_cap_kind: Which kind of ceiling occupies the cap slot (frozen enum,
444
445
  e.g. ``"individual_max"`` or ``"policy_cap"``).
446
+ lti_max: LTI maximum as a MONEY amount in the row's own currency
447
+ (decimal string; never converted): grant ceiling × vesting max
448
+ ÷ 100, capped at a share-count / %-of-capital ceiling. ``"0.00"``
449
+ is an attested zero; omit (None) when not served.
445
450
  performance_shares_number_level: Quote-cued disclosure level of the
446
451
  performance-shares COUNT (frozen enum, e.g. ``"target"`` or ``"max"``).
447
452
  performance_shares_level: Quote-cued disclosure level of the
@@ -512,6 +517,7 @@ class ComexMixin:
512
517
  lti_grant_target_basis=lti_grant_target_basis,
513
518
  lti_grant_target_value=lti_grant_target_value,
514
519
  lti_grant_cap_kind=lti_grant_cap_kind,
520
+ lti_max=lti_max,
515
521
  performance_shares_number_level=performance_shares_number_level,
516
522
  performance_shares_level=performance_shares_level,
517
523
  policy_changed_midyear=policy_changed_midyear,
@@ -577,6 +583,7 @@ class ComexMixin:
577
583
  lti_grant_target_basis: str | None = None,
578
584
  lti_grant_target_value: str | None = None,
579
585
  lti_grant_cap_kind: str | None = None,
586
+ lti_max: str | None = None,
580
587
  performance_shares_number_level: str | None = None,
581
588
  performance_shares_level: str | None = None,
582
589
  policy_changed_midyear: bool | None = None,
@@ -651,6 +658,10 @@ class ComexMixin:
651
658
  lti_grant_target_value: LTI grant at TARGET in the DISCLOSED basis (decimal string).
652
659
  lti_grant_cap_kind: Which kind of ceiling occupies the cap slot (frozen enum,
653
660
  e.g. ``"individual_max"`` or ``"policy_cap"``).
661
+ lti_max: LTI maximum as a MONEY amount in the row's own currency
662
+ (decimal string; never converted): grant ceiling × vesting max
663
+ ÷ 100, capped at a share-count / %-of-capital ceiling. ``"0.00"``
664
+ is an attested zero; omit (None) when not served.
654
665
  performance_shares_number_level: Quote-cued disclosure level of the
655
666
  performance-shares COUNT (frozen enum, e.g. ``"target"`` or ``"max"``).
656
667
  performance_shares_level: Quote-cued disclosure level of the
@@ -722,6 +733,7 @@ class ComexMixin:
722
733
  lti_grant_target_basis=lti_grant_target_basis,
723
734
  lti_grant_target_value=lti_grant_target_value,
724
735
  lti_grant_cap_kind=lti_grant_cap_kind,
736
+ lti_max=lti_max,
725
737
  performance_shares_number_level=performance_shares_number_level,
726
738
  performance_shares_level=performance_shares_level,
727
739
  policy_changed_midyear=policy_changed_midyear,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: boarddata
3
- Version: 6.8.2
3
+ Version: 6.8.4
4
4
  Summary: Python SDK for the BoardData V2 REST API
5
5
  License: MIT
6
6
  Classifier: Development Status :: 4 - Beta
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "boarddata"
7
- version = "6.8.2"
7
+ version = "6.8.4"
8
8
  description = "Python SDK for the BoardData V2 REST API"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -288,11 +288,51 @@ def test_envelope_scalar_kwargs_in_signatures():
288
288
  "reference_amount",
289
289
  "lti_grant_cap_basis",
290
290
  "lti_grant_cap_value",
291
+ "lti_max",
291
292
  "policy_changed_midyear",
292
293
  ):
293
294
  assert k in params, f"{method.__name__} missing {k}"
294
295
 
295
296
 
297
+ def test_lti_max_reaches_the_wire_including_attested_zero():
298
+ c = make_client()
299
+ c._request.return_value = {"id": "1", "action": "created"}
300
+ c.replace_comex_compensation("uid", 2026, "politic", lti_max="0.00")
301
+ _, kwargs = c._request.call_args
302
+ assert kwargs["json"]["lti_max"] == "0.00"
303
+
304
+
305
+ def test_lti_max_omitted_is_not_sent():
306
+ """None means 'not served' — the SDK must not write an explicit null
307
+ over a committed value on replace."""
308
+ c = make_client()
309
+ c._request.return_value = {"id": "1", "action": "created"}
310
+ c.replace_comex_compensation("uid", 2026, "politic", variable_max="1.00")
311
+ _, kwargs = c._request.call_args
312
+ assert "lti_max" not in kwargs["json"]
313
+
314
+
315
+ def test_lti_max_declared_on_every_compensation_typeddict():
316
+ """Read + write contract parity: every TypedDict that carries the twin
317
+ lti_grant_cap_kind slot must also carry lti_max."""
318
+ from boarddata.types.comex import (
319
+ ComexCompensationDetail,
320
+ ComexCompensationExportItem,
321
+ ComexCompensationItem,
322
+ CreateComexCompensationPayload,
323
+ UpdateComexCompensationPayload,
324
+ )
325
+
326
+ for typed_dict in (
327
+ ComexCompensationItem,
328
+ ComexCompensationDetail,
329
+ CreateComexCompensationPayload,
330
+ UpdateComexCompensationPayload,
331
+ ComexCompensationExportItem,
332
+ ):
333
+ assert "lti_max" in typed_dict.__annotations__, typed_dict.__name__
334
+
335
+
296
336
  def test_envelope_scalars_reach_the_wire():
297
337
  c = make_client()
298
338
  c._request.return_value = {"id": "1", "action": "created"}
@@ -70,6 +70,10 @@ class TestFieldSourcePayload:
70
70
  "extracted_by",
71
71
  "notes",
72
72
  "derivation",
73
+ # F-08 English twins
74
+ "extracted_value_en",
75
+ "notes_en",
76
+ "derivation_en",
73
77
  }
74
78
  assert required_keys(FieldSourcePayload) == {"document", "field_name"}
75
79
 
@@ -89,6 +93,10 @@ class TestFieldSourceReadShapes:
89
93
  "extracted_by",
90
94
  "notes",
91
95
  "derivation",
96
+ # F-08 English twins (absent on servers before core/0035)
97
+ "extracted_value_en",
98
+ "notes_en",
99
+ "derivation_en",
92
100
  # provenance bundle (slice 7a)
93
101
  "source",
94
102
  "provenance",
@@ -108,6 +116,9 @@ class TestFieldSourceReadShapes:
108
116
  "cause_end",
109
117
  "cause_end_display",
110
118
  "terminal_departure",
119
+ "extracted_value_en",
120
+ "notes_en",
121
+ "derivation_en",
111
122
  }
112
123
 
113
124
  def test_item_is_record_plus_field_name(self):
@@ -247,7 +258,18 @@ class TestWritePayloadTypes:
247
258
  "max_value",
248
259
  "payout_curve",
249
260
  "disclosure",
261
+ "plan_stage",
262
+ "plan_vintage",
250
263
  "field_sources",
264
+ # F-08 English twins
265
+ "label_en",
266
+ "target_value_en",
267
+ "achieved_value_en",
268
+ "min_value_en",
269
+ "max_value_en",
270
+ "plan_vintage_en",
271
+ "disclosure_note_en",
272
+ "payout_curve_en",
251
273
  }
252
274
  assert required_keys(CriterionWritePayload) == {"component_type"}
253
275
  # Server-stamped fields must never be sendable:
@@ -271,7 +293,15 @@ class TestWritePayloadTypes:
271
293
  "rate_basis",
272
294
  "disclosure",
273
295
  "payout_curve",
296
+ "achievement_plan",
297
+ "quote",
298
+ "page",
274
299
  "field_sources",
300
+ # F-08 English twins
301
+ "achievement_plan_en",
302
+ "quote_en",
303
+ "disclosure_note_en",
304
+ "payout_curve_en",
275
305
  }
276
306
  assert required_keys(ComponentAchievementRateWritePayload) == {"component_type"}
277
307
 
@@ -349,6 +379,43 @@ class TestReadTypeRefresh:
349
379
  missing = {"id", "rate_basis", "disclosure", "payout_curve"} - set(ComponentAchievementRate.__annotations__)
350
380
  assert not missing, sorted(missing)
351
381
 
382
+ def test_en_twins_are_typed_beside_their_originals(self):
383
+ # F-08: every served/accepted *_en twin is typed, NotRequired (older
384
+ # servers omit it), and never without its original.
385
+ from boarddata.types.comex import (
386
+ ComexCompensationCriteria,
387
+ ComponentAchievementRate,
388
+ ComponentAchievementRateWritePayload,
389
+ CriterionWritePayload,
390
+ )
391
+
392
+ criteria_twins = {
393
+ "label_en": "label",
394
+ "target_value_en": "target_value",
395
+ "achieved_value_en": "achieved_value",
396
+ "min_value_en": "min_value",
397
+ "max_value_en": "max_value",
398
+ "plan_vintage_en": "plan_vintage",
399
+ "disclosure_note_en": "disclosure",
400
+ "payout_curve_en": "payout_curve",
401
+ }
402
+ rate_twins = {
403
+ "achievement_plan_en": "achievement_plan",
404
+ "quote_en": "quote",
405
+ "disclosure_note_en": "disclosure",
406
+ "payout_curve_en": "payout_curve",
407
+ }
408
+ for td, twins in (
409
+ (ComexCompensationCriteria, criteria_twins),
410
+ (CriterionWritePayload, criteria_twins),
411
+ (ComponentAchievementRate, rate_twins),
412
+ (ComponentAchievementRateWritePayload, rate_twins),
413
+ ):
414
+ keys = set(td.__annotations__)
415
+ assert {k for k in keys if k.endswith("_en")} == set(twins), td.__name__
416
+ assert set(twins.values()) <= keys, td.__name__
417
+ assert set(twins) <= optional_keys(td), td.__name__
418
+
352
419
  def test_sources_are_dicts(self):
353
420
  """The API serves ``{field_name: [entry, ...]}`` (SourceTrackingMixin) —
354
421
  the list typing was a long-standing SDK bug."""
@@ -155,6 +155,22 @@ class ComexCompensationCriteria(TypedDict):
155
155
  family_source: str
156
156
  families: list[CriteriaFamilyWeightItem]
157
157
  classification_expected: bool
158
+ # Plan keying (NULL = legacy/unkeyed row)
159
+ plan_stage: NotRequired[str | None] # grant | vested
160
+ plan_vintage: NotRequired[str | None]
161
+ # F-08 English twins: "" / {} = no translation (the original is English
162
+ # or not yet translated). Servers before mandates/0047 omit them.
163
+ # payout_curve_en is keyed by path ("points.<i>.metric_level",
164
+ # "overlays.<i>.description", ...), disclosure_note_en translates
165
+ # disclosure["note"].
166
+ label_en: NotRequired[str]
167
+ target_value_en: NotRequired[str]
168
+ achieved_value_en: NotRequired[str]
169
+ min_value_en: NotRequired[str]
170
+ max_value_en: NotRequired[str]
171
+ plan_vintage_en: NotRequired[str]
172
+ disclosure_note_en: NotRequired[str]
173
+ payout_curve_en: NotRequired[dict[str, str]]
158
174
 
159
175
 
160
176
  class ComponentAchievementRate(TypedDict):
@@ -173,6 +189,15 @@ class ComponentAchievementRate(TypedDict):
173
189
  rate_basis: str
174
190
  disclosure: DisclosureMap
175
191
  payout_curve: PayoutCurvePayload | None
192
+ achievement_plan: NotRequired[str]
193
+ quote: NotRequired[str]
194
+ page: NotRequired[int | None]
195
+ # F-08 English twins: "" / {} = no translation. Servers before
196
+ # mandates/0047 omit them.
197
+ achievement_plan_en: NotRequired[str]
198
+ quote_en: NotRequired[str]
199
+ disclosure_note_en: NotRequired[str]
200
+ payout_curve_en: NotRequired[dict[str, str]]
176
201
 
177
202
 
178
203
  class CriteriaFamilyWeightPayload(TypedDict):
@@ -228,7 +253,22 @@ class CriterionWritePayload(TypedDict):
228
253
  max_value: NotRequired[str | None]
229
254
  payout_curve: NotRequired[PayoutCurvePayload | None]
230
255
  disclosure: NotRequired[DisclosureMap | None]
256
+ plan_stage: NotRequired[str | None] # grant | vested
257
+ plan_vintage: NotRequired[str | None]
231
258
  field_sources: NotRequired[list[FieldSourcePayload]]
259
+ # F-08 English twins (server >= mandates/0047). Send one only when its
260
+ # original is not English. Absent or null keeps the stored English only
261
+ # while the original is unchanged; "" clears it; a value must translate
262
+ # the originals in THIS payload (400 on an empty original or on a
263
+ # payout_curve_en path that does not resolve to a non-empty text).
264
+ label_en: NotRequired[str | None]
265
+ target_value_en: NotRequired[str | None]
266
+ achieved_value_en: NotRequired[str | None]
267
+ min_value_en: NotRequired[str | None]
268
+ max_value_en: NotRequired[str | None]
269
+ plan_vintage_en: NotRequired[str | None]
270
+ disclosure_note_en: NotRequired[str | None] # translates disclosure["note"]
271
+ payout_curve_en: NotRequired[dict[str, str] | None]
232
272
 
233
273
 
234
274
  class ComponentAchievementRateWritePayload(TypedDict):
@@ -249,7 +289,19 @@ class ComponentAchievementRateWritePayload(TypedDict):
249
289
  rate_basis: NotRequired[str | None]
250
290
  disclosure: NotRequired[DisclosureMap | None]
251
291
  payout_curve: NotRequired[PayoutCurvePayload | None]
292
+ achievement_plan: NotRequired[str | None]
293
+ quote: NotRequired[str | None]
294
+ page: NotRequired[int | None]
252
295
  field_sources: NotRequired[list[FieldSourcePayload]]
296
+ # F-08 English twins (server >= mandates/0047). Send one only when its
297
+ # original is not English. Absent or null keeps the stored English only
298
+ # while the original is unchanged; "" clears it; a value must translate
299
+ # the originals in THIS payload (400 on an empty original or on a
300
+ # payout_curve_en path that does not resolve to a non-empty text).
301
+ achievement_plan_en: NotRequired[str | None]
302
+ quote_en: NotRequired[str | None]
303
+ disclosure_note_en: NotRequired[str | None] # translates disclosure["note"]
304
+ payout_curve_en: NotRequired[dict[str, str] | None]
253
305
 
254
306
 
255
307
  # -- Comex compensation --
@@ -308,6 +360,7 @@ class ComexCompensationItem(TypedDict):
308
360
  lti_grant_target_basis: str
309
361
  lti_grant_target_value: str | None
310
362
  lti_grant_cap_kind: str
363
+ lti_max: str | None
311
364
  performance_shares_number_level: str
312
365
  performance_shares_level: str
313
366
  policy_changed_midyear: bool | None
@@ -373,6 +426,7 @@ class ComexCompensationDetail(TypedDict):
373
426
  lti_grant_target_basis: str
374
427
  lti_grant_target_value: str | None
375
428
  lti_grant_cap_kind: str
429
+ lti_max: str | None
376
430
  performance_shares_number_level: str
377
431
  performance_shares_level: str
378
432
  policy_changed_midyear: bool | None
@@ -437,6 +491,7 @@ class CreateComexCompensationPayload(TypedDict):
437
491
  lti_grant_target_basis: NotRequired[str]
438
492
  lti_grant_target_value: NotRequired[str | None]
439
493
  lti_grant_cap_kind: NotRequired[str]
494
+ lti_max: NotRequired[str | None]
440
495
  performance_shares_number_level: NotRequired[str]
441
496
  performance_shares_level: NotRequired[str]
442
497
  policy_changed_midyear: NotRequired[bool | None]
@@ -498,6 +553,7 @@ class UpdateComexCompensationPayload(TypedDict, total=False):
498
553
  lti_grant_target_basis: str
499
554
  lti_grant_target_value: str | None
500
555
  lti_grant_cap_kind: str
556
+ lti_max: str | None
501
557
  performance_shares_number_level: str
502
558
  performance_shares_level: str
503
559
  policy_changed_midyear: bool | None
@@ -640,6 +696,7 @@ class ComexCompensationExportItem(TypedDict, total=False):
640
696
  lti_grant_target_basis: str
641
697
  lti_grant_target_value: str | None
642
698
  lti_grant_cap_kind: str
699
+ lti_max: str | None
643
700
  performance_shares_number_level: str
644
701
  performance_shares_level: str
645
702
  policy_changed_midyear: bool | None
@@ -73,6 +73,15 @@ class FieldSourcePayload(TypedDict):
73
73
  extracted_by: NotRequired[str]
74
74
  notes: NotRequired[str]
75
75
  derivation: NotRequired[DerivationPayload | None]
76
+ # F-08 English twins (server >= core/0035). Send one only when its
77
+ # original is not English. Absent or null keeps the stored English only
78
+ # while the original is unchanged; "" clears it; a value must translate
79
+ # the originals in THIS entry (a text equal to its original is stored
80
+ # as ""). derivation_en is keyed by path: "formula_display",
81
+ # "inputs.<i>.quote", "missing_inputs.<i>".
82
+ extracted_value_en: NotRequired[str | None]
83
+ notes_en: NotRequired[str | None]
84
+ derivation_en: NotRequired[dict[str, str] | None]
76
85
 
77
86
 
78
87
  # -- Shared response types --
@@ -122,6 +131,11 @@ class FieldSourceRecord(TypedDict):
122
131
  extracted_by: str
123
132
  notes: str
124
133
  derivation: DerivationPayload | None
134
+ # F-08 English twins: "" / {} = no translation (the original is English
135
+ # or not yet translated). Servers before core/0035 omit them.
136
+ extracted_value_en: NotRequired[str]
137
+ notes_en: NotRequired[str]
138
+ derivation_en: NotRequired[dict[str, str]]
125
139
  # Normalized provenance bundle (slice 7a), merged server-side:
126
140
  source: str
127
141
  provenance: str
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes