Skip to content

Commit 65d0138

Browse files
anandgupta42claude
andcommitted
docs: add REVIEW.md for KiloCode automated review guidance
Built by mining 113 commits (11 fix commits over 18mo) and ~90 human PR review comments for this repo's real bug classes: pydantic extra="forbid" crashes on new dbt manifest fields, required fields breaking on disabled/ unit-test nodes, version-specific field access on vendored dbt-artifacts- parser types, checksum shape assumptions, mutable default arguments, and API-client failures not surfaced to the CLI user. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent e347ea4 commit 65d0138

1 file changed

Lines changed: 150 additions & 0 deletions

File tree

REVIEW.md

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
# REVIEW.md — datapilot-cli
2+
3+
Python CLI (`altimate-datapilot-cli` on PyPI) that parses dbt `manifest.json`/`catalog.json`
4+
artifacts across dbt manifest schema versions (vendored `dbt-artifacts-parser`, v1–v12) and runs
5+
data-quality "insight" checks over dbt projects, usable standalone or as a pre-commit hook.
6+
7+
## Goal: catch what CI cannot
8+
9+
CI runs formatting/linting/tests. You exist for correctness bugs in dbt-manifest parsing across
10+
schema versions, node-shape assumptions, and API/CLI behavior that no linter can see. If a diff is
11+
clean and doesn't touch any of the areas below, say `lgtm` — don't invent nits.
12+
13+
## Don't duplicate CI — never flag
14+
15+
This repo's `pre-commit` + `tox` pipeline (ruff, black) already enforces, so never re-flag:
16+
- Formatting (black) or import ordering (`ruff` rule `I`/isort).
17+
- Unused imports/variables, bare `except`, raise-from style (`RUF`, `RSE`), bugbear patterns (`B`),
18+
comprehension style (`C4`), pathlib usage (`PTH`), quote style (`Q`), pytest style (`PT`).
19+
- Naive `datetime` construction — ruff's `DTZ` rule already catches this.
20+
- Basic `flake8-bandit` (`S`) security smells (e.g. `assert` in non-test code, `eval`/`exec`).
21+
- Line length (140 cols, ruff-enforced).
22+
- Whether tests exist/pass — `tox` runs the suite; that's CI's job, not yours.
23+
Pre-existing issues in code the diff doesn't touch are out of scope — only review changed lines
24+
and their direct call sites.
25+
26+
## Evidence standard — no speculation
27+
28+
Before flagging a manifest/catalog-parsing issue, trace which manifest version(s) the changed code
29+
actually runs against and check the vendored schema in
30+
`src/vendor/dbt_artifacts_parser/parsers/manifest/manifest_vN.py` for that version — don't assume
31+
a field exists just because it's on `ManifestV12`. For CLI/API-client changes, trace whether the
32+
call site actually surfaces failures to the user before claiming error handling is missing.
33+
34+
## Severity calibration
35+
36+
- **Critical**: will raise/crash on a real dbt manifest version this tool claims to support (e.g.
37+
missing field, wrong required-ness, wrong checksum shape), or silently drops/misreports an
38+
insight-check result.
39+
- **Warning**: works today but is version-fragile, dialect-fragile, or duplicates logic that will
40+
drift (see focus areas below); or an API failure that's swallowed instead of surfaced to the CLI
41+
user.
42+
- **Nit**: naming, magic strings that have an existing constant, minor duplication.
43+
44+
## Focus areas — bug classes this repo actually ships
45+
46+
1. **Pydantic `extra="forbid"` on manifest/catalog wrapper models** (root cause of #92, #48, and
47+
related fixes). New dbt manifest versions add fields the `Altimate*` wrapper models don't know
48+
about yet; a wrapper `BaseModel` with `extra="forbid"` raises `ValidationError` and crashes
49+
parsing the moment dbt ships a minor version bump. Flag any new/edited model in
50+
`schemas/manifest.py`-style files whose `model_config` sets `extra="forbid"` (or omits it while
51+
inheriting a forbidding base) instead of `extra="allow"`.
52+
53+
2. **Fields marked required on node wrapper models** (#98, #95, #96`identifier`,
54+
`resource_type` made optional after crashing on `Disabled*`/unit-test nodes). A field required
55+
on the "normal" node case often isn't populated on disabled nodes, unit-test nodes, or older
56+
manifest versions. Flag any new required (non-`Optional`) field added to an `Altimate*` node
57+
model; verify it's actually present across disabled/unit-test/seed/source variants, not just
58+
the common model case.
59+
60+
3. **Version-specific field access without a version guard** (e.g. `deferred` not present on v12
61+
`RPCNode`; behavior tied to a specific dbt/artifact-parser version). Flag direct attribute
62+
access on a vendored `manifest_vN` type inside code paths meant to be version-agnostic; verify
63+
against the specific `manifest_vN.py` the code targets.
64+
65+
4. **Checksum/hash shape assumptions** (#94`checksum` is a dict on some node types, a
66+
string/object on others). Flag `.checksum.name` / `.checksum.checksum`-style access without
67+
confirming that node type has that structure in that manifest version.
68+
69+
5. **Catalog/manifest lookups assumed non-`None`** ("catalog nodes can be `None`", catalog can
70+
have fewer columns than the manifest). `--catalog-path` is optional per the CLI, and
71+
`dbt docs generate` output can be stale relative to the manifest. Flag catalog/manifest
72+
`.nodes[x]` / `.columns[y]` access without a `None`/`KeyError` guard in insight-check code.
73+
74+
6. **Mutable default arguments** (explicitly flagged in review — "list is mutable, weird things can
75+
happen" — not reliably caught by this repo's ruff config). Flag `def f(x=[])`, `def f(x={})`, or
76+
a list/dict class attribute mutated in place across calls instead of copied per-instance.
77+
78+
7. **Hardcoded materialization/dialect assumptions in insight checks** ("dialects may have
79+
multiple materializations and it might flag something we don't want to flag", "use the VIEW
80+
constant, we have it somewhere"). Flag magic materialization strings (`"view"`, `"table"`,
81+
`"incremental"`) instead of the shared constant, and any check that assumes one dialect's
82+
materialization vocabulary applies to all configured warehouses.
83+
84+
8. **Duplicated logic across check/insight classes** ("put this in the base `DBTCheck` class so
85+
it's not repeated", "make it a reusable util"). Flag near-identical blocks copy-pasted across
86+
files under the insights/checks package instead of factored onto a shared base class or util.
87+
88+
9. **API-client failures not surfaced to the CLI user** ("should handle exceptions and not-200s
89+
nicely", "get the response code and log it", "return the status so we inform the user what
90+
happened"). The API client intentionally doesn't raise on non-2xx (see Known-intentional) — the
91+
bug class is the CLI layer silently swallowing that failure instead of reporting it to the
92+
user. Flag call sites that ignore or only debug-log a non-2xx/error API response.
93+
94+
10. **Hardcoded environment-specific defaults** ("needs to be injectable from the CLI/environment,
95+
not hardcoded"). Flag new hardcoded endpoint/host/instance defaults added to config or client
96+
code that aren't also exposed as a CLI flag or env var override.
97+
98+
11. **Enum/resource-type completeness across manifest versions** ("how is it mapping between the
99+
enums", "not sure all types have labels"). `AltimateResourceType`/`AltimateAccess`-style enums
100+
must track new dbt node/resource types introduced by an artifact-parser version bump. Flag a
101+
vendored-parser bump or new node type handled without a corresponding Altimate enum member.
102+
103+
12. **Release/version-bump workflow trigger conditions** (`.bumpversion.cfg`, `bump-version.yml`,
104+
`tag-release.yml` — "won't this always release a tag? should be manual"). Flag changes to
105+
release-workflow `on:` triggers; verify a routine merge to main can't unintentionally fire a
106+
tag/release.
107+
108+
## Repo invariants & landmines
109+
110+
- `Manifest = Union[ManifestV1, ..., ManifestV12]` (`schemas/manifest.py`) — code that pattern-
111+
matches or accesses fields without checking manifest version breaks on the boundary versions.
112+
- `src/vendor/dbt_artifacts_parser` is a deliberately vendored/forked copy (see fix history: "use
113+
vendored dbt-artifacts-parser", "use forked dbt-artifacts-parser") — it exists because upstream
114+
didn't support something needed here; don't "clean up" divergence from upstream as if it were
115+
drift.
116+
- Two distinct run modes with different data completeness: a full CLI run (`datapilot dbt
117+
project-health`) vs. the pre-commit hook, which partially generates manifest/catalog from only
118+
changed files. Manifest-loading changes need to work under both.
119+
- Minimum supported Python is 3.7 per README — avoid relying on syntax/stdlib features unavailable
120+
there unless the actual tox/CI matrix has been checked.
121+
- `--catalog-path` is optional; code paths must degrade gracefully (skip catalog-dependent
122+
insights) rather than assume catalog data is present.
123+
124+
## Known-intentional — don't flag
125+
126+
- `APIClient.get()` not raising on non-2xx responses — deliberate design ("safer as it passes
127+
things through," per review). Only flag if the *caller* drops the failure silently (focus area
128+
9), not the client's non-raising behavior itself.
129+
- `print()` used for CLI-facing output — confirmed intentional ("this is the intended output, not
130+
really logging"). Don't suggest converting these to `logger` calls.
131+
- The manifest wrapper not modeling unit-test nodes in certain paths — confirmed as not a real gap
132+
where raised ("there's no unit_test/UnitTest reference in the wrapper code" — deliberate scope).
133+
134+
## High-blast-radius — never auto-edit
135+
136+
- `src/vendor/dbt_artifacts_parser/**` — vendored upstream parser; changes must be deliberate forks
137+
with a stated reason, not incidental "fixes."
138+
- `.bumpversion.cfg`, `bump-version.yml`, `tag-release.yml`, `release.sh`, `setup.py` — PyPI release
139+
plumbing for `altimate-datapilot-cli`.
140+
- `pyproject.toml`, `tox.ini`, `.pre-commit-config.yaml` — changing these changes what's enforced
141+
repo-wide, not just this PR.
142+
- `CHANGELOG.rst`, `AUTHORS.rst` — maintained by release tooling, not hand-edited in feature PRs.
143+
144+
## Comment style
145+
146+
- Point at exact lines; one finding per comment; most-severe first.
147+
- Phrase as suggestions the author can accept or reject, not mandates — link a `diff` snippet if a
148+
concrete fix is short.
149+
- Skip deep review of trivial diffs (README/CHANGELOG-only, formatting-only, dependency bumps with
150+
no vendored-parser impact).

0 commit comments

Comments
 (0)