Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -350,9 +350,9 @@ five-minute demo of persistence and sessions, with runnable scripts in
`runPath` gap below was found.

**`gemdb file.py` starts with a dirty session, so `gemdb.transaction()` cannot
be a script's first statement.** Measured 2026-08-23, and it contradicts what
`cli.ts` claims a few lines above its driver. Walking the preamble one send at
a time in a clean session: `___canonicalClassesEnabled___: true` leaves
be a script's first statement.** Measured 2026-08-23 against the payload of
that date, before Grail retired the canonical-modules flag. Walking the
preamble one send at a time in a clean session: setting the flag left
`System needsCommit` false, the `#GrailConsole` store leaves it false, and
`importlib runPath:` sets it true — twice from clean, so it is `runPath`
itself, not the file's own code (a script whose first line is
Expand Down
8 changes: 4 additions & 4 deletions src/__tests__/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,10 @@ describe('writeCliScripts', () => {
// SystemExit's Python args tuple, and CPython truncates an int to n % 256.
expect(run).toContain("objectNamed: #'SystemExit'");
expect(run).toContain('\\\\ 256');
// Deployed modules (gemdb above all) warm-bind only when the session
// enables canonical modules — without this line every CLI run
// cold-imports and gemdb's clean-session contract is void.
expect(run).toContain('___canonicalClassesEnabled___: true');
// Grail retired the canonical-modules flag when warm binding became its
// only path. Sending it is now a doesNotUnderstand that kills the run at
// that line, so the driver must not carry it.
expect(run).not.toContain('___canonicalClassesEnabled___');
// A file run is linked topaz, so it never reaches session.ts and would
// otherwise sit in the shared cache as the stock 'TopazL'. The truncation
// is not optional: 32 characters raises OutOfRange, at login.
Expand Down
11 changes: 5 additions & 6 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,12 +133,11 @@ set gemstone ${STONE_NAME}
login
run
| args ofs target status statusFile label |
"Canonical modules, session-local and default off: with it on, the modules
the extent ships deployed (gemdb above all) warm-bind instead of
cold-importing, so running a script leaves no uncommitted plumbing behind
and gemdb.transaction() works as its first statement. Mirrors the same
setting in the extension's session.ts."
importlib ___canonicalClassesEnabled___: true.
"No canonical-modules flag is set here; see cli.ts. Grail retired that
flag once warm binding became its only path -- what is warm is now
decided by what has been committed, which is what the shipped extent
already provides -- and the send became a doesNotUnderstand that killed
every file run at this line."
args := System commandLineArguments.
1 to: args size do: [:j | (args at: j) = '--' ifTrue: [ofs := j]].
statusFile := System gemEnvironmentVariable: 'GEMDB_STATUS_FILE'.
Expand Down
25 changes: 7 additions & 18 deletions src/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -418,24 +418,13 @@ export class GciSession {
}
const session = new GciSession(gci, result.session, resolved);
liveSessions.add(session);
// Canonical modules are a session-local Grail flag, default off. The
// shipped extent deploys gemdb (committed, caches warmed) precisely so
// that a session's `import gemdb` leaves nothing to commit — but the
// import only consults that deployed state when this flag is on. Without
// it every session cold-imports, and gemdb's transaction() entry check
// reports the import's own writes as the user's pending changes. Guarded
// send, best-effort: a database this extension did not install may
// predate the flag, and a session without it still works — just colder.
try {
session.execute(
'(System myUserProfile symbolList objectNamed: #importlib) ' +
'ifNotNil: [:imp | imp ___canonicalClassesEnabled___: true]. true',
);
} catch (e) {
log(
`Could not enable canonical modules (${resolved.label}): ${e instanceof Error ? e.message : e}`,
);
}
// Nothing is sent here to make imports warm. Grail once gated that on a
// session-local flag, `___canonicalClassesEnabled___`, which this login
// turned on; the flag was retired when warm binding became the only path,
// and what is warm is now decided by what has been committed — which is
// exactly what the shipped extent, with gemdb deployed and its caches
// warmed, provides. The send survived here for a while as a caught DNU,
// logging a failure on every single login, which is worse than nothing.
// Record GemStone's own serial for this session, so what the extension
// knows (which notebook owns it) can be joined to what the database knows
// (`gemdb.sessions`, idle times, who is holding resources). Best-effort:
Expand Down