Impact: the native host frees the capture lock when its request timer fires, but nothing stops the capture the extension already started.
- A second capture starts on top of the first. A capture that outlives its timeout (default 5 min,
--timeout up to 900 s) gets a timeout reply, and the CLI exits 1. The host has already deleted the pending entry, so it forwards the next request while the first capture still runs. That request is usually the caller's retry, so the same post gets published twice.
- The late link is lost. When the first capture publishes, its reply finds no
pending entry, and replyToCli returns at line 95. The branch that logs viewUrl for a CLI that walked away (lines 98-108) never runs. The capsule exists on the Worker, but neither stdout nor the host log names it. Only the share list on x.com still has it.
#21 fixed the same failure for a CLI socket that closes mid-capture, and the comment above pending (lines 87-90) describes that fix. The request timer still deletes the entry. The header (lines 10-11) also promises one capture at a time.
The extension has no deadline of its own. captureShare clamps timeoutMs to 900 s (line 226) but uses it only for waitForContentScript (line 242, capped at 90 s). chrome.tabs.sendMessage (line 243) waits as long as the capture runs. The CLI comment at line 32, "The extension clamps the capture to 900s", is wrong.
Where:
native-host/sourcecapsule-host.mjs: request timer at 154-157; replyToCli returns at 95 and deletes at 97; comments at 10-11 and 87-90.
extension-src/background.js: captureShare, lines 226, 242 and 243.
scripts/sourcecapsule-capture.mjs:32.
Evidence: the real host and CLI on a private pipe (SOURCECAPSULE_PIPE). A stub browser holds capture #1 open, answers it after #2 is forwarded, then answers #2.
CLI #1 exit 1 after 1070ms, stdout: { "ok": false, "error": "timeout", "message": "No reply within 1000ms." }
forwarded to the extension while #1 still runs: [
'capture-share https://x.com/a/status/1',
'capture-share https://x.com/a/status/2'
]
CLI #2 exit 0, stdout: { "ok": true, "viewUrl": "https://share.example/c/second", "warnings": [] }
late #1 viewUrl recorded anywhere in the host log: false
Host log of the same run:
2026-10-06T17:42:22.919Z forwarding 1791308542917-dfp4al capture-share
2026-10-06T17:42:23.988Z forwarding 1791308543986-zvrquy capture-share
The missing extension deadline is traced from the code, not run in a browser.
Environment: main 72777ef (v1.6.8). Node on Windows, private named pipe, no browser.
Next action:
- On timeout, answer the CLI but keep the
pending entry, marked cliGone, until the extension replies. Add a hard cap so a dead worker cannot hold the lock forever.
- Log a reply for an unknown id with its
viewUrl instead of dropping it.
- Race
chrome.tabs.sendMessage in captureShare against timeoutMs. The existing finally then closes the capture window, which stops the capture.
- Fix the comments at host lines 10-11 and CLI line 32.
- Add a case to
test/native-host.test.mjs: a reply after the timer must not let a second capture start, and its link must reach the log.
Impact: the native host frees the capture lock when its request timer fires, but nothing stops the capture the extension already started.
--timeoutup to 900 s) gets atimeoutreply, and the CLI exits 1. The host has already deleted thependingentry, so it forwards the next request while the first capture still runs. That request is usually the caller's retry, so the same post gets published twice.pendingentry, andreplyToClireturns at line 95. The branch that logsviewUrlfor a CLI that walked away (lines 98-108) never runs. The capsule exists on the Worker, but neither stdout nor the host log names it. Only the share list on x.com still has it.#21 fixed the same failure for a CLI socket that closes mid-capture, and the comment above
pending(lines 87-90) describes that fix. The request timer still deletes the entry. The header (lines 10-11) also promises one capture at a time.The extension has no deadline of its own.
captureShareclampstimeoutMsto 900 s (line 226) but uses it only forwaitForContentScript(line 242, capped at 90 s).chrome.tabs.sendMessage(line 243) waits as long as the capture runs. The CLI comment at line 32, "The extension clamps the capture to 900s", is wrong.Where:
native-host/sourcecapsule-host.mjs: request timer at 154-157;replyToClireturns at 95 and deletes at 97; comments at 10-11 and 87-90.extension-src/background.js:captureShare, lines 226, 242 and 243.scripts/sourcecapsule-capture.mjs:32.Evidence: the real host and CLI on a private pipe (
SOURCECAPSULE_PIPE). A stub browser holds capture #1 open, answers it after #2 is forwarded, then answers #2.Host log of the same run:
The missing extension deadline is traced from the code, not run in a browser.
Environment: main 72777ef (v1.6.8). Node on Windows, private named pipe, no browser.
Next action:
pendingentry, markedcliGone, until the extension replies. Add a hard cap so a dead worker cannot hold the lock forever.viewUrlinstead of dropping it.chrome.tabs.sendMessageincaptureShareagainsttimeoutMs. The existingfinallythen closes the capture window, which stops the capture.test/native-host.test.mjs: a reply after the timer must not let a second capture start, and its link must reach the log.