Delphi-RAG-Lint

drag-lint help

ide-release

Since: 1.13.0-alpha

Asks a running drag-lint Delphi IDE plugin not to respawn its drag-lint.exe child processes for a while -- and to drop the running one on its next request.

How to run it

Full flag list: ide-release in the CLI reference.

Flags

FlagArgumentMeaningDefault
--secondsN
--resume
--status
--json

Details

Asks a running drag-lint Delphi IDE plugin not to respawn its drag-lint.exe child processes for a while -- and to drop the running one on its next request -- so the engine binary can be rebuilt while the IDE stays open. The verb frees nothing by itself; see below.

The problem it solves

A running Windows process holds an execute lock on its own image. The plugin spawns drag-lint.exe lsp --stdio as a long-lived child, so an IDE that is merely open blocks the deploy step of an engine build:

  249716 lines, 12.41 seconds, 11077076 bytes code, 991240 bytes data.
ERROR: failed to stage C:\Projects\Delphi-RAG-lint\third_party\dll-win64\drag-lint.exe

The compile succeeded. Only the copy failed, one line later, naming the file and not the holder.

Killing the server alone does not work -- it is respawned within about a second and the build loses the race again. So this verb asks for a window, not an event: the plugin stays stopped until the hold expires.

Running it from the CLI

drag-lint ide-release [--seconds N] [--json]     # hold (default 120s)
drag-lint ide-release --resume                   # clear the hold now
drag-lint ide-release --status [--json]          # report, change nothing

Needs no index and no --db.

Reaching it in the IDE

No IDE surface. It is a message to the IDE, not an action in it. While a hold is active the plugin shows drag-lint: engine released for a rebuild (Ns left) on its status strip, so a temporarily quiet IDE is never a mystery.

Writing the hold does not itself free the file

The verb writes a sentinel and returns immediately. The plugin observes it lazily -- on the next call that wants the language server -- because a timer here would be a second uninvited source of process spawns, and this plugin has shipped one of those before.

That is also why stopping the running child is the build's job: build\stage-engine.ps1 writes the hold and kills the holder and retries. The hold is what makes the kill stick.

So a caller that needs the lock actually gone must wait and retry. You normally do not call this by hand at all -- build_draglint_win64.bat and build\pack-lint-release.ps1 (release packing, since 2026-10-16) stage through stage-engine.ps1 and get the recovery automatically.

Stopping the engine remotely (by hand)

When something else must replace or delete the deployed exe -- a manual deploy, a release step, a script that copies over third_party\dll-win64\drag-lint.exe -- stop the engine remotely instead of killing processes by name:

drag-lint ide-release --seconds 900      # the IDE plugin stops respawning for 15 min
drag-lint shutdown --all --wait 30       # running lsp engines close every store and exit 0

shutdown asks running lsp engines (over their per-user maintenance pipe) to stand down: close every store, exit 0. Its flags (--db, --wait, --dry-run, --all, --force) are on its drag-lint --help line. ide-release --resume lifts the hold early. Measured 2026-10-16: without this, a release pack lost the copy race twice to short-lived engines started by other sessions.

Flags

FlagMeaning
--seconds Nhold length; default 120, clamped to 1..3600
--resumeclear the hold immediately
--statusreport whether a hold is active; changes nothing
--jsonemit the stable ide-release/1 document

Exit codes: 0 on success (including --resume when there was no hold to clear -- that is the desired end state); 1 when the sentinel could not be written or removed.

It expires on its own

The sentinel carries a deadline, not a duration. A build that crashes half-way cannot leave the IDE permanently without hovers: the hold lapses and the next hover starts a fresh server. --resume is only for getting it back sooner.

A missing, empty, unparseable or expired sentinel means not held. That direction is deliberate -- failing open costs a blocked build, which is visible and retryable, while failing closed would leave the IDE silently mute with no error anywhere.

Where the sentinel lives

%LOCALAPPDATA%\drag-lint\engine-hold, containing a single UTC epoch second.

Not %TEMP%, and that is not arbitrary: TEMP is per-process. A shell may have TEMP=C:\TEMP while the IDE resolves it to %LOCALAPPDATA%\Temp, so a sentinel written to one is invisible to the other and the hold silently never arrives. A rendezvous between two processes cannot be built on a variable either of them may have inherited differently.

What it does NOT cover

The long-lived language server is gated, and since session 78 so is the plugin's job queue: reindex, lint-all, auto-document, forms-CSV and refresh-findings jobs are DEFERRED while a hold lasts, then run when it lifts. They are not dropped, and a job already running when the hold appears is not interrupted -- the hold's contract is "will not start anything". That matters beyond tidiness: a queued bare index job makes stage-engine.ps1 refuse to stage rather than kill, and killing a bat-wrapped job leaves cmd.exe running the next line of the bat.

Hover and lint also spawn short-lived drag-lint.exe processes, and one of those can still hold the file for a moment; the staging recovery retries with a backoff, which absorbs it.

The VS Code client is unaffected -- since extension v1.4 it runs its own private copy of the engine and never touches the deployed binary. See EDITORS.


All features | CLI reference | IDE menus