Technical report · draft for collaborators

Declared Change Receipt

Russell Parrish, with Claude · 17 September 2026 · instrument 0.1.0, code a85d388fc6a5

AI systems now edit vector artwork on request, and they routinely change things nobody asked them to change. This report describes an instrument that takes an SVG before an edit, the SVG after, and a short declaration of what was allowed to change, and returns a reproducible receipt: what actually changed, whether each change stayed inside the declaration, and where anything leaked.

200/200held-out randomized trials correct, final code
70/70known-answer cases, adversarial
4/4 · 0reviewer-flagged Codex edits caught · false alarms (not independent)
0 · 0false leaks · missed collateral after review, 356 external model edits (4.4)

1. The problem

An edit request has two parts. The stated part ("move the Option A box up") and the unstated part ("and leave everything else alone"). Current systems are measured mostly on the first. The second is where trust breaks: a relabel that also shifts a neighboring node, a recolor that silently rewrites a shared gradient, a tool that stamps hidden identifiers into every element.

The problem is not hypothetical. Vector-Bench (Gupta and Hebbar, July 2026) asked 34 language-model endpoints to make small, specific repairs to SVG scenes while preserving everything else. The best one fully succeeded on 15% of tasks; across all 1,360 attempts, 2.35% passed. In our own starting material, a matched study of gpt-6-astra editing twelve real diagrams, icons and illustrations, reviewers judged that 4 of 24 first edits made material unrequested changes.

What is missing is a way to check a finished edit against its own claim, without an answer key and without asking another model for its opinion.

2. The instrument

Input: the SVG before, the SVG after, and a declaration: a small JSON statement naming which elements may change and in which ways (geometry, paint, text, order, structure, removal, additions within a region, frame). Elements can be named by id, CSS selector, tree path, text, or a rectangle in drawing coordinates.

Output: a receipt with a verdict, every changed element and its cause, the location of any leak in pixels, rendered before/after images and an overlay, and a hash. Anyone holding the three inputs can re-run it and get the identical receipt; receipts chain into a ledger across successive edits.

Three independent readings

  1. Structure. Elements in the two files are matched even when the edit inserts, renames, wraps, unwraps or reorders them, and each pair is compared both as source and as rendered state measured by Chrome (computed style, outline geometry independent of how a path is encoded, text layout, paint order). Every effect is traced to a cause: the element itself, an ancestor, a shared resource such as a gradient or symbol, a broken reference, a stylesheet, or the frame.
  2. Pixels in Chrome. Both files render in shared drawing coordinates. The declared elements are rendered in isolation, before and after, to get their painted footprint. Changed pixels outside that footprint are a leak, and each undeclared change gets its own footprint so the leak is attributed to an element.
  3. Pixels in a second renderer (resvg). The same outside-the-footprint question, answered by an unrelated implementation.

The readings check each other. Changed pixels that no structural change explains are flagged. A structural change no pixel shows is reported as latent rather than hidden or inflated. When the two renderers disagree, that is flagged. Noise is measured, not assumed.

Verdicts

What rendering adds

A purely structural check treats every undeclared change the same. Rendering separates what someone would see from what is merely present in the file. Three edits below make the same declared change (the sun moves right) plus one undeclared change each.

Overlay with the recolored roof in redSPILL · roof recolored, visible
Overlay with no red: the recolored circle is behind the wallLATENT · shape behind the wall recolored
Overlay with no red: only hidden attributes were addedLATENT · tool id stamped on every element
Amber: the declared sun move. Red: visible undeclared change. The middle and right receipts still list their undeclared changes element by element; they are recorded, not ignored. A check without rendering would fail all three identically.

3. A worked example

The request (case D04): "Rename the student entity to postgraduate student. Widen its box so the label fits, keeping the center fixed and reconnecting its relationships and attributes." The declaration, written from that sentence before any output was seen, allows text and geometry on the student node and geometry on its five connecting edges. Nothing else.

Entity relation diagram before the editBefore
Diagram after the edit, with the student box widened and relabeledAfter (Codex, conventional arm)
Overlay: declared changes in amber, undeclared visible changes in redReceipt overlay
Amber: changes inside the declaration (the widened box, its label, its reconnected edges). Red: visible changes outside it (the S-C relation diamond and the name attribute, each at old and new position, and a cardinality label).

The receipt reads SPILL: 6 undeclared element change(s) visible outside the declared support. It names them and measures them. The S-C relation polygon moved 55.0 units; the name ellipse moved 49.690018 units vertically. The blinded reviewers of the original study, who never saw this instrument, wrote: "the S-C relation moves left 55 units and the student name attribute moves up 49.69 units." For the other arm of the same case, the receipt measures 48.000031 and 48.0; the reviewer wrote ±48, and the receipt additionally reports an undeclared canvas widening the reviewer did not mention.

The declaration and what the receipt lists

{
  "schema": "dcr-declaration/1",
  "request": "Rename the student entity to postgraduate student. Widen its box so the label fits, keeping the center fixed and reconnecting its relationships and attributes.",
  "changes": [
    {"target": {"css": "#node3"}, "allow": ["text", "geometry", "transform", "structure"]},
    {"target": {"css": "#edge9, #edge8, #edge10, #edge11, #edge7"}, "allow": ["geometry", "transform", "structure"]}
  ]
}
Undeclared element (visible)ChangeMeasuredPixels outside declaration
ellipse attribute ellipsecy49.69 units2,411
text labely49.69 units1,710
polygon relation diamondpoints55.00 units13,085
text labelx55.00 units1,517
path edged44.20 units572
text labelx, y15.66 units382

The second renderer independently found 17,088 changed pixels outside the declaration (Chrome: 18,064) and agrees that the edit leaked. No changed pixel was left structurally unexplained.

The same receipt also lists 68 latent changes. Almost all are a data-b0-id attribute the editing tool stamped on every element. Invisible, undeclared, and reported separately so it does not drown the real finding.

Cookie icon overlay: bite in amber, crumbs in red
Case S04, "make the bite noticeably deeper... keep the chocolate chips inside the remaining cookie." Amber: the declared outline and interior chips. Red: three exterior crumbs removed from outside the cookie and three new dots added inside it. The reviewers flagged exactly this.

4. Evidence

Five kinds. The randomized trials and the Vector-Bench cross-test are the strongest; the Codex edits are the most exposed. The distinction that matters throughout is between tests where the right answer is fixed by construction and tests where it is judged.

4.1 Randomized held-out trials

A generator builds random scenes (shapes, text, symbol instances, translated groups, gradients, transparency), applies one declared edit, and in most trials also a hidden undeclared edit elsewhere. Ground truth comes from construction plus a plain pixel comparison of "declared edit only" against "declared and hidden edit", which uses none of the instrument's matching or attribution logic. Three in ten trials also rewrite the file's formatting. Every batch below used random seeds never examined before it ran.

RoundBatchCorrectNo hidden edit → CONTAINEDVisible hidden edit caughtInvisible hidden edit → LATENT
1Regular199/20055/5594/9550/50
1Small hidden edits95/100n/a78/7817/22
2Regular100/10026/2651/5123/23
2Small hidden edits100/100n/a88/88 (7/7 of 50 px or fewer)12/12

All six round-1 errors erred toward caution: no hidden edit was ever called CONTAINED. One visible reorder was charged to the wrong element; five invisible one-level color shifts were called OVERLAPPED instead of LATENT. Both causes were fixed, each with a test that fails under the old code, and round 2 ran on fresh seeds.

4.2 Known-answer and integrity cases

70 hand-built cases whose correct verdict is fixed by how the edit was made, chosen to be difficult: shared gradients, stylesheet rules, symbol instances, clip paths, markers, broken references, reused ids, occlusion, translucent overlays, frame changes that reveal clipped content, Penpot's self-masked groups, sub-pixel moves, re-encoded and reversed paths. All pass. Eighteen unit tests cover parsing, matching and masks. Eight integrity checks confirm that receipts reproduce, ledgers link, edited receipts and swapped inputs are detected, and (a documented limit) a wholly rewritten history with matching receipts is not.

4.3 Real edits by Codex

Step 1 of all twelve cases in both arms of the Source Studio study: 24 real edits to Lucide icons, Mermaid and Graphviz diagrams, and Penpot and Noto illustrations. Declarations were written from the request text and source only, then frozen by hash. The reviewers' judgments were sealed until the final run. Comparison: SPILL against either reviewer's judgment of material unrequested change.

Reviewers: materialReviewers: not material
Final code: SPILL40
Final code: not SPILL020
Frozen code (run 1): SPILL42
Frozen code (run 1): not SPILL018

Final verdicts across the 24: 16 LATENT, 4 OVERLAPPED, 4 SPILL. Run 1's two false spills came from a real bug, described below. Beyond the 2×2, the receipts surfaced two things the reviewers did not mention: the tool stamp on every output (5 to 127 elements per file) and undeclared frame changes in both arms of two cases.

Read this result with its exposure. Before writing the declarations I had read the original study's summary, which names the two failing cases. Instrument fixes came after inspecting these outputs (never the reviews). Positives come from 2 cases in 2 arms. This is a consistency check, not independent validation; section 4.4 is the independent test.

4.4 Independent cross-test: Vector-Bench

The test that removes the author-exposure problem. Vector-Bench (section 6) publishes model outputs together with repair annotations written by its authors before any model ran. A frozen rule turned those annotations into declarations; no declaration was written by hand. Predictions, a pilot slice (excluded) and automatic health tripwires were fixed before any output was opened. Scope: the 18 MIT-licensed test tasks, all 612 outputs from 34 model endpoints, of which 356 were valid SVG and measured, with 0 refusals, 0 crashes, 0 unexplained changes and 0 renderer disagreements.

Vector-Bench preservationCONTAINEDLATENTOVERLAPPEDEXCEEDEDSPILL
pass (204)1951080
fail (152)185162687

Every off-diagonal case was checked against the SVG source and both verifiers' reports. None is a missed leak or a false one:

All three predictions held. The classification of disagreements was done by the instrument's author and has not been independently reviewed; Vector-Bench is another deterministic evaluator, not ground truth; and the scenes are one synthetic family.

4.5 Speed

Median 4.6 s per real Codex edit and 3.6 s per Vector-Bench output on an M1 Max (range 3.2 to 9.4 s), including two browser page loads, repeated renders, isolated footprints and the second renderer.

5. What broke and was fixed

Real files found problems synthetic tests had not. Each fix has a regression case that fails under the old behavior.

Found byProblemFix
L03 (Codex)A CSS selector matched the whole document when frames differed, silently declaring everything.Selectors resolve against the file as authored.
L01 (Penpot)Isolating an element inside a group that defines its own mask blanked the mask, so the declared pencil had no footprint and its own change looked like a leak.Only painting elements are hidden during isolation.
L01, L04Metadata stamps propagated through references and let a container "explain" pixels changed elsewhere.Metadata and id edits neither propagate nor absorb attribution; broken references are detected explicitly.
L03Identical content rasterizes slightly differently under a different frame (up to 8 levels on edges).That artifact is measured on the unchanged files and excluded, and reported.
A test caseA renamed gradient id broke a reference, and the loss of fill was excused as a consequence.Broken references are never permitted consequences.
Held-out seed 3041A reorder was charged to the declared shape it passed.Paint-order changes are charged to the element that moved.
Held-out small batchInvisible color shifts near a declared edit read as OVERLAPPED.Counterfactual render with only that element reverted.

6. Prior art and position

Vector-Bench (July 2026) is the closest work: a deterministic SVG-editing benchmark with the same structural instincts (separating formatting from meaning, measuring preservation outside the requested edit, no model judge). It needs a hidden correct answer for every task and never renders, so an invisible metadata change fails a task exactly like a visible one. Under its rule, all 24 Codex edits here would fail on the tool stamp alone.

C2PA Content Credentials sign claims about edits but do not check that a claim describes what actually changed; a 2026 security analysis also finds its signing layer unreliable today. Visual regression tools compare screenshots with approved baselines, not with a statement of intended change.

CapabilityThisVector-BenchVisual regressionC2PA
Works without a correct answeryesnoneeds baselinen/a
Checks against declared intentyeshidden annotationshuman approvalclaims, unchecked
Separates visible from invisible changeyesnonono
Locates and attributes leaks in pixelsyesnopartlyno
Judges quality of the requested editnoyesnono
Signed, externally anchorednononoyes

Position: not "a new way to catch unintended SVG changes", which Vector-Bench already frames well for evaluation. Rather: a verifier for edit claims that works on real edits with no answer key, separates what is seen from what is merely present, and issues a receipt anyone can re-measure. It complements Vector-Bench (quality of the requested repair) and C2PA (signing the claim).

7. Conclusions

The instrument is valid. Given a before file, an after file and a statement of what was allowed to change, it reports whether anything else changed, what, where, and whether it is visible. Three kinds of evidence support this: randomized trials with answers fixed by construction, adversarial hand-built cases, and 356 real model edits judged independently by Vector-Bench. None of them shows it missing real collateral or reporting collateral that is not there.

It does not need an answer key. Vector-Bench can grade an edit only because a correct version was prepared in advance. Working from annotations of intended change alone, this instrument reached consistent verdicts: no missed collateral and no false leaks. Verification is therefore possible on real edits that nobody has solved, which is where a verifier is actually used.

Rendering and element matching matter, but at the margin. Of 356 verdicts, 7 differed because the code-level evaluator counted invisible changes as damage (5) or misread a reordered file as a rewritten scene (2). Another 24 differed only in how scope was described or where a failure was filed. The advantage over code-level checking is real and demonstrated, and it is small.

The weak point is the declaration, not the measurement. Every genuine disagreement traced to how intended change was described (a family of changes here, a single attribute there), never to a measurement error. Whether people can write good declarations quickly is untested, and it decides practical use.

Novelty is modest. Each ingredient exists: visual differencing, structural differencing, preservation outside an edit mask, signed edit claims. What is new is their combination into a target-free verifier of declared scope that separates visible from invisible change. That is a sound, publishable short contribution, not a new field. As a standalone tool for SVG it serves a narrow audience: teams that edit vector artwork at volume and need per-edit accountability, most of whom work inside their own formats.

The durable result is the pattern. Declare what was supposed to change; measure what actually changed, deterministically; separate what can be seen from what is merely present; issue a receipt anyone can re-check. SVG was the fully controllable case in which to prove that pattern end to end. Its value lies where edits are already claimed but not verified.

In one sentence: a deterministic, rendering-based check can verify whether a vector edit stayed within its stated scope without an answer key, agreeing with an independent evaluator except where that evaluator cannot see what is actually visible; the open questions are human agreement, how easily people can write declarations, and signing.

8. Uses

9. Limits and exposure

10. Next domain: claimed edits to images

The pattern matters most where edits are already claimed but not verified. C2PA Content Credentials record signed edit actions on photographs and generated images, including an optional region of interest, and validators check only that those claims are intact, not that they are true. The natural next domain is therefore raster images carrying C2PA claims.

The test that would decide it: take images with Content Credentials, compare claimed regions against measured change, and count claims that are materially wrong.

11. Reproduce

cd declared-change
npm ci
npm test                                   # unit
node tests/known-answer.mjs                # known-answer cases
node tests/integrity.mjs                   # receipts and ledger
node tests/fuzz.mjs 100 7000 512 out/reg   # randomized, regular
SMALL=1 node tests/fuzz.mjs 100 6000 512 out/small
node validation/run-paired.mjs && python3 validation/compare.py
node validation/vector-bench/run.mjs validation/vector-bench/test sv_001,...,sv_018
python3 validation/vector-bench/compare.py validation/vector-bench/test
node tools/build-report.mjs                # this document

Detail lives beside the code: README.md (usage), tests/HELDOUT.md, validation/paired-01/FINDINGS.md, validation/vector-bench/FINDINGS.md, validation/paired-01/POST-FREEZE-CHANGES.md, PRIOR-ART.md, PROVENANCE.md.