visual_brief_guard 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 65166b9ead1264ae2cfec8acefedf80fb658910155b10953a0c81e287d8aa8e9
4
+ data.tar.gz: dae4ef2111c521d439d0c21a153d8da90b15f1160eb38e45e116b68daf562f7a
5
+ SHA512:
6
+ metadata.gz: b9e21a04afe44bc46792d521e708a410c3f116b68b0c449a335472ebe8f2a66ca964468a0f7e55c5f77b703eefad49e0385e539553acd2007eea9f4f9627f386
7
+ data.tar.gz: b3e8ab26e393ff8999deb43c2d34324cae763bde38cdde8f4d86d6961be1aa7eb0f0de2d3cd6af628622bdc14328862bcbaeb05011194f2aa84dac9cbcb93196
data/CHANGELOG.md ADDED
@@ -0,0 +1,8 @@
1
+ # Changelog
2
+
3
+ ## 1.0.0 - 2026-07-29
4
+
5
+ - Validate required and allowed visual brief fields.
6
+ - Canonicalize nested hashes and integral floats.
7
+ - Export stable JSON and calculate SHA-256 fingerprints.
8
+ - Add tests for ordering, validation, normalization, and export behavior.
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Martyn Foster
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,148 @@
1
+ # Visual Brief Guard
2
+
3
+ Visual Brief Guard is a small Ruby library for validating structured visual
4
+ briefs before they are exported to JSON. It keeps schema validation,
5
+ canonicalization, and fingerprinting separate from any downstream image
6
+ generation step.
7
+
8
+ ## Why deterministic briefs matter
9
+
10
+ Two briefs can describe the same creative intent but produce different JSON
11
+ bytes because their hash keys were inserted in a different order or because
12
+ one process serialized a whole number as `1.0` and another as `1`. That causes
13
+ avoidable cache misses, duplicate work, and noisy version-control diffs.
14
+
15
+ Visual Brief Guard converts nested hashes into a canonical form before JSON
16
+ serialization. Keys are converted to strings and sorted recursively, arrays
17
+ retain their meaningful order, and integral floats are normalized to integers.
18
+ The resulting JSON can be compared, versioned, or hashed consistently.
19
+
20
+ ## Installation
21
+
22
+ ```ruby
23
+ gem "visual_brief_guard"
24
+ ```
25
+
26
+ Then run:
27
+
28
+ ```text
29
+ bundle install
30
+ ```
31
+
32
+ ## Usage
33
+
34
+ ```ruby
35
+ require "visual_brief_guard"
36
+
37
+ brief = {
38
+ scene: "A ceramic mug on a clean studio surface",
39
+ aspect_ratio: "4:5",
40
+ color_palette: "warm neutrals",
41
+ reference_tags: ["mug", "front-view"],
42
+ constraints: {
43
+ width: 1200.0,
44
+ height: 1500
45
+ }
46
+ }
47
+
48
+ VisualBriefGuard.validate!(brief)
49
+ json = VisualBriefGuard.canonical_json(brief)
50
+ fingerprint = VisualBriefGuard.fingerprint(brief)
51
+ VisualBriefGuard.export(brief, "brief.json")
52
+ ```
53
+
54
+ The required fields are `scene`, `aspect_ratio`, `color_palette`, and
55
+ `reference_tags`. Optional fields are `schema_version`, `constraints`, and
56
+ `notes`. Unknown fields are rejected so transient data such as timestamps
57
+ cannot silently change an otherwise stable fingerprint.
58
+
59
+ ## Validation before serialization
60
+
61
+ Validation runs before canonicalization or digest calculation. This order
62
+ matters because a cryptographic digest can represent invalid data just as
63
+ reliably as valid data. A malformed brief should therefore fail at the
64
+ boundary, rather than receive a fingerprint that looks trustworthy.
65
+
66
+ `validate!` raises `VisualBriefGuard::InvalidBrief` with a specific reason when
67
+ the input is not a hash, a required field is missing or empty,
68
+ `reference_tags` is not an array, or an unexpected field is present. Calling
69
+ code can rescue that one exception type and return the message to an editor
70
+ without treating implementation errors as ordinary validation failures.
71
+
72
+ ```ruby
73
+ begin
74
+ VisualBriefGuard.fingerprint(candidate)
75
+ rescue VisualBriefGuard::InvalidBrief => error
76
+ warn "Brief rejected: #{error.message}"
77
+ end
78
+ ```
79
+
80
+ The field policy is intentionally explicit. Silently accepting every new key
81
+ would allow request IDs, timestamps, or UI state to alter a digest even though
82
+ those values do not describe creative intent. Add a field to the allowed
83
+ schema only when it should be part of the durable brief.
84
+
85
+ ## Canonicalization rules
86
+
87
+ Ruby hashes retain insertion order, but that order often reflects how data
88
+ arrived rather than what it means. Visual Brief Guard converts keys to
89
+ strings, sorts them at every nesting level, and emits compact JSON. Arrays
90
+ remain ordered because their order may be meaningful; reference priority, for
91
+ example, should not be rearranged automatically.
92
+
93
+ An integral float such as `1200.0` becomes `1200`. A non-integral value remains
94
+ a float. This small normalization prevents equivalent dimensions from
95
+ producing different bytes while preserving actual fractional values.
96
+
97
+ The SHA-256 fingerprint is calculated from this canonical JSON. It is useful
98
+ as a cache key, duplicate detector, or immutable audit reference. It is not a
99
+ security signature and does not prove who created or approved a brief.
100
+
101
+ ## Schema evolution
102
+
103
+ Treat the optional `schema_version` field as part of the canonical data when a
104
+ workflow begins evolving. Adding a new required field or changing the meaning
105
+ of an existing field should normally increment that version. Older records can
106
+ then be migrated deliberately instead of being reinterpreted silently.
107
+
108
+ Defaults also need care. If a future release adds an optional lighting field,
109
+ decide whether an omitted value and an explicit default should share a
110
+ fingerprint. Normalize the default before hashing when they are semantically
111
+ equivalent; leave them distinct when omission carries meaning. Tests should
112
+ capture that decision so a later refactor cannot change it accidentally.
113
+
114
+ ## Test the invariants
115
+
116
+ The most useful tests for deterministic serialization are not limited to the
117
+ happy path. A robust suite should verify that shuffled key insertion order
118
+ produces the same digest, malformed data fails before hashing, repeated
119
+ canonicalization is stable, and unknown fields are either rejected or
120
+ explicitly added to the schema.
121
+
122
+ This gem includes Minitest coverage for those boundaries:
123
+
124
+ ```text
125
+ ruby -Ilib test/visual_brief_guard_test.rb
126
+ ```
127
+
128
+ ## Workflow boundary
129
+
130
+ This library stops at a validated JSON artifact. It does not call an image
131
+ generation API and cannot judge whether a visual result matches the creative
132
+ direction. That separation is intentional: deterministic input validation and
133
+ creative output review are different responsibilities.
134
+
135
+ After review, a person can use the exported description in a separate
136
+ browser-based workspace such as the
137
+ [Seedream 5.0 Pro AI Image Generator](https://seedream50.pro/) for text-to-image,
138
+ image-to-image, sketch-guided, or multi-reference exploration. This is a manual
139
+ handoff to an independent tool, not a programmatic integration.
140
+
141
+ Stable input does not guarantee identical generated images because downstream
142
+ systems may be stochastic. It does provide a dependable record of intent: if
143
+ two accepted briefs have the same canonical fingerprint, their validated data
144
+ is structurally identical.
145
+
146
+ ## License
147
+
148
+ MIT
@@ -0,0 +1,3 @@
1
+ module VisualBriefGuard
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,77 @@
1
+ require "digest"
2
+ require "json"
3
+
4
+ require_relative "visual_brief_guard/version"
5
+
6
+ module VisualBriefGuard
7
+ REQUIRED_KEYS = %w[
8
+ scene
9
+ aspect_ratio
10
+ color_palette
11
+ reference_tags
12
+ ].freeze
13
+
14
+ ALLOWED_KEYS = (REQUIRED_KEYS + %w[schema_version constraints notes]).freeze
15
+
16
+ class InvalidBrief < ArgumentError; end
17
+
18
+ module_function
19
+
20
+ def validate!(brief)
21
+ raise InvalidBrief, "brief must be a Hash" unless brief.is_a?(Hash)
22
+
23
+ string_keys = brief.transform_keys(&:to_s)
24
+ missing = REQUIRED_KEYS.reject do |key|
25
+ string_keys.key?(key) && !blank?(string_keys[key])
26
+ end
27
+ raise InvalidBrief, "missing required fields: #{missing.join(', ')}" unless missing.empty?
28
+
29
+ unknown = string_keys.keys - ALLOWED_KEYS
30
+ raise InvalidBrief, "unknown fields: #{unknown.sort.join(', ')}" unless unknown.empty?
31
+
32
+ unless string_keys["reference_tags"].is_a?(Array)
33
+ raise InvalidBrief, "reference_tags must be an Array"
34
+ end
35
+
36
+ true
37
+ end
38
+
39
+ def canonical(brief)
40
+ validate!(brief)
41
+ canonicalize(brief.transform_keys(&:to_s))
42
+ end
43
+
44
+ def canonical_json(brief)
45
+ JSON.generate(canonical(brief))
46
+ end
47
+
48
+ def fingerprint(brief)
49
+ Digest::SHA256.hexdigest(canonical_json(brief))
50
+ end
51
+
52
+ def export(brief, path)
53
+ File.write(path, "#{canonical_json(brief)}\n")
54
+ path
55
+ end
56
+
57
+ def canonicalize(value)
58
+ case value
59
+ when Hash
60
+ value.transform_keys(&:to_s)
61
+ .sort_by { |key, _| key }
62
+ .to_h { |key, item| [key, canonicalize(item)] }
63
+ when Array
64
+ value.map { |item| canonicalize(item) }
65
+ when Float
66
+ value.finite? && value == value.to_i ? value.to_i : value
67
+ else
68
+ value
69
+ end
70
+ end
71
+ private_class_method :canonicalize
72
+
73
+ def blank?(value)
74
+ value.nil? || (value.respond_to?(:empty?) && value.empty?)
75
+ end
76
+ private_class_method :blank?
77
+ end
metadata ADDED
@@ -0,0 +1,49 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: visual_brief_guard
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Martyn Foster
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies: []
12
+ description: A small Ruby library for validating visual brief schemas, canonicalizing
13
+ nested data, exporting stable JSON, and calculating reproducible SHA-256 fingerprints.
14
+ email:
15
+ - FosterMartyn735@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - CHANGELOG.md
21
+ - LICENSE
22
+ - README.md
23
+ - lib/visual_brief_guard.rb
24
+ - lib/visual_brief_guard/version.rb
25
+ homepage: https://seedream50.pro/
26
+ licenses:
27
+ - MIT
28
+ metadata:
29
+ homepage_uri: https://seedream50.pro/
30
+ source_code_uri: https://github.com/FosterMartyn735/visual_brief_guard
31
+ changelog_uri: https://github.com/FosterMartyn735/visual_brief_guard/blob/main/CHANGELOG.md
32
+ rdoc_options: []
33
+ require_paths:
34
+ - lib
35
+ required_ruby_version: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: 3.1.0
40
+ required_rubygems_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ requirements: []
46
+ rubygems_version: 4.0.10
47
+ specification_version: 4
48
+ summary: Validate and fingerprint deterministic visual brief hashes.
49
+ test_files: []