Login
Check-in [4228a3de0b]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:In a checkout, OCX's wait for those outside OCX holding what a write writes waits on the checkout's own database alone, then on its repository alone, so waiting for a writer holding the checkout's own database keeps no read or write from the repository

Intent: the adversarial review of 39c646f7ad (scratchpad rv211-e/x39) found, confirmed on Fossil 2.27: in a checkout, OCX's wait, fossil sql there with no -R and BEGIN EXCLUSIVE, opened the repository as its main database, then .fslckout, then Fossil's settings, and took their locks in that order, keeping each while it waited for the next, so with only .fslckout held by a writer outside OCX it took the repository's exclusive lock at once and kept it, at each of its waits: a read of the repository and /timeline took 14.36 s during it (0.01 s beside a wait by -R); while OCX's add met .fslckout held 70 s, 15 of 50 pages took over 15 s, the longest 29.99 s, with no one holding the repository; and a person's commit-like command in the checkout, holding .fslckout and reading the repository, lost its write of the repository, "database is locked (5)". HISTORY.md decision 114 and the comments of LET_GO and Place#hosted, which said the wait waited on both, were wrong for this case. With two waits, in the review's trials and again here: each of those 50 pages answered within 0.01 s; the person's write was kept and OCX's add went through after 2.77 s (2.76 s here); and a commit in a checkout whose repository a reader held 20 s was stored after 19.69 s (19.7 s here).

Expression: src/ocx/fossil.cr: Place#hosted makes a command's wait, in a checkout, two: first fossil sql there with --no-repository and an empty main database (:memory:), which opens the checkout's own database with nothing but Fossil's settings after it, then, only once that has gone through, fossil sql -R on the repository alone, which opens no checkout database; the wait goes through only as both do, so the counting of tries and runs again is unchanged; a command naming its repository waits by -R alone, as before; a new private Place#let_go_of? makes one such wait; naming .fslckout itself as the main database opens it twice and the wait waits on itself, as tried; the comments of LET_GO, TRIES, hosted and let_go_of? say so, and that in a checkout a wait takes up to twice BUSY. spec/support/stand_in_fossil.cr: Holder::Person, a person's own fossil command in a checkout, as fossil commit is, writing the checkout's own database and reading its repository in one transaction, and, as it lets go, writing the repository's setting KEPT and committing (Holder#lets_go), where every other holder rolls back; Holder#waited_on, the shell test by which the stand-in knows OCX's wait is on the holder's database, a wait on the repository naming it by -R first and one on a checkout's own database naming no repository; the holder's sql shell keeps Fossil's settings in the stand-in's own directory, its HOME, since a writer's BEGIN IMMEDIATE takes the lock of every database its shell opens, the settings database among them, which OCX's first wait in a checkout opens too (spec/ocx/post_spec.cr's clone held at its write's first run failed so); holding_until's waited returns only once OCX's wait is asleep in SQLite's busy wait, as /proc/PID/wchan says, so it has taken each lock it takes before the holder's, where before it returned as the wait started and a read could come before the old wait locked the repository. spec/ocx/fossil_spec.cr: the new example. Docs: HISTORY.md decision 115, amending decision 114 and naming the review, decision 113's bound being in a checkout three waits of up to 30 s each; docs/fossil.md, "Forum posts", what was tried: the order fossil sql in a checkout opens and locks its databases, the stalled reads and the lost write, --no-repository :memory:, and .fslckout as the main database waiting on itself; docs/interfaces.md, fossil sql's row. The protocol's definitions are unchanged: section 5 and failure case 35 speak of a writer holding the repository, for which a wait in a checkout is still up to 15 s, its first wait going through at once where no one holds the checkout's own database.

Review: Gemma (gemma4:31b) read the change four times (scratchpad gemma-split-1.txt to -4.txt, the whole diff). Held: none. Misread, each a clarity finding, the code changed and asked again: first and second rounds, each opening that the two waits' let_go was flawed, overwritten by the second wait, or the repository's wait skipped when the first did not go through, each taken back by Gemma itself: hosted's comment now says the second wait is made only once the first has gone through and that the wait goes through only as both do, and the two assignments became one expression, let_go = (!in_checkout || let_go_of?(["--no-repository", ":memory:"])) && let_go_of?(["-R", file]). Wrong: third round, about counting each run as a try or a run again at the loop's start, code unchanged here, which it read right in the end; fourth round, that a wait on the checkout's own database that does not go through leaves the repository never waited on: the run after it meets that holder first, and the repository's wait follows once the first goes through, as Gemma itself then said.

Demonstrates: spec/ocx/fossil_spec.cr (in a checkout, a writer holding its own database alone, then a person's command holding it and reading the repository, while an add there waits: a read of the repository through the fossil program answers within a second, the add goes through once each lets go, and the person's write of the repository as it let go is kept; against 39c646f7ad's src/ocx/fossil.cr each read failed, nothing after a second, and the person's write was lost; and, as before, a writer holding the checkout's own database while add and commit run there, and past each wait), spec/ocx/writers_spec.cr (a read within a second while a report waits for a writer, and a reader holding the repository while a report write runs, OCX's waits known once asleep), spec/ocx/post_spec.cr (a post in a clone held at its write's first run, written whole and pushed once, the holder's settings apart from OCX's), spec/ocx/create_spec.cr (a run held past each wait of its write stores nothing), spec/support/stand_in_fossil.cr (Holder::Person, Holder#waited_on, the holder's own settings, holding_until answering once OCX's wait is asleep), src/ocx/fossil.cr, spec/guards/size_spec.cr (each file at most 400 lines), spec/guards/causation_spec.cr (no timer added), spec/guards/interfaces_spec.cr and spec/guards/documents_spec.cr (the edited documents rendering with their links), spec/coverage/run.cr (every statement still covered, Place#let_go_of? among them). crystal run ci.cr green: 496 examples, 0 failures.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QVpmCsBwVF3d4zhMHHDAxs

Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | agent-tools
Files: files | file ages | folders
SHA3-256: 4228a3de0b864ab09a49e35fda6a61f8c674b4664fafc47a5f9c1234eae8f291
User & Date: opus-ocx2 2026-10-23 05:50:26.085
Context
2026-10-23
06:38
OCX's wait for those outside OCX holding what a write writes detaches Fossil's settings database before it waits, so another of the account's fossil commands holding the settings keeps no read from the repository or a checkout's own database

Intent: the adversarial review of 4228a3de0b (scratchpad rv4228) found, confirmed on Fossil 2.27: fossil sql, by -R or by --no-repository :memory: in a checkout, opens Fossil's settings database (configdb, $HOME/.fossil) after the database waited on, and BEGIN EXCLUSIVE locks them in that order, keeping the first while it waits for the next, so whenever anyone held the settings, another of the account's fossil sql sessions or fossil commit itself, which writes them inside its transaction, the wait by -R kept the repository from every reader, and the wait in a checkout kept .fslckout, for up to 15 s: with a reader holding the settings 10 s, the wait by -R took 9.75 s, and a read of the repository, the page /timeline and fossil status each 9.15 s; as a report write met a writer outside OCX holding the repository 3 s, another session in OCX's home holding the settings 20 s, pages waited up to 11.95 s (0.01 s with the settings detached); and, a regression of 4228a3de0b, a person's fossil changes in a checkout took up to 13.45 s during an add's wait there (0.01 s on 39c646f7ad and with the settings detached). HISTORY.md decision 115 said only that a writer holding the settings holds a wait too. The specs had hidden it: the stand-in's holder kept its settings in a home of its own, so it never held OCX's. With the settings detached, in the review's trials: the person's write was kept and the add went through after 2.76 s; a commit in a checkout whose repository a reader held 20 s was stored after 19.7 s; and while an add met .fslckout held 70 s, each of 50 pages answered within 0.01 s, the add failing past three waits after 45.1 s. The cost: no wait of OCX's waits for a holder of the settings alone, which meets only a command writing them, such as fossil commit, whose own wait in Fossil, up to 15 s, waits for it (5.05 s for a writer holding them 5 s, tried here; fossil add and fossil bundle import went through at once), so a run failing so counts as a run again, not a try: one held on past each run fails after about nine runs of 15 s, 135 s, where before about 105 s, four runs and three waits; nothing fails sooner.

Expression: src/ocx/fossil.cr: LET_GO is [".timeout #{BUSY}", "DETACH configdb;", "BEGIN EXCLUSIVE; ROLLBACK;"], so each wait, by -R or in a checkout by --no-repository :memory:, waits on its one database alone; Fossil 2.27 opens the settings even under a home holding none yet, so the DETACH goes through there too (tried, exit 0, nothing on standard error); the comments of LET_GO, Place#hosted and Place#let_go_of? say so, and hosted's that a holder of the settings alone is waited for by each run's own wait in Fossil, never by OCX's, a run it fails counting as a run again. spec/support/stand_in_fossil.cr: the holder's sql shell no longer runs with HOME set to the stand-in's own directory; it shares OCX's home, as the account's own commands do, so a writer's BEGIN IMMEDIATE holds Fossil's settings too; Holder::Settings, a writer on Fossil's settings alone (a deferred BEGIN and an INSERT into configdb.global_config, rolled back as it lets go), which no write of a repository meets, so the stand-in fails the command's run as a writer outside OCX holding the repository at that run alone would (Holder#fails), and stands in for OCX's wait by -R (Holder#waited_on); holding_until's comment says it also answers once a wait not waiting on the holder's database has ended. spec/ocx/writers_spec.cr: the new example. Docs: HISTORY.md decision 116, amending decision 115 and naming the review; docs/fossil.md, "Forum posts": a new point, every fossil sql opening Fossil's settings last, what the review and this check-in tried, and the checkout point naming its waits with the settings detached; docs/interfaces.md, fossil sql's row. The protocol's definitions are unchanged.

Review: Gemma (gemma4:31b) read the change twice, the whole diff with its intent (scratchpad gemma-detach-1.txt and gemma-detach-2.txt): "I found no defects." and "I find nothing." Held: none. Misread: none. Wrong: none.

Demonstrates: spec/ocx/writers_spec.cr (a read of the repository through the fossil program answers within a second while a report's wait meets a writer holding Fossil's settings alone in OCX's home, and the report is stored once it lets go; against 4228a3de0b's src/ocx/fossil.cr the read did not answer within a second in each of seven runs, the report stored after; and, as before, a read within a second while a report waits for a writer, and a reader holding the repository while a report write runs), spec/ocx/post_spec.cr (a post in a clone that a writer outside OCX, now sharing OCX's home, holds at its write's first run, written whole and pushed once; against 4228a3de0b's src/ocx/fossil.cr the post was stopped), spec/ocx/fossil_spec.cr (in a checkout, a writer holding its own database, a person's command, and holders past each wait, each holder sharing OCX's home), spec/ocx/create_spec.cr (a run held past each wait of its write stores nothing), spec/support/stand_in_fossil.cr (Holder::Settings; the holder sharing OCX's home), src/ocx/fossil.cr, spec/guards/size_spec.cr (each file at most 400 lines), spec/guards/interfaces_spec.cr and spec/guards/documents_spec.cr (the edited documents rendering with their links), spec/coverage/run.cr (every statement still covered). crystal run ci.cr green: 497 examples, 0 failures.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QVpmCsBwVF3d4zhMHHDAxs check-in: aa650c80c4 user: opus-ocx2 tags: agent-tools

05:50
In a checkout, OCX's wait for those outside OCX holding what a write writes waits on the checkout's own database alone, then on its repository alone, so waiting for a writer holding the checkout's own database keeps no read or write from the repository

Intent: the adversarial review of 39c646f7ad (scratchpad rv211-e/x39) found, confirmed on Fossil 2.27: in a checkout, OCX's wait, fossil sql there with no -R and BEGIN EXCLUSIVE, opened the repository as its main database, then .fslckout, then Fossil's settings, and took their locks in that order, keeping each while it waited for the next, so with only .fslckout held by a writer outside OCX it took the repository's exclusive lock at once and kept it, at each of its waits: a read of the repository and /timeline took 14.36 s during it (0.01 s beside a wait by -R); while OCX's add met .fslckout held 70 s, 15 of 50 pages took over 15 s, the longest 29.99 s, with no one holding the repository; and a person's commit-like command in the checkout, holding .fslckout and reading the repository, lost its write of the repository, "database is locked (5)". HISTORY.md decision 114 and the comments of LET_GO and Place#hosted, which said the wait waited on both, were wrong for this case. With two waits, in the review's trials and again here: each of those 50 pages answered within 0.01 s; the person's write was kept and OCX's add went through after 2.77 s (2.76 s here); and a commit in a checkout whose repository a reader held 20 s was stored after 19.69 s (19.7 s here).

Expression: src/ocx/fossil.cr: Place#hosted makes a command's wait, in a checkout, two: first fossil sql there with --no-repository and an empty main database (:memory:), which opens the checkout's own database with nothing but Fossil's settings after it, then, only once that has gone through, fossil sql -R on the repository alone, which opens no checkout database; the wait goes through only as both do, so the counting of tries and runs again is unchanged; a command naming its repository waits by -R alone, as before; a new private Place#let_go_of? makes one such wait; naming .fslckout itself as the main database opens it twice and the wait waits on itself, as tried; the comments of LET_GO, TRIES, hosted and let_go_of? say so, and that in a checkout a wait takes up to twice BUSY. spec/support/stand_in_fossil.cr: Holder::Person, a person's own fossil command in a checkout, as fossil commit is, writing the checkout's own database and reading its repository in one transaction, and, as it lets go, writing the repository's setting KEPT and committing (Holder#lets_go), where every other holder rolls back; Holder#waited_on, the shell test by which the stand-in knows OCX's wait is on the holder's database, a wait on the repository naming it by -R first and one on a checkout's own database naming no repository; the holder's sql shell keeps Fossil's settings in the stand-in's own directory, its HOME, since a writer's BEGIN IMMEDIATE takes the lock of every database its shell opens, the settings database among them, which OCX's first wait in a checkout opens too (spec/ocx/post_spec.cr's clone held at its write's first run failed so); holding_until's waited returns only once OCX's wait is asleep in SQLite's busy wait, as /proc/PID/wchan says, so it has taken each lock it takes before the holder's, where before it returned as the wait started and a read could come before the old wait locked the repository. spec/ocx/fossil_spec.cr: the new example. Docs: HISTORY.md decision 115, amending decision 114 and naming the review, decision 113's bound being in a checkout three waits of up to 30 s each; docs/fossil.md, "Forum posts", what was tried: the order fossil sql in a checkout opens and locks its databases, the stalled reads and the lost write, --no-repository :memory:, and .fslckout as the main database waiting on itself; docs/interfaces.md, fossil sql's row. The protocol's definitions are unchanged: section 5 and failure case 35 speak of a writer holding the repository, for which a wait in a checkout is still up to 15 s, its first wait going through at once where no one holds the checkout's own database.

Review: Gemma (gemma4:31b) read the change four times (scratchpad gemma-split-1.txt to -4.txt, the whole diff). Held: none. Misread, each a clarity finding, the code changed and asked again: first and second rounds, each opening that the two waits' let_go was flawed, overwritten by the second wait, or the repository's wait skipped when the first did not go through, each taken back by Gemma itself: hosted's comment now says the second wait is made only once the first has gone through and that the wait goes through only as both do, and the two assignments became one expression, let_go = (!in_checkout || let_go_of?(["--no-repository", ":memory:"])) && let_go_of?(["-R", file]). Wrong: third round, about counting each run as a try or a run again at the loop's start, code unchanged here, which it read right in the end; fourth round, that a wait on the checkout's own database that does not go through leaves the repository never waited on: the run after it meets that holder first, and the repository's wait follows once the first goes through, as Gemma itself then said.

Demonstrates: spec/ocx/fossil_spec.cr (in a checkout, a writer holding its own database alone, then a person's command holding it and reading the repository, while an add there waits: a read of the repository through the fossil program answers within a second, the add goes through once each lets go, and the person's write of the repository as it let go is kept; against 39c646f7ad's src/ocx/fossil.cr each read failed, nothing after a second, and the person's write was lost; and, as before, a writer holding the checkout's own database while add and commit run there, and past each wait), spec/ocx/writers_spec.cr (a read within a second while a report waits for a writer, and a reader holding the repository while a report write runs, OCX's waits known once asleep), spec/ocx/post_spec.cr (a post in a clone held at its write's first run, written whole and pushed once, the holder's settings apart from OCX's), spec/ocx/create_spec.cr (a run held past each wait of its write stores nothing), spec/support/stand_in_fossil.cr (Holder::Person, Holder#waited_on, the holder's own settings, holding_until answering once OCX's wait is asleep), src/ocx/fossil.cr, spec/guards/size_spec.cr (each file at most 400 lines), spec/guards/causation_spec.cr (no timer added), spec/guards/interfaces_spec.cr and spec/guards/documents_spec.cr (the edited documents rendering with their links), spec/coverage/run.cr (every statement still covered, Place#let_go_of? among them). crystal run ci.cr green: 496 examples, 0 failures.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QVpmCsBwVF3d4zhMHHDAxs check-in: 4228a3de0b user: opus-ocx2 tags: agent-tools

04:35
OCX's wait for those outside OCX holding what a write writes waits for readers as well as writers, BEGIN EXCLUSIVE in place of BEGIN IMMEDIATE, and, for a command in a checkout, on the checkout's own database as well as its repository

Intent: the adversarial review of 211a7d966b (scratchpad rv211, rv211-fix, rv211-e) found two things, each confirmed on Fossil 2.27: (1) a reader outside OCX, an open read transaction on the repository held on past 15 s, with no writer anywhere, lost a report write after 9 runs and 135 s: each run's COMMIT waited Fossil's 15 s for the reader and failed, "database is locked: {COMMIT}", but no reader holds back BEGIN IMMEDIATE, so each of OCX's waits went through at once and counted as one that ended as a writer let go, until the eight runs again were used up; (2) a writer outside OCX holding a checkout's own database, .fslckout, as a person's own fossil command there does, lost an add OCX made there in 0.19 s with no wait, since the wait named the repository alone, by -R, which no one held. With BEGIN EXCLUSIVE, in the review's trials, a reader holding 20 s let the write store after 19.69 s and one holding 150 s failed it after 105.19 s and four runs, the bound HISTORY.md decision 113 states, and an add in a checkout whose database a writer held 5 s waited 4.77 s and went through.

Expression: src/ocx/fossil.cr: LET_GO is ".timeout 15000" "BEGIN EXCLUSIVE; ROLLBACK;", which waits for readers and writers both and, past its .timeout, says what BEGIN IMMEDIATE said, so such a wait counts as a try; Place#run tells Place#hosted whether a command is in a checkout (in_checkout: neither `on` nor `repository` names its repository, so its repository file came from the checkout `dir` is in), and hosted makes such a command's wait with no -R, in the checkout, where fossil sql opens the checkout's own database with its repository and waits on both; Locked's message says writers or a reader outside OCX held the repository, and, for a command in a checkout, or the checkout's own database, which of the two neither Fossil nor the wait says; the comments of Locked, LOCKED, LET_GO, TRIES, RERUNS, turn, run and hosted say so, and that while the wait waits for a reader new reads wait behind it, up to BUSY, as they did behind a COMMIT waiting for that reader. spec/support/stand_in_fossil.cr: Holder, who holds a database outside OCX: Writer, an open BEGIN IMMEDIATE on the repository, as before; Reader, an open read transaction on it, a command meeting it failed by the stand-in at once, in Fossil's words, in place of its COMMIT's wait of 15 s; Checkout, an open write on a checkout's own database alone; holding_past and holding_until take `by`; OCX's wait known as its last argument is LET_GO's last, as one in a checkout has no -R; holding_past's wait the real one with .timeout 0 in place of LET_GO's .timeout, whatever names the database. spec/ocx/create_spec.cr, spec/ocx/post_spec.cr, spec/ocx/remote_node_spec.cr and spec/ocx/fossil_spec.cr follow Locked's new words. Docs: HISTORY.md decision 114, amending decision 113 and naming the review; docs/fossil.md, "Forum posts", what was tried: a reader fails a write at its COMMIT after 15 s, BEGIN IMMEDIATE goes through at once past it, BEGIN EXCLUSIVE waits for it, returning as it let go at 1 s and saying "Error: stepping, database is locked (5)" after 2 s under .timeout 2000, and a read starting while it waits answers once it goes through; and a checkout's own database is locked apart from its repository: add and commit there failed at once while a wait by -R went through, and fossil sql in the checkout with no -R waits on both; docs/interfaces.md, fossil sql's row, and docs/backlog.md, its first row, name the new wait. The protocol's definitions are unchanged: failure case 35 and section 5 speak of writers, which stays true.

Review: Gemma (gemma4:31b) read the change five times (scratchpad gemma-exclusive-1.txt to -5.txt, the whole diff). Held: first round, that Locked's message named a checkout's own database for a command with no repository file at all: it now does so only for a command in a checkout. Misread, each a clarity finding, the code changed and asked again: third round, that a directory holding a repository file but no checkout would read as a checkout: the -R arguments hosted was given, from which it inferred a checkout, became an explicit in_checkout, run's comment saying a command naming no repository has a repository file only from the checkout `dir` is in; fourth round, that LET_GO gaining a trailing argument would defeat the stand-in's match for OCX's wait: its comments now say it compares the last argument with LET_GO's last, which Place#hosted puts after whatever names the database. Wrong: first and fourth rounds, that matching the last argument is fragile: a wait the stand-in did not know would make the holding specs fail, not pass; second round, that "the repository or the checkout's own database" is imprecise: neither Fossil's error nor the wait says which of the two was held, and its proposed check was the same condition; fourth round, that the message conflates the two bounds: it names the runs in all and how each wait ended, unchanged here, as the review of 211a7d966b's own fifth round found; fifth round, that the message contradicts the specs: each spec's words match it, and each checks the part it names, the rest being what Fossil said.

Demonstrates: spec/ocx/writers_spec.cr (a reader holding the repository while a report write runs: the report stored once the reader lets go during OCX's wait; a write failed after TRIES runs, three waits ending with it still held, storing nothing, while the reader holds on past each wait, which with BEGIN IMMEDIATE failed after RERUNS + 1 runs, each wait going through at once), spec/ocx/fossil_spec.cr (a writer holding a checkout's own database while add and then commit run there: each waits and goes through, the file committed; an add failed after TRIES runs, storing nothing, while it holds on past each wait, which with the wait by -R failed after RERUNS + 1; and Locked's new words), spec/ocx/create_spec.cr, spec/ocx/post_spec.cr and spec/ocx/remote_node_spec.cr (Locked's new words, a push in a checkout naming the checkout's own database), spec/support/stand_in_fossil.cr (Holder; holding_past and holding_until by a holder; OCX's wait known by its last argument), src/ocx/fossil.cr, spec/guards/size_spec.cr (each file at most 400 lines), spec/guards/causation_spec.cr (no timer added), spec/guards/interfaces_spec.cr and spec/guards/documents_spec.cr (the edited documents rendering with their links), spec/coverage/run.cr (every statement still covered). crystal run ci.cr green: 495 examples, 0 failures.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QVpmCsBwVF3d4zhMHHDAxs check-in: 39c646f7ad user: opus-ocx2 tags: agent-tools

Changes
Unified Diff Side-by-Side Diff Patch
Changes to HISTORY.md.
Changes to docs/fossil.md.
Changes to docs/interfaces.md.
Changes to spec/ocx/fossil_spec.cr.
Changes to spec/support/stand_in_fossil.cr.
Changes to src/ocx/fossil.cr.