Add Legendary Moses card and production guidance

This commit is contained in:
2026-09-17 09:26:45 -07:00
parent ee0113442d
commit 5e49435127
514 changed files with 9974 additions and 13 deletions

View File

@@ -0,0 +1,35 @@
{
"schemaVersion": 1,
"cardId": "BP-001",
"title": "Moses",
"rarity": "Legendary",
"folderName": "BP-001-moses",
"history": [
{
"revision": "v01",
"path": "history/revisions/v01",
"status": "superseded-revision-record"
},
{
"revision": "v02",
"path": "history/revisions/v02",
"status": "superseded-revision-record"
},
{
"revision": "v03",
"path": "history/revisions/v03",
"status": "superseded-revision-record"
},
{
"revision": "v04",
"path": "history/revisions/v04",
"status": "superseded-revision-record"
}
],
"selectedRevision": "v05",
"stage": "approved",
"approval": {
"by": "user",
"note": "This looks great. This iteration is approved"
}
}

View File

@@ -0,0 +1,17 @@
# BP-001 — Moses — v01
Provisional Legendary illustration, direction **The Threshold**. Art direction and prompt by Astra medium; raster generated with the built-in image tool. Awaiting user review.
- `source/art-native.png`: unchanged generated native output.
- `source/prompt.txt`: exact generation prompt.
- `source/concepts.md`: alternatives, selected concept and bespoke blend direction.
- `source/generation.json`: tool/source provenance.
- `source/art-fit.json`: native size, uniform fit, crop and hashes.
- `source/art-master.png`: fitted 2000×2800 artwork.
- `review/art-review.png`: 1000×1400 art review.
- `review/art-thumbnail.png`: 250×350 focal readability review.
- `review/illustration-review.md`: strengths, risks and prospective overlap.
Reproduce fitted art and review sizes with `python3 source/fit-art.py`. This does not regenerate artwork or assemble card products. Native generation is stochastic; the exact original source is retained.
The complete hanging tent curtain is the proposed concrete foreground connection for later bespoke backing integration. No production backings, lettering, masks or printings exist yet.

View File

@@ -0,0 +1,22 @@
{
"cardId": "BP-001",
"title": "Moses",
"name": "MOSES",
"rarity": "Legendary",
"folderName": "BP-001-moses",
"artApproval": null,
"reference": "Deuteronomy 34:10",
"translation": "ESV",
"referenceDisplay": "Deuteronomy 34:10 \u2022 ESV",
"excerpt": "And there has not arisen a prophet since in Israel like Moses, whom the LORD knew face to face",
"direction": "The Threshold",
"artStatus": "Provisional illustration awaiting user review",
"layoutStatus": "Bespoke Legendary layout pending; no production assembly authorized",
"sceneConstraints": [
"Outside the tent of meeting after speaking with the Lord; divine cloud by the tent; Israelite camp recedes behind.",
"Calm, holy, weight-bearing; reverent intimacy, solemn responsibility, quiet authority, sacred awe.",
"No Red Sea or Sinai action; these belong to event cards."
],
"art": "in-progress/cards/BP-001-moses/revisions/v01/source/art-master.png",
"artSHA256": "5bda2e8e5610f4e47eb875d09c024208953c91eb204c0cb5871e853fc81f11b3"
}

View File

@@ -0,0 +1,48 @@
{
"schemaVersion": 1,
"cardId": "BP-001",
"folderName": "BP-001-moses",
"revision": "v01",
"stage": "art-review",
"approval": null,
"content": "card.json",
"files": {
"README.md": {
"sha256": "33e07c5325e22b883784a205cfd013d18db57474983e7aabd3b0ff39bd1e21f3"
},
"card.json": {
"sha256": "c84644aeea69fb47f4bd561035d9afc7f42f163e4aa47d11b876a1ef93cdb1fb"
},
"source/art-fit.json": {
"sha256": "e35c43cd69fdcb82f9eaade078829d8a6fef867744b499ddda16ae1897ac41f6"
},
"source/art-master.png": {
"sha256": "5bda2e8e5610f4e47eb875d09c024208953c91eb204c0cb5871e853fc81f11b3"
},
"source/art-native.png": {
"sha256": "722b61d2beef84751675b98a32fa1b218583260107e4712432979c0c67a11615"
},
"source/concepts.md": {
"sha256": "ad904bab23d6cf4a1362007456757c4ee6748f19c00adf4ee83db507a59e9412"
},
"source/fit-art.py": {
"sha256": "f9aea5aa6b192591db9db7c8d26c6d0a1ee272ab977ed3940891ca32c17acd07"
},
"source/generation.json": {
"sha256": "aed55cc70ecb79e9d92e58eabfd99af5cb54c60afd92527d0183fccd4383bbdd"
},
"source/prompt.txt": {
"sha256": "3029fdda719a4011d29b424be10e1f01e0a1893f912c7a35401bcf9e3f43a148"
},
"low/manifest.json": {
"sha256": "c6b4ddddd22832e4ea6e2201c12a90cf4a7a1d25594b5878f12c23f22987261b"
},
"med/manifest.json": {
"sha256": "e9cd197e91ccc2477ec18ccd62d903a29b166bc23b431dad8aa25ca60ef59ac6"
},
"high/manifest.json": {
"sha256": "7ec683bc1aaf3a6c3d941a9011c97c5681bd1f18a21a1ea359745f092bcf2b6a"
}
},
"compatibilityAliases": []
}

View File

@@ -0,0 +1,17 @@
{
"schemaVersion": 1,
"kind": "compact-card-revision",
"cardId": "BP-001",
"folderName": "BP-001-moses",
"revision": "v01",
"originalStage": "art-review",
"approval": null,
"reason": "superseded when v05 was accepted",
"originalManifest": "full-manifest.json",
"originalManifestSHA256": "a518aa6886d250d20fdee62315ca54fd14d2ef58026962dacb654cf477f09ef9",
"referenceResolution": "low",
"referenceImages": [
"review/art-reference.png"
],
"retention": "Text provenance plus one 500 x 700 card face per available printing; full current acceptance lives in artifacts/cards."
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 834 KiB

View File

@@ -0,0 +1,26 @@
# Moses v01 — provisional illustration review
Status: **art-review; no user artwork approval and no assembly**. Astra medium authored the concept and prompt. The built-in image tool rendered one provisional study. Inspected its returned native image and the saved thumbnail (250×350). The image-view tool failed sandbox initialization; thumbnail inspection used a JPEG transported from the saved review image. No GPU or moving-light review applies to this art-only study.
## What works
- The governing threshold idea reads immediately: Moses turns to the presence beside the tent, with the people’s camp behind him. Folded hands and a lowered gaze express responsibility and attention rather than triumph.
- Pose is coherent. Both hands belong to visible forearms; resting fingers and wrist overlap read plausibly. The robe has weight, shoulders connect clearly and no theatrical action competes with the face.
- Tent curtain, flowing robes, clouds, sky, mountains and broad ground forms are built from meaningful glass panes. Clothing divisions follow fold direction. Dark leading and the sapphire/burgundy/ivory color masses organize the thumbnail strongly.
- Cloud is luminous but non-anthropomorphic. No literal divine figure, horns, tablets, Sinai or Red Sea motif appears. Cloud light connects the face, inner garment and tent threshold.
- Tent opening is readable at normal and thumbnail scale. Rows of tents recede into the right background and establish the people Moses serves.
- Face and folded hands are well above a prospective lower-quarter verse region. The top-right sky offers title clearance under the David surrogate. Full-bleed lower scene remains complete.
## Risks and next refinement
- Face, hands and beard lean toward conventional detailed illustration: realistic wrinkles, veins and hair strands exceed the desired stylized pane modeling. Before art lock, consider a targeted edit simplifying skin into expressive bounded areas and quieting microtexture without altering expression, hand ownership or pose.
- Camp contains small figures, animal and vegetation details beyond what the scene needs. It remains subordinate but could simplify into fewer grouped panes.
- Cloud has scalloped bulb-like repetition; it works as assembled glass but could gain broader, less repetitive flowing shapes if revising. It is brighter than the face; assess whether its brightness competes with Moses on the composed card.
- Environmental surface is pronounced and also carries through the robe. Its grain remains below the leading at thumbnail scale; keep any future skin correction much gentler than the environment.
- This is a waist-to-feet cropped full-height presence, not a literal full-body image with visible feet. Lower robe continues through the bottom edge intentionally.
## Bespoke blend feasibility
The left burgundy curtain is visibly attached to the tent lintel and hangs to a complete hem. Its lower folds can later cross a backing’s left edge while remaining visibly attached above; this is a stronger structural overlap candidate than a detached cloud fragment. The adjacent luminous cloud can inform a curved panel boundary, but avoid wrapping decorative cloud around lettering. The exact panel shape is unresolved. Do not simply reuse David’s mantle geometry or cut off the curtain where a backing hides its base.
No frame, backing, text, masks, printings, finish coverage or harness fixtures were generated. Production assembly remains deferred until the user concludes the artwork loop.

View File

@@ -0,0 +1,24 @@
{
"source": "art-native.png",
"sourceDimensions": [
1060,
1484
],
"canvas": [
2000,
2800
],
"method": "uniform centered cover fit",
"scale": 1.8867924528301887,
"scaledDimensions": [
2000.0,
2800.0
],
"cropOffset": [
0.0,
0.0
],
"resampling": "Pillow LANCZOS",
"sourceSHA256": "722b61d2beef84751675b98a32fa1b218583260107e4712432979c0c67a11615",
"outputSHA256": "5bda2e8e5610f4e47eb875d09c024208953c91eb204c0cb5871e853fc81f11b3"
}

View File

@@ -0,0 +1,19 @@
# Moses — composition exploration
BP-001 / Moses / Legendary, catalogue identity confirmed from `docs/sanctification-master-card-catalog.xlsx`. The catalogue reserves Red Sea and Sinai actions for their event cards and describes Moses as “Authority with dependence.” The user supplied the exact verse excerpt; the reference spelling is normalized to Deuteronomy.
1. **The Threshold — selected.** Moses stands beside the tent opening, turned quietly toward the presence while the camp recedes behind. The lighting connects face, curtain and threshold. This most directly holds intimacy and responsibility in one still image.
2. **Returning to the people.** Moses steps from the tent toward the camp, looking back. Strong narrative movement, but departure becomes the action and weakens the requested calm.
3. **Held in the cloud.** A close portrait against an enclosing cloud and a very small camp. Iconic scale, but risks theatrical radiance and loses the tent/camp relationship.
## Bespoke Legendary direction
Explore the tent threshold as the later boundary between illustration and backing. A complete curtain attached to the tent can carry its hanging folds over a future backing edge; broad cloud panes can curve around that edge. This would connect sacred presence to the card’s physical composition without repeating David’s mantle. The curtain is the primary concrete overlap candidate; cloud is an atmospheric alternative, not a mandated special effect. Do not extract or assemble either until artwork approval and a composed-preview request.
## Provisional layout assumptions
No Moses tier template exists. David’s accepted bespoke layout is a clearance surrogate only: its title occupies the upper-right approximately x1048–1942/y58–365 on the 2000×2800 master, and verse backing begins around y2075 in x466–1942. Moses’s later layout must be bespoke, not a David clone. Generation reserves quiet upper sky and lower-right scene continuation; focal face, hands and threshold should remain above the lower quarter. Covered regions still receive complete full-bleed art. No guide overlay or final panel geometry is generated at this stage.
## Model and provenance
Art direction and prompt authored by Astra with medium reasoning, as requested. Raster generation uses the built-in image generation tool; the direction model is distinct from the image renderer. Exact generation prompt is retained in `prompt.txt`.

View File

@@ -0,0 +1,16 @@
#!/usr/bin/env python3
"""Reproduce only the fitted art and review sizes; never assemble a card."""
from pathlib import Path
from PIL import Image, ImageOps
import hashlib,json
root=Path(__file__).resolve().parent.parent
native=root/'source/art-native.png'
with Image.open(native) as source:
im=source.convert('RGB');w,h=im.size
scale=max(2000/w,2800/h)
art=ImageOps.fit(im,(2000,2800),Image.Resampling.LANCZOS,centering=(.5,.5))
art.save(root/'source/art-master.png')
for name,size in [('art-review.png',(1000,1400)),('art-thumbnail.png',(250,350))]:
art.resize(size,Image.Resampling.LANCZOS).save(root/'review'/name)
fit={'source':'art-native.png','sourceDimensions':[w,h],'canvas':[2000,2800],'method':'uniform centered cover fit','scale':scale,'scaledDimensions':[w*scale,h*scale],'cropOffset':[(w*scale-2000)/2,(h*scale-2800)/2],'resampling':'Pillow LANCZOS','sourceSHA256':hashlib.sha256(native.read_bytes()).hexdigest(),'outputSHA256':hashlib.sha256((root/'source/art-master.png').read_bytes()).hexdigest()}
(root/'source/art-fit.json').write_text(json.dumps(fit,indent=2)+'\n')

View File

@@ -0,0 +1,13 @@
{
"mode": "built-in imagegen",
"artDirectionModel": "gpt-6-astra",
"artDirectionReasoning": "medium",
"prompt": "prompt.txt",
"native": "art-native.png",
"originalGeneratedPath": "/home/dkzver/.codex/generated_images/01a0acd6-0b11-7932-a7f3-c69f609e9974/exec-d3da0af1-8b0e-49d3-a877-da339325b2c3.png",
"referenceImages": [],
"fit": "art-fit.json",
"reproduceFit": "python3 source/fit-art.py",
"generationReproducibility": "Exact prompt and native bytes retained; image generation is stochastic, fit and review resizing are deterministic.",
"approval": null
}

View File

@@ -0,0 +1,9 @@
Use case: historical-scene
Asset type: provisional base illustration for Sanctification Legendary card BP-001 Moses; full-bleed portrait 5:7 art only.
Primary request: Moses stands just outside the tent of meeting after speaking with the Lord. The divine cloud remains near the tent entrance; the Israelite camp recedes into the background. Reverent intimacy with God, solemn responsibility, quiet authority, sacred awe. His presence is weight-bearing and calm, not triumphant or theatrical.
Composition: One governing idea, the threshold between holy presence and responsibility for the people. Moses is a substantial upright three-quarter figure slightly right of center, head near 48% x / 28% y, shoulders below the upper fifth; face and hands fully readable in the middle field. His head turns gently toward the tent at left, eyes lowered in reflective attention, shoulders settled with the gravity of service. An elderly Near Eastern man with expressive lined face and a long pale beard, dressed in substantial deep sapphire and muted wine-colored folded robes, warm sand-colored inner garment. One hand rests quietly over the other near his waist, hands anatomically legible with clear forearm connections. No triumphant raised arms. The tent is directly behind and left of him, recognizable woven ochre walls and a deep dark opening, with one complete hanging entrance curtain attached at the upper-left and falling in broad weighty folds toward the lower-left. A restrained luminous ivory and pale gold cloud curves downward at the entrance beside him, built of broad interlocking glass forms. Its light touches the turned face and the nearer garment edge; its source is the holy cloud, not a literal divine figure. Distant rows of modest Israelite tents recede far behind to the right across quiet desert terrain, smaller and lower-contrast than Moses. Leave the topmost 10% as quiet sky and the lower-right quarter as naturally subdued ground/robe continuation that could later sit beneath a text backing; do not draw panels or blank spaces. Keep his face, both hands, and the tent threshold above the lowest quarter. The complete lower-left tent curtain and adjacent cloud create a natural connected silhouette for a future bespoke foreground overlap; this image itself is continuous, complete artwork.
Style/medium: Deliberate stained-glass construction, colored glass panes joined by coherent dark lead-like divisions. The panes build silhouettes and form from the outset, never a crack pattern pasted over a painting. Robes use long flowing curved panes following folds; beard and hair use purposeful expressive areas; face and hands retain fine expressive details inside a few carefully shaped glass areas, without heavy leading crossing every feature. Clouds are broad curved luminous panes, tent fabric folds long cohesive panes, terrain angular and flowing interlocking segments. No photographic skin or sky. Tactile lightly etched or brushed satin glass, diffuse pane-contained cloudy variation strongest in environmental panes, very gentle on face and hands. Varied pane size with quiet large areas and selective detail. Luminous deep sapphire, wine, muted turquoise, amber and ivory; tasteful gold accents and a cohesive warm cloud / cool camp lighting relationship. Legendary refinement comes from iconic composition, beautiful selective light, depth and restraint; no excess cracks or ornament. Soft bounded hue shifts and sparse shaped highlights inside selected panes, many panes quiet. Leading and major color shapes must organize the scene at thumbnail size.
Constraints: Full-bleed 5:7 artwork only, complete intentional scene even in areas a later backing could cover. No lettering, no scripture text, no card frame, no border, no backing panels, no corner ornaments, no watermark. No tablets, no Red Sea, no Sinai spectacle, no horns, no literal face/body of God, no halo disk. No triumphal hero pose, fantasy armor, lightning, neon glow, particles, glitter, dotted glints, uniform mosaic grid, random cracking or photorealism.

View File

@@ -0,0 +1,16 @@
# BP-001 — Moses — v02
**The Threshold — glass-language refinement.** Astra medium direction; built-in image edit. Composition retained from v01, with Moses and background translated into clearer stylized glass planes. Pending user artwork review.
- `review/art-review.png`: 1000×1400 review.
- `review/art-thumbnail.png`: 250×350 review.
- `review/illustration-review.md`: assessment and tradeoffs.
- `source/art-native.png`: unchanged generated output.
- `source/art-master.png`: fitted 2000×2800 illustration.
- `source/edit-prompt.txt`: exact edit instructions.
- `source/edit-reference.jpg`: actual conversation-visible edit input.
- `source/generation.json`: source/edit provenance and hashes.
- `source/art-fit.json`: uniform fit record.
- `source/refinement.md`: scope and input fallback explanation.
Run `python3 source/fit-art.py` from this revision to reproduce the fit and review sizes. Image editing itself is stochastic. No production layers or printings are assembled; v01 remains preserved.

View File

@@ -0,0 +1,27 @@
{
"cardId": "BP-001",
"title": "Moses",
"name": "MOSES",
"rarity": "Legendary",
"folderName": "BP-001-moses",
"artApproval": null,
"reference": "Deuteronomy 34:10",
"translation": "ESV",
"referenceDisplay": "Deuteronomy 34:10 \u2022 ESV",
"excerpt": "And there has not arisen a prophet since in Israel like Moses, whom the LORD knew face to face",
"direction": "The Threshold",
"artStatus": "Provisional v02 glass-language refinement awaiting user review",
"layoutStatus": "Bespoke Legendary layout pending; no production assembly authorized",
"sceneConstraints": [
"Outside the tent of meeting after speaking with the Lord; divine cloud by the tent; Israelite camp recedes behind.",
"Calm, holy, weight-bearing; reverent intimacy, solemn responsibility, quiet authority, sacred awe.",
"No Red Sea or Sinai action; these belong to event cards."
],
"compositionApproval": {
"by": "user",
"scope": "v01 composition only; visual rendering requested to change"
},
"revisionIntent": "Preserve composition; reduce figure and background realism into meaningful glass planes.",
"art": "in-progress/cards/BP-001-moses/revisions/v02/source/art-master.png",
"artSHA256": "ce37c2e61dc79b089ef69ed673253cd63912b9ef0d012798f8f0f2a9c9d85b88"
}

View File

@@ -0,0 +1,51 @@
{
"schemaVersion": 1,
"cardId": "BP-001",
"folderName": "BP-001-moses",
"revision": "v02",
"stage": "art-review",
"approval": null,
"content": "card.json",
"files": {
"README.md": {
"sha256": "4c1e8c66a5f78e4149e9306ccb14416f2b371d2b6d054984a750a330b966046b"
},
"card.json": {
"sha256": "872fc613c73be7628042efcc31cb005442e5d2b9c0513f34795df5f3db868e36"
},
"source/art-fit.json": {
"sha256": "1f32b29c1d506ab758358c8a109215152ee8aa41d01f02f43429da4afba734d9"
},
"source/art-master.png": {
"sha256": "ce37c2e61dc79b089ef69ed673253cd63912b9ef0d012798f8f0f2a9c9d85b88"
},
"source/art-native.png": {
"sha256": "145d11849d97e9007fb4066b167b1bfa53e608aeb025ef8783ab1a79fc59f71d"
},
"source/edit-prompt.txt": {
"sha256": "51ddbfbd53f9e85db0aa33d68e3b65c21a420390bdbab2fbe244aeebd53cb77b"
},
"source/edit-reference.jpg": {
"sha256": "a97ed77927b5aab8953f9931e51697964d6ee5b55db13a265cfdda6dcc07eaba"
},
"source/fit-art.py": {
"sha256": "f9aea5aa6b192591db9db7c8d26c6d0a1ee272ab977ed3940891ca32c17acd07"
},
"source/generation.json": {
"sha256": "312243f950f859e0c78584628c09d043f9c3a1863be8fcf59f36a16ea1de4f34"
},
"source/refinement.md": {
"sha256": "e5242baa1dc8837034a17e75c992fe648f95571df4869b13c3931732889c6636"
},
"low/manifest.json": {
"sha256": "c6b4ddddd22832e4ea6e2201c12a90cf4a7a1d25594b5878f12c23f22987261b"
},
"med/manifest.json": {
"sha256": "e9cd197e91ccc2477ec18ccd62d903a29b166bc23b431dad8aa25ca60ef59ac6"
},
"high/manifest.json": {
"sha256": "7ec683bc1aaf3a6c3d941a9011c97c5681bd1f18a21a1ea359745f092bcf2b6a"
}
},
"compatibilityAliases": []
}

View File

@@ -0,0 +1,17 @@
{
"schemaVersion": 1,
"kind": "compact-card-revision",
"cardId": "BP-001",
"folderName": "BP-001-moses",
"revision": "v02",
"originalStage": "art-review",
"approval": null,
"reason": "superseded when v05 was accepted",
"originalManifest": "full-manifest.json",
"originalManifestSHA256": "61a59ae93df4e9a3c1feb09922301f4f718b9302c744a062e53e016b26edd68d",
"referenceResolution": "low",
"referenceImages": [
"review/art-reference.png"
],
"retention": "Text provenance plus one 500 x 700 card face per available printing; full current acceptance lives in artifacts/cards."
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 839 KiB

View File

@@ -0,0 +1,24 @@
# Moses v02 — targeted glass-language refinement
**Art-review; user approval pending.** The user selected v01’s composition and requested less realism in Moses and the background. This v02 edits that artwork with Astra medium direction and the built-in image tool. Inspected the returned full native image and saved 250×350 thumbnail. Compared visually with the approved Calling of the First Disciples glass reference.
## Result
Realism is substantially reduced. Face, hair, beard, hands, camp, mountains and ground now read as designed glass forms rather than realistic illustration with incidental divisions. This is sufficient to present as a direct answer to the requested style correction, not a claim that the user has accepted it.
- Face is organized into amber, gold and umber planes. Brow, eyelid, nose and mouth preserve the quiet left-facing expression; realistic wrinkles and skin texture have largely gone.
- Hair and beard use broad ivory and cool-gray flowing locks with dark boundaries. Individual realistic hairs are gone. Their curved segmentation remains expressive.
- Folded hands preserve visible forearm ownership and finger contours. Veins and wrinkles have been removed; broad warm planes model their overlap. Some lighter internal hand shapes remain painterly, but no longer read as realistic skin. No obvious extra hand or disconnected wrist is visible.
- Robes and curtain retain the original color arrangement and flow. Their panes have stronger leading and fewer realistic cloth shadows. The curtain remains visibly attached at the lintel and has its complete lower hem.
- Camp is simplified into grouped tents and small graphic silhouettes. People and an animal remain as subordinate shapes rather than detailed miniatures. Mountains and ground now consist of explicit bounded planes. The more graphic foliage forms are repetitive in places but remain secondary.
- Cloud remains luminous, non-anthropomorphic and located at the tent entrance. Its curved panes connect the light to Moses. No new symbols or narrative events appeared.
- Pose, framing, tent relationship, camp recession, title clearance and lower-quarter clearance remain substantially intact. The future connected curtain overlap is still viable.
- At thumbnail scale, sapphire figure, burgundy curtain and bright cloud stay distinct; pane boundaries now visibly organize the full scene. The focal face remains readable.
## Tradeoffs / remaining review points
Leading is heavier and more graphic than v01 and somewhat stronger than the Disciples reference. This makes the style correction decisive, but the user may prefer a slightly gentler balance. Face geometry and beard locks are intentionally simplified, while a few shaded transitions remain inside panes. Surface texture is still visible throughout robes and environment; it does not replace the panes. The cloud is still a bright repeated scalloped form, retained to preserve the accepted composition. No further edit was made without review.
Direct native-path input failed because the filesystem sandbox helper could not initialize. The successful edit used the already-visible 700×980 JPEG reference; the exact reference is retained in `source/edit-reference.jpg`, with provenance and hashes in `source/generation.json`. Native output is saved unchanged. Fitting to 2000×2800 and review resizing are recorded and reproducible.
No frame, backing, text, mask, printing, finish or harness output was built. v01 remains preserved. No GPU or moving-light review applies to this art-only pass.

View File

@@ -0,0 +1,24 @@
{
"source": "art-native.png",
"sourceDimensions": [
1060,
1484
],
"canvas": [
2000,
2800
],
"method": "uniform centered cover fit",
"scale": 1.8867924528301887,
"scaledDimensions": [
2000.0,
2800.0
],
"cropOffset": [
0.0,
0.0
],
"resampling": "Pillow LANCZOS",
"sourceSHA256": "145d11849d97e9007fb4066b167b1bfa53e608aeb025ef8783ab1a79fc59f71d",
"outputSHA256": "ce37c2e61dc79b089ef69ed673253cd63912b9ef0d012798f8f0f2a9c9d85b88"
}

View File

@@ -0,0 +1,19 @@
Use case: style-transfer
Asset type: Sanctification Legendary Moses BP-001 v02, targeted edit of the supplied v01 artwork.
Input image: the existing Moses illustration is the edit target. Preserve its composition and identity exactly; change its visual construction throughout.
Primary request: Translate this entire illustration much more decisively into designed stained and painted glass. The present Moses and camp are too realistic. This is not a new scene and not a crack overlay. Rebuild the visual rendering inside the existing silhouettes as meaningful colored glass pieces joined by dark leading, using stylized expressive forms, broad bounded areas of color, and selectively intricate shards. Make the difference unmistakable at normal viewing size.
Keep unchanged: portrait 5:7 canvas; Moses's size and position, elderly Near Eastern identity, left-facing reflective expression, lowered eyes, quiet authority and burdened calm; exact head turn and body stance; both folded hands and their clear forearm ownership; sapphire, burgundy and ivory robe arrangement; tent at left and its dark doorway; complete attached burgundy curtain with its hanging folds and visible hem; luminous non-anthropomorphic ivory/gold cloud; distant Israelite camp to the right; mountain horizon; warm presence / cool camp lighting relationship. Preserve the curtain’s continuity for a future foreground overlap. No new action, props or symbols.
Required changes:
- Moses’s face: designed warm amber, cream and umber facial planes with clear brow, eyelid, nose and mouth. Simplify wrinkles and all flesh shading into a small number of expressive bounded shapes. Stylized flat glass anatomy, not realistic skin with lead lines. Gentle transitions only inside a few panes. Retain age through dignified contour and expression, not skin pores or creases.
- Hands: preserve the existing folded gesture and plausible fingers but reduce veins, tendons, wrinkles and realistic modeling. Distinct legible finger silhouettes with a few warm bounded planes and restrained painted-glass detail, no arbitrary black grid across fingers.
- Hair and beard: replace realistic individual strands with flowing ivory, pale gold and cool gray locks made of expressive connected glass segments. Broad contour rhythms and selected fine painted marks only, no photorealistic wisps.
- Robes and curtain: keep their existing flowing structure, enrich chosen folds with meaningful smaller jewel panes, but reduce continuous realistic fabric modeling and grain. Broad quiet glass areas should alternate with detail.
- Camp and land: decisively simplify tiny people, animals, foliage, tent fabric detail and realistic rocks into grouped colored tent silhouettes and interlocking ground panes. Tents remain clearly tents, decreasing in size into the distance, but not a realistic miniature landscape. Mountains become broad purposeful angular blue/violet panes with crisp boundary changes and little atmospheric softness.
- Sky and cloud: coherent broad curved glass shapes, selected intricate shards near luminous cloud edges and sky accents, substantial quiet panes elsewhere. Avoid uniform tessellation. Keep the luminous cloud beside the tent without introducing God as a figure.
- Surface: tactile lightly brushed or etched glass with restrained cloudy satin variation contained within panes, not a uniform noise overlay. Very gentle texture on face/hands. Sparse purposeful shaped highlights, never pervasive sparkles.
Overall: unmistakable stained-glass image construction throughout the focal figure AND environment, with dark leading describing real form and material changes. Expressive sacred illustration, stylized and iconic rather than realistic painting. A few intricate regions are welcome; not every surface should have the same shard size or density. Preserve depth through overlap, scale and color hierarchy rather than realistic gradients.
No text, letters, card frame, backing, border, watermark, halo disk, horns, tablets, lightning, particles, photographic skin, realistic landscape rendering, crack-pattern overlay, uniform polygon grid or all-over micro-mosaic.

View File

@@ -0,0 +1,16 @@
#!/usr/bin/env python3
"""Reproduce only the fitted art and review sizes; never assemble a card."""
from pathlib import Path
from PIL import Image, ImageOps
import hashlib,json
root=Path(__file__).resolve().parent.parent
native=root/'source/art-native.png'
with Image.open(native) as source:
im=source.convert('RGB');w,h=im.size
scale=max(2000/w,2800/h)
art=ImageOps.fit(im,(2000,2800),Image.Resampling.LANCZOS,centering=(.5,.5))
art.save(root/'source/art-master.png')
for name,size in [('art-review.png',(1000,1400)),('art-thumbnail.png',(250,350))]:
art.resize(size,Image.Resampling.LANCZOS).save(root/'review'/name)
fit={'source':'art-native.png','sourceDimensions':[w,h],'canvas':[2000,2800],'method':'uniform centered cover fit','scale':scale,'scaledDimensions':[w*scale,h*scale],'cropOffset':[(w*scale-2000)/2,(h*scale-2800)/2],'resampling':'Pillow LANCZOS','sourceSHA256':hashlib.sha256(native.read_bytes()).hexdigest(),'outputSHA256':hashlib.sha256((root/'source/art-master.png').read_bytes()).hexdigest()}
(root/'source/art-fit.json').write_text(json.dumps(fit,indent=2)+'\n')

View File

@@ -0,0 +1,16 @@
{
"mode": "built-in imagegen edit",
"artDirectionModel": "gpt-6-astra",
"artDirectionReasoning": "medium",
"prompt": "edit-prompt.txt",
"native": "art-native.png",
"originalGeneratedPath": "/home/dkzver/.codex/generated_images/01a0acd6-0b11-7932-a7f3-c69f609e9974/exec-1298335b-5086-41b2-ad33-6d19aad9c690.png",
"reference": "edit-reference.jpg",
"referenceSHA256": "a97ed77927b5aab8953f9931e51697964d6ee5b55db13a265cfdda6dcc07eaba",
"referenceProvenance": "700x980 JPEG quality 82 made from v01 source/art-native.png and shown in conversation; direct native-path edit failed sandbox initialization, so num_last_images_to_include=1 was used.",
"priorNative": "../../v01/source/art-native.png",
"priorNativeSHA256": "722b61d2beef84751675b98a32fa1b218583260107e4712432979c0c67a11615",
"fit": "art-fit.json",
"generationReproducibility": "Stochastic targeted edit; exact prompt, actual displayed reference and native output retained; fit and review sizes deterministic.",
"approval": null
}

View File

@@ -0,0 +1,5 @@
# Moses v02 — glass language refinement
The user selected v01 composition but requested less realism in Moses and the background. Preserve the silhouette, pose, expression, hand ownership, tent/cloud/camp staging and future connected curtain overlap. Translate the figure and environment into deliberate glass construction with broader expressive planes, selected intricate shards, and restrained within-pane modeling. No production assembly is authorized.
Astra medium authored the targeted edit prompt. Direct file-path edit input failed because the filesystem sandbox helper could not initialize. The built-in tool therefore edits the most recent visible conversation image: a 700×980 JPEG preview of v01’s exact native output, saved as `edit-reference.jpg`. This is the inspected reference, not a new composition. The full-resolution v01 native remains preserved in its prior revision.

View File

@@ -0,0 +1,20 @@
# BP-001 — Moses — v03
**The Threshold — Legendary intricacy.** Astra medium direction; built-in targeted image edit. Retains v02’s stylized figure and composition while adding curated glass detail to cloud, curtain, robes, tent and selected environment forms. Artwork selected for bespoke layout review.
- `review/art-review.png`: 1000×1400 review.
- `review/art-thumbnail.png`: 250×350 review.
- `review/illustration-review.md`: assessment and tradeoffs.
- `source/art-native.png`: unchanged generated output.
- `source/art-master.png`: fitted 2000×2800 illustration.
- `source/edit-prompt.txt`: exact edit prompt.
- `source/edit-reference.jpg`: actual displayed v02 edit input.
- `source/generation.json`: source/edit provenance and hashes.
- `source/art-fit.json`: uniform fit record.
- `source/refinement.md`: scope and input fallback.
Run python3 source/fit-art.py from this revision to reproduce the fitted art and review sizes. Image generation is stochastic. Earlier revisions remain preserved.
## Provisional Legendary composition
[Threshold composition preview](review/layout-preview-v1/README.md) tests a bespoke gold/lapis frame, upper-right title plaque, lower textile backing, real P052 lettering, and an authored attached-curtain foreground. This is an explicitly requested layout preview only. No production printings, finish masks, text masks, or harness fixture were generated.

View File

@@ -0,0 +1,48 @@
{
"cardId": "BP-001",
"title": "Moses",
"name": "MOSES",
"rarity": "Legendary",
"folderName": "BP-001-moses",
"artApproval": {
"by": "user",
"note": "I like the art a lot.",
"scope": "BP-001 Moses v03 illustration; layout preview authorized separately."
},
"reference": "Deuteronomy 34:10",
"translation": "ESV",
"referenceDisplay": "Deuteronomy 34:10 \u2022 ESV",
"excerpt": "And there has not arisen a prophet since in Israel like Moses, whom the LORD knew face to face",
"direction": "The Threshold",
"artStatus": "v03 illustration selected for bespoke layout review",
"layoutStatus": "Provisional Legendary compact textile composition under review/layout-preview-v2; production assembly not authorized.",
"sceneConstraints": [
"Outside the tent of meeting after speaking with the Lord; divine cloud by the tent; Israelite camp recedes behind.",
"Calm, holy, weight-bearing; reverent intimacy, solemn responsibility, quiet authority, sacred awe.",
"No Red Sea or Sinai action; these belong to event cards."
],
"compositionApproval": {
"by": "user",
"scope": "v01 composition only; visual rendering requested to change"
},
"revisionIntent": "Preserve v02 glass stylization and composition; add curated hierarchical glass intricacy for Legendary presence.",
"art": "in-progress/cards/BP-001-moses/revisions/v03/source/art-master.png",
"artSHA256": "73f933cf34719dfda09b719376948bcf072bec504a88329b5a22e4ac1ed76d85",
"layoutCandidate": {
"id": "BP-001-moses-threshold-v2",
"preview": "review/layout-preview-v2/composed-preview-1000.png",
"governingIdea": "Tent threshold with compact woven screen",
"foreground": "Attached burgundy curtain crossing the frame and meeting the stepped left edge of the verse backing",
"typography": {
"titleSize": 138,
"verseSize": 72,
"verseBaselineGap": 104,
"referenceSize": 57,
"verseCenterGaps": [
90,
91
]
},
"reviewState": "pending user review"
}
}

View File

@@ -0,0 +1,51 @@
{
"schemaVersion": 1,
"cardId": "BP-001",
"folderName": "BP-001-moses",
"revision": "v03",
"stage": "art-review",
"approval": null,
"content": "card.json",
"files": {
"README.md": {
"sha256": "d8174612d3d3455ad9a5ff972cba19908df343bab5d9a15d25f641be1f8fbef2"
},
"card.json": {
"sha256": "678d59f142b18b6a589b524968c65eb06525bd6137c8de720f9090730ef74317"
},
"source/art-fit.json": {
"sha256": "f06f3666c0add4fd52084a0998d9e48146ec84e0f04e51dddcfc5ca2aa396388"
},
"source/art-master.png": {
"sha256": "73f933cf34719dfda09b719376948bcf072bec504a88329b5a22e4ac1ed76d85"
},
"source/art-native.png": {
"sha256": "ab234b91b260dba3b1a6973d765fc987e59339c21ec790d0ce51528e8fd98157"
},
"source/edit-prompt.txt": {
"sha256": "0bd8e3c5c40d59dd6f1f9caf9094371d0c3f52748e48a9cbd924609da1205e5a"
},
"source/edit-reference.jpg": {
"sha256": "0f6754052e7e739cf00a8f8360cb922112b2aa8501b8adfe981d9544e314108f"
},
"source/fit-art.py": {
"sha256": "f9aea5aa6b192591db9db7c8d26c6d0a1ee272ab977ed3940891ca32c17acd07"
},
"source/generation.json": {
"sha256": "bf4cd2254dc45d51c79a5d29abe40613a9a8530fbf17a02d60448cf64a5a0e36"
},
"source/refinement.md": {
"sha256": "ea3a4f27292be136543286d4d67d7780ceeb4fb986992f428736de749de6ff4d"
},
"low/manifest.json": {
"sha256": "c6b4ddddd22832e4ea6e2201c12a90cf4a7a1d25594b5878f12c23f22987261b"
},
"med/manifest.json": {
"sha256": "e9cd197e91ccc2477ec18ccd62d903a29b166bc23b431dad8aa25ca60ef59ac6"
},
"high/manifest.json": {
"sha256": "7ec683bc1aaf3a6c3d941a9011c97c5681bd1f18a21a1ea359745f092bcf2b6a"
}
},
"compatibilityAliases": []
}

View File

@@ -0,0 +1,17 @@
{
"schemaVersion": 1,
"kind": "compact-card-revision",
"cardId": "BP-001",
"folderName": "BP-001-moses",
"revision": "v03",
"originalStage": "art-review",
"approval": null,
"reason": "superseded when v05 was accepted",
"originalManifest": "full-manifest.json",
"originalManifestSHA256": "b0b5443349cab2bdc9705aa388e45565ab60ed467d4c134f936b87bd274d0029",
"referenceResolution": "low",
"referenceImages": [
"review/art-reference.png"
],
"retention": "Text provenance plus one 500 x 700 card face per available printing; full current acceptance lives in artifacts/cards."
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 877 KiB

View File

@@ -0,0 +1,24 @@
# Moses v03 — Legendary intricacy review
**Art-review; approval pending.** Astra medium direction, built-in image edit of v02. Inspected the returned full native image and the saved 250×350 thumbnail. This pass answers the user’s request for greater Legendary intricacy while retaining v02’s reduction in realism.
## Assessment
The refinement succeeds at making selected parts more finely crafted without returning to realism or changing the governing composition. Moses’s head, lowered gaze, solemn expression, folded hands, silhouette and robe arrangement remain substantially intact. Tent/cloud/camp positions and the complete attached curtain remain stable.
- Cloud gains nested ivory/champagne curves and secondary panes; its large bright silhouette still reads from a distance. It remains non-anthropomorphic.
- Curtain has finer tapering divisions that follow its folds, plus a narrow geometric glass border and complete patterned hem. Its visible attachment to the tent is maintained, retaining future foreground-overlap feasibility.
- Sapphire robes gain selected finer blue/turquoise planes within major folds. Warm trim has small triangular/elongated glass rhythms, stronger near the cuffs and opening. Broad blue and burgundy masses remain dominant.
- Tent lintel and supports gain small facets and lead joints. Sky light transition, mountain planes and some foreground ground paths gain secondary divisions while the camp remains simple and subordinate.
- Broad amber facial planes, expressive ivory beard locks and simplified hands are preserved. No realistic hair strands, veins or wrinkles return. Both wrists and hands remain visibly connected; no obvious new finger/anatomy defect appears.
- At thumbnail size, the bright cloud, dark tent opening and sapphire figure still establish the hierarchy. New detail reads mainly as richer material rather than a change in scene or action.
## Tradeoffs
This is visibly denser than v02, especially along the cloud column and lower robe. The primary/secondary lead hierarchy exists, but some new lines are still fairly strong; cloud curves and triangular trim repeat. Surface variation is also lively in sky and cloth. These are the areas to quiet if the user finds the new richness excessive. The gold trim is more noticeable, though confined to existing edges rather than spread across the scene. Face and hands remain comparatively simple, as requested.
This is an art candidate for user review, not artwork approval. No composition, text placement, mask, finish, printing or moving-light validation was performed.
## Provenance and retention
The edit used only the most recent visible 900×1260 JPEG reference of v02 (`source/edit-reference.jpg`). Native-path reads remain unavailable because of the sandbox helper initialization error. Prompt, exact displayed reference, generated native bytes and hashes are retained. Fit and review resizing are deterministic via `source/fit-art.py`. v01 and v02 are preserved.

View File

@@ -0,0 +1,28 @@
# Moses — threshold of the Presence
Provisional Legendary Normal composition by Astra Medium. This candidate makes the **pillar of cloud** the shared gesture between illustration and card: its crown passes in front of the name field, and its substantial lower lobes enter the verse field. The original attached curtain continues down the left edge. The panels sit behind those materials instead of advertising a specially cut panel corner.
- [Full preview, 1000 px](composed-preview-1000.png)
- [Delivery preview, 500 px](composed-preview-500.png)
- [V2 / Astra v3 comparison](v2-v3-comparison.png)
- [Cloud interaction details](cloud-interaction-detail.png)
- [Layer-order comparison](curtain-comparison.png)
- [Editable composition](composed-preview.svg)
- [Builder](build-preview.py)
- [Validation](preview-validation.json)
## Design
The story here is intimate access to the divine Presence at the tent threshold. The cloud supplies the luminous ivory, champagne and opal silhouette; the tent supplies the burgundy and gold material. Both belong to Moses's existing scene. A restrained gold enclosure and warm ivory fields let those illustrated forms carry the identity. No David mantle or leaf motif is borrowed.
The verse is set in three substantial lines to the right of the cloud, preserving its exact wording without quotation marks. The text deliberately occupies the unobstructed part of the backing. The title remains MOSES; the reference remains Deuteronomy 34:10 • ESV. All lettering uses the repository P052 faces and is generated deterministically.
## Source and scope
The selected `../../source/art-master.png` remains unchanged, at its original 2000 × 2800 coordinates. Foreground SVG clips re-show its existing cloud and curtain above the panels. A short seven-pixel dark lead arc closes the lowest cloud lobe where the original scene's rocky ground interrupted its silhouette. This is an editable contour treatment, not regenerated source art.
A separate generated curtain adaptation was attempted with built-in imagegen, but generation failed before reading the image because the environment's filesystem sandbox helper reported `bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted`. No generated donor exists or is used. This candidate is the completed SVG-native cloud direction.
Run `python3 in-progress/cards/BP-001-moses/revisions/v03/review/layout-preview-astra-v3/build-preview.py` from the repository. Inkscape, Fontconfig, Pillow and NumPy are required. The builder checks font hashes and glyph coverage, exact content, measured glyph bounds, opaque backing coverage, zero foreground/text overlap, and preservation of the source hash. Verse spacing is 77 master pixels from its upper rule and 86 pixels to the reference ink.
Static card-size and enlarged-join inspection completed. This remains a review candidate: no production printing exports, finish masks, harness fixtures, promotion or final approval were produced. The existing v1/v2 review folders and production assets are untouched.

View File

@@ -0,0 +1,185 @@
#!/usr/bin/env python3
"""Provisional BP-001 Moses Legendary composition; no production assembly."""
from pathlib import Path
import hashlib,json,os,subprocess
from xml.sax.saxutils import escape
import numpy as np
from PIL import Image,ImageDraw,ImageFont
P=Path(__file__).resolve().parent
REV=P.parents[1]
REPO=next(p for p in P.parents if (p/'docs/card-layer-pipeline.md').is_file())
ART=REV/'source/art-master.png'
FONTS=REPO/'fonts'
sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest()
art_hash=sha(ART)
font_manifest=json.loads((FONTS/'manifest.json').read_text())
font_files={'title':'P052-Bold.otf','verse':'SanctificationP052-Medium.otf','reference':'SanctificationP052-Medium.otf'}
for name in set(font_files.values()):
assert sha(FONTS/name)==font_manifest['files'][name]['sha256']
(P/'font-cache').mkdir(exist_ok=True)
(P/'fonts.conf').write_text(f'<fontconfig><dir>{FONTS}</dir><cachedir>{P/"font-cache"}</cachedir></fontconfig>')
env=dict(os.environ,FONTCONFIG_FILE=str(P/'fonts.conf'))
def ink(*args):return subprocess.check_output(['inkscape',*map(str,args)],env=env,text=True,stderr=subprocess.PIPE).strip()
def render(src,dst):ink(src,'--export-type=png',f'--export-filename={dst}','--export-width=2000','--export-height=2800')
def resolve(family,style):return Path(subprocess.check_output(['fc-match','-f','%{file}',f'{family}:style={style}'],env=env,text=True).strip()).resolve()
assert resolve('P052','Bold')==(FONTS/'P052-Bold.otf').resolve()
assert resolve('Sanctification P052','Medium')==(FONTS/'SanctificationP052-Medium.otf').resolve()
head='<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="2000" height="2800" viewBox="0 0 2000 2800">'
defs='''<defs>
<linearGradient id="paper" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#fbf3df"/><stop offset=".5" stop-color="#f4ead4"/><stop offset="1" stop-color="#e8d4b0"/></linearGradient>
<linearGradient id="gold" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#f0d47d"/><stop offset=".48" stop-color="#bd8b31"/><stop offset="1" stop-color="#71501e"/></linearGradient>
<pattern id="weave" width="44" height="44" patternUnits="userSpaceOnUse"><path d="M0 11H44 M0 33H44 M11 0V44 M33 0V44" stroke="#9d783d" stroke-width="1" opacity=".045"/><path d="M22 7l7 15-7 15-7-15Z" fill="none" stroke="#806438" stroke-width="1" opacity=".07"/></pattern>
<pattern id="lapis-band" width="34" height="34" patternUnits="userSpaceOnUse"><path d="M17 2L32 17 17 32 2 17Z" fill="#173e59" stroke="#d7b35a" stroke-width="3"/><circle cx="17" cy="17" r="4" fill="url(#gold)"/></pattern>
<clipPath id="curtain-clip"><path d="M-10-10 H770 C734 204 694 395 650 585 C610 770 566 972 522 1170 C474 1388 426 1604 378 1818 C338 2000 302 2190 286 2380 C276 2510 304 2640 412 2810 H-10Z"/></clipPath>
</defs>'''
base='<image xlink:href="../../source/art-master.png" width="2000" height="2800"/>'
frame='''<g id="legendary-frame">
<rect x="14" y="14" width="1972" height="2772" rx="34" fill="none" stroke="#2c2115" stroke-width="28"/>
<rect x="25" y="25" width="1950" height="2750" rx="27" fill="none" stroke="url(#gold)" stroke-width="14"/>
<rect x="43" y="43" width="1914" height="2714" rx="20" fill="none" stroke="#173e59" stroke-width="10"/>
<rect x="55" y="55" width="1890" height="2690" rx="15" fill="none" stroke="#d7b35a" stroke-width="4"/>
<path d="M68 260V68H260 M1740 68H1932V260 M68 2540V2732H260 M1740 2732H1932V2540" fill="none" stroke="#9b6d26" stroke-width="6"/>
<g fill="url(#gold)" stroke="#533810" stroke-width="3"><path d="M68 68l14 14-14 14-14-14Z"/><path d="M1932 68l14 14-14 14-14-14Z"/><path d="M68 2732l14 14-14 14-14-14Z"/><path d="M1932 2732l14 14-14 14-14-14Z"/></g>
<rect x="1906" y="430" width="27" height="255" fill="url(#lapis-band)" opacity=".95"/><rect x="1906" y="2115" width="27" height="255" fill="url(#lapis-band)" opacity=".95"/>
</g>'''
# The panels tuck behind the original tent and the pillar of cloud.
# No alteration, retouch, rescaling or regeneration of the selected source.
title_path='M700 62 H1900 Q1940 62 1940 102 V342 H700Z'
verse_path='M260 2092 H1895 Q1942 2092 1942 2139 V2712 Q1942 2758 1895 2758 H260Z'
title_inner='<path d="M710 82 H1898 Q1920 82 1920 104 V320 H710" fill="none" stroke="#bb9040" stroke-width="3"/><path d="M1070 301H1870" stroke="#c5a257" stroke-width="2"/>'
verse_inner='<path d="M280 2114H1893 Q1920 2114 1920 2141 V2708 Q1920 2736 1892 2736 H280" fill="none" stroke="#bb9040" stroke-width="3"/><path d="M827 2172H1815 M827 2700H1815" stroke="#c5a257" stroke-width="2"/>'
def panel(path,inner,ident):return f'<g id="{ident}"><path d="{path}" fill="url(#paper)" stroke="#62421e" stroke-width="9"/><path d="{path}" fill="url(#weave)" stroke="url(#gold)" stroke-width="4"/>{inner}</g>'
panels=panel(title_path,title_inner,'title-backing')+panel(verse_path,verse_inner,'verse-backing')
# Trace the visible source silhouette, including the whole cloud lower lobe.
# Upper crown is separately registered; both layers stay at source coordinates.
cloud_top='M0 0H714 Q752 43 748 83 Q805 43 852 48 C926 52 977 107 964 179 C1008 212 1010 268 983 320 Q964 353 929 370 L730 475 H0Z'
cloud_low='M0 400H352 V1780H765 L758 1985 Q750 2040 726 2108 L709 2220 L709 2314 Q695 2340 660 2362 Q651 2405 612 2434 Q579 2462 538 2473 Q507 2485 467 2488 Q401 2473 353 2410 L350 2633 Q286 2659 205 2635 Q101 2583 0 2612Z'
defs=defs.replace('</defs>',f'<clipPath id="cloud-top"><path d="{cloud_top}"/></clipPath><clipPath id="cloud-low"><path d="{cloud_low}"/></clipPath></defs>')
# Quiet fine-gold enclosure: motifs reside in the original curtain and cloud.
frame='''<g id="legendary-frame"><rect x="19" y="19" width="1962" height="2762" rx="28" fill="none" stroke="#251c15" stroke-width="21"/><rect x="23" y="23" width="1954" height="2754" rx="23" fill="none" stroke="url(#gold)" stroke-width="11"/><rect x="43" y="43" width="1914" height="2714" rx="15" fill="none" stroke="#e2bf6c" stroke-width="3"/></g>'''
curtain='<g id="presence-foreground">'+''.join(f'<g clip-path="url(#{clip})"><image xlink:href="../../source/art-master.png" width="2000" height="2800"/></g>' for clip in ['cloud-top','cloud-low'])+'<path d="M467 2488 Q401 2473 353 2410" fill="none" stroke="#2e2417" stroke-width="7" stroke-linecap="round"/></g>'
(P/'foreground-curtain.svg').write_text(head+defs+curtain+'</svg>');render(P/'foreground-curtain.svg',P/'foreground-curtain.png')
fg=Image.open(P/'foreground-curtain.png').convert('RGBA')
fa=np.asarray(fg.getchannel('A'))
assert fa.max()==255 and fa.min()==0
rows=[
('title','MOSES',1458,244,142,'P052',700),
('verse-0','And there has not arisen a',1320,2305,74,'Sanctification P052',500),
('verse-1','prophet since in Israel like Moses,',1320,2413,74,'Sanctification P052',500),
('verse-2','whom the LORD knew face to face',1320,2521,74,'Sanctification P052',500),
('reference','Deuteronomy 34:10 • ESV',1320,2650,57,'Sanctification P052',500),
]
for ident,content,*_ in rows:
fontfile=font_files['title' if ident=='title' else 'reference' if ident=='reference' else 'verse']
charset=subprocess.check_output(['fc-query','--format=%{charset}',str(FONTS/fontfile)],text=True).split()
ranges=[tuple(int(v,16) for v in token.split('-')) for token in charset]
assert all(any(r[0]<=ord(char)<=r[-1] for r in ranges) for char in content), (ident, 'missing glyph')
texts=''.join(f'<text id="{ident}" x="{x}" y="{y}" font-family="{family}" font-weight="{weight}" font-size="{size}" text-anchor="middle" fill="#263f50">{escape(text)}</text>' for ident,text,x,y,size,family,weight in rows)
(P/'text-preview.svg').write_text(head+texts+'</svg>');render(P/'text-preview.svg',P/'text-preview.png')
text_image=Image.open(P/'text-preview.png').convert('RGBA');ta=np.asarray(text_image.getchannel('A'))
assert not np.any((ta>0)&(fa>0))
(P/'border-backing-preview.svg').write_text(head+defs+frame+panels+'</svg>');render(P/'border-backing-preview.svg',P/'border-backing-preview.png')
backing=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A'))
checks=[];verse_bounds=[]
for ident,text,x,y,size,family,weight in rows:
node=next(v for v in texts.split('</text>') if f'id="{ident}"' in v)+'</text>'
src=P/(ident+'-ink.svg');dst=P/(ident+'-ink.png');src.write_text(head+node+'</svg>');render(src,dst)
alpha=np.asarray(Image.open(dst).convert('RGBA').getchannel('A'));ys,xs=np.where(alpha>0);assert len(xs)
bounds=[int(xs.min()),int(ys.min()),int(xs.max()+1),int(ys.max()+1)]
safe=[1050,90,1905,330] if ident=='title' else [730,2180,1900,2690]
assert safe[0]<=bounds[0] and safe[1]<=bounds[1] and bounds[2]<=safe[2] and bounds[3]<=safe[3],(ident,bounds,safe)
assert np.all(backing[alpha>0]==255),ident
assert not np.any((alpha>0)&(fa>0)),ident
checks.append({'id':ident,'text':text,'baseline':y,'font':family,'weight':weight,'size':size,'inkBounds':bounds,'safeBounds':safe,'glyphPixelsOutsideOpaqueBacking':0,'foregroundIntersectionPixels':0})
if ident.startswith('verse-'):verse_bounds.append(bounds)
assert ' '.join(c['text'] for c in checks if c['id'].startswith('verse-'))==json.loads((REV/'card.json').read_text())['excerpt']
assert checks[-1]['text']==json.loads((REV/'card.json').read_text())['referenceDisplay']
union=[min(b[0] for b in verse_bounds),min(b[1] for b in verse_bounds),max(b[2] for b in verse_bounds),max(b[3] for b in verse_bounds)]
upper=2174;ref_top=checks[-1]['inkBounds'][1];above=union[1]-upper;below=ref_top-union[3]
assert min(above,below)>=35 and abs(above-below)<=20,(above,below)
for name,include_curtain in [('composed-preview',True),('without-curtain',False)]:
body=base+frame+panels+(curtain if include_curtain else '')+texts
src=P/(name+'.svg');src.write_text(head+defs+body+'</svg>');render(src,P/(name+'-2000.png'))
master=Image.open(P/(name+'-2000.png')).convert('RGB')
master.resize((1000,1400),Image.Resampling.LANCZOS).save(P/(name+'-1000.png'))
master.resize((500,700),Image.Resampling.LANCZOS).save(P/(name+'-500.png'))
# Compare the structural effect of the attached curtain.
board=Image.new('RGB',(1000,750),'#11161c');draw=ImageDraw.Draw(board)
for i,(label,path) in enumerate([('Panels before foreground',P/'without-curtain-1000.png'),('Pillar of cloud over panels',P/'composed-preview-1000.png')]):
draw.text((i*500+12,12),label,fill='#f4ead4')
image=Image.open(path).convert('RGB');image.thumbnail((480,672));board.paste(image,(i*500+10,45))
board.save(P/'curtain-comparison.png')
# A focused junction comparison at master crop.
detail=Image.new('RGB',(1200,620),'#11161c');draw=ImageDraw.Draw(detail)
for i,(label,path) in enumerate([('Without foreground',P/'without-curtain-2000.png'),('Cloud and curtain over backing',P/'composed-preview-2000.png')]):
draw.text((i*600+12,12),label,fill='#f4ead4')
image=Image.open(path).convert('RGB').crop((0,1780,900,2800));image.thumbnail((575,545));detail.paste(image,(i*600+12,52))
detail.save(P/'curtain-junction-detail.png')
frame_alpha=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A'))
foreground_frame_overlap=int(np.count_nonzero((fa>0)&(frame_alpha>0)))
report={
'status':'passed',
'scope':'Provisional Legendary Normal — threshold of the Presence',
'canvas':[2000,2800],
'sourceArtwork':{'path':str(ART.relative_to(REPO)),'sha256':art_hash,'unchanged':sha(ART)==art_hash},
'template':{
'governingIdea':'The pillar of cloud enters the text field and crowns the name; the tent curtain remains attached',
'titleBackingPath':title_path,
'verseBackingPath':verse_path,
'compositionOrder':['base-art','frame-and-backings','attached-curtain-foreground','deterministic-text'],
'curtainContour':'foreground-curtain.svg',
'cloudUpperContour':cloud_top,
'cloudLowerContour':cloud_low,
'curtainAlphaBounds':list(Image.fromarray(fa).getbbox()),
'curtainFrameOrBackingOverlapPixels':foreground_frame_overlap,
},
'typography':{
'fontManifestSHA256':sha(FONTS/'manifest.json'),
'checks':checks,
'verseInkUnion':union,
'upperFlourishToVerseInk':above,
'verseInkToReferenceLineBox':below,
'centeringDifference':abs(above-below),
'lineBaselineGap':108,
},
'structuralChecks':{
'curtainTextIntersectionPixels':int(np.count_nonzero((ta>0)&(fa>0))),
'glyphPixelsOutsideOpaqueBacking':0,
'artworkSHA256Unchanged':True,
'fontGlyphCoverage':'passed',
'fontManifestHashes':'passed',
},
'notPerformed':['Borderless/Textless/Boundless composition','Production finish masks','Production text masks','Harness fixture installation','GPU moving-light validation','Final card approval'],
'staticReview':'Inspected at card size and enlarged cloud joins; provisional, pending user review',
'generationAttempt':{'tool':'built-in imagegen','status':'failed before generation','reason':'filesystem sandbox helper: bwrap loopback Failed RTM_NEWADDR: Operation not permitted','fallback':'Native SVG contours around existing source artwork; no generated donor'},
'foregroundAdaptation':'Cloud extracted in original coordinates. A 7px dark SVG lead arc closes the lowest lobe across its original ground occlusion; source file unchanged.'
}
(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n')
assert sha(ART)==art_hash
print(json.dumps({'status':'passed','preview':str((P/'composed-preview-1000.png').relative_to(REPO)),'curtainOverlapPixels':foreground_frame_overlap,'spacing':[above,below]},indent=2))
# Reproducible v2/v3 presentation and focused interaction review.
label_font=ImageFont.truetype(str(FONTS/'P052-Bold.otf'),26)
board=Image.new('RGB',(1500,1100),'#11161c');d=ImageDraw.Draw(board)
for i,(label,path) in enumerate([('V2 — textile edge',P.parent/'layout-preview-v2/composed-preview-1000.png'),('Astra v3 — pillar of cloud',P/'composed-preview-1000.png')]):
d.text((i*750+24,18),label,font=label_font,fill='#f4ead4')
im=Image.open(path).convert('RGB');im.thumbnail((730,1022));board.paste(im,(i*750+10,63))
board.save(P/'v2-v3-comparison.png')
master=Image.open(P/'composed-preview-2000.png').convert('RGB')
detail=Image.new('RGB',(1500,1050),'#11161c');d=ImageDraw.Draw(detail)
for label,box,origin,size in [('Cloud crowns the name',(570,0,1140,470),(22,65),(650,536)),('Pillar enters the verse field',(220,1960,850,2580),(795,65),(650,640))]:
d.text((origin[0],20),label,font=label_font,fill='#f4ead4')
im=master.crop(box);im.thumbnail(size);detail.paste(im,origin)
im=master.crop((250,2080,1980,2780));im.thumbnail((1450,320));detail.paste(im,(25,722))
detail.save(P/'cloud-interaction-detail.png')

View File

@@ -0,0 +1 @@
<fontconfig><dir>/home/dkzver/docker/sanctification/fonts</dir><cachedir>/home/dkzver/docker/sanctification/in-progress/cards/BP-001-moses/revisions/v03/review/layout-preview-astra-v3/font-cache</cachedir></fontconfig>

View File

@@ -0,0 +1,182 @@
{
"status": "passed",
"scope": "Provisional Legendary Normal \u2014 threshold of the Presence",
"canvas": [
2000,
2800
],
"sourceArtwork": {
"path": "in-progress/cards/BP-001-moses/revisions/v03/source/art-master.png",
"sha256": "73f933cf34719dfda09b719376948bcf072bec504a88329b5a22e4ac1ed76d85",
"unchanged": true
},
"template": {
"governingIdea": "The pillar of cloud enters the text field and crowns the name; the tent curtain remains attached",
"titleBackingPath": "M700 62 H1900 Q1940 62 1940 102 V342 H700Z",
"verseBackingPath": "M260 2092 H1895 Q1942 2092 1942 2139 V2712 Q1942 2758 1895 2758 H260Z",
"compositionOrder": [
"base-art",
"frame-and-backings",
"attached-curtain-foreground",
"deterministic-text"
],
"curtainContour": "foreground-curtain.svg",
"cloudUpperContour": "M0 0H714 Q752 43 748 83 Q805 43 852 48 C926 52 977 107 964 179 C1008 212 1010 268 983 320 Q964 353 929 370 L730 475 H0Z",
"cloudLowerContour": "M0 400H352 V1780H765 L758 1985 Q750 2040 726 2108 L709 2220 L709 2314 Q695 2340 660 2362 Q651 2405 612 2434 Q579 2462 538 2473 Q507 2485 467 2488 Q401 2473 353 2410 L350 2633 Q286 2659 205 2635 Q101 2583 0 2612Z",
"curtainAlphaBounds": [
0,
0,
1001,
2647
],
"curtainFrameOrBackingOverlapPixels": 347899
},
"typography": {
"fontManifestSHA256": "ef68ee6393981d54ca068a950f21c53b374d20e6e46c21c788f36380d3aae178",
"checks": [
{
"id": "title",
"text": "MOSES",
"baseline": 244,
"font": "P052",
"weight": 700,
"size": 142,
"inkBounds": [
1202,
145,
1711,
247
],
"safeBounds": [
1050,
90,
1905,
330
],
"glyphPixelsOutsideOpaqueBacking": 0,
"foregroundIntersectionPixels": 0
},
{
"id": "verse-0",
"text": "And there has not arisen a",
"baseline": 2305,
"font": "Sanctification P052",
"weight": 500,
"size": 74,
"inkBounds": [
890,
2251,
1749,
2307
],
"safeBounds": [
730,
2180,
1900,
2690
],
"glyphPixelsOutsideOpaqueBacking": 0,
"foregroundIntersectionPixels": 0
},
{
"id": "verse-1",
"text": "prophet since in Israel like Moses,",
"baseline": 2413,
"font": "Sanctification P052",
"weight": 500,
"size": 74,
"inkBounds": [
769,
2359,
1869,
2435
],
"safeBounds": [
730,
2180,
1900,
2690
],
"glyphPixelsOutsideOpaqueBacking": 0,
"foregroundIntersectionPixels": 0
},
{
"id": "verse-2",
"text": "whom the LORD knew face to face",
"baseline": 2521,
"font": "Sanctification P052",
"weight": 500,
"size": 74,
"inkBounds": [
752,
2466,
1886,
2523
],
"safeBounds": [
730,
2180,
1900,
2690
],
"glyphPixelsOutsideOpaqueBacking": 0,
"foregroundIntersectionPixels": 0
},
{
"id": "reference",
"text": "Deuteronomy 34:10 \u2022 ESV",
"baseline": 2650,
"font": "Sanctification P052",
"weight": 500,
"size": 57,
"inkBounds": [
991,
2609,
1649,
2667
],
"safeBounds": [
730,
2180,
1900,
2690
],
"glyphPixelsOutsideOpaqueBacking": 0,
"foregroundIntersectionPixels": 0
}
],
"verseInkUnion": [
752,
2251,
1886,
2523
],
"upperFlourishToVerseInk": 77,
"verseInkToReferenceLineBox": 86,
"centeringDifference": 9,
"lineBaselineGap": 108
},
"structuralChecks": {
"curtainTextIntersectionPixels": 0,
"glyphPixelsOutsideOpaqueBacking": 0,
"artworkSHA256Unchanged": true,
"fontGlyphCoverage": "passed",
"fontManifestHashes": "passed"
},
"notPerformed": [
"Borderless/Textless/Boundless composition",
"Production finish masks",
"Production text masks",
"Harness fixture installation",
"GPU moving-light validation",
"Final card approval"
],
"staticReview": "Inspected at card size and enlarged cloud joins; provisional, pending user review",
"generationAttempt": {
"tool": "built-in imagegen",
"status": "failed before generation",
"reason": "filesystem sandbox helper: bwrap loopback Failed RTM_NEWADDR: Operation not permitted",
"fallback": "Native SVG contours around existing source artwork; no generated donor"
},
"foregroundAdaptation": "Cloud extracted in original coordinates. A 7px dark SVG lead arc closes the lowest lobe across its original ground occlusion; source file unchanged."
}

View File

@@ -0,0 +1,24 @@
# Moses — refined cloud foreground
This provisional Normal candidate retains the v3 composition and refines the cloud where it crosses the name and verse backings.
- [Full preview](composed-preview-1000.png)
- [500 px preview](composed-preview-500.png)
- [V3 / V4 card comparison](v3-v4-comparison.png)
- [V3 / V4 enlarged junction comparison](v3-v4-junction-comparison.png)
- [V4 interaction details](cloud-interaction-detail.png)
- [Editable foreground](foreground-curtain.svg)
- [Reproducible builder](build-preview.py)
- [Validation](preview-validation.json)
The prior contour passed through existing lead seams and retained small sky/terrain slivers, producing a clipped edge. V4 traces the cloud with fewer continuous cubic Bézier segments. A consistent eight-master-pixel dark lead perimeter integrates the exposed edges with the stained-glass construction. At the lower end, an authored rounded closure replaces the broken boundary inherited from the source's rocky ground. This foreground adaptation is deliberate; it does not need to retain that original occlusion.
The cloud interior stays registered to the original illustration. Geometry-heavy outputs render at 4000 × 5600, then downsample to the 2000 × 2800 master in premultiplied RGBA using Lanczos. The glass structure receives no blur. The full source art remains unchanged.
Exact title, verse and reference are retained. Font hashes, glyph coverage, measured safe bounds, zero foreground/text intersection, and opaque backing under all lettering are checked. The text and frame/backing raster components must match v3 pixel for pixel. Static inspection covers full card and enlarged upper/lower joins.
Rebuild from the repository with:
`python3 in-progress/cards/BP-001-moses/revisions/v03/review/layout-preview-astra-v4/build-preview.py`
Requires Inkscape, Fontconfig, Pillow and NumPy. This is a provisional review composition only. V3 is preserved. No production layers, printing exports, finish masks, harness fixture, GPU review or promotion are included.

View File

@@ -0,0 +1,216 @@
#!/usr/bin/env python3
"""Provisional BP-001 Moses Legendary composition; no production assembly."""
from pathlib import Path
import hashlib,json,os,subprocess
from xml.sax.saxutils import escape
import numpy as np
from PIL import Image,ImageDraw,ImageFont
P=Path(__file__).resolve().parent
REV=P.parents[1]
REPO=next(p for p in P.parents if (p/'docs/card-layer-pipeline.md').is_file())
ART=REV/'source/art-master.png'
FONTS=REPO/'fonts'
sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest()
art_hash=sha(ART)
font_manifest=json.loads((FONTS/'manifest.json').read_text())
font_files={'title':'P052-Bold.otf','verse':'SanctificationP052-Medium.otf','reference':'SanctificationP052-Medium.otf'}
for name in set(font_files.values()):
assert sha(FONTS/name)==font_manifest['files'][name]['sha256']
(P/'font-cache').mkdir(exist_ok=True)
(P/'fonts.conf').write_text(f'<fontconfig><dir>{FONTS}</dir><cachedir>{P/"font-cache"}</cachedir></fontconfig>')
env=dict(os.environ,FONTCONFIG_FILE=str(P/'fonts.conf'))
def ink(*args):return subprocess.check_output(['inkscape',*map(str,args)],env=env,text=True,stderr=subprocess.PIPE).strip()
def render(src,dst):
# Supersample only geometry-heavy composite/foreground outputs; text measurements
# remain native. LANCZOS uses premultiplied RGBA via Pillow's RGBa path.
factor=2 if src.stem in {'foreground-curtain','composed-preview','without-curtain'} else 1
ink(src,'--export-type=png',f'--export-filename={dst}',f'--export-width={2000*factor}',f'--export-height={2800*factor}')
if factor>1:
im=Image.open(dst).convert('RGBA')
im.convert('RGBa').resize((2000,2800),Image.Resampling.LANCZOS).convert('RGBA').save(dst)
def resolve(family,style):return Path(subprocess.check_output(['fc-match','-f','%{file}',f'{family}:style={style}'],env=env,text=True).strip()).resolve()
assert resolve('P052','Bold')==(FONTS/'P052-Bold.otf').resolve()
assert resolve('Sanctification P052','Medium')==(FONTS/'SanctificationP052-Medium.otf').resolve()
head='<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="2000" height="2800" viewBox="0 0 2000 2800">'
defs='''<defs>
<linearGradient id="paper" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#fbf3df"/><stop offset=".5" stop-color="#f4ead4"/><stop offset="1" stop-color="#e8d4b0"/></linearGradient>
<linearGradient id="gold" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#f0d47d"/><stop offset=".48" stop-color="#bd8b31"/><stop offset="1" stop-color="#71501e"/></linearGradient>
<pattern id="weave" width="44" height="44" patternUnits="userSpaceOnUse"><path d="M0 11H44 M0 33H44 M11 0V44 M33 0V44" stroke="#9d783d" stroke-width="1" opacity=".045"/><path d="M22 7l7 15-7 15-7-15Z" fill="none" stroke="#806438" stroke-width="1" opacity=".07"/></pattern>
<pattern id="lapis-band" width="34" height="34" patternUnits="userSpaceOnUse"><path d="M17 2L32 17 17 32 2 17Z" fill="#173e59" stroke="#d7b35a" stroke-width="3"/><circle cx="17" cy="17" r="4" fill="url(#gold)"/></pattern>
<clipPath id="curtain-clip"><path d="M-10-10 H770 C734 204 694 395 650 585 C610 770 566 972 522 1170 C474 1388 426 1604 378 1818 C338 2000 302 2190 286 2380 C276 2510 304 2640 412 2810 H-10Z"/></clipPath>
</defs>'''
base='<image xlink:href="../../source/art-master.png" width="2000" height="2800"/>'
frame='''<g id="legendary-frame">
<rect x="14" y="14" width="1972" height="2772" rx="34" fill="none" stroke="#2c2115" stroke-width="28"/>
<rect x="25" y="25" width="1950" height="2750" rx="27" fill="none" stroke="url(#gold)" stroke-width="14"/>
<rect x="43" y="43" width="1914" height="2714" rx="20" fill="none" stroke="#173e59" stroke-width="10"/>
<rect x="55" y="55" width="1890" height="2690" rx="15" fill="none" stroke="#d7b35a" stroke-width="4"/>
<path d="M68 260V68H260 M1740 68H1932V260 M68 2540V2732H260 M1740 2732H1932V2540" fill="none" stroke="#9b6d26" stroke-width="6"/>
<g fill="url(#gold)" stroke="#533810" stroke-width="3"><path d="M68 68l14 14-14 14-14-14Z"/><path d="M1932 68l14 14-14 14-14-14Z"/><path d="M68 2732l14 14-14 14-14-14Z"/><path d="M1932 2732l14 14-14 14-14-14Z"/></g>
<rect x="1906" y="430" width="27" height="255" fill="url(#lapis-band)" opacity=".95"/><rect x="1906" y="2115" width="27" height="255" fill="url(#lapis-band)" opacity=".95"/>
</g>'''
# The panels tuck behind the original tent and the pillar of cloud.
# No alteration, retouch, rescaling or regeneration of the selected source.
title_path='M700 62 H1900 Q1940 62 1940 102 V342 H700Z'
verse_path='M260 2092 H1895 Q1942 2092 1942 2139 V2712 Q1942 2758 1895 2758 H260Z'
title_inner='<path d="M710 82 H1898 Q1920 82 1920 104 V320 H710" fill="none" stroke="#bb9040" stroke-width="3"/><path d="M1070 301H1870" stroke="#c5a257" stroke-width="2"/>'
verse_inner='<path d="M280 2114H1893 Q1920 2114 1920 2141 V2708 Q1920 2736 1892 2736 H280" fill="none" stroke="#bb9040" stroke-width="3"/><path d="M827 2172H1815 M827 2700H1815" stroke="#c5a257" stroke-width="2"/>'
def panel(path,inner,ident):return f'<g id="{ident}"><path d="{path}" fill="url(#paper)" stroke="#62421e" stroke-width="9"/><path d="{path}" fill="url(#weave)" stroke="url(#gold)" stroke-width="4"/>{inner}</g>'
panels=panel(title_path,title_inner,'title-backing')+panel(verse_path,verse_inner,'verse-backing')
# Trace the visible source silhouette, including the whole cloud lower lobe.
# Upper crown is separately registered; both layers stay at source coordinates.
# Cubic contours follow the source lead, not the rough v3 clipping guesses.
# The lower closing arc ends above the old terrain occlusion.
top_edge='M714 0 C741 25 750 55 746 82 C787 58 814 43 846 45 C926 50 980 112 952 191 C1008 238 1002 319 925 369'
lower_edge='M736 2080 C728 2148 722 2213 710 2262 C704 2290 684 2310 660 2323 C650 2348 629 2367 608 2374 C599 2432 541 2477 480 2479 C435 2481 375 2418 354 2350'
cloud_top=top_edge+' L730 475 H0 V0Z'
cloud_low='M0 400H352 V1780H765 C765 1920 748 2010 736 2080 '+lower_edge.removeprefix('M736 2080 ')+' L350 2633 Q286 2659 205 2635 Q101 2583 0 2612Z'
defs=defs.replace('</defs>',f'<clipPath id="cloud-top"><path d="{cloud_top}"/></clipPath><clipPath id="cloud-low"><path d="{cloud_low}"/></clipPath></defs>')
# Quiet fine-gold enclosure: motifs reside in the original curtain and cloud.
frame='''<g id="legendary-frame"><rect x="19" y="19" width="1962" height="2762" rx="28" fill="none" stroke="#251c15" stroke-width="21"/><rect x="23" y="23" width="1954" height="2754" rx="23" fill="none" stroke="url(#gold)" stroke-width="11"/><rect x="43" y="43" width="1914" height="2714" rx="15" fill="none" stroke="#e2bf6c" stroke-width="3"/></g>'''
# A narrow opaque lead edge removes blue/terrain fringe without softening glass.
lead=f'<g fill="none" stroke="#2b2116" stroke-width="8" stroke-linejoin="round" stroke-linecap="round"><path d="{top_edge}"/><path d="{lower_edge}"/></g>'
curtain='<g id="presence-foreground">'+''.join(f'<g clip-path="url(#{clip})"><image xlink:href="../../source/art-master.png" width="2000" height="2800"/></g>' for clip in ['cloud-top','cloud-low'])+lead+'</g>'
(P/'foreground-curtain.svg').write_text(head+defs+curtain+'</svg>');render(P/'foreground-curtain.svg',P/'foreground-curtain.png')
fg=Image.open(P/'foreground-curtain.png').convert('RGBA')
fa=np.asarray(fg.getchannel('A'))
assert fa.max()==255 and fa.min()==0
rows=[
('title','MOSES',1458,244,142,'P052',700),
('verse-0','And there has not arisen a',1320,2305,74,'Sanctification P052',500),
('verse-1','prophet since in Israel like Moses,',1320,2413,74,'Sanctification P052',500),
('verse-2','whom the LORD knew face to face',1320,2521,74,'Sanctification P052',500),
('reference','Deuteronomy 34:10 • ESV',1320,2650,57,'Sanctification P052',500),
]
for ident,content,*_ in rows:
fontfile=font_files['title' if ident=='title' else 'reference' if ident=='reference' else 'verse']
charset=subprocess.check_output(['fc-query','--format=%{charset}',str(FONTS/fontfile)],text=True).split()
ranges=[tuple(int(v,16) for v in token.split('-')) for token in charset]
assert all(any(r[0]<=ord(char)<=r[-1] for r in ranges) for char in content), (ident, 'missing glyph')
texts=''.join(f'<text id="{ident}" x="{x}" y="{y}" font-family="{family}" font-weight="{weight}" font-size="{size}" text-anchor="middle" fill="#263f50">{escape(text)}</text>' for ident,text,x,y,size,family,weight in rows)
(P/'text-preview.svg').write_text(head+texts+'</svg>');render(P/'text-preview.svg',P/'text-preview.png')
text_image=Image.open(P/'text-preview.png').convert('RGBA');ta=np.asarray(text_image.getchannel('A'))
assert not np.any((ta>0)&(fa>0))
(P/'border-backing-preview.svg').write_text(head+defs+frame+panels+'</svg>');render(P/'border-backing-preview.svg',P/'border-backing-preview.png')
backing=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A'))
checks=[];verse_bounds=[]
for ident,text,x,y,size,family,weight in rows:
node=next(v for v in texts.split('</text>') if f'id="{ident}"' in v)+'</text>'
src=P/(ident+'-ink.svg');dst=P/(ident+'-ink.png');src.write_text(head+node+'</svg>');render(src,dst)
alpha=np.asarray(Image.open(dst).convert('RGBA').getchannel('A'));ys,xs=np.where(alpha>0);assert len(xs)
bounds=[int(xs.min()),int(ys.min()),int(xs.max()+1),int(ys.max()+1)]
safe=[1050,90,1905,330] if ident=='title' else [730,2180,1900,2690]
assert safe[0]<=bounds[0] and safe[1]<=bounds[1] and bounds[2]<=safe[2] and bounds[3]<=safe[3],(ident,bounds,safe)
assert np.all(backing[alpha>0]==255),ident
assert not np.any((alpha>0)&(fa>0)),ident
checks.append({'id':ident,'text':text,'baseline':y,'font':family,'weight':weight,'size':size,'inkBounds':bounds,'safeBounds':safe,'glyphPixelsOutsideOpaqueBacking':0,'foregroundIntersectionPixels':0})
if ident.startswith('verse-'):verse_bounds.append(bounds)
assert ' '.join(c['text'] for c in checks if c['id'].startswith('verse-'))==json.loads((REV/'card.json').read_text())['excerpt']
assert checks[-1]['text']==json.loads((REV/'card.json').read_text())['referenceDisplay']
union=[min(b[0] for b in verse_bounds),min(b[1] for b in verse_bounds),max(b[2] for b in verse_bounds),max(b[3] for b in verse_bounds)]
upper=2174;ref_top=checks[-1]['inkBounds'][1];above=union[1]-upper;below=ref_top-union[3]
assert min(above,below)>=35 and abs(above-below)<=20,(above,below)
for name,include_curtain in [('composed-preview',True),('without-curtain',False)]:
body=base+frame+panels+(curtain if include_curtain else '')+texts
src=P/(name+'.svg');src.write_text(head+defs+body+'</svg>');render(src,P/(name+'-2000.png'))
master=Image.open(P/(name+'-2000.png')).convert('RGB')
master.resize((1000,1400),Image.Resampling.LANCZOS).save(P/(name+'-1000.png'))
master.resize((500,700),Image.Resampling.LANCZOS).save(P/(name+'-500.png'))
# Compare the structural effect of the attached curtain.
board=Image.new('RGB',(1000,750),'#11161c');draw=ImageDraw.Draw(board)
for i,(label,path) in enumerate([('Panels before foreground',P/'without-curtain-1000.png'),('Pillar of cloud over panels',P/'composed-preview-1000.png')]):
draw.text((i*500+12,12),label,fill='#f4ead4')
image=Image.open(path).convert('RGB');image.thumbnail((480,672));board.paste(image,(i*500+10,45))
board.save(P/'curtain-comparison.png')
# A focused junction comparison at master crop.
detail=Image.new('RGB',(1200,620),'#11161c');draw=ImageDraw.Draw(detail)
for i,(label,path) in enumerate([('Without foreground',P/'without-curtain-2000.png'),('Cloud and curtain over backing',P/'composed-preview-2000.png')]):
draw.text((i*600+12,12),label,fill='#f4ead4')
image=Image.open(path).convert('RGB').crop((0,1780,900,2800));image.thumbnail((575,545));detail.paste(image,(i*600+12,52))
detail.save(P/'curtain-junction-detail.png')
frame_alpha=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A'))
foreground_frame_overlap=int(np.count_nonzero((fa>0)&(frame_alpha>0)))
report={
'status':'passed',
'scope':'Provisional Legendary Normal — threshold of the Presence',
'canvas':[2000,2800],
'sourceArtwork':{'path':str(ART.relative_to(REPO)),'sha256':art_hash,'unchanged':sha(ART)==art_hash},
'template':{
'governingIdea':'The pillar of cloud enters the text field and crowns the name; the tent curtain remains attached',
'titleBackingPath':title_path,
'verseBackingPath':verse_path,
'compositionOrder':['base-art','frame-and-backings','attached-curtain-foreground','deterministic-text'],
'curtainContour':'foreground-curtain.svg',
'cloudUpperContour':cloud_top,
'cloudLowerContour':cloud_low,
'curtainAlphaBounds':list(Image.fromarray(fa).getbbox()),
'curtainFrameOrBackingOverlapPixels':foreground_frame_overlap,
},
'typography':{
'fontManifestSHA256':sha(FONTS/'manifest.json'),
'checks':checks,
'verseInkUnion':union,
'upperFlourishToVerseInk':above,
'verseInkToReferenceLineBox':below,
'centeringDifference':abs(above-below),
'lineBaselineGap':108,
},
'structuralChecks':{
'curtainTextIntersectionPixels':int(np.count_nonzero((ta>0)&(fa>0))),
'glyphPixelsOutsideOpaqueBacking':0,
'artworkSHA256Unchanged':True,
'fontGlyphCoverage':'passed',
'fontManifestHashes':'passed',
},
'notPerformed':['Borderless/Textless/Boundless composition','Production finish masks','Production text masks','Harness fixture installation','GPU moving-light validation','Final card approval'],
'staticReview':'Inspected at card size and enlarged cloud joins; provisional, pending user review',
'edgeRendering':{'geometry':'source-traced cubic Bezier paths','leadWidthMasterPixels':8,'renderScale':2,'downsample':'premultiplied RGBA LANCZOS','blur':False},
'foregroundAdaptation':'Source-registered cloud with retraced cubic perimeter and 8px dark lead; lower lobe closes before rocky-ground interruption. Source artwork, panel geometry and text unchanged.'
}
(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n')
assert sha(ART)==art_hash
print(json.dumps({'status':'passed','preview':str((P/'composed-preview-1000.png').relative_to(REPO)),'curtainOverlapPixels':foreground_frame_overlap,'spacing':[above,below]},indent=2))
# Reproducible v3/v4 presentation and focused interaction review.
label_font=ImageFont.truetype(str(FONTS/'P052-Bold.otf'),26)
board=Image.new('RGB',(1500,1100),'#11161c');d=ImageDraw.Draw(board)
for i,(label,path) in enumerate([('Astra v3 — initial contours',P.parent/'layout-preview-astra-v3/composed-preview-1000.png'),('Astra v4 — refined contours',P/'composed-preview-1000.png')]):
d.text((i*750+24,18),label,font=label_font,fill='#f4ead4')
im=Image.open(path).convert('RGB');im.thumbnail((730,1022));board.paste(im,(i*750+10,63))
board.save(P/'v3-v4-comparison.png')
master=Image.open(P/'composed-preview-2000.png').convert('RGB')
detail=Image.new('RGB',(1500,1050),'#11161c');d=ImageDraw.Draw(detail)
for label,box,origin,size in [('Cloud crowns the name',(570,0,1140,470),(22,65),(650,536)),('Pillar enters the verse field',(220,1960,850,2580),(795,65),(650,640))]:
d.text((origin[0],20),label,font=label_font,fill='#f4ead4')
im=master.crop(box);im.thumbnail(size);detail.paste(im,origin)
im=master.crop((250,2080,1980,2780));im.thumbnail((1450,320));detail.paste(im,(25,722))
detail.save(P/'cloud-interaction-detail.png')
# Verify the refinement has no layout/type drift from v3.
previous=P.parent/'layout-preview-astra-v3'
for filename in ['text-preview.png','border-backing-preview.png']:
assert np.array_equal(np.asarray(Image.open(P/filename)),np.asarray(Image.open(previous/filename))), filename
report['structuralChecks']['textPixelsIdenticalToV3']=True
report['structuralChecks']['frameAndBackingPixelsIdenticalToV3']=True
report['edgeRendering']['alphaEdge']='Premultiplied before downsampling; no transparent-RGB halo'
(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n')
# Matched enlarged crops make contour changes directly reviewable.
board=Image.new('RGB',(1480,1430),'#11161c');d=ImageDraw.Draw(board)
for col,(label,folder) in enumerate([('V3 — initial contour',previous),('V4 — refined lead contour',P)]):
d.text((col*740+22,18),label,font=label_font,fill='#f4ead4')
im=Image.open(folder/'composed-preview-2000.png').convert('RGB')
for box,y,size in [((680,0,1030,400),65,(690,600)),((320,2070,765,2520),725,(690,670))]:
crop=im.crop(box);crop.thumbnail(size);crop=crop.resize((round(crop.width*1.45),round(crop.height*1.45)),Image.Resampling.LANCZOS);crop.thumbnail(size);board.paste(crop,(col*740+22,y))
board.save(P/'v3-v4-junction-comparison.png')

View File

@@ -0,0 +1 @@
<fontconfig><dir>/home/dkzver/docker/sanctification/fonts</dir><cachedir>/home/dkzver/docker/sanctification/in-progress/cards/BP-001-moses/revisions/v03/review/layout-preview-astra-v4/font-cache</cachedir></fontconfig>

View File

@@ -0,0 +1,186 @@
{
"status": "passed",
"scope": "Provisional Legendary Normal \u2014 threshold of the Presence",
"canvas": [
2000,
2800
],
"sourceArtwork": {
"path": "in-progress/cards/BP-001-moses/revisions/v03/source/art-master.png",
"sha256": "73f933cf34719dfda09b719376948bcf072bec504a88329b5a22e4ac1ed76d85",
"unchanged": true
},
"template": {
"governingIdea": "The pillar of cloud enters the text field and crowns the name; the tent curtain remains attached",
"titleBackingPath": "M700 62 H1900 Q1940 62 1940 102 V342 H700Z",
"verseBackingPath": "M260 2092 H1895 Q1942 2092 1942 2139 V2712 Q1942 2758 1895 2758 H260Z",
"compositionOrder": [
"base-art",
"frame-and-backings",
"attached-curtain-foreground",
"deterministic-text"
],
"curtainContour": "foreground-curtain.svg",
"cloudUpperContour": "M714 0 C741 25 750 55 746 82 C787 58 814 43 846 45 C926 50 980 112 952 191 C1008 238 1002 319 925 369 L730 475 H0 V0Z",
"cloudLowerContour": "M0 400H352 V1780H765 C765 1920 748 2010 736 2080 C728 2148 722 2213 710 2262 C704 2290 684 2310 660 2323 C650 2348 629 2367 608 2374 C599 2432 541 2477 480 2479 C435 2481 375 2418 354 2350 L350 2633 Q286 2659 205 2635 Q101 2583 0 2612Z",
"curtainAlphaBounds": [
0,
0,
996,
2649
],
"curtainFrameOrBackingOverlapPixels": 342547
},
"typography": {
"fontManifestSHA256": "ef68ee6393981d54ca068a950f21c53b374d20e6e46c21c788f36380d3aae178",
"checks": [
{
"id": "title",
"text": "MOSES",
"baseline": 244,
"font": "P052",
"weight": 700,
"size": 142,
"inkBounds": [
1202,
145,
1711,
247
],
"safeBounds": [
1050,
90,
1905,
330
],
"glyphPixelsOutsideOpaqueBacking": 0,
"foregroundIntersectionPixels": 0
},
{
"id": "verse-0",
"text": "And there has not arisen a",
"baseline": 2305,
"font": "Sanctification P052",
"weight": 500,
"size": 74,
"inkBounds": [
890,
2251,
1749,
2307
],
"safeBounds": [
730,
2180,
1900,
2690
],
"glyphPixelsOutsideOpaqueBacking": 0,
"foregroundIntersectionPixels": 0
},
{
"id": "verse-1",
"text": "prophet since in Israel like Moses,",
"baseline": 2413,
"font": "Sanctification P052",
"weight": 500,
"size": 74,
"inkBounds": [
769,
2359,
1869,
2435
],
"safeBounds": [
730,
2180,
1900,
2690
],
"glyphPixelsOutsideOpaqueBacking": 0,
"foregroundIntersectionPixels": 0
},
{
"id": "verse-2",
"text": "whom the LORD knew face to face",
"baseline": 2521,
"font": "Sanctification P052",
"weight": 500,
"size": 74,
"inkBounds": [
752,
2466,
1886,
2523
],
"safeBounds": [
730,
2180,
1900,
2690
],
"glyphPixelsOutsideOpaqueBacking": 0,
"foregroundIntersectionPixels": 0
},
{
"id": "reference",
"text": "Deuteronomy 34:10 \u2022 ESV",
"baseline": 2650,
"font": "Sanctification P052",
"weight": 500,
"size": 57,
"inkBounds": [
991,
2609,
1649,
2667
],
"safeBounds": [
730,
2180,
1900,
2690
],
"glyphPixelsOutsideOpaqueBacking": 0,
"foregroundIntersectionPixels": 0
}
],
"verseInkUnion": [
752,
2251,
1886,
2523
],
"upperFlourishToVerseInk": 77,
"verseInkToReferenceLineBox": 86,
"centeringDifference": 9,
"lineBaselineGap": 108
},
"structuralChecks": {
"curtainTextIntersectionPixels": 0,
"glyphPixelsOutsideOpaqueBacking": 0,
"artworkSHA256Unchanged": true,
"fontGlyphCoverage": "passed",
"fontManifestHashes": "passed",
"textPixelsIdenticalToV3": true,
"frameAndBackingPixelsIdenticalToV3": true
},
"notPerformed": [
"Borderless/Textless/Boundless composition",
"Production finish masks",
"Production text masks",
"Harness fixture installation",
"GPU moving-light validation",
"Final card approval"
],
"staticReview": "Inspected at card size and enlarged cloud joins; provisional, pending user review",
"edgeRendering": {
"geometry": "source-traced cubic Bezier paths",
"leadWidthMasterPixels": 8,
"renderScale": 2,
"downsample": "premultiplied RGBA LANCZOS",
"blur": false,
"alphaEdge": "Premultiplied before downsampling; no transparent-RGB halo"
},
"foregroundAdaptation": "Source-registered cloud with retraced cubic perimeter and 8px dark lead; lower lobe closes before rocky-ground interruption. Source artwork, panel geometry and text unchanged."
}

View File

@@ -0,0 +1,33 @@
# Moses — suspended tent textile and coherent entrance
This provisional Normal candidate preserves the v4 nameplate and cloud crown. Only the lower composition and near entrance structure are revised.
- [Full preview](composed-preview-1000.png)
- [500 px preview](composed-preview-500.png)
- [V4 / V5 full comparison](v4-v5-comparison.png)
- [V4 / V5 lower junction comparison](v4-v5-junction-comparison.png)
- [Editable composition](composed-preview.svg)
- [Builder](build-preview.py)
- [Validation](preview-validation.json)
## Lower textile
The rigid plaque becomes a suspended warm linen field. Its shallow sag reveals more of Moses's robe; its left edge curves inward behind the cloud instead of running to the curtain pole as a wide blank rectangle. A fine woven hem replaces the metal plaque rules. The right corners bind to the existing frame. Its upper-left corner extends behind the cloud to a binding on the near entrance post, so the field belongs to the tent structure.
The v4 cloud contour is retained, but the narrower backing changes the relationship: the cloud occupies and follows the textile edge rather than dangling over an expansive blank field. The text retains the exact words, font faces, sizes and horizontal alignment; verse baselines move down 55 master pixels and the reference moves down 40 pixels. Measured spacing is 66 pixels above the verse and 71 pixels between verse and reference ink.
## Near entrance post
A separate authored foreground jamb follows the existing curtain-side socket and vertical. Its dark outline, warm gold face and segmented joints match the structural language of the far post. It stays visible in front of the cloud and continues to the ground. Its slight lean and stronger near-side width make the entrance read as a pair of supports. A small binding ties the textile's concealed left corner to this post.
This is an SVG-native foreground adaptation; `../../source/art-master.png` is unchanged. No donor image or source repaint is used. The approved nameplate/crown region and title glyph pixels must remain identical to v4, verified programmatically.
## Verification and scope
The builder verifies the source hash, exact text, shared font hashes and glyph coverage, safe glyph bounds, opaque backing beneath lettering, zero foreground/text intersection, and preservation of the nameplate/crown. Compositing remains at 2× resolution with premultiplied-alpha Lanczos downsampling. No glass-blurring filter is used. Static review includes the full card and matched lower-junction crops.
Rebuild from the repository:
`python3 in-progress/cards/BP-001-moses/revisions/v03/review/layout-preview-astra-v5/build-preview.py`
Requires Inkscape, Fontconfig, Pillow and NumPy. Earlier candidates remain intact. This is a review candidate only: no production layers, printing exports, finish masks, harness fixture, GPU review or promotion.

View File

@@ -0,0 +1,242 @@
#!/usr/bin/env python3
"""Provisional BP-001 Moses Legendary composition; no production assembly."""
from pathlib import Path
import hashlib,json,os,subprocess
from xml.sax.saxutils import escape
import numpy as np
from PIL import Image,ImageDraw,ImageFont
P=Path(__file__).resolve().parent
REV=P.parents[1]
REPO=next(p for p in P.parents if (p/'docs/card-layer-pipeline.md').is_file())
ART=REV/'source/art-master.png'
FONTS=REPO/'fonts'
sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest()
art_hash=sha(ART)
font_manifest=json.loads((FONTS/'manifest.json').read_text())
font_files={'title':'P052-Bold.otf','verse':'SanctificationP052-Medium.otf','reference':'SanctificationP052-Medium.otf'}
for name in set(font_files.values()):
assert sha(FONTS/name)==font_manifest['files'][name]['sha256']
(P/'font-cache').mkdir(exist_ok=True)
(P/'fonts.conf').write_text(f'<fontconfig><dir>{FONTS}</dir><cachedir>{P/"font-cache"}</cachedir></fontconfig>')
env=dict(os.environ,FONTCONFIG_FILE=str(P/'fonts.conf'))
def ink(*args):return subprocess.check_output(['inkscape',*map(str,args)],env=env,text=True,stderr=subprocess.PIPE).strip()
def render(src,dst):
# Supersample only geometry-heavy composite/foreground outputs; text measurements
# remain native. LANCZOS uses premultiplied RGBA via Pillow's RGBa path.
factor=2 if src.stem in {'foreground-curtain','composed-preview','without-curtain'} else 1
ink(src,'--export-type=png',f'--export-filename={dst}',f'--export-width={2000*factor}',f'--export-height={2800*factor}')
if factor>1:
im=Image.open(dst).convert('RGBA')
im.convert('RGBa').resize((2000,2800),Image.Resampling.LANCZOS).convert('RGBA').save(dst)
def resolve(family,style):return Path(subprocess.check_output(['fc-match','-f','%{file}',f'{family}:style={style}'],env=env,text=True).strip()).resolve()
assert resolve('P052','Bold')==(FONTS/'P052-Bold.otf').resolve()
assert resolve('Sanctification P052','Medium')==(FONTS/'SanctificationP052-Medium.otf').resolve()
head='<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="2000" height="2800" viewBox="0 0 2000 2800">'
defs='''<defs>
<linearGradient id="paper" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#fbf3df"/><stop offset=".5" stop-color="#f4ead4"/><stop offset="1" stop-color="#e8d4b0"/></linearGradient>
<linearGradient id="gold" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#f0d47d"/><stop offset=".48" stop-color="#bd8b31"/><stop offset="1" stop-color="#71501e"/></linearGradient>
<pattern id="weave" width="44" height="44" patternUnits="userSpaceOnUse"><path d="M0 11H44 M0 33H44 M11 0V44 M33 0V44" stroke="#9d783d" stroke-width="1" opacity=".045"/><path d="M22 7l7 15-7 15-7-15Z" fill="none" stroke="#806438" stroke-width="1" opacity=".07"/></pattern>
<pattern id="lapis-band" width="34" height="34" patternUnits="userSpaceOnUse"><path d="M17 2L32 17 17 32 2 17Z" fill="#173e59" stroke="#d7b35a" stroke-width="3"/><circle cx="17" cy="17" r="4" fill="url(#gold)"/></pattern>
<clipPath id="curtain-clip"><path d="M-10-10 H770 C734 204 694 395 650 585 C610 770 566 972 522 1170 C474 1388 426 1604 378 1818 C338 2000 302 2190 286 2380 C276 2510 304 2640 412 2810 H-10Z"/></clipPath>
</defs>'''
base='<image xlink:href="../../source/art-master.png" width="2000" height="2800"/>'
frame='''<g id="legendary-frame">
<rect x="14" y="14" width="1972" height="2772" rx="34" fill="none" stroke="#2c2115" stroke-width="28"/>
<rect x="25" y="25" width="1950" height="2750" rx="27" fill="none" stroke="url(#gold)" stroke-width="14"/>
<rect x="43" y="43" width="1914" height="2714" rx="20" fill="none" stroke="#173e59" stroke-width="10"/>
<rect x="55" y="55" width="1890" height="2690" rx="15" fill="none" stroke="#d7b35a" stroke-width="4"/>
<path d="M68 260V68H260 M1740 68H1932V260 M68 2540V2732H260 M1740 2732H1932V2540" fill="none" stroke="#9b6d26" stroke-width="6"/>
<g fill="url(#gold)" stroke="#533810" stroke-width="3"><path d="M68 68l14 14-14 14-14-14Z"/><path d="M1932 68l14 14-14 14-14-14Z"/><path d="M68 2732l14 14-14 14-14-14Z"/><path d="M1932 2732l14 14-14 14-14-14Z"/></g>
<rect x="1906" y="430" width="27" height="255" fill="url(#lapis-band)" opacity=".95"/><rect x="1906" y="2115" width="27" height="255" fill="url(#lapis-band)" opacity=".95"/>
</g>'''
# The panels tuck behind the original tent and the pillar of cloud.
# No alteration, retouch, rescaling or regeneration of the selected source.
title_path='M700 62 H1900 Q1940 62 1940 102 V342 H700Z'
verse_path='M340 2106 C445 2114 550 2118 655 2140 C960 2230 1435 2260 1960 2095 L1960 2720 C1535 2782 1045 2728 580 2750 C488 2755 437 2684 453 2592 C462 2506 520 2470 574 2404 C625 2341 641 2230 655 2140 L340 2106Z'
title_inner='<path d="M710 82 H1898 Q1920 82 1920 104 V320 H710" fill="none" stroke="#bb9040" stroke-width="3"/><path d="M1070 301H1870" stroke="#c5a257" stroke-width="2"/>'
verse_inner='''<path d="M690 2170 C1000 2250 1470 2250 1925 2138 M490 2677 C516 2726 551 2728 591 2725 C1060 2702 1530 2757 1925 2696" fill="none" stroke="#a17533" stroke-width="3"/><path d="M688 2182 C1000 2262 1470 2262 1923 2150 M489 2665 C516 2714 551 2716 591 2713 C1060 2690 1530 2745 1925 2684" fill="none" stroke="#8c3d3c" stroke-width="2" opacity=".65"/><path d="M1925 2140V2695" fill="none" stroke="#b7924a" stroke-width="3" stroke-dasharray="7 9"/>'''
def panel(path,inner,ident):
if ident=='verse-backing':
# A linen field with a woven seam, rather than a metallic plaque.
return f'<g id="{ident}"><path d="{path}" transform="translate(3 6)" fill="#211b15" opacity=".6"/><path d="{path}" fill="url(#linen)" stroke="#79552b" stroke-width="4"/><path d="{path}" fill="url(#weave)"/>{inner}</g>'
return f'<g id="{ident}"><path d="{path}" fill="url(#paper)" stroke="#62421e" stroke-width="9"/><path d="{path}" fill="url(#weave)" stroke="url(#gold)" stroke-width="4"/>{inner}</g>'
defs=defs.replace('</defs>','<linearGradient id="linen" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#f7edd4"/><stop offset=".55" stop-color="#f2e7cd"/><stop offset="1" stop-color="#e8d7b6"/></linearGradient></defs>')
# Ties sit at the existing right rail and visibly support the hanging field.
ties='<g fill="none" stroke-linecap="round">'+''.join(f'<path d="M1940 {y+9} C1950 {y-4} 1964 {y-5} 1977 {y-1}" stroke="#362719" stroke-width="13"/><path d="M1940 {y+9} C1950 {y-4} 1964 {y-5} 1977 {y-1}" stroke="#d6b260" stroke-width="7"/>' for y in [2110,2700])+'</g>'
panels=panel(title_path,title_inner,'title-backing')+panel(verse_path,verse_inner,'verse-backing')+ties
# Trace the visible source silhouette, including the whole cloud lower lobe.
# Upper crown is separately registered; both layers stay at source coordinates.
# Cubic contours follow the source lead, not the rough v3 clipping guesses.
# The lower closing arc ends above the old terrain occlusion.
top_edge='M714 0 C741 25 750 55 746 82 C787 58 814 43 846 45 C926 50 980 112 952 191 C1008 238 1002 319 925 369'
lower_edge='M736 2080 C728 2148 722 2213 710 2262 C704 2290 684 2310 660 2323 C650 2348 629 2367 608 2374 C599 2432 541 2477 480 2479 C435 2481 375 2418 354 2350'
cloud_top=top_edge+' L730 475 H0 V0Z'
cloud_low='M0 400H352 V1780H765 C765 1920 748 2010 736 2080 '+lower_edge.removeprefix('M736 2080 ')+' L350 2633 Q286 2659 205 2635 Q101 2583 0 2612Z'
defs=defs.replace('</defs>',f'<clipPath id="cloud-top"><path d="{cloud_top}"/></clipPath><clipPath id="cloud-low"><path d="{cloud_low}"/></clipPath></defs>')
# Quiet fine-gold enclosure: motifs reside in the original curtain and cloud.
frame='''<g id="legendary-frame"><rect x="19" y="19" width="1962" height="2762" rx="28" fill="none" stroke="#251c15" stroke-width="21"/><rect x="23" y="23" width="1954" height="2754" rx="23" fill="none" stroke="url(#gold)" stroke-width="11"/><rect x="43" y="43" width="1914" height="2714" rx="15" fill="none" stroke="#e2bf6c" stroke-width="3"/></g>'''
# A narrow opaque lead edge removes blue/terrain fringe without softening glass.
lead=f'<g fill="none" stroke="#2b2116" stroke-width="8" stroke-linejoin="round" stroke-linecap="round"><path d="{top_edge}"/><path d="{lower_edge}"/></g>'
curtain='<g id="presence-foreground">'+''.join(f'<g clip-path="url(#{clip})"><image xlink:href="../../source/art-master.png" width="2000" height="2800"/></g>' for clip in ['cloud-top','cloud-low'])+lead+'</g>'
# Near entrance jamb: aligned to the source curtain-side upper socket.
# The cloud sits behind this continuous dark/gold glass structure.
defs=defs.replace('</defs>','<linearGradient id="post-gold" x1="0" y1="0" x2="1" y2="0"><stop offset="0" stop-color="#654016"/><stop offset=".25" stop-color="#b78829"/><stop offset=".52" stop-color="#f5dc75"/><stop offset=".72" stop-color="#d8ae45"/><stop offset="1" stop-color="#80601c"/></linearGradient></defs>')
post_path='M326 250 L360 250 L350 2640 L316 2637Z'
post=f'<g id="near-entrance-post"><path d="{post_path}" fill="url(#post-gold)" stroke="#241c12" stroke-width="7" stroke-linejoin="round"/>'
# Uneven joints follow the counterpart post's segmented construction.
for y in [310,493,720,944,1186,1432,1669,1915,2167,2406,2590]:
x=326-(y-250)*10/2390
post+=f'<path d="M{x} {y}l34 1v15l-34-1Z" fill="#372916" stroke="#17170f" stroke-width="3"/><path d="M{x+7} {y+22}l0 101" fill="none" stroke="#f4d373" stroke-width="3" opacity=".5"/>'
post+='<path d="M326 254Q342 242 359 254 M319 2625Q333 2633 350 2629" fill="none" stroke="#edcf70" stroke-width="4"/>'
# A small woven binding holds the concealed left textile corner to the jamb.
post+='<path d="M317 2108 C324 2101 345 2101 357 2110 L355 2124 C341 2114 326 2115 317 2121Z" fill="#b88b3f" stroke="#2b2216" stroke-width="4"/><path d="M319 2113 C333 2109 344 2112 355 2117" fill="none" stroke="#f2d484" stroke-width="3"/></g>'
curtain+=post
(P/'foreground-curtain.svg').write_text(head+defs+curtain+'</svg>');render(P/'foreground-curtain.svg',P/'foreground-curtain.png')
fg=Image.open(P/'foreground-curtain.png').convert('RGBA')
fa=np.asarray(fg.getchannel('A'))
assert fa.max()==255 and fa.min()==0
rows=[
('title','MOSES',1458,244,142,'P052',700),
('verse-0','And there has not arisen a',1320,2360,74,'Sanctification P052',500),
('verse-1','prophet since in Israel like Moses,',1320,2468,74,'Sanctification P052',500),
('verse-2','whom the LORD knew face to face',1320,2576,74,'Sanctification P052',500),
('reference','Deuteronomy 34:10 • ESV',1320,2690,57,'Sanctification P052',500),
]
for ident,content,*_ in rows:
fontfile=font_files['title' if ident=='title' else 'reference' if ident=='reference' else 'verse']
charset=subprocess.check_output(['fc-query','--format=%{charset}',str(FONTS/fontfile)],text=True).split()
ranges=[tuple(int(v,16) for v in token.split('-')) for token in charset]
assert all(any(r[0]<=ord(char)<=r[-1] for r in ranges) for char in content), (ident, 'missing glyph')
texts=''.join(f'<text id="{ident}" x="{x}" y="{y}" font-family="{family}" font-weight="{weight}" font-size="{size}" text-anchor="middle" fill="#263f50">{escape(text)}</text>' for ident,text,x,y,size,family,weight in rows)
(P/'text-preview.svg').write_text(head+texts+'</svg>');render(P/'text-preview.svg',P/'text-preview.png')
text_image=Image.open(P/'text-preview.png').convert('RGBA');ta=np.asarray(text_image.getchannel('A'))
assert not np.any((ta>0)&(fa>0))
(P/'border-backing-preview.svg').write_text(head+defs+frame+panels+'</svg>');render(P/'border-backing-preview.svg',P/'border-backing-preview.png')
backing=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A'))
checks=[];verse_bounds=[]
for ident,text,x,y,size,family,weight in rows:
node=next(v for v in texts.split('</text>') if f'id="{ident}"' in v)+'</text>'
src=P/(ident+'-ink.svg');dst=P/(ident+'-ink.png');src.write_text(head+node+'</svg>');render(src,dst)
alpha=np.asarray(Image.open(dst).convert('RGBA').getchannel('A'));ys,xs=np.where(alpha>0);assert len(xs)
bounds=[int(xs.min()),int(ys.min()),int(xs.max()+1),int(ys.max()+1)]
safe=[1050,90,1905,330] if ident=='title' else [730,2275,1900,2730]
assert safe[0]<=bounds[0] and safe[1]<=bounds[1] and bounds[2]<=safe[2] and bounds[3]<=safe[3],(ident,bounds,safe)
assert np.all(backing[alpha>0]==255),ident
assert not np.any((alpha>0)&(fa>0)),ident
checks.append({'id':ident,'text':text,'baseline':y,'font':family,'weight':weight,'size':size,'inkBounds':bounds,'safeBounds':safe,'glyphPixelsOutsideOpaqueBacking':0,'foregroundIntersectionPixels':0})
if ident.startswith('verse-'):verse_bounds.append(bounds)
assert ' '.join(c['text'] for c in checks if c['id'].startswith('verse-'))==json.loads((REV/'card.json').read_text())['excerpt']
assert checks[-1]['text']==json.loads((REV/'card.json').read_text())['referenceDisplay']
union=[min(b[0] for b in verse_bounds),min(b[1] for b in verse_bounds),max(b[2] for b in verse_bounds),max(b[3] for b in verse_bounds)]
upper=2240;ref_top=checks[-1]['inkBounds'][1];above=union[1]-upper;below=ref_top-union[3]
assert min(above,below)>=35 and abs(above-below)<=20,(above,below)
for name,include_curtain in [('composed-preview',True),('without-curtain',False)]:
body=base+frame+panels+(curtain if include_curtain else '')+texts
src=P/(name+'.svg');src.write_text(head+defs+body+'</svg>');render(src,P/(name+'-2000.png'))
master=Image.open(P/(name+'-2000.png')).convert('RGB')
master.resize((1000,1400),Image.Resampling.LANCZOS).save(P/(name+'-1000.png'))
master.resize((500,700),Image.Resampling.LANCZOS).save(P/(name+'-500.png'))
# Compare the structural effect of the attached curtain.
board=Image.new('RGB',(1000,750),'#11161c');draw=ImageDraw.Draw(board)
for i,(label,path) in enumerate([('Panels before foreground',P/'without-curtain-1000.png'),('Pillar of cloud over panels',P/'composed-preview-1000.png')]):
draw.text((i*500+12,12),label,fill='#f4ead4')
image=Image.open(path).convert('RGB');image.thumbnail((480,672));board.paste(image,(i*500+10,45))
board.save(P/'curtain-comparison.png')
# A focused junction comparison at master crop.
detail=Image.new('RGB',(1200,620),'#11161c');draw=ImageDraw.Draw(detail)
for i,(label,path) in enumerate([('Without foreground',P/'without-curtain-2000.png'),('Cloud and curtain over backing',P/'composed-preview-2000.png')]):
draw.text((i*600+12,12),label,fill='#f4ead4')
image=Image.open(path).convert('RGB').crop((0,1780,900,2800));image.thumbnail((575,545));detail.paste(image,(i*600+12,52))
detail.save(P/'curtain-junction-detail.png')
frame_alpha=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A'))
foreground_frame_overlap=int(np.count_nonzero((fa>0)&(frame_alpha>0)))
report={
'status':'passed',
'scope':'Provisional Legendary Normal — suspended tent textile',
'canvas':[2000,2800],
'sourceArtwork':{'path':str(ART.relative_to(REPO)),'sha256':art_hash,'unchanged':sha(ART)==art_hash},
'template':{
'governingIdea':'Nameplate unchanged; a suspended linen field tucks behind the pillar of cloud and ties to the right frame',
'titleBackingPath':title_path,
'verseBackingPath':verse_path,
'compositionOrder':['base-art','frame-and-backings','attached-curtain-foreground','deterministic-text'],
'curtainContour':'foreground-curtain.svg',
'cloudUpperContour':cloud_top,
'cloudLowerContour':cloud_low,
'curtainAlphaBounds':list(Image.fromarray(fa).getbbox()),
'curtainFrameOrBackingOverlapPixels':foreground_frame_overlap,
},
'typography':{
'fontManifestSHA256':sha(FONTS/'manifest.json'),
'checks':checks,
'verseInkUnion':union,
'upperFlourishToVerseInk':above,
'verseInkToReferenceLineBox':below,
'centeringDifference':abs(above-below),
'lineBaselineGap':108,
},
'structuralChecks':{
'curtainTextIntersectionPixels':int(np.count_nonzero((ta>0)&(fa>0))),
'glyphPixelsOutsideOpaqueBacking':0,
'artworkSHA256Unchanged':True,
'fontGlyphCoverage':'passed',
'fontManifestHashes':'passed',
},
'notPerformed':['Borderless/Textless/Boundless composition','Production finish masks','Production text masks','Harness fixture installation','GPU moving-light validation','Final card approval'],
'staticReview':'Inspected at card size and enlarged cloud joins; provisional, pending user review',
'edgeRendering':{'geometry':'source-traced cubic Bezier paths','leadWidthMasterPixels':8,'renderScale':2,'downsample':'premultiplied RGBA LANCZOS','blur':False},
'foregroundAdaptation':'V4 cloud and nameplate preserved. Lower linen field now curves behind the cloud along its side; shallow sag exposes more robe. Lower text moves down with exact words, faces, sizes and horizontal alignment retained. A continuous near entrance post is authored over the cloud, with the textile left corner bound to it behind the cloud.'
}
(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n')
assert sha(ART)==art_hash
print(json.dumps({'status':'passed','preview':str((P/'composed-preview-1000.png').relative_to(REPO)),'curtainOverlapPixels':foreground_frame_overlap,'spacing':[above,below]},indent=2))
# Reproducible v3/v4 presentation and focused interaction review.
label_font=ImageFont.truetype(str(FONTS/'P052-Bold.otf'),26)
board=Image.new('RGB',(1500,1100),'#11161c');d=ImageDraw.Draw(board)
for i,(label,path) in enumerate([('Astra v4 — rigid lower plaque',P.parent/'layout-preview-astra-v4/composed-preview-1000.png'),('Astra v5 — suspended textile',P/'composed-preview-1000.png')]):
d.text((i*750+24,18),label,font=label_font,fill='#f4ead4')
im=Image.open(path).convert('RGB');im.thumbnail((730,1022));board.paste(im,(i*750+10,63))
board.save(P/'v4-v5-comparison.png')
master=Image.open(P/'composed-preview-2000.png').convert('RGB')
detail=Image.new('RGB',(1500,1050),'#11161c');d=ImageDraw.Draw(detail)
for label,box,origin,size in [('Cloud crowns the name',(570,0,1140,470),(22,65),(650,536)),('Pillar enters the verse field',(220,1960,850,2580),(795,65),(650,640))]:
d.text((origin[0],20),label,font=label_font,fill='#f4ead4')
im=master.crop(box);im.thumbnail(size);detail.paste(im,origin)
im=master.crop((250,2080,1980,2780));im.thumbnail((1450,320));detail.paste(im,(25,722))
detail.save(P/'cloud-interaction-detail.png')
# The approved nameplate and cloud crown remain pixel-identical.
previous=P.parent/'layout-preview-astra-v4'
current_image=np.asarray(Image.open(P/'composed-preview-2000.png'))
previous_image=np.asarray(Image.open(previous/'composed-preview-2000.png'))
assert np.array_equal(current_image[:200],previous_image[:200])
assert np.array_equal(current_image[:500,600:],previous_image[:500,600:])
assert np.array_equal(np.asarray(Image.open(P/'title-ink.png')),np.asarray(Image.open(previous/'title-ink.png')))
report['structuralChecks']['titleAndCloudCrownRegionIdenticalToV4']=True
report['structuralChecks']['titlePixelsIdenticalToV4']=True
report['structuralChecks']['nearEntrancePostAuthored']=True
report['template']['nearEntrancePostPath']=post_path
report['template']['leftTextileAnchor']=[340,2106]
report['edgeRendering']['alphaEdge']='Premultiplied before downsampling; no transparent-RGB halo'
(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n')
# Matched lower card crops show the textile silhouette and interaction.
board=Image.new('RGB',(1480,930),'#11161c');d=ImageDraw.Draw(board)
for col,(label,folder) in enumerate([('V4 — rigid plaque',previous),('V5 — suspended textile',P)]):
d.text((col*740+22,18),label,font=label_font,fill='#f4ead4')
im=Image.open(folder/'composed-preview-2000.png').convert('RGB')
crop=im.crop((190,1940,2000,2800));crop.thumbnail((710,850));board.paste(crop,(col*740+15,75))
detail=im.crop((340,2050,830,2790));detail.thumbnail((380,485));board.paste(detail,(col*740+185,425))
board.save(P/'v4-v5-junction-comparison.png')

View File

@@ -0,0 +1 @@
<fontconfig><dir>/home/dkzver/docker/sanctification/fonts</dir><cachedir>/home/dkzver/docker/sanctification/in-progress/cards/BP-001-moses/revisions/v03/review/layout-preview-astra-v5/font-cache</cachedir></fontconfig>

View File

@@ -0,0 +1,192 @@
{
"status": "passed",
"scope": "Provisional Legendary Normal \u2014 suspended tent textile",
"canvas": [
2000,
2800
],
"sourceArtwork": {
"path": "in-progress/cards/BP-001-moses/revisions/v03/source/art-master.png",
"sha256": "73f933cf34719dfda09b719376948bcf072bec504a88329b5a22e4ac1ed76d85",
"unchanged": true
},
"template": {
"governingIdea": "Nameplate unchanged; a suspended linen field tucks behind the pillar of cloud and ties to the right frame",
"titleBackingPath": "M700 62 H1900 Q1940 62 1940 102 V342 H700Z",
"verseBackingPath": "M340 2106 C445 2114 550 2118 655 2140 C960 2230 1435 2260 1960 2095 L1960 2720 C1535 2782 1045 2728 580 2750 C488 2755 437 2684 453 2592 C462 2506 520 2470 574 2404 C625 2341 641 2230 655 2140 L340 2106Z",
"compositionOrder": [
"base-art",
"frame-and-backings",
"attached-curtain-foreground",
"deterministic-text"
],
"curtainContour": "foreground-curtain.svg",
"cloudUpperContour": "M714 0 C741 25 750 55 746 82 C787 58 814 43 846 45 C926 50 980 112 952 191 C1008 238 1002 319 925 369 L730 475 H0 V0Z",
"cloudLowerContour": "M0 400H352 V1780H765 C765 1920 748 2010 736 2080 C728 2148 722 2213 710 2262 C704 2290 684 2310 660 2323 C650 2348 629 2367 608 2374 C599 2432 541 2477 480 2479 C435 2481 375 2418 354 2350 L350 2633 Q286 2659 205 2635 Q101 2583 0 2612Z",
"curtainAlphaBounds": [
0,
0,
996,
2714
],
"curtainFrameOrBackingOverlapPixels": 189298,
"nearEntrancePostPath": "M326 250 L360 250 L350 2640 L316 2637Z",
"leftTextileAnchor": [
340,
2106
]
},
"typography": {
"fontManifestSHA256": "ef68ee6393981d54ca068a950f21c53b374d20e6e46c21c788f36380d3aae178",
"checks": [
{
"id": "title",
"text": "MOSES",
"baseline": 244,
"font": "P052",
"weight": 700,
"size": 142,
"inkBounds": [
1202,
145,
1711,
247
],
"safeBounds": [
1050,
90,
1905,
330
],
"glyphPixelsOutsideOpaqueBacking": 0,
"foregroundIntersectionPixels": 0
},
{
"id": "verse-0",
"text": "And there has not arisen a",
"baseline": 2360,
"font": "Sanctification P052",
"weight": 500,
"size": 74,
"inkBounds": [
890,
2306,
1749,
2362
],
"safeBounds": [
730,
2275,
1900,
2730
],
"glyphPixelsOutsideOpaqueBacking": 0,
"foregroundIntersectionPixels": 0
},
{
"id": "verse-1",
"text": "prophet since in Israel like Moses,",
"baseline": 2468,
"font": "Sanctification P052",
"weight": 500,
"size": 74,
"inkBounds": [
769,
2414,
1869,
2490
],
"safeBounds": [
730,
2275,
1900,
2730
],
"glyphPixelsOutsideOpaqueBacking": 0,
"foregroundIntersectionPixels": 0
},
{
"id": "verse-2",
"text": "whom the LORD knew face to face",
"baseline": 2576,
"font": "Sanctification P052",
"weight": 500,
"size": 74,
"inkBounds": [
752,
2521,
1886,
2578
],
"safeBounds": [
730,
2275,
1900,
2730
],
"glyphPixelsOutsideOpaqueBacking": 0,
"foregroundIntersectionPixels": 0
},
{
"id": "reference",
"text": "Deuteronomy 34:10 \u2022 ESV",
"baseline": 2690,
"font": "Sanctification P052",
"weight": 500,
"size": 57,
"inkBounds": [
991,
2649,
1649,
2707
],
"safeBounds": [
730,
2275,
1900,
2730
],
"glyphPixelsOutsideOpaqueBacking": 0,
"foregroundIntersectionPixels": 0
}
],
"verseInkUnion": [
752,
2306,
1886,
2578
],
"upperFlourishToVerseInk": 66,
"verseInkToReferenceLineBox": 71,
"centeringDifference": 5,
"lineBaselineGap": 108
},
"structuralChecks": {
"curtainTextIntersectionPixels": 0,
"glyphPixelsOutsideOpaqueBacking": 0,
"artworkSHA256Unchanged": true,
"fontGlyphCoverage": "passed",
"fontManifestHashes": "passed",
"titleAndCloudCrownRegionIdenticalToV4": true,
"titlePixelsIdenticalToV4": true,
"nearEntrancePostAuthored": true
},
"notPerformed": [
"Borderless/Textless/Boundless composition",
"Production finish masks",
"Production text masks",
"Harness fixture installation",
"GPU moving-light validation",
"Final card approval"
],
"staticReview": "Inspected at card size and enlarged cloud joins; provisional, pending user review",
"edgeRendering": {
"geometry": "source-traced cubic Bezier paths",
"leadWidthMasterPixels": 8,
"renderScale": 2,
"downsample": "premultiplied RGBA LANCZOS",
"blur": false,
"alphaEdge": "Premultiplied before downsampling; no transparent-RGB halo"
},
"foregroundAdaptation": "V4 cloud and nameplate preserved. Lower linen field now curves behind the cloud along its side; shallow sag exposes more robe. Lower text moves down with exact words, faces, sizes and horizontal alignment retained. A continuous near entrance post is authored over the cloud, with the textile left corner bound to it behind the cloud."
}

View File

@@ -0,0 +1,36 @@
# Moses — provisional Legendary threshold composition
This requested preview tests the selected v03 illustration with real P052 typography and a bespoke threshold-driven Normal layout. It is a composition candidate, not production assembly.
- [Composed preview](composed-preview-1000.png)
- [500 × 700 preview](composed-preview-500.png)
- [Curtain comparison](curtain-comparison.png)
- [Enlarged curtain/backing junction](curtain-junction-detail.png)
- [Authored curtain foreground](foreground-curtain.svg)
- [Frame and backing source](border-backing-preview.svg)
- [Deterministic text source](text-preview.svg)
- [Validation](preview-validation.json)
- [Reproducible builder](build-preview.py)
## Governing idea
The outer frame uses narrow antique gold, lapis, ivory and tent-derived geometric bands. The upper-right plaque occupies open sky without covering Moses. The lower ivory textile backing begins below his folded hands and reserves a wide central-right field for the four-line verse.
The complete attached burgundy curtain is selected from the original illustration with an authored full-canvas contour. It composes above the frame and backing, crossing the left perimeter and far-left backing corner while remaining visibly attached to the tent. It does not intersect the lettering. Moses stays behind the backing, avoiding a second character-mantle treatment like David.
The narrative verse is presented without quotation marks:
And there has not arisen<br>
a prophet since in Israel<br>
like Moses, whom the LORD<br>
knew face to face
Deuteronomy 34:10 • ESV
P052 Bold 700 renders MOSES at 138px. Sanctification P052 Medium 500 renders the verse at 80px with 94px baselines and the reference at 57px. The measured verse ink has 54 master pixels above and 53 below within its reserved band.
## Scope
Only the provisional Normal composition is rendered. The proposed printing behavior remains a hypothesis until this layout is accepted: Normal would use frame, backings, curtain, and text; Borderless would use backings, curtain, and text; Textless would use frame and curtain; Boundless would use the unmodified illustration.
No production printings, finish masks, text masks, harness fixtures, moving-light review, or final approval were created.

View File

@@ -0,0 +1,150 @@
#!/usr/bin/env python3
"""Provisional BP-001 Moses Legendary composition; no production assembly."""
from pathlib import Path
import hashlib,json,os,subprocess
from xml.sax.saxutils import escape
import numpy as np
from PIL import Image,ImageDraw,ImageFont
P=Path(__file__).resolve().parent
REV=P.parents[1]
REPO=next(p for p in P.parents if (p/'docs/card-layer-pipeline.md').is_file())
ART=REV/'source/art-master.png'
FONTS=REPO/'fonts'
sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest()
art_hash=sha(ART)
font_manifest=json.loads((FONTS/'manifest.json').read_text())
font_files={'title':'P052-Bold.otf','verse':'SanctificationP052-Medium.otf','reference':'SanctificationP052-Medium.otf'}
for name in set(font_files.values()):
assert sha(FONTS/name)==font_manifest['files'][name]['sha256']
(P/'font-cache').mkdir(exist_ok=True)
(P/'fonts.conf').write_text(f'<fontconfig><dir>{FONTS}</dir><cachedir>{P/"font-cache"}</cachedir></fontconfig>')
env=dict(os.environ,FONTCONFIG_FILE=str(P/'fonts.conf'))
def ink(*args):return subprocess.check_output(['inkscape',*map(str,args)],env=env,text=True,stderr=subprocess.PIPE).strip()
def render(src,dst):ink(src,'--export-type=png',f'--export-filename={dst}','--export-width=2000','--export-height=2800')
def resolve(family,style):return Path(subprocess.check_output(['fc-match','-f','%{file}',f'{family}:style={style}'],env=env,text=True).strip()).resolve()
assert resolve('P052','Bold')==(FONTS/'P052-Bold.otf').resolve()
assert resolve('Sanctification P052','Medium')==(FONTS/'SanctificationP052-Medium.otf').resolve()
head='<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="2000" height="2800" viewBox="0 0 2000 2800">'
defs='''<defs>
<linearGradient id="paper" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#fbf3df"/><stop offset=".5" stop-color="#f4ead4"/><stop offset="1" stop-color="#e8d4b0"/></linearGradient>
<linearGradient id="gold" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#f0d47d"/><stop offset=".48" stop-color="#bd8b31"/><stop offset="1" stop-color="#71501e"/></linearGradient>
<pattern id="weave" width="44" height="44" patternUnits="userSpaceOnUse"><path d="M0 11H44 M0 33H44 M11 0V44 M33 0V44" stroke="#9d783d" stroke-width="1" opacity=".045"/><path d="M22 7l7 15-7 15-7-15Z" fill="none" stroke="#806438" stroke-width="1" opacity=".07"/></pattern>
<pattern id="lapis-band" width="34" height="34" patternUnits="userSpaceOnUse"><path d="M17 2L32 17 17 32 2 17Z" fill="#173e59" stroke="#d7b35a" stroke-width="3"/><circle cx="17" cy="17" r="4" fill="url(#gold)"/></pattern>
<clipPath id="curtain-clip"><path d="M-10-10 H770 C734 204 694 395 650 585 C610 770 566 972 522 1170 C474 1388 426 1604 378 1818 C338 2000 302 2190 286 2380 C276 2510 304 2640 412 2810 H-10Z"/></clipPath>
</defs>'''
base='<image xlink:href="../../source/art-master.png" width="2000" height="2800"/>'
frame='''<g id="legendary-frame">
<rect x="14" y="14" width="1972" height="2772" rx="34" fill="none" stroke="#2c2115" stroke-width="28"/>
<rect x="25" y="25" width="1950" height="2750" rx="27" fill="none" stroke="url(#gold)" stroke-width="14"/>
<rect x="43" y="43" width="1914" height="2714" rx="20" fill="none" stroke="#173e59" stroke-width="10"/>
<rect x="55" y="55" width="1890" height="2690" rx="15" fill="none" stroke="#d7b35a" stroke-width="4"/>
<path d="M68 260V68H260 M1740 68H1932V260 M68 2540V2732H260 M1740 2732H1932V2540" fill="none" stroke="#9b6d26" stroke-width="6"/>
<g fill="url(#gold)" stroke="#533810" stroke-width="3"><path d="M68 68l14 14-14 14-14-14Z"/><path d="M1932 68l14 14-14 14-14-14Z"/><path d="M68 2732l14 14-14 14-14-14Z"/><path d="M1932 2732l14 14-14 14-14-14Z"/></g>
<rect x="1906" y="430" width="27" height="255" fill="url(#lapis-band)" opacity=".95"/><rect x="1906" y="2115" width="27" height="255" fill="url(#lapis-band)" opacity=".95"/>
</g>'''
title_path='M1110 66 H1888 L1938 116 V320 L1895 364 H1110 L1060 314 V116 Z'
verse_path='M205 2075 H1892 Q1942 2075 1942 2125 V2715 Q1942 2765 1892 2765 H205 Q164 2765 164 2724 V2116 Q164 2075 205 2075 Z'
title_inner='<path d="M1125 84 H1878 L1918 124 V310 L1884 344 H1124 L1080 302 V126 Z" fill="none" stroke="#9b732b" stroke-width="3"/><path d="M1326 312H1438 M1562 312H1674" stroke="#98702a" stroke-width="4"/><path d="M1500 299l13 13-13 13-13-13Z" fill="url(#gold)" stroke="#654516" stroke-width="2"/>'
verse_inner='<path d="M222 2095 H1882 Q1922 2095 1922 2135 V2705 Q1922 2745 1882 2745 H220 Q184 2745 184 2709 V2131 Q184 2095 222 2095Z" fill="none" stroke="#9b732b" stroke-width="3"/><path d="M1138 2150H1268 M1372 2150H1502" stroke="#98702a" stroke-width="4"/><path d="M1320 2137l13 13-13 13-13-13Z" fill="url(#gold)" stroke="#654516" stroke-width="2"/><path d="M1148 2705H1273 M1367 2705H1492" stroke="#98702a" stroke-width="4"/><path d="M1320 2692l13 13-13 13-13-13Z" fill="url(#gold)" stroke="#654516" stroke-width="2"/><path d="M1880 2180v120 M1880 2538v120" stroke="#173e59" stroke-width="7"/><path d="M1863 2180h34v120h-34z M1863 2538h34v120h-34z" fill="url(#lapis-band)" stroke="#8c6827" stroke-width="3"/>'
def panel(path,inner,ident):return f'<g id="{ident}"><path d="{path}" fill="url(#paper)" stroke="#5f431d" stroke-width="10"/><path d="{path}" fill="url(#weave)" stroke="url(#gold)" stroke-width="5"/>{inner}</g>'
panels=panel(title_path,title_inner,'title-backing')+panel(verse_path,verse_inner,'verse-backing')
curtain=f'<g id="curtain-foreground" clip-path="url(#curtain-clip)"><image xlink:href="../../source/art-master.png" width="2000" height="2800"/></g>'
(P/'foreground-curtain.svg').write_text(head+defs+curtain+'</svg>');render(P/'foreground-curtain.svg',P/'foreground-curtain.png')
fg=Image.open(P/'foreground-curtain.png').convert('RGBA')
fa=np.asarray(fg.getchannel('A'))
assert fa.max()==255 and fa.min()==0
rows=[
('title','MOSES',1500,244,138,'P052',700),
('verse-0','And there has not arisen',1320,2276,80,'Sanctification P052',500),
('verse-1','a prophet since in Israel',1320,2370,80,'Sanctification P052',500),
('verse-2','like Moses, whom the LORD',1320,2464,80,'Sanctification P052',500),
('verse-3','knew face to face',1320,2558,80,'Sanctification P052',500),
('reference','Deuteronomy 34:10 • ESV',1320,2655,57,'Sanctification P052',500),
]
texts=''.join(f'<text id="{ident}" x="{x}" y="{y}" font-family="{family}" font-weight="{weight}" font-size="{size}" text-anchor="middle" fill="#263f50">{escape(text)}</text>' for ident,text,x,y,size,family,weight in rows)
(P/'text-preview.svg').write_text(head+texts+'</svg>');render(P/'text-preview.svg',P/'text-preview.png')
text_image=Image.open(P/'text-preview.png').convert('RGBA');ta=np.asarray(text_image.getchannel('A'))
assert not np.any((ta>0)&(fa>0))
(P/'border-backing-preview.svg').write_text(head+defs+frame+panels+'</svg>');render(P/'border-backing-preview.svg',P/'border-backing-preview.png')
backing=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A'))
checks=[];verse_bounds=[]
for ident,text,x,y,size,family,weight in rows:
node=next(v for v in texts.split('</text>') if f'id="{ident}"' in v)+'</text>'
src=P/(ident+'-ink.svg');dst=P/(ident+'-ink.png');src.write_text(head+node+'</svg>');render(src,dst)
alpha=np.asarray(Image.open(dst).convert('RGBA').getchannel('A'));ys,xs=np.where(alpha>0);assert len(xs)
bounds=[int(xs.min()),int(ys.min()),int(xs.max()+1),int(ys.max()+1)]
safe=[1100,90,1905,340] if ident=='title' else [570,2165,1900,2690]
assert safe[0]<=bounds[0] and safe[1]<=bounds[1] and bounds[2]<=safe[2] and bounds[3]<=safe[3],(ident,bounds,safe)
assert np.all(backing[alpha>0]==255),ident
assert not np.any((alpha>0)&(fa>0)),ident
checks.append({'id':ident,'text':text,'baseline':y,'font':family,'weight':weight,'size':size,'inkBounds':bounds,'safeBounds':safe,'glyphPixelsOutsideOpaqueBacking':0,'foregroundIntersectionPixels':0})
if ident.startswith('verse-'):verse_bounds.append(bounds)
assert ' '.join(c['text'] for c in checks if c['id'].startswith('verse-'))==json.loads((REV/'card.json').read_text())['excerpt']
assert checks[-1]['text']==json.loads((REV/'card.json').read_text())['referenceDisplay']
union=[min(b[0] for b in verse_bounds),min(b[1] for b in verse_bounds),max(b[2] for b in verse_bounds),max(b[3] for b in verse_bounds)]
upper=2163;ref_top=2613;above=union[1]-upper;below=ref_top-union[3]
assert min(above,below)>=35 and abs(above-below)<=4,(above,below)
for name,include_curtain in [('composed-preview',True),('without-curtain',False)]:
body=base+frame+panels+(curtain if include_curtain else '')+texts
src=P/(name+'.svg');src.write_text(head+defs+body+'</svg>');render(src,P/(name+'-2000.png'))
master=Image.open(P/(name+'-2000.png')).convert('RGB')
master.resize((1000,1400),Image.Resampling.LANCZOS).save(P/(name+'-1000.png'))
master.resize((500,700),Image.Resampling.LANCZOS).save(P/(name+'-500.png'))
# Compare the structural effect of the attached curtain.
board=Image.new('RGB',(1000,750),'#11161c');draw=ImageDraw.Draw(board)
for i,(label,path) in enumerate([('Backing and frame alone',P/'without-curtain-1000.png'),('Threshold curtain overlap',P/'composed-preview-1000.png')]):
draw.text((i*500+12,12),label,fill='#f4ead4')
image=Image.open(path).convert('RGB');image.thumbnail((480,672));board.paste(image,(i*500+10,45))
board.save(P/'curtain-comparison.png')
# A focused junction comparison at master crop.
detail=Image.new('RGB',(1200,620),'#11161c');draw=ImageDraw.Draw(detail)
for i,(label,path) in enumerate([('Without foreground',P/'without-curtain-2000.png'),('Curtain over frame/backing',P/'composed-preview-2000.png')]):
draw.text((i*600+12,12),label,fill='#f4ead4')
image=Image.open(path).convert('RGB').crop((0,1780,900,2800));image.thumbnail((575,545));detail.paste(image,(i*600+12,52))
detail.save(P/'curtain-junction-detail.png')
frame_alpha=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A'))
foreground_frame_overlap=int(np.count_nonzero((fa>0)&(frame_alpha>0)))
report={
'status':'passed',
'scope':'Provisional Legendary Normal composition only',
'canvas':[2000,2800],
'sourceArtwork':{'path':str(ART.relative_to(REPO)),'sha256':art_hash,'unchanged':sha(ART)==art_hash},
'template':{
'governingIdea':'Tent threshold',
'titleBackingPath':title_path,
'verseBackingPath':verse_path,
'compositionOrder':['base-art','frame-and-backings','attached-curtain-foreground','deterministic-text'],
'curtainContour':'foreground-curtain.svg',
'curtainAlphaBounds':list(Image.fromarray(fa).getbbox()),
'curtainFrameOrBackingOverlapPixels':foreground_frame_overlap,
},
'typography':{
'fontManifestSHA256':sha(FONTS/'manifest.json'),
'checks':checks,
'verseInkUnion':union,
'upperFlourishToVerseInk':above,
'verseInkToReferenceLineBox':below,
'centeringDifference':abs(above-below),
'lineBaselineGap':94,
},
'structuralChecks':{
'curtainTextIntersectionPixels':int(np.count_nonzero((ta>0)&(fa>0))),
'glyphPixelsOutsideOpaqueBacking':0,
'artworkSHA256Unchanged':True,
},
'notPerformed':['Borderless/Textless/Boundless composition','Production finish masks','Production text masks','Harness fixture installation','GPU moving-light validation','Final card approval'],
'staticReview':'pending user review'
}
(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n')
assert sha(ART)==art_hash
print(json.dumps({'status':'passed','preview':str((P/'composed-preview-1000.png').relative_to(REPO)),'curtainOverlapPixels':foreground_frame_overlap,'spacing':[above,below]},indent=2))

View File

@@ -0,0 +1 @@
<fontconfig><dir>/home/dkzver/docker/sanctification/fonts</dir><cachedir>/home/dkzver/docker/sanctification/in-progress/cards/BP-001-moses/revisions/v03/review/layout-preview-v1/font-cache</cachedir></fontconfig>

View File

@@ -0,0 +1,193 @@
{
"status": "passed",
"scope": "Provisional Legendary Normal composition only",
"canvas": [
2000,
2800
],
"sourceArtwork": {
"path": "in-progress/cards/BP-001-moses/revisions/v03/source/art-master.png",
"sha256": "73f933cf34719dfda09b719376948bcf072bec504a88329b5a22e4ac1ed76d85",
"unchanged": true
},
"template": {
"governingIdea": "Tent threshold",
"titleBackingPath": "M1110 66 H1888 L1938 116 V320 L1895 364 H1110 L1060 314 V116 Z",
"verseBackingPath": "M205 2075 H1892 Q1942 2075 1942 2125 V2715 Q1942 2765 1892 2765 H205 Q164 2765 164 2724 V2116 Q164 2075 205 2075 Z",
"compositionOrder": [
"base-art",
"frame-and-backings",
"attached-curtain-foreground",
"deterministic-text"
],
"curtainContour": "foreground-curtain.svg",
"curtainAlphaBounds": [
0,
0,
769,
2800
],
"curtainFrameOrBackingOverlapPixels": 283408
},
"typography": {
"fontManifestSHA256": "ef68ee6393981d54ca068a950f21c53b374d20e6e46c21c788f36380d3aae178",
"checks": [
{
"id": "title",
"text": "MOSES",
"baseline": 244,
"font": "P052",
"weight": 700,
"size": 138,
"inkBounds": [
1251,
148,
1746,
247
],
"safeBounds": [
1100,
90,
1905,
340
],
"glyphPixelsOutsideOpaqueBacking": 0,
"foregroundIntersectionPixels": 0
},
{
"id": "verse-0",
"text": "And there has not arisen",
"baseline": 2276,
"font": "Sanctification P052",
"weight": 500,
"size": 80,
"inkBounds": [
886,
2217,
1755,
2278
],
"safeBounds": [
570,
2165,
1900,
2690
],
"glyphPixelsOutsideOpaqueBacking": 0,
"foregroundIntersectionPixels": 0
},
{
"id": "verse-1",
"text": "a prophet since in Israel",
"baseline": 2370,
"font": "Sanctification P052",
"weight": 500,
"size": 80,
"inkBounds": [
902,
2311,
1739,
2393
],
"safeBounds": [
570,
2165,
1900,
2690
],
"glyphPixelsOutsideOpaqueBacking": 0,
"foregroundIntersectionPixels": 0
},
{
"id": "verse-2",
"text": "like Moses, whom the LORD",
"baseline": 2464,
"font": "Sanctification P052",
"weight": 500,
"size": 80,
"inkBounds": [
813,
2405,
1827,
2477
],
"safeBounds": [
570,
2165,
1900,
2690
],
"glyphPixelsOutsideOpaqueBacking": 0,
"foregroundIntersectionPixels": 0
},
{
"id": "verse-3",
"text": "knew face to face",
"baseline": 2558,
"font": "Sanctification P052",
"weight": 500,
"size": 80,
"inkBounds": [
1019,
2499,
1620,
2560
],
"safeBounds": [
570,
2165,
1900,
2690
],
"glyphPixelsOutsideOpaqueBacking": 0,
"foregroundIntersectionPixels": 0
},
{
"id": "reference",
"text": "Deuteronomy 34:10 \u2022 ESV",
"baseline": 2655,
"font": "Sanctification P052",
"weight": 500,
"size": 57,
"inkBounds": [
991,
2614,
1649,
2672
],
"safeBounds": [
570,
2165,
1900,
2690
],
"glyphPixelsOutsideOpaqueBacking": 0,
"foregroundIntersectionPixels": 0
}
],
"verseInkUnion": [
813,
2217,
1827,
2560
],
"upperFlourishToVerseInk": 54,
"verseInkToReferenceLineBox": 53,
"centeringDifference": 1,
"lineBaselineGap": 94
},
"structuralChecks": {
"curtainTextIntersectionPixels": 0,
"glyphPixelsOutsideOpaqueBacking": 0,
"artworkSHA256Unchanged": true
},
"notPerformed": [
"Borderless/Textless/Boundless composition",
"Production finish masks",
"Production text masks",
"Harness fixture installation",
"GPU moving-light validation",
"Final card approval"
],
"staticReview": "pending user review"
}

View File

@@ -0,0 +1,36 @@
# Moses — provisional Legendary compact textile composition
This preview revises the selected v03 illustration with a narrower, tent-derived backing and a three-line verse setting. It is a composition candidate, not production assembly.
- [Composed preview](composed-preview-1000.png)
- [500 × 700 preview](composed-preview-500.png)
- [v1 / v2 backing comparison](backing-comparison.png)
- [Curtain comparison](curtain-comparison.png)
- [Enlarged curtain/backing junction](curtain-junction-detail.png)
- [Authored curtain foreground](foreground-curtain.svg)
- [Frame and backing source](border-backing-preview.svg)
- [Deterministic text source](text-preview.svg)
- [Validation](preview-validation.json)
- [Reproducible builder](build-preview.py)
## Governing idea
The outer frame uses narrow antique gold, lapis, ivory and tent-derived geometric bands. The upper-right plaque now reads as a sloped canopy rather than a chamfered royal plaque. The lower ivory backing enters beneath the curtain, then steps inward to form a compact woven screen. Burgundy, lapis, and restrained gold bands replace the centered lozenge flourish language used on David.
The attached burgundy curtain remains above the frame and backing. The backing no longer spans the unused lower-left bay: its visible edge follows the threshold transition, while the three-line verse expands across the usable textile field. Moses stays behind the backing.
The narrative verse is presented without quotation marks:
And there has not arisen a prophet since<br>
in Israel like Moses, whom the LORD<br>
knew face to face
Deuteronomy 34:10 • ESV
P052 Bold 700 renders MOSES at 138px. Sanctification P052 Medium 500 renders the verse at 72px with 104px baselines and the reference at 57px. Validation measures the actual verse ink between the upper textile band and the top of the reference.
## Scope
Only the provisional Normal composition is rendered. The proposed printing behavior remains a hypothesis until this layout is accepted: Normal would use frame, backings, curtain, and text; Borderless would use backings, curtain, and text; Textless would use frame and curtain; Boundless would use the unmodified illustration.
No production printings, finish masks, text masks, harness fixtures, moving-light review, or final approval were created.

View File

@@ -0,0 +1,149 @@
#!/usr/bin/env python3
"""Provisional BP-001 Moses Legendary composition; no production assembly."""
from pathlib import Path
import hashlib,json,os,subprocess
from xml.sax.saxutils import escape
import numpy as np
from PIL import Image,ImageDraw,ImageFont
P=Path(__file__).resolve().parent
REV=P.parents[1]
REPO=next(p for p in P.parents if (p/'docs/card-layer-pipeline.md').is_file())
ART=REV/'source/art-master.png'
FONTS=REPO/'fonts'
sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest()
art_hash=sha(ART)
font_manifest=json.loads((FONTS/'manifest.json').read_text())
font_files={'title':'P052-Bold.otf','verse':'SanctificationP052-Medium.otf','reference':'SanctificationP052-Medium.otf'}
for name in set(font_files.values()):
assert sha(FONTS/name)==font_manifest['files'][name]['sha256']
(P/'font-cache').mkdir(exist_ok=True)
(P/'fonts.conf').write_text(f'<fontconfig><dir>{FONTS}</dir><cachedir>{P/"font-cache"}</cachedir></fontconfig>')
env=dict(os.environ,FONTCONFIG_FILE=str(P/'fonts.conf'))
def ink(*args):return subprocess.check_output(['inkscape',*map(str,args)],env=env,text=True,stderr=subprocess.PIPE).strip()
def render(src,dst):ink(src,'--export-type=png',f'--export-filename={dst}','--export-width=2000','--export-height=2800')
def resolve(family,style):return Path(subprocess.check_output(['fc-match','-f','%{file}',f'{family}:style={style}'],env=env,text=True).strip()).resolve()
assert resolve('P052','Bold')==(FONTS/'P052-Bold.otf').resolve()
assert resolve('Sanctification P052','Medium')==(FONTS/'SanctificationP052-Medium.otf').resolve()
head='<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="2000" height="2800" viewBox="0 0 2000 2800">'
defs='''<defs>
<linearGradient id="paper" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#fbf3df"/><stop offset=".5" stop-color="#f4ead4"/><stop offset="1" stop-color="#e8d4b0"/></linearGradient>
<linearGradient id="gold" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#f0d47d"/><stop offset=".48" stop-color="#bd8b31"/><stop offset="1" stop-color="#71501e"/></linearGradient>
<pattern id="weave" width="44" height="44" patternUnits="userSpaceOnUse"><path d="M0 11H44 M0 33H44 M11 0V44 M33 0V44" stroke="#9d783d" stroke-width="1" opacity=".045"/><path d="M22 7l7 15-7 15-7-15Z" fill="none" stroke="#806438" stroke-width="1" opacity=".07"/></pattern>
<pattern id="lapis-band" width="34" height="34" patternUnits="userSpaceOnUse"><path d="M17 2L32 17 17 32 2 17Z" fill="#173e59" stroke="#d7b35a" stroke-width="3"/><circle cx="17" cy="17" r="4" fill="url(#gold)"/></pattern>
<clipPath id="curtain-clip"><path d="M-10-10 H770 C734 204 694 395 650 585 C610 770 566 972 522 1170 C474 1388 426 1604 378 1818 C338 2000 302 2190 286 2380 C276 2510 304 2640 412 2810 H-10Z"/></clipPath>
</defs>'''
base='<image xlink:href="../../source/art-master.png" width="2000" height="2800"/>'
frame='''<g id="legendary-frame">
<rect x="14" y="14" width="1972" height="2772" rx="34" fill="none" stroke="#2c2115" stroke-width="28"/>
<rect x="25" y="25" width="1950" height="2750" rx="27" fill="none" stroke="url(#gold)" stroke-width="14"/>
<rect x="43" y="43" width="1914" height="2714" rx="20" fill="none" stroke="#173e59" stroke-width="10"/>
<rect x="55" y="55" width="1890" height="2690" rx="15" fill="none" stroke="#d7b35a" stroke-width="4"/>
<path d="M68 260V68H260 M1740 68H1932V260 M68 2540V2732H260 M1740 2732H1932V2540" fill="none" stroke="#9b6d26" stroke-width="6"/>
<g fill="url(#gold)" stroke="#533810" stroke-width="3"><path d="M68 68l14 14-14 14-14-14Z"/><path d="M1932 68l14 14-14 14-14-14Z"/><path d="M68 2732l14 14-14 14-14-14Z"/><path d="M1932 2732l14 14-14 14-14-14Z"/></g>
<rect x="1906" y="430" width="27" height="255" fill="url(#lapis-band)" opacity=".95"/><rect x="1906" y="2115" width="27" height="255" fill="url(#lapis-band)" opacity=".95"/>
</g>'''
title_path='M1080 72 H1888 L1940 124 V342 H1045 V148 Z'
verse_path='M445 2088 H1896 Q1942 2088 1942 2134 V2719 Q1942 2765 1896 2765 H495 Q460 2765 460 2730 V2260 L405 2190 V2128 Q405 2088 445 2088 Z'
title_inner='<path d="M1092 92 H1878 L1918 132 V320 H1067 V156 Z" fill="none" stroke="#9b732b" stroke-width="3"/><path d="M1110 299H1880" stroke="#7f2934" stroke-width="4"/><path d="M1110 309H1880" stroke="#173e59" stroke-width="5"/><path d="M1120 317H1870" stroke="#b98a32" stroke-width="2"/><g fill="#b98a32"><path d="M1190 297l9 9-9 9-9-9Z"/><path d="M1500 297l9 9-9 9-9-9Z"/><path d="M1810 297l9 9-9 9-9-9Z"/></g>'
verse_inner='<path d="M458 2108 H1882 Q1922 2108 1922 2148 V2705 Q1922 2745 1882 2745 H510 Q480 2745 480 2715 V2252 L425 2182 V2141 Q425 2108 458 2108Z" fill="none" stroke="#9b732b" stroke-width="3"/><path d="M650 2144H1840" stroke="#7f2934" stroke-width="4"/><path d="M650 2154H1840" stroke="#173e59" stroke-width="5"/><path d="M650 2162H1840" stroke="#b98a32" stroke-width="2"/><path d="M650 2691H1840" stroke="#7f2934" stroke-width="4"/><path d="M650 2701H1840" stroke="#173e59" stroke-width="5"/><path d="M650 2709H1840" stroke="#b98a32" stroke-width="2"/><g fill="#b98a32"><path d="M760 2142l10 10-10 10-10-10Z"/><path d="M1245 2142l10 10-10 10-10-10Z"/><path d="M1730 2142l10 10-10 10-10-10Z"/><path d="M760 2689l10 10-10 10-10-10Z"/><path d="M1245 2689l10 10-10 10-10-10Z"/><path d="M1730 2689l10 10-10 10-10-10Z"/></g>'
def panel(path,inner,ident):return f'<g id="{ident}"><path d="{path}" fill="url(#paper)" stroke="#5f431d" stroke-width="10"/><path d="{path}" fill="url(#weave)" stroke="url(#gold)" stroke-width="5"/>{inner}</g>'
panels=panel(title_path,title_inner,'title-backing')+panel(verse_path,verse_inner,'verse-backing')
curtain=f'<g id="curtain-foreground" clip-path="url(#curtain-clip)"><image xlink:href="../../source/art-master.png" width="2000" height="2800"/></g>'
(P/'foreground-curtain.svg').write_text(head+defs+curtain+'</svg>');render(P/'foreground-curtain.svg',P/'foreground-curtain.png')
fg=Image.open(P/'foreground-curtain.png').convert('RGBA')
fa=np.asarray(fg.getchannel('A'))
assert fa.max()==255 and fa.min()==0
rows=[
('title','MOSES',1500,244,138,'P052',700),
('verse-0','And there has not arisen a prophet since',1190,2308,72,'Sanctification P052',500),
('verse-1','in Israel like Moses, whom the LORD',1190,2412,72,'Sanctification P052',500),
('verse-2','knew face to face',1190,2516,72,'Sanctification P052',500),
('reference','Deuteronomy 34:10 • ESV',1190,2650,57,'Sanctification P052',500),
]
texts=''.join(f'<text id="{ident}" x="{x}" y="{y}" font-family="{family}" font-weight="{weight}" font-size="{size}" text-anchor="middle" fill="#263f50">{escape(text)}</text>' for ident,text,x,y,size,family,weight in rows)
(P/'text-preview.svg').write_text(head+texts+'</svg>');render(P/'text-preview.svg',P/'text-preview.png')
text_image=Image.open(P/'text-preview.png').convert('RGBA');ta=np.asarray(text_image.getchannel('A'))
assert not np.any((ta>0)&(fa>0))
(P/'border-backing-preview.svg').write_text(head+defs+frame+panels+'</svg>');render(P/'border-backing-preview.svg',P/'border-backing-preview.png')
backing=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A'))
checks=[];verse_bounds=[]
for ident,text,x,y,size,family,weight in rows:
node=next(v for v in texts.split('</text>') if f'id="{ident}"' in v)+'</text>'
src=P/(ident+'-ink.svg');dst=P/(ident+'-ink.png');src.write_text(head+node+'</svg>');render(src,dst)
alpha=np.asarray(Image.open(dst).convert('RGBA').getchannel('A'));ys,xs=np.where(alpha>0);assert len(xs)
bounds=[int(xs.min()),int(ys.min()),int(xs.max()+1),int(ys.max()+1)]
safe=[1080,90,1905,330] if ident=='title' else [500,2180,1900,2690]
assert safe[0]<=bounds[0] and safe[1]<=bounds[1] and bounds[2]<=safe[2] and bounds[3]<=safe[3],(ident,bounds,safe)
assert np.all(backing[alpha>0]==255),ident
assert not np.any((alpha>0)&(fa>0)),ident
checks.append({'id':ident,'text':text,'baseline':y,'font':family,'weight':weight,'size':size,'inkBounds':bounds,'safeBounds':safe,'glyphPixelsOutsideOpaqueBacking':0,'foregroundIntersectionPixels':0})
if ident.startswith('verse-'):verse_bounds.append(bounds)
assert ' '.join(c['text'] for c in checks if c['id'].startswith('verse-'))==json.loads((REV/'card.json').read_text())['excerpt']
assert checks[-1]['text']==json.loads((REV/'card.json').read_text())['referenceDisplay']
union=[min(b[0] for b in verse_bounds),min(b[1] for b in verse_bounds),max(b[2] for b in verse_bounds),max(b[3] for b in verse_bounds)]
upper=2165;ref_top=checks[-1]['inkBounds'][1];above=union[1]-upper;below=ref_top-union[3]
assert min(above,below)>=35 and abs(above-below)<=4,(above,below)
for name,include_curtain in [('composed-preview',True),('without-curtain',False)]:
body=base+frame+panels+(curtain if include_curtain else '')+texts
src=P/(name+'.svg');src.write_text(head+defs+body+'</svg>');render(src,P/(name+'-2000.png'))
master=Image.open(P/(name+'-2000.png')).convert('RGB')
master.resize((1000,1400),Image.Resampling.LANCZOS).save(P/(name+'-1000.png'))
master.resize((500,700),Image.Resampling.LANCZOS).save(P/(name+'-500.png'))
# Compare the structural effect of the attached curtain.
board=Image.new('RGB',(1000,750),'#11161c');draw=ImageDraw.Draw(board)
for i,(label,path) in enumerate([('Backing and frame alone',P/'without-curtain-1000.png'),('Threshold curtain overlap',P/'composed-preview-1000.png')]):
draw.text((i*500+12,12),label,fill='#f4ead4')
image=Image.open(path).convert('RGB');image.thumbnail((480,672));board.paste(image,(i*500+10,45))
board.save(P/'curtain-comparison.png')
# A focused junction comparison at master crop.
detail=Image.new('RGB',(1200,620),'#11161c');draw=ImageDraw.Draw(detail)
for i,(label,path) in enumerate([('Without foreground',P/'without-curtain-2000.png'),('Curtain over frame/backing',P/'composed-preview-2000.png')]):
draw.text((i*600+12,12),label,fill='#f4ead4')
image=Image.open(path).convert('RGB').crop((0,1780,900,2800));image.thumbnail((575,545));detail.paste(image,(i*600+12,52))
detail.save(P/'curtain-junction-detail.png')
frame_alpha=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A'))
foreground_frame_overlap=int(np.count_nonzero((fa>0)&(frame_alpha>0)))
report={
'status':'passed',
'scope':'Provisional Legendary Normal compact textile composition only',
'canvas':[2000,2800],
'sourceArtwork':{'path':str(ART.relative_to(REPO)),'sha256':art_hash,'unchanged':sha(ART)==art_hash},
'template':{
'governingIdea':'Tent threshold with compact woven screen',
'titleBackingPath':title_path,
'verseBackingPath':verse_path,
'compositionOrder':['base-art','frame-and-backings','attached-curtain-foreground','deterministic-text'],
'curtainContour':'foreground-curtain.svg',
'curtainAlphaBounds':list(Image.fromarray(fa).getbbox()),
'curtainFrameOrBackingOverlapPixels':foreground_frame_overlap,
},
'typography':{
'fontManifestSHA256':sha(FONTS/'manifest.json'),
'checks':checks,
'verseInkUnion':union,
'upperFlourishToVerseInk':above,
'verseInkToReferenceLineBox':below,
'centeringDifference':abs(above-below),
'lineBaselineGap':104,
},
'structuralChecks':{
'curtainTextIntersectionPixels':int(np.count_nonzero((ta>0)&(fa>0))),
'glyphPixelsOutsideOpaqueBacking':0,
'artworkSHA256Unchanged':True,
},
'notPerformed':['Borderless/Textless/Boundless composition','Production finish masks','Production text masks','Harness fixture installation','GPU moving-light validation','Final card approval'],
'staticReview':'pending user review'
}
(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n')
assert sha(ART)==art_hash
print(json.dumps({'status':'passed','preview':str((P/'composed-preview-1000.png').relative_to(REPO)),'curtainOverlapPixels':foreground_frame_overlap,'spacing':[above,below]},indent=2))

View File

@@ -0,0 +1 @@
<fontconfig><dir>/home/dkzver/docker/sanctification/fonts</dir><cachedir>/home/dkzver/docker/sanctification/in-progress/cards/BP-001-moses/revisions/v03/review/layout-preview-v2/font-cache</cachedir></fontconfig>

View File

@@ -0,0 +1,171 @@
{
"status": "passed",
"scope": "Provisional Legendary Normal compact textile composition only",
"canvas": [
2000,
2800
],
"sourceArtwork": {
"path": "in-progress/cards/BP-001-moses/revisions/v03/source/art-master.png",
"sha256": "73f933cf34719dfda09b719376948bcf072bec504a88329b5a22e4ac1ed76d85",
"unchanged": true
},
"template": {
"governingIdea": "Tent threshold with compact woven screen",
"titleBackingPath": "M1080 72 H1888 L1940 124 V342 H1045 V148 Z",
"verseBackingPath": "M445 2088 H1896 Q1942 2088 1942 2134 V2719 Q1942 2765 1896 2765 H495 Q460 2765 460 2730 V2260 L405 2190 V2128 Q405 2088 445 2088 Z",
"compositionOrder": [
"base-art",
"frame-and-backings",
"attached-curtain-foreground",
"deterministic-text"
],
"curtainContour": "foreground-curtain.svg",
"curtainAlphaBounds": [
0,
0,
769,
2800
],
"curtainFrameOrBackingOverlapPixels": 181809
},
"typography": {
"fontManifestSHA256": "ef68ee6393981d54ca068a950f21c53b374d20e6e46c21c788f36380d3aae178",
"checks": [
{
"id": "title",
"text": "MOSES",
"baseline": 244,
"font": "P052",
"weight": 700,
"size": 138,
"inkBounds": [
1251,
148,
1746,
247
],
"safeBounds": [
1080,
90,
1905,
330
],
"glyphPixelsOutsideOpaqueBacking": 0,
"foregroundIntersectionPixels": 0
},
{
"id": "verse-0",
"text": "And there has not arisen a prophet since",
"baseline": 2308,
"font": "Sanctification P052",
"weight": 500,
"size": 72,
"inkBounds": [
548,
2255,
1831,
2329
],
"safeBounds": [
500,
2180,
1900,
2690
],
"glyphPixelsOutsideOpaqueBacking": 0,
"foregroundIntersectionPixels": 0
},
{
"id": "verse-1",
"text": "in Israel like Moses, whom the LORD",
"baseline": 2412,
"font": "Sanctification P052",
"weight": 500,
"size": 72,
"inkBounds": [
598,
2359,
1782,
2424
],
"safeBounds": [
500,
2180,
1900,
2690
],
"glyphPixelsOutsideOpaqueBacking": 0,
"foregroundIntersectionPixels": 0
},
{
"id": "verse-2",
"text": "knew face to face",
"baseline": 2516,
"font": "Sanctification P052",
"weight": 500,
"size": 72,
"inkBounds": [
919,
2463,
1460,
2518
],
"safeBounds": [
500,
2180,
1900,
2690
],
"glyphPixelsOutsideOpaqueBacking": 0,
"foregroundIntersectionPixels": 0
},
{
"id": "reference",
"text": "Deuteronomy 34:10 \u2022 ESV",
"baseline": 2650,
"font": "Sanctification P052",
"weight": 500,
"size": 57,
"inkBounds": [
861,
2609,
1519,
2667
],
"safeBounds": [
500,
2180,
1900,
2690
],
"glyphPixelsOutsideOpaqueBacking": 0,
"foregroundIntersectionPixels": 0
}
],
"verseInkUnion": [
548,
2255,
1831,
2518
],
"upperFlourishToVerseInk": 90,
"verseInkToReferenceLineBox": 91,
"centeringDifference": 1,
"lineBaselineGap": 104
},
"structuralChecks": {
"curtainTextIntersectionPixels": 0,
"glyphPixelsOutsideOpaqueBacking": 0,
"artworkSHA256Unchanged": true
},
"notPerformed": [
"Borderless/Textless/Boundless composition",
"Production finish masks",
"Production text masks",
"Harness fixture installation",
"GPU moving-light validation",
"Final card approval"
],
"staticReview": "pending user review"
}

View File

@@ -0,0 +1,24 @@
{
"source": "art-native.png",
"sourceDimensions": [
1060,
1484
],
"canvas": [
2000,
2800
],
"method": "uniform centered cover fit",
"scale": 1.8867924528301887,
"scaledDimensions": [
2000.0,
2800.0
],
"cropOffset": [
0.0,
0.0
],
"resampling": "Pillow LANCZOS",
"sourceSHA256": "ab234b91b260dba3b1a6973d765fc987e59339c21ec790d0ce51528e8fd98157",
"outputSHA256": "73f933cf34719dfda09b719376948bcf072bec504a88329b5a22e4ac1ed76d85"
}

View File

@@ -0,0 +1,16 @@
Use case: style-transfer
Asset type: targeted v03 refinement of the supplied Moses stained-glass illustration for a Legendary Sanctification card.
Primary request: Keep this exact composition and its decisive non-realistic glass language, but make the craftsmanship richer and more intricate in carefully chosen regions. Simple and iconic at thumbnail size, finely worked and jewel-like up close. This is an EDIT of this image, not a new composition.
Strict invariants: preserve Moses's position, silhouette, size, left-facing lowered gaze, solemn expression, broad stylized amber facial planes, expressive ivory beard locks, calm posture and exact folded-hand gesture/anatomy/ownership. Preserve face and hands as broad legible planes, never realistic wrinkles, veins, pores, individual hair strands, or smooth anatomical gradients. Keep tent, attached hanging burgundy curtain and its full hem, cloud, camp, mountain horizon, robe colors and folds, and overall framing in the same positions. Curtain must stay visibly connected to its tent attachment for a future overlap. Keep non-anthropomorphic cloud and warm cloud-light/cool landscape relationship. Keep full-bleed 5:7.
Refinement: Introduce a clear hierarchy of primary structural leading and thinner secondary leading. Existing large pane silhouettes remain the composition's foundation. Selectively subdivide some of them into beautifully designed curving, tapered and angular smaller glass pieces which follow the forms. Do not simply add a uniform crack network.
1. Cloud at tent entrance and upper left: the main broad lobed forms stay readable, with interlocking smaller ivory, champagne, pale amber and cool opal panes nested selectively along bright transitions and curled edges. Some large quiet glowing pieces remain. This should feel skillfully assembled from precious glass, not cottony realism, glitter or repeated tiny tiles.
2. Attached burgundy curtain: preserve its long substantial drape and dark fold valleys. Add fine flowing secondary pane divisions following chosen folds, and restrained inset glass patterning along a narrow edge/hem band. Patterns belong to the cloth's construction; no new symbols, words or ornate fantasy brocade. Keep broad wine-colored rests between detailed passages.
3. Moses's robe: preserve the broad sapphire and burgundy masses. Refine select major fold ridges into nested elongated jewel panes and subtly contrasting blue/turquoise sub-panes. Add a restrained small geometric glass rhythm to the existing warm trim near sleeves and robe opening, sparse and subordinate. Face, beard and hands remain simpler than the cloth, retaining their present stylization.
4. Tent structure: finer lead joints and small warm glass facets around the existing supports and upper angled cloth edge, without changing architecture or adding decoration that competes with the figure.
5. Sky and mountains: selected thin layered panes at the gold-to-blue light transition and a few thoughtfully split violet mountain planes, while most sky remains broad and restful. No extra rays, halos or sun symbols.
6. Camp and terrain: keep simplified tent silhouettes and grouped vegetation. Add modest secondary pane rhythms to a few tent folds and stepping ground planes near the foreground, not realistic tiny life or extra objects. Keep distant camp quieter than Moses.
Material: jewel-colored worked glass, delicate pane-contained brushed/etched satin texture, richer selective bounded color transitions and purposeful form-following highlights. Occasional fine painted-glass accents within panes, not photorealistic shading. Craft detail rather than all-over grain. Gold is restrained warm glass trim, not metallic decoration everywhere.
Avoid: same shard size/density throughout, uniform tessellation, micro-mosaic, random cracks, omnipresent speckles or sparkles, noisy ornament, gaudy gold, realistic face/hands/landscape, clutter, altered pose, altered hand anatomy, new props or narrative events. No text, border, card frame, backing panels, watermark or literal divine figure.

View File

@@ -0,0 +1,16 @@
#!/usr/bin/env python3
"""Reproduce only the fitted art and review sizes; never assemble a card."""
from pathlib import Path
from PIL import Image, ImageOps
import hashlib,json
root=Path(__file__).resolve().parent.parent
native=root/'source/art-native.png'
with Image.open(native) as source:
im=source.convert('RGB');w,h=im.size
scale=max(2000/w,2800/h)
art=ImageOps.fit(im,(2000,2800),Image.Resampling.LANCZOS,centering=(.5,.5))
art.save(root/'source/art-master.png')
for name,size in [('art-review.png',(1000,1400)),('art-thumbnail.png',(250,350))]:
art.resize(size,Image.Resampling.LANCZOS).save(root/'review'/name)
fit={'source':'art-native.png','sourceDimensions':[w,h],'canvas':[2000,2800],'method':'uniform centered cover fit','scale':scale,'scaledDimensions':[w*scale,h*scale],'cropOffset':[(w*scale-2000)/2,(h*scale-2800)/2],'resampling':'Pillow LANCZOS','sourceSHA256':hashlib.sha256(native.read_bytes()).hexdigest(),'outputSHA256':hashlib.sha256((root/'source/art-master.png').read_bytes()).hexdigest()}
(root/'source/art-fit.json').write_text(json.dumps(fit,indent=2)+'\n')

View File

@@ -0,0 +1,16 @@
{
"mode": "built-in imagegen edit",
"artDirectionModel": "gpt-6-astra",
"artDirectionReasoning": "medium",
"prompt": "edit-prompt.txt",
"native": "art-native.png",
"originalGeneratedPath": "/home/dkzver/.codex/generated_images/01a0acd6-0b11-7932-a7f3-c69f609e9974/exec-1638b62b-56e6-4a1a-8198-8420dfb9f761.png",
"reference": "edit-reference.jpg",
"referenceSHA256": "0f6754052e7e739cf00a8f8360cb922112b2aa8501b8adfe981d9544e314108f",
"referenceProvenance": "900x1260 JPEG quality 90 made from v02 source/art-native.png and shown in conversation; native-path tool reads unavailable due to sandbox initialization failure, so num_last_images_to_include=1 was used.",
"priorNative": "../../v02/source/art-native.png",
"priorNativeSHA256": "145d11849d97e9007fb4066b167b1bfa53e608aeb025ef8783ab1a79fc59f71d",
"fit": "art-fit.json",
"generationReproducibility": "Stochastic targeted edit; exact prompt, actual displayed reference and native output retained; fit and review sizes deterministic.",
"approval": null
}

View File

@@ -0,0 +1,5 @@
# Moses v03 — curated Legendary intricacy
The user requested greater intricacy for Legendary presentation after the v02 realism reduction. Retain the exact governing composition and broad stylized face, hands and beard. Add form-following secondary leading and finer nested glass shapes selectively in the cloud, attached curtain, robe folds/trim, tent joints, sky transition and some terrain rhythms. Retain large quiet regions and camp subordination.
Astra medium authored the edit. Native-path image input is unavailable due to sandbox-helper initialization failures, so the successful conversation-image path uses a retained 900×1260 JPEG quality 90 made from v02 native output, shown immediately before the edit call. No other image was included. Earlier revisions remain intact. No assembly assets are generated.

View File

@@ -0,0 +1,16 @@
# BP-001 — Moses — v04
**The Threshold — continuous cloud/presence.** Astra medium direction; built-in targeted image edit from v03’s master. The cloud changes from golden circular cells to rising pearl/ivory layers with cool internal shadows and restrained warm edges. Pending user artwork review.
- `review/art-card-size.png`: 500×700 artwork review.
- `review/art-review.png`: 1000×1400 artwork review.
- `review/illustration-review.md`: visual assessment and tradeoffs.
- `review/source-validation.json`: source, fit and v03 preservation checks.
- `source/art-native.png`: unchanged generated output.
- `source/art-master.png`: fitted 2000×2800 illustration.
- `source/edit-prompt.txt`: exact targeted prompt.
- `source/edit-reference.jpg`: actual edit reference derived from v03 master.
- `source/generation.json`: source/provenance and hashes.
- `source/art-fit.json`: uniform fit record.
Reproduce review images with `python3 source/fit-art.py`; verify with `python3 source/validate-art.py`. Native-path input failed sandbox initialization, so the edit used the retained conversation-visible reference derived from v03 master. No new card assembly assets were generated; v03 remains unchanged.

View File

@@ -0,0 +1,27 @@
{
"cardId": "BP-001",
"title": "Moses",
"name": "MOSES",
"rarity": "Legendary",
"folderName": "BP-001-moses",
"artApproval": null,
"reference": "Deuteronomy 34:10",
"translation": "ESV",
"referenceDisplay": "Deuteronomy 34:10 \u2022 ESV",
"excerpt": "And there has not arisen a prophet since in Israel like Moses, whom the LORD knew face to face",
"direction": "The Threshold",
"artStatus": "Provisional v04 cloud/presence refinement awaiting review",
"layoutStatus": "Art-only v04 cloud refinement. Prior v03 provisional layouts are preserved in v03; no layout rebuilt for this revision.",
"sceneConstraints": [
"Outside the tent of meeting after speaking with the Lord; divine cloud by the tent; Israelite camp recedes behind.",
"Calm, holy, weight-bearing; reverent intimacy, solemn responsibility, quiet authority, sacred awe.",
"No Red Sea or Sinai action; these belong to event cards."
],
"compositionApproval": {
"by": "user",
"scope": "v01 composition only; visual rendering requested to change"
},
"revisionIntent": "Edit only the cloud/presence to read as a continuous atmospheric pillar, not golden bubbles; preserve v03 composition and glass style.",
"art": "in-progress/cards/BP-001-moses/revisions/v04/source/art-master.png",
"artSHA256": "da9624d7f5c897fbcaaada9774b2fa8c11db65288b7885040e17ad16a71d925e"
}

View File

@@ -0,0 +1,57 @@
{
"schemaVersion": 1,
"cardId": "BP-001",
"folderName": "BP-001-moses",
"revision": "v04",
"stage": "art-review",
"approval": null,
"content": "card.json",
"files": {
"README.md": {
"sha256": "311a75b80ae042313a8479d295715ea6a1d12406964aad89537dc0b1d1ce0e1f"
},
"card.json": {
"sha256": "e10b36169c7e5c682c5e8741ee05340e8250fa3ff5d961f53546a28eb9fbc1f7"
},
"source/art-fit.json": {
"sha256": "c945ae1717313d1f83f94e7ddd563c05c63529fe3b0dd0d4e76f4d99f0bf2d0f"
},
"source/art-master.png": {
"sha256": "da9624d7f5c897fbcaaada9774b2fa8c11db65288b7885040e17ad16a71d925e"
},
"source/art-native.png": {
"sha256": "36f6b3ab7360b7e3d8eaf93ce1dd9edd259dfc77424ca1ef4ac729cfa126ade6"
},
"source/edit-prompt.txt": {
"sha256": "bd590461332bf9afec442d46ebef829978a64fd51c1a76074b140bd177cd56d9"
},
"source/edit-reference.jpg": {
"sha256": "29abc4ff23b6badc4a321b067735a4bed2eb31666759e4d12a35e4bdde69fcaa"
},
"source/fit-art.py": {
"sha256": "ae5418915f2bcfb464714b1898a9fdfa86e4117f2de226a01260be1fd841c611"
},
"source/generation.json": {
"sha256": "f6b2c5d8158aa8ef03544cf61eae75e4f4f120bf7f330e2b813f014a612a2c2a"
},
"source/prior-revision-hashes.json": {
"sha256": "435f9b4a7af82e53bec288fc74f7853e2cf4039b3509e4f5a6fcbf9e4a0df9b8"
},
"source/refinement.md": {
"sha256": "685f8e1f1875caa43fa9d1267835d096b02aa83ced550a28e8fa387fc3cd2c2e"
},
"source/validate-art.py": {
"sha256": "d6ccbe387f1dc91eac5b4c1753cdc0d475f34527530a915c68c6905dc07bd6a3"
},
"low/manifest.json": {
"sha256": "c6b4ddddd22832e4ea6e2201c12a90cf4a7a1d25594b5878f12c23f22987261b"
},
"med/manifest.json": {
"sha256": "e9cd197e91ccc2477ec18ccd62d903a29b166bc23b431dad8aa25ca60ef59ac6"
},
"high/manifest.json": {
"sha256": "7ec683bc1aaf3a6c3d941a9011c97c5681bd1f18a21a1ea359745f092bcf2b6a"
}
},
"compatibilityAliases": []
}

View File

@@ -0,0 +1,17 @@
{
"schemaVersion": 1,
"kind": "compact-card-revision",
"cardId": "BP-001",
"folderName": "BP-001-moses",
"revision": "v04",
"originalStage": "art-review",
"approval": null,
"reason": "superseded when v05 was accepted",
"originalManifest": "full-manifest.json",
"originalManifestSHA256": "c054d46d8ac354ee24170b42301e3698056a70f385be2cb2386e6f2f461fc78a",
"referenceResolution": "low",
"referenceImages": [
"review/art-reference.png"
],
"retention": "Text provenance plus one 500 x 700 card face per available printing; full current acceptance lives in artifacts/cards."
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 865 KiB

View File

@@ -0,0 +1,13 @@
# Moses v04 — cloud/presence review
**Art-review, not approved.** Astra medium directed a targeted built-in edit from the v03 master. Inspected the generated full image and saved 500×700 card-size artwork.
The bubble/orb reading is substantially reduced. The cloud is now an upward-connected atmospheric column made of irregular overlapping billows, elongated pale channels and cool blue-gray/lavender folds. Pearl and ivory occupy most of the body; warm champagne light sits along selected edges. The column continues above the tent roof and exits the upper edge, clarifying its vertical continuity. No literal divine figure appears.
The cloud retains stained-glass boundaries and pane-contained worked texture rather than becoming photographic vapor. Leading inside the cloud is gentler than the main structural outlines. Nested circular cells have largely been replaced by extended forms. Some rounded scalloped billow edges remain, especially above the roof, but they now join continuous layers rather than closed golden orbs. At card size the cool internal shadows make the cloud read as one luminous presence. Its visual luminosity is calmer and less gold-dominant than v03.
Moses’s face, lowered gaze, pose, stylized beard, folded hands, robe trim and silhouette appear preserved. Tent supports, entrance geometry, attached curtain/hem and camp remain visually stable. Generative editing can shift small details; non-cloud pixel identity is not claimed. The edit slightly expands some upper cloud contours and changes internal density within the original general footprint. Curtain-overlap feasibility is retained.
Tradeoff: the new cloud has broader, softer internal divisions and somewhat less small-pane intricacy than v03’s circular construction. This is useful for atmospheric continuity but should be judged with the user before any later assembly. No mask can substitute for this illustration decision.
No new card frame, backing, lettering, printing, finish mask or runtime fixture was generated. Existing v03 files were hash-checked unchanged. Source validation verifies native/master hashes, RGB dimensions and byte-identical deterministic fitting/review exports; it does not certify final card composition or GPU behavior.

View File

@@ -0,0 +1,28 @@
{
"status": "pass",
"scope": "Art source/fit/review only; no card assembly or material validation",
"priorRevision": "v03",
"priorFilesUnchanged": 200,
"nativeSourceHashVerified": true,
"masterHashVerified": true,
"reviewDimensions": {
"source/art-master.png": [
2000,
2800
],
"review/art-review.png": [
1000,
1400
],
"review/art-card-size.png": [
500,
700
],
"review/art-thumbnail.png": [
250,
350
]
},
"repeatableFitAndReviews": true,
"nonCloudPixelIdentity": "Not claimed; visually reviewed generative edit"
}

View File

@@ -0,0 +1,24 @@
{
"source": "art-native.png",
"sourceDimensions": [
1060,
1484
],
"canvas": [
2000,
2800
],
"method": "uniform centered cover fit",
"scale": 1.8867924528301887,
"scaledDimensions": [
2000.0,
2800.0
],
"cropOffset": [
0.0,
0.0
],
"resampling": "Pillow LANCZOS",
"sourceSHA256": "36f6b3ab7360b7e3d8eaf93ce1dd9edd259dfc77424ca1ef4ac729cfa126ade6",
"outputSHA256": "da9624d7f5c897fbcaaada9774b2fa8c11db65288b7885040e17ad16a71d925e"
}

View File

@@ -0,0 +1,11 @@
Use case: precise-object-edit
Asset type: Moses Legendary BP-001 v04 full-bleed illustration.
Edit target: the supplied v03 Moses art. Change ONLY the divine cloud/presence, currently the column of bright gold circular/scalloped shapes within the tent doorway at left and its connected continuation above the sloping tent roof. Preserve everything outside that cloud as closely as possible.
Problem to fix: the existing cloud reads as stacked golden bubbles, coins or glowing orbs instead of the divine Presence. Redesign it unmistakably as ONE continuous atmospheric pillar of cloud, rising from the threshold through the doorway and continuing upward beyond the roof and top edge.
New cloud design: irregular layered billows connected by long drifting, rising forms. Tall overlapping ivory and pearl ribbons of vapor, broad asymmetric bulges, slender branching wisps and elongated interlocking shapes. Varied scale and direction within a coherent upward movement. The overall existing cloud footprint stays in its current position beside Moses and behind the unchanged tent structure, but inside that footprint replace ALL repeated circles, nested rings, regular scallops and stacks of rounded cells. Its structure should resemble a continuous rising cloud bank, not beadwork, rosettes, cotton balls or discrete objects. Keep a luminous pearl/ivory body with clearly visible cool blue-gray and subtle lavender inner folds/shadows. Restrained pale champagne and warm gold only as thin localized illuminated rims or selected warm edges, NOT solid gold cloud bodies. Sacred radiance and atmospheric depth through color hierarchy inside bounded glass shapes, not neon or realistic smoke. Keep the cloud brighter than its cool internal shadows but do not make every cell glow as an individual light bulb.
Medium: exactly the established richly crafted stained-glass language. Meaningful lead divisions trace sweeping vapor layers and irregular atmospheric forms; thinner secondary leading selectively articulates the long cloud shapes. Large quiet panes mixed with a few intricate smaller shapes, pane-contained brushed/etched glass texture and subtle translucent color shifts. Glass construction stays visible. No photographic cloud pasted into the image, no vague airbrushed fog, no random cracks, no uniform tiny tessellation. Preserve the current Legendary intricacy in the rest of the art.
Strict invariants: Moses's face, expression, head angle, broad stylized skin planes, beard, hair, pose, folded hands and anatomy, robes and trim are unchanged. Tent poles, sloping roof/lintel, dark entrance geometry, attached burgundy curtain, its complete patterned hem and connection to the tent remain unchanged. Preserve camp, people, animal, vegetation, terrain, mountains, sky outside the specific cloud, framing, scale, palette and all existing non-cloud detail. Do not add props, symbols or other figures. Preserve 5:7 aspect and full-bleed art. No lettering, card frame, backing, watermark, literal divine figure or face, halo, lightning, particles or orbs.

View File

@@ -0,0 +1,16 @@
#!/usr/bin/env python3
"""Reproduce only the fitted art and review sizes; never assemble a card."""
from pathlib import Path
from PIL import Image, ImageOps
import hashlib,json
root=Path(__file__).resolve().parent.parent
native=root/'source/art-native.png'
with Image.open(native) as source:
im=source.convert('RGB');w,h=im.size
scale=max(2000/w,2800/h)
art=ImageOps.fit(im,(2000,2800),Image.Resampling.LANCZOS,centering=(.5,.5))
art.save(root/'source/art-master.png')
for name,size in [('art-review.png',(1000,1400)),('art-card-size.png',(500,700)),('art-thumbnail.png',(250,350))]:
art.resize(size,Image.Resampling.LANCZOS).save(root/'review'/name)
fit={'source':'art-native.png','sourceDimensions':[w,h],'canvas':[2000,2800],'method':'uniform centered cover fit','scale':scale,'scaledDimensions':[w*scale,h*scale],'cropOffset':[(w*scale-2000)/2,(h*scale-2800)/2],'resampling':'Pillow LANCZOS','sourceSHA256':hashlib.sha256(native.read_bytes()).hexdigest(),'outputSHA256':hashlib.sha256((root/'source/art-master.png').read_bytes()).hexdigest()}
(root/'source/art-fit.json').write_text(json.dumps(fit,indent=2)+'\n')

View File

@@ -0,0 +1,17 @@
{
"mode": "built-in imagegen edit",
"artDirectionModel": "gpt-6-astra",
"artDirectionReasoning": "medium",
"prompt": "edit-prompt.txt",
"native": "art-native.png",
"originalGeneratedPath": "/home/dkzver/.codex/generated_images/01a0acd6-0b11-7932-a7f3-c69f609e9974/exec-51715a57-b0ec-4350-b0dc-24a69fb429d8.png",
"reference": "edit-reference.jpg",
"referenceSHA256": "29abc4ff23b6badc4a321b067735a4bed2eb31666759e4d12a35e4bdde69fcaa",
"referenceProvenance": "1000x1400 JPEG quality 93 derived from v03 source/art-master.png and shown in conversation. Native-path image reference failed sandbox initialization; successful edit used num_last_images_to_include=1.",
"priorMaster": "../../v03/source/art-master.png",
"priorMasterSHA256": "73f933cf34719dfda09b719376948bcf072bec504a88329b5a22e4ac1ed76d85",
"fit": "art-fit.json",
"scope": "Cloud/presence region only requested; non-cloud preservation assessed visually, not asserted pixel-identical.",
"generationReproducibility": "Stochastic targeted edit; exact prompt, displayed reference and native bytes retained; deterministic fit and review sizing.",
"approval": null
}

View File

@@ -0,0 +1,202 @@
{
"README.md": "d8174612d3d3455ad9a5ff972cba19908df343bab5d9a15d25f641be1f8fbef2",
"card.json": "5aeddc9b7ad2412cf7d5785fa728e3158926c059fc3819e6259d273663a47335",
"high/manifest.json": "7ec683bc1aaf3a6c3d941a9011c97c5681bd1f18a21a1ea359745f092bcf2b6a",
"low/manifest.json": "c6b4ddddd22832e4ea6e2201c12a90cf4a7a1d25594b5878f12c23f22987261b",
"manifest.json": "b0b5443349cab2bdc9705aa388e45565ab60ed467d4c134f936b87bd274d0029",
"med/manifest.json": "e9cd197e91ccc2477ec18ccd62d903a29b166bc23b431dad8aa25ca60ef59ac6",
"review/art-review.png": "395f2e1fb10fe749bb9424aac0e043616dcfe42f3f6448aa2db7fd543c160cc9",
"review/art-thumbnail.png": "680317bf953d5f3b984e85c968388d03f710e0d82dd02a9cc1a815b7b769b38a",
"review/illustration-review.md": "c1399a3838afa2db0d0ce83940f56e365509df5c2eb81c35a166467821fa9ca7",
"review/layout-preview-astra-v3/README.md": "032739b971442f011bfb494be3481db906607e73899854c29d71a8f24b072018",
"review/layout-preview-astra-v3/border-backing-preview.png": "0c7960dbf90c534c10921b1234e84f55640f0f58a5ce528e15e174d51932f32e",
"review/layout-preview-astra-v3/border-backing-preview.svg": "0bf3c3be6e26ac656f5dff652d79c65d8b410dc19a03dba047c8b51da6167f9c",
"review/layout-preview-astra-v3/build-preview.py": "9026f5e67e1264f3e6d819ab0c9b4f613394f8d8ab327b5c3f3c9fb4e7099aa4",
"review/layout-preview-astra-v3/cloud-interaction-detail.png": "2f27955d889e1d102b4c8681a206e8bfd2b61e66c12e34d0b36dd605558399aa",
"review/layout-preview-astra-v3/composed-preview-1000.png": "1e0a21d705b43b1fdc8eeff36d01bef39b2ae074b98b0a4610cf30ef023df3c0",
"review/layout-preview-astra-v3/composed-preview-2000.png": "15dcac4fc1bee145de7ae2b4d3fc8c95b771720a40fd8b766419d13f687aa836",
"review/layout-preview-astra-v3/composed-preview-500.png": "35ec98353db0e2607637bdd3929fad6b0818f7ce4a54e1400caf21f569112378",
"review/layout-preview-astra-v3/composed-preview.svg": "3abac9c5f7e3bf9c9906bf7a654a989739fbc01fbfe27f4ce4707a31ed5d441b",
"review/layout-preview-astra-v3/curtain-comparison.png": "223c294bc572e53f4ddc95266a945cc4a471c0c1e88cb6f2fa2cf50af4b6f631",
"review/layout-preview-astra-v3/curtain-junction-detail.png": "6fae788bca47afb0cbdaf52b76f269c14e8190bdd05ae5fd9020a19b84831633",
"review/layout-preview-astra-v3/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9": "464a2950361202013d83f869f3513422735df1d8e76f05a8ae4c2b0697091130",
"review/layout-preview-astra-v3/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9": "0a387174a7a8c5be94a53f86e5789c77da44d9700bad89aba12d090c8a64697c",
"review/layout-preview-astra-v3/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9": "dec2022698647dc7bfd7f5354bc086c407992f4bc4ba0081d88ef03b55572264",
"review/layout-preview-astra-v3/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9": "f7c8c19ff02b7c463e758031944bc3777bc86aa8ed2cd961e9b27d4ec295a260",
"review/layout-preview-astra-v3/fonts.conf": "64384d173deefb1043c4d62cbff3dec729d57e5066fbf21923148477340b3a6d",
"review/layout-preview-astra-v3/foreground-curtain.png": "1bcb6335dc4ce58f7be024595dcb4e95a636f00bbe49e8484a77753529d1c542",
"review/layout-preview-astra-v3/foreground-curtain.svg": "ce49f4db7c621484ca7659057fa8c82d9af2aae9fffc79e47525b6c45cfe3ba3",
"review/layout-preview-astra-v3/preview-validation.json": "2af1b6de742a24783e5d14ab673da748a502de871532077517897c43859a163c",
"review/layout-preview-astra-v3/reference-ink.png": "ad1d595d08725d0ea31e09d25b6b8084581fb10a9baca170fcb2b4b9c6c8579f",
"review/layout-preview-astra-v3/reference-ink.svg": "362bd19e588158d2e67ac3d9adb6ed5ff2b4e7cd64e9809c1d52af6d1bdaa41a",
"review/layout-preview-astra-v3/text-preview.png": "f2bde9899bf80b2a904dfad69cacca55070110db348b8664e428bf52152b8c45",
"review/layout-preview-astra-v3/text-preview.svg": "51f3a5f2bfed5f8ee6fdede5818a8f0d7afa954773ade715d38252bf37a746e0",
"review/layout-preview-astra-v3/title-ink.png": "cb0d3620a82d28fd50970e6bbba5be4318db418b2578c61c9209fcb655f060aa",
"review/layout-preview-astra-v3/title-ink.svg": "27d03213b219942a2800cba962d320c99b3ae9c8e91523587ff4ce95b45beaee",
"review/layout-preview-astra-v3/v2-v3-comparison.png": "262f8ae4004f9e4c85f7612a0c05802723451640608b07dc70e58ccddc19344e",
"review/layout-preview-astra-v3/verse-0-ink.png": "52691dbf58024400d4c9b833b5462fead3e591bfe42f5d6b4724899948837989",
"review/layout-preview-astra-v3/verse-0-ink.svg": "5f765f4a3737357d1dc729765c605156d25db7b2e61db41662a3973e6114c850",
"review/layout-preview-astra-v3/verse-1-ink.png": "8e2ea55c3dc5abede57b7e0b08c6687ff092d65763676ff28561fe9070c5ce2d",
"review/layout-preview-astra-v3/verse-1-ink.svg": "35fbf3657b3e2843bd590b66d8465eb6d2668f48603c28fcabb035e293cf1d33",
"review/layout-preview-astra-v3/verse-2-ink.png": "ea5aacebd89f9c6dd9fe75da842a362a64b2dbdaba3d833e86458fd81ae34f4a",
"review/layout-preview-astra-v3/verse-2-ink.svg": "0db7333d35aeb8c428f08b126dd8e0f8b7c4f963f39a2176dc6015081d4f3782",
"review/layout-preview-astra-v3/without-curtain-1000.png": "a42b0eada050563e56395f891b6dd3517b595223450f963c8e472d0ce16f1f44",
"review/layout-preview-astra-v3/without-curtain-2000.png": "6af2d52a3a2dc6bae5a2731b90b4f10221a26745f608beb30f25b01237f5beec",
"review/layout-preview-astra-v3/without-curtain-500.png": "cc157de6537e7a9055330c0a4d391c59d2b0924c1ff94061d69cf68a989953bc",
"review/layout-preview-astra-v3/without-curtain.svg": "fbd8c3598c547950c08730d52ef9d6b9f4af0f6107be121252001c47a4e896b0",
"review/layout-preview-astra-v4/README.md": "01b3499895bcb9c7a459ea47c5d7900d1d0eb7ced43764607cca4914690e9da6",
"review/layout-preview-astra-v4/border-backing-preview.png": "0c7960dbf90c534c10921b1234e84f55640f0f58a5ce528e15e174d51932f32e",
"review/layout-preview-astra-v4/border-backing-preview.svg": "ae3fcf414b48023dab4f12617c979ca85aeb6b453193697be1ac358867e8aa07",
"review/layout-preview-astra-v4/build-preview.py": "5720b67bc0a41ecc7be3e214e047764c6dca83ee2299c67f03d9e4709591cc4d",
"review/layout-preview-astra-v4/cloud-interaction-detail.png": "e4fd42aa95a28b4757d12449875cbae9cbf38d86810f3605390db308ec2026b1",
"review/layout-preview-astra-v4/composed-preview-1000.png": "5985c8945a053d76c6db9ce61d609eac8cd4c5610b2f8026809eb15bf6827828",
"review/layout-preview-astra-v4/composed-preview-2000.png": "e8cdec18a358f38a806cb83fb3042c1dba77a559701be2bf5bc07f1ea7414796",
"review/layout-preview-astra-v4/composed-preview-500.png": "f59509cec2b7d1ec6a60524af91f9d01505ca0a83ef12cf5760e7761d4b4c9a8",
"review/layout-preview-astra-v4/composed-preview.svg": "6ed89a3482798afbb4c1853584d4e2412d7efded7be05d5523f24835e6087826",
"review/layout-preview-astra-v4/curtain-comparison.png": "e7ceedf10c580ffc2779e5d752b74a306bb507931abead6d1d7fed5d9842b4a9",
"review/layout-preview-astra-v4/curtain-junction-detail.png": "7eb2eac11828069aee08df7a961fa7bf600256d931d199a6ff5055f076ec1d15",
"review/layout-preview-astra-v4/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9": "464a2950361202013d83f869f3513422735df1d8e76f05a8ae4c2b0697091130",
"review/layout-preview-astra-v4/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9": "0a387174a7a8c5be94a53f86e5789c77da44d9700bad89aba12d090c8a64697c",
"review/layout-preview-astra-v4/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9": "dec2022698647dc7bfd7f5354bc086c407992f4bc4ba0081d88ef03b55572264",
"review/layout-preview-astra-v4/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9": "f7c8c19ff02b7c463e758031944bc3777bc86aa8ed2cd961e9b27d4ec295a260",
"review/layout-preview-astra-v4/fonts.conf": "3fecf336cb50d58261a01704b80ca2ebb0c26c9f5546ae7ef42c5a5139b1df1b",
"review/layout-preview-astra-v4/foreground-curtain.png": "9a2d9420dadf534d1e989997ae733a425e226a108ca63d6bad593e0de37f506d",
"review/layout-preview-astra-v4/foreground-curtain.svg": "c19142d4c8b2f5faed75a9d3260a2755f7609b01b190c48ad91672447e07c6ae",
"review/layout-preview-astra-v4/preview-validation.json": "9c154dd52ea9e5900c490415eadbe0d5296c1f5ae1531fd91cc4bc56cd15b1c8",
"review/layout-preview-astra-v4/reference-ink.png": "ad1d595d08725d0ea31e09d25b6b8084581fb10a9baca170fcb2b4b9c6c8579f",
"review/layout-preview-astra-v4/reference-ink.svg": "362bd19e588158d2e67ac3d9adb6ed5ff2b4e7cd64e9809c1d52af6d1bdaa41a",
"review/layout-preview-astra-v4/text-preview.png": "f2bde9899bf80b2a904dfad69cacca55070110db348b8664e428bf52152b8c45",
"review/layout-preview-astra-v4/text-preview.svg": "51f3a5f2bfed5f8ee6fdede5818a8f0d7afa954773ade715d38252bf37a746e0",
"review/layout-preview-astra-v4/title-ink.png": "cb0d3620a82d28fd50970e6bbba5be4318db418b2578c61c9209fcb655f060aa",
"review/layout-preview-astra-v4/title-ink.svg": "27d03213b219942a2800cba962d320c99b3ae9c8e91523587ff4ce95b45beaee",
"review/layout-preview-astra-v4/v3-v4-comparison.png": "165f5f255c83a5675958ef42731d3d07257e7a410efd45487dc27fad4af4fd40",
"review/layout-preview-astra-v4/v3-v4-junction-comparison.png": "68c6733de075d7b8af7f32fc70c63b705c3f9aff7db95925f3962758c65c70b1",
"review/layout-preview-astra-v4/verse-0-ink.png": "52691dbf58024400d4c9b833b5462fead3e591bfe42f5d6b4724899948837989",
"review/layout-preview-astra-v4/verse-0-ink.svg": "5f765f4a3737357d1dc729765c605156d25db7b2e61db41662a3973e6114c850",
"review/layout-preview-astra-v4/verse-1-ink.png": "8e2ea55c3dc5abede57b7e0b08c6687ff092d65763676ff28561fe9070c5ce2d",
"review/layout-preview-astra-v4/verse-1-ink.svg": "35fbf3657b3e2843bd590b66d8465eb6d2668f48603c28fcabb035e293cf1d33",
"review/layout-preview-astra-v4/verse-2-ink.png": "ea5aacebd89f9c6dd9fe75da842a362a64b2dbdaba3d833e86458fd81ae34f4a",
"review/layout-preview-astra-v4/verse-2-ink.svg": "0db7333d35aeb8c428f08b126dd8e0f8b7c4f963f39a2176dc6015081d4f3782",
"review/layout-preview-astra-v4/without-curtain-1000.png": "0d9792bcd074971f9c6e8e9d43ba62e0dc949dc91cae67b267439745fb321591",
"review/layout-preview-astra-v4/without-curtain-2000.png": "d1411a9b2edee91c8dcaeda10caf39bc45a245d478fa864b30f8a803d857cc09",
"review/layout-preview-astra-v4/without-curtain-500.png": "59502b9428e4f67a23c2fbc67229506d1bacb42864427a0d31a0dcf9ee40d383",
"review/layout-preview-astra-v4/without-curtain.svg": "494c942badd2b07f82fd68b33583982cb9a37b64d14c483d551c075f0900811b",
"review/layout-preview-astra-v5/README.md": "834ae192edcefb960d2ce1a955237faca4460ee39480131317d6911f15544858",
"review/layout-preview-astra-v5/border-backing-preview.png": "81d1982f18068c2d37d6b526ba91e2752729f2fea2254a787167bd88987c5616",
"review/layout-preview-astra-v5/border-backing-preview.svg": "9bdc25e80a6f3a4e0a38f0e18210f47bbc33daee90c66614b7339ce94f42dcc9",
"review/layout-preview-astra-v5/build-preview.py": "1e3127478ece35df94f04192323482d30a94290ed00028016645c6d3fea756a5",
"review/layout-preview-astra-v5/cloud-interaction-detail.png": "1b0f7e17cd650dffb2680084de454f46106be8fd522884019b623ba25949ec35",
"review/layout-preview-astra-v5/composed-preview-1000.png": "51cf1312f7613a8fe32952204e28eb0d41a73cc20c134d83d5b138e03c9914e3",
"review/layout-preview-astra-v5/composed-preview-2000.png": "e81cedd0d1df161a40d65f916724c0c3e852f990618159b2888f4731d3b3e4ef",
"review/layout-preview-astra-v5/composed-preview-500.png": "057b7ae9140005e88619711a026987ef7b48f865b73c550b083172666ea965d9",
"review/layout-preview-astra-v5/composed-preview.svg": "31ede5888735f11024d362fb79b9d622b3dec3fec470054d8e595ee407217298",
"review/layout-preview-astra-v5/curtain-comparison.png": "4fd017872d0f4cd03cccc2ddb1a66c9f1815c3454a581c33a9bf9cc9d7a22581",
"review/layout-preview-astra-v5/curtain-junction-detail.png": "29d02965d7d3fe78e0558c8746c1c509bd1e078ba0febe38dcd38915147da97a",
"review/layout-preview-astra-v5/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9": "464a2950361202013d83f869f3513422735df1d8e76f05a8ae4c2b0697091130",
"review/layout-preview-astra-v5/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9": "0a387174a7a8c5be94a53f86e5789c77da44d9700bad89aba12d090c8a64697c",
"review/layout-preview-astra-v5/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9": "dec2022698647dc7bfd7f5354bc086c407992f4bc4ba0081d88ef03b55572264",
"review/layout-preview-astra-v5/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9": "f7c8c19ff02b7c463e758031944bc3777bc86aa8ed2cd961e9b27d4ec295a260",
"review/layout-preview-astra-v5/fonts.conf": "a405d10a74b35906d3fd8f997eef6c9f527b9630bf88b5c9594a9bf862ff1aba",
"review/layout-preview-astra-v5/foreground-curtain.png": "a69131c4ea105e610faff03a57ad7804f2eac420c03ac36c5cf4609d419bfd03",
"review/layout-preview-astra-v5/foreground-curtain.svg": "36d5b8255928e61b714e0821942ab710240969fa2c6cf306eac6545ed79dcc46",
"review/layout-preview-astra-v5/preview-validation.json": "9819289bf141bc108f0324717e0734168ae5104a77046567e4eada61d80f5666",
"review/layout-preview-astra-v5/reference-ink.png": "56513923c9aa8b2ca2617379999a43561c6b05a1aa4f42914ec0487d544fa743",
"review/layout-preview-astra-v5/reference-ink.svg": "6c2059d51c08679342ccde8db143c4a8cd68f4122b7b5a3781b4f0fac1490a38",
"review/layout-preview-astra-v5/text-preview.png": "ad5b99d82439d547dcc7d06a74e9431ddac40050ae1e820e9858eb4a2314e2e8",
"review/layout-preview-astra-v5/text-preview.svg": "ddfadbe9dc5f0e671a6856669315f245e8ba23f992347bad90f44d580d27c91c",
"review/layout-preview-astra-v5/title-ink.png": "cb0d3620a82d28fd50970e6bbba5be4318db418b2578c61c9209fcb655f060aa",
"review/layout-preview-astra-v5/title-ink.svg": "27d03213b219942a2800cba962d320c99b3ae9c8e91523587ff4ce95b45beaee",
"review/layout-preview-astra-v5/v4-v5-comparison.png": "e48166e93a2d23eb10f81e1cb87cab4c5766cfdbc09cf19050e61adb92ac59a1",
"review/layout-preview-astra-v5/v4-v5-junction-comparison.png": "d576298f859992ff6d1c38da14cbe653000313dacc28110aa7c36d0933462138",
"review/layout-preview-astra-v5/verse-0-ink.png": "ca240a715a006a87f40deef69a6de0c6e27e2f9a662b7971b5a8ee944062fba2",
"review/layout-preview-astra-v5/verse-0-ink.svg": "f00584994b39ebec0a04aedbe456aa9cd0ea283e0924ca280f9995bb803f4956",
"review/layout-preview-astra-v5/verse-1-ink.png": "ac92560704c2fdfdfe3194b3f390b5a91c68cc4db67e5396a9acf1286881eacd",
"review/layout-preview-astra-v5/verse-1-ink.svg": "191433d0459bced61d9d8e08345b76614e2b6ea4544bed1b19cc7a2257746daa",
"review/layout-preview-astra-v5/verse-2-ink.png": "b86f7608a18900abd66b95c4019969ce851439fd6699d9f579676d731b7e3a26",
"review/layout-preview-astra-v5/verse-2-ink.svg": "2cca7b142219050f36f8c3e3250b64e9bc30edfa7afb62c5eff2cd135e64457a",
"review/layout-preview-astra-v5/without-curtain-1000.png": "4db949f4d5a1ea8b56aa9dca27e2d1e038b0b175d3342d6ae88c70bb0d3f1620",
"review/layout-preview-astra-v5/without-curtain-2000.png": "a063b89297b6f6e1e4cf013b716125b97086e25d4e6a6d609be275f57faeed50",
"review/layout-preview-astra-v5/without-curtain-500.png": "0a2351848de37ba4b90a9e75fcc17d49bfc9a60e18d03a0b001c704cf9e7e4a2",
"review/layout-preview-astra-v5/without-curtain.svg": "1d890b8c2a75c2ce81572e397b3c1496ee802394f96810c652e7bbf7f39c554c",
"review/layout-preview-v1/README.md": "dd84b5f63ffedb84d0cad5b242a9d945b550e81df82bb4a71675cfbecf0495c1",
"review/layout-preview-v1/border-backing-preview.png": "5c335ab3425888f729f19608faf5909561a7c2087d1166a29b7fb6236aa7a9b1",
"review/layout-preview-v1/border-backing-preview.svg": "931e130a798a33be35a38752fcfea0f8c899e25bcafb9a40251497dc3c39fe30",
"review/layout-preview-v1/build-preview.py": "02c8b7b48caf63a6e6b06d0c67e5b0441c583beeda2d85ad30c349a729f86f5e",
"review/layout-preview-v1/composed-preview-1000.png": "30ac9768afe4b5839e894f478804da9957c5a3ab48c9263a4428da4edbd7a730",
"review/layout-preview-v1/composed-preview-2000.png": "0304b7d3d2bca3046b45d391d1093d150a4196bcf7d31552b0315f18621fa2fd",
"review/layout-preview-v1/composed-preview-500.png": "0e04b5d39270302c710c6d1ccdf99d51f2008c8c29f8b5ec29abb92e64e38907",
"review/layout-preview-v1/composed-preview.svg": "4797dd28f1330e5255ed53d9338ad4614d1a28bd1f5cc5268660482f95856d3f",
"review/layout-preview-v1/curtain-comparison.png": "870ff517d3f7a03f6ac29535add0bd814d019238c495d174b2edfa314bf8e47b",
"review/layout-preview-v1/curtain-junction-detail.png": "e699ad7f48542c076f9ba4636c38ad7df47582887835b1a1fa13f5277f87d322",
"review/layout-preview-v1/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9": "464a2950361202013d83f869f3513422735df1d8e76f05a8ae4c2b0697091130",
"review/layout-preview-v1/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9": "0a387174a7a8c5be94a53f86e5789c77da44d9700bad89aba12d090c8a64697c",
"review/layout-preview-v1/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9": "dec2022698647dc7bfd7f5354bc086c407992f4bc4ba0081d88ef03b55572264",
"review/layout-preview-v1/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9": "f7c8c19ff02b7c463e758031944bc3777bc86aa8ed2cd961e9b27d4ec295a260",
"review/layout-preview-v1/fonts.conf": "d260a1075d0536ca7e42e6f0d4701e0015b13e2f49a7da256856fd4f07d06cf0",
"review/layout-preview-v1/foreground-curtain.png": "92be2d148c4f8581f48c659c7981880b9b4a1b27cadd5003d466d20bf8581f88",
"review/layout-preview-v1/foreground-curtain.svg": "aaac72b44c4423dae42308af6076017cc491d49dd8560f409ca944a9054b2f29",
"review/layout-preview-v1/preview-validation.json": "91b6200b2346805da9b2c797ed61f07975cafbfbf9e84f677746bf2bd3a4accf",
"review/layout-preview-v1/reference-ink.png": "b65779df016759845852ec705e9325b512c274037f2d4f44b71f4a86cd91248b",
"review/layout-preview-v1/reference-ink.svg": "fa5673866345f955c93df273de286946ad91def05f10c313a828a5b5caa4beec",
"review/layout-preview-v1/text-preview.png": "2e02ee337794e76f8414195f541ab126d825d4f2e84347c92f642e3913ab8f67",
"review/layout-preview-v1/text-preview.svg": "b027a50379222be7ad002b4a806f490ad35cbcf7639e079468badab3714c8081",
"review/layout-preview-v1/title-ink.png": "73b8c268f1604d956c56863e02684d66934d011fe3dab28f14e756a40f8ae024",
"review/layout-preview-v1/title-ink.svg": "b2ca810f03405ae03b05793418b65ea0df3599d6ff2a8395b7036ff806da8b9c",
"review/layout-preview-v1/verse-0-ink.png": "7e9061d4663a5495e54170987a76bd85bdf957ea1a8500e1afe0537bd56f7e80",
"review/layout-preview-v1/verse-0-ink.svg": "5a3d5b6d06f919099fe47e93930731f4a261b46025672aa77d595b6352a75f87",
"review/layout-preview-v1/verse-1-ink.png": "83271ea2d9f2e068dc419b80d6f58ceeda55acd06392abad9b74f5a014d0d747",
"review/layout-preview-v1/verse-1-ink.svg": "e5af9ff770b72c85da69e098da1fa4967157cec9bc5db13109bcdc1ef2be74bb",
"review/layout-preview-v1/verse-2-ink.png": "d22f1e3b1d67ade6d4021e73a314b675d3a6d1e92548fcf591cdd777b88a807c",
"review/layout-preview-v1/verse-2-ink.svg": "9027bc23acf2edc1ed2b5bce110324d396b94bda6d0fb5c332f298154b7988b9",
"review/layout-preview-v1/verse-3-ink.png": "9eaecd5c3d3eae12ac7c76bc691ec192f8645b5a7ac40aa74c8a7c7a96d8f4e4",
"review/layout-preview-v1/verse-3-ink.svg": "5f200c654ba47fd8fb54b4d6ffc7968de5bad6ef83ddf7b2a7cfd3c4a8b8ebb2",
"review/layout-preview-v1/without-curtain-1000.png": "17eedd7ffe471d185cb6c1dd6b090bfd1f06b4517714529bffad4c749f547619",
"review/layout-preview-v1/without-curtain-2000.png": "3b136d72dca4aa18ed4c952d59d0a87a71f9e8120958e3847474112676925bb6",
"review/layout-preview-v1/without-curtain-500.png": "2e057afca6bf3b14a745bd3328f1833ad402f37cb948ce5b671c83101fdc1183",
"review/layout-preview-v1/without-curtain.svg": "5ef5b40c113193cedd1eba63e0b5c70c60dd823a65159137940a533604344260",
"review/layout-preview-v2/README.md": "7029b973c35219c108ae191bbfd43bf19bd4c79b6b24849bb062c77a85687ff0",
"review/layout-preview-v2/backing-comparison.png": "54336c48917204033be863f5a97b292d43c402b694ca450e7621afd86a827756",
"review/layout-preview-v2/border-backing-preview.png": "f368fa3c4e3bb68ebfa138c5d19824c33bf94fb3365f6e6b31ac50f277a1bb2e",
"review/layout-preview-v2/border-backing-preview.svg": "a1695255614e204ce8c0180ae3646c4a8f449cc54d24dac817c3f285336077b0",
"review/layout-preview-v2/build-preview.py": "b380476cb1872db5517bbacf0f44d9881e181340870aff99d8a5ab75d4c1e0fd",
"review/layout-preview-v2/composed-preview-1000.png": "7ff672992bb597ce6f63d071c32fe755d9d9d47f9651fd10ff6aa83198e78563",
"review/layout-preview-v2/composed-preview-2000.png": "29d74f488bc935fe7c1100bc0e8dc74cfef8f2f09ac9833cc7e5f6698aba4d49",
"review/layout-preview-v2/composed-preview-500.png": "9a33f271bb1c3e9084f983d0a340675422dbbad6fa05900fcfcccd3734c9d7a8",
"review/layout-preview-v2/composed-preview.svg": "42ddf9f7ee2761fc32e1800e23dc6f34e7f13a24ebd68c29f9911cea2e3ca490",
"review/layout-preview-v2/curtain-comparison.png": "5b97cdc928704fd2f0d16a1470b1bfa9dcfdc4feacf829e4eb336506348b5d1a",
"review/layout-preview-v2/curtain-junction-detail.png": "57f4a2786f90d3bd5446ca12151bb55681635dc59454059b1ecb20bd0b5931d3",
"review/layout-preview-v2/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9": "464a2950361202013d83f869f3513422735df1d8e76f05a8ae4c2b0697091130",
"review/layout-preview-v2/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9": "0a387174a7a8c5be94a53f86e5789c77da44d9700bad89aba12d090c8a64697c",
"review/layout-preview-v2/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9": "dec2022698647dc7bfd7f5354bc086c407992f4bc4ba0081d88ef03b55572264",
"review/layout-preview-v2/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9": "f7c8c19ff02b7c463e758031944bc3777bc86aa8ed2cd961e9b27d4ec295a260",
"review/layout-preview-v2/fonts.conf": "8737a504b7dbe041a6b3d5cf96ae3039d40b6bcbe0842c7fbc463738a13b061e",
"review/layout-preview-v2/foreground-curtain.png": "92be2d148c4f8581f48c659c7981880b9b4a1b27cadd5003d466d20bf8581f88",
"review/layout-preview-v2/foreground-curtain.svg": "aaac72b44c4423dae42308af6076017cc491d49dd8560f409ca944a9054b2f29",
"review/layout-preview-v2/preview-validation.json": "0176dfe18f5e61e201be699d22a77da9494d98ad137df4aaedac9bbb556f59cb",
"review/layout-preview-v2/reference-ink.png": "91cba7a3b3139efdaeecb01cc40abb34dc02035e015879a0df38bb62ccd4747c",
"review/layout-preview-v2/reference-ink.svg": "2b57ed78a89309e2d4a0281476d21b1b2c0ac99638075581f6873ce5a8022f5e",
"review/layout-preview-v2/text-preview.png": "d2559475d6d908fb8844132e2db1ababa84cc5d64d7eea16b89ad205507333ec",
"review/layout-preview-v2/text-preview.svg": "325e020f8ff1d529a5ef48c45bb396ec38d91a3aaace6de8e6202b10873cf27e",
"review/layout-preview-v2/title-ink.png": "73b8c268f1604d956c56863e02684d66934d011fe3dab28f14e756a40f8ae024",
"review/layout-preview-v2/title-ink.svg": "b2ca810f03405ae03b05793418b65ea0df3599d6ff2a8395b7036ff806da8b9c",
"review/layout-preview-v2/verse-0-ink.png": "3c772feed78b5cfc4b2669c6cf248ac4d4002b7b8ae3ab8d06ad39d3a51574a2",
"review/layout-preview-v2/verse-0-ink.svg": "39f20cf6bd9c4b2222b3c47c57c20a4f1a352498687878540947c6e30b6a3669",
"review/layout-preview-v2/verse-1-ink.png": "d4fa2d04df13023c346ddca5182edc4c0fe0901b8eb352fecdf50e0cbe7c3aff",
"review/layout-preview-v2/verse-1-ink.svg": "7be7f61dfab438fff3b1dae28fa00cebdbb27568cb9477af0dec99839f2a9232",
"review/layout-preview-v2/verse-2-ink.png": "2c35c89bcc4d4715ad813e0cedcc63dfef02325c2831d7d9f25dd4d6eb4f7f55",
"review/layout-preview-v2/verse-2-ink.svg": "03624c77c33bd6ef166cf3a3f8308a38f65d6440e3f46990087ac96b948a7d41",
"review/layout-preview-v2/verse-3-ink.png": "9eaecd5c3d3eae12ac7c76bc691ec192f8645b5a7ac40aa74c8a7c7a96d8f4e4",
"review/layout-preview-v2/verse-3-ink.svg": "5f200c654ba47fd8fb54b4d6ffc7968de5bad6ef83ddf7b2a7cfd3c4a8b8ebb2",
"review/layout-preview-v2/without-curtain-1000.png": "77140f7f393fbb2ba626e968fd661976f725fdda25ba56a0a84f31853451513a",
"review/layout-preview-v2/without-curtain-2000.png": "7832b597183f9925fe755fc21b7783ee3ab810b99c4dda73291fd7b388b31b97",
"review/layout-preview-v2/without-curtain-500.png": "cc396a823b18cfb80c9f523120ff822ca74ad7d6ef6b103d64f5fb4ad1a5b076",
"review/layout-preview-v2/without-curtain.svg": "ffff56dec49826e92a5bae026802afb77fd39e001ae0fd7a8c3c455eebe968a8",
"source/art-fit.json": "f06f3666c0add4fd52084a0998d9e48146ec84e0f04e51dddcfc5ca2aa396388",
"source/art-master.png": "73f933cf34719dfda09b719376948bcf072bec504a88329b5a22e4ac1ed76d85",
"source/art-native.png": "ab234b91b260dba3b1a6973d765fc987e59339c21ec790d0ce51528e8fd98157",
"source/edit-prompt.txt": "0bd8e3c5c40d59dd6f1f9caf9094371d0c3f52748e48a9cbd924609da1205e5a",
"source/edit-reference.jpg": "0f6754052e7e739cf00a8f8360cb922112b2aa8501b8adfe981d9544e314108f",
"source/fit-art.py": "f9aea5aa6b192591db9db7c8d26c6d0a1ee272ab977ed3940891ca32c17acd07",
"source/generation.json": "bf4cd2254dc45d51c79a5d29abe40613a9a8530fbf17a02d60448cf64a5a0e36",
"source/refinement.md": "ea3a4f27292be136543286d4d67d7780ceeb4fb986992f428736de749de6ff4d"
}

View File

@@ -0,0 +1,5 @@
# Moses v04 — continuous cloud/presence
User feedback: the golden cloud reads as bubbles or orbs, weakening the intended Presence. This is a base-illustration correction, not a finish-mask adjustment. Preserve all non-cloud subjects and structure as closely as possible. Replace circular/scalloped cloud cells with continuous rising irregular pearl/ivory vapor layers, cool shadows and restrained warm rim light.
Astra medium direction, built-in targeted image edit. The starting source is v03 `source/art-master.png`. Native-path reads failed sandbox initialization; the edit therefore uses the single conversation-visible 1000×1400 JPEG quality 93 derived from that master, retained as `edit-reference.jpg`. v03 remains preserved, and its pre-edit hashes are retained for a non-mutation check. No card assembly is authorized.

View File

@@ -0,0 +1,23 @@
#!/usr/bin/env python3
"""Validate retained art inputs, fit, review dimensions and v03 preservation only."""
from pathlib import Path
from PIL import Image
import hashlib,json,subprocess,sys
p=Path(__file__).resolve().parent.parent
sha=lambda f:hashlib.sha256(f.read_bytes()).hexdigest()
prior=json.loads((p/'source/prior-revision-hashes.json').read_text())
assert all(sha(p.parent/'v03'/f)==h for f,h in prior.items()), 'v03 changed'
fit=json.loads((p/'source/art-fit.json').read_text())
assert sha(p/'source/art-native.png')==fit['sourceSHA256']
assert sha(p/'source/art-master.png')==fit['outputSHA256']
expected={'source/art-master.png':[2000,2800],'review/art-review.png':[1000,1400],'review/art-card-size.png':[500,700],'review/art-thumbnail.png':[250,350]}
before={f:sha(p/f) for f in expected}
for f,size in expected.items():
with Image.open(p/f) as im:
assert list(im.size)==size
assert im.mode=='RGB'
subprocess.run([sys.executable,str(p/'source/fit-art.py')],check=True)
assert before=={f:sha(p/f) for f in expected}, 'fit not byte-repeatable'
result={'status':'pass','scope':'Art source/fit/review only; no card assembly or material validation','priorRevision':'v03','priorFilesUnchanged':len(prior),'nativeSourceHashVerified':True,'masterHashVerified':True,'reviewDimensions':expected,'repeatableFitAndReviews':True,'nonCloudPixelIdentity':'Not claimed; visually reviewed generative edit'}
(p/'review/source-validation.json').write_text(json.dumps(result,indent=2)+'\n')
print(json.dumps(result,indent=2))