feat: end-of-task column impact, an offline report with a column graph, and /lineage-report for any column
RA
10h ago+3552+3552+0123456789012345678901234567890123456789-155-155-012345678901234567890123456789
What changes for users
- What a change reaches, at the end of the task. When Claude finishes a task that changed a dbt model, a summary above the prompt says how far each changed column's value goes downstream. For example:
mrt_query_usage · 39 downstream models · query_cost reaches 24 columns in 11 models (11 end columns) · 28 more may change · ⚠ Lower bound: 6 models could not be read. - An offline report with a column graph. Open report or
/lineage-reportopens one HTML file: what changed, what to expect downstream, who consumes it (dbt exposures), and a column graph, upstream left and downstream right, where a click lights up a column's path. - Any column, without a task.
/lineage-report <model>.<column>opens the same page for any column, leading with where it comes from: the "this number looks wrong" question. It reads the manifest as it is now and never replaces the task's report; an unknown model or column gets a plain error. - Plans. When a plan goes up for approval, one line names the models it touches, with their downstream counts.
Demo
After an edit, the band says "· 28 more may change", and the report groups those 28 models by why each may carry the value: nothing is called unaffected. The second clip is /lineage-report int_role_query_usage.total_cost_30d, which leads with where the value comes from. Click a clip for the MP4.
Recorded on this head (c9cdec1) with AltimateAI/altimate-code#1710 at 0189b55c71, both local builds, in an isolated Claude Code. Clicks in the report go through Chrome's input API at the pointer's spot; what the page does in response is real.
How it works
- At the start of a task the plugin copies
target/manifest.jsonaside. At the end it asksimpact_analysisabout each changed column through the altimate CLI, so the answer describes the SQL as it was before the task. - The page is one file and makes no requests; its CSP allows only its own inline script and style.
- A graph edge carries a label only when the engine says how the value changes (same, renamed, calculated, aggregated). Edges it can't classify are dashed with no label, and a note under the graph says what that means. A click on a column counts even if the pointer drifts up to 12 px; a longer drag pans.
- The graph reads on first view: its key names every mark it draws (exposures, engine notes,
end, the changed column), each count says what it counts, a narrow window opens on the changed column, and Tab moves left to right. The path bar floats over the page, so a click never moves what is under the pointer. - Nothing is reported unaffected. altimate-code lists every downstream model the value isn't traced to as "may carry it", each with a reason, and the report groups them by reason: SQL it couldn't read, an ephemeral model,
*, NATURAL JOIN, a name given at run time, a whole row, a model the value reaches, a model past one of these, or not traced at all. A group with no models isn't shown. - The band is saved with the task, so it comes back after Claude Code's session list (Left arrow at an empty prompt, then back), unless a prompt came since or it was dismissed.
- The per-edit column lookup goes through the CLI instead of MCP, so it raises no permission prompt in accept-edits mode.
- Usage events for the report go through altimate-code's telemetry (
tool report-event): the action and the session id only, off under the telemetry opt-out. See PRIVACY.md.
What it needs
- The altimate-code release containing https://github.com/AltimateAI/altimate-code/pull/1710 (the column answer and its shared column resolver; issue https://github.com/AltimateAI/altimate-code/issues/1709). Release this plugin with it or after it. With an older altimate-code, the summary and report keep the model-level count and say to update it; the pane and the per-edit note keep today's column reach.
- Usage events need altimate-code's
tool report-event, added in https://github.com/AltimateAI/altimate-code/pull/1718 (merged). With an older altimate-code, that call exits 1 and nothing is sent. - The plugin names the release it needs in one place:
MIN_ALTIMATE_CODEinhooks/altimate-cli.ts, shown in the band, the report and the README. It is a placeholder (0.12.11) until the release with https://github.com/AltimateAI/altimate-code/pull/1710 is tagged.
Claims (for review)
- C1. With an altimate-code whose
impact_analysisgives neithercolumn_answernorcolumn_answer_error, the summary and report keep the model-level count and say to update altimate-code, never that the trace failed; nothing throws. - C2. The report page makes no request: its CSP is
default-src 'none'with inline script and style only, and it names no external URL. - C3.
/lineage-reportwith no argument opens the last task's report as before./lineage-report <model>.<column>writescolumn-report.htmland never overwrites the task's report; an unknown model or column, or a malformed argument, gives a plain error and writes nothing. - C4. A graph edge is labelled only for
=,~,ƒandΣ; any other value is dashed and unlabelled. - C5. A press and release on the same column within 12 px selects it once, never twice; a longer drag pans and selects nothing; keyboard activation still selects.
- C6. When the copy at the task's start succeeded, the end-of-task answer reads that copy, not the manifest the task left behind.
- C7. A usage event carries the action and the session id only.
- C8. The per-edit column note never calls MCP; it runs the altimate CLI.
Residuals:
- R1. Usage events need altimate-code's
tool report-event(a separate PR); until then the call exits 1 and nothing is sent. - R2. A level of more than 12 models folds into one box; two levels each way by default.
- R3. A model with about 100 or more downstream models gets a model-level note instead of column detail.
- R4. When that copy failed, the answer reads the current manifest.
- R5. Each session's copies are its own and are removed a day after they were made; a report asked for after that reads the project's current manifest and says so.
Held for Ralph
- Codex round 3 (the cap) on 6947714: two tasks in one session can overlap when a queued prompt starts the next turn before the previous task's end step has moved its manifest copy. If Claude recompiled during the earlier task, that task's answer then reads the later copy. The fix is a per-task part in the copy names; it is held at the round cap, not made.
How it was checked
claude plugin test: 304 pass. They include the graph layout, the unlabelled edges, a click with a few pixels of drift against a drag, and/lineage-report <model>.<column>with an unknown model, an unknown column and a malformed argument.tscandclaude plugin validatepass.- Live at 64d63e4, in an isolated Claude Code 2.1.322 on a 233-model dbt project with altimate-code at the PR above: three runs of edit, summary, report, column click, more levels, Fit and
/lineage-report, with the same numbers each time. Dropping the "proven unaffected" count (d39218c) is checked by the tests above, not in a live run. - In headless Chromium with real pointer events, clicks with 0, 4 and 10 px of drift light the path and 20 px drags pan. The page before the last commit drops the 10 px click.
🤖 Generated with Claude Code