thisness 0.2.0__tar.gz → 0.3.0__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 (246) hide show
  1. {thisness-0.2.0 → thisness-0.3.0}/CHANGELOG.md +15 -0
  2. {thisness-0.2.0 → thisness-0.3.0}/PKG-INFO +10 -4
  3. {thisness-0.2.0 → thisness-0.3.0}/README.md +9 -3
  4. thisness-0.3.0/docs/design/09-real-people.md +294 -0
  5. {thisness-0.2.0 → thisness-0.3.0}/docs/vision.md +4 -4
  6. {thisness-0.2.0 → thisness-0.3.0}/pyproject.toml +1 -1
  7. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/benchmark/__main__.py +20 -0
  8. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/benchmark/data/README.md +7 -0
  9. thisness-0.3.0/src/thisness/benchmark/data/edgar/README.md +7 -0
  10. thisness-0.3.0/src/thisness/benchmark/data/edgar/former_names.parquet +0 -0
  11. thisness-0.3.0/src/thisness/benchmark/data/edgar/names.parquet +0 -0
  12. thisness-0.3.0/src/thisness/benchmark/data/edgar/people.parquet +0 -0
  13. thisness-0.3.0/src/thisness/benchmark/data/edgar/places.parquet +0 -0
  14. thisness-0.3.0/src/thisness/benchmark/data/edgar/roles.parquet +0 -0
  15. thisness-0.3.0/src/thisness/benchmark/edgar.py +706 -0
  16. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/benchmark/evaluate.py +43 -0
  17. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/cli.py +56 -8
  18. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/comparisons/person.py +58 -15
  19. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/normalize/person.py +73 -8
  20. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/profiles/data/person_starter.json +44 -39
  21. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/profiles/person.py +16 -3
  22. {thisness-0.2.0 → thisness-0.3.0}/tests/test_cli.py +15 -0
  23. thisness-0.3.0/tests/test_edgar.py +281 -0
  24. thisness-0.3.0/tests/test_name_order.py +130 -0
  25. {thisness-0.2.0 → thisness-0.3.0}/uv.lock +1 -1
  26. {thisness-0.2.0 → thisness-0.3.0}/.github/workflows/ci.yml +0 -0
  27. {thisness-0.2.0 → thisness-0.3.0}/.github/workflows/publish.yml +0 -0
  28. {thisness-0.2.0 → thisness-0.3.0}/.gitignore +0 -0
  29. {thisness-0.2.0 → thisness-0.3.0}/LICENSE +0 -0
  30. {thisness-0.2.0 → thisness-0.3.0}/docs/design/01-resolution-runs.md +0 -0
  31. {thisness-0.2.0 → thisness-0.3.0}/docs/design/02-core-model-and-storage.md +0 -0
  32. {thisness-0.2.0 → thisness-0.3.0}/docs/design/03-resolution-pipeline.md +0 -0
  33. {thisness-0.2.0 → thisness-0.3.0}/docs/design/04-m2-review-dossier-workbench-person.md +0 -0
  34. {thisness-0.2.0 → thisness-0.3.0}/docs/design/05-m3-llm-layer.md +0 -0
  35. {thisness-0.2.0 → thisness-0.3.0}/docs/design/06-m4-taxonomy.md +0 -0
  36. {thisness-0.2.0 → thisness-0.3.0}/docs/design/07-m5-real-organizations.md +0 -0
  37. {thisness-0.2.0 → thisness-0.3.0}/docs/design/08-supplier-lists.md +0 -0
  38. {thisness-0.2.0 → thisness-0.3.0}/docs/design/sketches/02_schema.sql +0 -0
  39. {thisness-0.2.0 → thisness-0.3.0}/docs/design/sketches/02_schema_check.out +0 -0
  40. {thisness-0.2.0 → thisness-0.3.0}/docs/design/sketches/02_schema_check.py +0 -0
  41. {thisness-0.2.0 → thisness-0.3.0}/docs/design/sketches/03_fs_sketch.out +0 -0
  42. {thisness-0.2.0 → thisness-0.3.0}/docs/design/sketches/03_fs_sketch.py +0 -0
  43. {thisness-0.2.0 → thisness-0.3.0}/docs/design/sketches/consensus_sketch.out +0 -0
  44. {thisness-0.2.0 → thisness-0.3.0}/docs/design/sketches/consensus_sketch.py +0 -0
  45. {thisness-0.2.0 → thisness-0.3.0}/docs/prd-appendix-a-llm-resolution.md +0 -0
  46. {thisness-0.2.0 → thisness-0.3.0}/docs/prd.md +0 -0
  47. {thisness-0.2.0 → thisness-0.3.0}/docs/research/entity-resolution-landscape.md +0 -0
  48. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/__init__.py +0 -0
  49. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/_canonical.py +0 -0
  50. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/adjudicate.py +0 -0
  51. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/assistant.py +0 -0
  52. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/benchmark/__init__.py +0 -0
  53. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/benchmark/data/uk/README.md +0 -0
  54. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/benchmark/data/uk/ch.parquet +0 -0
  55. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/benchmark/data/uk/gleif.parquet +0 -0
  56. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/benchmark/data/uk/gleif_rr.parquet +0 -0
  57. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/benchmark/data/wfo_fixture.csv.gz +0 -0
  58. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/benchmark/oracle.py +0 -0
  59. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/benchmark/organizations.py +0 -0
  60. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/benchmark/people.py +0 -0
  61. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/benchmark/rewrite.py +0 -0
  62. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/benchmark/taxa.py +0 -0
  63. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/benchmark/uk.py +0 -0
  64. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/browse.py +0 -0
  65. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/candidates.py +0 -0
  66. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/canonicalize.py +0 -0
  67. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/comparisons/__init__.py +0 -0
  68. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/comparisons/base.py +0 -0
  69. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/comparisons/organization.py +0 -0
  70. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/comparisons/taxon.py +0 -0
  71. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/consensus.py +0 -0
  72. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/core/__init__.py +0 -0
  73. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/core/base.py +0 -0
  74. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/core/components.py +0 -0
  75. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/core/decisions.py +0 -0
  76. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/core/evidence.py +0 -0
  77. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/core/identity.py +0 -0
  78. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/core/invocation.py +0 -0
  79. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/core/ledger.py +0 -0
  80. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/core/observation.py +0 -0
  81. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/core/review.py +0 -0
  82. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/core/run.py +0 -0
  83. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/core/types.py +0 -0
  84. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/display_names.py +0 -0
  85. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/dossier.py +0 -0
  86. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/edges.py +0 -0
  87. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/engine.py +0 -0
  88. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/fellegi_sunter.py +0 -0
  89. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ids.py +0 -0
  90. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/labels.py +0 -0
  91. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/llm/__init__.py +0 -0
  92. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/llm/client.py +0 -0
  93. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/llm/prices.py +0 -0
  94. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/llm/privacy.py +0 -0
  95. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/llm/prompts/.keep +0 -0
  96. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/llm/prompts/adjudicate_pairs.v1.md +0 -0
  97. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/llm/prompts/adjudicate_pairs.v2.md +0 -0
  98. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/llm/prompts/group_names.v1.md +0 -0
  99. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/llm/prompts/group_names.v2.md +0 -0
  100. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/llm/prompts/recommend_review.v1.md +0 -0
  101. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/llm/prompts/recommend_review.v2.md +0 -0
  102. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/llm/prompts/suggest_labels.v1.md +0 -0
  103. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/llm/prompts.py +0 -0
  104. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/llm/providers/__init__.py +0 -0
  105. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/llm/providers/anthropic.py +0 -0
  106. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/llm/providers/function.py +0 -0
  107. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/llm/providers/openai_compatible.py +0 -0
  108. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/llm/providers/replay.py +0 -0
  109. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/llm/types.py +0 -0
  110. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/name_only.py +0 -0
  111. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/normalize/__init__.py +0 -0
  112. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/normalize/organization.py +0 -0
  113. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/normalize/taxon.py +0 -0
  114. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/policy.py +0 -0
  115. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/profiles/__init__.py +0 -0
  116. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/profiles/base.py +0 -0
  117. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/profiles/data/organization_starter.json +0 -0
  118. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/profiles/data/taxon_starter.json +0 -0
  119. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/profiles/organization.py +0 -0
  120. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/profiles/taxon.py +0 -0
  121. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/py.typed +0 -0
  122. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/relationships.py +0 -0
  123. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/reviews.py +0 -0
  124. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/scoring.py +0 -0
  125. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/starter.py +0 -0
  126. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/storage/__init__.py +0 -0
  127. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/storage/_commit.py +0 -0
  128. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/storage/duckdb/__init__.py +0 -0
  129. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/storage/duckdb/invariants.py +0 -0
  130. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/storage/duckdb/migrations/0001_initial.sql +0 -0
  131. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/storage/duckdb/migrations/0002_pair_constraints.sql +0 -0
  132. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/storage/duckdb/migrations/0003_model_invocations.sql +0 -0
  133. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/storage/duckdb/store.py +0 -0
  134. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/storage/errors.py +0 -0
  135. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/storage/protocol.py +0 -0
  136. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/training.py +0 -0
  137. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/__init__.py +0 -0
  138. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/app.py +0 -0
  139. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/THIRD_PARTY.md +0 -0
  140. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/app.css +0 -0
  141. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/app.js +0 -0
  142. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/adjustments-horizontal.svg +0 -0
  143. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/alert-triangle.svg +0 -0
  144. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/arrow-back-up.svg +0 -0
  145. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/arrows-join.svg +0 -0
  146. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/arrows-split.svg +0 -0
  147. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/briefcase.svg +0 -0
  148. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/building.svg +0 -0
  149. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/cake.svg +0 -0
  150. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/chart-dots.svg +0 -0
  151. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/chevron-down.svg +0 -0
  152. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/chevron-right.svg +0 -0
  153. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/circle-check.svg +0 -0
  154. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/circle-dashed.svg +0 -0
  155. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/circle-x.svg +0 -0
  156. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/clock.svg +0 -0
  157. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/database.svg +0 -0
  158. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/download.svg +0 -0
  159. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/external-link.svg +0 -0
  160. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/file-text.svg +0 -0
  161. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/filter.svg +0 -0
  162. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/fingerprint.svg +0 -0
  163. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/git-branch.svg +0 -0
  164. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/git-merge.svg +0 -0
  165. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/hash.svg +0 -0
  166. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/history.svg +0 -0
  167. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/home.svg +0 -0
  168. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/id.svg +0 -0
  169. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/layout-sidebar-right.svg +0 -0
  170. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/leaf.svg +0 -0
  171. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/link.svg +0 -0
  172. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/list-check.svg +0 -0
  173. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/mail.svg +0 -0
  174. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/map-pin.svg +0 -0
  175. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/message-question.svg +0 -0
  176. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/phone.svg +0 -0
  177. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/player-play.svg +0 -0
  178. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/plus.svg +0 -0
  179. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/scale.svg +0 -0
  180. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/search.svg +0 -0
  181. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/sparkles.svg +0 -0
  182. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/timeline-event.svg +0 -0
  183. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/user.svg +0 -0
  184. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/users.svg +0 -0
  185. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/world.svg +0 -0
  186. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/icons/x.svg +0 -0
  187. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/theme.css +0 -0
  188. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/vendor/Sortable.min.js +0 -0
  189. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/vendor/htmx.min.js +0 -0
  190. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/vendor/inter-latin-ext-wght-normal.woff2 +0 -0
  191. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/static/vendor/inter-latin-wght-normal.woff2 +0 -0
  192. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/templates/_macros.html +0 -0
  193. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/templates/_panel_head.html +0 -0
  194. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/templates/base.html +0 -0
  195. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/templates/entities.html +0 -0
  196. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/templates/entity.html +0 -0
  197. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/templates/entity_tab.html +0 -0
  198. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/templates/llm.html +0 -0
  199. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/templates/model.html +0 -0
  200. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/templates/not_found.html +0 -0
  201. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/templates/panel_candidate.html +0 -0
  202. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/templates/panel_decision.html +0 -0
  203. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/templates/panel_entity.html +0 -0
  204. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/templates/panel_invocation.html +0 -0
  205. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/templates/panel_observation.html +0 -0
  206. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/templates/relationships.html +0 -0
  207. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/templates/review.html +0 -0
  208. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/templates/reviews.html +0 -0
  209. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/templates/runs.html +0 -0
  210. {thisness-0.2.0 → thisness-0.3.0}/src/thisness/ui/templates/search_results.html +0 -0
  211. {thisness-0.2.0 → thisness-0.3.0}/tests/__init__.py +0 -0
  212. {thisness-0.2.0 → thisness-0.3.0}/tests/conftest.py +0 -0
  213. {thisness-0.2.0 → thisness-0.3.0}/tests/storage_helpers.py +0 -0
  214. {thisness-0.2.0 → thisness-0.3.0}/tests/synthetic.py +0 -0
  215. {thisness-0.2.0 → thisness-0.3.0}/tests/test_adjudicate.py +0 -0
  216. {thisness-0.2.0 → thisness-0.3.0}/tests/test_assistant.py +0 -0
  217. {thisness-0.2.0 → thisness-0.3.0}/tests/test_benchmark.py +0 -0
  218. {thisness-0.2.0 → thisness-0.3.0}/tests/test_candidates.py +0 -0
  219. {thisness-0.2.0 → thisness-0.3.0}/tests/test_canonical.py +0 -0
  220. {thisness-0.2.0 → thisness-0.3.0}/tests/test_cleaning.py +0 -0
  221. {thisness-0.2.0 → thisness-0.3.0}/tests/test_comparisons.py +0 -0
  222. {thisness-0.2.0 → thisness-0.3.0}/tests/test_consensus.py +0 -0
  223. {thisness-0.2.0 → thisness-0.3.0}/tests/test_constraints.py +0 -0
  224. {thisness-0.2.0 → thisness-0.3.0}/tests/test_core_models.py +0 -0
  225. {thisness-0.2.0 → thisness-0.3.0}/tests/test_display_names.py +0 -0
  226. {thisness-0.2.0 → thisness-0.3.0}/tests/test_dossier.py +0 -0
  227. {thisness-0.2.0 → thisness-0.3.0}/tests/test_engine.py +0 -0
  228. {thisness-0.2.0 → thisness-0.3.0}/tests/test_engine_plan.py +0 -0
  229. {thisness-0.2.0 → thisness-0.3.0}/tests/test_fellegi_sunter.py +0 -0
  230. {thisness-0.2.0 → thisness-0.3.0}/tests/test_ids.py +0 -0
  231. {thisness-0.2.0 → thisness-0.3.0}/tests/test_llm.py +0 -0
  232. {thisness-0.2.0 → thisness-0.3.0}/tests/test_mapping.py +0 -0
  233. {thisness-0.2.0 → thisness-0.3.0}/tests/test_messy.py +0 -0
  234. {thisness-0.2.0 → thisness-0.3.0}/tests/test_name_only.py +0 -0
  235. {thisness-0.2.0 → thisness-0.3.0}/tests/test_normalize_organization.py +0 -0
  236. {thisness-0.2.0 → thisness-0.3.0}/tests/test_person.py +0 -0
  237. {thisness-0.2.0 → thisness-0.3.0}/tests/test_prompt_hints.py +0 -0
  238. {thisness-0.2.0 → thisness-0.3.0}/tests/test_relationships_labels.py +0 -0
  239. {thisness-0.2.0 → thisness-0.3.0}/tests/test_reviews.py +0 -0
  240. {thisness-0.2.0 → thisness-0.3.0}/tests/test_rewrite.py +0 -0
  241. {thisness-0.2.0 → thisness-0.3.0}/tests/test_store.py +0 -0
  242. {thisness-0.2.0 → thisness-0.3.0}/tests/test_taxon.py +0 -0
  243. {thisness-0.2.0 → thisness-0.3.0}/tests/test_training.py +0 -0
  244. {thisness-0.2.0 → thisness-0.3.0}/tests/test_ui.py +0 -0
  245. {thisness-0.2.0 → thisness-0.3.0}/tests/test_uk.py +0 -0
  246. {thisness-0.2.0 → thisness-0.3.0}/tests/test_upgrade.py +0 -0
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.0 (2026-10-16)
4
+
5
+ Real people (design 09).
6
+
7
+ - **Name order.** A person record's `name_order` attribute (`given_first`, the default, or `family_first`) says how its source writes a name without a comma; registries such as SEC EDGAR write "SMITH JOHN A JR". A family-first name of three words or more also gets the reading with a compound family name ("Pineyro Sublett Carla" as Carla Pineyro Sublett), which proposes candidates and counts as a `fuzzy` name match. A family-first name is also a blocking key with its order swapped, since registries are not always consistent. Person name normalizer and comparison version 2; the person starter model is rebuilt. Given-first names are unaffected.
8
+ - **Person name comparison** (version 2) also:
9
+ - asks a Soundex match to be close by Jaro-Winkler (.8), since Soundex alone pairs "Buchanan" with "Beckmann";
10
+ - makes a near match with a different middle initial or suffix a `middle_conflict`, as an exact one is;
11
+ - gives a suffix on one side only its own level, `suffix_one_side`, capped at 4 bits: a dropped "Jr" and a father and son at one company look the same, so such pairs go to review.
12
+
13
+ The `initial` level is capped at 8 bits. On 10,000 real people, false-merge clusters fall from 51 to 29 (precision .9967 to .9985, recall .726 to .715).
14
+ - **Given name and employer** is a new person blocking key (`given_employer`): where there is no date of birth, it finds family names that changed or were mistyped. Colleagues share employers, so EM does not learn from its pairs.
15
+ - **Benchmarks.** `python -m thisness.benchmark edgar-stage --store PATH` stages SEC Section 16 insiders from an edgar-persons-pool store; `edgar-truth` and `edgar-audit` measure the CIK as truth; `thisness eval --type person --source edgar` runs the benchmark: real people and namesakes, each with their EDGAR records and up to three records from other systems written as those write people (nicknames, initials, dropped suffixes, typos, former names, employers without their legal form). A fixture of about 150 insiders ships for tests. `thisness eval --assistant` has the review assistant answer every review a run leaves open and scores its suggestions against the truth (`benchmark.evaluate.recommendation_accuracy`). On 1,000 real people gpt-6-luna deciding pairs raised false-merge clusters from 2 to 18, while its review suggestions were right 86% of the time; people keep `model_role="suggest"`.
16
+ - **Upgrading.** A store built with 0.2.0 keeps working: opening it recomputes person features with the new normalizer, and a person model fitted for the old comparisons is replaced on the next resolve.
17
+
3
18
  ## 0.2.0 (2026-10-15)
4
19
 
5
20
  Supplier lists (design 08).
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: thisness
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: Identity resolution for imperfect observations: evidence, stable identities, reversible history.
5
5
  Project-URL: Homepage, https://thisness.dev
6
6
  Project-URL: Repository, https://github.com/mfbaig35r/thisness
@@ -84,7 +84,7 @@ Every decision is stored with its evidence; merges and splits are reversible led
84
84
  A domain is a resolution profile: its attributes, normalizers, comparisons, constraints and the guidance a model is given. Three ship.
85
85
 
86
86
  - **Organizations** (`thisness.profiles.organization`): names with legal forms, abbreviations, acronyms and group-company qualifiers; websites, emails, registration IDs, addresses. A shared brand or a group's sibling companies are relationship candidates, never merges.
87
- - **People** (`thisness.profiles.person`): names with nicknames, initials and spelling variants; dates of birth, emails, phones, addresses. Same name at the same address (a household, a parent and child) stays a relationship until evidence says otherwise.
87
+ - **People** (`thisness.profiles.person`): names with nicknames, initials and spelling variants; dates of birth, emails, phones, addresses. Same name at the same address (a household, a parent and child) stays a relationship until evidence says otherwise. A source that writes names family first, as registries such as SEC EDGAR do ("SMITH JOHN A JR"), says so with a `name_order` attribute.
88
88
  - **Taxa** (`thisness.profiles.taxon`): scientific names parsed into genus, epithets, rank and authorship. Synonyms join their accepted taxon when an authority such as World Flora Online or the Catalogue of Life is loaded, and every label says which authority supplied it.
89
89
 
90
90
  ## From the command line
@@ -120,7 +120,7 @@ Set `--budget`: there is no default limit.
120
120
 
121
121
  ## How well it works
122
122
 
123
- `thisness eval` resolves a benchmark with known truth and reports precision, recall and false-merge clusters. Measured so far (design 07 §13; design 06 §10):
123
+ `thisness eval` resolves a benchmark with known truth and reports precision, recall and false-merge clusters. Measured so far (design 09 §11; design 07 §13; design 06 §10):
124
124
 
125
125
  | Benchmark | Records | P | R | False-merge clusters |
126
126
  |---|---|---|---|---|
@@ -128,6 +128,8 @@ Set `--budget`: there is no default limit.
128
128
  | The same, gpt-6-luna deciding contested pairs ($0.62) | 3,507 | .9965 | .938 | 5 |
129
129
  | Real UK companies, at scale | 46,565 | .9965 | .934 | 63 |
130
130
  | Synthetic organizations, 4,000 | | .9995 | .938 | 4 |
131
+ | Real people: 10,000 SEC Section 16 insiders (EDGAR), no model | 28,692 | .9985 | .715 | 29 |
132
+ | The same, every contested pair answered correctly | 28,692 | .9992 | .948 | 15 |
131
133
 
132
134
  ```bash
133
135
  thisness eval --size 805 4000 # synthetic organizations (or --type person)
@@ -139,10 +141,13 @@ thisness eval --source uk --adjudicator openai --model gpt-6-luna --mode decide
139
141
 
140
142
  The UK benchmark's hard cases are real: 62% of live UK companies share a registered address, and a record that says only "Sports Direct" sits beside four real Sports Direct companies. The record variations (shortened names, typos, partial addresses) are generated from the true company, so the truth is known.
141
143
 
144
+ The people benchmark is real company insiders and their real namesakes: one insider name in sixteen is shared, and a father and son often sit on one board. It is staged from a local [edgar-persons-pool](docs/design/09-real-people.md) store (`python -m thisness.benchmark edgar-stage --store PATH`, then `thisness eval --type person --source edgar`); a fixture of about 150 insiders ships for tests. `thisness eval --assistant` scores the review assistant's suggestions against the truth.
145
+
142
146
  ## Known limits
143
147
 
144
148
  - **Memory grows with candidate pairs.** 46,565 records took 7.5 minutes of CPU and 7.8 GB peak memory; scoring holds every candidate pair in Python. Tested to about fifty thousand records.
145
149
  - **Candidates come from blocking keys.** There is no embedding-based candidate generator yet, so two records that share no blocking key (a renamed company with nothing else in common, a heterotypic synonym with no authority loaded) are never compared directly.
150
+ - **For people, the model suggests.** On 1,000 real people, gpt-6-luna deciding contested pairs merged namesakes (false-merge clusters 2 to 18); as the review assistant, 86% of its suggestions would be right. People default to `model_role="suggest"`.
146
151
  - **Model costs are real.** About $0.62 for 400 UK companies with gpt-6-luna; the default model is `claude-sonnet-5`. Use `--budget`, and `--replay` to re-run without paying again.
147
152
  - **The network benchmarks download data.** WFO families beyond the shipped three, the Catalogue of Life, and the UK sources are fetched on demand and cached under `~/.cache/thisness`. Everything else, including the Workbench, runs offline.
148
153
 
@@ -168,6 +173,7 @@ The UK benchmark's hard cases are real: 62% of live UK companies share a registe
168
173
  - [06 taxonomy, the third domain](docs/design/06-m4-taxonomy.md)
169
174
  - [07 real organizations](docs/design/07-m5-real-organizations.md)
170
175
  - [08 supplier lists](docs/design/08-supplier-lists.md)
176
+ - [09 real people](docs/design/09-real-people.md)
171
177
  - [Changelog](CHANGELOG.md)
172
178
 
173
179
  ## Development
@@ -184,4 +190,4 @@ Releases publish to PyPI from a version tag: bump `version` in `pyproject.toml`,
184
190
 
185
191
  Thisness is Apache-2.0. See [LICENSE](LICENSE).
186
192
 
187
- Bundled and fetched data keep their own terms, credited where they ship: World Flora Online (CC0 1.0) in `benchmark/data`; GLEIF Golden Copy (CC0 1.0) and Companies House data (Open Government Licence v3.0) in `benchmark/data/uk`; the Catalogue of Life (CC BY 4.0), fetched on demand with attribution; and the Workbench's fonts, icons and scripts in `ui/static/THIRD_PARTY.md`.
193
+ Bundled and fetched data keep their own terms, credited where they ship: World Flora Online (CC0 1.0) in `benchmark/data`; GLEIF Golden Copy (CC0 1.0) and Companies House data (Open Government Licence v3.0) in `benchmark/data/uk`; SEC EDGAR Section 16 filings (US government records) in `benchmark/data/edgar`; the Catalogue of Life (CC BY 4.0), fetched on demand with attribution; and the Workbench's fonts, icons and scripts in `ui/static/THIRD_PARTY.md`.
@@ -52,7 +52,7 @@ Every decision is stored with its evidence; merges and splits are reversible led
52
52
  A domain is a resolution profile: its attributes, normalizers, comparisons, constraints and the guidance a model is given. Three ship.
53
53
 
54
54
  - **Organizations** (`thisness.profiles.organization`): names with legal forms, abbreviations, acronyms and group-company qualifiers; websites, emails, registration IDs, addresses. A shared brand or a group's sibling companies are relationship candidates, never merges.
55
- - **People** (`thisness.profiles.person`): names with nicknames, initials and spelling variants; dates of birth, emails, phones, addresses. Same name at the same address (a household, a parent and child) stays a relationship until evidence says otherwise.
55
+ - **People** (`thisness.profiles.person`): names with nicknames, initials and spelling variants; dates of birth, emails, phones, addresses. Same name at the same address (a household, a parent and child) stays a relationship until evidence says otherwise. A source that writes names family first, as registries such as SEC EDGAR do ("SMITH JOHN A JR"), says so with a `name_order` attribute.
56
56
  - **Taxa** (`thisness.profiles.taxon`): scientific names parsed into genus, epithets, rank and authorship. Synonyms join their accepted taxon when an authority such as World Flora Online or the Catalogue of Life is loaded, and every label says which authority supplied it.
57
57
 
58
58
  ## From the command line
@@ -88,7 +88,7 @@ Set `--budget`: there is no default limit.
88
88
 
89
89
  ## How well it works
90
90
 
91
- `thisness eval` resolves a benchmark with known truth and reports precision, recall and false-merge clusters. Measured so far (design 07 §13; design 06 §10):
91
+ `thisness eval` resolves a benchmark with known truth and reports precision, recall and false-merge clusters. Measured so far (design 09 §11; design 07 §13; design 06 §10):
92
92
 
93
93
  | Benchmark | Records | P | R | False-merge clusters |
94
94
  |---|---|---|---|---|
@@ -96,6 +96,8 @@ Set `--budget`: there is no default limit.
96
96
  | The same, gpt-6-luna deciding contested pairs ($0.62) | 3,507 | .9965 | .938 | 5 |
97
97
  | Real UK companies, at scale | 46,565 | .9965 | .934 | 63 |
98
98
  | Synthetic organizations, 4,000 | | .9995 | .938 | 4 |
99
+ | Real people: 10,000 SEC Section 16 insiders (EDGAR), no model | 28,692 | .9985 | .715 | 29 |
100
+ | The same, every contested pair answered correctly | 28,692 | .9992 | .948 | 15 |
99
101
 
100
102
  ```bash
101
103
  thisness eval --size 805 4000 # synthetic organizations (or --type person)
@@ -107,10 +109,13 @@ thisness eval --source uk --adjudicator openai --model gpt-6-luna --mode decide
107
109
 
108
110
  The UK benchmark's hard cases are real: 62% of live UK companies share a registered address, and a record that says only "Sports Direct" sits beside four real Sports Direct companies. The record variations (shortened names, typos, partial addresses) are generated from the true company, so the truth is known.
109
111
 
112
+ The people benchmark is real company insiders and their real namesakes: one insider name in sixteen is shared, and a father and son often sit on one board. It is staged from a local [edgar-persons-pool](docs/design/09-real-people.md) store (`python -m thisness.benchmark edgar-stage --store PATH`, then `thisness eval --type person --source edgar`); a fixture of about 150 insiders ships for tests. `thisness eval --assistant` scores the review assistant's suggestions against the truth.
113
+
110
114
  ## Known limits
111
115
 
112
116
  - **Memory grows with candidate pairs.** 46,565 records took 7.5 minutes of CPU and 7.8 GB peak memory; scoring holds every candidate pair in Python. Tested to about fifty thousand records.
113
117
  - **Candidates come from blocking keys.** There is no embedding-based candidate generator yet, so two records that share no blocking key (a renamed company with nothing else in common, a heterotypic synonym with no authority loaded) are never compared directly.
118
+ - **For people, the model suggests.** On 1,000 real people, gpt-6-luna deciding contested pairs merged namesakes (false-merge clusters 2 to 18); as the review assistant, 86% of its suggestions would be right. People default to `model_role="suggest"`.
114
119
  - **Model costs are real.** About $0.62 for 400 UK companies with gpt-6-luna; the default model is `claude-sonnet-5`. Use `--budget`, and `--replay` to re-run without paying again.
115
120
  - **The network benchmarks download data.** WFO families beyond the shipped three, the Catalogue of Life, and the UK sources are fetched on demand and cached under `~/.cache/thisness`. Everything else, including the Workbench, runs offline.
116
121
 
@@ -136,6 +141,7 @@ The UK benchmark's hard cases are real: 62% of live UK companies share a registe
136
141
  - [06 taxonomy, the third domain](docs/design/06-m4-taxonomy.md)
137
142
  - [07 real organizations](docs/design/07-m5-real-organizations.md)
138
143
  - [08 supplier lists](docs/design/08-supplier-lists.md)
144
+ - [09 real people](docs/design/09-real-people.md)
139
145
  - [Changelog](CHANGELOG.md)
140
146
 
141
147
  ## Development
@@ -152,4 +158,4 @@ Releases publish to PyPI from a version tag: bump `version` in `pyproject.toml`,
152
158
 
153
159
  Thisness is Apache-2.0. See [LICENSE](LICENSE).
154
160
 
155
- Bundled and fetched data keep their own terms, credited where they ship: World Flora Online (CC0 1.0) in `benchmark/data`; GLEIF Golden Copy (CC0 1.0) and Companies House data (Open Government Licence v3.0) in `benchmark/data/uk`; the Catalogue of Life (CC BY 4.0), fetched on demand with attribution; and the Workbench's fonts, icons and scripts in `ui/static/THIRD_PARTY.md`.
161
+ Bundled and fetched data keep their own terms, credited where they ship: World Flora Online (CC0 1.0) in `benchmark/data`; GLEIF Golden Copy (CC0 1.0) and Companies House data (Open Government Licence v3.0) in `benchmark/data/uk`; SEC EDGAR Section 16 filings (US government records) in `benchmark/data/edgar`; the Catalogue of Life (CC BY 4.0), fetched on demand with attribution; and the Workbench's fonts, icons and scripts in `ui/static/THIRD_PARTY.md`.
@@ -0,0 +1,294 @@
1
+ # Design 09: real people, from SEC EDGAR
2
+
3
+ Status: accepted 2026-10-15, with the recommendations in section 9. Complete 2026-10-16 at step 5 (§11.5).
4
+
5
+ Builds on: design 04 (the Person profile), design 06 §10.8 to §10.10 (models on people), design 07 (real organizations, whose method this follows) and the edgar-persons-pool store as of its commit 2452789.
6
+
7
+ ## 1. Why
8
+
9
+ Every person result so far is on the synthetic generator. Design 07 found what synthetic organizations hid (shared addresses, numbered vehicles, group siblings) only once real data was measured; people have had no such test. Design 06 §10.8 to §10.10 also left the model's role for people at `suggest` because a model was both too cautious and too credulous on synthetic people; whether that holds on real people is unknown.
10
+
11
+ SEC EDGAR has what a people benchmark needs: real individuals with an identifier. Every Section 16 insider (director, officer, holder of more than 10%) files under a CIK, and their filings name their employer, title and business address. The edgar-persons-pool project has already built a store of them: 1,342,527 filings from 2020 to 2026 and 107,860 individuals classified with confidence 0.85 or more who have filed.
12
+
13
+ **This reverses design 07 §8**, which kept real personal data out of benchmarks. The ground for the change: Section 16 insiders are named by law in a public role, the data is public, and the benchmark keeps only what resolution needs (§7). Section 7 is where the data is limited; decision 1 asks for the reversal to be accepted explicitly.
14
+
15
+ ## 2. What EDGAR is, measured
16
+
17
+ From the edgar-persons-pool store (individuals, confidence 0.85 or more, with Section 16 filings):
18
+
19
+ - **Names are conformed, not as written.** EDGAR writes a filer's name one way for its CIK: of 107,860 filers, 106,990 (99.2%) have a single spelling across all their filings, 856 two, 13 three or more. A benchmark of EDGAR records alone would test almost nothing about spelling.
20
+ - **Namesakes are real and many.** 2,854 names (as token sets, suffixes ignored) are shared by 6,579 filers: "James Miller", "Michael Smith", "Robert Smith", four or more each. 3,440 filers carry a generational suffix (Jr, Sr, II, III). This is the hard case synthetic people imitate and EDGAR supplies.
21
+ - **Names change.** 2,034 filers have former names in EDGAR, often marriage ("Ruhlman Maegan Adams" to "Cross Maegan Adams Ruhlman", "Nye Jean McClung" to "Halloran Jean").
22
+ - **Employers and roles per filing.** 83,827 filers report at one issuer, 24,033 at two or more. Titles are free text ("Chief Financial Officer", "See Remarks", "President and CEO").
23
+ - **Addresses are business addresses**, often the company's own: a reporting owner at Alphabet gives "ALPHABET INC., 1600 AMPHITHEATRE PARKWAY, MOUNTAIN VIEW, CA".
24
+ - **No date of birth, email or phone.** The Person profile's strongest evidence is absent.
25
+ - **The truth has defects.** CIK 0001706713 carries both "Morice Rosemary" and "Counts Travis": a CIK that holds two people. Some people hold two CIKs: 308 names among filers appear under more than one CIK, and how many are one person is unmeasured (§5).
26
+
27
+ ## 3. Name order
28
+
29
+ EDGAR writes names family first with no comma: "SMITH JOHN A", "Vojvodich Radakovich Lynn M". The Person profile's parser reads a name without a comma as given first, so it reads "SMITH JOHN A" as given name "smith", family name "a". Every EDGAR name is misparsed today.
30
+
31
+ The fix is a record attribute, `name_order` (`given_first`, the default, or `family_first`), set by whoever ingests the source: a source's convention is a fact about the source, not something to guess per name. With `family_first`, the first token is the family name, the second the given name, the rest middle names or initials. Compound family names ("Vojvodich Radakovich Lynn", "Owen Adams Catherine") cannot be split from the name alone; the parser also emits the split with a two-token family name as an alternative key for blocking, so either reading finds its candidates, and the comparison scores the better of the two (decision 3). A new version of the name normalizer and comparison; the starter is rebuilt.
32
+
33
+ ## 4. The benchmark
34
+
35
+ Like the UK one (design 07 §5): real people and their real namesakes; records generated the way other systems write them, from the true person.
36
+
37
+ - **Sample.** About n filers (default 1,000): half from namesake groups (with every namesake), an eighth with former names, an eighth with generational suffixes, the rest at random. Kinds: `plain`, `namesake`, `renamed`, `suffix`, `multi_issuer`.
38
+ - **Records per person:**
39
+ - **EDGAR**: the conformed name (`name_order: family_first`), the issuer as employer, the title, and city and state from the filing.
40
+ - **Up to three from other systems** (a CRM, a board list, a screening list), given first: the name with a nickname (from the profile's name groups), a dropped or initialled middle name, a former name for renamed people, a typo, case; the employer as written by others (legal form dropped, abbreviated); city only, state only, or none.
41
+ - **Truth** is the CIK. People whose CIK holds two people (§2) are left out; people holding two CIKs are one person when the audit (§5) says so.
42
+ - **Identifiers.** The CIK is hidden from records by default; `--identifiers partial` shows it on EDGAR records only, as a real feed would.
43
+
44
+ ## 5. Truth audit, before any result
45
+
46
+ As design 07 did before its first measurement:
47
+
48
+ - **A CIK with two people.** Count CIKs whose source records share no family-name token across sources; read a sample; exclude them.
49
+ - **A person with two CIKs.** For the 308 shared names among filers, compare issuers, titles and addresses; read a sample; record which are one person, and how often, before treating every CIK as a distinct person.
50
+ - **Namesakes are different people.** Read a sample of namesake groups to confirm different issuers and places, not one person's two CIKs.
51
+
52
+ ## 6. Evaluations
53
+
54
+ 1. The Person profile as it is (no name order): the cost of misparsing, measured.
55
+ 2. With `name_order` (§3).
56
+ 3. The model on real people: gpt-6-luna deciding contested pairs, and as the review assistant, against the oracle, to revisit `model_role="suggest"` for people (design 06 §10.11).
57
+ 4. At scale: 10,000 filers, for memory and review counts, as design 07 §13.4 did.
58
+
59
+ ## 7. Data handling
60
+
61
+ - **The benchmark is built from a local edgar-persons-pool store** (`python -m thisness.benchmark edgar-stage --store PATH`), which projects what the benchmark needs into `~/.cache/thisness/edgar/edgar.duckdb`. Thisness does not fetch Section 16 filings itself: that is a multi-day, rate-limited crawl the other project already does.
62
+ - **What is kept:** names, former names, CIKs, issuer CIKs and names, titles, role flags, filing dates, city and state. **Not kept:** street addresses and postal codes (a business address identifies a person no better than their employer and city do, and a home address sometimes appears), and nothing outside Section 16.
63
+ - **The shipped fixture** is about 150 people from namesake groups and name changes, with the fields above, for tests. EDGAR filings are US government records with no copyright; the README credits the SEC as a courtesy, as the WFO fixture credits WFO.
64
+ - **Reproducibility for others** (decision 5): edgar-persons-pool is private, so nobody else can build the full benchmark. Options: publish edgar-persons-pool, publish a derived extract with the §7 fields, or keep the full benchmark local and the fixture public.
65
+
66
+ ## 8. Order
67
+
68
+ 1. Staging and the truth audit (§5, §7).
69
+ 2. Name order (§3), and evaluation 1 then 2.
70
+ 3. The benchmark generator (§4) and the fixture.
71
+ 4. Models on real people (evaluation 3).
72
+ 5. Scale (evaluation 4).
73
+
74
+ ## 9. Decisions
75
+
76
+ | # | Question | Recommendation |
77
+ |---|---|---|
78
+ | 1 | Real personal data in a benchmark (reverses design 07 §8) | Yes, for Section 16 insiders only, with the fields of §7 and no street addresses |
79
+ | 2 | Records | Real people and namesakes; records generated as other systems write them, as in design 07 |
80
+ | 3 | Name order | A `name_order` record attribute set by the source, with an alternative split for compound family names |
81
+ | 4 | Staging | From a local edgar-persons-pool store; Thisness does not crawl EDGAR |
82
+ | 5 | Reproducibility for others | Fixture public, full benchmark local, for now; publishing edgar-persons-pool or a derived extract decided after the audit |
83
+ | 6 | Truth | The CIK, with CIKs holding two people excluded and people holding two CIKs merged where the audit confirms it |
84
+ | 7 | Default size | 1,000 sampled filers; 10,000 for scale |
85
+
86
+ ## 10. Risks
87
+
88
+ - **Synthetic records on real people** test the generator's idea of how other systems write names, as design 07's supplier records did. The measured gains are on that; a real second source (a board list, a proxy statement) would be a stronger test, out of scope here.
89
+ - **Business addresses and employers are strong and shared**: two different directors of one issuer share both. Namesakes at one company (a father and son on one board) are the hardest real case, and the profile's generational-suffix constraint is what separates them.
90
+ - **Compound family names** may defeat the alternative split; the audit's sample and evaluation 2 show how often.
91
+
92
+ ## 11. Implementation notes
93
+
94
+ ### 11.1 Step 1: staging and the truth audit
95
+
96
+ `python -m thisness.benchmark edgar-stage --store PATH` attaches the edgar-persons-pool store read only and writes `~/.cache/thisness/edgar/edgar.duckdb` in under two seconds: 107,860 people (individual, confidence 0.85 or more, at least one Section 16 role), 326,531 names from three sources (section16, submissions, cik_lookup), 2,115 former names, 157,433 roles, and 154,839 city and state rows with filing counts. No street, no postal code; a `meta` table records the source store, the time and the classifier version (2). `edgar-truth` and `edgar-audit` report and sample; `benchmark.edgar.truth()` is the truth the generator will use.
97
+
98
+ **Spellings.** Counted on letters only (cik_lookup writes capitals, the filings mixed case), 106,077 CIKs (98.3%) have one spelling, 1,724 two, 59 three or more. The second spellings are what the benchmark wants: middle names added or dropped ("Hennemuth Robert" and "Hennemuth Robert George"), typos ("Woelkers Pau C." and "Woelkers Paul C."), suffixes moved ("DAY II ROWLAND W" and "DAY ROWLAND W II"). EDGAR's own namesake marker, a slash and a state ("MCDONALD JOHN D/MO"), appears on 41 CIKs and is stripped before comparing.
99
+
100
+ **Shared names.** 2,845 name keys (token sets, initials and suffixes ignored) are held by 6,564 CIKs, giving 5,106 pairs. The key is loose on purpose; the pairs were split by name and then by evidence, and samples of each read:
101
+
102
+ | Pairs | Count | What they are, read |
103
+ |---|---|---|
104
+ | Conflicting names (different initials, or a suffix on one) | 3,571 | different people: "Walsh Robert B" and "WALSH ROBERT T.", "PENSKE ROGER S" and "Penske Roger S Jr" |
105
+ | Compatible names, a shared issuer | 33 | one person with two CIKs, every pair read apart from two Hyatt trusts (left out as not people): "ROLLINS RANDALL R" and "ROLLINS R RANDALL" (Rollins Inc), "Carter Miller Jocelyn" and "Carter-Miller Jocelyn" (Interpublic, Backblaze), "Li Ka Shing" and "SHING LI KA" (ChromaDex) |
106
+ | Compatible names, a shared city only | 35 | either: one person whose issuer renamed itself ("Corbus Barclay", Bed Bath & Beyond and Beyond Inc), compound family names reordered ("Pineyro Sublett Carla" and "Sublett Carla Pineyro"), namesakes in a large city ("Zhang Wei", New York) |
107
+ | Compatible names, nothing shared | 1,467 | different people: none of 40 read was clearly one person, most with different careers ("Johnson Allison", PayPal marketing and a Pennsylvania bank CFO) |
108
+
109
+ **One CIK, unrelated names.** 21 CIKs carry two names that share no word. They are not mostly two people:
110
+ - 12 are one person renamed: marriage ("ISBELL JERI L" and "STROMQUIST JERI I"), typos corrected ("NEUKERNONS ARNAND"), nicknames and transliterations ("MEYER SOL" and "Mayer Solomon", "Markowiecki Bartek" and "Makowiecki Bartlomiej", "Bakalova Sofya" and "Garnette Sophia").
111
+ - 5 look like a CIK handed to another person, CIK 0001706713 among them; one ("West Shauna Faegre Drinker Biddle & Reath LLP" to "Varma Atul") was opened in a lawyer's name.
112
+ - 1 has an entity's former name.
113
+ - 3 are entities.
114
+
115
+ **Not people.** About 35 names the edgar-persons-pool classifier (v2) passes as individuals are entities:
116
+ - Danish ApS, Norwegian AS, French SAS and SCS;
117
+ - grantor trusts ("RF 2018 GRAT");
118
+ - SPAC sponsors;
119
+ - "GOULD INVESTORS L P".
120
+
121
+ A rule on those words and on digits finds them. The digit rule also catches 4 people whose EDGAR name carries a number ("Xu Lin 1962", "Isett Thomas Francis 3rd"). The classifier gap is edgar-persons-pool's to fix.
122
+
123
+ **The truth** (`edgar.truth`), from the above:
124
+ - **Left out:**
125
+ - names that are not people (35);
126
+ - CIKs with unrelated names (18 after the entities), since telling the renamed from the reassigned takes reading;
127
+ - both CIKs of the 35 compatible pairs linked only by a city (70).
128
+ - **Merged:** the 33 compatible pairs at a shared issuer.
129
+ - **Result:** 107,737 CIKs kept, 107,705 people.
130
+ - **Different people:** everything else. That is wrong for an unknown few of the 1,467 compatible pairs with nothing shared; the sample read found none clearly.
131
+
132
+ The renamed people left out are the hardest real cases and are worth a reviewed list later. They are few (12), and the 2,034 people with former names (2,115 former names in all) remain for the `renamed` kind.
133
+
134
+ **Other counts.** 3,566 people carry a generational suffix; 107,770 have a city; 24,033 report at two or more issuers.
135
+
136
+ ### 11.2 Step 2: name order, evaluations 1 and 2
137
+
138
+ **Name order.** `name_order` is a person attribute; the name normalizer (version 2) reads a name without a comma family first when it is `family_first`:
139
+ - the first word is the family name, with particles before it ("von Emster Kurt") or, as EDGAR sometimes writes them, after the name ("EMSTER KURT VON");
140
+ - the next word is the given name; the rest are middle names, kept as initials;
141
+ - suffixes may stand anywhere after the family name ("DAY II ROWLAND W").
142
+
143
+ A comma still means "Family, Given" in either order, and a value other than the two is ignored.
144
+
145
+ **The compound reading** (decision 3). A family-first name with a full third word gets a second reading: the third word as the given name and the second as the family name ("Pineyro Sublett Carla" read as Carla, Sublett). That is how a given-first record of the same person parses ("Carla Pineyro Sublett" has family name Sublett, middle initial P), and how EDGAR's own second CIK sometimes writes it ("LOSADA MORENO ANGEL" and "Moreno Angel Losada").
146
+ - The reading is a blocking key (`pname.reading`, a new `name_reading` generator).
147
+ - In the comparison (version 2) it is tried only when the main reading disagrees (`given_only`, `family_only`, `else`). When it agrees, the level is `fuzzy`, as for a given and family name swapped, never `exact`, so "Smith John Albert" read as Albert John is weak support at most.
148
+ - Joining the two words ("pineyrosublett") was left out: no given-first record parses that way.
149
+
150
+ **The benchmark used here** is the minimal one, with step 3's variants still to come. About n people are sampled as §4 says. Each has their EDGAR records (every spelling and former name, family first, the latest issuer as employer, a city) and one record from another system: the conformed name given first ("John A Smith JR"), an issuer 80% of the time, a city 60%. Former names that are trusts or companies ("MCCABE FAMILY TRUST ...") are not records. Default seed 20261016; 1,000 people give about 2,116 records.
151
+
152
+ **Results** (1,000 people, seeds 20261016 and 1; "oracle" is the simulated adjudicator deciding every contested pair):
153
+
154
+ | | B3 precision | B3 recall | Candidate pair recall | Reviews |
155
+ |---|---|---|---|---|
156
+ | Evaluation 1: no name order | .9986 / .9995 | .476 / .477 | .027 / .026 | 53 / 49 |
157
+ | Evaluation 1, oracle | .9986 / .9995 | .481 / .481 | | 7 / 3 |
158
+ | Evaluation 2: name order | .9975 / .9981 | .718 / .710 | .924 / .923 | 506 / 514 |
159
+ | Evaluation 2, oracle | .9975 / .9981 | .939 / .942 | | 58 / 53 |
160
+
161
+ - **Misparsing costs everything.** Without name order an EDGAR record and a given-first record of the same person almost never meet: "SMITH JOHN A" reads as given name Smith, family name A. Recall is the share of people with a single record.
162
+ - **With it, candidates are found** (.92). Without the oracle, most of what remains is boundary reviews: one EDGAR record against one other record, exact name and same issuer, comes to about 12 bits, the threshold.
163
+ - The name weight is fitted on a sample that is half namesakes by design, so an exact name is worth about 10 bits.
164
+ - Employer is capped at 2 bits (design 04 §6.4: colleagues share an employer).
165
+ - Whether a model should decide these is evaluation 3.
166
+ - **With the oracle**, recall by kind is about .99 for plain, suffix and multi-issuer people, .96 for namesakes and .78 for renamed people. A married name with nothing else shared (no date of birth in EDGAR) is not found, and should not be on a name alone.
167
+ - **The compound reading** adds .7 points of candidate recall (.912 without it) and .2 of B3 recall, with no false merge.
168
+ - **The false merges read (seed 20261016) all have a suffix on one side only.**
169
+ - Three are a father and son at one issuer, the father written without a suffix: "SHUFORD ROBERT F" and "SHUFORD ROBERT F JR" (Old Point Financial), "BERKLEY WILLIAM R" and "BERKLEY WILLIAM R JR" (W. R. Berkley), "SCHORSCH NICHOLAS S" and "Schorsch Nicholas S. Jr.".
170
+ - The fourth, "R Scott Greer" and "GREER ROBERT S. JR.", may be one person with two CIKs.
171
+ - The generational-suffix constraint needs a suffix on both sides.
172
+ - The truth calls these different people (§11.1, conflicting suffixes).
173
+ - This is design §10's hardest case, and it is real. A suffix on one side only, at the same issuer, could be its own name level or a review trigger; left for step 4, where the model's answer on these pairs is measured.
174
+ - **Given-first names are unchanged:** the synthetic person benchmark gives identical results before and after (seeds 11 and 12).
175
+
176
+ ### 11.3 Step 3: the generator and the fixture
177
+
178
+ **Records from other systems** (generator version 2). Each person has one to three, from a CRM, a board list and a screening list, each from one of the person's CIKs.
179
+ - **The name** is the current conformed name, or a former name 35% of the time when there is one, as others write it:
180
+ - given first, or "Family, Given" 15% of the time;
181
+ - a nickname or transliteration from the person profile's name groups 30% of the time ("Bob F. Shuford", "Jack D. Mcdonald");
182
+ - middle names dropped (35%) or initialled (35%);
183
+ - the suffix dropped 30% of the time ("Robert F Shuford" for Robert F Shuford Jr, the hardest real case);
184
+ - a typo 6% of the time, capitals 15%.
185
+ - **The employer** is one of the person's issuers (a directorship's for the board list), on 80% of CRM records, every board-list record and 30% of screening records. It is written without EDGAR's state marker ("/DE/", "/NEW/"), half the time without its legal form, often in title case ("Comerica" for COMERICA INC /NEW/).
186
+ - **The place** is a filing city half the time, the country alone a fifth of the time, otherwise none.
187
+ - **A compound family name is retyped as if its second word were the given name** ("Pineyro, Sublett"), as design §4 accepted. The generator cannot tell which names are compound.
188
+
189
+ **What it found in the profile.** About 7.7% of true pairs (228 of 2,949, seed 20261016) were never candidates.
190
+ - 169 differ in family name: typos in either source, married names ("Julie Huber" and "Smith Julie A"), EDGAR's own "ET AL".
191
+ - 13 are swapped: one CIK holds "Hui Liu" and "Liu Hui".
192
+ - 131 of the 228 shared the employer exactly.
193
+
194
+ The person profile's key for a changed family name is the given name with a date of birth, which EDGAR does not have. Two keys were added:
195
+ - **`given_employer`**: canonical given name and employer key, max block 30, excluded from EM as phone and address are, since colleagues share employers.
196
+ - **The swapped order** of a family-first name as a reading.
197
+
198
+ Candidate recall rose from .923 to .961 (and .929 to .963 on seed 1). The synthetic person benchmark is unchanged (B3 identical; pair recall .929 to .930), and so is the starter model.
199
+
200
+ **Results** (1,000 people, about 2,860 records, seeds 20261016 and 1):
201
+
202
+ | | B3 precision | B3 recall | Candidate pair recall | Reviews |
203
+ |---|---|---|---|---|
204
+ | No adjudicator | .9984 / .9984 | .543 / .549 | .961 / .963 | 616 / 624 |
205
+ | Oracle deciding | .9993 / .9995 | .906 / .912 | | 150 / 136 |
206
+
207
+ - Recall without an adjudicator falls from .72 (step 2) to .54. Records from other systems carry less (no city on half, no employer on most screening records), so most true pairs land in review. That is the room evaluation 3 measures a model in.
208
+ - With the oracle, recall is about .97 for plain people and .89 to .90 for namesakes. The rest is what a name cannot carry: married names with nothing else shared, compound family names retyped wrong, typos with no employer.
209
+
210
+ **The fixture** (`benchmark/data/edgar`, 40 KB, `python -m thisness.benchmark edgar-fixture`) is 149 CIKs (134 people), with the §7 fields only:
211
+ - 15 whole shared-name groups holding one person with two CIKs;
212
+ - 15 holding a father and son at one issuer, or conflicting suffixes;
213
+ - 15 other whole groups;
214
+ - 30 renamed people and 20 plain ones.
215
+
216
+ `edgar.from_fixture()` builds the benchmark from it: 417 records; precision 1.0 and candidate recall .95 with no adjudicator.
217
+
218
+ ### 11.4 Step 4: models on real people (evaluation 3)
219
+
220
+ `thisness eval --assistant {oracle,anthropic,openai}` (new): after the run, the review assistant answers every review the run left open, and `benchmark.evaluate.recommendation_accuracy` scores each suggestion as if accepted. A suggestion is right when its grouping is the truth's; a wrong one would merge different people, leave a match apart, or both. With the oracle as assistant every suggestion is right (620 of 620), a check on the scorer.
221
+
222
+ All runs: 1,000 people, seed 11 (`thisness eval --type person --source edgar --size 1000 --seeds 11`), 2,893 records, gpt-6-luna, concurrency 16. Cassettes and saved stores are in `~/thisness-demo` (`edgar1000-decide-gpt-6-luna.jsonl`, `edgar1000-recommend-gpt-6-luna.jsonl`); `--replay` answers them again, free.
223
+
224
+ | | B3 precision | B3 recall | False-merge clusters | Reviews | Cost |
225
+ |---|---|---|---|---|---|
226
+ | No model | .9990 | .526 | 2 | 620 | |
227
+ | gpt-6-luna deciding (yielding) | .9874 | .837 | 18 | 283 | $0.55 |
228
+ | Oracle deciding | .9990 | .896 | 2 | 171 | |
229
+
230
+ **Luna deciding merges namesakes.** Of 4,158 contested pairs it answered 2,063 "same", 232 "different" and 1,844 "unsure".
231
+ - Of the 2,068 pairs the truth calls different people, it said "same" for 420 and "unsure" for 1,421. That is 65% of its decided answers on different people, against 25% on synthetic people (design 06 §10.8).
232
+ - All but two of the 420 are namesake pairs where nothing contradicts. Either one record has only a name ("Neil Martin" and "Martin, Neil S" at the Korea Fund), or the two have different employers and cities ("Matt Roberts", Vacasa, Portland, and "Roberts Matthew K", LSC Communications, Chicago).
233
+ - The people guidance tells the model that variants of one name are the same person "when nothing contradicts it". On synthetic people that held; among real insiders, where one name in sixteen is shared, it does not.
234
+ - Its "different" is reliable: of the 2,072 pairs that are one person, it called 6 different (and 423 unsure).
235
+
236
+ **Luna as the review assistant** ($0.25, 620 calls, every answer valid):
237
+ - 450 of the 526 decided suggestions would be right if accepted (86%), and 94 were unsure.
238
+ - By answer: "same" 363 of 377 (96%), a regrouping 83 of 142 (58%), "different" 4 of 7.
239
+ - Accepting every suggestion would merge different people in 26 reviews (4.2%) and leave a match apart in 59.
240
+
241
+ This is design 06 §10.10's result on synthetic people almost exactly (86%; "same" 97.5%). A model shown every record of a review is far more careful than one shown a pair.
242
+
243
+ The 26 merges are the hardest real cases:
244
+ - a father and son at one company ("Patrick G. Ryan" and "Patrick G. Ryan Jr", Ryan Specialty);
245
+ - the same name at two companies in one city;
246
+ - a few that may be the truth's error. "Paul S. Michaels" at Keurig Dr Pepper and at an Invesco fund is probably one person with two CIKs. The truth calls compatible names with nothing shared different people (§11.1), and that rule is wrong for an unknown few.
247
+
248
+ **Decision 3 of design 06 §10.11 stands: people keep `model_role="suggest"`.**
249
+ - On real people, deciding costs nine times the false merges (2 to 18) for 31 points of recall.
250
+ - The assistant's suggestions are as reliable as on synthetic people, and a person accepts or rejects each one.
251
+
252
+ The guidance hint above is the lever if people are ever to be decided by a model. Design 06 §10.9 tried a stricter one, which made luna unsure of nearly everything; on real data a narrower rule (a common name with no shared employer, place or identifier is not evidence) is worth one run. Not done here.
253
+
254
+ ### 11.5 Step 5: scale (evaluation 4)
255
+
256
+ No model, seed 11, `thisness eval --type person --source edgar --size N --seeds 11`, timed with `/usr/bin/time -l`.
257
+
258
+ **At the start of the step**, 10,000 people (28,692 records) gave:
259
+ - precision .9967, recall .726, 51 false-merge clusters, 3,741 reviews;
260
+ - 73 s and 1.9 GB peak resident memory.
261
+
262
+ False merges grew with size (2, 10 and 51 at 1,000, 3,000 and 10,000) as namesakes multiply. Read one by one, the 51 came from four faults in the person profile; three are fixed:
263
+
264
+ 1. **Soundex alone made a near match.** "Buchanan" and "Beckmann" share Soundex B255, "John" and "Jami" J500, "DeFao" and "Duffy" D100. The comparison called two names fuzzy when Jaro-Winkler or Soundex agreed.
265
+ - A Soundex match now needs Jaro-Winkler .8 as well (`soundex_floor`). True pairs that rely on Soundex score .805 or more (Sean and Shawn); the false ones .775 or less.
266
+ - Alone it moved nothing (51 to 52): with fewer false fuzzy pairs, EM trusts the level more (11.1 to 12.7 bits).
267
+ 2. **`initial` outweighed an exact name.** EM fitted it at 15.1 bits against 14.0 for an exact name, from few examples. "D M. Wilson" merged with "Davd Wilson" on that alone, and EDGAR's "JACOBS M CHRISTINE" (given name M) with "Michael Jacobs".
268
+ - It is capped at 8 bits: an initial and a family name are not a match without something else.
269
+ - Effect: 45 false merges, recall .735.
270
+ 3. **A suffix on one side only** (step 2's open case): a father and son at one company, the son's record written without "Jr" ("SHUFORD ROBERT F" and "SHUFORD ROBERT F JR"; Dorrance at Campbell's; Ryan at Ryan Specialty; Kidd at Central Securities). With employer and city that is 20 bits.
271
+ - It is now its own name level, `suffix_one_side`, capped at 4 bits. With employer and city such a pair reaches review, not a merge. The records cannot tell a dropped suffix from a second generation; a person can.
272
+ - Effect: 32 false merges, recall .714, reviews 4,050.
273
+ 4. **A fuzzy match ignored the middle initial.** "Erik J Anderson" and "Eric R. Anderson", "WALDROP WILLIAM H" and "WALTERS WILLIAM THURMAN" were fuzzy where an exact or alias match with those middles is `middle_conflict`. A fuzzy match with a different middle initial or suffix is now `middle_conflict` too. Effect: 29 false merges.
274
+
275
+ Person name comparison version 2 (still unreleased) carries all four. The person starter model is rebuilt, and the synthetic person benchmark is as good as it was: recall .7231 to .7163 on seed 11, .6979 to .6978 on seed 12, false merges unchanged at 4 and 2.
276
+
277
+ **Results, no model:**
278
+
279
+ | People | Records | B3 precision | B3 recall | False-merge clusters | Reviews | Pairs unasked | Time | Peak memory |
280
+ |---|---|---|---|---|---|---|---|---|
281
+ | 1,000 | 2,893 | 1.0000 | .521 | 0 | 641 | 176 | 8 s | 0.6 GB |
282
+ | 3,000 | 8,613 | .9993 | .609 | 4 | 1,582 | 244 | 16 s | 1.0 GB |
283
+ | 10,000 | 28,692 | .9985 | .715 | 29 | 4,059 | 2,192 | 67 s | 2.5 GB |
284
+ | 10,000, oracle deciding | 28,692 | .9992 | .948 | 15 | 592 | 0 | 93 s | 2.9 GB |
285
+
286
+ - **Recall rises with size** (.52, .61, .72): EM has more pairs to learn from. At 1,000 people the profile merged nothing wrongly; the step 4 run at that size had 2 false merges and recall .526.
287
+ - **The 29 left at 10,000** are mostly what the records cannot settle:
288
+ - a son's record whose "Jr" the generator dropped, identical to his father's ("Claiborne R Rankin" and "RANKIN CLAIBORNE R" at Hyster-Yale);
289
+ - one common name in one city at two employers (exact name and city, 17 bits);
290
+ - names mistyped on both sides.
291
+ - The oracle's 15 are among them: their evidence is above T_high, so no reviewer is asked.
292
+ - **Reviews grow about linearly** (one per seven records), and boundary reviews stay bounded (design 07 §13.4): 2,192 contested pairs at 10,000 were not asked because they would have joined two full reviews.
293
+ - **Memory** is 2.5 GB at 28,692 records and 108,213 scored pairs, against 7.8 GB for 46,565 organization records (design 07 §13.4). Scoring still holds every candidate pair's row in Python; at this size that is not yet the constraint.
294
+ - Step 4's cassettes record requests built with the old comparison levels. Replayed now, 3,809 of the decide cassette's 3,865 pairs find no matching request. Step 4's numbers stand as measured at 2d66482; a new run would need new calls (about $0.80 for both).
@@ -724,8 +724,8 @@ Its job is identity.
724
724
  ┌───────────────▼──────────────┐
725
725
  │ Semantic / Ontology │
726
726
  │ │
727
- │ People • Companies • Risks │
728
- │ Contracts • Products • etc. │
727
+ │ People • Companies • Risks │
728
+ │ Contracts • Products • etc. │
729
729
  └───────────────┬──────────────┘
730
730
  │
731
731
  ┌───────────────▼──────────────┐
@@ -742,8 +742,8 @@ Its job is identity.
742
742
  ┌───────────────▼──────────────┐
743
743
  │ Enterprise Data │
744
744
  │ │
745
- │ DBs • Docs • Email • APIs │
746
- │ Video • Code • Warehouses │
745
+ │ DBs • Docs • Email • APIs │
746
+ │ Video • Code • Warehouses │
747
747
  └──────────────────────────────┘
748
748
  ```
749
749
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "thisness"
3
- version = "0.2.0"
3
+ version = "0.3.0"
4
4
  description = "Identity resolution for imperfect observations: evidence, stable identities, reversible history."
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -42,6 +42,15 @@ def main() -> None:
42
42
  uk_audit = sub.add_parser("uk-audit", help="read a sample of the join")
43
43
  uk_audit.add_argument("--n", type=int, default=400)
44
44
  sub.add_parser("uk-fixture", help="rebuild the shipped UK fixture from the staged data")
45
+ edgar_stage = sub.add_parser(
46
+ "edgar-stage", help="stage EDGAR insiders from an edgar-persons-pool store (design 09)"
47
+ )
48
+ edgar_stage.add_argument("--store", type=Path, required=True,
49
+ help="the edgar-persons-pool DuckDB file (read only)") # fmt: skip
50
+ sub.add_parser("edgar-truth", help="count how good the CIK is as truth")
51
+ edgar_audit = sub.add_parser("edgar-audit", help="read samples of the truth's doubts")
52
+ edgar_audit.add_argument("--n", type=int, default=40)
53
+ sub.add_parser("edgar-fixture", help="rebuild the shipped EDGAR fixture from the staged data")
45
54
  starter = sub.add_parser("starter", help="rebuild a starter model")
46
55
  starter.add_argument("--type", default="organization", choices=sorted(GENERATORS))
47
56
  starter.add_argument("--out", type=Path, default=None)
@@ -70,6 +79,17 @@ def main() -> None:
70
79
  print(f"- {d['gleif']!r} | Companies House: {d['companies_house']!r}")
71
80
  else:
72
81
  print(f"wrote {uk.make_fixture()}")
82
+ elif args.command.startswith("edgar-"):
83
+ from thisness.benchmark import edgar
84
+
85
+ if args.command == "edgar-stage":
86
+ print(f"staged {edgar.stage(args.store)}")
87
+ elif args.command == "edgar-truth":
88
+ print(json.dumps(edgar.truth_report(), indent=1))
89
+ elif args.command == "edgar-fixture":
90
+ print(f"wrote {edgar.make_fixture()}")
91
+ else:
92
+ print(json.dumps(edgar.audit(n=args.n), indent=1))
73
93
  elif args.command == "wfo-fixture":
74
94
  target = Path(taxa.__file__).parent / "data" / "wfo_fixture.csv.gz"
75
95
  taxa.write_rows(taxa.families(taxa.FIXTURE_FAMILIES), target)
@@ -11,3 +11,10 @@ it as a courtesy:
11
11
 
12
12
  Rebuild with `python -m thisness.benchmark wfo-fixture`. Other families are fetched on
13
13
  demand and cached under `~/.cache/thisness/wfo`.
14
+
15
+ `edgar/`: about 150 SEC Section 16 insiders (directors, officers, holders of more than
16
+ 10%) with their names, former names, CIKs, issuers, titles, role flags, filing dates,
17
+ city and state, for the people benchmark's tests (design 09). No street addresses or
18
+ postal codes. EDGAR filings are records of the US government; we credit the SEC as a
19
+ courtesy. Rebuild with `python -m thisness.benchmark edgar-fixture` from data staged by
20
+ `edgar-stage`.
@@ -0,0 +1,7 @@
1
+ # EDGAR people fixture
2
+
3
+ A small slice of real data for tests (design 09): Section 16 insiders as staged from an edgar-persons-pool store (classifier version 2): names, former names, CIKs, issuers, titles, role flags, filing dates, city and state. No street addresses or postal codes.
4
+
5
+ Contains information from SEC EDGAR Section 16 filings (Forms 3, 4 and 5). EDGAR filings are records of the US government; the SEC is credited as a courtesy.
6
+
7
+ Rebuild with `python -m thisness.benchmark edgar-fixture`.