HarperFast / HarperFast/harper
Document lock()'s exclusion-only limitations in the public API documentation
- Dominant language
- JavaScript
- Stars
- 89
- Forks
- 10
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 200
Description
The Phase 1 `lock()` guarantee was settled as **exclusion-only**
([harper#483 comment](https://github.com/HarperFast/harper/issues/483#issuecomment-5603461590),
`docs/record-lock-ownership.md` §10). That decision was taken *on the condition* that the two things
`lock()` does not promise are documented rather than implied — §10 says so in terms:
> Nothing above may be softened into "rare" or "best-effort" in the API docs, and neither limitation
> may be described as crash-only or as unreachable on a clean handoff.
The design note is the normative source and `DESIGN.md` routes writers to it, but the §14 work
decomposition assigns issues to every *implementation* slice and none to the documentation change.
This issue is that owner.
## What the public `lock()` documentation must say
Lifted from §10, which is normative and must not be paraphrased into something weaker:
1. **`lock()` guarantees exclusive admission** of a critical section for a key, and successor
freshness after a clean handoff — the latter only while the key's home still holds that handoff's
dependency set.
2. **A predecessor's write can outrank its successor's.** `lock()` changes nothing about how two
conflicting writes resolve, so whatever the pair would do to the record without a lock is what
they do with one, silently and with no error raised. Reachable on a *completely clean* handoff
when a caller stamps a future `context.timestamp`.
3. **Successor freshness is not promised on the recovery path** — the predecessor crashed, is
unreachable, or its native commit settled after the recovery barrier was measured. Both the
predecessor's transaction and the successor's derived write end up wrong, which is exactly the
read-then-conditionally-write case `lock()` exists for.
4. **There is no caller-side mitigation.** The obvious candidate (`X-Replicate-To` / `confirm=`) is
super-user-gated and entangled with residency, and closes none of limitation 3's routes anyway.
5. **Neither limitation is crash-only, and both are reachable on a clean handoff.** Three separate
pre-push review rounds on #2498 produced text that got this wrong in three different directions,
so it is worth stating plainly rather than in a caveat.
## Why this is worth its own issue
The honesty argument is load-bearing for the *decision*: exclusion-only was chosen over fenced and
quorum-confirmed (#2540) partly because the limitations would be written down. If Phase 1 ships
behind its gate and the public `lock()` page still describes exclusion without (2) and (3), an app
author reaches for `lock()` for read-modify-write and hits the failure the design note predicts, with
no warning anywhere they read — and the tradeoff that justified the decision was never actually paid.
Blocked on nothing, but it should land with or before the Phase 1 enablement, not after.
Refs #483, #2540
🤖 Filed by Claude Opus 5 on behalf of Kris.
Contributor guide
Research direction
Read docs/record-lock-ownership.md §10 and DESIGN.md first, then locate the public lock() API documentation they govern. Update that page to state the five listed exclusion-only guarantees and limitations without weakening them, including clean-handoff reachability and the lack of caller-side mitigation. Done means the public documentation matches the normative wording and covers both failure paths.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript
- Domain
- documentation
- Issue type
- Documentation
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 76/100