Skip to content

Add a five-minute demo of persistence and sessions - #9

Merged
jgfoster merged 1 commit into
mainfrom
docs/demo-rabbit
Aug 23, 2026
Merged

Add a five-minute demo of persistence and sessions#9
jgfoster merged 1 commit into
mainfrom
docs/demo-rabbit

Conversation

@jgfoster

Copy link
Copy Markdown
Member

The rabbit-in-the-hat demo: one session puts an ASCII rabbit in gemdb.root["hat"], another pulls it out. Three runnable scripts in docs/demo/ and a walkthrough that says what each step proves.

Act What it shows
1 — gemdb hide.py two lines store a Python object; no open(), no format, no schema
2 — gemdb reveal.py a different process reads it; nothing was loaded because nothing was saved
3 — stop the database, gemdb reveal.py the wrapper restarts it and the rabbit is still there
4 — gemdb tricks.py ×3 a counter surviving processes and restarts
5 — two GemDB Shells separate transactions: B keeps seeing the rabbit after A commits, until B asks for a new view
6 — with gemdb.transaction(): one unit of work — and the gap below

Every command and every line of output in the doc was run against a real database rather than composed: the scripts through the generated gemdb wrapper, the restart with the stone confirmed down first, the two-session act through two live GCI sessions.

Which is how it found a bug

tricks.py was written with with gemdb.transaction():. It failed — the block refuses to start, reporting pending changes the script never made.

Walking the file-run preamble one send at a time, from a clean session:

Step System needsCommit
fresh session false
importlib ___canonicalClassesEnabled___: true false
SessionTemps … #'GrailConsole' put: false
importlib runPath: '…py' true
abort, then runPath again true

So a script is dirty before its first line runs — a file whose first statement is import gemstone; print(gemstone.needs_commit) already prints True. transaction()'s entry check, which exists to stop the block sweeping your forgotten work into its commit, fires on Grail's own plumbing instead.

This contradicts the claim in cli.ts (and CLAUDE.md) that canonical modules make gemdb.transaction() work as a script's first statement. Shell and notebook sessions are unaffected — they evaluate through evaluateSource:usingModuleScope:, and a fresh one runs a transaction block as its first action, verified. So it is the file-run path alone and the fix belongs in Grail.

Handled here rather than papered over: tricks.py uses commit(), the doc carries a Known gap note saying what to do until it is fixed, and CLAUDE.md records the measurement next to the claim it contradicts.

Scope

Documentation and demo scripts only — docs/** is kept out of the .vsix by .vscodeignore, so nothing ships. No source change, and the suites are untouched at 109 unit / 46 integration.

🤖 Generated with Claude Code

A walkthrough built around the classic trick: one session puts an ASCII
rabbit in gemdb.root["hat"], another pulls it out. Three runnable scripts
in docs/demo/ and a doc that shows what each step proves -- no save step
in act 1, a different process in act 2, the database fully stopped and
restarted in act 3, and two shells with separate transactions in act 5,
where B keeps seeing the rabbit after A commits until B asks for a new
view.

Every command and every line of output in the doc was run against a real
database rather than composed: hide/reveal/tricks through the generated
gemdb wrapper, the restart with the stone confirmed down first, and the
two-session act through two GCI sessions.

That is how the doc earned its keep. tricks.py was written with `with
gemdb.transaction():` and it failed -- the block refuses to start,
reporting pending changes the script never made. Walking the file-run
preamble one send at a time from a clean session:
___canonicalClassesEnabled___ leaves needsCommit false, the #GrailConsole
store leaves it false, and `importlib runPath:` sets it true, twice from
clean. So a script is dirty before its first line: a file whose first
statement is `import gemstone; print(gemstone.needs_commit)` prints True.
Shell and notebook sessions are clean and run a transaction block as
their first action, so this is the file-run path alone, and the fix
belongs in Grail. tricks.py uses commit() instead, the doc says why in a
Known gap, and CLAUDE.md records the measurement next to the claim in
cli.ts that it contradicts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jgfoster
jgfoster merged commit 567bb94 into main Aug 23, 2026
5 checks passed
@jgfoster
jgfoster deleted the docs/demo-rabbit branch August 23, 2026 15:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant