fix(accounting,assets): a conversion claim counts only where the code wrote it
Both red guards green, and the whole suite is 2204 passed / 1 skipped (2199 before this round, +5 new tests, no golden moved). TWO HALVES, AND NEITHER IS SUFFICIENT ALONE. The judge now reads the clause only from inside a POINTER BLOCK -- the markdown image line plus the detail line under it -- and only where the clause names the asset that block points at, anchored to the end of the line because the build writes it last. That closes ordinary body text and a table cell. It cannot close an image's own alt text, because a label is document text that the build writes INSIDE a pointer block, which is the second half: `assets._inline` disarms a checksum field in anything that came from the document. WHERE THE BOUNDARY RUNS, stated in both files. Everything `_inline` returns came from the document -- an alt attribute, an STS caption, a publisher's file name. Everything `render_block` appends after it came from the run: the size it measured, the type it sniffed, the digests it computed. The second line carries both, so document text may not emit the grammar the run writes there. The digits are kept, because a reader is owed what the document said; the colon that makes them a FIELD is not. The judge's expression stays restated rather than imported, for the reason `asset_holds` already gives about the naming rule: a judge sharing the judged's own expression agrees with it by construction. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
39f6f0a4fb
commit
24a828469f
2 changed files with 61 additions and 4 deletions
|
|
@ -938,6 +938,12 @@ def asset_href(image: ExtractedImage) -> str:
|
|||
return f"/{ASSETS_DIR}/{asset_name(image)}"
|
||||
|
||||
|
||||
#: `sha256:` immediately in front of 64 hex digits -- the CHECKSUM FIELD this
|
||||
#: module writes on a pointer's second line, and the grammar the content
|
||||
#: accounting gate reads a conversion claim with.
|
||||
_CHECKSUM_FIELD = re.compile(r"sha256:(?=[0-9a-fA-F]{64})")
|
||||
|
||||
|
||||
def _inline(value: str) -> str:
|
||||
"""A label, made safe for the one line it is written on.
|
||||
|
||||
|
|
@ -947,9 +953,21 @@ def _inline(value: str) -> str:
|
|||
and this text reaches a title through no route, but the pointer is body text
|
||||
a proposer reads, and a half-open link there is a pointer that resolves
|
||||
nowhere.
|
||||
|
||||
WHERE THE BOUNDARY RUNS. Everything this function returns came from the
|
||||
DOCUMENT -- an `alt` attribute, an STS `<caption>`, a file name a publisher
|
||||
chose. Everything `render_block` appends after it came from the run: the
|
||||
size it measured, the type it sniffed, the digests it computed. The second
|
||||
line carries both, so document text must not be able to emit the metadata
|
||||
grammar the run writes there. Measured by PM 2026-09-19: an `alt` attribute
|
||||
stating `converted from ... sha256:<a> to ... sha256:<b>` made the content
|
||||
accounting gate report a picture as carried that was refused
|
||||
`asset_too_large` and is not in `assets/` at all. A checksum field is
|
||||
therefore disarmed here -- the digits are kept, because a reader is owed
|
||||
what the document said, and the colon that makes them a FIELD is not.
|
||||
"""
|
||||
collapsed = " ".join(value.split())
|
||||
return collapsed.replace("[", "(").replace("]", ")")
|
||||
return _CHECKSUM_FIELD.sub("sha256 ", collapsed.replace("[", "(").replace("]", ")"))
|
||||
|
||||
|
||||
def render_block(image: ExtractedImage) -> str:
|
||||
|
|
|
|||
|
|
@ -361,17 +361,56 @@ def _sha12(path: Path) -> str:
|
|||
return _sha256(path)[:12]
|
||||
|
||||
|
||||
#: One POINTER BLOCK, as `assets.render_block` writes it: the markdown image
|
||||
#: line naming a file under `assets/`, and the detail line directly under it.
|
||||
#: Restated here rather than imported, for the reason `asset_holds` gives about
|
||||
#: the naming rule -- a judge sharing the judged's own expression agrees with
|
||||
#: it by construction. It is deliberately the SHAPE and not the readable tail.
|
||||
_POINTER = re.compile(
|
||||
r"^!\[[^\]\n]*\]\(/assets/(?P<asset>[^)\s]+)\)\n(?P<detail>Image: [^\n]*)$",
|
||||
re.MULTILINE,
|
||||
)
|
||||
|
||||
#: The conversion clause `assets.render_block` writes: the source's media type
|
||||
#: and sha256, and the media type and sha256 of what the run actually carried.
|
||||
#: One expression, so the judge has one definition of the claim it verifies.
|
||||
#: Anchored to the END of the line it is searched in, because the build writes
|
||||
#: it LAST on the detail line, after every field that came from the document.
|
||||
_CONVERSION = re.compile(
|
||||
r"converted from \S+ sha256:(?P<before>[0-9a-f]{64}) to \S+ sha256:(?P<after>[0-9a-f]{64})"
|
||||
r"converted from \S+ sha256:(?P<before>[0-9a-f]{64}) to \S+ sha256:(?P<after>[0-9a-f]{64})$"
|
||||
)
|
||||
|
||||
|
||||
def _conversions(bundle_text: str) -> dict[str, str]:
|
||||
"""source digest -> the digest the bundle says it carried instead."""
|
||||
return {m.group("before"): m.group("after") for m in _CONVERSION.finditer(bundle_text)}
|
||||
"""source digest -> the digest the bundle says it carried instead.
|
||||
|
||||
READ ONLY FROM A POINTER BLOCK THIS BUILD WROTE, and only where the clause
|
||||
names the asset that block points at. The expression used to run over the
|
||||
whole bundle text, which put an untrusted document inside the judge's own
|
||||
input: measured by PM 2026-09-19, a BMP refused `asset_too_large` and never
|
||||
carried was reported as held, both through an image's alt text and through
|
||||
ordinary body text, because the document simply wrote the sentence. The
|
||||
gate's first sentence is that the fasit never comes from the reader it
|
||||
judges, and this route had quietly stopped obeying it.
|
||||
|
||||
The other half of the boundary is in the build, not here: a label is
|
||||
document text written INSIDE a pointer block, so `assets._inline` is what
|
||||
keeps it from emitting this grammar. Neither half is sufficient alone --
|
||||
this one cannot tell a label from a field, and that one does not reach
|
||||
body text or a table cell at all.
|
||||
"""
|
||||
found: dict[str, str] = {}
|
||||
for pointer in _POINTER.finditer(bundle_text):
|
||||
clause = _CONVERSION.search(pointer.group("detail"))
|
||||
if clause is None:
|
||||
continue
|
||||
after = clause.group("after")
|
||||
# The claim has to be about the picture the block points at. A clause
|
||||
# standing in one asset's block while naming another's digest is a
|
||||
# sentence nothing in this build writes.
|
||||
if pointer.group("asset").startswith(after[:12]):
|
||||
found[clause.group("before")] = after
|
||||
return found
|
||||
|
||||
|
||||
def asset_holds(build: Build, source: Path) -> bool:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue