ripple-sql 0.1.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.
- ripple_sql-0.1.0/.gitignore +31 -0
- ripple_sql-0.1.0/CHANGELOG.md +226 -0
- ripple_sql-0.1.0/LICENSE +202 -0
- ripple_sql-0.1.0/PKG-INFO +285 -0
- ripple_sql-0.1.0/README.md +245 -0
- ripple_sql-0.1.0/pyproject.toml +79 -0
- ripple_sql-0.1.0/src/ripple/__init__.py +31 -0
- ripple_sql-0.1.0/src/ripple/answer.py +473 -0
- ripple_sql-0.1.0/src/ripple/answer_page.py +214 -0
- ripple_sql-0.1.0/src/ripple/cache.py +80 -0
- ripple_sql-0.1.0/src/ripple/ci.py +422 -0
- ripple_sql-0.1.0/src/ripple/ci_signature.py +374 -0
- ripple_sql-0.1.0/src/ripple/cli.py +733 -0
- ripple_sql-0.1.0/src/ripple/doctor.py +225 -0
- ripple_sql-0.1.0/src/ripple/engine/__init__.py +111 -0
- ripple_sql-0.1.0/src/ripple/engine/budget.py +86 -0
- ripple_sql-0.1.0/src/ripple/engine/column_lineage.py +112 -0
- ripple_sql-0.1.0/src/ripple/engine/column_ref.py +818 -0
- ripple_sql-0.1.0/src/ripple/engine/cte_tracing.py +1309 -0
- ripple_sql-0.1.0/src/ripple/engine/dependencies.py +466 -0
- ripple_sql-0.1.0/src/ripple/engine/dialect.py +132 -0
- ripple_sql-0.1.0/src/ripple/engine/dispatch.py +12 -0
- ripple_sql-0.1.0/src/ripple/engine/extraction.py +27 -0
- ripple_sql-0.1.0/src/ripple/engine/jinja.py +282 -0
- ripple_sql-0.1.0/src/ripple/engine/json_sources.py +241 -0
- ripple_sql-0.1.0/src/ripple/engine/macro_source.py +127 -0
- ripple_sql-0.1.0/src/ripple/engine/pipeline.py +265 -0
- ripple_sql-0.1.0/src/ripple/engine/preprocess.py +174 -0
- ripple_sql-0.1.0/src/ripple/engine/safe_gen.py +21 -0
- ripple_sql-0.1.0/src/ripple/engine/schema_qualification.py +151 -0
- ripple_sql-0.1.0/src/ripple/engine/scope.py +488 -0
- ripple_sql-0.1.0/src/ripple/engine/select_sources.py +1038 -0
- ripple_sql-0.1.0/src/ripple/engine/sql_script.py +729 -0
- ripple_sql-0.1.0/src/ripple/engine/statement.py +449 -0
- ripple_sql-0.1.0/src/ripple/engine/tech_debt.py +169 -0
- ripple_sql-0.1.0/src/ripple/engine/tsql_catalog.py +83 -0
- ripple_sql-0.1.0/src/ripple/engine/tsql_scalar_vars.py +248 -0
- ripple_sql-0.1.0/src/ripple/engine/tsql_tvf.py +653 -0
- ripple_sql-0.1.0/src/ripple/engine/tsql_xml.py +97 -0
- ripple_sql-0.1.0/src/ripple/engine/types.py +167 -0
- ripple_sql-0.1.0/src/ripple/engine/unused_deps.py +555 -0
- ripple_sql-0.1.0/src/ripple/engine/validation.py +158 -0
- ripple_sql-0.1.0/src/ripple/graph.py +1499 -0
- ripple_sql-0.1.0/src/ripple/home.py +232 -0
- ripple_sql-0.1.0/src/ripple/loaders/__init__.py +7 -0
- ripple_sql-0.1.0/src/ripple/loaders/dbt.py +359 -0
- ripple_sql-0.1.0/src/ripple/loaders/dbt_config.py +339 -0
- ripple_sql-0.1.0/src/ripple/loaders/identity.py +328 -0
- ripple_sql-0.1.0/src/ripple/loaders/sidecar.py +65 -0
- ripple_sql-0.1.0/src/ripple/loaders/sqldir.py +262 -0
- ripple_sql-0.1.0/src/ripple/loaders/types.py +197 -0
- ripple_sql-0.1.0/src/ripple/lookml.py +163 -0
- ripple_sql-0.1.0/src/ripple/mcp_server.py +600 -0
- ripple_sql-0.1.0/src/ripple/names.py +40 -0
- ripple_sql-0.1.0/src/ripple/project.py +167 -0
- ripple_sql-0.1.0/src/ripple/py.typed +0 -0
- ripple_sql-0.1.0/src/ripple/render.py +426 -0
- ripple_sql-0.1.0/src/ripple/render_shims.py +209 -0
- ripple_sql-0.1.0/src/ripple/schemas.py +155 -0
- ripple_sql-0.1.0/src/ripple/semantic.py +232 -0
- ripple_sql-0.1.0/src/ripple/server.py +184 -0
- ripple_sql-0.1.0/src/ripple/sourcefiles.py +64 -0
- ripple_sql-0.1.0/src/ripple/star_resolution.py +100 -0
- ripple_sql-0.1.0/src/ripple/static/answer.css +146 -0
- ripple_sql-0.1.0/src/ripple/static/answer.html +358 -0
- ripple_sql-0.1.0/src/ripple/static/answer_twin.js +299 -0
- ripple_sql-0.1.0/src/ripple/static/explore.js +133 -0
- ripple_sql-0.1.0/src/ripple/usage/__init__.py +18 -0
- ripple_sql-0.1.0/src/ripple/usage/cli.py +78 -0
- ripple_sql-0.1.0/src/ripple/usage/collect.py +315 -0
- ripple_sql-0.1.0/src/ripple/usage/discover.py +190 -0
- ripple_sql-0.1.0/src/ripple/usage/ingest.py +414 -0
- ripple_sql-0.1.0/src/ripple/usage/report.py +131 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
.venv/
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.egg-info/
|
|
4
|
+
dist/
|
|
5
|
+
build/
|
|
6
|
+
.pytest_cache/
|
|
7
|
+
.ruff_cache/
|
|
8
|
+
.mypy_cache/
|
|
9
|
+
target/
|
|
10
|
+
.coverage
|
|
11
|
+
htmlcov/
|
|
12
|
+
.env
|
|
13
|
+
.DS_Store
|
|
14
|
+
.idea/
|
|
15
|
+
.vscode/
|
|
16
|
+
|
|
17
|
+
# keep uv.lock tracked (reproducible installs); do not ignore it
|
|
18
|
+
benchmark/corpus/
|
|
19
|
+
|
|
20
|
+
# run logs are local receipts, never tracked
|
|
21
|
+
benchmark/*.log
|
|
22
|
+
coverage_stderr.log
|
|
23
|
+
|
|
24
|
+
benchmark/accuracy_partial.json
|
|
25
|
+
|
|
26
|
+
benchmark/corpus_holdout/*/
|
|
27
|
+
!benchmark/corpus_holdout/manifest.json
|
|
28
|
+
|
|
29
|
+
# private review handoff, never ship
|
|
30
|
+
REVIEW-HANDOFF.md
|
|
31
|
+
PLAN-PRIVATE.md
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Notable changes to Ripple. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and Ripple aims for
|
|
5
|
+
[semantic versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.1.0] - 2026-09-08
|
|
10
|
+
|
|
11
|
+
First public release.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
- Column-level SQL lineage, offline, across Snowflake, BigQuery, Databricks, Redshift, Postgres, DuckDB, and Spark SQL.
|
|
15
|
+
- Three ways in: a dbt project (compiled manifest or raw), a monorepo of dbt projects each parsed in its own dialect, or a plain folder of `.sql` files.
|
|
16
|
+
- `breaks`, `trace`, `models`, `columns`, `graph`, `ci`, `serve`, and `mcp` commands.
|
|
17
|
+
- A trust label on every edge (`verified`, `high_confidence`, `moderate`, `review_required`). Uncertain lineage is flagged, never guessed, and trust follows the path.
|
|
18
|
+
- Multi-statement scripts and dumps handled statement by statement, so a data dump becomes its tables and views instead of a parse error.
|
|
19
|
+
- Downstream reach into the dbt semantic layer (metrics, dimensions) and Looker (LookML dashboards and fields), so a column traces to the metric and dashboard it feeds. Looker support is the `looker` extra.
|
|
20
|
+
- An MCP server, so an agent can ask what a change breaks before it opens a pull request.
|
|
21
|
+
- A GitHub Action and `ripple ci` for pull-request blast-radius comments.
|
|
22
|
+
- `ripple serve` opens on the map: every model in the project in its layer, source tables on the left, dashboards on the right, with the links between them. Hovering a model lights its whole chain upstream and down and fades the rest; clicking one unfolds its columns; clicking a column asks. Typing in the question box filters the map before it asks. Models in a layer sit level with the models feeding them, so links cross as little as one pass can manage. Past forty models a layer folds behind "more", and past four hundred links the wires draw only for the lit chain, so a large repo stays readable. The map is computed in `home.model_map` from the graph's nodes and edges, pure and tested; the page draws it from `static/explore.js` and never fetches. An answer replaces the map with "All models" as the way back. The answer page's stylesheet moved to `static/answer.css`.
|
|
23
|
+
- Two more doors to the answer page. `ripple ci --html PATH` writes one page for the whole change, a view per changed column with the rebuild list and the graph embedded for follow-ups; the GitHub Action keeps it as a workflow artifact and links it from the comment. The MCP `breaks` and `trace` tools now carry the answer's text for the agent to relay, and with `page: true` return `html_path`, a file the person can open. Nothing is sent anywhere.
|
|
24
|
+
- The text answer, rewritten after a readability review: columns that share a source share one line, so the arrow only appears where a name changes; "step" replaces "hop"; dashboard numbers sit in their own section with the kind as a word after the name ("orders (semantic model): is_food_order"); a column the graph could not prove keeps its own line ending in "needs review". jaffle-shop's `ecom.raw_products.type` went from 34 lines to 16. The answer page's JavaScript twin of this text is checked against the Python under node in the test suite.
|
|
25
|
+
- The answer page. `ripple breaks orders.order_total --open` (and `trace`) writes one self-contained HTML file and opens it: the question, the ripple hop by hop with review flags and dashboard numbers marked, the terminal text beside it with a copy button, and a box to ask the next question, answered inside the page from the graph it carries (under a 5 MB budget, `RIPPLE_EMBED_GRAPH_BYTES`). `--html PATH` writes it where you say. No server, nothing sent, nothing loaded from the network. A link with `?q=model.column&dir=trace` opens straight on a question.
|
|
26
|
+
- One answer contract, `ripple.answer`: a `breaks` or `trace` result as nodes with hop depth and trust, links, and summary counts, plus the headline every surface prints. The CLI headline and "hit hardest" line now come from it, so the terminal, MCP, the pull-request comment, and the coming answer page render the same answer.
|
|
27
|
+
|
|
28
|
+
- `benchmark/walk.py` walks the MCP ladder on every corpus and holdout
|
|
29
|
+
repository the way an agent would (summary, unresolved tables, a question
|
|
30
|
+
about each, ingest their columns, ask again, name every model) and fails
|
|
31
|
+
on anything a first-time user would hit. It runs nightly. The first passes
|
|
32
|
+
found seven classes no unit test had, each now pinned: dbt-disabled models
|
|
33
|
+
the summary counted but no tool could name; a monorepo source declared
|
|
34
|
+
twice that took ingested columns on one copy; an ingest swallowed by a
|
|
35
|
+
same-named model; a table created `LIKE` another that accepted any column;
|
|
36
|
+
a model's own name refused as ambiguous; deploy-time placeholder
|
|
37
|
+
qualifiers (`{hscic}.ccgs`, `{{params.dataset}}.blocks`) listed in a
|
|
38
|
+
spelling no ingest could use; and a column list an agent would copy,
|
|
39
|
+
truncated at 200 on a table read on more.
|
|
40
|
+
- `breaks` answers carry `also_matches` when a spelling was settled by being
|
|
41
|
+
one model's own name and other models carry it as an alias.
|
|
42
|
+
- Every MCP response now ends with a `next` line an agent can act on, the
|
|
43
|
+
way the CLI ends on `try:`. The summary and `breaks` hints count before
|
|
44
|
+
they blame: review links that trace to tables outside the project are sent
|
|
45
|
+
to `unresolved_tables`, links reading columns a declared schema lacks say
|
|
46
|
+
which table to re-ingest, and the rest are named as ambiguous names or
|
|
47
|
+
unparsed statements, which no ingest fixes. `ingest_schema` reports the
|
|
48
|
+
review and star-only counts before and after, including an honest
|
|
49
|
+
"nothing moved".
|
|
50
|
+
- `breaks` on a table outside the project answers with the models that read
|
|
51
|
+
it; a column it never saw read returns the columns readers actually use
|
|
52
|
+
(the full list, so it can be handed straight to `ingest_schema`) instead of
|
|
53
|
+
"0 impacted, complete".
|
|
54
|
+
- `unresolved_tables` reports the qualified spelling the SQL wrote (the one an
|
|
55
|
+
ingest must use) and includes tables only the engine saw, such as a raw
|
|
56
|
+
table read by name inside a dbt model.
|
|
57
|
+
- dbt seeds are loaded in raw mode: a CSV's header row is the table's
|
|
58
|
+
columns, so `ref('state_codes')` resolves and `SELECT *` over it expands.
|
|
59
|
+
|
|
60
|
+
- Dialect is read from the SQL when nothing declares it: a file starting
|
|
61
|
+
with `-- Databricks notebook source` is databricks (dbdemos-notebooks was
|
|
62
|
+
parsed as tsql), stl_/svv_ system tables and DISTKEY/SORTKEY are redshift
|
|
63
|
+
(dbt-labs/redshift has no profile and was guessed snowflake), and a dbt
|
|
64
|
+
project with no profile now sniffs its models before falling back to the
|
|
65
|
+
default, saying "the SQL reads as X" rather than "guessed". Plain SQL
|
|
66
|
+
beside a dbt project inherits the project's declared dialect instead of
|
|
67
|
+
assuming postgres (Snowflake's tasty_bytes demo).
|
|
68
|
+
- The summary prints three shared-name warnings and a count instead of
|
|
69
|
+
every one (HTTP Archive's almanac printed 841 lines); `ripple --json` now
|
|
70
|
+
carries the full `warnings` list. The "try:" hint prefers a name a person
|
|
71
|
+
can type over a path-qualified collision name.
|
|
72
|
+
- Plain SQL folders laid out as `<project>/<dataset>/<table>/query.sql`
|
|
73
|
+
(Mozilla's bigquery-etl, Dataform-style repos) now name each relation by
|
|
74
|
+
its directory and answer to `dataset.table` and `project.dataset.table`.
|
|
75
|
+
Before, 887 files were all called "query" and no reference could bind.
|
|
76
|
+
- A directory holding a `schema.yaml`/`schema.json` and no SQL becomes a
|
|
77
|
+
base table with those declared columns, so `SELECT *` views over tables
|
|
78
|
+
loaded outside the repo expand instead of degrading everything downstream
|
|
79
|
+
to review. On bigquery-etl this took trusted links from 6,191 to 22,916.
|
|
80
|
+
- An ambiguous target now suggests the spellings a person would type
|
|
81
|
+
(`telemetry_derived.events_v1`), not path-mangled internal names.
|
|
82
|
+
- `breaks` and `trace` take `--depth N` (and the MCP tools a `depth`
|
|
83
|
+
argument) to follow lineage past the default 25 hops; `trace` now
|
|
84
|
+
discloses when it stopped at the cap, the way `breaks` always has.
|
|
85
|
+
- The per-file wall-clock budget (`RIPPLE_PARSE_BUDGET_S`) now covers the
|
|
86
|
+
analysis phase too, not just the parse: a generated 3.7MB one-line SELECT
|
|
87
|
+
parses in seconds and then wedged column analysis for over two minutes.
|
|
88
|
+
A model that blows the budget reports `timed_out`; the rest of the
|
|
89
|
+
project still resolves.
|
|
90
|
+
- Statements over 1MB are refused up front with the file named and
|
|
91
|
+
`RIPPLE_MAX_SQL_BYTES` to override, instead of grinding for minutes: a
|
|
92
|
+
10,000-model repo with two multi-MB generated dumps now cold-builds in
|
|
93
|
+
43s instead of 8+ minutes.
|
|
94
|
+
- Usage ingest: `ripple ingest-usage <file>` takes a query-history export
|
|
95
|
+
(JSONL or JSON array) and `ripple usage` reports which models actually ran
|
|
96
|
+
in the window, which didn't, and what the queries read outside the project.
|
|
97
|
+
Statements with no database context are skipped rather than guessed, the
|
|
98
|
+
word "unused" never appears (a quarterly job looks identical to a dead one),
|
|
99
|
+
and the query text itself is never stored.
|
|
100
|
+
- Usage collect: `ripple collect-usage` (and the `collect_usage` MCP tool)
|
|
101
|
+
runs your already-authenticated warehouse CLI — `snow`, `bq`, or
|
|
102
|
+
`databricks` — and ingests the result in one step. No credential is read,
|
|
103
|
+
stored, or transmitted; the raw rows are spooled to a temp file, aggregated,
|
|
104
|
+
and deleted inside the same call. A partial or polluted export is refused
|
|
105
|
+
outright, and with several configured connections and no declared default,
|
|
106
|
+
collect lists them instead of picking one.
|
|
107
|
+
- MCP: `ingest_usage`, `usage_summary`, and `collect_usage` tools; `ripple
|
|
108
|
+
doctor` self-test for MCP client setup.
|
|
109
|
+
- `ripple ci --select dbt` prints the models to rebuild as a dbt `--select`
|
|
110
|
+
string, computed from git and column lineage: no state backend, no
|
|
111
|
+
manifest artifact, no meter. Enumerates only what the diff actually
|
|
112
|
+
touches; anything uncertain (unparseable diff, depth-capped blast, changed
|
|
113
|
+
row filter, a deleted model's readers) widens to `model+` rather than
|
|
114
|
+
being skipped, so a missed rebuild can't happen silently. The PR comment
|
|
115
|
+
and `--json` report carry the same rebuild set, and `breaks_all` now
|
|
116
|
+
reports honest completeness instead of a hardcoded `true`.
|
|
117
|
+
|
|
118
|
+
### Changed
|
|
119
|
+
- The answer page looks like its name. A cool water palette: an off-white ground with a hint of blue, deep slate ink, one teal that marks the column you asked about (the drop, with two faint rings that settle when the page opens) and the one button. Links leaving that column carry the teal and fade to gray with distance. A three-ring mark sits by the name. Less to read: the coverage ladder is a status line per rung with the sentence behind a details link, the command's text is folded under a Show text button with Copy text beside it, and the fine print under the question box is one short line.
|
|
120
|
+
- The answer page and `ripple serve` wear a plainer skin: one type scale, one corner radius, system faces, hairlines instead of cards, the accent kept for the one action, state colors kept apart from it (ink for the column you asked about, teal for a dashboard number, amber for a link that needs review), and the command's text on a light well instead of a dark panel. The dotted background, the tracked all-caps labels, and the pill chips are gone.
|
|
121
|
+
- `ripple serve` is now the answer page, live. It opens on a question box, five columns worth asking about first, and the coverage ladder (which external tables still block links, whether query history has been brought in, and the command that moves each rung) instead of painting the whole graph. Answers are computed per request from the files as they are now, by the same renderer the CLI uses, so the page and the terminal cannot disagree. `ripple serve orders.order_total` opens straight on a question. The server stays localhost-only and read-only. `/api/breaks` and `/api/trace` return the answer contract with its text; `/api/graph` is unchanged.
|
|
122
|
+
|
|
123
|
+
### Fixed
|
|
124
|
+
- Ripple holds on Windows. Every SQL, YAML, LookML, manifest, and cache file is read and written as UTF-8 whatever the machine's code page (a test scans the source for a text read or write that forgets to say so). A pipe out of `ripple` carries UTF-8 too, so the arrows in the text survive `ripple breaks ... > out.txt` and the MCP transport on Windows, and a console that cannot draw an arrow shows a placeholder instead of crashing. A model's path is the same forward-slash string on every OS, so a PR comment written on Linux names the same model on a Windows laptop. CI runs the suite on Windows.
|
|
125
|
+
- Eight shapes `ripple ci` used to miss, all of them edits that change which rows survive while every per-column signature stays the same: a filter reached through a join alias (`join flags f ... where f.big`), a filter reached through two CTE hops, a GROUP BY key or expression, a QUALIFY on the select's own alias, and a union branch. One rule replaces the case-by-case checks: the row-deciding closure, every WHERE, HAVING, QUALIFY, JOIN, GROUP BY, and DISTINCT clause plus the whole chain of expressions behind every column those clauses read, through CTEs, derived tables, and set operations. A change anywhere in it widens to the whole model; when sqlglot cannot resolve the statement the raw clause text is the floor. The closure also covers the clauses that pick which rows survive rather than filtering them: an ORDER BY under a row cap (`limit`, `offset`, `fetch`, `top`) or a `DISTINCT ON`, and every projected expression under a plain `SELECT DISTINCT`, since those decide which rows count as duplicates. Ordering without a cap decides nothing and stays precise, and a projection inside a joined subquery no longer widens readers of untouched columns, while the join's kind, source, match condition, and join condition still do. The set operator itself is part of it, since `UNION` keeps one row per duplicate where `UNION ALL` keeps both and `EXCEPT` is not `INTERSECT`, and so are the clauses that reshape a row set without filtering a column (`TABLESAMPLE`, `CONNECT BY`, `PIVOT`, `MATCH_RECOGNIZE`). Duplicates are compared by value, so renaming an output under `SELECT DISTINCT` decides nothing.
|
|
126
|
+
- A column read only by a downstream WHERE, JOIN, or window key changes every row of that reader. The comment said "nothing downstream" and `--fail-on breaks` let it through; the comment now says "filters rows in paid" (or joins, or frames a window) and the gate fails.
|
|
127
|
+
- `ripple ci --html` crashed with a KeyError on any changed column whose blast radius passed the depth cap, and the change page flattened a window key or a review flag into "filters rows on it". Both facts now travel from ci.py into the page.
|
|
128
|
+
- `ripple breaks --json`, `ripple trace --json`, and the MCP `breaks` and `trace` tools returned the raw graph counts next to the contract's text, so a payload could disagree with its own words. Every JSON door now carries the contract under `answer`, the preview text under `text`, and top-level counts aligned to it. The MCP `page` flag is now inside the tool's declared properties.
|
|
129
|
+
- The answer page's own follow-up walk counted dashboard numbers as columns and skipped wildcard links; it is now a port of the graph's walk, checked against Python under node. A dashboard-only hit no longer reads as "Nothing downstream reads it".
|
|
130
|
+
- A trace stand-in for a reader the walk never listed sat one step further back instead of one step nearer. The contract's merge order now knows the graph's fourth trust level, "moderate", instead of raising on it.
|
|
131
|
+
- `ripple ci --select dbt` no longer lists `metric:`, `semantic:`, or `exposure:` names, which dbt cannot build.
|
|
132
|
+
- `ripple serve` refuses requests whose Host header is not loopback, so a page on another domain that rebinds DNS to 127.0.0.1 cannot read the graph. YAML and LookML edits now refresh the live graph and the disk cache. Concurrent first requests build the graph once.
|
|
133
|
+
- One pruned walk decides which files on disk feed lineage, shared by the disk cache and the live server, so a `dbt run` writing under `target/` no longer invalidates the graph on every request; the live server computes the fingerprint before taking its lock.
|
|
134
|
+
- A follow-up asked on the answer page resolves a model spelling the way the command does, whatever its case, so a name `ripple breaks` answers is no longer refused by the page it wrote. A spelling that names several models says so instead of guessing, and keeps what else it could have meant. A column no link touches is still a column, so `select 1 as x` answers there too. A link that resolves to nothing says so rather than showing the answer the page was written for.
|
|
135
|
+
- The live server saw a file rename that kept the file count and timestamps as no change and answered from a stale graph. One stamp over path, size, and time now serves the live check and the disk cache, and build directories are excluded whatever their case.
|
|
136
|
+
- The answer page's export carries each link's reason once, so an offline follow-up keeps the explanation behind a review flag; equal-depth hits are ordered by depth, trust label, and model on both sides, so the offline text matches the command's; the twin says "at the depth cap" when no depth is known.
|
|
137
|
+
- The home page's starters rank by reader models, not dashboard entries.
|
|
138
|
+
- The `breaks` headline and the pull-request comment counted dashboard numbers as columns: "19 columns in 4 models and 7 dashboard numbers" was 12 columns plus the 7 numbers themselves. One helper now counts model columns and dashboard numbers apart, in the CLI, the MCP payload, the answer page, and the CI comment. A change that reaches only dashboard numbers still fails `--fail-on breaks`.
|
|
139
|
+
|
|
140
|
+
- Ingested schemas were invisible to a dbt project living in a subfolder:
|
|
141
|
+
the CLI and MCP write `.ripple/schemas.json` at the repo root, the loader
|
|
142
|
+
read only the dbt folder. Every ingest on such a repo was a silent no-op.
|
|
143
|
+
- A read of a column missing from a declared or ingested schema was
|
|
144
|
+
downgraded with no reason attached; the edge now says which column and
|
|
145
|
+
which table's list may be incomplete.
|
|
146
|
+
- An ingested qualified table no longer claims a bare name a model owns, and
|
|
147
|
+
`model_columns` prefers the analyzed model when a name is shared with a
|
|
148
|
+
source.
|
|
149
|
+
- The semantic layer parser reads dbt's inline syntax (`semantic_model:`
|
|
150
|
+
nested under a model, dimensions and entities on columns, metrics on the
|
|
151
|
+
model). jaffle-shop migrated to it upstream and the old top-level parser
|
|
152
|
+
silently produced zero semantic nodes, so the README's own hero output
|
|
153
|
+
stopped reproducing. A metric with a constant or computed `expr` is
|
|
154
|
+
dropped, never guessed. The rename now also reaches `metric:lifetime_spend`
|
|
155
|
+
four hops down.
|
|
156
|
+
- Review sweep (11 findings fixed, each pinned by a test):
|
|
157
|
+
a hostile BigQuery `region` value can no longer smuggle SQL into the
|
|
158
|
+
user's authenticated `bq` session; Databricks collection reports its
|
|
159
|
+
scope as `visible-to-this-login` (its history API has no mine/account
|
|
160
|
+
split); a Snowflake window clamped to INFORMATION_SCHEMA's 7 days is
|
|
161
|
+
disclosed instead of silently misreported, and account-scope collections
|
|
162
|
+
hitting the row limit flag truncation; a qualified table read inside a
|
|
163
|
+
same-named CTE is no longer erased; two tables sharing a leaf name both
|
|
164
|
+
count; a valid export mixing offset-less and offset-bearing timestamps
|
|
165
|
+
no longer crashes ingestion; usage summaries (MCP and CLI) warn when the
|
|
166
|
+
project's models changed since ingest; and `ripple doctor`'s fallback
|
|
167
|
+
command now actually forwards `--path`, so the copy-paste config serves
|
|
168
|
+
the project it printed instead of the client's working directory.
|
|
169
|
+
- Usage attribution now uses model identity instead of bare leaf names.
|
|
170
|
+
With dbt-manifest aliases, a read of `other_db.payments` is an outside
|
|
171
|
+
table, not a sighting of this project's `payments`. Without identity,
|
|
172
|
+
one model name referenced from several databases in a single export is
|
|
173
|
+
refused into the ambiguous bucket rather than merged, because merging
|
|
174
|
+
them is exactly how usage counts end up confidently wrong.
|
|
175
|
+
- The graph cache is written atomically (write-then-rename). A crash mid
|
|
176
|
+
write used to leave a corrupt file at the live path, where the matching
|
|
177
|
+
fingerprint made every later session fail the load and silently pay the
|
|
178
|
+
full rebuild, which on a large repo is exactly the MCP client timeout.
|
|
179
|
+
- The declared sqlglot floor was a promise the tests didn't keep: versions
|
|
180
|
+
below 30.14 parse T-SQL temp tables and UPDATE derivations differently,
|
|
181
|
+
producing silently wrong lineage. The floor is now the tested truth
|
|
182
|
+
(`sqlglot>=30.14,<31`), a CI job runs the full suite against the exact
|
|
183
|
+
floor, and a test asserts sqlglot's license stays MIT so a change inside
|
|
184
|
+
the allowed range fails loudly instead of arriving silently.
|
|
185
|
+
- A CTE named after the model that contains it (dbt's `with X as (...)
|
|
186
|
+
select * from X` house style) no longer downgrades every pass-through
|
|
187
|
+
edge to review_required. The CTE shadows only the model's own name, which
|
|
188
|
+
is not ambiguous; on dbt's jaffle-shop this alone was 17 of 18 review
|
|
189
|
+
flags. A CTE shadowing a different model still downgrades.
|
|
190
|
+
- The summary's "why so many review links" line now counts before it blames:
|
|
191
|
+
external tables are only named as the cause with the number of review
|
|
192
|
+
links they actually account for. `ripple ingest-schema` prints whether the
|
|
193
|
+
review count moved, including an honest "still N".
|
|
194
|
+
- `ripple ci` no longer says "nothing downstream" about a removed column
|
|
195
|
+
whose consumers exist: the current graph has already lost the column and
|
|
196
|
+
its edges, so the blast is now computed against the base state.
|
|
197
|
+
- Usage ingest only counts statements that read or write data. GRANT, SHOW,
|
|
198
|
+
DESCRIBE and other administrative statements no longer register as model
|
|
199
|
+
sightings (a GRANT could make a dead model look alive), and `SHOW TABLES
|
|
200
|
+
IN <schema>` no longer fabricates the schema as an outside table.
|
|
201
|
+
`COPY INTO` now counts as a write.
|
|
202
|
+
- `ripple doctor` shell-quotes the config command it prints, so the
|
|
203
|
+
no-binary-on-PATH fallback (`python -c "..."`) survives copy-paste.
|
|
204
|
+
- A collected usage report names its platform and connection instead of the
|
|
205
|
+
temp spool filename.
|
|
206
|
+
|
|
207
|
+
- Window `PARTITION BY` and `ORDER BY` columns are no longer reported as value
|
|
208
|
+
sources. `lag(mrr) over (partition by customer_id order by date_month)` now
|
|
209
|
+
traces to `mrr` alone. The keys are kept as `window` edges into
|
|
210
|
+
`(window key)`, so `breaks` still reports a model whose window partitions on
|
|
211
|
+
a column you renamed. This was the largest miss in the held-out benchmark;
|
|
212
|
+
see `benchmark/HOLDOUT.md` for what that does and does not let us claim.
|
|
213
|
+
- `ripple ci` widened any edit inside a CTE to the whole model: one renamed
|
|
214
|
+
column in a `with source, renamed select *` model reported "internal
|
|
215
|
+
logic changed: 30 columns in 15 downstream models derive differently".
|
|
216
|
+
Inner expressions are now attributed to the output columns whose lineage
|
|
217
|
+
passes through them, so a rename is a remove and an add, and a formula
|
|
218
|
+
change lands on the columns that derive from it. Only an inner expression
|
|
219
|
+
no output column reaches (a flag used just in a WHERE) still widens the
|
|
220
|
+
whole model.
|
|
221
|
+
|
|
222
|
+
### Removed
|
|
223
|
+
- The dark whole-graph canvas that `ripple serve` used to open (`static/index.html`). Every surface now draws one answer.
|
|
224
|
+
|
|
225
|
+
[Unreleased]: https://github.com/bteh/ripple/compare/v0.1.0...HEAD
|
|
226
|
+
[0.1.0]: https://github.com/bteh/ripple/releases/tag/v0.1.0
|
ripple_sql-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright 2026 Brian Teh
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|