478 Commits
Author SHA1 Message Date
Victor Kuznetsov 8871eb3f67 Release 0.29.0
pre-commit: 1) maintain.sh - exit 1 on uv-secure, lightning PYSEC-2026-3624 unchanged from 0.28.1, no fixed release exists, vulnerable API unreachable (no load_from_checkpoint in project or trustmark); ruff, pyright src/, and 1386 tests passed separately; 2) /simplify - version bump only; 3) docs sync - no version refs outside pyproject, __init__.py, uv.lock; 4) CLAUDE.md - no change
v0.29.0
2026-08-25 13:25:33 -07:00
Victor Kuznetsov 44ec203c22 Drop the stale untiled restriction from text restoration docs 2026-08-25 13:22:49 -07:00
Victor Kuznetsov 4beb61463a Say metadata check and strip results are not a clean verdict 2026-08-25 13:22:38 -07:00
Victor Kuznetsov 0d11e4099c Fix adaptive text restoration crop bounds 2026-08-25 12:44:05 -07:00
Victor Kuznetsov fa83c1444f Add geometry-only text manifest schema 2026-08-25 11:33:21 -07:00
Victor Kuznetsov f52e40ae24 Name Copilot, Dreamina, and Qwen in C2PA source labels 2026-08-21 14:43:39 -07:00
Victor Kuznetsov 0aa03c4e89 Release 0.28.1
pre-commit: 1 maintain.sh - uv-secure lightning PYSEC-2026-3624 known, no fix, same as 0.28.0; ruff/pyright/1378 tests passed; 2) /simplify - version bump only; 3) docs sync - no refs; 4) CLAUDE.md - no change
EOF
)
v0.28.1
2026-08-19 21:46:14 -07:00
Victor Kuznetsov 91aa49b03a Allow verified-text restoration on tiled images
Tile the Qwen VAE donor the same way as the global pass. Glyph restore already runs on the blended full frame, so the old tile ban was an artificial gate.
2026-08-19 21:33:22 -07:00
Victor Kuznetsov ee865e95e4 Add geometry draft mode and pad silhouette descenders
pre-commit: 1 maintain.sh - not rerun full, ruff + 34 targeted tests passed; 2) /simplify - pad lives in silhouette crop, stable=False reuses probes; 3) docs sync - no README/cli refs to update; 4) CLAUDE.md - no change
EOF
)
v0.28.0
2026-08-19 19:53:38 -07:00
Victor Kuznetsov d84fd1a125 Merge pull request #86 from wiltodelta/dependabot/uv/minor-and-patch-267694dd16
Bump the minor-and-patch group across 1 directory with 7 updates
2026-08-19 12:22:54 -07:00
Victor Kuznetsov e315eacb14 Document automated-operator verified manifests
verified:true may attest machine-verified geometry (stability-gated
boxes inside caps) set by a service; the pipeline consumes box/script
only, text is advisory, so the verification burden is on the geometry.
2026-08-19 12:21:32 -07:00
Victor Kuznetsov 3ba4ace7e0 Disable mkldnn in the draft engines (linux PIR crash) 2026-08-19 10:22:58 -07:00
dependabot[bot] 13888704f8 Bump the minor-and-patch group across 1 directory with 7 updates
Bumps the minor-and-patch group with 7 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [python-dotenv](https://github.com/theskumar/python-dotenv) | `1.2.2` | `1.2.3` |
| [c2pa-python](https://github.com/contentauth/c2pa-python) | `0.37.5` | `0.37.7` |
| [av](https://github.com/PyAV-Org/PyAV) | `18.0.0` | `18.1.0` |
| [transformers](https://github.com/huggingface/transformers) | `5.14.1` | `5.15.0` |
| diffsynth | `2.1.0` | `2.1.2` |
| [paddleocr](https://github.com/PaddlePaddle/PaddleOCR) | `3.4.1` | `3.7.0` |
| [ruff](https://github.com/astral-sh/ruff) | `0.16.2` | `0.16.3` |



Updates `python-dotenv` from 1.2.2 to 1.2.3
- [Release notes](https://github.com/theskumar/python-dotenv/releases)
- [Changelog](https://github.com/theskumar/python-dotenv/blob/main/CHANGELOG.md)
- [Commits](https://github.com/theskumar/python-dotenv/compare/v1.2.2...v1.2.3)

Updates `c2pa-python` from 0.37.5 to 0.37.7
- [Release notes](https://github.com/contentauth/c2pa-python/releases)
- [Changelog](https://github.com/contentauth/c2pa-python/blob/main/docs/release-notes.md)
- [Commits](https://github.com/contentauth/c2pa-python/compare/v0.37.5...v0.37.7)

Updates `av` from 18.0.0 to 18.1.0
- [Release notes](https://github.com/PyAV-Org/PyAV/releases)
- [Changelog](https://github.com/PyAV-Org/PyAV/blob/v18.1.0/CHANGELOG.rst)
- [Commits](https://github.com/PyAV-Org/PyAV/compare/v18.0.0...v18.1.0)

Updates `transformers` from 5.14.1 to 5.15.0
- [Release notes](https://github.com/huggingface/transformers/releases)
- [Commits](https://github.com/huggingface/transformers/compare/v5.14.1...v5.15.0)

Updates `diffsynth` from 2.1.0 to 2.1.2

Updates `paddleocr` from 3.4.1 to 3.7.0
- [Release notes](https://github.com/PaddlePaddle/PaddleOCR/releases)
- [Commits](https://github.com/PaddlePaddle/PaddleOCR/compare/v3.4.1...v3.7.0)

Updates `ruff` from 0.16.2 to 0.16.3
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/0.16.2...0.16.3)

---
updated-dependencies:
- dependency-name: python-dotenv
  dependency-version: 1.2.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: minor-and-patch
- dependency-name: c2pa-python
  dependency-version: 0.37.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: minor-and-patch
- dependency-name: av
  dependency-version: 18.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: minor-and-patch
- dependency-name: transformers
  dependency-version: 5.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: minor-and-patch
- dependency-name: diffsynth
  dependency-version: 2.1.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: minor-and-patch
- dependency-name: paddleocr
  dependency-version: 3.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: minor-and-patch
- dependency-name: ruff
  dependency-version: 0.16.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: minor-and-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-19 17:19:11 +00:00
Victor Kuznetsov e938b57f8c Package the verified-text draft annotator as text_draft
Move the draft-annotation logic (PaddleOCR line detection, word-box
grouping, three script-chosen recognition engines, crop-jitter
stability gate) from the evaluation-only scripts into the installable
package, with lazy paddle imports and a text-draft extra (CPU, no
torch). draft_text_lines() returns accepted (crop-stable, NEVER
ground-truth-correct - precision on the reference posters was 90.0% /
94.4%) and rejected proposals; source_pixel_sha256 is re-exported for
manifest building. scripts/infer_text_lines.py now dogfoods the
package module instead of loading the eval script by path.
2026-08-19 10:13:22 -07:00
Victor Kuznetsov 3cd1e47935 Add the oracle-measured Google strength floor to qwen-zimage
The resolution curve's 0.154 top left the 4.33 MP CJK-sign fixture
SynthID-detected x3 in Gemini on the full production path (visible
stage -> qwen-zimage seed 0 -> resize-back -> metadata strip), with a
valid pixel-identical stripped control in the same session
(2026-08-18). Google-provenance content now resolves to the flat
QWEN_ZIMAGE_GOOGLE_STRENGTH 0.30 floor instead of the area curve -
anchors at 0.30 measured clean in Gemini on two fixtures (CJK sign +
18-face) at 3/3 checks across two work accounts, and stayed clean
under the vae-glyphs donor layer. openai/unknown content keeps the
curve; an explicit strength still wins.
2026-08-18 22:34:41 -07:00
Victor Kuznetsov 2298282b8b Thread fidelity_anchor through WatermarkRemover
The engine forwards the new kwarg into WatermarkRemover.remove_watermark,
which rejected it with TypeError on the real (non-fake) path - caught by
the deployed Modal smoke test, not by the unit fakes.
2026-08-18 21:32:57 -07:00
Victor Kuznetsov 42d427731f Turn the verified-text fidelity anchor off by default
The whole-frame 15% Qwen-VAE blend returned detector-visible OpenAI
SynthID on poster-scale manifests through the engine text-manifest
path (official Content Provenance API, 2026-08-19: restored detected
x6 with the anchor, clean x6 without it; base outputs clean x6;
pixel-identical stripped controls detected, proving the pixel channel).
Add fidelity_anchor=False to remove_watermark and InvisibleOptions and
--fidelity-anchor on the CLI to reproduce the 0.27.0 research
behavior. Text-box MAE cost of the new default is under one point on
all three fixtures (11.60->11.72, 7.79->7.86, 7.57->8.13).
2026-08-18 21:23:25 -07:00
Victor Kuznetsov 50d9f44829 Guide text-heavy content away from forced diffusion scrubs
Issue #84: text, tables, and UI screenshots are the worst case for
--force regeneration. Document that the default pipeline skips the
invisible stage without a detectable signal, that visible/metadata
stages never redraw glyphs, and why paste-back is not offered.
2026-08-18 11:03:53 -07:00
Victor Kuznetsov 2e1428406e Release 0.27.0 v0.27.0 2026-08-18 11:02:18 -07:00
Victor Kuznetsov 21ce1dfadd Cover QuickTime TC260 placements, Luma AI tags, and MiniMax video provenance 2026-08-17 19:38:37 -07:00
Victor Kuznetsov 2369d351a9 Merge pull request #83 from wiltodelta/chore/text-preservation-evaluation
Add verified text restoration
2026-08-15 12:30:27 -07:00
Victor Kuznetsov 432b63b6d7 Add verified text restoration 2026-08-15 12:25:35 -07:00
Victor Kuznetsov 8c00525946 Evaluate selective text restoration 2026-08-15 12:20:25 -07:00
Victor Kuznetsov f8003f505a Merge pull request #82 from wiltodelta/agent/c2pa-validation
Validate C2PA credentials before attribution
2026-08-15 11:38:47 -07:00
Victor Kuznetsov 2eab24a2e1 Validate C2PA credentials before attribution 2026-08-15 11:31:59 -07:00
Victor Kuznetsov 8201ada070 Merge pull request #81 from wiltodelta/docs/video-synthid-parked-state
Record the video SynthID measurements and park the investigation
2026-08-15 11:11:05 -07:00
Victor KuznetsovandClaude Opus 5 b7d44ff1a3 Warn users that the invisible video path leaves audio untouched
The finding lived only in the research archive, whose own banner says it may
describe prototypes that were later removed. It belongs where users read
limitations: the path strips every metadata marker while copying the audio
bitstream byte for byte, so a clean local report on a clip with generated audio
is unproven rather than a guarantee.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 11:08:09 -07:00
Victor KuznetsovandClaude Opus 5 e8f65022cc Record the video SynthID measurements and park the investigation
Everything here is evidence and state, not behavior: the shipped operating point
is untouched at 512 px / 12 fps / noise_std=0.15.

The carrier was downloaded and its sha256 matched the manifest, so its properties
are now measured rather than assumed: 1920x1080 at 24 fps with an AAC audio track.
Those three fields go into the two 2026-07-31 rows, which could not previously
tell a reader what the downscale factor even was.

The geometry prize is measured end to end for the first time. Against the
untouched source, 1024 scores +3.46 dB over the shipped 512, while the entire
noise_std axis is worth 1.92 dB. Two readings that the table alone hides are
recorded with it: the temporal residual IMPROVES with resolution, because the
shared noise field lives on the latent grid and is four times finer relative to
the frame at 1920; and the frame-rate arm cannot be judged by these metrics at
all, since they price its cost and not the smoothness it buys.

Two findings that outrank the quality question. The certified row does not
reproduce -- a rerun of the same configuration on a different device and dtype
gives a different hash and different metrics, and the manifest records neither.
And the pipeline copies audio byte for byte while stripping every metadata
marker, so it can emit a file our own detector calls clean with an untouched
Google-generated audio track inside it. The mechanism is proven on two carriers;
whether that audio carries a mark the verifier reads is not, and only the oracle
can say.

The six prepared oracle submissions were never run: file upload to the verifier
failed at the tool level. Their artifacts are gone with the scratch directory,
which is the intended lifecycle for generated media, and the document says what
rebuilding costs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 11:01:14 -07:00
Victor KuznetsovandClaude Opus 5 4855586834 Halve decoder memory with strip processing and a raveled Haar pass
Each Haar pass is one flat pywt.downcoef call over a raveled strip instead of
pywt.dwt(..., axis=1)[0], and the plane is walked in strips so no full-plane
float64 intermediate exists. Exact only while the last axis is even, so
_approximation raises on an odd width rather than returning wrong bits, and
TestRaveledHaarPass pins both that raise and the downcoef/dwt equivalence a
pywt upgrade could take away.

Drops the block constructor knob: the fold chains are written for 4, nothing
ever passed another value, and a knob that silently decodes wrong is worse
than no knob.

Peak RSS 111 MB to 21 MB on a 4.3 MP image; the decoder itself 0.011s to
0.007s, which is only 0.4% of identify() now that it is under 2% of the run.
Output bits and detector verdicts over 200 sampled data/ images, two
synthesized carriers and eight degenerate shapes are byte-identical to the
pre-vectorization decoder.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 13:33:34 -07:00
Victor KuznetsovandClaude Opus 5 37789e02f6 Vectorize the DWT-DCT decode path, 15x on the decoder
Output stays bit-identical: decoder bits and detector verdicts recorded over
200 sampled data/ images plus two synthesized carriers before and after, and
the record is byte-identical.

Measured on a 1536x2816 image -- decoder 0.280s to 0.016s, warm identify()
1.757s to 1.365s with both arms timed in one process.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 11:59:56 -07:00
Victor Kuznetsov 2d03a00a39 Merge pull request #80 from wiltodelta/dependabot/uv/minor-and-patch-2677c98c32
Bump the minor-and-patch group across 1 directory with 5 updates
2026-08-12 20:49:38 -07:00
dependabot[bot] acbf6afac3 Bump the minor-and-patch group across 1 directory with 5 updates
Bumps the minor-and-patch group with 5 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [c2pa-python](https://github.com/contentauth/c2pa-python) | `0.37.2` | `0.37.5` |
| diffsynth | `2.0.18` | `2.1.0` |
| [huggingface-hub](https://github.com/huggingface/huggingface_hub) | `1.26.0` | `1.27.0` |
| [packaging](https://github.com/pypa/packaging) | `26.2` | `26.3` |
| [ruff](https://github.com/astral-sh/ruff) | `0.16.1` | `0.16.2` |



Updates `c2pa-python` from 0.37.2 to 0.37.5
- [Release notes](https://github.com/contentauth/c2pa-python/releases)
- [Changelog](https://github.com/contentauth/c2pa-python/blob/main/docs/release-notes.md)
- [Commits](https://github.com/contentauth/c2pa-python/compare/v0.37.2...v0.37.5)

Updates `diffsynth` from 2.0.18 to 2.1.0

Updates `huggingface-hub` from 1.26.0 to 1.27.0
- [Release notes](https://github.com/huggingface/huggingface_hub/releases)
- [Commits](https://github.com/huggingface/huggingface_hub/compare/v1.26.0...v1.27.0)

Updates `packaging` from 26.2 to 26.3
- [Release notes](https://github.com/pypa/packaging/releases)
- [Changelog](https://github.com/pypa/packaging/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pypa/packaging/compare/26.2...26.3)

Updates `ruff` from 0.16.1 to 0.16.2
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/0.16.1...0.16.2)

---
updated-dependencies:
- dependency-name: c2pa-python
  dependency-version: 0.37.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: minor-and-patch
- dependency-name: diffsynth
  dependency-version: 2.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: minor-and-patch
- dependency-name: huggingface-hub
  dependency-version: 1.27.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: minor-and-patch
- dependency-name: packaging
  dependency-version: '26.3'
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: minor-and-patch
- dependency-name: ruff
  dependency-version: 0.16.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: minor-and-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-12 22:57:56 +00:00
Victor Kuznetsov 25eaa7458c Support Python 3.11 through 3.14 2026-08-12 15:52:11 -07:00
Victor KuznetsovandClaude Opus 5 58146dbc6d Say what the hosted service is instead of offering it as a fallback
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 14:24:56 -07:00
Victor KuznetsovandClaude Opus 5 b08899dc00 Note that the package name is the published distribution
Renaming it strands every pip install and every pinned dependency on it,
including the ComfyUI node package, and the console script has the same
property since users have it on PATH. Recorded at the name itself, where
whoever considers changing it will be looking.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 13:52:48 -07:00
Victor Kuznetsov b1319b1d5a Release 0.26.3 v0.26.3 2026-08-09 21:15:15 -07:00
Victor Kuznetsov 353bc5f12c Harden TrustMark detection with an official fixture 2026-08-09 21:08:09 -07:00
Victor KuznetsovandClaude Opus 5 370556ffe6 Ignore other harnesses' generated config before it appears
These directories are empty today, so git shows nothing and the gap is
invisible. The moment the sync writes into one, a routine running
git add -A commits another harness's generated config, and the harness
audit then fails after the fact rather than before it. Paths are the
canonical untrackedConsumerPaths list, anchored to the repo root so a
same-named source file deeper in the tree is not caught.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 17:04:05 -07:00
Victor KuznetsovandClaude Opus 5 14a27d224d Drop the settings.json that only repeats global defaults
All four plugins it enabled are enabled globally, and its allow list says
nothing under the global defaultMode: bypassPermissions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 16:51:37 -07:00
Victor Kuznetsov 015df40f49 Release 0.26.2 and document distribution surfaces v0.26.2 2026-08-09 00:27:10 -07:00
Victor Kuznetsov f9beef365f Remove conda distribution support 2026-08-08 21:18:10 -07:00
Victor Kuznetsov 9b656513f1 Improve C2PA attribution and ByteDance metadata handling 2026-08-08 19:34:40 -07:00
github-actions[bot] ab03ecc507 Sync conda recipe with v0.26.1 2026-08-07 01:21:28 +00:00
Victor Kuznetsov 14b7247e0b Fix SynthID provenance evidence and release 0.26.1 v0.26.1 2026-08-06 18:17:40 -07:00
Victor Kuznetsov d3eeba3a33 Merge pull request #78 from wiltodelta/research/video-synthid-quality-groundwork
Make the video SynthID operating point measurable and hard to move silently
2026-08-05 22:35:17 -07:00
Victor KuznetsovandClaude Opus 5 613b349358 Cover the fidelity probe, and run it where ffmpeg actually exists
The probe shipped with no tests while every sibling research script has them,
and it needs neither a model nor a GPU, so nothing justified the gap. Its whole
output is a ranking and a mispaired comparison still prints a plausible number,
so the pairing tests carry more weight than the metric ones.

The centerpiece builds a correctly phased and a wrong-phased delivery of the
same clip. Both have six frames, so the frame-count check the probe used to rely
on passes for both; the misaligned one scores near 9 dB against 40+. A separate
assertion pins that the probe binds the engine's sampler rather than a copy,
which is the contract a same-phase private copy would otherwise satisfy
silently. This file now holds the only constraint on that sampler's phase.

CI ran none of this. The test matrix installs no ffmpeg, and the job that does
install it ran one class from another file, so five of these tests would have
skipped on every runner. The video-e2e job now collects this file too.

Recorded in .claude/rules/development.md: mutating the sampler's >= to > proves
nothing, because the 1e-9 epsilon makes both branches agree on exact equality.
The phase or the period is what has to move. That inert mutation passed a green
suite here before the real one failed five tests.

Dropped the downscale test's PSNR bound: solid colors survive both the downscale
and the upscale exactly, so any score there pins the local ffmpeg's chroma
rounding rather than the probe. Geometry is the assertion that means something.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 22:24:49 -07:00
Victor Kuznetsov acd7092477 Merge remote-tracking branch 'origin/main' into research/video-synthid-quality-groundwork 2026-08-05 21:46:19 -07:00
github-actions[bot] e08644dcf2 Sync conda recipe with v0.26.0 2026-08-06 04:23:38 +00:00
Victor Kuznetsov 1a38c556f8 Merge pull request #77 from wiltodelta/claude/image-metadata-detection-benchmark-0bb6ec
Add versioned metadata and pixel evidence transports
v0.26.0
2026-08-05 21:19:32 -07:00
Victor Kuznetsov a83952e375 Add versioned forensic metadata transports 2026-08-05 21:10:55 -07:00