davinci-resolve-mcp 2.95.0 → 2.95.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,97 @@
2
2
 
3
3
  Release history for the DaVinci Resolve MCP Server. The latest release is summarized in the root README; older entries live here to keep the README focused.
4
4
 
5
+ ## What's New in v2.95.2
6
+
7
+ Adds the live round-trip harness the offline `.drp` tier never had — the absence
8
+ of which is why three unbacked "verified live" claims survived in its README, one
9
+ reaching a shipped `api_truth` recommendation before being caught.
10
+
11
+ ### Added — `tests/live_drp_roundtrip_verification.py`
12
+
13
+ Authors a `.drp` per primitive, imports it into a running Resolve, asserts intent
14
+ against structural readback, **and exports the composited frame to assert the
15
+ item is actually visible on screen.**
16
+
17
+ That last assertion is the whole point. `place_fusion_title` satisfies every
18
+ structural check — right track, frame, duration, `PrettyType`, and correctly
19
+ encoded text — while rendering nothing, so a harness that only diffed
20
+ `GetStart()`/`GetDuration()` would have called it green. Visibility is measured
21
+ with `Project.ExportCurrentFrameAsStill` plus a luma pass: an inert item yields
22
+ `max=0` across the entire frame, a real one does not (the live control returns
23
+ `max=255` with ~16k bright pixels).
24
+
25
+ **Known-broken cases are declared, not skipped.** `place_fusion_title` is
26
+ recorded as `KNOWN BROKEN`; if it starts working the harness reports `UNEXPECTED
27
+ PASS` and fails, forcing the docs that describe it to be updated.
28
+
29
+ Current state on Studio 21.0.4.5: **7 passed, 1 known-broken, 0 needing
30
+ attention** — media placement, blade, trim, cross-track move, and generator
31
+ visibility all verified end to end.
32
+
33
+ ### Fixed — the `RESOLVE_VERIFY=1` gate was unrunnable
34
+
35
+ It ran a single test that threw `TODO — implement once fixtures land`, so the
36
+ flag reported a failing suite and everyone learned to ignore it. A gate nobody
37
+ can run green is worse than no gate. It is now a documented skip pointing at the
38
+ harness above, and `RESOLVE_VERIFY=1 npm test` is clean.
39
+
40
+ ### Changed — release process states the rule
41
+
42
+ "The file round-trips" and "Resolve honours it" are different claims; only a live
43
+ import establishes the second. Changes to the offline `.drp` tier must run the
44
+ harness, and a doc may not say "verified live" unless a runnable command produced
45
+ that result.
46
+
47
+ The harness is also robust to `DeleteProject`'s session lock: projects opened in
48
+ a session cannot be deleted until Resolve restarts, so it steps off the current
49
+ project, retries, falls back to run-unique names rather than colliding, and
50
+ reports what it could not remove instead of failing silently.
51
+
52
+ ## What's New in v2.95.1
53
+
54
+ Withdraws a recommendation this project shipped two releases ago. **`drp
55
+ place_fusion_title` produces a title Resolve never renders**, and v2.93.2/v2.93.3
56
+ told callers to use it.
57
+
58
+ ### Fixed — the offline title is inert
59
+
60
+ Measured on Studio 21.0.4.5. The placed clip is structurally perfect: right
61
+ track, right frame, right duration, `PrettyType` = `Fusion Title`, and the text
62
+ genuinely is written into the `CompositionBA` — it decodes back. But Resolve
63
+ never instantiates the comp:
64
+
65
+ | | offline-placed | live-inserted |
66
+ |---|---|---|
67
+ | `GetFusionCompCount()` | 1 | 1 |
68
+ | `GetToolList()` | **`[]`** | `['Template','MediaOut1']` |
69
+ | Inspector Title tab | **blank** | shows `Text+` |
70
+ | Viewer | **black** | renders the text |
71
+
72
+ Reproduced four ways — alone, inside an edit chain, onto a bundled-template base
73
+ project, and onto a genuine 21.0.4.5 Resolve export. Ruled out: template
74
+ staleness (the bundled clip element is structurally identical to a real one, same
75
+ tags, size differing only by text length) and DbId rewriting (the comp blobs hold
76
+ no DbId references). Root cause open.
77
+
78
+ It passes every structural readback while being invisible on screen — the exact
79
+ silent-lie shape the `api_truth` ledger exists to catch. The **nested-timeline
80
+ route is unaffected** and remains the working answer.
81
+
82
+ ### Narrowed — it is the Fusion-comp path, not clone-a-template
83
+
84
+ `place_generator`, built the same way, works: an imported Solid Color shows a
85
+ populated `Generator - Solid Color` Inspector with a live Color parameter. A
86
+ generator's parameters are a protobuf `EffectFiltersBA`, which Resolve reads; a
87
+ title's are a `CompositionBA` Fusion-comp blob, which it does not instantiate.
88
+
89
+ ### Changed — "mapped" no longer implies "Resolve honours it"
90
+
91
+ The drp-format README's honest-scope section conflated file-level fidelity with
92
+ live behaviour. A blob that survives a write/read cycle has been *encoded*
93
+ correctly; only a live import proves it is *instantiated*. The section now says
94
+ so, and the Fusion-title row carries the warning.
95
+
5
96
  ## What's New in v2.95.0
6
97
 
7
98
  Compound clips can now be walked into and edited offline — the one capability
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  English | [简体中文](README.zh-CN.md)
4
4
 
5
- [![Version](https://img.shields.io/badge/version-2.95.0-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
5
+ [![Version](https://img.shields.io/badge/version-2.95.2-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
6
6
  [![npm](https://img.shields.io/npm/v/davinci-resolve-mcp.svg?label=npm&color=CB3837)](https://www.npmjs.com/package/davinci-resolve-mcp)
7
7
  [![API Coverage](https://img.shields.io/badge/API%20Coverage-100%25-brightgreen.svg)](docs/reference/api-coverage.md)
8
8
  [![Tools](https://img.shields.io/badge/MCP%20Tools-34%20(353%20full)-blue.svg)](#server-modes)
package/README.zh-CN.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [English](README.md) | 简体中文
4
4
 
5
- [![Version](https://img.shields.io/badge/version-2.95.0-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
5
+ [![Version](https://img.shields.io/badge/version-2.95.2-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
6
6
  [![npm](https://img.shields.io/npm/v/davinci-resolve-mcp.svg?label=npm&color=CB3837)](https://www.npmjs.com/package/davinci-resolve-mcp)
7
7
  [![API Coverage](https://img.shields.io/badge/API%20Coverage-100%25-brightgreen.svg)](docs/reference/api-coverage.md)
8
8
  [![Tools](https://img.shields.io/badge/MCP%20Tools-34%20(353%20full)-blue.svg)](#服务器模式)
@@ -12,7 +12,7 @@
12
12
  [![Python](https://img.shields.io/badge/python-3.10+-green.svg)](https://www.python.org/downloads/)
13
13
  [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT)
14
14
 
15
- > 本翻译对应 v2.95.0 版 README。如与英文原版有出入,以 [英文原版](README.md) 为准。
15
+ > 本翻译对应 v2.95.2 版 README。如与英文原版有出入,以 [英文原版](README.md) 为准。
16
16
 
17
17
  一个 Model Context Protocol (MCP) 服务器,让 AI 助手通过官方脚本 API 控制 DaVinci Resolve Studio(达芬奇)。它提供完整的 API 覆盖,外加带护栏的工作流助手,涵盖剪辑、媒体池整理、渲染设置、审阅标记、调色、Fusion、Fairlight、项目生命周期任务、扩展开发,以及不碰源媒体的媒体分析。
18
18
 
@@ -113,6 +113,26 @@ Resolve validation before release. Use disposable projects and synthetic media
113
113
  only. Never modify, transcode, proxy, or create derivatives of source media
114
114
  unless the user explicitly requests it.
115
115
 
116
+ **Changes to the offline `.drp` authoring tier must run the round-trip harness,
117
+ and a structural readback is NOT sufficient evidence on its own:**
118
+
119
+ ```bash
120
+ RESOLVE_VERIFY=1 venv/bin/python tests/live_drp_roundtrip_verification.py
121
+ ```
122
+
123
+ It authors a `.drp` per primitive, imports it into Resolve, asserts intent
124
+ against structural readback, **and exports the composited frame to assert the
125
+ item is visible on screen.** That last assertion exists because
126
+ `place_fusion_title` passes every structural check — right track, frame,
127
+ duration, type, and correctly-encoded text — while rendering nothing. Anything
128
+ that only diffs `GetStart()`/`GetDuration()` will call that green. Known-broken
129
+ cases are declared in the harness rather than skipped, so a fix reports
130
+ `UNEXPECTED PASS` and fails until the docs describing it are updated.
131
+
132
+ The same rule generalises: **"the file round-trips" and "Resolve honours it" are
133
+ different claims.** Only a live import establishes the second one. Do not write
134
+ "verified live" in a doc unless a runnable command produced that result.
135
+
116
136
  Examples:
117
137
 
118
138
  ```bash
@@ -67,7 +67,7 @@ equivalent, blocking full automation.
67
67
 
68
68
  - **Object:** `Timeline`
69
69
  - **Behavior:** There is no API to read or set the Source/Auto Track Selector (the Edit-page patch panel that picks the destination track). InsertTitleIntoTimeline, InsertFusionTitleIntoTimeline, InsertGeneratorIntoTimeline, InsertFusionGeneratorIntoTimeline, InsertOFXGeneratorIntoTimeline and InsertFusionCompositionIntoTimeline take no trackIndex and always drop the clip on the selector's current target (V1 in practice). Locking lower video tracks does NOT redirect the insert — verified live on 21.0.0: locking V1 makes the insert FAIL rather than land on V2. Titles/generators also can't be moved afterward (no MediaPoolItem, so AppendToTimeline clipInfo and MoveClips don't apply). RE-MEASURED on Studio 21.0.4.5 (2026-08-12, version read live), one fresh 3-video-track timeline per arm so no insert could fail on a collision: nothing locked -> lands V1; V1 locked via SetTrackLock -> returns None; V1 locked by CLICKING THE PADLOCK IN THE UI -> returns None, IDENTICAL to the API lock. That kills the recurring theory that the GUI lock advances the selector where the API lock does not — it does not, and a revision of resolve-advanced/vendor/drp-format/README.md that claimed otherwise as 'verified live' has been withdrawn as false. WHAT DOES WORK, and it is the whole mechanism: dragging the SOURCE PATCH badge onto V2 in the Edit-page patch panel sends the next insert to V2 (measured, same rig, nothing locked). The destination is the patch panel, never the lock state. In the track header the per-track badge column is the auto-track-selector toggle, while the source patch badge appears only on the patched track and dragging THAT is what re-targets. So THE SELECTOR itself is reachable only by GUI automation, and exposing read/write on the patch panel is a smaller, better-defined API request than adding trackIndex to all six Insert*IntoTimeline methods. Do not read that as 'a title cannot be placed on a chosen track from a script' — it can, without touching the selector at all, by nesting it (see the recommendation). The gap is in these six methods and in selector access, not in the outcome.
70
- - **Workaround / current handling:** USE THE NESTED-TIMELINE ROUTE — the insert method is a dead end but the goal is not, and this is entirely public API. Measured end to end on Studio 21.0.4.5 (2026-08-12): put the title on its OWN timeline (CreateEmptyTimeline + InsertFusionTitleIntoTimeline; it lands on V1 there and that does not matter), then place THAT timeline's media pool item — Timeline.GetMediaPoolItem(), clip property Type='Timeline' — with AppendToTimeline's clipInfo trackIndex/recordFrame. It lands on the requested track at the requested frame, exactly. The text stays settable afterwards: placedItem.GetMediaPoolItem().GetTimeline() (Resolve 21.0.4+) opens the inner timeline, and the Text+ item there still reports GetFusionCompCount()==1, so GetFusionCompByIndex(1).FindTool('Template').SetInput('StyledText', ...) works and persists across processes. Duration is controllable the same way: duration = endFrame - startFrame, endFrame EXCLUSIVE (verified at 1, 119 and 120 frames). This composes — a PNG plus two titles each placed by trackIndex into one container timeline, and the container placed as a single clip, all elements individually reachable and editable through the nesting. TWO CONSTRAINTS: (a) every placed instance shares ONE media pool item, so a text edit propagates to all of them — use one source timeline per distinct card; (b) placements must not overlap on a track, or the append is silently rejected (see the overlapping-record entry). COMPOUND CLIPS ARE THE TRAP HERE: Timeline.CreateCompoundClip also gives a source-less title a MediaPoolItem and also places correctly, but it SEVERS the text — FusionCompCount drops 1->0 and GetTimeline() returns None for Type='Compound' while working correctly for Type='Timeline', so there is no route back to the Text+. Use a nested timeline, never a compound, when the text must stay editable. For clips that already have a MediaPoolItem, AppendToTimeline clipInfo 'trackIndex' was always the answer (exposed as media_pool append_to_timeline clip_infos). Do NOT reach for track locking in either form — it blocks the insert rather than re-targeting it. GUI automation of the patch panel works but is a last resort and is unverifiable from the API side; if used, read the landing track back with TimelineItem.GetTrackTypeAndIndex() and treat a wrong track as a failure. Offline, the advanced server's drp place_fusion_title places a title by track and frame directly. See issue #74.
70
+ - **Workaround / current handling:** USE THE NESTED-TIMELINE ROUTE — the insert method is a dead end but the goal is not, and this is entirely public API. Measured end to end on Studio 21.0.4.5 (2026-08-12): put the title on its OWN timeline (CreateEmptyTimeline + InsertFusionTitleIntoTimeline; it lands on V1 there and that does not matter), then place THAT timeline's media pool item — Timeline.GetMediaPoolItem(), clip property Type='Timeline' — with AppendToTimeline's clipInfo trackIndex/recordFrame. It lands on the requested track at the requested frame, exactly. The text stays settable afterwards: placedItem.GetMediaPoolItem().GetTimeline() (Resolve 21.0.4+) opens the inner timeline, and the Text+ item there still reports GetFusionCompCount()==1, so GetFusionCompByIndex(1).FindTool('Template').SetInput('StyledText', ...) works and persists across processes. Duration is controllable the same way: duration = endFrame - startFrame, endFrame EXCLUSIVE (verified at 1, 119 and 120 frames). This composes — a PNG plus two titles each placed by trackIndex into one container timeline, and the container placed as a single clip, all elements individually reachable and editable through the nesting. TWO CONSTRAINTS: (a) every placed instance shares ONE media pool item, so a text edit propagates to all of them — use one source timeline per distinct card; (b) placements must not overlap on a track, or the append is silently rejected (see the overlapping-record entry). COMPOUND CLIPS ARE THE TRAP HERE: Timeline.CreateCompoundClip also gives a source-less title a MediaPoolItem and also places correctly, but it SEVERS the text — FusionCompCount drops 1->0 and GetTimeline() returns None for Type='Compound' while working correctly for Type='Timeline', so there is no route back to the Text+. Use a nested timeline, never a compound, when the text must stay editable. For clips that already have a MediaPoolItem, AppendToTimeline clipInfo 'trackIndex' was always the answer (exposed as media_pool append_to_timeline clip_infos). Do NOT reach for track locking in either form — it blocks the insert rather than re-targeting it. GUI automation of the patch panel works but is a last resort and is unverifiable from the API side; if used, read the landing track back with TimelineItem.GetTrackTypeAndIndex() and treat a wrong track as a failure. DO NOT REACH FOR drp place_fusion_title HERE — an earlier revision of this entry recommended it and that recommendation is WITHDRAWN. Measured on Studio 21.0.4.5: it places a Fusion Title that is structurally perfect — right track, right frame, right duration, PrettyType 'Fusion Title', and the text really is written into the CompositionBA, which decodes back correctly — but Resolve never instantiates the comp. The imported item reports GetFusionCompCount()==1 while GetFusionCompByIndex(1).GetToolList() is EMPTY (a live-inserted Text+ returns ['Template','MediaOut1']), the Edit-page Inspector's Title tab is blank, and the viewer renders nothing. Reproduced four ways: placed alone, placed in an edit chain, onto a bundled-template base project, and onto a genuine 21.0.4.5 Resolve export. It therefore passes every structural readback while being invisible on screen — the exact silent-lie shape this ledger exists to catch. The nested-timeline route above is the one that actually works. See issue #74.
71
71
  - **Reference:** [issue #74](https://github.com/samuelgursky/davinci-resolve-mcp/issues/74)
72
72
  - **Tags:** missing-method, timeline, title, generator, track
73
73
 
package/install.py CHANGED
@@ -36,7 +36,7 @@ from src.utils.update_check import (
36
36
 
37
37
  # ─── Version ──────────────────────────────────────────────────────────────────
38
38
 
39
- VERSION = "2.95.0"
39
+ VERSION = "2.95.2"
40
40
  # Only hard floor: mcp[cli] requires Python 3.10+. There is no upper bound —
41
41
  # Resolve's scripting bridge loads into newer interpreters on recent builds
42
42
  # (Python 3.14 verified against Resolve Studio 20.3.2). Older Resolve builds
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "davinci-resolve-mcp",
3
- "version": "2.95.0",
3
+ "version": "2.95.2",
4
4
  "description": "NPM bootstrapper for the DaVinci Resolve MCP Server.",
5
5
  "license": "MIT",
6
6
  "author": "Samuel Gursky <samgursky@gmail.com>",
@@ -1,8 +1,24 @@
1
1
  # drp-format — offline DaVinci Resolve project authoring & editing
2
2
 
3
3
  Read, author, and edit **real, importable** DaVinci Resolve 21 projects (`.drp`) and timelines
4
- (`.drt`) as files — no Resolve required. Everything here is verified by a live round-trip
5
- (author/edit → `import_project` → `lint`/`clip_where` → re-export decode), not just self-parse.
4
+ (`.drt`) as files — no Resolve required.
5
+
6
+ > **How to verify a claim in this file.** This README previously said "everything here is verified
7
+ > by a live round-trip". It was not: the gate it referred to threw `TODO — implement once fixtures
8
+ > land`, so nobody ever ran it green, and three unbacked "verified live" claims survived here —
9
+ > one of which reached a shipped `api_truth` recommendation before being caught. The round-trip
10
+ > that actually exists is:
11
+ >
12
+ > ```bash
13
+ > RESOLVE_VERIFY=1 venv/bin/python tests/live_drp_roundtrip_verification.py
14
+ > ```
15
+ >
16
+ > It authors a `.drp` per primitive, imports it into a running Resolve, asserts intent against
17
+ > structural readback, **and exports the composited frame to assert the item is visible on
18
+ > screen.** That last check is the point: `place_fusion_title` satisfies every structural
19
+ > assertion — correct track, frame, duration, type, and text encoded in the blob — while
20
+ > rendering nothing. Structure alone cannot tell you a thing works. Known-broken cases are
21
+ > declared in the harness, so a fix reports `UNEXPECTED PASS` and forces the docs to be updated.
6
22
 
7
23
  Background + the full schema map: `docs/design/drp-drx-drt-closeout-harness/knowledge/resolve21-schema-reconciliation.md`
8
24
  and `.../resolve-authoring-completion.md`.
@@ -37,7 +53,7 @@ and `.../resolve-authoring-completion.md`.
37
53
  **Place elements (track-targeted — the #74 bypass, offline)**
38
54
  | Action | What it does |
39
55
  |---|---|
40
- | `place_fusion_title` | Text+ on a chosen track. Options: `text, font, style, size, vJustify, hJustify, color:{r,g,b}`, `trackIndex`, `startFrame`, `durationFrames` |
56
+ | `place_fusion_title` | ⚠️ **INERT on 21.0.4.5 — placed but not rendered, see below.** Text+ on a chosen track. Options: `text, font, style, size, vJustify, hJustify, color:{r,g,b}`, `trackIndex`, `startFrame`, `durationFrames` |
41
57
  | `place_generator` | built-in generator (`generatorName`, e.g. "Solid Color") on a chosen track |
42
58
  | `place_transition` | cross-dissolve at an abutting cut (`track`, `atFrame`, `durationFrames`) — clips need handle media |
43
59
 
@@ -117,16 +133,40 @@ Selector's current target (V1 in practice), which no API can read or set.
117
133
  > locking. Treat it as such: it is not verifiable from the API side, since nothing in the
118
134
  > scripting surface can confirm the selector landed where you dragged it.
119
135
 
120
- For export-based workflows, `place_fusion_title` places a title on any track at an exact frame
121
- offline, with no selector involved.
136
+ > ⚠️ **`place_fusion_title` currently produces an INERT title do not use it for this.**
137
+ > Measured on Studio 21.0.4.5: the placed clip is structurally perfect (right track, frame and
138
+ > duration, `PrettyType` = `Fusion Title`, and the text really is written into the
139
+ > `CompositionBA` — it decodes back), but **Resolve never instantiates the comp**. The imported
140
+ > item reports `GetFusionCompCount() == 1` while `GetFusionCompByIndex(1).GetToolList()` is
141
+ > **empty** (a live-inserted Text+ returns `['Template','MediaOut1']`), the Inspector's Title tab
142
+ > is blank, and the viewer renders nothing. Reproduced four ways: alone, in an edit chain, onto a
143
+ > bundled-template base, and onto a genuine 21.0.4.5 export. Ruled out so far — template staleness
144
+ > (the bundled clip element is structurally identical to a real one: same tags, size differing
145
+ > only by text length) and DbId rewriting (the comp blobs contain no DbId references). Root cause
146
+ > open. Use the live nested-timeline route until this is fixed.
147
+ >
148
+ > **The defect is specific to the Fusion-comp path, not to clone-a-template.** `place_generator`,
149
+ > built the same way, works: an imported Solid Color shows a populated `Generator - Solid Color`
150
+ > Inspector with a live Color parameter and renders on the timeline. The difference is where the
151
+ > parameters live — a generator's are a protobuf `EffectFiltersBA`, which Resolve reads happily,
152
+ > while a title's are a `CompositionBA` Fusion-comp blob, which it does not instantiate. That is
153
+ > the narrowest place to start looking.
122
154
 
123
155
  ---
124
156
 
125
157
  ## What's mapped (honest scope)
126
158
 
159
+ > **Read "mapped" as file-level fidelity, not as "Resolve honours it."** Those are different
160
+ > claims and this README conflated them. `place_fusion_title` round-trips byte-for-byte and its
161
+ > text decodes back correctly, yet Resolve builds no comp from it (see the ⚠️ above). A blob that
162
+ > survives a write/read cycle has been *encoded* correctly; only a live import proves it is
163
+ > *instantiated*. Everything below is the first claim. The second is only established where an
164
+ > entry says it was measured against a running Resolve build.
165
+
127
166
  **Fully mapped (read + write, round-trips byte-for-byte):** project packaging; tracks
128
167
  (`Sm2TiTrack`+Type+Sequence); clips; source in-points (`<In>`); Fusion titles
129
- (text/font/style/size/justify/color); generators; transitions; markers; grades (full DRX body);
168
+ (text/font/style/size/justify/color **encoded correctly but not instantiated by Resolve 21.0.4.5;
169
+ see the ⚠️ above**); generators; transitions; markers; grades (full DRX body);
130
170
  media path + resolution + frame-count + fps. **Plus the full Media-Pool metadata layer:** the
131
171
  keyed-dict format (`Geometry`/`Time`/`VideoMetadata`/`Proxy`/audio/small `FieldsBlob`) with typed
132
172
  values (`keyed-dict.js`); audio config (`TracksBA`/`VirtualAudioTrackBA` → sample-rate/channels/codec
@@ -18,7 +18,6 @@ const os = require('node:os');
18
18
  const JSZip = require('jszip');
19
19
 
20
20
  const drpFormat = require('..');
21
- const { resolveVerifyTest } = require('./_resolve-verify');
22
21
 
23
22
  // A plausible-shaped synthetic body. The injector does not parse the bytes,
24
23
  // only substitutes them. Hex characters only.
@@ -229,10 +228,19 @@ test('injectGrades: internals — extractDrxBodyHex throws on missing Body', ()
229
228
  // the byte-identical-render check requires Resolve. When the env flag is
230
229
  // unset the test skips with a clear marker.
231
230
 
232
- resolveVerifyTest('injectGrades: rendered frame matches direct DRX apply', async () => {
233
- // The harness's resolve-verifications.md has the full recipe. The actual
234
- // implementation will live here once a captured fixture + a Resolve session
235
- // is in flight. Until then it's a placeholder that exists to demonstrate
236
- // the resolveVerifyTest gate and to surface in the skip list as a TODO.
237
- throw new Error('TODO implement once fixtures/inject-target.drp + Resolve session land');
238
- });
231
+ // NOT a throwing placeholder any more. It used to `throw new Error('TODO ...')`,
232
+ // which meant RESOLVE_VERIFY=1 reported a FAILING suite and everyone learned to
233
+ // ignore the flag which is a large part of why this package carried three
234
+ // unbacked "verified live" claims. A gate nobody can run green is worse than no
235
+ // gate. The live round-trip that DOES exist now lives on the Python side, where
236
+ // the Resolve scripting API is reachable directly:
237
+ //
238
+ // RESOLVE_VERIFY=1 venv/bin/python tests/live_drp_roundtrip_verification.py
239
+ //
240
+ // It authors a .drp per primitive, imports it into Resolve, asserts intent
241
+ // against structural readback AND exports the composited frame to assert the
242
+ // item is actually VISIBLE — the check that catches place_fusion_title, which
243
+ // passes every structural assertion while rendering nothing.
244
+ test.skip('injectGrades: rendered frame matches direct DRX apply '
245
+ + '(covered by tests/live_drp_roundtrip_verification.py — grade-render compare still TODO)',
246
+ () => {});
@@ -87,7 +87,7 @@ if not logging.getLogger().handlers:
87
87
  handlers=[logging.StreamHandler()],
88
88
  )
89
89
 
90
- VERSION = "2.95.0"
90
+ VERSION = "2.95.2"
91
91
  logger = logging.getLogger("davinci-resolve-mcp")
92
92
  logger.info(f"Starting DaVinci Resolve MCP Server v{VERSION}")
93
93
  logger.info(f"Detected platform: {get_platform()}")
package/src/server.py CHANGED
@@ -11,7 +11,7 @@ Usage:
11
11
  python src/server.py --full # Start the 353-tool granular server instead
12
12
  """
13
13
 
14
- VERSION = "2.95.0"
14
+ VERSION = "2.95.2"
15
15
 
16
16
  import base64
17
17
  import os
@@ -643,9 +643,23 @@ API_TRUTH: List[Dict[str, Any]] = [
643
643
  "works but is a last resort and is unverifiable from the API "
644
644
  "side; if used, read the landing track back with "
645
645
  "TimelineItem.GetTrackTypeAndIndex() and treat a wrong track as "
646
- "a failure. Offline, the advanced server's drp "
647
- "place_fusion_title places a title by track and frame directly. "
648
- "See issue #74.",
646
+ "a failure. DO NOT REACH FOR drp place_fusion_title HERE — an "
647
+ "earlier revision of this entry recommended it and that "
648
+ "recommendation is WITHDRAWN. Measured on Studio 21.0.4.5: it "
649
+ "places a Fusion Title that is structurally perfect — right "
650
+ "track, right frame, right duration, PrettyType 'Fusion Title', "
651
+ "and the text really is written into the CompositionBA, which "
652
+ "decodes back correctly — but Resolve never instantiates the "
653
+ "comp. The imported item reports GetFusionCompCount()==1 while "
654
+ "GetFusionCompByIndex(1).GetToolList() is EMPTY (a live-inserted "
655
+ "Text+ returns ['Template','MediaOut1']), the Edit-page "
656
+ "Inspector's Title tab is blank, and the viewer renders nothing. "
657
+ "Reproduced four ways: placed alone, placed in an edit chain, "
658
+ "onto a bundled-template base project, and onto a genuine "
659
+ "21.0.4.5 Resolve export. It therefore passes every structural "
660
+ "readback while being invisible on screen — the exact silent-lie "
661
+ "shape this ledger exists to catch. The nested-timeline route "
662
+ "above is the one that actually works. See issue #74.",
649
663
  "tags": ["missing-method", "timeline", "title", "generator", "track"],
650
664
  "submit": "missing",
651
665
  "issue": 74,