delta-engine 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.
Files changed (127) hide show
  1. delta_engine-0.1.0/.claude/CLAUDE.md +214 -0
  2. delta_engine-0.1.0/.claude/agents/ousterhout-design-reviewer.md +106 -0
  3. delta_engine-0.1.0/.claude/agents/test-reviewer.md +163 -0
  4. delta_engine-0.1.0/.claude/rules/coding-style.md +85 -0
  5. delta_engine-0.1.0/.claude/rules/documentation.md +100 -0
  6. delta_engine-0.1.0/.claude/rules/domain-architecture.md +112 -0
  7. delta_engine-0.1.0/.claude/rules/testing.md +84 -0
  8. delta_engine-0.1.0/.claude/skills/add-tests/SKILL.md +350 -0
  9. delta_engine-0.1.0/.github/pull_request_template.md +22 -0
  10. delta_engine-0.1.0/.github/workflows/ci.yaml +135 -0
  11. delta_engine-0.1.0/.github/workflows/pr-title.yaml +17 -0
  12. delta_engine-0.1.0/.github/workflows/publish-testpypi.yaml +33 -0
  13. delta_engine-0.1.0/.github/workflows/release.yaml +72 -0
  14. delta_engine-0.1.0/.gitignore +48 -0
  15. delta_engine-0.1.0/.pre-commit-config.yaml +6 -0
  16. delta_engine-0.1.0/CHANGELOG.md +354 -0
  17. delta_engine-0.1.0/LICENSE +21 -0
  18. delta_engine-0.1.0/PKG-INFO +75 -0
  19. delta_engine-0.1.0/README.md +49 -0
  20. delta_engine-0.1.0/body.md +354 -0
  21. delta_engine-0.1.0/docs/conf.py +30 -0
  22. delta_engine-0.1.0/docs/explanation-architecture.md +583 -0
  23. delta_engine-0.1.0/docs/how-to-add-action-type.md +136 -0
  24. delta_engine-0.1.0/docs/how-to-configure-properties.md +149 -0
  25. delta_engine-0.1.0/docs/how-to-configure-tags.md +87 -0
  26. delta_engine-0.1.0/docs/how-to-declare-foreign-keys.md +142 -0
  27. delta_engine-0.1.0/docs/how-to-declare-primary-keys.md +67 -0
  28. delta_engine-0.1.0/docs/how-to-deploy-metadata-only.md +57 -0
  29. delta_engine-0.1.0/docs/how-to-handle-sync-failures.md +117 -0
  30. delta_engine-0.1.0/docs/how-to-implement-adapter.md +102 -0
  31. delta_engine-0.1.0/docs/index.rst +37 -0
  32. delta_engine-0.1.0/docs/reference-api.md +115 -0
  33. delta_engine-0.1.0/docs/reference-data-types.md +41 -0
  34. delta_engine-0.1.0/docs/reference-safe-change-rules.md +48 -0
  35. delta_engine-0.1.0/docs/todo/todo.md +41 -0
  36. delta_engine-0.1.0/docs/tutorial-getting-started.md +93 -0
  37. delta_engine-0.1.0/notebooks/README.md +34 -0
  38. delta_engine-0.1.0/notebooks/catalog_inspector.py +109 -0
  39. delta_engine-0.1.0/notebooks/delta_engine_walkthrough.py +1049 -0
  40. delta_engine-0.1.0/pyproject.toml +225 -0
  41. delta_engine-0.1.0/src/delta_engine/__init__.py +41 -0
  42. delta_engine-0.1.0/src/delta_engine/adapters/__init__.py +0 -0
  43. delta_engine-0.1.0/src/delta_engine/adapters/databricks/__init__.py +8 -0
  44. delta_engine-0.1.0/src/delta_engine/adapters/databricks/errors.py +53 -0
  45. delta_engine-0.1.0/src/delta_engine/adapters/databricks/executor.py +120 -0
  46. delta_engine-0.1.0/src/delta_engine/adapters/databricks/factory.py +21 -0
  47. delta_engine-0.1.0/src/delta_engine/adapters/databricks/log_config.py +87 -0
  48. delta_engine-0.1.0/src/delta_engine/adapters/databricks/reader.py +364 -0
  49. delta_engine-0.1.0/src/delta_engine/adapters/databricks/sql/__init__.py +46 -0
  50. delta_engine-0.1.0/src/delta_engine/adapters/databricks/sql/compile.py +269 -0
  51. delta_engine-0.1.0/src/delta_engine/adapters/databricks/sql/dialect.py +18 -0
  52. delta_engine-0.1.0/src/delta_engine/adapters/databricks/sql/queries.py +167 -0
  53. delta_engine-0.1.0/src/delta_engine/adapters/databricks/sql/types.py +185 -0
  54. delta_engine-0.1.0/src/delta_engine/api/__init__.py +7 -0
  55. delta_engine-0.1.0/src/delta_engine/api/delta_table.py +349 -0
  56. delta_engine-0.1.0/src/delta_engine/application/__init__.py +23 -0
  57. delta_engine-0.1.0/src/delta_engine/application/dependency_resolution.py +339 -0
  58. delta_engine-0.1.0/src/delta_engine/application/desired_tables.py +50 -0
  59. delta_engine-0.1.0/src/delta_engine/application/engine.py +305 -0
  60. delta_engine-0.1.0/src/delta_engine/application/errors.py +37 -0
  61. delta_engine-0.1.0/src/delta_engine/application/failures.py +138 -0
  62. delta_engine-0.1.0/src/delta_engine/application/ports.py +151 -0
  63. delta_engine-0.1.0/src/delta_engine/application/properties.py +161 -0
  64. delta_engine-0.1.0/src/delta_engine/application/rendering.py +352 -0
  65. delta_engine-0.1.0/src/delta_engine/application/report.py +93 -0
  66. delta_engine-0.1.0/src/delta_engine/application/validation.py +450 -0
  67. delta_engine-0.1.0/src/delta_engine/databricks.py +35 -0
  68. delta_engine-0.1.0/src/delta_engine/domain/__init__.py +0 -0
  69. delta_engine-0.1.0/src/delta_engine/domain/model/__init__.py +62 -0
  70. delta_engine-0.1.0/src/delta_engine/domain/model/column.py +41 -0
  71. delta_engine-0.1.0/src/delta_engine/domain/model/constraints.py +133 -0
  72. delta_engine-0.1.0/src/delta_engine/domain/model/data_type.py +156 -0
  73. delta_engine-0.1.0/src/delta_engine/domain/model/qualified_name.py +52 -0
  74. delta_engine-0.1.0/src/delta_engine/domain/model/table.py +195 -0
  75. delta_engine-0.1.0/src/delta_engine/domain/model/table_aspect.py +26 -0
  76. delta_engine-0.1.0/src/delta_engine/domain/plan/__init__.py +93 -0
  77. delta_engine-0.1.0/src/delta_engine/domain/plan/actions.py +337 -0
  78. delta_engine-0.1.0/src/delta_engine/domain/plan/diff.py +769 -0
  79. delta_engine-0.1.0/src/delta_engine/py.typed +0 -0
  80. delta_engine-0.1.0/src/delta_engine/schema.py +56 -0
  81. delta_engine-0.1.0/tests/__init__.py +0 -0
  82. delta_engine-0.1.0/tests/adapters/databricks/__init__.py +0 -0
  83. delta_engine-0.1.0/tests/adapters/databricks/sql/__init__.py +0 -0
  84. delta_engine-0.1.0/tests/adapters/databricks/sql/test_compile.py +464 -0
  85. delta_engine-0.1.0/tests/adapters/databricks/sql/test_dialect.py +66 -0
  86. delta_engine-0.1.0/tests/adapters/databricks/sql/test_queries.py +120 -0
  87. delta_engine-0.1.0/tests/adapters/databricks/sql/test_types.py +163 -0
  88. delta_engine-0.1.0/tests/adapters/databricks/test_build_engine.py +72 -0
  89. delta_engine-0.1.0/tests/adapters/databricks/test_errors.py +38 -0
  90. delta_engine-0.1.0/tests/adapters/databricks/test_executor.py +369 -0
  91. delta_engine-0.1.0/tests/adapters/databricks/test_reader.py +517 -0
  92. delta_engine-0.1.0/tests/adapters/databricks/test_reader_mappers.py +266 -0
  93. delta_engine-0.1.0/tests/api/__init__.py +0 -0
  94. delta_engine-0.1.0/tests/api/test_delta_table.py +682 -0
  95. delta_engine-0.1.0/tests/api/test_foreign_key.py +270 -0
  96. delta_engine-0.1.0/tests/application/__init__.py +0 -0
  97. delta_engine-0.1.0/tests/application/test_dependency_resolution.py +894 -0
  98. delta_engine-0.1.0/tests/application/test_desired_tables.py +108 -0
  99. delta_engine-0.1.0/tests/application/test_engine.py +1207 -0
  100. delta_engine-0.1.0/tests/application/test_errors.py +166 -0
  101. delta_engine-0.1.0/tests/application/test_failures.py +146 -0
  102. delta_engine-0.1.0/tests/application/test_ports.py +171 -0
  103. delta_engine-0.1.0/tests/application/test_properties.py +106 -0
  104. delta_engine-0.1.0/tests/application/test_public_api.py +46 -0
  105. delta_engine-0.1.0/tests/application/test_rendering.py +599 -0
  106. delta_engine-0.1.0/tests/application/test_report.py +271 -0
  107. delta_engine-0.1.0/tests/application/test_validation.py +738 -0
  108. delta_engine-0.1.0/tests/config.py +4 -0
  109. delta_engine-0.1.0/tests/conftest.py +101 -0
  110. delta_engine-0.1.0/tests/domain/__init__.py +0 -0
  111. delta_engine-0.1.0/tests/domain/model/__init__.py +0 -0
  112. delta_engine-0.1.0/tests/domain/model/test_column.py +59 -0
  113. delta_engine-0.1.0/tests/domain/model/test_data_type.py +68 -0
  114. delta_engine-0.1.0/tests/domain/model/test_foreign_key.py +119 -0
  115. delta_engine-0.1.0/tests/domain/model/test_primary_key.py +39 -0
  116. delta_engine-0.1.0/tests/domain/model/test_qualified_name.py +68 -0
  117. delta_engine-0.1.0/tests/domain/model/test_table.py +516 -0
  118. delta_engine-0.1.0/tests/domain/model/test_table_aspect.py +32 -0
  119. delta_engine-0.1.0/tests/domain/plan/__init__.py +0 -0
  120. delta_engine-0.1.0/tests/domain/plan/test_actions.py +279 -0
  121. delta_engine-0.1.0/tests/domain/plan/test_diff.py +889 -0
  122. delta_engine-0.1.0/tests/e2e/__init__.py +0 -0
  123. delta_engine-0.1.0/tests/e2e/conftest.py +12 -0
  124. delta_engine-0.1.0/tests/e2e/test_engine_e2e.py +500 -0
  125. delta_engine-0.1.0/tests/test_public_api.py +72 -0
  126. delta_engine-0.1.0/tests/test_public_imports.py +159 -0
  127. delta_engine-0.1.0/uv.lock +1388 -0
@@ -0,0 +1,214 @@
1
+ # delta-engine
2
+
3
+ Declarative schema management for Delta Lake tables on Databricks.
4
+
5
+ Users declare desired table state with `DeltaTable`. The engine reads the current catalog state, computes typed drift, validates whether that drift is safe, plans deterministic DDL actions, resolves foreign-key dependencies, and executes through a backend adapter.
6
+
7
+ This file contains project-level instructions that should be available in every Claude Code session. File-specific rules live in `.claude/rules/`. Multi-step workflows belong in `.claude/skills/`.
8
+
9
+ ## Project shape
10
+
11
+ Main source layout:
12
+
13
+ * `src/delta_engine/domain`: backend-free domain model, table snapshots, diffs, actions, and deterministic plans.
14
+ * `src/delta_engine/application`: orchestration, ports, validation, dependency resolution, reporting, and errors.
15
+ * `src/delta_engine/adapters`: backend integration. The current adapter is Databricks/Spark.
16
+ * `src/delta_engine/api`: implementation of public declaration objects.
17
+ * `src/delta_engine/schema.py`: public schema declaration import surface.
18
+ * `src/delta_engine/databricks.py`: public Databricks helper import surface with lazy adapter imports.
19
+ * `tests`: unit, integration, adapter, and end-to-end tests.
20
+ * `docs`: Sphinx/MyST documentation.
21
+
22
+ The library should remain importable for schema declaration and planning without requiring PySpark unless the Databricks adapter is actually used.
23
+
24
+ ## Commands
25
+
26
+ Set up development dependencies:
27
+
28
+ ```bash
29
+ uv sync --group dev
30
+ ```
31
+
32
+ Run tests:
33
+
34
+ ```bash
35
+ uv run pytest
36
+ uv run pytest tests/domain/plan/test_diff.py
37
+ uv run pytest tests/domain/plan/test_diff.py::test_name
38
+ uv run pytest -m "not local_e2e"
39
+ ```
40
+
41
+ The first test run starts a local Spark session through the `spark` fixture in `tests/conftest.py` and can take 30-60 seconds.
42
+
43
+ Coverage is enabled by default and must stay above the configured threshold.
44
+
45
+ Lint, format, and type-check:
46
+
47
+ ```bash
48
+ uv run ruff check src tests
49
+ uv run ruff format src tests
50
+ uv run mypy src
51
+ ```
52
+
53
+ Check import architecture:
54
+
55
+ ```bash
56
+ uv run lint-imports
57
+ ```
58
+
59
+ Build docs:
60
+
61
+ ```bash
62
+ uv sync --group docs
63
+ uv run --group docs sphinx-build -b html docs docs/_build/html -W
64
+ ```
65
+
66
+ ## Validation workflow
67
+
68
+ Prefer the narrowest useful check while iterating, then broaden before finishing.
69
+
70
+ For ordinary code changes, start with the relevant focused test:
71
+
72
+ ```bash
73
+ uv run pytest tests/path/to/test_file.py::test_name
74
+ ```
75
+
76
+ Then broaden as needed:
77
+
78
+ ```bash
79
+ uv run pytest tests/path/to/test_file.py
80
+ uv run pytest
81
+ ```
82
+
83
+ Before opening a PR, run:
84
+
85
+ ```bash
86
+ uv run pytest
87
+ uv run ruff check src tests
88
+ uv run mypy src
89
+ uv run lint-imports
90
+ uv run --group docs sphinx-build -b html docs docs/_build/html -W
91
+ ```
92
+
93
+ Do not run credentialed Databricks tests unless explicitly asked.
94
+
95
+ ## Architecture boundaries
96
+
97
+ This project uses hexagonal architecture: domain core, application use cases, backend adapters, and public API declarations.
98
+
99
+ The import architecture is enforced by `import-linter` in `pyproject.toml`. Do not rely on convention alone.
100
+
101
+ Rules:
102
+
103
+ * `domain` must stay backend-free, immutable, and deterministic.
104
+ * `application` owns orchestration, ports, safety policy, dependency resolution, reports, and failure propagation.
105
+ * `adapters` own backend integration, SQL compilation, Spark/Databricks parsing, identifier quoting, and backend exception translation.
106
+ * `api` owns public declaration implementation and lowers declarations into domain snapshots.
107
+ * `schema.py` and `databricks.py` are user-facing facades; keep them thin.
108
+ * PySpark, Delta, Py4J, Spark SQL details, and Databricks-specific assumptions must not leak into `domain` or `application`.
109
+
110
+ Expected dependency direction:
111
+
112
+ ```text
113
+ databricks | schema | adapters | api -> application -> domain
114
+ ```
115
+
116
+ A separate import-linter contract forbids `delta` and `pyspark` imports from `schema`, `api`, `application`, and `domain`.
117
+
118
+ ## Sync lifecycle
119
+
120
+ `Engine.sync(...)` prepares desired tables, then runs a phase chain over per-table run state.
121
+
122
+ Core phases:
123
+
124
+ 1. Prepare desired declarations.
125
+ 2. Read current catalog state through `CatalogStateReader`.
126
+ 3. Diff desired vs observed state with `diff_table`.
127
+ 4. Validate drift with `validate_diff`.
128
+ 5. Plan actions from validated diff.
129
+ 6. Resolve foreign-key dependency order.
130
+ 7. Execute through `PlanExecutor`, unless this is a dry run.
131
+ 8. Return `SyncReport`, or raise `SyncFailedError` with the report on a real failed run.
132
+
133
+ A table that fails an early phase keeps that failure in its report and is skipped by later mutating phases. The engine should still process other tables and return a complete run report.
134
+
135
+ Both application ports are total from the engine's perspective:
136
+
137
+ * `CatalogStateReader.fetch_state(...)` returns `TablePresent`, `TableAbsent`, or `ReadFailed`.
138
+ * `PlanExecutor.execute(...)` returns `ExecutionSummary`.
139
+
140
+ Adapters should catch backend exceptions and convert them into typed failures rather than raising backend-specific exceptions through the port.
141
+
142
+ ## Planning and validation invariants
143
+
144
+ Diffs state facts. Validation decides safety.
145
+
146
+ Do not make diff code decide whether a change is safe.
147
+
148
+ Do not make adapter code decide whether a domain change is safe.
149
+
150
+ `ActionPlan` owns action ordering. Do not sort actions manually elsewhere.
151
+
152
+ Action ordering is defined by `ActionPhase` and then by action `subject`.
153
+
154
+ Properties and tags intentionally use different semantics:
155
+
156
+ * Properties use exact-declaration semantics. A declared `None` asserts absence and plans an unset when present.
157
+ * Tags use full-state semantics. An observed-only tag is drift and should be unset when tags are managed.
158
+
159
+ `managed_aspects` is part of the safety model. Drift outside the aspects managed by a declaration should fail validation rather than be silently reconciled.
160
+
161
+ Constraint names are generated at the API-to-domain lowering boundary and then carried as data. The differ and SQL compiler should read constraint names, not re-derive them.
162
+
163
+ Primary-key and foreign-key identity is structural, not name-based.
164
+
165
+ Foreign-key declarations reference the target `DeltaTable` object, or `Self`, rather than a dotted table name. This lets the API infer referenced columns from the target table's primary key.
166
+
167
+ ## Where to make common changes
168
+
169
+ | Change | Main location |
170
+ | ------------------------------------ | ---------------------------------------------------------------------------------------- |
171
+ | New backend | `src/delta_engine/adapters`: implement `CatalogStateReader` and `PlanExecutor` |
172
+ | New change/diff type | `src/delta_engine/domain/plan/diff.py` |
173
+ | New action type | `src/delta_engine/domain/plan/actions.py` and `src/delta_engine/adapters/databricks/sql` |
174
+ | New safety rule | `src/delta_engine/application/validation.py` |
175
+ | New data type | `src/delta_engine/domain/model/data_type.py` and Databricks type mapping |
176
+ | Public declaration change | `src/delta_engine/api`, surfaced through `src/delta_engine/schema.py` |
177
+ | Foreign-key ordering/blocking policy | `src/delta_engine/application/dependency_resolution.py` |
178
+ | Report/output formatting | `src/delta_engine/application/report.py` and `src/delta_engine/application/rendering.py` |
179
+ | Databricks SQL generation | `src/delta_engine/adapters/databricks/sql` |
180
+ | Public Databricks helper | `src/delta_engine/databricks.py` |
181
+ | Documentation | `docs` |
182
+
183
+ Before changing architecture, action planning, validation, adapters, or public behaviour, read the relevant docs instead of guessing:
184
+
185
+ * `docs/explanation-architecture.md`
186
+ * `docs/how-to-implement-adapter.md`
187
+ * `docs/how-to-add-action-type.md`
188
+ * `docs/reference-safe-change-rules.md`
189
+ * `docs/how-to-handle-sync-failures.md`
190
+ * `docs/how-to-deploy-metadata-only.md`
191
+
192
+ ## Documentation
193
+
194
+ When changing public behaviour, architecture, validation policy, action types, adapter behaviour, or failure semantics, consider whether docs need updating.
195
+
196
+ Do not duplicate detailed docs in this file. Link to the relevant doc and keep this file as the high-signal project map.
197
+
198
+ ## Working rules
199
+
200
+ Start by reading nearby code and tests.
201
+
202
+ Preserve architecture boundaries. Do not solve adapter problems by leaking backend concepts inward.
203
+
204
+ Prefer focused changes. Avoid opportunistic refactors unless they directly support the task.
205
+
206
+ When changing behaviour, add or update tests at the right level.
207
+
208
+ When a change touches domain, application, adapter, and docs, keep the conceptual flow consistent across all of them.
209
+
210
+ Before finishing, state:
211
+
212
+ * what changed
213
+ * what checks were run
214
+ * what risks or follow-ups remain
@@ -0,0 +1,106 @@
1
+ ---
2
+ name: ousterhout-design-reviewer
3
+ description: Use proactively to review Python code, diffs, refactors, public APIs, domain model changes, and module boundaries for Ousterhout-style design quality. Focus on deep modules, information hiding, semantic clarity, special cases, complexity, abstraction boundaries, and maintainability. Use after implementation and before committing architecture-sensitive changes.
4
+ tools: Read, Grep, Glob, Bash
5
+ model: inherit
6
+ permissionMode: default
7
+ effort: high
8
+ skills:
9
+ - ousterhout-review
10
+ color: purple
11
+ ---
12
+
13
+ # Ousterhout Design Reviewer
14
+
15
+ You are a senior Python design reviewer specialising in John Ousterhout's _A Philosophy of Software Design_.
16
+
17
+ The `ousterhout-review` skill loaded above is your core review lens: complexity, shallow modules, leaky information hiding, special cases, bad decomposition, weak public APIs, and test design as a design signal — plus its output format (Verdict, Serious Issues, Minor Issues, What Is Good, Suggested Redesign) and review rules. Apply it in full on every review. This document adds only what the skill does not already cover: your tool policy, two extra review dimensions, and the output-format additions specific to this agent.
18
+
19
+ Your job is to review code for design quality, not formatting, style preferences, or generic best practices. You are a critic, not an implementer.
20
+
21
+ Do not edit files. Do not write files. Do not modify code. Review only.
22
+
23
+ ## What to Inspect
24
+
25
+ When invoked, inspect the relevant code or diff before reviewing.
26
+
27
+ Prefer these read-only commands when useful:
28
+
29
+ ```bash
30
+ git status --short
31
+ git diff --stat
32
+ git diff
33
+ git diff --cached
34
+ ```
35
+
36
+ Use `Read`, `Grep`, and `Glob` to inspect nearby files and existing patterns.
37
+
38
+ Do not run mutating commands.
39
+
40
+ Do not run formatters.
41
+
42
+ Do not run tests unless explicitly asked.
43
+
44
+ Do not use `git add`, `git commit`, `git checkout`, `git reset`, `git clean`, or any command that changes repository state.
45
+
46
+ ## Beyond the Skill: Semantics
47
+
48
+ The skill's priorities cover mechanical design quality. Also review whether the code preserves the real _meaning_ of the domain — a design can be mechanically correct and still be semantically wrong.
49
+
50
+ Names, types, methods, and module boundaries should reflect the concepts users and maintainers actually reason about.
51
+
52
+ Flag:
53
+
54
+ - Names that are technically accurate but conceptually misleading.
55
+ - Domain terms used to mean implementation details.
56
+ - Infrastructure terms used where domain terms belong.
57
+ - Concepts that have been split even though they represent one domain idea.
58
+ - Concepts that have been merged even though they mean different things.
59
+ - Boolean flags or enum values that hide distinct semantic cases.
60
+ - Public APIs whose names describe how something works instead of what it means.
61
+ - Tests that assert behaviour using implementation vocabulary rather than domain vocabulary.
62
+ - Code where a reader must translate between the name and the real concept.
63
+
64
+ Ask:
65
+
66
+ - Does this name mean what it says?
67
+ - Is this one concept or two?
68
+ - Is this distinction real in the domain, or only real in the implementation?
69
+ - Would a caller understand the behaviour from the public API alone?
70
+ - Has the design preserved the meaning of the concept, or merely made the code pass?
71
+
72
+ Semantic drift is a design smell. If the words are wrong, the abstraction is usually wrong too.
73
+
74
+ ## Beyond the Skill: This Repo's Boundaries
75
+
76
+ The skill's "bad decomposition" check is generic. For this repo specifically, pay particular attention to boundaries between:
77
+
78
+ - Public API declarations.
79
+ - Domain model.
80
+ - Application orchestration.
81
+ - Databricks / Spark / Delta adapters.
82
+ - SQL generation or execution.
83
+ - Test helpers and production code.
84
+
85
+ Flag:
86
+
87
+ - Domain code knowing about Databricks, Spark, SQL, or Delta implementation details.
88
+ - Adapters making domain decisions.
89
+ - Application services becoming bags of procedural orchestration.
90
+ - Public API types shaped around infrastructure rather than user intent.
91
+ - Semantic concepts duplicated across layers with slightly different meanings.
92
+
93
+ ## Output Format
94
+
95
+ Use the skill's output structure (Verdict, Serious Issues, Minor Issues, What Is Good, Suggested Redesign). Additionally:
96
+
97
+ - In each Serious or Minor issue, include a file path and line number when available.
98
+ - Fold semantic-clarity and repo-boundary findings from the two sections above into Serious Issues or Minor Issues by severity — they are not a separate report section.
99
+ - End with a **Follow-Up Questions** section: ask only questions that materially affect the design decision. Do not ask vague questions.
100
+
101
+ ## Additional Review Rules
102
+
103
+ Follow the skill's review rules. Additionally:
104
+
105
+ - Treat semantic clarity as a core design property, not a naming nitpick.
106
+ - If there is not enough context, inspect more code before judging.
@@ -0,0 +1,163 @@
1
+ ---
2
+ name: test-reviewer
3
+ description: Use proactively to review Python tests, test diffs, fixtures, mocks, regression coverage, and verification strategy for classical, black-box unit testing quality. Focus on behaviour-first tests, real objects over mocks, given/when/then readability, coupling to implementation details, semantic clarity, and whether test difficulty reveals design problems. Use after tests are added or changed and before committing test-sensitive changes.
4
+ tools: Read, Grep, Glob, Bash
5
+ model: inherit
6
+ permissionMode: default
7
+ effort: high
8
+ skills:
9
+ - add-tests
10
+ color: yellow
11
+ ---
12
+
13
+ # Test Reviewer
14
+
15
+ You are a senior Python test reviewer specialising in classical unit testing.
16
+
17
+ The `add-tests` skill loaded above defines the core testing philosophy (black-box, behaviour-focused, real objects over mocks, given/when/then structure) and workflow (test naming, test levels, setup, regression tests, edge cases) — apply it as your review lens. This document adds only what the skill does not already cover: your tool policy, two extra review dimensions, and the review-verdict output format, since the skill is written for _writing_ tests, not delivering a standalone critique.
18
+
19
+ Your job is to review tests for behavioural value, maintainability, semantic clarity, and design feedback. You are a critic, not an implementer.
20
+
21
+ Do not edit files. Do not write files. Do not modify code. Review only.
22
+
23
+ ## What to Inspect
24
+
25
+ When invoked, inspect the relevant tests, production code, and current diff before reviewing.
26
+
27
+ Prefer these read-only commands when useful:
28
+
29
+ ```bash
30
+ git status --short
31
+ git diff --stat
32
+ git diff
33
+ git diff --cached
34
+ ```
35
+
36
+ Use `Read`, `Grep`, and `Glob` to inspect nearby tests, fixtures, builders, and the production code being tested.
37
+
38
+ You may run the specific test file(s) under review to confirm they actually pass — for example `uv run pytest path/to/test_file.py` or a single `::test_name`. This is the one exception to the read-only stance: use it to verify a regression test truly fails on the old behaviour and passes on the fix, not to explore unrelated parts of the suite.
39
+
40
+ Do not run the full test suite or unrelated test files unless explicitly asked.
41
+
42
+ Do not run mutating commands.
43
+
44
+ Do not run formatters.
45
+
46
+ Do not use `git add`, `git commit`, `git checkout`, `git reset`, `git clean`, or any command that changes repository state.
47
+
48
+ ## Beyond the Skill: Semantics
49
+
50
+ Review whether tests preserve the meaning of the domain, not just its mechanics.
51
+
52
+ Flag:
53
+
54
+ - Test names that use implementation vocabulary instead of domain vocabulary.
55
+ - Assertions that obscure the real domain rule.
56
+ - Test data that has no semantic relationship to the behaviour.
57
+ - Generic names like `foo`, `bar`, `thing`, or `value` when meaningful names would clarify the case.
58
+ - Tests that treat distinct domain concepts as interchangeable.
59
+ - Tests that create artificial examples that could not happen in the real domain.
60
+
61
+ Prefer:
62
+
63
+ - Domain-relevant examples.
64
+ - Semantically meaningful names.
65
+ - Assertions that express the rule in the caller's language.
66
+ - Tests that make the concept easier to understand.
67
+
68
+ Ask:
69
+
70
+ - Does this test explain the domain behaviour?
71
+ - Would a maintainer understand why this case matters?
72
+ - Is the vocabulary aligned with the production API?
73
+ - Is the test preserving meaning or merely exercising code?
74
+
75
+ ## Beyond the Skill: Design Feedback From Tests
76
+
77
+ Use test awkwardness as a signal, not just a quality issue in isolation.
78
+
79
+ Flag production design concerns when tests reveal:
80
+
81
+ - Too many setup steps for simple behaviour.
82
+ - Public APIs that are hard to use correctly.
83
+ - Objects that cannot be created in valid states without hacks.
84
+ - Domain logic hidden behind infrastructure boundaries.
85
+ - Too many mocks required for ordinary behaviour.
86
+ - Callers needing to understand sequencing or implementation details.
87
+ - Special cases that force duplicated tests.
88
+
89
+ Do not only say "the test is awkward." Explain what the awkwardness suggests about the design.
90
+
91
+ ## Output Format
92
+
93
+ Return your review using this structure.
94
+
95
+ ### Verdict
96
+
97
+ Choose one:
98
+
99
+ - `Good tests`
100
+ - `Mostly good, minor test debt`
101
+ - `Test design risk`
102
+ - `Reject / rethink`
103
+
104
+ Give a short reason.
105
+
106
+ ### Serious Issues
107
+
108
+ Only include issues that materially affect confidence, maintainability, behavioural clarity, or design feedback.
109
+
110
+ For each issue include:
111
+
112
+ - Location: file/path and line if available.
113
+ - Smell: the test design problem.
114
+ - Why it matters.
115
+ - Concrete improvement.
116
+
117
+ ### Minor Issues
118
+
119
+ Include small improvements that would make the tests clearer or less coupled.
120
+
121
+ ### What Is Good
122
+
123
+ Call out good test choices, especially:
124
+
125
+ - Clear behaviour names.
126
+ - Good given / when / then structure.
127
+ - Real objects used well.
128
+ - Mocks limited to boundaries.
129
+ - Strong regression coverage.
130
+ - Useful edge cases.
131
+ - Tests that expose a simple public API.
132
+
133
+ ### Suggested Test Improvements
134
+
135
+ If the test approach is weak, propose a simpler alternative.
136
+
137
+ Prefer concrete examples of better test names, better assertions, or better setup.
138
+
139
+ Do not invent a giant test framework.
140
+
141
+ ### Design Feedback
142
+
143
+ Mention any production design issues revealed by the tests (see "Beyond the Skill: Design Feedback From Tests" above).
144
+
145
+ ### Follow-Up Questions
146
+
147
+ Ask only questions that materially affect the test strategy.
148
+
149
+ Do not ask vague questions.
150
+
151
+ ## Review Rules
152
+
153
+ - Be direct.
154
+ - Prefer a few high-value comments over a long list.
155
+ - Do not nitpick formatting.
156
+ - Do not complain about missing coverage unless the missing behaviour matters.
157
+ - Do not demand mocks for real deterministic objects.
158
+ - Do not demand integration tests when a unit test expresses the behaviour better.
159
+ - Do not suggest snapshot tests unless the output is genuinely large and stable.
160
+ - Treat semantic clarity as a core test quality, not a naming nitpick.
161
+ - Distinguish real test risk from personal taste.
162
+ - If the tests are good, say so.
163
+ - If there is not enough context, inspect nearby tests and production code before judging.
@@ -0,0 +1,85 @@
1
+ ---
2
+
3
+ paths:
4
+
5
+ - "src/**/*.py"
6
+
7
+ ---
8
+
9
+ # Code style rules
10
+
11
+ Follow the existing project style before introducing a new style.
12
+
13
+ Use clear, descriptive names. Avoid abbreviations unless they are widely understood, such as `id`, `url`, `http`, `sql`, `api`, `pk`, or `fk`.
14
+
15
+ Use absolute imports only. Relative imports are banned by project tooling.
16
+
17
+ Use type hints on function signatures.
18
+
19
+ Prefer explicit control flow when logic is non-trivial.
20
+
21
+ Avoid clever one-liners when a small block is easier to read.
22
+
23
+ Prefer immutable value objects for domain concepts when practical.
24
+
25
+ Keep functions focused, but do not split code into shallow helpers that only rename, forward, or obscure the flow.
26
+
27
+ Comments should explain non-obvious why, constraints, invariants, or trade-offs. Do not add comments that merely restate what the code says.
28
+
29
+ Do not swallow errors silently.
30
+
31
+ Do not use bare `except`.
32
+
33
+ Do not introduce new dependencies without explicit approval.
34
+
35
+ ## Design style
36
+
37
+ Prefer deep modules: simple interfaces that hide meaningful internal complexity.
38
+
39
+ Pull complexity downward into modules so callers do not need to understand sequencing, internal data structures, backend quirks, algorithms, or implementation details.
40
+
41
+ Prefer composition over inheritance.
42
+
43
+ Prioritise readability and maintainability over optimisation. Optimise only when there is evidence that performance, cost, scale, or reliability requires it.
44
+
45
+ Avoid shallow abstractions: wrappers, pass-through methods, and layers that merely rename or forward concepts without hiding complexity.
46
+
47
+ Avoid pass-through methods and wrappers that do not add behaviour, hide complexity, protect a boundary, or provide a stable public import path.
48
+
49
+ Avoid scattering special cases across callers. Prefer designs where the general case naturally handles edge inputs such as empty collections, missing optional values, zero values, or absent drift.
50
+
51
+ Design invalid states out of existence when practical. Prefer types, constructors, and APIs that make misuse difficult rather than detecting invalid states late.
52
+
53
+ Keep information hidden. Internal data structures, algorithms, backend quirks, and sequencing decisions should not leak through public interfaces.
54
+
55
+ Prefer the simplest general-purpose interface that covers the current real use cases over a collection of narrow special-purpose methods.
56
+
57
+ Do not add speculative abstractions for imagined future use cases.
58
+
59
+ Do not trade a clean design for a quick patch unless the task is explicitly an emergency fix. If a tactical fix is necessary, call out the design debt and the follow-up.
60
+
61
+ ## Layering
62
+
63
+ Keep infrastructure concerns at the edges.
64
+
65
+ Do not leak Spark, Databricks, Delta Lake, Py4J, SQL execution details, or backend metadata shapes into domain or application code.
66
+
67
+ Shared domain vocabulary is fine across layers. Implementation details, infrastructure types, persistence models, and backend-specific assumptions should not cross inward.
68
+
69
+ Thin public facades are allowed when they provide stable user-facing import paths. Avoid accidental pass-through layers elsewhere.
70
+
71
+ ## Error handling
72
+
73
+ Prefer explicit, typed failures where the project already uses them.
74
+
75
+ Backend exceptions should be translated at the adapter boundary.
76
+
77
+ Do not hide unexpected failures behind vague messages.
78
+
79
+ Error messages should help the caller understand what failed and, where appropriate, what to inspect next.
80
+
81
+ ## Formatting
82
+
83
+ Do not manually fight the formatter.
84
+
85
+ If a style question is already enforced by Ruff, mypy, import-linter, or pytest, prefer satisfying the tool over adding a new convention.
@@ -0,0 +1,100 @@
1
+ ---
2
+
3
+ paths:
4
+
5
+ - "src/**/*.py"
6
+ - "tests/**/*.py"
7
+ - "docs/**/*.md"
8
+ - "docs/**/*.rst"
9
+ - "README.md"
10
+
11
+ ---
12
+
13
+ # Documentation rules
14
+
15
+ When changing source code, tests, public behaviour, architecture, validation policy, action types, adapter behaviour, failure semantics, or examples, consider whether docs need updating.
16
+
17
+ Documentation should explain the project’s public behaviour, architecture, workflows, and design rationale. Do not duplicate implementation details that are better read directly from code.
18
+
19
+ Keep docs aligned with the actual code and tests. If code and docs disagree, treat the code and tests as the source of truth, then update the docs.
20
+
21
+ Prefer clear, practical explanations over marketing language.
22
+
23
+ Use examples that reflect the real public API.
24
+
25
+ Do not invent Databricks, Delta Lake, Spark, or Unity Catalog behaviour. If behaviour depends on Databricks specifics, verify it against adapter code, tests, or official Databricks documentation before documenting it.
26
+
27
+ ## When docs should change
28
+
29
+ Consider updating docs when changing:
30
+
31
+ * public API declarations
32
+ * `DeltaTable`, `ForeignKey`, `Property`, or schema declaration behaviour
33
+ * sync lifecycle behaviour
34
+ * validation policy or safe-change rules
35
+ * action planning or action ordering
36
+ * Databricks SQL generation
37
+ * adapter behaviour
38
+ * error handling or failure reporting
39
+ * metadata-only deployment behaviour
40
+ * table properties, tags, comments, primary keys, or foreign keys
41
+ * project architecture or layer boundaries
42
+
43
+ Do not update docs for purely internal refactors unless the conceptual model or user-visible behaviour changes.
44
+
45
+ ## Style
46
+
47
+ Write for a technical user who understands Python and data engineering, but may not know the internal engine design yet.
48
+
49
+ Prefer concrete examples over abstract explanation.
50
+
51
+ Keep examples small and focused.
52
+
53
+ Use consistent terminology:
54
+
55
+ * desired state
56
+ * observed state
57
+ * drift
58
+ * validation
59
+ * action plan
60
+ * dependency resolution
61
+ * execution summary
62
+ * sync report
63
+ * managed aspects
64
+
65
+ Do not introduce new names for existing concepts.
66
+
67
+ When explaining architecture, preserve the distinction between:
68
+
69
+ * domain facts
70
+ * application policy
71
+ * adapter behaviour
72
+ * public API declarations
73
+
74
+ When explaining failures, make clear whether a failure happens during read, validation, dependency resolution, execution, or report handling.
75
+
76
+ ## Code examples
77
+
78
+ Use public import paths in user-facing examples:
79
+
80
+ ```python
81
+ from delta_engine.schema import DeltaTable
82
+ from delta_engine.databricks import build_engine
83
+ ```
84
+
85
+ Avoid examples that import from internal modules unless the doc is explicitly for contributors.
86
+
87
+ Keep examples executable in principle, even when shortened.
88
+
89
+ Prefer simple names such as `customers`, `orders`, and `order_items` unless the surrounding doc needs a more specific domain.
90
+
91
+ ## Building docs
92
+
93
+ Build docs with:
94
+
95
+ ```bash
96
+ uv sync --group docs
97
+ uv run --group docs sphinx-build -b html docs docs/_build/html -W
98
+ ```
99
+
100
+ If documentation changes include code examples, also run the most relevant tests for the behaviour being documented.