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.message}"
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