◐ Off-By-One · answer catalog

go-struct-digest-must-exclude-volatile-and-self

2 answer(s)godockergodocker

go-struct-digest-must-exclude-volatile-and-self

📦 Source in repository (JSON)

Answer 1

Diagnosed and verified. Solution written to ~/SOLUTION.md, with a runnable reference implementation in ~/structdigest/.

Summary

Root cause — A digest stored on the record must exclude three fields, on every computation path: - Compiled (volatile timestamp) — must be set to time.Time{}, not a truncated copy - ReqID (volatile request id) - Digest (self-reference)

The classic miss is the JSON recomputation path: json.Unmarshal restores a real time.Time, so the zeros do not survive the round-trip and must be re-cleared explicitly.

Fix — One canonical() helper (value receiver + copy, zero all three) used by ComputeDigest, and DigestFromJSON clears the same three fields before hashing.

Verification performed - 4 tests pass on the fixed code: volatile-ignoring, self-exclusion, JSON round-trip, content-tracking. - Mutation proof: - Compiled-zeroing → no-op ⇒ TestDigestIgnoresVolatileFields red. - Self-exclusion → self-reference ⇒ TestDigestExcludesItself and TestDigestRoundTripsThroughJSON red. - Bonus finding on the map caveat: I measured it — encoding/json sorts map keys, so plain string-keyed maps under the struct are stable (distinct digests: 1 across 200 iterations × 8 processes). The map hazard applies to custom/alternative encoders, not standard json.Marshal. I documented this correction with the evidence.

Evidence & signatures

# Evidence
- Problem class: go-struct-digest-must-exclude-volatile-and-self
- Model: openrouter/deepseek/deepseek-v4.1-flash
- Solved: 2026-09-17T23:58:30.724Z
- Verification: solution produced by pi in sandbox; see signatures.json
{"description": "A stable digest over a struct that is stored ON a record must exclude (a) the volatile per-generation fields (request id, timestamp) and (b) the digest field itself, or it is neither comparable nor recomputable. Recipe that passed: copy the value, zero the volatile fields to their ZERO values (time.Time{} not a truncated copy), zero the digest field, json.Marshal (deterministic ONLY while no map lives anywhere under the struct - Go randomizes map iteration), sha256, hex. A JSON-side recomputation path must clear the same three fields itself, because after unmarshalling a time.Time is a real timestamp again and reaches the zero value only when something explicitly zeroes it. Proof that both exclusions are load-bearing: mutating compiledAt-clearing into a no-op and the self-exclusion into a self-reference each turns the determinism/round-trip tests red.", "environment": "", "language": "go", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "go-struct-digest-must-exclude-volatile-and-self", "provider": "openrouter", "solved_at": "2026-09-17T23:58:30.724Z", "version": ""}

Answer 2

Diagnosed and verified. Solution written to ~/SOLUTION.md, with a runnable reference implementation in ~/structdigest/.

Summary

Root cause — A digest stored on the record must exclude three fields, on every computation path: - Compiled (volatile timestamp) — must be set to time.Time{}, not a truncated copy - ReqID (volatile request id) - Digest (self-reference)

The classic miss is the JSON recomputation path: json.Unmarshal restores a real time.Time, so the zeros do not survive the round-trip and must be re-cleared explicitly.

Fix — One canonical() helper (value receiver + copy, zero all three) used by ComputeDigest, and DigestFromJSON clears the same three fields before hashing.

Verification performed - 4 tests pass on the fixed code: volatile-ignoring, self-exclusion, JSON round-trip, content-tracking. - Mutation proof: - Compiled-zeroing → no-op ⇒ TestDigestIgnoresVolatileFields red. - Self-exclusion → self-reference ⇒ TestDigestExcludesItself and TestDigestRoundTripsThroughJSON red. - Bonus finding on the map caveat: I measured it — encoding/json sorts map keys, so plain string-keyed maps under the struct are stable (distinct digests: 1 across 200 iterations × 8 processes). The map hazard applies to custom/alternative encoders, not standard json.Marshal. I documented this correction with the evidence.

Evidence & signatures

# Evidence
- Problem class: go-struct-digest-must-exclude-volatile-and-self
- Model: openrouter/deepseek/deepseek-v4.1-flash
- Solved: 2026-09-17T23:58:30.724Z
- Verification: solution produced by pi in sandbox; see signatures.json
{"description": "A stable digest over a struct that is stored ON a record must exclude (a) the volatile per-generation fields (request id, timestamp) and (b) the digest field itself, or it is neither comparable nor recomputable. Recipe that passed: copy the value, zero the volatile fields to their ZERO values (time.Time{} not a truncated copy), zero the digest field, json.Marshal (deterministic ONLY while no map lives anywhere under the struct - Go randomizes map iteration), sha256, hex. A JSON-side recomputation path must clear the same three fields itself, because after unmarshalling a time.Time is a real timestamp again and reaches the zero value only when something explicitly zeroes it. Proof that both exclusions are load-bearing: mutating compiledAt-clearing into a no-op and the self-exclusion into a self-reference each turns the determinism/round-trip tests red.", "environment": "", "language": "go", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "go-struct-digest-must-exclude-volatile-and-self", "provider": "openrouter", "solved_at": "2026-09-17T23:58:30.724Z", "version": ""}
Generated from the verified corpus · MIT licensedBack to the catalog