From 672b30945833b3df0f86c437854e853057b10159 Mon Sep 17 00:00:00 2001 From: lvcas-dotcom Date: Thu, 27 Aug 2026 18:47:58 -0300 Subject: [PATCH 1/8] =?UTF-8?q?fix(docs):=20o=20comando=20exibido=20no=20R?= =?UTF-8?q?EADME=20=C3=A9=20o=20que=20gerou=20aquela=20sa=C3=ADda?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit O bloco mostrava `discover --schema public` acima de um transcript cuja segunda linha diz "full validation — every row was examined". Sem `--full`, essa linha sai como "sampled mode (100000 rows/table target), but every table fit the target and was read whole": o demo tem tabelas pequenas, então o método acaba sendo full de qualquer jeito, mas o texto do relatório é outro. O `make demo-svg` e o `demo/compose.yaml` sempre passaram `--full` — a gravação está certa, o comando ao lado dela é que estava incompleto. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index aea54bd..3dee3a9 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ your diagram. It never checked a single pre-existing row. ## What it does ```console -$ pgfathom discover --schema public +$ pgfathom discover --schema public --full ``` ``` From b1fb2c8986dc6d816fe8f97023c0186f729fac7f Mon Sep 17 00:00:00 2001 From: lvcas-dotcom Date: Thu, 27 Aug 2026 18:48:17 -0300 Subject: [PATCH 2/8] docs(demo): regrava a imagem e o transcript na v0.2.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Os dois estavam carimbados `v0.1.2` com a v0.2.0 publicada. Regravados pelo caminho do `make demo-svg` — binário construído na tag mais recente, Postgres 16 descartável, `demo/schema.sql`, `discover --schema public --full` — e não editados à mão. A gravação mudou em dois tokens, versão e duração, o que de quebra confirma que a mudança da penalidade de nome genérico não altera este schema: ele não tem nenhuma coluna com nome genérico apontando para tabela pequena, então o sinal nunca é emitido aqui. --- README.md | 4 ++-- assets/demo.svg | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3dee3a9..0f12c4f 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,7 @@ $ pgfathom discover --schema public --full ``` - pgfathom v0.1.2 · PostgreSQL 16.14 · profile pt-br · threshold 0.50 + pgfathom v0.2.0 · PostgreSQL 16.14 · profile pt-br · threshold 0.50 full validation — every row was examined; verdicts are conclusive BROKEN — the relationship is real; its integrity is not (1) @@ -117,7 +117,7 @@ $ pgfathom discover --schema public --full ──────────────────────────────────────────────────────────────────────────────────────────────── key name id 5 occurrences (100%) — e.g. cliente, funcionario, nota_fiscal - 1 broken · 2 confirmed · 0 weak · 0 unvalidated · 0 discarded · 133ms + 1 broken · 2 confirmed · 0 weak · 0 unvalidated · 0 discarded · 148ms 6 tables · 6 analyzed (100%) stats prefilter: 3 checked · 0 rejected · 0 without statistics ! statistics reset time unknown — usage counters carry no meaning diff --git a/assets/demo.svg b/assets/demo.svg index 6d3d2ab..af4d117 100644 --- a/assets/demo.svg +++ b/assets/demo.svg @@ -1 +1 @@ - pgfathom v0.1.2 · PostgreSQL 16.14 · profile pt-br · threshold 0.50 full validation — every row was examined; verdicts are conclusiveBROKEN — the relationship is real; its integrity is not (1) ──────────────────────────────────────────────────────────────────────────────────────────────── relation rows values orphan rows orphan values examined method public.os_servico.resp_tecnico → public.funcionario.id 97.5% 60.0% 200 200 8.0k fullCONFIRMED — total containment, verified row by row (2) ──────────────────────────────────────────────────────────────────────────────────────────────── relation rows values orphan rows orphan values examined method public.item_pedido.pedido_id → public.pedido.id 100.0% 100.0% 0 0 60.0k full public.pedido.cliente_id → public.cliente.id 100.0% 100.0% 0 0 20.0k fullWEAK — the data supports no conclusion either way (0) ────────────────────────────────────────────────────────────────────────────────────────────────noneUNVALIDATED — no evidence gathered; not the same as clean (0) ────────────────────────────────────────────────────────────────────────────────────────────────noneDETECTED — conventions read from the schema itself, added to pt-br ──────────────────────────────────────────────────────────────────────────────────────────────── key name id 5 occurrences (100%) — e.g. cliente, funcionario, nota_fiscal1 broken · 2 confirmed · 0 weak · 0 unvalidated · 0 discarded · 133ms 6 tables · 6 analyzed (100%) stats prefilter: 3 checked · 0 rejected · 0 without statistics ! statistics reset time unknown — usage counters carry no meaning \ No newline at end of file + pgfathom v0.2.0 · PostgreSQL 16.14 · profile pt-br · threshold 0.50 full validation — every row was examined; verdicts are conclusiveBROKEN — the relationship is real; its integrity is not (1) ──────────────────────────────────────────────────────────────────────────────────────────────── relation rows values orphan rows orphan values examined method public.os_servico.resp_tecnico → public.funcionario.id 97.5% 60.0% 200 200 8.0k fullCONFIRMED — total containment, verified row by row (2) ──────────────────────────────────────────────────────────────────────────────────────────────── relation rows values orphan rows orphan values examined method public.item_pedido.pedido_id → public.pedido.id 100.0% 100.0% 0 0 60.0k full public.pedido.cliente_id → public.cliente.id 100.0% 100.0% 0 0 20.0k fullWEAK — the data supports no conclusion either way (0) ────────────────────────────────────────────────────────────────────────────────────────────────noneUNVALIDATED — no evidence gathered; not the same as clean (0) ────────────────────────────────────────────────────────────────────────────────────────────────noneDETECTED — conventions read from the schema itself, added to pt-br ──────────────────────────────────────────────────────────────────────────────────────────────── key name id 5 occurrences (100%) — e.g. cliente, funcionario, nota_fiscal1 broken · 2 confirmed · 0 weak · 0 unvalidated · 0 discarded · 148ms 6 tables · 6 analyzed (100%) stats prefilter: 3 checked · 0 rejected · 0 without statistics ! statistics reset time unknown — usage counters carry no meaning \ No newline at end of file From 36ade98fe3d3a0e8d4d1e31f872c087c4bbf2a4f Mon Sep 17 00:00:00 2001 From: lvcas-dotcom Date: Thu, 27 Aug 2026 18:48:47 -0300 Subject: [PATCH 3/8] =?UTF-8?q?docs(readme):=20os=20n=C3=BAmeros=20aparece?= =?UTF-8?q?m=20na=20primeira=20tela,=20com=20os=20dois=20regimes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit O recall estava na linha 439, e o único número acima dele era "around 61%" — o mais fraco dos quatro, dentro de um aviso. Quem decide em quinze segundos se vai ler o resto nunca chegava no 84,9%, e o 84,9% é a demonstração mais curta que o projeto tem de por que a detecção de convenção existe. Os quatro números sobem juntos, de propósito: 62,6% e 61,5% no GitLab, 84,9% e 16,6% no schema municipal. Publicar só o 84,9% seria escolher o número conveniente, que é exatamente o que este README passa a seção de medição inteira dizendo que não faz. O par é mais forte que a metade boa dele. O aviso perde a frase de recall, agora redundante, e ganha a informação de que a imagem é gravada da saída real pelo `make demo-svg`. A navegação passa a listar Prior art e o guia em `docs/guide/`. --- README.md | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 0f12c4f..97a00d3 100644 --- a/README.md +++ b/README.md @@ -22,13 +22,25 @@ Safety · How it works · Measurements · - Roadmap + Prior art · + Roadmap · + Guide

--- `pgfathom` finds the relationships your database has but never declared — and proves them against the data instead of guessing from column names. +Take a 1,857-key GitLab schema, drop half its foreign keys, and it brings **62.6%** of +them back; drop all of them and it still brings back **61.5%**. On a Brazilian municipal +schema whose reference affix no shipped profile knows — `idkey_lote` in one table, +`lote_idkey` in the next — it brings back **84.9%**, because it reads that convention off +the keys the schema still declares. Take those away too and the same schema falls to +**16.6%**, which is the number that says what the technique actually rests on. Both +regimes are published for every schema in [a corpus anyone can re-run](#the-measured-corpus), +and [what none of them measures](#how-correctness-is-measured) is stated as plainly as +what they do. +

pgfathom discover finding one broken and two confirmed relationships

@@ -44,13 +56,11 @@ own integrity, and prints exactly what you see above. > [!IMPORTANT] > **Early, and measured.** -> `pgfathom audit` and `pgfathom discover` run end to end, verdicts and reviewable -> `.sql` artifacts included, against real production schemas and against a -> [public corpus](#the-measured-corpus) anyone can re-run with `make benchmark`. -> Recovery is around 61% on a 1,857-key schema, and the report says what that -> number does not measure as plainly as what it does. What the tool never does is -> write to your database. The terminal output below is a real run against the -> demo schema in [`docs/DEMO.md`](docs/DEMO.md), which you can reproduce in a +> `pgfathom audit` and `pgfathom discover` run end to end — verdicts and reviewable +> `.sql` artifacts included — against real production schemas and against the public +> corpus. What the tool never does is write to your database. The image above is a real +> run against the demo schema in [`docs/DEMO.md`](docs/DEMO.md), re-recorded from the +> tool's own bytes by `make demo-svg` rather than screenshotted, and reproducible in a > container in about a minute. --- From fc1fc8938bbfdd0eec08cd767ea43807d94d2219 Mon Sep 17 00:00:00 2001 From: lvcas-dotcom Date: Thu, 27 Aug 2026 18:49:15 -0300 Subject: [PATCH 4/8] =?UTF-8?q?docs:=20diz=20que=20n=C3=A3o=20existe=20n?= =?UTF-8?q?=C3=BAmero=20de=20precision,=20e=20o=20que=20faltaria=20para=20?= =?UTF-8?q?existir?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A seção de medição já dizia que veredito não é medido e que candidato fora do truth set não é erro. As duas juntas implicam a ausência de precision sem nunca nomeá-la, e "implica" não serve aqui: é a primeira pergunta que qualquer pessoa cética faz, e deixá-la sem resposta escrita transfere a resposta para uma thread de fórum, onde ela sai pior. Fica dito o que faltaria para medir — linhas, para existir veredito, e um gabarito para os candidatos fora do truth set — e fica dito que a afirmação disponível é a estreita: nenhum falso positivo confirmado contra os fixtures de integração, o que é deliberadamente menos do que "precision é alta". Apontado para a issue #36, que é onde isso vira número. --- README.md | 12 +++++++++++- .../measuring-correctness.md | 9 +++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 97a00d3..b84db38 100644 --- a/README.md +++ b/README.md @@ -487,7 +487,7 @@ That is a fact about this corpus, not about the technique: the demo schema in view. What the corpus establishes is that a schema dump is a poor place to look for joins, and that anyone quoting these numbers should say so. -Three things these tables are not saying. +Four things these tables are not saying. **No verdict is measured.** A published `structure.sql` has no rows, so nothing can be confirmed or broken; what is measured is whether the right candidate was *raised*. The rule @@ -498,6 +498,16 @@ the answer was built alongside the scenario. that was never declared is this tool's product, so the count is published as what it is and enters no error rate. +**There is no precision figure, and that is the open gap.** Recall is measured; precision +is not. Measuring it needs two things this corpus cannot give — rows, so that a verdict +exists to be right or wrong about, and a labelled answer for the candidates outside the +truth set, which today are counted rather than classified. Until both exist, the strongest +honest claim is the narrow one: no false positive has ever been confirmed against the +integration fixtures. That is deliberately narrower than "precision is high", and the +difference is not rhetorical. Closing it is +[issue #36](https://github.com/lvcas-dotcom/pgfathom/issues/36) — synthetic rows in the +corpus, so verdicts enter the measurement instead of sitting beside it. + **Composite keys: 1 of 53 recovered on GitLab**, and the 52 are explained. Every one has the shape `(partition_id, build_id) → (id, partition_id)`: one position matching the key column by name, one anchoring on the target. Matching used to refuse that mix; it no longer does, diff --git a/docs/guide/05-positioning-and-metrics/measuring-correctness.md b/docs/guide/05-positioning-and-metrics/measuring-correctness.md index 19c1399..382b8ba 100644 --- a/docs/guide/05-positioning-and-metrics/measuring-correctness.md +++ b/docs/guide/05-positioning-and-metrics/measuring-correctness.md @@ -18,13 +18,18 @@ On the pt-BR municipal schema in the corpus, partial recall is **84.9%**; greenf the exact same schema, is **16.6%**. That gap is the entire argument for naming-convention detection — see [Naming profiles](../03-features-and-safety/naming-profiles.md). -## Three things the recall table does not say +## Four things the recall table does not say 1. **No verdict is measured** — the public dumps have no rows, so nothing gets confirmed or broken. What's measured is whether the right candidate was *raised*. 2. **Candidates outside the ground truth aren't errors** — in a real schema, a true relationship nobody declared is the product, not a false positive. -3. **Composite keys: 1 of 53 recovered on GitLab**, and the other 52 are explained (they +3. **There is no precision figure** — recall is measured, precision is not, because that + needs rows (so a verdict exists to be right about) and a labelled answer for the + candidates outside the truth set. The honest claim stays the narrow one: no false + positive has ever been confirmed against the integration fixtures. Tracked as + [issue #36](https://github.com/lvcas-dotcom/pgfathom/issues/36). +4. **Composite keys: 1 of 53 recovered on GitLab**, and the other 52 are explained (they only match half the key's shape) — not silently absorbed into a lower headline number. ## Coverage is part of the metric From 09c5d60352cfe08f352eaa4c47880835d672a386 Mon Sep 17 00:00:00 2001 From: lvcas-dotcom Date: Thu, 27 Aug 2026 18:49:36 -0300 Subject: [PATCH 5/8] docs: o roadmap aponta para a release corrente e para as issues abertas MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "After v0.1" com a v0.2.0 no ar lê como projeto parado, e o documento inteiro se chama "Roadmap de implementação — v0.1" enquanto as oito fases dele estão fechadas. Nos dois casos o texto está certo sobre o passado e mudo sobre o presente, que é a forma mais fácil de um repositório parecer abandonado para quem chega. O README passa a dizer qual é a release corrente e a listar o que vem como issue — #36 e #38 — em vez de prometer no próprio README. Promessa em roadmap não tem onde ser discutida; issue tem. --- README.md | 16 +++++++++++----- docs/ROADMAP.md | 6 ++++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index b84db38..76580b7 100644 --- a/README.md +++ b/README.md @@ -591,11 +591,17 @@ like from a schema that finally knows its own relationships. | 7 | Terminal, JSON and SQL output | Done | | 8 | Composite keys, benchmark corpus and release | Done | -Full detail in [`docs/ROADMAP.md`](docs/ROADMAP.md). - -**After v0.1:** `pgfathom check --baseline` for CI — fail the build when a new undeclared -relationship appears or an orphan count grows. Then structural findings, cross-cutting -patterns (tenant columns, polymorphic pairs), and DBML/Mermaid/PlantUML export. +Those eight phases are the v0.1 roadmap, closed; full detail in +[`docs/ROADMAP.md`](docs/ROADMAP.md). The current release is **v0.2.0**, and what changed +in it is in [`CHANGELOG.md`](CHANGELOG.md). + +**Next, and tracked as issues rather than promised here:** +[measuring verdicts instead of only candidate generation](https://github.com/lvcas-dotcom/pgfathom/issues/36), +which is the open gap in the numbers above, and +[`pgfathom check --baseline`](https://github.com/lvcas-dotcom/pgfathom/issues/38) for CI — +fail the build when a new undeclared relationship appears or an orphan count grows. Then +structural findings, cross-cutting patterns (tenant columns, polymorphic pairs), and +DBML/Mermaid/PlantUML export. Code generation is explicitly *not* on the roadmap. diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 57facee..c419bc4 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -1,5 +1,11 @@ # Roadmap de implementação — v0.1 +> **Status.** As oito fases abaixo estão fechadas e saíram na v0.1.0; a release +> corrente é a **v0.2.0**. O que mudou depois está no +> [CHANGELOG.md](../CHANGELOG.md), e o trabalho seguinte é acompanhado como +> issue, não aqui. Este documento continua sendo o registro de como o MVP foi +> ordenado e por quê — que é a parte que não envelhece. + Oito fases até o MVP. Cada fase é uma change do OpenSpec e termina em algo verificável — não em "camada pronta", mas em comportamento observável que passa em teste. O princípio de ordenação é **empurrar o risco para a frente**. As duas primeiras fases não tocam em dado de usuário e não dependem de rede, então falham barato. A validação contra dados, que é onde mora o risco operacional, chega com o resto já testado embaixo dela. From 897de9e617382b41e048f9ae4711dbe80443ea60 Mon Sep 17 00:00:00 2001 From: lvcas-dotcom Date: Thu, 27 Aug 2026 18:49:56 -0300 Subject: [PATCH 6/8] =?UTF-8?q?docs(contributing):=20nomeia=20o=20que=20es?= =?UTF-8?q?t=C3=A1=20em=20portugu=C3=AAs=20e=20aponta=20o=20guia=20em=20in?= =?UTF-8?q?gl=C3=AAs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A seção de idioma afirmava que tudo na árvore é inglês exceto commits e `openspec/`. `docs/PGFATHOM.md` e `docs/ROADMAP.md` também são português, e são justamente os dois que o README chama de fonte da verdade — quem foi conferir encontrava a exceção não declarada, que é pior do que a exceção. Falta maior: o `docs/guide/` existe desde a change community-guide, é a camada de orientação em inglês sobre exatamente esse material, e o CONTRIBUTING não o mencionava em lugar nenhum. Um contribuidor de fora lia "o registro de design é em português" e não tinha como descobrir que havia um caminho. Agora tem, no topo e na própria seção de idioma. --- CONTRIBUTING.md | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 27bd7ad..da506d1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,6 +3,11 @@ Thanks for looking. This document is the short version of how the project works; the reasoning behind the rules lives in `openspec/project.md`. +Never seen the codebase? [`docs/guide/`](docs/guide/README.md) is the map — the +architecture, the seven-stage pipeline, the safety rules and what the recall +numbers mean, one page at a time. It is written for someone who has read none of +the rest. + ## What you need - **Go 1.25** or later @@ -89,13 +94,19 @@ both. ### Language -**Everything in the repository tree is English** — code, comments, `--help`, -build files, CI workflows, and the documents you are reading. If a contributor -can open it, it is in English. +**Everything a contributor needs is in English** — code, comments, `--help`, +build files, CI workflows, the tests, and the documents you are reading. + +Three things are in Portuguese, the maintainer's language, and deliberately so: +**commit messages**, the design record under `openspec/`, and the two documents +it grew out of — [`docs/PGFATHOM.md`](docs/PGFATHOM.md) and +[`docs/ROADMAP.md`](docs/ROADMAP.md). They are the project's history and its +reasoning, not its interface. -Two exceptions, both deliberate: **commit messages** and the design record under -`openspec/` are written in Portuguese, the maintainer's language. They are the -project's history and its reasoning, not its interface. +You do not need to read any of them to send a patch. [`docs/guide/`](docs/guide/README.md) +covers the same ground in English and links back to each Portuguese source for +the full detail. Where the two disagree, the Portuguese source is the +authoritative one — worth knowing before you argue with a comment. Commits follow [Conventional Commits](https://www.conventionalcommits.org/), and the body should say why, not restate the diff. From 417b92e0308fb472a03b67ae5025544a74199431 Mon Sep 17 00:00:00 2001 From: lvcas-dotcom Date: Thu, 27 Aug 2026 18:50:06 -0300 Subject: [PATCH 7/8] =?UTF-8?q?docs(readme):=20a=20instala=C3=A7=C3=A3o=20?= =?UTF-8?q?por=20Homebrew=20deixa=20de=20ser=20condicional?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "Homebrew works once the tap exists" era verdade quando foi escrito e virou o contrário: `lvcas-dotcom/homebrew-tap` existe, com `Casks/pgfathom.rb` gerado pelo goreleaser na 0.2.0. A frase condicional numa seção de instalação lê como "isto ainda não funciona", que é o pior lugar possível para uma ressalva vencida. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 76580b7..3333f97 100644 --- a/README.md +++ b/README.md @@ -206,7 +206,7 @@ check it against the `checksums.txt` published beside it. The artifacts are not signed; the checksum file is what exists today, and [`docs/RELEASING.md`](docs/RELEASING.md) says so where it can be found. -On macOS, Homebrew works once the tap exists: +On macOS, from the project's tap: ```console $ brew install lvcas-dotcom/tap/pgfathom From 2a6c56e682b666295c70927f917814b9fec1ce49 Mon Sep 17 00:00:00 2001 From: lvcas-dotcom Date: Thu, 27 Aug 2026 18:50:19 -0300 Subject: [PATCH 8/8] =?UTF-8?q?docs(readme):=20a=20contagem=20de=20m=C3=B3?= =?UTF-8?q?dulos=20diz=20em=20que=20alvo=20foi=20medida?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vinte e cinco módulos e 11 MB está certo, e só no alvo do release: em `linux/amd64` são exatamente 25 e 11,1 MB. Construindo em Windows são 28, porque entram os módulos específicos da plataforma. O número está numa seção que convida o leitor a conferir — "vai abrir o go.mod primeiro" — então um leitor que confere e acha 28 encontra o projeto errado sobre si mesmo em vez de encontrar uma medição com alvo implícito. --- README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 3333f97..1d5e6db 100644 --- a/README.md +++ b/README.md @@ -315,11 +315,12 @@ coverage block on every run. A clean report means "I looked and it's clean", nev couldn't look". **Small dependency tree, on purpose.** Six direct dependencies, twenty-five modules in the -linked binary, 11 MB, no cgo. Four of the six do the work — the driver, the CLI framework, -the TOML reader and `x/sync`. The other two draw the interactive `setup` guide and are -reachable from nothing else; that cost was argued before it was paid, in -[`openspec/project.md`](openspec/project.md). The person who has to approve running this -against production will open `go.mod` first, and we intend that to be a short read. +linked binary, 11 MB on `linux/amd64`, no cgo. Four of the six do the work — the driver, +the CLI framework, the TOML reader and `x/sync`. The other two draw the interactive +`setup` guide and are reachable from nothing else; that cost was argued before it was +paid, in [`openspec/project.md`](openspec/project.md). The person who has to approve +running this against production will open `go.mod` first, and we intend that to be a short +read. ## How it works