pipeline: reject resref collisions when chunking; document erf post-write hashing #8

Merged
archvillainette merged 1 commits from content-sureness into main 2026-06-20 07:53:04 +00:00
Owner

What

Two correctness fixes surfaced in review of the direct-depot HAK artifact work.

Reject resref+type collisions in chunksFromManifest

A manifest can name two distinct source paths that collapse to the same
resref+type (e.g. creature/foo.tga and placeable/foo.tga — resref is the
lowercase basename minus extension). The ERF writer keys resources on
Name:Type, so the second silently shadowed the first: an asset would vanish
from the packed HAK with no error.

chunksFromManifest now runs ensureUniqueChunkResources per chunk and fails
the build on a duplicate. This guards both build paths — the legacy
--source-manifest flow and the direct content-addressed flow
(chunksFromSourceManifestchunksFromManifest).

Document erf post-write hash coupling

writeResourceData streams the source into the output while hashing, so
size/SHA mismatches are only detected after the bytes are written. A non-nil
return therefore means the writer holds partial, unverified output and the
caller must discard it. Added a comment making that contract explicit;
writeHAKArchive already honours it (writes to a temp file, removes on any
Write error, never renames a bad archive into place).

Tests

  • TestChunksFromManifestRejectsResrefCollision: collision → error, distinct
    resrefs → clean. Asserts only error presence/absence — silent asset loss is
    the contract, not any specific wording.
  • go vet ./internal/erf/ ./internal/pipeline/ clean.
  • go test ./internal/erf/ ./internal/pipeline/ green.

Follow-up

A new crucible release must be cut after this merges so the guard ships in the
binary sow-assets-manifest pins.

Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com

## What Two correctness fixes surfaced in review of the direct-depot HAK artifact work. ### Reject resref+type collisions in `chunksFromManifest` A manifest can name two distinct source paths that collapse to the same `resref+type` (e.g. `creature/foo.tga` and `placeable/foo.tga` — resref is the lowercase basename minus extension). The ERF writer keys resources on `Name:Type`, so the second silently shadowed the first: an asset would vanish from the packed HAK with no error. `chunksFromManifest` now runs `ensureUniqueChunkResources` per chunk and fails the build on a duplicate. This guards **both** build paths — the legacy `--source-manifest` flow and the direct content-addressed flow (`chunksFromSourceManifest` → `chunksFromManifest`). ### Document erf post-write hash coupling `writeResourceData` streams the source into the output while hashing, so size/SHA mismatches are only detected *after* the bytes are written. A non-nil return therefore means the writer holds partial, unverified output and the caller must discard it. Added a comment making that contract explicit; `writeHAKArchive` already honours it (writes to a temp file, removes on any Write error, never renames a bad archive into place). ## Tests - `TestChunksFromManifestRejectsResrefCollision`: collision → error, distinct resrefs → clean. Asserts only error presence/absence — silent asset loss is the contract, not any specific wording. - `go vet ./internal/erf/ ./internal/pipeline/` clean. - `go test ./internal/erf/ ./internal/pipeline/` green. ## Follow-up A new crucible release must be cut after this merges so the guard ships in the binary `sow-assets-manifest` pins. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
archvillainette added 1 commit 2026-06-20 07:45:12 +00:00
Content Sureness
build-binaries / build-binaries (pull_request) Successful in 2m16s
test-image / build-image (pull_request) Successful in 48s
test / test (pull_request) Successful in 1m26s
3b23910055
archvillainette merged commit f1fd03ee83 into main 2026-06-20 07:53:04 +00:00
archvillainette deleted branch content-sureness 2026-06-20 07:53:04 +00:00
Sign in to join this conversation.