FA-76571 / Email MIME structure / Open access
Choose the MIME skeleton for an outgoing message: related root first · case 01
The HTML root is placed after the images inside multipart/related.
ROOT CAUSE
The related root first decision evaluates `['img'] * inline_images + ['html']` where the contract requires `['html'] + ['img'] * inline_images`.
VERIFIED REPAIR
Use `['html'] + ['img'] * inline_images` for the related root first decision and keep every other rule of the model unchanged.
Unsuccessful approach: Moving the root to second place still puts an image first. The attempted `['img'] + ['html'] + ['img'] * (inline_images - 1)` still disagrees with a fixture.
Case contract
The body is multipart/alternative(text, html) when both versions exist, the html alone when only html exists, else text. Inline images go into multipart/related with the html root first; without html they become attachments. When there are attachments the result is multipart/mixed(body, attachments...). Output is a compact string such as "mixed(alternative(text,related(html,img)),att)".
Why this case matters
Composing the wrong skeleton makes clients show images as attachments, hide the plain-text version or drop parts.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(has_text, has_html, inline_images, attachments):
atts = attachments
html = None
if has_html:
html = 'html' if inline_images == 0 else 'related(' + ','.join(['img'] * inline_images + ['html']) + ')'
else:
atts += inline_images
if has_text and html:
body = 'alternative(text,' + html + ')'
elif html:
body = html
else:
body = 'text'
if atts:
return 'mixed(' + ','.join([body] + ['att'] * atts) + ')'
return body
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
_CASES = {1: [('images without html', (False, False, 2, 0), 'mixed(text,att,att)'), ('text and html', (True, True, 0, 0), 'alternative(text,html)'), ('text html and images', (True, True, 2, 0), 'alternative(text,related(html,img,img))'), ('html images and attachment', (False, True, 1, 1), 'mixed(related(html,img),att)'), ('text and attachments', (True, False, 0, 1), 'mixed(text,att)'), ('plain text only', (True, False, 0, 0), 'text'), ('everything', (True, True, 2, 1), 'mixed(alternative(text,related(html,img,img)),att)')], 2: [('images without html', (False, False, 3, 0), 'mixed(text,att,att,att)'), ('text and html', (True, True, 0, 0), 'alternative(text,html)'), ('text html and images', (True, True, 3, 0), 'alternative(text,related(html,img,img,img))'), ('html images and attachment', (False, True, 2, 1), 'mixed(related(html,img,img),att)'), ('text and attachments', (True, False, 0, 2), 'mixed(text,att,att)'), ('plain text only', (True, False, 0, 0), 'text'), ('everything', (True, True, 2, 2), 'mixed(alternative(text,related(html,img,img)),att,att)')], 3: [('images without html', (False, False, 4, 0), 'mixed(text,att,att,att,att)'), ('text and html', (True, True, 0, 0), 'alternative(text,html)'), ('text html and images', (True, True, 4, 0), 'alternative(text,related(html,img,img,img,img))'), ('html images and attachment', (False, True, 3, 1), 'mixed(related(html,img,img,img),att)'), ('text and attachments', (True, False, 0, 3), 'mixed(text,att,att,att)'), ('plain text only', (True, False, 0, 0), 'text'), ('everything', (True, True, 2, 3), 'mixed(alternative(text,related(html,img,img)),att,att,att)')], 4: [('images without html', (False, False, 5, 0), 'mixed(text,att,att,att,att,att)'), ('text and html', (True, True, 0, 0), 'alternative(text,html)'), ('text html and images', (True, True, 5, 0), 'alternative(text,related(html,img,img,img,img,img))'), ('html images and attachment', (False, True, 4, 1), 'mixed(related(html,img,img,img,img),att)'), ('text and attachments', (True, False, 0, 4), 'mixed(text,att,att,att,att)'), ('plain text only', (True, False, 0, 0), 'text'), ('everything', (True, True, 2, 4), 'mixed(alternative(text,related(html,img,img)),att,att,att,att)')], 5: [('images without html', (False, False, 6, 0), 'mixed(text,att,att,att,att,att,att)'), ('text and html', (True, True, 0, 0), 'alternative(text,html)'), ('text html and images', (True, True, 6, 0), 'alternative(text,related(html,img,img,img,img,img,img))'), ('html images and attachment', (False, True, 5, 1), 'mixed(related(html,img,img,img,img,img),att)'), ('text and attachments', (True, False, 0, 5), 'mixed(text,att,att,att,att,att)'), ('plain text only', (True, False, 0, 0), 'text'), ('everything', (True, True, 2, 5), 'mixed(alternative(text,related(html,img,img)),att,att,att,att,att)')]}
for _label, _args, _expected in _CASES[N]:
check(_label, solve(*_args), _expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| images without html | mixed(text,att,att) | mixed(text,att,att) | Passed |
| text and html | alternative(text,html) | alternative(text,html) | Passed |
| text html and images | alternative(text,related(img,img,html)) | alternative(text,related(html,img,img)) | Failed |
| html images and attachment | mixed(related(img,html),att) | mixed(related(html,img),att) | Failed |
| text and attachments | mixed(text,att) | mixed(text,att) | Passed |
| plain text only | text | text | Passed |
| everything | mixed(alternative(text,related(img,img,html)),att) | mixed(alternative(text,related(html,img,img)),att) | Failed |
SHA-256 / cc62a02a245085764ecfaa902008ad4d8a5472d77d2179fcd9737005d15a457f
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(has_text, has_html, inline_images, attachments):
atts = attachments
html = None
if has_html:
html = 'html' if inline_images == 0 else 'related(' + ','.join(['img'] + ['html'] + ['img'] * (inline_images - 1)) + ')'
else:
atts += inline_images
if has_text and html:
body = 'alternative(text,' + html + ')'
elif html:
body = html
else:
body = 'text'
if atts:
return 'mixed(' + ','.join([body] + ['att'] * atts) + ')'
return body
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
_CASES = {1: [('images without html', (False, False, 2, 0), 'mixed(text,att,att)'), ('text and html', (True, True, 0, 0), 'alternative(text,html)'), ('text html and images', (True, True, 2, 0), 'alternative(text,related(html,img,img))'), ('html images and attachment', (False, True, 1, 1), 'mixed(related(html,img),att)'), ('text and attachments', (True, False, 0, 1), 'mixed(text,att)'), ('plain text only', (True, False, 0, 0), 'text'), ('everything', (True, True, 2, 1), 'mixed(alternative(text,related(html,img,img)),att)')], 2: [('images without html', (False, False, 3, 0), 'mixed(text,att,att,att)'), ('text and html', (True, True, 0, 0), 'alternative(text,html)'), ('text html and images', (True, True, 3, 0), 'alternative(text,related(html,img,img,img))'), ('html images and attachment', (False, True, 2, 1), 'mixed(related(html,img,img),att)'), ('text and attachments', (True, False, 0, 2), 'mixed(text,att,att)'), ('plain text only', (True, False, 0, 0), 'text'), ('everything', (True, True, 2, 2), 'mixed(alternative(text,related(html,img,img)),att,att)')], 3: [('images without html', (False, False, 4, 0), 'mixed(text,att,att,att,att)'), ('text and html', (True, True, 0, 0), 'alternative(text,html)'), ('text html and images', (True, True, 4, 0), 'alternative(text,related(html,img,img,img,img))'), ('html images and attachment', (False, True, 3, 1), 'mixed(related(html,img,img,img),att)'), ('text and attachments', (True, False, 0, 3), 'mixed(text,att,att,att)'), ('plain text only', (True, False, 0, 0), 'text'), ('everything', (True, True, 2, 3), 'mixed(alternative(text,related(html,img,img)),att,att,att)')], 4: [('images without html', (False, False, 5, 0), 'mixed(text,att,att,att,att,att)'), ('text and html', (True, True, 0, 0), 'alternative(text,html)'), ('text html and images', (True, True, 5, 0), 'alternative(text,related(html,img,img,img,img,img))'), ('html images and attachment', (False, True, 4, 1), 'mixed(related(html,img,img,img,img),att)'), ('text and attachments', (True, False, 0, 4), 'mixed(text,att,att,att,att)'), ('plain text only', (True, False, 0, 0), 'text'), ('everything', (True, True, 2, 4), 'mixed(alternative(text,related(html,img,img)),att,att,att,att)')], 5: [('images without html', (False, False, 6, 0), 'mixed(text,att,att,att,att,att,att)'), ('text and html', (True, True, 0, 0), 'alternative(text,html)'), ('text html and images', (True, True, 6, 0), 'alternative(text,related(html,img,img,img,img,img,img))'), ('html images and attachment', (False, True, 5, 1), 'mixed(related(html,img,img,img,img,img),att)'), ('text and attachments', (True, False, 0, 5), 'mixed(text,att,att,att,att,att)'), ('plain text only', (True, False, 0, 0), 'text'), ('everything', (True, True, 2, 5), 'mixed(alternative(text,related(html,img,img)),att,att,att,att,att)')]}
for _label, _args, _expected in _CASES[N]:
check(_label, solve(*_args), _expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| images without html | mixed(text,att,att) | mixed(text,att,att) | Passed |
| text and html | alternative(text,html) | alternative(text,html) | Passed |
| text html and images | alternative(text,related(img,html,img)) | alternative(text,related(html,img,img)) | Failed |
| html images and attachment | mixed(related(img,html),att) | mixed(related(html,img),att) | Failed |
| text and attachments | mixed(text,att) | mixed(text,att) | Passed |
| plain text only | text | text | Passed |
| everything | mixed(alternative(text,related(img,html,img)),att) | mixed(alternative(text,related(html,img,img)),att) | Failed |
SHA-256 / f8281d157bf0f136e8b31044f298c6105ca02a5d4970f7c11fc309d741867c6c
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(has_text, has_html, inline_images, attachments):
atts = attachments
html = None
if has_html:
html = 'html' if inline_images == 0 else 'related(' + ','.join(['html'] + ['img'] * inline_images) + ')'
else:
atts += inline_images
if has_text and html:
body = 'alternative(text,' + html + ')'
elif html:
body = html
else:
body = 'text'
if atts:
return 'mixed(' + ','.join([body] + ['att'] * atts) + ')'
return body
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
_CASES = {1: [('images without html', (False, False, 2, 0), 'mixed(text,att,att)'), ('text and html', (True, True, 0, 0), 'alternative(text,html)'), ('text html and images', (True, True, 2, 0), 'alternative(text,related(html,img,img))'), ('html images and attachment', (False, True, 1, 1), 'mixed(related(html,img),att)'), ('text and attachments', (True, False, 0, 1), 'mixed(text,att)'), ('plain text only', (True, False, 0, 0), 'text'), ('everything', (True, True, 2, 1), 'mixed(alternative(text,related(html,img,img)),att)')], 2: [('images without html', (False, False, 3, 0), 'mixed(text,att,att,att)'), ('text and html', (True, True, 0, 0), 'alternative(text,html)'), ('text html and images', (True, True, 3, 0), 'alternative(text,related(html,img,img,img))'), ('html images and attachment', (False, True, 2, 1), 'mixed(related(html,img,img),att)'), ('text and attachments', (True, False, 0, 2), 'mixed(text,att,att)'), ('plain text only', (True, False, 0, 0), 'text'), ('everything', (True, True, 2, 2), 'mixed(alternative(text,related(html,img,img)),att,att)')], 3: [('images without html', (False, False, 4, 0), 'mixed(text,att,att,att,att)'), ('text and html', (True, True, 0, 0), 'alternative(text,html)'), ('text html and images', (True, True, 4, 0), 'alternative(text,related(html,img,img,img,img))'), ('html images and attachment', (False, True, 3, 1), 'mixed(related(html,img,img,img),att)'), ('text and attachments', (True, False, 0, 3), 'mixed(text,att,att,att)'), ('plain text only', (True, False, 0, 0), 'text'), ('everything', (True, True, 2, 3), 'mixed(alternative(text,related(html,img,img)),att,att,att)')], 4: [('images without html', (False, False, 5, 0), 'mixed(text,att,att,att,att,att)'), ('text and html', (True, True, 0, 0), 'alternative(text,html)'), ('text html and images', (True, True, 5, 0), 'alternative(text,related(html,img,img,img,img,img))'), ('html images and attachment', (False, True, 4, 1), 'mixed(related(html,img,img,img,img),att)'), ('text and attachments', (True, False, 0, 4), 'mixed(text,att,att,att,att)'), ('plain text only', (True, False, 0, 0), 'text'), ('everything', (True, True, 2, 4), 'mixed(alternative(text,related(html,img,img)),att,att,att,att)')], 5: [('images without html', (False, False, 6, 0), 'mixed(text,att,att,att,att,att,att)'), ('text and html', (True, True, 0, 0), 'alternative(text,html)'), ('text html and images', (True, True, 6, 0), 'alternative(text,related(html,img,img,img,img,img,img))'), ('html images and attachment', (False, True, 5, 1), 'mixed(related(html,img,img,img,img,img),att)'), ('text and attachments', (True, False, 0, 5), 'mixed(text,att,att,att,att,att)'), ('plain text only', (True, False, 0, 0), 'text'), ('everything', (True, True, 2, 5), 'mixed(alternative(text,related(html,img,img)),att,att,att,att,att)')]}
for _label, _args, _expected in _CASES[N]:
check(_label, solve(*_args), _expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| images without html | mixed(text,att,att) | mixed(text,att,att) | Passed |
| text and html | alternative(text,html) | alternative(text,html) | Passed |
| text html and images | alternative(text,related(html,img,img)) | alternative(text,related(html,img,img)) | Passed |
| html images and attachment | mixed(related(html,img),att) | mixed(related(html,img),att) | Passed |
| text and attachments | mixed(text,att) | mixed(text,att) | Passed |
| plain text only | text | text | Passed |
| everything | mixed(alternative(text,related(html,img,img)),att) | mixed(alternative(text,related(html,img,img)),att) | Passed |
SHA-256 / ec74f87d73b4fa1010c8151d39edf169ac8f01d74a67260bb62559e9e13dab15
Verification & scope
Stipulated offline model over pre-parsed MIME dictionaries; not a conforming MIME parser, generator or mail client. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.
Observations recorded using Python 3.12.14 at 2026-09-29T14:49:17.418669+00:00.
Case digest / e8d1f48e136fd5ff5d030a9353cc87a6a77878f1573bf9dcb60779358b8b5126