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