Every read/write resolved to the locale codec (cp1252/cp949 on Windows):
non-ASCII files were silently mojibake'd, and because Path.write_text
truncates before encoding, a UnicodeEncodeError left the target at 0 bytes.
The backup readback check couldn't catch it — it read back with the same
wrong codec.
- encoding=utf-8 pinned on every I/O call site (compress, validate,
detect, benchmark); validate now decodes strict — it's the fidelity gate
- write_text_atomic: encode first, temp file in same dir, fsync, preserve
permissions, os.replace; temp unlinked on any failure
- fix-retry pass gains the same empty-output guard as the first pass
- fix-retry preamble leak (#588): output must start at the original's
structural anchor (frontmatter/heading) or the attempt is rejected
- primary-write failure now prints the backup path — users hitting the
crash had no idea a backup existed
- extract_inline_codes: strip fences via the CommonMark-aware extractor;
the old column-0 regex leaked indented fences into inline-code pairing,
causing false validation failures (extracted from PR #619's diagnosis)
- SKILL.md/README/SECURITY corrected: backups live in the out-of-tree data
dir (#420), not beside the source file
Supersedes PRs #683#678#626#534 and the fence fix from #619 with a
local implementation. 58 python tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ySX6TBWZuvFze4ajf7Hpf
- Move caveman-compress/ source (SKILL.md, scripts/, README.md, SECURITY.md) to skills/caveman-compress/.
- Delete skills/compress/ — the CI-generated rename mirror that caused dual-source confusion.
- Move plugins/caveman/skills/compress/ to plugins/caveman/skills/caveman-compress/. Plugin keeps the consolidated name; CI no longer rewrites the frontmatter.
- Replace the two sed-heavy CI sync steps with one verbatim cp -r from source to plugin.
- Update verify_repo.py, test_compress_safety.py, test_validate_inline.py, GEMINI.md, AGENTS.md, CONTRIBUTING.md, README.md, and the workflow paths to reference the new location.
- Use Path.resolve().parents[N] for the benchmark.py repo-root walk now that the directory depth changed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Five user-contributed fixes consolidated:
* UTF-8 stdout (#289 by @ousamabenyounes) — reconfigure stdout/stderr at the
top of cli.py so Windows cp1252 consoles don't crash on the ❌ glyph in
error/validation branches and mask the real error.
* Empty / unchanged compression guards (#292 by @ousamabenyounes, closes#237)
— refuse empty input, refuse empty/whitespace/identical Claude output,
read back the backup before touching the input. Five regression tests in
tests/test_compress_safety.py.
* Inline backtick validation (#309 by @hireblackout) — validate_inline_codes
closes the silent-overwrite gap where `npm install` → `yarn install`
passed validation. Wired into validate(); 11 unit tests in
tests/test_validate_inline.py.
* Frontmatter angle-bracket fix (#268 by @Bortlesboat, closes#266) —
caveman-compress/SKILL.md description now uses FILEPATH instead of
<filepath>, plus verify_repo gains a new
verify_skill_frontmatter_upload_compatibility check, UTF-8 hardening for
Windows, and the activation-banner regex no longer requires a trailing
period.
* Two test fixtures (claude-md-project.md, mixed-with-code.md) updated so
the new inline-backtick validator passes — they were silently dropping
`server/src/`, `type(scope): description`, and `status` references in
compression. The fixture is documentation of "good" compression, so the
fix is to preserve those references.
Co-Authored-By: Ben Younes <ousama.benyounes@oratelecom.net>
Co-Authored-By: hireblackout <hireblackout@users.noreply.github.com>
Co-Authored-By: Andrew Barnes <Bortlesboat@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>