feat(usage): report which Blendkit assets are still in the file at every save [User issue investigation] - #2296
Open
PetrDlouhy wants to merge 9 commits into
Open
feat(usage): report which Blendkit assets are still in the file at every save [User issue investigation]#2296PetrDlouhy wants to merge 9 commits into
PetrDlouhy wants to merge 9 commits into
Conversation
…ery save
The save-time usage report has been dead for years, twice over:
- get_asset_usages() returned {} on every call (issue #1013), so the
save_pre handler never sent anything.
- Even when it had data, client_lib.report_usages() posted to the Client's
/report_usages route, which only the retired Python daemon ever had. The Go
Client answers 404 and requests.post does not raise, so the report vanished
silently. Production holds 3 "save" reports for all of 2026.
This repairs both and gives the report the semantics the server side
(BlenderKit-server #3727, #3792, #3805) stores as per-scene presence state:
- collect_present_assets(scene) counts the Blendkit assets a scene actually
uses: objects and collection instances linked to it, materials in their
slots (one per slot), its world, the scene itself when it is a scene
asset; brushes and node groups are file-wide and attributed once, to the
active scene. Orphan datablocks do not count - Blender drops them on save.
- build_save_reports() emits one report per scene that ever touched Blendkit.
A scene with a uuid is reported even when empty: "nothing left" is the
removal the server needs to see. Unchanged presence is re-reported at most
hourly (SAVE_REPORT_HEARTBEAT_SECONDS); any change reports on the next
save; the memory is in-process, so the first save of a session always
reports.
- The report goes through the Client's generic nonblocking forwarder to
/api/v1/usage_report/, which adds the auth headers; the result task is
logged, never shown as a popup (handle_nonblocking_request_task), and a
Client that is not running is a warning, never a failed save.
- utils.get_scene_id(scene=None) now guarantees a uuid unique across the
file. Blender copies custom properties when a scene is created from
another one ("New" included), so a second scene inherited the first one's
uuid and the server merged both histories. The first scene in
bpy.data.scenes keeps a shared uuid; later scenes holding it get a fresh
one. This also fixes download attribution in multi-scene files.
Verified end to end on devel.blendkit.com with Blender 5.2 and Client
1.12.13: download / save / delete / save / save-as, a two-scene file, logged
out (anonymous scene rows), and the Client-down warning path.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
PetrDlouhy
force-pushed
the
claude/save-presence
branch
from
September 9, 2026 09:31
7bf8073 to
e3665cd
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #2296 +/- ##
==========================================
+ Coverage 33.14% 34.10% +0.96%
==========================================
Files 86 87 +1
Lines 27187 27527 +340
==========================================
+ Hits 9010 9388 +378
+ Misses 18177 18139 -38
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
… list tests/test.py imported a hard-coded list of test modules. A new test file that was not added to the list never ran in CI while the workflow stayed green - exactly what happened to tests/test_save_presence.py in this PR: CI ran 513 tests, the Codecov report showed the file at 0%, and nothing else complained. Keep the explicit list only for what it is actually needed for - the modules that must run first, in that order, because the client tests depend on the state earlier modules leave behind - and append every other tests/test_*.py of the installed package after them. A listed module that does not exist is fatal, so the list cannot rot either. Discovery stays import-by-qualified-name (not TestLoader.discover) for the extension-mode relative-import reason documented above it. Blender 5.2.0 locally: 528 tests from 25 modules (was 513 from 24). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
agajdosi
requested changes
Sep 9, 2026
Review (agajdosi): the usage-report result was special-cased inside the generic utils.handle_nonblocking_request_task, which put feature logic and report parsing into a shared utility. The generic handler is back to what it was. The report result now has its own download.handle_usage_report_task (logged, never a popup), and timer.handle_task routes it there like every other task type. The Client's forwarder returns every request as "wrappers/nonblocking_request" and has no usage-report route of its own, so the routing key is the request URL (download.is_usage_report_task); a dedicated Client route would remove even that, and is a Client change, not an add-on one. Blender 5.2.0 locally: 530 tests (two new dispatch tests in test_timer). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The server gave the save-time presence report its own endpoint (Blendkit-server#3831) instead of telling it apart from usage reports by the report_type in the body. Post there; the endpoint is the type, so reportType is gone from the payload. The result task is still recognised by its URL, since the Client's forwarder has no route of its own for it. Requires Blendkit-server#3831 on production before release. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Review (agajdosi): riding the general-purpose non-blocking wrapper for a
frequent, specialised report is an anti-pattern however the result task
is routed afterwards. The Client gets a dedicated /report_usages route
(bk_client, claude/usage-report-route) that forwards the report to
/api/v1/scene_save_reports/ with the standard headers and, like
/report_event, creates no Task at all.
The add-on posts {"report": ...} there and stops sniffing task URLs: the
task-type routing in timer.py, download.handle_usage_report_task and
is_usage_report_task are gone. A Client without the route answers 404,
which scene_save only logs, so nothing breaks before the Client release.
Requires the bk_client route released in the v1.12 series (the add-on
pins the minor and resolves the patch at build time).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A save report only sees files that get saved: an HDR dropped into the world for a test render or a material tried once is used and never saved, so on save data alone those asset types look unused. A finished render job is the strongest evidence of use and cannot be reconstructed later, so it is reported from the first release. render_complete (once per render job, not per frame) sends the same presence report for the rendered scene with event "render"; save reports now carry event "save". Saves and renders are deduplicated apart, so a render right after an identical save still reports, and a repeat render within the hour does not. Server side: Blendkit-server#3835. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ient Michal (#add-on, 2026-09-10): data observed on the user's machine needs an opt-out, in one place every host shares. The choice lives in the Client's shared settings (bk_client#52, `usage_data_opt_out`), which the Client enforces at its /report_usages route; this add-on only exposes and mirrors it. - Preference "Send usage data to improve Blendkit" (default on). Changing it posts the opt-out to /settings/set and saves the preferences; a Client that is not running is logged. Persisted with the other preferences. - The Client broadcasts its settings with every report; a new "settings" task handler mirrors the opt-out into the preference (changed from Maya, seen in Blender) without echoing it back. - Save and render handlers return before collecting anything when the preference is off. - Transport per agajdosi's review: the report is now a "report_usages" task of its own, routed by timer.handle_task to download.handle_usage_report_task (logged, never a popup). Requires bk_client#52 released in the v1.12 series before this ships. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The save-time usage report ("which Blendkit assets are still in the file") has been dead for years, twice over:
get_asset_usages()returned{}on every call (#1013), andclient_lib.report_usages()posted to a Client route (/report_usages) that only the retired Python daemon ever had; the Go Client answers 404 andrequests.postdoes not raise, so nothing ever left the machine. Production holds 3 such reports for all of 2026.The report is the first phase of the search-relevance and redistribution data plan (server side: BlenderKit-server #3727 storage, #3792 anonymous reports, #3805 per-scene locking). Nothing consumes it yet; the server keeps per-scene presence state (first download, first/last seen at a save, instance count, removed-at) for later simulation.
What
collect_present_assets(scene, file_wide)counts what a scene actually uses: objects and collection instances linked to it, materials per slot, its world, the scene itself when it is a scene asset; brushes and node groups are file-wide and attributed once, to the active scene. Orphan datablocks do not count (Blender drops them on save).build_save_reports()emits one report per scene that ever touched Blendkit, an empty list included (that is the removal signal). Unchanged presence is re-reported at most hourly (SAVE_REPORT_HEARTBEAT_SECONDS); any change reports on the next save. The memory is in-process, so the first save of every session reports./wrappers/nonblocking_requestto/api/v1/usage_report/(auth headers added by the Client). The result task is logged, never a popup; a Client that is not running is one warning and a normal save.utils.get_scene_id(scene=None)guarantees a uuid unique across the file. Blender copies custom properties when creating a scene from another ("New" included), so a second scene inherited the first one's uuid and the server merged their histories. First scene inbpy.data.sceneskeeps it; later scenes get a fresh one. This also fixes download attribution in multi-scene files.Verified
tests/test_save_presence.py, run under a realbpy5.0 wheel and in the Blender test matrix: collector counts, file-wide attribution, per-scene reports, uuid de-duplication, dedupe and heartbeat, transport call, silent result handling, background mode.Not changed
No UI, no prompt.
check_unused()is untouched. TheproximitySetfield is sent empty for compatibility with the server serializer.🤖 Generated with Claude Code
Second commit: the test runner skipped new test files
The Codecov report on the first push showed
tests/test_save_presence.pyat 0%:tests/test.pyimported a hard-coded module list and the new file was not on it, so CI ran 513 tests, green, without the 15 new ones. The second commit keeps the list only for the modules that must run first (in that order) and appends every othertests/test_*.pyautomatically; a listed module that does not exist is fatal. CI now runs 528 tests; patch coverage 96% (the only misses are the runner script itself, which coverage cannot measure, plus one line in the test module).Side finding while looking for the report: Codecov had processed nothing for this repo since the rename to
Blendkiton 2026-08-21 (uploads were accepted and dropped). A GET on the Codecov v2 API with the new name resynced the repo; the workflows for this PR and for the latestmainwere rerun afterwards, so the base is current again.Third and fourth commits: review + endpoint
410c0cdb(agajdosi's review): the usage-report result has its owndownload.handle_usage_report_task, routed fromtimer.handle_task; the generic non-blocking handler is generic again.b478ccd8: reports post to/api/v1/scene_save_reports/(Blendkit-server#3831 gives them their own endpoint instead of areport_typebranch in the usage endpoint);reportTypeis gone from the payload. Requires Blendkit-server#3831 on production before this is released.Fifth commit: the Client's own route (agajdosi, round two)
The report no longer rides
wrappers/nonblocking_requestat all. The add-on posts{"report": ...}to the Client's dedicated/report_usagesroute, which forwards it to/api/v1/scene_save_reports/in the background and, like/report_event, creates no Task; the URL-sniffing handler and the timer routing are gone. Depends on the bk_client route (branchclaude/usage-report-route, PR to follow) being released in the v1.12 series. A Client without it answers 404, whichscene_saveonly logs.Sixth commit: renders
render_complete(once per render job) sends the same presence report for the rendered scene with"event": "render"; save reports carry"event": "save". Renders are the strongest evidence of use and the only one for files that are rendered but never saved (test renders of HDRs and materials), and they cannot be reconstructed later. Deduplicated apart from saves. Server side: Blendkit-server#3835 (event column + render presence columns).Seventh commit: opt-out + the Client's own task type
usage_data_opt_out, feat: /report_usages route and the usage_data_opt_out shared setting [User issue investigation] bk_client#52) and enforced there; this add-on exposes it, pushes changes to/settings/set, mirrors the Client's settings broadcast back into the preference (a newsettingstask handler), and skips collecting entirely when off.report_usagestask of its own, routed bytimer.handle_tasklike every other task type; nothing rides the generic wrapper any more.Release order: bk_client#52 → Blendkit-server#3843 → this.