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. diff --git a/README.md b/README.md index aea54bd..1d5e6db 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. --- @@ -86,12 +96,12 @@ your diagram. It never checked a single pre-existing row. ## What it does ```console -$ pgfathom discover --schema public +$ 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 +127,7 @@ $ pgfathom discover --schema public ──────────────────────────────────────────────────────────────────────────────────────────────── 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 @@ -196,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 @@ -305,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 @@ -477,7 +488,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 @@ -488,6 +499,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, @@ -571,11 +592,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/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 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. 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