Browse Source
Motivated by a make.tcl project deploy failure ("could not delete target
binary") where the locker turned out to be 7-Zip inspecting the exe -
identifying it took several dead ends because Windows has no cheap
file-to-process reverse lookup (name matching and Process.Modules scans
both miss plain file handles; openfiles needs a pre-enabled global flag;
handle.exe is external and elevation-bound).
G-028: a punkboot::utils helper returns the locking processes via the
Windows Restart Manager API (the installers-grade answer to "who is
blocking this file"), with twapi or cffi required lazily at call time -
punkboot::utils stays pure Tcl per the bootsupport no-compiled-extensions
contract, and unavailability degrades to the existing message. make.tcl
kit deploy failures append the locker report when determinable. Recurring
customer: G-023 versioned/dev binaries rewritten every build.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
2 changed files with 73 additions and 0 deletions
@ -0,0 +1,67 @@ |
|||||||
|
# G-028 Name the process locking a file when builds cannot replace a target |
||||||
|
|
||||||
|
Status: proposed |
||||||
|
Scope: src/modules/punkboot/utils-999999.0a1.0.tm (locker-report helper), src/make.tcl (kit deploy failure reporting) |
||||||
|
Acceptance: as in root GOALS.md index (canonical). |
||||||
|
|
||||||
|
## Context |
||||||
|
|
||||||
|
Motivating incident (2026-07-07, tomlish project): `make.tcl project` failed |
||||||
|
with "could not delete target binary at .../bin/tomlish.exe" and no further |
||||||
|
information. The lock turned out to be 7-Zip holding the exe open for archive |
||||||
|
inspection - but identifying that took several dead ends, because Windows has |
||||||
|
no cheap reverse lookup from file to holding process: |
||||||
|
|
||||||
|
- tasklist / Get-Process by name only finds processes *running as* the file. |
||||||
|
- Scanning Process.Modules only sees files mapped as images (loaded EXE/DLLs), |
||||||
|
not plain open handles, and silently skips inaccessible processes. |
||||||
|
- Full handle enumeration (NtQuerySystemInformation) needs elevation and |
||||||
|
per-handle queries; `openfiles` requires a globally pre-enabled tracking |
||||||
|
flag; Sysinternals handle.exe / Resource Monitor are external/manual. |
||||||
|
|
||||||
|
The **Restart Manager API** (RmStartSession/RmRegisterResources/RmGetList) is |
||||||
|
the practical programmatic answer: it is what installers use for the "these |
||||||
|
applications are using this file" dialog, does not require elevation for the |
||||||
|
common cases, and directly answers "who is blocking my replace/delete". It is |
||||||
|
callable from Tcl via twapi or cffi, both already shipped in the punk Windows |
||||||
|
kit. |
||||||
|
|
||||||
|
Deploy-target locks will recur - G-023's versioned/dev binary outputs are |
||||||
|
rewritten every build, and a running or inspected punk8/9-dev.exe is exactly |
||||||
|
the kind of file this fails on. |
||||||
|
|
||||||
|
## Approach |
||||||
|
|
||||||
|
- `punkboot::utils` helper (e.g. file_lockers <path>) returning a list of |
||||||
|
{pid <n> name <procname> ...} records, empty when unlocked. |
||||||
|
- **Constraint: bootsupport carries no compiled extensions** (src/bootsupport |
||||||
|
AGENTS.md). punkboot::utils stays pure Tcl; twapi (RmStartSession et al via |
||||||
|
its API coverage) or cffi (direct Rstrtmgr.dll binding) is package-required |
||||||
|
lazily inside the helper, from the environment's auto_path. Unavailable |
||||||
|
binding, non-Windows platform, or API failure all yield a clean |
||||||
|
'unavailable' result the caller renders as the existing message. |
||||||
|
- make.tcl kit deploy (and any other replace-target failure sites worth |
||||||
|
covering) appends the locker report to its failure output when available. |
||||||
|
- Prefer twapi if its Restart Manager coverage suffices; cffi binding as the |
||||||
|
fallback implementation choice - record the choice here when made. |
||||||
|
|
||||||
|
## Alternatives considered |
||||||
|
|
||||||
|
- Full handle-table enumeration (NtQuerySystemInformation) - rejected: |
||||||
|
elevation-dependent, expensive, and reimplements what Restart Manager |
||||||
|
already provides for the blocking-file case. |
||||||
|
- Shelling out to Sysinternals handle.exe - rejected: external download, |
||||||
|
license prompt on first run, elevation required; not dependable build |
||||||
|
tooling. |
||||||
|
- `openfiles` - rejected: requires a pre-enabled global tracking flag with a |
||||||
|
standing performance cost. |
||||||
|
|
||||||
|
## Notes |
||||||
|
|
||||||
|
- Related: G-023 (versioned binary deploys are the recurring customer), |
||||||
|
G-020 (same optionally-available twapi/cffi pattern for platform features), |
||||||
|
punkboot::utils chicken-and-egg ordering rules in src/bootsupport/AGENTS.md |
||||||
|
apply when adding the helper. |
||||||
|
- Unix analogues (lsof/fuser) could slot behind the same helper interface |
||||||
|
later; out of scope for acceptance (Windows is where the pain is and where |
||||||
|
the API answer is non-obvious). |
||||||
Loading…
Reference in new issue