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
|
|
@ -34,6 +34,7 @@ from .assets import (
|
|||
AssetRejection,
|
||||
ExtractedImage,
|
||||
asset_name,
|
||||
conversion,
|
||||
)
|
||||
from .connectors import safe_resolve
|
||||
from .errors import IngestError, MaterializationError, SegmentationError, SourceError
|
||||
|
|
@ -652,11 +653,19 @@ class InboxResult:
|
|||
|
||||
@dataclass(frozen=True)
|
||||
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
|
||||
carried: int
|
||||
rejected: tuple[AssetRejection, ...]
|
||||
conversions: tuple[tuple[str, str], ...] = ()
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
|
@ -1448,6 +1457,11 @@ def process_inbox(
|
|||
source_file=source_name(path),
|
||||
carried=len(document.images),
|
||||
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:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue