Case study / mark
A second Close returned before shutdown finished.
A small concurrency bug in a Go tool, found with Go support that is not released yet.
Go is in test and not released. arnx 0.1.11 reviews Python, JavaScript, TypeScript and Rust. This review ran on a development build with Go support. You cannot reproduce it with the version on npm today.
mark syncs Markdown files to Confluence. A review of its recent commits
produced a test showing that a second, concurrent
Close returned while the resource was still stopping, against
what the function's own comment promises. The fix was merged upstream on
2026-11-11 as
kovetskiy/mark#1244.
1. Repository and reviewed commit
kovetskiy/mark, the last ten
commits on master on 2026-11-10, range
3113164..a18f773: 29 files, 1,694 lines added and 399 removed.
arnx had no earlier contact with the repository.
2. Expected behavior
internal/lazy starts a resource on first use and stops it on
Close. mark uses it for the headless browser and the mermaid
engine. The comment on Close says whatever was made
is stopped before Close returns
. That should hold for every caller.
3. Actual behavior
The first Close set a closed flag, released the lock and ran the
stop function, which can take a while. A second Close arriving
meanwhile saw the flag and returned at once. Its caller could carry on while
the resource was still shutting down.
4. Suspected defect
Before any test ran, the second hunter (gpt-6.1-sol) suspected:
Concurrent Close calls can return before resource shutdown has
completed.
5. Generated test
The reviewer wrote TestSecondCloseWaitsForStop: start a resource
whose stop function blocks, call Close in one goroutine, call it
again in another, and check that the second call has not returned while the
first is still stopping.
6. Execution result
### F1 · confirmed by the second opinion
- Test: `arnx_generated/lazy_arnx/lazy_arnx_test.go::TestSecondCloseWaitsForStop`
- Failure: `lazy_arnx_test.go:33: second Close returned while the resource was still stopping`
- Reviewer: The first Close sets isClosed, then stops the resource outside the lock.
A second concurrent Close sees isClosed and returns at once. Close's documented
guarantee that shutdown is finished on return does not hold for the second caller.
- Before the change (unknown): could not run on the code before the change
The review wrote 12 tests; 11 passed in every run and this one failed in every run. It took 1 minute 54 seconds and cost $0.58. Reviewer and second opinion: claude-sonnet-5-5. These numbers come from our local review record, not from a public page.
7. Correction
The fix adds a channel that is closed when the first
Close has finished stopping. A later Close waits on
it. Closing twice in a row still returns at once. The change is 14 lines of
code in internal/lazy/lazy.go and one test. The fix was drafted
with AI assistance; the owner of arnx reviewed it and opened the pull request,
with an AI disclosure.
8. Verification result
From the pull request: TestSecondCloseWaitsForStop fails on
master and passes with the fix. The race detector ran the package
30 times clean, and go test ./... passes. A maintainer merged it
about an hour after it was opened, with the comment Thanks!
9. Upstream references
- Pull request #1244: fix(lazy): let a concurrent Close wait for the first one to finish (merged 2026-11-11).
-
internal/lazy/lazy.go, the file with
Close.
10. Limitations and observations
- Unreleased build. Go support is in test. It covers less than the released languages and may change before it ships.
-
A narrow bug. It only happens when
Closeis called from two places at once. We did not show that mark's own command-line use does that; a program using the package as a library could. - No before/after evidence. The test could not run on the code before the change: the sandbox has no network and a module was missing there. So this was not shown to be a regression, only a failure on the current code.
- Both the reviewer and the second opinion were the same model in this run. A second opinion from the same model is weaker than one from another vendor.
- One review of one repository. It says nothing about how often arnx is right; the research note has counts for the released languages.
Last reviewed 2026-11-12 against arnx 0.1.11. The release documentation is canonical for your installed version.