Verify which script a Pine save actually wrote to - #514
azuzubairezeasor-cmd wants to merge 2 commits into
Conversation
TradingView keeps the Pine Editor bound to one saved script. pine_open and
pine_new only call monaco.setValue(), which replaces the editor text but does
not move that binding, so a subsequent save lands on whichever script was
bound before — silently, and reported as success. openScript even returned the
script_id it fetched from, which reads as confirmation of a binding that never
happened.
The binding is not readable from the page, so this snapshots the saved-script
list around every save and reports which script actually changed. A mismatch
against the script the caller opened now throws, naming the script that was
overwritten and its new version so it can be restored from version history.
That converts a silent overwrite into a loud, recoverable error. Pass
force:true to write to whatever the editor is bound to.
Also fixes name resolution in openScript. The match loop tested scriptName and
scriptTitle with equal precedence in list order, so a script whose *title*
collided with the query could win over the one actually *named* that —
pine_open({name: "My script"}) opened a script named "Azu's Gold Sweeps v2"
because its title was "My script". Precedence is now exact name, exact title,
then substring on each, and the result reports which field matched.
newScript keeps its behaviour but stops implying it created anything: the
action is now editor_buffer_reset and it carries a warning, and it marks the
pending save so verifySaveTarget catches the overwrite rather than allowing it.
Resolution moved from the injected page script into Node so it is unit
testable; adds tests covering the title-collision regression and the diff.
Fixes tradesdontlie#513
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The Pine Editor's title button reports the script the editor is actually bound to, independent of the text buffer. That is the binding pine_open cannot move, and reading it turns the previous post-hoc check into a pre-flight guard: save() and smartCompile() now throw before writing anything if the editor is bound to a different script than the caller opened, naming the script that would have been clobbered. The saved-script diff stays as a second net for when the control cannot be read. openScript now reads the binding back after injecting and reports editor_bound_to and save_target_moved, instead of returning a script_id that implies a rebind that never happened. When the editor already happened to be bound to the requested script there is nothing to guard against, so no warning is emitted. pine_new + save is now refused outright rather than silently overwriting whatever was bound. force:true still writes to the bound script. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Pushed What changedWhile digging into whether the binding could be moved programmatically, I found it can at least be read: the Pine Editor's title button ( That's the missing piece.
On the real rebindI tried to make
Filtering the list works. Activating a row does not. I tried a synthetic So the rebind stays manual. Anyone who knows how that dialog commits a selection could finish it — the selectors above are the hard part, and they're all stable 🤖 Generated with Claude Code |
|
Correction to my previous comment: row activation in the Open-script dialog does work. I said it didn't. It does — it's just asynchronous with a long delay, and I checked too soon. What I saw: That changes the outlook for a proper rebind. The flow is:
Step 6 is also the completion signal, so the whole thing is verifiable rather than fire-and-forget. I have not implemented this — the guard in this PR still assumes the binding cannot be moved, which remains correct and safe behaviour. But it means a real fix is reachable and the earlier pessimism in this thread was mine, not the platform's. 🤖 Generated with Claude Code |
Fixes #513.
The problem
TradingView keeps the Pine Editor bound to exactly one saved script.
pine_openandpine_newonly callmonaco.setValue()— that replaces the editor text but does not move the binding. A save issued afterwards therefore writes to whichever script was bound before, silently, and is reported as success.openScriptmade this harder to notice by returningscript_id— the id it had just fetched from — which reads like confirmation that the editor is now bound to that script.I hit this in real use: opened script A, injected source, saved, and watched script B get overwritten. Nothing in the tool results indicated anything was wrong.
What this does
Verifies the save target. The binding isn't readable from the page, so
save()andsmartCompile()now snapshot the saved-script list before and after and report which script actually changed. If it isn't the one the caller opened, it throws — naming the overwritten script and its new version, so it can be restored from version history. A silent overwrite becomes a loud, recoverable error.Both paths needed it:
smartCompileclicks the editor's own Save button, so it writes to the same bound scriptpine_savedoes.force: trueskips the check for callers that deliberately want to write to whatever is bound.Fixes name resolution. The match loop tested
scriptNameandscriptTitlewith equal precedence in list order:A script whose title collided with the query could win over the one actually named it. Concretely,
pine_open({name: "My script"})returned a script namedAzu's Gold Sweeps v2, because its title happened to be"My script"and it sorted earlier. Precedence is now exact name → exact title → substring on each, and the result reportsmatched_on.Combined with the save bug, that pair is how a user overwrites a script they never named.
Stops
pine_newimplying it created something. It doesn't — it blanks the current script's buffer. The action is noweditor_buffer_resetwith a warning, and it marks the pending save so the verification catches the overwrite rather than letting it through.Notes
resolveScriptMatchanddiffScriptVersionsare pure and exported.node --test tests/pine_analyze.test.js tests/pine_save_target.test.js→ 26/26 pass.npm testonmainrunstests/e2e.test.jsagainst a live TradingView with no opt-in guard. Running the suite drove my real chart.fix/drawing-di-resolveappears to address this already.🤖 Generated with Claude Code