@4fu/arbor-journal-sqlite 0.1.0 → 2026.1106.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -27,9 +27,11 @@ everything that takes judgment, such as git, merges and conflicts, to the agent.
27
27
  guarantees what an agent cannot guarantee for itself - the record, isolation,
28
28
  exactly-once effects, the checks, dispatch. Everything that takes judgment,
29
29
  such as git, merges and conflicts, is the agent's.
30
- - **A directory per window.** Each window gets its own directory, and a
31
- continuation keeps working in its predecessor's directory in place. It is not
32
- bound to a repository; the agent clones and pushes what it needs.
30
+ - **A shared workspace with per-window checkouts.** The default workspace is
31
+ ovnfs: one shared folder every window reads, and a window opens its own
32
+ copy-on-write checkout of the project it needs, works there and merges back
33
+ with `apply`. It falls back to a plain directory per window when ovnfs is not
34
+ installed. It is not a sandbox: ovnfs isolates files, not processes.
33
35
  - **A self-changing environment.** The model adds and changes skills, MCP
34
36
  servers and resident services itself; a `reload` validates the change and
35
37
  broadcasts it to every live window as a delta of names and descriptions.
@@ -118,7 +120,8 @@ window gets, and how a continuation keeps working in it, are in
118
120
  | [`@4fu/arbor-journal-sqlite`](packages/journal-sqlite) | the default `Journal` and `BlobStore` on SQLite |
119
121
  | [`@4fu/arbor-notes-sqlite`](packages/notes-sqlite) | the default `NoteStore` on SQLite, beside the journal |
120
122
  | [`@4fu/arbor-segmenter-jieba`](packages/segmenter-jieba) | the default `Segmenter`: jieba's dictionary segmentation for the word index, optional |
121
- | [`@4fu/arbor-workspace-dir`](packages/workspace-dir) | the default `Workspace`: one directory per window, with a continuation working in its predecessor's directory in place |
123
+ | [`@4fu/arbor-workspace-dir`](packages/workspace-dir) | the fallback `Workspace`: one directory per window, with a continuation working in its predecessor's directory in place |
124
+ | [`@4fu/arbor-workspace-ovnfs`](packages/workspace-ovnfs) | the default `Workspace` when ovnfs is installed: one shared folder, per-window copy-on-write checkouts the model opens and applies back |
122
125
  | [`@4fu/arbor-environment-dir`](packages/environment-dir) | the default `Environment`: one `/env` directory, a revision per item, validation and deltas |
123
126
  | [`@4fu/arbor-supervisor-process`](packages/supervisor-process) | the default `Supervisor`: one host process per service, restart with backoff, logs |
124
127
  | [`@4fu/arbor-judge-jev`](packages/judge-jev) | the default `Judge`: Jev, TypeSafe AI's System One classifier, answering the dispatch decisions |
@@ -151,7 +154,7 @@ deployment can see what it would be taking on before it adopts one.
151
154
  | [docs/coordination.md](docs/coordination.md) | the board, expectations, deliveries and broadcasts, staying current, effects |
152
155
  | [docs/environment.md](docs/environment.md) | skills, MCP servers and services: using, changing, reload, built-in skills, deltas |
153
156
  | [docs/memory.md](docs/memory.md) | notes, the active set, anchors, links |
154
- | [docs/workspaces.md](docs/workspaces.md) | per-window directories: isolation, continuity, what the agent does |
157
+ | [docs/workspaces.md](docs/workspaces.md) | the shared folder and per-window checkouts: isolation, continuity, what the agent does |
155
158
  | [docs/tools.md](docs/tools.md) | direct tools and code mode modules |
156
159
  | [docs/prompting.md](docs/prompting.md) | how every model-facing text is written: layers, notes, deliveries, errors, judge questions, terms |
157
160
  | [docs/release.md](docs/release.md) | calendar versioning, the manual release steps, how a package is published |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@4fu/arbor-journal-sqlite",
3
- "version": "0.1.0",
3
+ "version": "2026.1106.1",
4
4
  "type": "module",
5
5
  "description": "The default Journal: append-only events with a global sequence, targets, native ids, refs, blobs and lexical search on SQLite.",
6
6
  "exports": {
@@ -40,11 +40,11 @@
40
40
  "access": "public"
41
41
  },
42
42
  "dependencies": {
43
- "@4fu/arbor-errors": "0.1.0",
44
- "@4fu/arbor-interfaces": "0.1.0"
43
+ "@4fu/arbor-errors": "2026.1106.1",
44
+ "@4fu/arbor-interfaces": "2026.1106.1"
45
45
  },
46
46
  "peerDependencies": {
47
- "@4fu/arbor-segmenter-jieba": "0.1.0"
47
+ "@4fu/arbor-segmenter-jieba": "2026.1106.1"
48
48
  },
49
49
  "peerDependenciesMeta": {
50
50
  "@4fu/arbor-segmenter-jieba": {
@@ -64,7 +64,13 @@ export declare class SqliteJournal implements Journal {
64
64
  /** One column of one table, for the child rows of an event. */
65
65
  private strings;
66
66
  private countEvents;
67
+ /**
68
+ * The newest event on a target that another window wrote after one window
69
+ * opened: what that window can still be behind on.
70
+ */
67
71
  private latestOn;
72
+ /** The sequence of one window's own `window_start`, or 0 before it opened. */
73
+ private openedAt;
68
74
  /** Where a ref came from: the links that go out of it, strongest first. */
69
75
  private sourcesOf;
70
76
  private resolveRelated;
package/sqlite-journal.js CHANGED
@@ -359,15 +359,21 @@ export class SqliteJournal {
359
359
  WHERE e.window = ?
360
360
  GROUP BY et.target`)
361
361
  .all(window);
362
+ const openedAt = await this.openedAt(window);
362
363
  const gaps = [];
363
364
  for (const row of touched) {
364
- const latest = this.latestOn(row.target);
365
+ // What a window is behind on is what arrived after it opened and what
366
+ // another window wrote: the task and the outside events it was opened
367
+ // for, and the window's own `window_start`, are not open work
368
+ // (docs/windows.md#ending-and-continuing).
369
+ const latest = this.latestOn(row.target, window, openedAt);
365
370
  const watermark = (await this.watermark(window, row.target)) ?? 0;
366
371
  if (latest <= watermark)
367
372
  continue;
368
373
  const behind = this.db
369
- .prepare("SELECT count(*) AS n FROM event_targets WHERE target = ? AND seq > ?")
370
- .get(row.target, watermark);
374
+ .prepare(`SELECT count(*) AS n FROM event_targets et JOIN events e ON e.seq = et.seq
375
+ WHERE et.target = ? AND et.seq > ? AND et.seq > ? AND e.window IS NOT ?`)
376
+ .get(row.target, watermark, openedAt, window);
371
377
  gaps.push({
372
378
  target: row.target,
373
379
  latest,
@@ -527,12 +533,25 @@ export class SqliteJournal {
527
533
  .get(...built.params);
528
534
  return toNumber(row.n);
529
535
  }
530
- latestOn(target) {
536
+ /**
537
+ * The newest event on a target that another window wrote after one window
538
+ * opened: what that window can still be behind on.
539
+ */
540
+ latestOn(target, window, after) {
531
541
  const row = this.db
532
- .prepare("SELECT MAX(seq) AS latest FROM event_targets WHERE target = ?")
533
- .get(target);
542
+ .prepare(`SELECT MAX(et.seq) AS latest FROM event_targets et
543
+ JOIN events e ON e.seq = et.seq
544
+ WHERE et.target = ? AND et.seq > ? AND e.window IS NOT ?`)
545
+ .get(target, after, window);
534
546
  return row?.latest == null ? 0 : toNumber(row.latest);
535
547
  }
548
+ /** The sequence of one window's own `window_start`, or 0 before it opened. */
549
+ async openedAt(window) {
550
+ const row = this.db
551
+ .prepare("SELECT MIN(seq) AS seq FROM events WHERE window = ? AND kind = 'window_start'")
552
+ .get(window);
553
+ return row?.seq == null ? 0 : toNumber(row.seq);
554
+ }
536
555
  /** Where a ref came from: the links that go out of it, strongest first. */
537
556
  sourcesOf(ref) {
538
557
  const rows = this.db