fix(accounting,gate): the conversion claim comes from the run's ledger
Chose the side channel over neutralising pointer-shaped document text, because the second fix changes what every document SAYS in order to defend a tool outside the build: a source quoting a bundle listing would come out altered and existing bundles would move bytes. This reads a file the run already writes. `assets.conversion` names the pair, `DocumentAssets.conversions` carries it out of the run, `DocumentAccount.conversions` books it, and the accounting JSON states it per document. `_declared_conversions` reads it; `_conversions` now believes a pair only when the RUN booked it AND a pointer block confirms it for the asset it names. The confirmation can be forged and the ledger cannot, which is why the ledger decides. Measured through the real `okf build`: the three arms PM reproduced (two `<p>`, one `<p>` with `<br>`, a markdown note beside the carrier) go forged -> refused, 3 of 3, with the known-positive True in all three. The text-level regression guard goes 3 arms to 13, the two new ones being a perfectly written pointer block the run never booked. R761, rebuilt: 25 BMP sources, 19 held, 19 of 19 conversions confirmed against 19 declared, 50 assets (29 JPEG + 21 PNG, 0 BMP), SHY 71, u = 0, d = 0, exit 0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
aa2abe8293
commit
1c958ab8d6
5 changed files with 226 additions and 59 deletions
|
|
@ -732,6 +732,14 @@ class DocumentAccount:
|
||||||
counts: dict[str, int]
|
counts: dict[str, int]
|
||||||
fates: dict[str, Fate]
|
fates: dict[str, Fate]
|
||||||
error: str | None = None
|
error: str | None = None
|
||||||
|
#: The `(source digest, asset digest)` pairs for the images this run
|
||||||
|
#: REWROTE -- a BMP that reaches the bundle as a PNG, today's only case.
|
||||||
|
#: Booked because the bundle states the same pairs only as prose on a
|
||||||
|
#: pointer's second line, and measured by PM 2026-09-19 an ordinary HTML
|
||||||
|
#: document with two `<p>` elements writes exactly that prose. A reader
|
||||||
|
#: proving a conversion off the bundle text is reading the document; this
|
||||||
|
#: is the same fact written by the run.
|
||||||
|
conversions: tuple[tuple[str, str], ...] = ()
|
||||||
#: How many U+00AD the normalisation door removed from this document's
|
#: How many U+00AD the normalisation door removed from this document's
|
||||||
#: text before the persist gate saw it (operator decision 2026-09-18).
|
#: text before the persist gate saw it (operator decision 2026-09-18).
|
||||||
#: Booked rather than silently applied: a door that changes a source's
|
#: Booked rather than silently applied: a door that changes a source's
|
||||||
|
|
@ -765,6 +773,7 @@ class DocumentAccount:
|
||||||
"status": self.status,
|
"status": self.status,
|
||||||
"code": self.code,
|
"code": self.code,
|
||||||
"normalised_soft_hyphen": self.normalised_soft_hyphen,
|
"normalised_soft_hyphen": self.normalised_soft_hyphen,
|
||||||
|
"conversions": [{"from": before, "to": after} for before, after in self.conversions],
|
||||||
"inventory": dict(self.counts),
|
"inventory": dict(self.counts),
|
||||||
"fates": {kind: self.fates[kind].to_json() for kind in self.counts},
|
"fates": {kind: self.fates[kind].to_json() for kind in self.counts},
|
||||||
"unaccounted": self.unaccounted,
|
"unaccounted": self.unaccounted,
|
||||||
|
|
@ -943,7 +952,14 @@ def _persisted_account(
|
||||||
fates[kind].carried += 1
|
fates[kind].carried += 1
|
||||||
if "image" in fates:
|
if "image" in fates:
|
||||||
_book_images(inv, fates["image"], assets, finder)
|
_book_images(inv, fates["image"], assets, finder)
|
||||||
return DocumentAccount(inv.source_file, PERSISTED, None, counts, fates)
|
return DocumentAccount(
|
||||||
|
inv.source_file,
|
||||||
|
PERSISTED,
|
||||||
|
None,
|
||||||
|
counts,
|
||||||
|
fates,
|
||||||
|
conversions=() if assets is None else assets.conversions,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _refused_account(inv: Inventory, code: str) -> DocumentAccount:
|
def _refused_account(inv: Inventory, code: str) -> DocumentAccount:
|
||||||
|
|
|
||||||
|
|
@ -887,6 +887,23 @@ def digest(data: bytes) -> str:
|
||||||
return hashlib.sha256(data).hexdigest()
|
return hashlib.sha256(data).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def conversion(image: ExtractedImage) -> tuple[str, str] | None:
|
||||||
|
"""`(the source's digest, the carried asset's digest)`, or `None`.
|
||||||
|
|
||||||
|
THE RUN'S OWN RECORD OF WHAT IT REWROTE, for a reader that must not have
|
||||||
|
to take the bundle's word for it. `render_block` states the same pair on
|
||||||
|
the pointer's second line, which is where a person reads it -- but that
|
||||||
|
line is markdown in a concept body, and measured by PM 2026-09-19 an
|
||||||
|
ordinary HTML document with two `<p>` elements produces the same two
|
||||||
|
lines. A judge reading the claim off the bundle text is therefore reading
|
||||||
|
an untrusted document; a judge reading it off the accounting is reading
|
||||||
|
this function's output, which no document can reach.
|
||||||
|
"""
|
||||||
|
if image.converted_from is None or image.source_sha256 is None:
|
||||||
|
return None
|
||||||
|
return (image.source_sha256, digest(image.data))
|
||||||
|
|
||||||
|
|
||||||
def _reduce(text: str) -> str:
|
def _reduce(text: str) -> str:
|
||||||
return _SEPARATOR_RUN.sub("-", unicodedata.normalize("NFC", text).lower()).strip("-")
|
return _SEPARATOR_RUN.sub("-", unicodedata.normalize("NFC", text).lower()).strip("-")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ from .assets import (
|
||||||
AssetRejection,
|
AssetRejection,
|
||||||
ExtractedImage,
|
ExtractedImage,
|
||||||
asset_name,
|
asset_name,
|
||||||
|
conversion,
|
||||||
)
|
)
|
||||||
from .connectors import safe_resolve
|
from .connectors import safe_resolve
|
||||||
from .errors import IngestError, MaterializationError, SegmentationError, SourceError
|
from .errors import IngestError, MaterializationError, SegmentationError, SourceError
|
||||||
|
|
@ -652,11 +653,19 @@ class InboxResult:
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class DocumentAssets:
|
class DocumentAssets:
|
||||||
"""One persisted document's image outcome."""
|
"""One persisted document's image outcome.
|
||||||
|
|
||||||
|
`conversions` is the run's own list of `(source digest, asset digest)`
|
||||||
|
pairs for the images it REWROTE, in the order they were carried. The
|
||||||
|
bundle states the same pairs in prose on each pointer's second line; this
|
||||||
|
is the machine-readable side of the same fact, and the difference is who
|
||||||
|
wrote it -- a document can produce that prose and cannot produce this.
|
||||||
|
"""
|
||||||
|
|
||||||
source_file: str
|
source_file: str
|
||||||
carried: int
|
carried: int
|
||||||
rejected: tuple[AssetRejection, ...]
|
rejected: tuple[AssetRejection, ...]
|
||||||
|
conversions: tuple[tuple[str, str], ...] = ()
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
|
|
@ -1448,6 +1457,11 @@ def process_inbox(
|
||||||
source_file=source_name(path),
|
source_file=source_name(path),
|
||||||
carried=len(document.images),
|
carried=len(document.images),
|
||||||
rejected=document.rejected,
|
rejected=document.rejected,
|
||||||
|
conversions=tuple(
|
||||||
|
pair
|
||||||
|
for pair in (conversion(image) for image in document.images)
|
||||||
|
if pair is not None
|
||||||
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
for target_name, content, reasons in outputs:
|
for target_name, content, reasons in outputs:
|
||||||
|
|
|
||||||
|
|
@ -501,6 +501,29 @@ def _build(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _ledger(*pairs: tuple[str, str]) -> dict[str, Any]:
|
||||||
|
"""The accounting a run writes when it REWROTE these pictures.
|
||||||
|
|
||||||
|
The gate's conversion route reads its pairs from here and confirms them
|
||||||
|
against the bundle text, so a test constructing a `Build` by hand has to
|
||||||
|
say what the run booked. Passing none of them is the case where the run
|
||||||
|
converted nothing -- which is every bundle before the viewable-asset
|
||||||
|
round, and the state a forged pointer block leaves the ledger in.
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
"accounting_version": 1,
|
||||||
|
"documents": [
|
||||||
|
{
|
||||||
|
"source_file": "a.md",
|
||||||
|
"status": "persisted",
|
||||||
|
"code": None,
|
||||||
|
"conversions": [{"from": before, "to": after} for before, after in pairs],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"files": [],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_the_judge_proves_a_CONVERTED_image_was_carried(tmp_path: Path) -> None:
|
def test_the_judge_proves_a_CONVERTED_image_was_carried(tmp_path: Path) -> None:
|
||||||
"""A BMP reaches the bundle as a PNG, so the SOURCE's bytes are not in
|
"""A BMP reaches the bundle as a PNG, so the SOURCE's bytes are not in
|
||||||
`assets/` and never will be.
|
`assets/` and never will be.
|
||||||
|
|
@ -528,20 +551,28 @@ def test_the_judge_proves_a_CONVERTED_image_was_carried(tmp_path: Path) -> None:
|
||||||
f"Image: graphics/figur.bmp (8x4 px) -- converted from image/bmp "
|
f"Image: graphics/figur.bmp (8x4 px) -- converted from image/bmp "
|
||||||
f"sha256:{before} to image/png sha256:{after}\n"
|
f"sha256:{before} to image/png sha256:{after}\n"
|
||||||
)
|
)
|
||||||
build = _build(assets={f"{after[:12]}-figur.png": after}, bundle_text=text)
|
ledger = _ledger((before, after))
|
||||||
|
build = _build(assets={f"{after[:12]}-figur.png": after}, bundle_text=text, accounting=ledger)
|
||||||
assert gate.asset_holds(build, source) is True
|
assert gate.asset_holds(build, source) is True
|
||||||
|
|
||||||
# Known-negative on the same shape: the bundle says it converted, and the
|
# Known-negative on the same shape: the run booked the conversion, and the
|
||||||
# file it names is not there. A route that read the claim alone would pass.
|
# file it names is not there. A route that read the claim alone would pass.
|
||||||
empty = _build(assets={}, bundle_text=text)
|
empty = _build(assets={}, bundle_text=text, accounting=ledger)
|
||||||
assert gate.asset_holds(empty, source) is False
|
assert gate.asset_holds(empty, source) is False
|
||||||
|
|
||||||
# Known-negative two: an asset IS there, under a digest the bundle never
|
# Known-negative two: an asset IS there, under a digest nothing tied to
|
||||||
# tied to this source.
|
# this source.
|
||||||
other = "0" * 64
|
other = "0" * 64
|
||||||
stranger = _build(assets={f"{other[:12]}-x.png": other}, bundle_text=text)
|
stranger = _build(assets={f"{other[:12]}-x.png": other}, bundle_text=text, accounting=ledger)
|
||||||
assert gate.asset_holds(stranger, source) is False
|
assert gate.asset_holds(stranger, source) is False
|
||||||
|
|
||||||
|
# Known-negative three: the same bundle text, and a run that booked NO
|
||||||
|
# conversion. This is the pointer block a document can write for itself.
|
||||||
|
unbooked = _build(
|
||||||
|
assets={f"{after[:12]}-figur.png": after}, bundle_text=text, accounting=_ledger()
|
||||||
|
)
|
||||||
|
assert gate.asset_holds(unbooked, source) is False
|
||||||
|
|
||||||
|
|
||||||
# --- the claim the judge reads must be one the CODE wrote --------------------
|
# --- the claim the judge reads must be one the CODE wrote --------------------
|
||||||
|
|
||||||
|
|
@ -579,21 +610,26 @@ def _huge_bmp() -> bytes:
|
||||||
def test_a_document_cannot_forge_a_conversion_claim(tmp_path: Path) -> None:
|
def test_a_document_cannot_forge_a_conversion_claim(tmp_path: Path) -> None:
|
||||||
"""THE FASIT NEVER COMES FROM THE READER IT JUDGES -- including this route.
|
"""THE FASIT NEVER COMES FROM THE READER IT JUDGES -- including this route.
|
||||||
|
|
||||||
The conversion route reads two digests out of the bundle. Before this
|
The conversion route used to read its two digests out of the bundle text.
|
||||||
guard it read them out of ANY text in it, so a document could write the
|
Measured by PM 2026-09-19 that was a way IN for content this repository
|
||||||
sentence itself and the judge would believe it: measured by PM 2026-09-19,
|
does not trust: a BMP declaring 50 000 x 50 000, refused
|
||||||
a BMP declaring 50 000 x 50 000 that was refused `asset_too_large` and
|
`asset_too_large` and never carried, gave `asset_holds = True` from an
|
||||||
never carried gave `asset_holds = True`, both through an image's alt text
|
image's alt text and from ordinary body text. Anchoring the claim to a
|
||||||
and through ordinary body text. The route the judge had before the
|
pointer block closed those two and not the class -- a pointer block is two
|
||||||
conversion landed hashed the source file and nothing else, so no document
|
lines of markdown, and one HTML file with two `<p>` elements writes them.
|
||||||
could reach it; this round opened a way IN for content this repository
|
|
||||||
does not trust.
|
|
||||||
|
|
||||||
A claim counts only where THIS CODE put it: inside a pointer block, tied
|
So the pair the judge believes comes from the RUN's accounting, and the
|
||||||
to the asset that block names. Every arm below carries a source that was
|
bundle text only confirms it. Every arm below is a way a document can put
|
||||||
never carried, and a bundle holding one unrelated REAL asset -- so the
|
the sentence, or the whole block, into a bundle; the run's ledger holds
|
||||||
digest the forgery names really is in `assets/`, which is what made the
|
ONE conversion and it is about other pictures, so the route is live and no
|
||||||
measured forgeries work.
|
arm may reach it. The known-positive at the end books the pair and shows
|
||||||
|
the route still works.
|
||||||
|
|
||||||
|
Thirteen arms. Eleven are PM's own list of ways the shape anchoring
|
||||||
|
already refused, kept as a regression guard -- narrowing this rule later
|
||||||
|
must not quietly widen one of them -- and two are the form that defeated
|
||||||
|
it: a perfectly written pointer block the run never booked, once in the
|
||||||
|
bundle's ordinary text and once naming the asset it points at exactly.
|
||||||
"""
|
"""
|
||||||
never_carried = tmp_path / "figur.bmp"
|
never_carried = tmp_path / "figur.bmp"
|
||||||
never_carried.write_bytes(_huge_bmp())
|
never_carried.write_bytes(_huge_bmp())
|
||||||
|
|
@ -604,26 +640,63 @@ def test_a_document_cannot_forge_a_conversion_claim(tmp_path: Path) -> None:
|
||||||
assets = {f"{after[:12]}-ekte.png": after}
|
assets = {f"{after[:12]}-ekte.png": after}
|
||||||
clause = f"converted from image/bmp sha256:{before} to image/png sha256:{after}"
|
clause = f"converted from image/bmp sha256:{before} to image/png sha256:{after}"
|
||||||
pointer = f"\nImage: ekte.png (1x1 px)"
|
pointer = f"\nImage: ekte.png (1x1 px)"
|
||||||
|
# The same clause with U+00A0 where the spaces are: PM's N6.
|
||||||
|
nbsp_clause = clause.replace(" ", "\u00a0")
|
||||||
|
other = "0" * 64
|
||||||
|
|
||||||
arms = {
|
arms = {
|
||||||
"plain body text": f"{pointer}\n\nProsess 84. {clause}. Se figuren over.\n",
|
"plain body text": f"{pointer}\n\nProsess 84. {clause}. Se figuren over.\n",
|
||||||
"a table cell": f"{pointer}\n\n| Krav | Kilde |\n| --- | --- |\n| 84-1 | {clause} |\n",
|
"a table cell": f"{pointer}\n\n| Krav | Kilde |\n| --- | --- |\n| 84-1 | {clause} |\n",
|
||||||
|
"a figure caption": f"{pointer}\n\nFigur 84-1 -- {clause}\n",
|
||||||
|
"a fenced code block": f"{pointer}\n\n```\n{clause}\n```\n",
|
||||||
|
"link text with a URL": f"{pointer}\n\n[{clause}](https://example.invalid/x)\n",
|
||||||
|
"the clause with non-breaking spaces": f"{pointer}\n\n{nbsp_clause}\n",
|
||||||
|
"a clause naming another asset's digest": (
|
||||||
|
f"\n"
|
||||||
|
f"Image: ekte.png (1x1 px) -- converted from image/bmp sha256:{before} "
|
||||||
|
f"to image/png sha256:{other}\n"
|
||||||
|
),
|
||||||
"a pointer block naming another asset": (
|
"a pointer block naming another asset": (
|
||||||
f"\nImage: annen.png (1x1 px) -- {clause}\n"
|
f"\nImage: annen.png (1x1 px) -- {clause}\n"
|
||||||
f"{pointer}\n"
|
f"{pointer}\n"
|
||||||
),
|
),
|
||||||
|
"an Image: line of its own": f"{pointer}\n\nImage: figur.bmp (8x4 px) -- {clause}\n",
|
||||||
|
"the whole block inside a code fence": (
|
||||||
|
f"{pointer}\n\n```\n\n"
|
||||||
|
f"Image: figur.bmp (8x4 px) -- {clause}\n```\n"
|
||||||
|
),
|
||||||
|
"the whole block on one line": (
|
||||||
|
f"{pointer}\n\n "
|
||||||
|
f"Image: figur.bmp (8x4 px) -- {clause}\n"
|
||||||
|
),
|
||||||
|
# The two that defeated the shape anchoring. Written exactly as the
|
||||||
|
# code writes them, because that is the point: the form is not a
|
||||||
|
# signature, and only the ledger can tell these from the real thing.
|
||||||
|
"a whole pointer block the run never booked": (
|
||||||
|
f"{pointer}\n\n\n"
|
||||||
|
f"Image: figur.bmp (8x4 px) -- {clause}\n"
|
||||||
|
),
|
||||||
|
"that block alone in the bundle": (
|
||||||
|
f"\nImage: figur.bmp (8x4 px) -- {clause}\n"
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
# The run booked ONE conversion and it is about neither of these files, so
|
||||||
|
# the route is live in every arm and nothing it could believe is true.
|
||||||
|
live = _ledger(("f" * 64, "e" * 64))
|
||||||
for label, text in arms.items():
|
for label, text in arms.items():
|
||||||
build = _build(assets=assets, bundle_text=text)
|
build = _build(assets=assets, bundle_text=text, accounting=live)
|
||||||
assert gate.asset_holds(build, never_carried) is False, (
|
assert gate.asset_holds(build, never_carried) is False, (
|
||||||
f"{label}: a document talked the judge into a carry that never happened"
|
f"{label}: a document talked the judge into a carry that never happened"
|
||||||
)
|
)
|
||||||
|
|
||||||
# KNOWN-POSITIVE on the same bytes: the clause where the code writes it,
|
# KNOWN-POSITIVE on the same bytes: the last arm's text, believed once the
|
||||||
# in the pointer block for the asset it names. Without this the arms above
|
# RUN books the pair. Without it every arm above would pass on a route
|
||||||
# would pass on a route that had simply stopped working.
|
# that had simply stopped working.
|
||||||
honest_text = f"\nImage: figur.bmp (1x1 px) -- {clause}\n"
|
honest = _build(
|
||||||
honest = _build(assets=assets, bundle_text=honest_text)
|
assets=assets,
|
||||||
|
bundle_text=arms["that block alone in the bundle"],
|
||||||
|
accounting=_ledger((before, after)),
|
||||||
|
)
|
||||||
assert gate.asset_holds(honest, never_carried) is True
|
assert gate.asset_holds(honest, never_carried) is True
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -810,7 +883,11 @@ def test_the_judge_proves_carriage_and_says_it_does_not_prove_fidelity(tmp_path:
|
||||||
f"Image: figur.bmp (8x4 px) -- converted from image/bmp sha256:{before} "
|
f"Image: figur.bmp (8x4 px) -- converted from image/bmp sha256:{before} "
|
||||||
f"to image/png sha256:{after}\n"
|
f"to image/png sha256:{after}\n"
|
||||||
)
|
)
|
||||||
build = _build(assets={f"{after[:12]}-figur.png": after}, bundle_text=text)
|
build = _build(
|
||||||
|
assets={f"{after[:12]}-figur.png": after},
|
||||||
|
bundle_text=text,
|
||||||
|
accounting=_ledger((before, after)),
|
||||||
|
)
|
||||||
assert gate.asset_holds(build, source) is True
|
assert gate.asset_holds(build, source) is True
|
||||||
|
|
||||||
doc = gate.asset_holds.__doc__ or ""
|
doc = gate.asset_holds.__doc__ or ""
|
||||||
|
|
|
||||||
|
|
@ -383,35 +383,78 @@ _CONVERSION = re.compile(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _conversions(bundle_text: str) -> dict[str, str]:
|
def _declared_conversions(build: Build) -> set[tuple[str, str]]:
|
||||||
"""source digest -> the digest the bundle says it carried instead.
|
"""Every `(source digest, asset digest)` pair THE RUN booked.
|
||||||
|
|
||||||
READ ONLY FROM A POINTER BLOCK THIS BUILD WROTE, and only where the clause
|
Read from the accounting the build wrote, which is a side channel no
|
||||||
names the asset that block points at. The expression used to run over the
|
document can reach -- and that is the whole of the change. The pair used
|
||||||
whole bundle text, which put an untrusted document inside the judge's own
|
to be read out of the bundle text, so a claim was accepted because it had
|
||||||
input: measured by PM 2026-09-19, a BMP refused `asset_too_large` and never
|
the SHAPE of a pointer block. Measured by PM 2026-09-19 through the real
|
||||||
carried was reported as held, both through an image's alt text and through
|
`okf build`: one HTML file with two `<p>` elements writes those two lines
|
||||||
ordinary body text, because the document simply wrote the sentence. The
|
into a concept body, and a BMP refused `asset_too_large` and absent from
|
||||||
gate's first sentence is that the fasit never comes from the reader it
|
`assets/` read as carried. Narrowing the shape cannot close that -- a
|
||||||
judges, and this route had quietly stopped obeying it.
|
document that can write the form can write any form -- so the claim is
|
||||||
|
bound to the run instead.
|
||||||
|
|
||||||
The other half of the boundary is in the build, not here: a label is
|
CHOSEN over neutralising pointer-shaped text at extraction, because that
|
||||||
document text written INSIDE a pointer block, so `assets._inline` is what
|
fix would change what every document says to defend a judge: a source
|
||||||
keeps it from emitting this grammar. Neither half is sufficient alone --
|
quoting a bundle listing would come out altered, the bytes of existing
|
||||||
this one cannot tell a label from a field, and that one does not reach
|
bundles would move, and the production path would carry a rule that
|
||||||
body text or a table cell at all.
|
exists for a tool outside it. This reads a file the run already writes.
|
||||||
|
|
||||||
|
A build with no accounting door has no ledger, so no conversion is
|
||||||
|
provable and `asset_holds` falls back to its first route alone. That is
|
||||||
|
the honest reading and it is VISIBLE: the images concerned are reported
|
||||||
|
claimed-and-not-found, the same as before the conversion route existed.
|
||||||
"""
|
"""
|
||||||
|
accounting = build.accounting
|
||||||
|
if not isinstance(accounting, dict):
|
||||||
|
return set()
|
||||||
|
documents = accounting.get("documents")
|
||||||
|
if not isinstance(documents, list):
|
||||||
|
return set()
|
||||||
|
pairs: set[tuple[str, str]] = set()
|
||||||
|
for document in documents:
|
||||||
|
if not isinstance(document, dict):
|
||||||
|
continue
|
||||||
|
for entry in document.get("conversions") or ():
|
||||||
|
if isinstance(entry, dict):
|
||||||
|
before, after = entry.get("from"), entry.get("to")
|
||||||
|
if isinstance(before, str) and isinstance(after, str):
|
||||||
|
pairs.add((before, after))
|
||||||
|
return pairs
|
||||||
|
|
||||||
|
|
||||||
|
def _conversions(build: Build) -> dict[str, str]:
|
||||||
|
"""source digest -> the digest the run says it carried instead.
|
||||||
|
|
||||||
|
THE RUN'S LEDGER DECIDES AND THE BUNDLE ONLY CONFIRMS. A pair counts here
|
||||||
|
when `_declared_conversions` holds it AND a pointer block in the bundle
|
||||||
|
states the same pair for the asset it points at, so the two halves of the
|
||||||
|
same run have to agree before the judge believes either. The confirmation
|
||||||
|
can be forged and the ledger cannot, which is why the ledger is the one
|
||||||
|
that decides: a document adding a pointer block adds nothing, and a
|
||||||
|
document REMOVING the run's own is not a thing a document can do.
|
||||||
|
|
||||||
|
The gate's first sentence is that the fasit never comes from the reader it
|
||||||
|
judges. Reading the claim itself out of the bundle had quietly stopped
|
||||||
|
obeying it -- twice, and the second time the text had the exact shape this
|
||||||
|
code writes.
|
||||||
|
"""
|
||||||
|
declared = _declared_conversions(build)
|
||||||
|
if not declared:
|
||||||
|
return {}
|
||||||
found: dict[str, str] = {}
|
found: dict[str, str] = {}
|
||||||
for pointer in _POINTER.finditer(bundle_text):
|
for pointer in _POINTER.finditer(build.bundle_text):
|
||||||
clause = _CONVERSION.search(pointer.group("detail"))
|
clause = _CONVERSION.search(pointer.group("detail"))
|
||||||
if clause is None:
|
if clause is None:
|
||||||
continue
|
continue
|
||||||
after = clause.group("after")
|
before, after = clause.group("before"), clause.group("after")
|
||||||
# The claim has to be about the picture the block points at. A clause
|
# 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
|
# standing in one asset's block while naming another's digest is a
|
||||||
# sentence nothing in this build writes.
|
# sentence nothing in this build writes.
|
||||||
if pointer.group("asset").startswith(after[:12]):
|
if (before, after) in declared and pointer.group("asset").startswith(after[:12]):
|
||||||
found[clause.group("before")] = after
|
found[before] = after
|
||||||
return found
|
return found
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -436,12 +479,12 @@ def asset_holds(build: Build, source: Path) -> bool:
|
||||||
round a source in a format no model can be shown reaches the bundle as a
|
round a source in a format no model can be shown reaches the bundle as a
|
||||||
PNG, so its own bytes are not in `assets/` and never will be -- measured,
|
PNG, so its own bytes are not in `assets/` and never will be -- measured,
|
||||||
the day that landed R761 went from 0 to 19 claimed-and-not-found, which is
|
the day that landed R761 went from 0 to 19 claimed-and-not-found, which is
|
||||||
exactly its RLE8 BMP count. The bundle states both digests on the pointer
|
exactly its RLE8 BMP count. The pair of digests comes from the RUN's own
|
||||||
line, and this reads them and then HASHES THE ASSET ITSELF: the claim is
|
accounting (`_conversions`), never from the bundle's prose, and this then
|
||||||
accepted only when a file in `assets/` really holds the bytes the bundle
|
HASHES THE ASSET ITSELF: the claim is accepted only when a file in
|
||||||
says were written. A bundle claiming a conversion it did not perform still
|
`assets/` really holds the bytes the run says it wrote. A bundle claiming
|
||||||
fails, which is the difference between reading the bundle and believing
|
a conversion the run did not book still fails, which is the difference
|
||||||
the report.
|
between reading the artifacts and believing the report.
|
||||||
|
|
||||||
WHAT NEITHER ROUTE PROVES IS FIDELITY. Both ask whether a file in
|
WHAT NEITHER ROUTE PROVES IS FIDELITY. Both ask whether a file in
|
||||||
`assets/` holds the bytes the bundle names, and neither decodes a PIXEL:
|
`assets/` holds the bytes the bundle names, and neither decodes a PIXEL:
|
||||||
|
|
@ -458,7 +501,7 @@ def asset_holds(build: Build, source: Path) -> bool:
|
||||||
found == digest and name.startswith(digest[:12]) for name, found in build.assets.items()
|
found == digest and name.startswith(digest[:12]) for name, found in build.assets.items()
|
||||||
):
|
):
|
||||||
return True
|
return True
|
||||||
written = _conversions(build.bundle_text).get(digest)
|
written = _conversions(build).get(digest)
|
||||||
return written is not None and any(
|
return written is not None and any(
|
||||||
found == written and name.startswith(written[:12]) for name, found in build.assets.items()
|
found == written and name.startswith(written[:12]) for name, found in build.assets.items()
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue