Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
faab776
Especifica a spec 005: escrita, ASCII, exibição e agrupamento de vari…
matbrgz Oct 12, 2026
dfee58b
Planeja a spec 005: plano e tarefas
matbrgz Oct 12, 2026
2507b00
Corrige a detecção de escrita: largura total, 々, ー, Jpan, Kore e escr…
matbrgz Oct 12, 2026
6b701d5
Translitera para ASCII pelo ICAO Doc 9303: latim com NFKD e cirílico
matbrgz Oct 12, 2026
427f1a7
Põe partículas em minúsculas na capitalização das fontes de caixa uni…
matbrgz Oct 12, 2026
203ee3e
Acrescenta strips_accents às fontes que removem acentos (StatCan, INE…
matbrgz Oct 12, 2026
d3a292c
Escolhe a forma de exibição sem somar tipos, edições e fontes não con…
matbrgz Oct 12, 2026
41f0845
Agrupa variantes em duas fases, com different_from como corte
matbrgz Oct 12, 2026
f551cc4
Preenche group_id no índice e lista as fontes das ligações no manifesto
matbrgz Oct 12, 2026
89b6dbd
Passa SCHEMA_VERSION a 1.2.0, confere group_id na CI e aponta os cont…
matbrgz Oct 12, 2026
9a84f6f
Grava as observações em lotes e inclui o Wikidata na medição de R8
matbrgz Oct 12, 2026
0cfe3c6
docs(variants): record measurements and follow-up issues for spec 005
matbrgz Oct 12, 2026
88118b2
docs(variants): link spec 005 tasks and roadmap to PR #38
matbrgz Oct 12, 2026
554bfc9
fix(variants): stop homograph labels and competing transliterations f…
matbrgz Oct 12, 2026
0aa499c
fix(normalize): casefold after NFKD, follow ISO Hrkt and keep final p…
matbrgz Oct 12, 2026
276792d
docs(variants): update spec 005, ADR 0010 and roadmap after PR #38 re…
matbrgz Oct 12, 2026
c417a65
fix(variants): block hidden homographs, transliteration fan-out and C…
matbrgz Oct 12, 2026
6d5af76
docs(variants): record second review numbers and clarify validate in …
matbrgz Oct 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,6 @@ jobs:
--out "$RUNNER_TEMP/dist-oficiais" --release "$(date -u +%Y.%m)"
for pais in CA ES FR IE NO NZ US; do test -s "$RUNNER_TEMP/dist-oficiais/by_country/$pais.csv"; done
test -s "$RUNNER_TEMP/dist-oficiais/variant_links.parquet"
for indice in given_names family_names; do
uv run python -c 'import csv, sys; linhas = list(csv.DictReader(open(sys.argv[1], encoding="utf-8"))); sys.exit(not linhas or not all(l["group_id"] for l in linhas))' "$RUNNER_TEMP/dist-oficiais/$indice.csv"
done
34 changes: 34 additions & 0 deletions docs/adr/0010-regras-de-agrupamento.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# 0010. Agrupamento de variantes em duas fases, sem a P460 e com a P1889 como corte

## Status

Aceito (2026-10-12)

## Contexto

A spec 005 preenche `group_id` (spec 001, R3.2) a partir da tabela `variant_links` (ADR 0009) e das próprias observações. O agrupamento precisa ser determinístico e explicável: quem lê `given_names.csv` deve conseguir dizer por que dois nomes estão no mesmo grupo.

O fecho transitivo das ligações publicadas pelo Wikidata não serve como grupo. Medido em 2026-10-12 sobre os nós com observação no lote real (811.117 nós do Wikidata):

- Com `same_item` e `transliteration`, o maior componente tem 1.124 nomes: romanizações como `Chen`, `Chan` e `Ang` são transliteração de vários caracteres han diferentes e os ligam entre si.
- Somando `said_to_be_the_same` (P460), o maior componente tem 15.892 nomes (de `Aadne` a `Zygmunt`), e 14.039 mesmo pulando as junções proibidas por `different_from` (P1889). A P460 liga cognatos de línguas diferentes, e nomes como `Elisa`, `Lisa` e `Ελισάβετ` têm mais de 500 ligações cada.
- Limitar a P460 a nomes com poucas ligações não estabiliza: com até 8 ligações o maior grupo tem 30 nomes e `Ivanov` fica sozinho; com até 16, o maior tem 2.976.

## Decisão

- Os nós são os pares (`kind`, `name_key`) com observação. As ligações se juntam aos nós pela chave, nunca pelo texto do nome, e são tratadas como não dirigidas. Ligação com ponta sem observação é ignorada.
- Prenome e sobrenome nunca se juntam; `group_id` é único dentro de cada `kind`.
- **Fase 1, mesmo item:** os itens se juntam só por `other_gender` em sobrenomes (P5278: `Иванов` e `Иванова` são o mesmo sobrenome flexionado); em prenomes, `other_gender` (P1560: `Paulo` e `Paula`) não junta. Cada nó entra no grupo dos itens de que é rótulo (por `same_item`, como origem de transliteração ou, em sobrenomes, `other_gender`) só se todos esses itens já são um grupo. Um rótulo homógrafo de itens diferentes fica sozinho: na primeira versão desta regra, que juntava por `same_item` direto, `장` ligava `张` e `蒋`, `진` ligava `陈`, `秦` e `真`, e 61 grupos de prenomes e 162 de sobrenomes misturavam itens diferentes (revisão do PR #38).
- **Fase 2, anexação inequívoca,** calculada sobre os grupos da fase 1: a transliteração junta o destino ao grupo de origem só se todas as transliterações que chegam ao destino vêm do mesmo grupo, se o destino não é rótulo homógrafo e se o destino não é já nome de outro grupo ou rótulo de outro item com um nome na escrita da origem (`見` -> `현` não entra no grupo de `玄`, de que `현` é rótulo; `Герман` -> `Herman` entra no grupo latino de `Herman` e `Hermanová` quando `Герман` é rótulo de um item só). Romanização de han, kana ou hangul nunca se anexa a nome de outro grupo ou item (leitura homófona: `suk`/`suková` e `术`, na segunda revisão do PR #38), e as transliterações de um grupo que chegam a dois ou mais grupos de outros nomes não se anexam (`Кінаш` -> `Kinach` e `Kinasz`). A regra sem acento (ADR 0011) junta um nome sem acento aos nomes com o mesmo `ascii` só se todos eles estão no mesmo grupo e se o próprio grupo do nome, quando tem outros nomes, é esse.
- `said_to_be_the_same` (P460) não junta. Ela continua em `variant_links` para quem quiser cognatos.
- `different_from` (P1889) proíbe que dois nós fiquem no mesmo grupo. As junções são aplicadas em ordem fixa (fase, depois os dois `name_key` em ordem de código Unicode); a que violaria a proibição é pulada.
- `group_id` é o menor `name_key` do grupo em ordem de código Unicode; nome sem variante é seu próprio grupo.
- A transliteração publicada como qualificador de um rótulo, que a spec 004 liga a todos os rótulos do item, não é restrita: os rótulos do mesmo item e `kind` já estão no mesmo grupo pela fase 1, e a fase 2 só olha para o grupo.

## Consequências

- Os grupos são pequenos (no lote real, o maior tem 18 nomes, todos rótulos do mesmo item) e cada junção tem uma causa nomeável: mesmo item, mesmo sobrenome no outro gênero, transliteração não ambígua ou fonte que remove acentos. Medido: 2.310 rótulos homógrafos sem ponte, 2.820 transliterações anexadas (1.305 ambíguas, 27 concorrentes, 9 de CJK para outro item e 2 em leque de fora), 14.136 nomes sem acento anexados (3.099 ambíguos de fora) e 241 junções puladas por `different_from`. Restam 87 grupos de prenomes e 59 de sobrenomes com rótulos de itens diferentes, quase todos o mesmo nome em itens duplicados de línguas diferentes ligados pela transliteração do cirílico (`Алексей` e `Aleksei`, `Омельчук` e `Omelczuk`).
- Cognatos (`John`, `Johann`, `Juan`) e grafias alternativas ligadas só pela P460 (`Ivanov` e `Iwanow`) ficam em grupos separados. Quem precisar delas usa `variant_links`.
- Romanizações ambíguas, rótulos homógrafos e nomes sem acento ambíguos (`Ines` entre `Inès` e `Inés`) ficam fora de qualquer grupo com variantes, o que perde junções corretas em troca de não criar erradas.
- `group_id` muda quando entra no grupo um nome com `name_key` menor; ele identifica o grupo dentro de uma release, não entre releases.
- Uma relação nova do Wikidata, ou outra fonte com `links`, só passa a juntar nomes com mudança desta decisão.
26 changes: 26 additions & 0 deletions docs/adr/0011-correspondencia-sem-acento.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# 0011. Nome sem acento só se junta ao acentuado quando a fonte remove acentos

## Status

Aceito (2026-10-12)

## Contexto

A Statistics Canada publica `MARIE-EVE` e `JOSEE` (nota 7 da tabela 17-10-0147-01), o INE remove acentos por norma ("eliminando los acentos", nota metodológica dos nomes) e a SSA não tem nenhum caractere fora do ASCII em 104.819 nomes. Os mesmos nomes aparecem com acento no Wikidata e no INSEE. Sem regra, `Marie-Eve` (CA) e `Marie-Ève` ficam em grupos diferentes (issue #4).

Juntar quaisquer dois nomes com a mesma forma sem acento erra em línguas em que o acento distingue nomes: `Đức` e `Đúc` em vietnamita, `Aleš` e `Alés`, `Abrahám` e `Ábrahám`. Esses pares vêm de fontes que publicam acentos, e a falta de acento num deles não é perda de informação.

## Decisão

- `sources.yaml` ganha o campo opcional `strips_accents` (padrão `false`). Ele só é `true` com evidência registrada na spec: documentação da fonte que diz que os acentos são removidos, ou medição no arquivo completo de que nenhum nome tem letra acentuada apesar de nomes de línguas que a usam.
- A correspondência sem acento só parte de um nome latino atestado por fonte com `strips_accents: true`. Ele se junta aos nomes do mesmo `kind` com o mesmo `ascii` (ADR 0012) apenas se todos esses nomes já estão no mesmo grupo (ADR 0010, fase 2).
- Nomes acentuados nunca se juntam entre si só por terem o mesmo `ascii`.
- Hoje: `ca-statcan-births`, `es-ine-residentes` e `us-ssa-births`.

## Consequências

- `Marie-Eve` e `Marie-Ève`, `Josee` e `Josée`, `Garcia` e `García` ficam no mesmo grupo; as contagens continuam em linhas separadas.
- Nome sem acento com mais de uma leitura acentuada em grupos diferentes (`Ines`: `Inès`, `Inés`) não se junta a nenhuma; medido na simulação: 2.883 casos contra 14.394 junções.
- O INE mantém `ñ` e `ç`; como `ascii` também os remove, `Begoña` do INE se junta a `Begona` quando esta é a única leitura, o que é aceito como variante.
- Se a SSA tiver nomes sem acento por escolha de quem registrou e não por limitação do sistema, a junção ainda liga grafias do mesmo nome; o custo de errar é baixo.
- Fonte nova que remove acentos precisa da evidência na spec para entrar na regra; sem ela, seus nomes ficam sem junção por acento.
27 changes: 27 additions & 0 deletions docs/adr/0012-transliteracao-ascii.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# 0012. Transliteração para ASCII pelo ICAO Doc 9303, sem forma parcial

## Status

Aceito (2026-10-12)

## Contexto

O campo `ascii` (spec 001, R1) era a remoção de diacríticos em escrita latina e vazio nas demais. A revisão da spec 001 (issue #4) mostrou que ligaduras (`Dž`), apóstrofos curvos e letras sem decomposição (`ə`, `ŋ`) eram descartados em silêncio, gerando formas que não correspondem ao nome. O agrupamento sem acento (ADR 0011) depende de `ascii`, e o Wikidata traz 14.682 nomes em cirílico e 2.645 em grego.

Uma biblioteca de transliteração genérica (Unidecode, ICU) seria dependência nova de runtime e daria resultados sem norma citável. O ICAO Doc 9303 (parte 3, seção 6, 8.ª edição, 2021) tem tabelas recomendadas para nomes em documentos de viagem: latim multinacional (tabela A) e cirílico (tabela B); não tem grego.

## Decisão

- Latim: NFKD, remoção das marcas combinantes e tabela fechada das letras sem decomposição, com os valores da tabela A do ICAO e uma letra só onde ela dá alternativa (`Ä` vira `a`, não `ae`), para que a forma sem acento das fontes que removem acentos coincida.
- Cirílico: tabela B do ICAO na forma padrão de cada letra, sem as exceções por língua (o dataset não sabe a língua do nome); `ь` omitido; `ћ`, ausente, vira `c` como o `ć` sérvio.
- Apóstrofos e hífens tipográficos viram `'` e `-`; ponto médio e caracteres de formatação são omitidos.
- Se algum caractere não tem transliteração, `ascii` fica vazio. Não se emite forma parcial.
- Grego, hangul, kana e as demais escritas ficam vazias até uma spec própria escolher a norma (ELOT 743 para o grego, romanização revisada para o coreano, Hepburn para o japonês).
- Nenhuma dependência nova.

## Consequências

- `ascii` passa a ter valor para nomes em cirílico (`Иванов` vira `ivanov`, `Мария` vira `mariia`), e a forma difere de transliterações de outras normas publicadas no Wikidata (`Mariya`).
- Nome latino com letra fora da tabela tem `ascii` vazio; `validate` deixa de exigir `ascii` em escrita latina e passa a exigir só caracteres ASCII sem maiúsculas.
- Ucraniano e sérvio ficam com a forma russa das letras comuns (`Г` vira `g`, não `h`), como o ICAO faz quando a língua não é conhecida.
- Mudar a norma de uma escrita muda valores de `ascii` e pede versão menor de `SCHEMA_VERSION`.
6 changes: 6 additions & 0 deletions docs/specs/001-esquema/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ Nota (spec 004, R5): a exigência de exatamente uma `primary` vale só para `bir
| `group_id` | Grupo de variantes, preenchido pela spec 005. Vazio até lá |
| `sources` | Lista ordenada de `source_id` que atestam o nome |

Nota (spec 005): `group_id` é preenchido pela [spec 005](../005-variantes/spec.md) (R5), a forma de exibição (`name`) segue a spec 005 (R4) e `ascii` segue o [ADR 0012](../../adr/0012-transliteracao-ascii.md). `group_id` é o menor `name_key` do grupo e identifica o grupo dentro de uma release: ele pode mudar entre releases quando entra no grupo um nome com chave menor ou quando as ligações mudam.

**R3.3 `by_country/<CC>.csv`.** Uma linha por (`name_key`, `kind`) do país, com `name`, `ascii`, `script`, `count`, `count_type`, `rank`, `female_share`, `gender_basis`, `first_year`, `last_year`. Agregação:

- `births`: soma de todos os anos disponíveis; `first_year` e `last_year` delimitam a série.
Expand All @@ -69,6 +71,8 @@ Nota (spec 004, R5): a exigência de exatamente uma `primary` vale só para `bir
- País só com `attested`: `count` e `rank` vazios.
- `rank` é denso, por `kind` e `count_type`, em ordem decrescente de `count`.

Nota (spec 004, R4.6): além das saídas acima, a release pode trazer `variant_links.parquet`, a tabela de ligações entre variantes quando alguma fonte selecionada implementa `links`.

### R4. Layout do repositório

```
Expand All @@ -95,6 +99,8 @@ docs/specs/, docs/adr/
- Cada release inclui `manifest.json` com `schema_version` (semver), fontes usadas com `retrieved`, número de linhas por arquivo e SHA-256 de cada arquivo.
- Remover ou renomear campo, ou mudar o significado de um valor, incrementa a versão maior de `schema_version`.

Nota: a release pode incluir `variant_links.parquet` (spec 004, R4.6), contado no `manifest.json`, cuja lista de fontes é a união das fontes das observações e das ligações (spec 005, R7.4). Versões: `1.1.0` (spec 004, R4.7) e `1.2.0` (spec 005, R8).

### R6. Legado

`first_names.all.txt`, `last_names.all.txt` e `generate/` vão para `legacy/` sem alteração de conteúdo e não são ingeridos no dataset novo (ADR 0001). A retirada é decidida na spec 006.
Expand Down
4 changes: 4 additions & 0 deletions docs/specs/002-pipeline/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ Cada fonte de `sources.yaml` com adaptador tem um módulo `src/namescommons/adap
- **R1.4** Registros repetidos pela chave de unicidade da 001 (R1.4) são somados.
- **R1.5** Uma fonte não pode emitir, para o mesmo (`name_key`, `kind`, `country`, `year`), linhas com `gender` informado e linhas sem `gender`. A violação falha a ingestão daquela fonte.

Nota (spec 004, R4.1): o módulo pode expor também a função opcional `links(files) -> Iterator[RawLink]`, com as ligações entre variantes; o contrato dela é da spec 004.

Nota: a spec 003 (R2.1) esclarece que o adaptador converte os valores; "como a fonte publica" vale só para o nome.

### R2. Extensões de `sources.yaml`
Expand All @@ -39,6 +41,8 @@ Campos novos, opcionais:
- `uniform_case` (booleano, padrão `false`): a fonte publica todos os nomes na mesma caixa e a capitalização por palavra da 001 se aplica a todos.
- `adapter` (booleano, padrão `true`): `false` registra uma fonte usada só manualmente.

Nota (spec 005, R6): `strips_accents` (booleano, padrão `false`) marca a fonte que publica os nomes sem acento por regra própria; ele decide a correspondência sem acento do agrupamento de variantes ([ADR 0011](../../adr/0011-correspondencia-sem-acento.md)).

### R3. Validação cruzada com a fonte

`validate` passa a recusar, além do que a 001 recusa:
Expand Down
Loading
Loading