Skip to content

Commit 9bc7f87

Browse files
bokelleyclaude
andcommitted
fix: remove stale generated files and improve type generation infrastructure
This commit addresses the downstream report that pricing options were missing the is_fixed discriminator field. Investigation revealed the discriminator already exists in the individual pricing option schema files generated by datamodel-code-generator. The root causes were: 1. Stale files (pricing_option.py, brand_manifest_ref.py, index.py, start_timing.py) persisting from previous generations 2. No mechanism to clean output directory before regeneration 3. Timestamp-only changes creating noisy commits Changes: - Remove stale generated files that no longer correspond to current schemas - Add clean slate generation: delete entire output directory before regenerating types to prevent stale artifacts from old schema versions - Add timestamp change detection: restore files where only the generation timestamp changed to avoid noisy commits - Update CLAUDE.md with patterns for preventing stale files and noisy commits All pricing options now correctly have the is_fixed discriminator: - Fixed-rate: CpmFixedRatePricingOption, VcpmFixedRatePricingOption, etc. (is_fixed: Annotated[Literal[True], ...]) - Auction: CpmAuctionPricingOption, VcpmAuctionPricingOption (is_fixed: Annotated[Literal[False], ...]) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent e3e5066 commit 9bc7f87

File tree

104 files changed

+194
-854
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

104 files changed

+194
-854
lines changed

CLAUDE.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ Files in `src/adcp/types/generated_poc/` and `src/adcp/types/generated.py` are a
2727

2828
We use `scripts/post_generate_fixes.py` which runs automatically after type generation to apply necessary modifications that can't be generated.
2929

30+
**Preventing Stale Files:**
31+
32+
The generation script (`scripts/generate_types.py`) **deletes the entire output directory** before regenerating types. This prevents stale files from persisting when schemas are renamed or removed. Without this, old generated files could remain checked in indefinitely, causing import errors and confusion about which types are actually current.
33+
34+
**Avoiding Noisy Commits:**
35+
36+
After generation, the script automatically restores files where only the timestamp changed (e.g., `# timestamp: 2025-11-18T03:32:03+00:00`). This prevents commits with 100+ file changes where the only difference is the generation timestamp, making actual changes easier to review.
37+
3038
**Type Name Collisions:**
3139

3240
The upstream AdCP schemas define multiple types with the same name (e.g., `Contact`, `Asset`, `Status`) in different schema files. These are **genuinely different types** with different fields, not duplicates.
@@ -59,6 +67,14 @@ from adcp.types.generated_poc.format import Asset as FormatAsset
5967
- `create_media_buy_request.py`
6068
- `get_products_request.py`
6169

70+
**Note on Pricing Options:**
71+
72+
The code generator creates individual files for each pricing option (e.g., `cpm_fixed_option.py`, `cpm_auction_option.py`) with the `is_fixed` discriminator field already included:
73+
- Fixed-rate options: `is_fixed: Annotated[Literal[True], ...]`
74+
- Auction options: `is_fixed: Annotated[Literal[False], ...]`
75+
76+
These are used via union types in `Product.pricing_options`. No post-generation fix is needed for pricing options.
77+
6278
**To add new post-generation fixes:**
6379
Edit `scripts/post_generate_fixes.py` and add a new function. The script:
6480
- Runs automatically via `generate_types.py`

scripts/generate_types.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,76 @@ def generate_types(input_dir: Path):
179179
return True
180180

181181

182+
def normalize_timestamp(content: str) -> str:
183+
"""Remove timestamp from generated file for comparison.
184+
185+
Timestamps look like:
186+
# timestamp: 2025-11-18T03:32:03+00:00
187+
"""
188+
return re.sub(r"#\s+timestamp:.*\n", "", content)
189+
190+
191+
def restore_unchanged_files():
192+
"""Restore files where only the timestamp changed.
193+
194+
This prevents noisy commits where the only change is the generation timestamp.
195+
We compare file contents ignoring timestamp lines.
196+
"""
197+
print("Checking for timestamp-only changes...")
198+
199+
# Get git status to see modified files
200+
result = subprocess.run(
201+
["git", "diff", "--name-only", str(OUTPUT_DIR)],
202+
capture_output=True,
203+
text=True,
204+
cwd=REPO_ROOT,
205+
)
206+
207+
if result.returncode != 0:
208+
print(" Could not check git status (skipping restoration)")
209+
return
210+
211+
modified_files = [f for f in result.stdout.strip().split("\n") if f]
212+
restored_count = 0
213+
214+
for rel_path in modified_files:
215+
file_path = REPO_ROOT / rel_path
216+
if not file_path.exists():
217+
continue
218+
219+
# Get current (new) content
220+
with open(file_path) as f:
221+
new_content = f.read()
222+
223+
# Get old content from git
224+
git_result = subprocess.run(
225+
["git", "show", f"HEAD:{rel_path}"],
226+
capture_output=True,
227+
text=True,
228+
cwd=REPO_ROOT,
229+
)
230+
231+
if git_result.returncode != 0:
232+
continue
233+
234+
old_content = git_result.stdout
235+
236+
# Compare without timestamps
237+
if normalize_timestamp(old_content) == normalize_timestamp(new_content):
238+
# Only timestamp changed, restore old version
239+
subprocess.run(
240+
["git", "checkout", "HEAD", "--", rel_path],
241+
cwd=REPO_ROOT,
242+
capture_output=True,
243+
)
244+
restored_count += 1
245+
246+
if restored_count > 0:
247+
print(f" ✓ Restored {restored_count} file(s) with only timestamp changes")
248+
else:
249+
print(" No timestamp-only changes found")
250+
251+
182252
def apply_post_generation_fixes():
183253
"""Apply post-generation fixes using the dedicated script."""
184254
print("Running post-generation fixes...")
@@ -211,6 +281,13 @@ def main():
211281

212282
temp_schemas = None
213283
try:
284+
# Clean output directory to prevent stale files
285+
# This ensures old/renamed schema files don't persist
286+
if OUTPUT_DIR.exists():
287+
print("Cleaning output directory...")
288+
shutil.rmtree(OUTPUT_DIR)
289+
print(" ✓ Removed stale generated files\n")
290+
214291
# Ensure output directory exists
215292
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
216293

@@ -228,6 +305,9 @@ def main():
228305
if not apply_post_generation_fixes():
229306
return 1
230307

308+
# Restore files where only timestamp changed
309+
restore_unchanged_files()
310+
231311
# Count generated files
232312
py_files = list(OUTPUT_DIR.glob("*.py"))
233313
print("\n✓ Successfully generated types")
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# generated by datamodel-codegen:
22
# filename: .schema_temp
3-
# timestamp: 2025-11-18T03:04:10+00:00
3+
# timestamp: 2025-11-18T03:35:10+00:00

src/adcp/types/generated_poc/activate_signal_request.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# generated by datamodel-codegen:
22
# filename: activate-signal-request.json
3-
# timestamp: 2025-11-18T03:04:10+00:00
3+
# timestamp: 2025-11-18T03:35:10+00:00
44

55
from __future__ import annotations
66

src/adcp/types/generated_poc/activate_signal_response.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# generated by datamodel-codegen:
22
# filename: activate-signal-response.json
3-
# timestamp: 2025-11-18T03:04:10+00:00
3+
# timestamp: 2025-11-18T03:35:10+00:00
44

55
from __future__ import annotations
66

src/adcp/types/generated_poc/activation_key.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# generated by datamodel-codegen:
22
# filename: activation-key.json
3-
# timestamp: 2025-11-18T03:04:10+00:00
3+
# timestamp: 2025-11-18T03:35:10+00:00
44

55
from __future__ import annotations
66

src/adcp/types/generated_poc/adagents.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# generated by datamodel-codegen:
22
# filename: adagents.json
3-
# timestamp: 2025-11-18T03:04:10+00:00
3+
# timestamp: 2025-11-18T03:35:10+00:00
44

55
from __future__ import annotations
66

src/adcp/types/generated_poc/asset_type.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# generated by datamodel-codegen:
22
# filename: asset-type.json
3-
# timestamp: 2025-11-18T03:04:10+00:00
3+
# timestamp: 2025-11-18T03:35:10+00:00
44

55
from __future__ import annotations
66

src/adcp/types/generated_poc/audio_asset.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# generated by datamodel-codegen:
22
# filename: audio-asset.json
3-
# timestamp: 2025-11-18T03:04:10+00:00
3+
# timestamp: 2025-11-18T03:35:10+00:00
44

55
from __future__ import annotations
66

src/adcp/types/generated_poc/brand_manifest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# generated by datamodel-codegen:
22
# filename: brand-manifest.json
3-
# timestamp: 2025-11-18T03:04:10+00:00
3+
# timestamp: 2025-11-18T03:35:10+00:00
44

55
from __future__ import annotations
66

0 commit comments

Comments
 (0)