The question, and why it is the one the queue turns on
Ordinary small files cost roughly 229 ms per chunk on the measured production host, unchanged by anything done in 2026-08. Times ~48,500 files that is about three hours — after the large-file catastrophe was solved, this is the entire run. Halving it is worth more than everything measured so far, and nothing has attributed it.
This is task 6 of ~/Projects/kntnt-transfer-engine-open-work.md, and it is still open. The 2026-08-18 production measurement bounded the cost and ruled out per-file path resolution and stat calls as the dominant term for large files; it did not attribute the small-file cost, and it produced no limit-pair reading at all.
An earlier estimate attributed roughly 95 ms of the per-chunk cost to the record-split commit. That estimate did not survive measurement: moving time came in at 257 ms per chunk against roughly 210 ms from the prior release on the same host, so save() was a far smaller share than assumed and the unattributed remainder is very nearly the whole cost.
The hypothesis worth testing first
Filesystem latency. A chunk makes fifteen to twenty-five filesystem calls: realpath() twice, three stats, the file's own open-read-close, and the container's suspend()/resume() pair — two opens, an ftruncate and a close on a file growing past a gigabyte, per 44 KB packaged. At a few milliseconds each that is the right order of magnitude on a managed host whose uploads directory is networked or overlay-mounted. It is a hypothesis and it cannot be settled from a laptop, where the same path costs about 0.25 ms.
Attributing it needs timing inside a tick, which this build does not have. So this is instrumentation work first, then a run on a real host — not a pure measurement task.
Read the free signal first: if a stall shows the host limit raise was granted and the chunk still died, the kill came from the web server or the container rather than from PHP, which on a LiteSpeed host points at LSAPI's process limits and makes the filesystem hypothesis much more likely.
If the per-chunk half holds up, the amortisation to try first is sealing several segments under one open container — no container-format change and no api_version bump, since each file keeps its own segment and its own name on the wire. Coarsening segments into multi-file packages stays decided against; reopen only if all three of these hold: this question is measured, the amortisation is done, and the numbers still demand it.
Rule R1 — what this blocks
Nothing in the adaptation family is built until this is answered. That family is host-limit raising, budget halving, the attempt counter, the floor, and any recovery on top of them. It already has three budgets, a counter, a floor and a halving rule, all chosen blind. If the answer is that the kill comes from the web server or the container rather than from PHP, a smaller chunk is the wrong lever entirely and the right move is to remove knobs, not add one — several queued items are expected to be deleted by the answer rather than done.
Any issue proposing adaptation work should say it is blocked by this one.
Why ready-for-human
It needs a real managed host, and the only host with a measured baseline is a live client site that must never be touched unasked.
Blocked by
An operator decision about which host to measure on, and about landing the in-tick instrumentation first.
The question, and why it is the one the queue turns on
Ordinary small files cost roughly 229 ms per chunk on the measured production host, unchanged by anything done in 2026-08. Times ~48,500 files that is about three hours — after the large-file catastrophe was solved, this is the entire run. Halving it is worth more than everything measured so far, and nothing has attributed it.
This is task 6 of
~/Projects/kntnt-transfer-engine-open-work.md, and it is still open. The 2026-08-18 production measurement bounded the cost and ruled out per-file path resolution and stat calls as the dominant term for large files; it did not attribute the small-file cost, and it produced no limit-pair reading at all.An earlier estimate attributed roughly 95 ms of the per-chunk cost to the record-split commit. That estimate did not survive measurement: moving time came in at 257 ms per chunk against roughly 210 ms from the prior release on the same host, so
save()was a far smaller share than assumed and the unattributed remainder is very nearly the whole cost.The hypothesis worth testing first
Filesystem latency. A chunk makes fifteen to twenty-five filesystem calls:
realpath()twice, threestats, the file's own open-read-close, and the container'ssuspend()/resume()pair — two opens, anftruncateand a close on a file growing past a gigabyte, per 44 KB packaged. At a few milliseconds each that is the right order of magnitude on a managed host whose uploads directory is networked or overlay-mounted. It is a hypothesis and it cannot be settled from a laptop, where the same path costs about 0.25 ms.Attributing it needs timing inside a tick, which this build does not have. So this is instrumentation work first, then a run on a real host — not a pure measurement task.
Read the free signal first: if a stall shows the host limit raise was granted and the chunk still died, the kill came from the web server or the container rather than from PHP, which on a LiteSpeed host points at LSAPI's process limits and makes the filesystem hypothesis much more likely.
If the per-chunk half holds up, the amortisation to try first is sealing several segments under one open container — no container-format change and no
api_versionbump, since each file keeps its own segment and its own name on the wire. Coarsening segments into multi-file packages stays decided against; reopen only if all three of these hold: this question is measured, the amortisation is done, and the numbers still demand it.Rule R1 — what this blocks
Nothing in the adaptation family is built until this is answered. That family is host-limit raising, budget halving, the attempt counter, the floor, and any recovery on top of them. It already has three budgets, a counter, a floor and a halving rule, all chosen blind. If the answer is that the kill comes from the web server or the container rather than from PHP, a smaller chunk is the wrong lever entirely and the right move is to remove knobs, not add one — several queued items are expected to be deleted by the answer rather than done.
Any issue proposing adaptation work should say it is blocked by this one.
Why
ready-for-humanIt needs a real managed host, and the only host with a measured baseline is a live client site that must never be touched unasked.
Blocked by
An operator decision about which host to measure on, and about landing the in-tick instrumentation first.