* fix(scanner): cache decoded text atomically
_cache_text_file keyed the cache on the virtual path and returned the path, and
the caller serves that path as the file's content, so a write that failed
part-way left a truncated entry that every later read handed back as the preview.
Publish through a temporary file plus os.replace.
* test(scanner): pin the cache entry against a failed publish
The helper-level case cannot distinguish the call site, so this drives the real
entry point with a failing publish and asserts the cached entry is untouched.
* docs(sandbox): keep agent guidance within its size limit
* fix(scanner): preserve publish errors during cleanup
---------
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>