Skip to content

fix(generator): refuse to regenerate templates from empty resolved source#100

Merged
silversurfer562 merged 2 commits into
mainfrom
fix/empty-source-regen-guard
Jul 12, 2026
Merged

fix(generator): refuse to regenerate templates from empty resolved source#100
silversurfer562 merged 2 commits into
mainfrom
fix/empty-source-regen-guard

Conversation

@silversurfer562

Copy link
Copy Markdown
Member

Summary

  • Guards the template generator against overwriting good, existing templates when the resolved source for a feature is empty or unreadable — a prior run rewrote 36 good templates with degraded empty-source output.
  • If a feature's matched source files all resolve to empty/unreadable content and templates already exist for it, refuse the overwrite rather than stamping degraded content with an empty-string source_hash.
  • Scaffolding a genuinely new feature from empty source (no existing templates at risk) still proceeds normally.

Test plan

  • tests/test_empty_source_guard.py — 6/6 passed locally
  • Full suite: 1248 passed, 38 skipped (expected — optional extras), 1 deselected
  • Rebased clean onto current main, no conflicts

🤖 Generated with Claude Code

…urce

2026-07-05 incident: a bulk regen in attune-ai ran against a wrong
project root, resolved every feature's source globs to zero files,
computed source_hash of the empty string (e3b0c442...), and rewrote
36 existing .help/templates files with drastically degraded content
(1044 insertions vs 2609 deletions). The pipeline generated happily
from empty input instead of failing.

Guard (prepare_polish_phase, before any mkdir/write): when globs
match nothing — or every matched file is empty/unreadable — and
generated output already exists on disk, raise EmptySourceError
instead of overwriting. Scaffolding a brand-new feature from empty
source (nothing on disk yet) stays allowed, per the documented
zero-match scaffold contract in test_generator.py.

- staleness.py: EMPTY_SOURCE_SHA256 constant + EmptySourceError
- generator.py: overwrite guard covering help templates and
  project-doc kinds; both hash paths (byte and semantic) produce
  the empty-string SHA on empty input, so one sentinel covers both
- cli.py: catch EmptySourceError at the run_command boundary and
  exit 1 with the message (RuntimeError subclass on purpose — the
  batch-submit path swallows ValueError as "nothing to do" exit 0)
- run_maintenance/batch submit propagate it, so `regenerate` aborts
  loudly rather than recording a silent per-feature failure

Tests: tests/test_empty_source_guard.py — refusal preserves on-disk
content, zero-byte source refused, scaffold-from-empty still allowed,
healthy source unaffected, CLI exits 1 without traceback.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@codecov

codecov Bot commented Jul 12, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.15385% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/attune_author/generator.py 94.73% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Codecov flagged the guard at 80.76% patch coverage (target 86.93%).
Covers: an unrecognized depth name outside _ALL_TEMPLATE_NAMES is
skipped rather than crashing the at-risk scan, and a project-doc kind
(how-to/tutorial/cli-reference/architecture) resolves its at-risk path
via _project_doc_output_path, not just .help/ template_dir.
@silversurfer562 silversurfer562 merged commit 706b984 into main Jul 12, 2026
14 checks passed
@silversurfer562 silversurfer562 deleted the fix/empty-source-regen-guard branch July 12, 2026 02:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant