Warn when a company's identifiers disagree with each other, offline first and against Wikidata when asked #675
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
performance
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo#675
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
From the list: “Make the plugin perform a cross reference check and a warning be displayed if some identifiers are not coherent. For example, a company with a valid wikidata but references a wrong ISNI.” Identifiers are validated one at a time, by shape and where a scheme has one by check digit; nothing compares one identifier with another, and the plugin says it looks nothing up online.
What exists today
tidy, and optionally achecksum(src/postulo/core/identifiers.py,Scheme; the table issrc/postulo/plugins/identifiers/schemes.py:118-250). Only ORCID and LEI have a checksum (orcid_checks_outat:95,lei_checks_outat:110); ISNI has none (#637, open: the check character is never verified and its link carries spaces). #638 (open) lists values the table refuses or reads wrongly.CompanyIdentifier.cleanapplies the scheme and refuses what fails (src/postulo/jobs/models.py:543-549).jobs/models.py:512-537).src/postulo/plugins/identifiers/__init__.py:59(“Nothing here is ever looked up online.”) andsrc/postulo/core/identifiers.py:21(“Nothing here touches the network”); #42 deferred any lookup as “behind a deliberate per-request action and never automatically”. No code fetches from Wikidata, GLEIF, ISNI or OpenCorporates.jobs/duplicates.pyworks out possible duplicates when a page is drawn, with no table to keep true, and<c-duplicates>draws them as a note on the company's page (src/postulo/jobs/duplicates.py:1-27,src/postulo/templates/cotton/duplicates.html, used atsrc/postulo/templates/jobs/company_detail.html:71, directly above the identifiers list at:73-90).register, “PT 501234567”) and OpenCorporates (pt/501234567), which both carry a jurisdiction and a company number. A Wikidata item shape is already checked by the pattern (^Q[1-9]\d{0,11}$). A LEI does not encode a country: its first four characters are the issuing operator's prefix, characters 5 to 18 carry no embedded meaning, the last two are check digits (ISO 17442, as GLEIF and others describe it). The idea of checking “the LEI's country prefix” has no basis, and this issue does not do it.Special:EntityData/Q29671.jsonand one to the property labels): an item can hold the ISNI (P213), the LEI (P1278), the OpenCorporates id (P1320), the Crunchbase id (P2088), the LinkedIn company id (P4264), the official website (P856) and coordinates (P625). The University of Aveiro item has P213 (written without spaces), P856 and P2088 and no P1278 or P1320: an item often holds some and not the others, and anyone can edit it.Checked against
mainatf09b6010b.Proposal
Phase 1, offline, and part of this issue.
postulo.plugins.identifiersbeside the schemes, exposed bypostulo.core.identifiers(cross_checks()), the way the registry is. A finding is a sentence, the identifiers it concerns, and a severity that is only ever “warning”.Scheme.normaliseandchecksumover a company's stored rows and says which would be refused today.<c-duplicates>, in the same component shape, drawn only when there is a finding, each line saying which two identifiers and why, in words and not by colour. It never blocks saving, never changes a value and never fires in the background: it is computed when the page is drawn. The identifier rows on the form do not change.Phase 2, online, after #673 lands the guarded Wikidata read, and a separate issue when it does.
postulo.plugins.http, with size, time and address limits) and the same module and dialog shape as #673 and #674. It compares the item's ISNI, LEI, OpenCorporates, Crunchbase and LinkedIn ids and its website with what the company holds, shows both values, and offers to add an identifier the company lacks. It does not decide which side is right and never overwrites.Decisions
jobsbeside #673's module and is not part of the registry. The plugin's description is unchanged.jobswith the duplicate notice. Recommended: the plugin, because the relations are between schemes the plugin defines, with no third-party contract: the identifier kind advertises no entry-point group (plugins/identifiers/__init__.py, “Internal only, for now”), so nopostulo.plugins.apiname is added.jobs/duplicates.py(tell, never refuse) is the precedent.Prior art
No new dependency in phase 1: the rules are a few comparisons on values the registry already normalises. For the check digits that #637 adds, ISO 7064 MOD 11-2 is already hand-written for ORCID (
schemes.py:95-107) and is what ISNI uses. Under the library-first rule,python-stdnum2.2 (LGPL-2.1-or-later, which combines with AGPL-3.0; released 2026-01-04; no advisory on OSV) already hasstdnum.isni,stdnum.leiandstdnum.iso7064, and is not a dependency today (absent fromuv.lock). Recommended: leave that choice to #637, which adds the ISNI check and is where to decide between takingstdnum.isniand extending the short function that exists; the rules here compare values and need neither. For phase 2 the candidate clients, their licences and why none is adopted are in #673; the same document and the same guarded client are reused here. Not verified: whether OpenCorporates' and the registers' jurisdiction codes agree for every country.Builds on
Issues #637 (ISNI check character and link), #638 (schemes that refuse real values), #109 (one registry and a subject matrix), #42 (identifiers, lookup deferred). #673 for phase 2 (the guarded Wikidata read). Related: #672 (which identifiers a person chooses to see; a warning concerns hidden ones too).
Done when
tests/test_identifiers.py(andtests/test_identifier_registry.pyfor the registry hook): a company with a register number and an OpenCorporates id that agree has no finding; one with different numbers has a finding naming both; one with different countries has the jurisdiction finding; a stored ISNI with a wrong check character is reported once #637 lands; a company with one identifier has none.tests/e2e/test_accessibility.py), and never changes what saving does;tests/test_page_coverage.pyis unchanged because no page is added.tests/test_identifiers.py::test_another_persons_page_never_shows_my_idsis the model).f09b6010b) and the candidate format (4) stay as they are. New strings are in the working catalogues (en-GB, fr-FR, pt-PT as drafts) and Weblate (#349).MockTransportthat the comparison goes through the guarded client and stores nothing.From the list of improvements of 2026-10-02.
Landed on main with a green Forgejo CI run. Its entry in the CHANGELOG's Unreleased section ends in (#675).