Visual Brief Guard
Visual Brief Guard is a small Ruby library for validating structured visual briefs before they are exported to JSON. It keeps schema validation, canonicalization, and fingerprinting separate from any downstream image generation step.
Why deterministic briefs matter
Two briefs can describe the same creative intent but produce different JSON
bytes because their hash keys were inserted in a different order or because
one process serialized a whole number as 1.0 and another as 1. That causes
avoidable cache misses, duplicate work, and noisy version-control diffs.
Visual Brief Guard converts nested hashes into a canonical form before JSON serialization. Keys are converted to strings and sorted recursively, arrays retain their meaningful order, and integral floats are normalized to integers. The resulting JSON can be compared, versioned, or hashed consistently.
Installation
gem "visual_brief_guard"
Then run:
bundle install
Usage
require "visual_brief_guard"
brief = {
scene: "A ceramic mug on a clean studio surface",
aspect_ratio: "4:5",
color_palette: "warm neutrals",
reference_tags: ["mug", "front-view"],
constraints: {
width: 1200.0,
height: 1500
}
}
VisualBriefGuard.validate!(brief)
json = VisualBriefGuard.canonical_json(brief)
fingerprint = VisualBriefGuard.fingerprint(brief)
VisualBriefGuard.export(brief, "brief.json")
The required fields are scene, aspect_ratio, color_palette, and
reference_tags. Optional fields are schema_version, constraints, and
notes. Unknown fields are rejected so transient data such as timestamps
cannot silently change an otherwise stable fingerprint.
Validation before serialization
Validation runs before canonicalization or digest calculation. This order matters because a cryptographic digest can represent invalid data just as reliably as valid data. A malformed brief should therefore fail at the boundary, rather than receive a fingerprint that looks trustworthy.
validate! raises VisualBriefGuard::InvalidBrief with a specific reason when
the input is not a hash, a required field is missing or empty,
reference_tags is not an array, or an unexpected field is present. Calling
code can rescue that one exception type and return the message to an editor
without treating implementation errors as ordinary validation failures.
begin
VisualBriefGuard.fingerprint(candidate)
rescue VisualBriefGuard::InvalidBrief => error
warn "Brief rejected: #{error.}"
end
The field policy is intentionally explicit. Silently accepting every new key would allow request IDs, timestamps, or UI state to alter a digest even though those values do not describe creative intent. Add a field to the allowed schema only when it should be part of the durable brief.
Canonicalization rules
Ruby hashes retain insertion order, but that order often reflects how data arrived rather than what it means. Visual Brief Guard converts keys to strings, sorts them at every nesting level, and emits compact JSON. Arrays remain ordered because their order may be meaningful; reference priority, for example, should not be rearranged automatically.
An integral float such as 1200.0 becomes 1200. A non-integral value remains
a float. This small normalization prevents equivalent dimensions from
producing different bytes while preserving actual fractional values.
The SHA-256 fingerprint is calculated from this canonical JSON. It is useful as a cache key, duplicate detector, or immutable audit reference. It is not a security signature and does not prove who created or approved a brief.
Schema evolution
Treat the optional schema_version field as part of the canonical data when a
workflow begins evolving. Adding a new required field or changing the meaning
of an existing field should normally increment that version. Older records can
then be migrated deliberately instead of being reinterpreted silently.
Defaults also need care. If a future release adds an optional lighting field, decide whether an omitted value and an explicit default should share a fingerprint. Normalize the default before hashing when they are semantically equivalent; leave them distinct when omission carries meaning. Tests should capture that decision so a later refactor cannot change it accidentally.
Test the invariants
The most useful tests for deterministic serialization are not limited to the happy path. A robust suite should verify that shuffled key insertion order produces the same digest, malformed data fails before hashing, repeated canonicalization is stable, and unknown fields are either rejected or explicitly added to the schema.
This gem includes Minitest coverage for those boundaries:
ruby -Ilib test/visual_brief_guard_test.rb
Workflow boundary
This library stops at a validated JSON artifact. It does not call an image generation API and cannot judge whether a visual result matches the creative direction. That separation is intentional: deterministic input validation and creative output review are different responsibilities.
After review, a person can use the exported description in a separate browser-based workspace such as the Seedream 5.0 Pro AI Image Generator for text-to-image, image-to-image, sketch-guided, or multi-reference exploration. This is a manual handoff to an independent tool, not a programmatic integration.
Stable input does not guarantee identical generated images because downstream systems may be stochastic. It does provide a dependable record of intent: if two accepted briefs have the same canonical fingerprint, their validated data is structurally identical.
License
MIT