@retaw106/mmc-oc 2.2.1 → 2.3.0

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.
Files changed (211) hide show
  1. package/dist/cli.js +2 -2
  2. package/dist/config/deprecated-config.d.ts +4 -0
  3. package/dist/config/deprecated-config.d.ts.map +1 -0
  4. package/dist/config/index.d.ts.map +1 -1
  5. package/dist/config/project-security.d.ts +2 -2
  6. package/dist/config/schema/magic-context.d.ts +3 -3
  7. package/dist/config/schema/magic-context.d.ts.map +1 -1
  8. package/dist/config-init.js +2 -2
  9. package/dist/{context-mrvm92n8.js → context-wt7g4f8k.js} +1641 -1543
  10. package/dist/{derive-budgets-rjjz5f7y.js → derive-budgets-s1p37t2g.js} +2 -2
  11. package/dist/features/magic-context/compaction-marker.d.ts.map +1 -1
  12. package/dist/features/magic-context/historian-failure-reason.d.ts +7 -0
  13. package/dist/features/magic-context/historian-failure-reason.d.ts.map +1 -0
  14. package/dist/features/magic-context/message-index-async.d.ts +21 -3
  15. package/dist/features/magic-context/message-index-async.d.ts.map +1 -1
  16. package/dist/features/magic-context/message-index.d.ts +7 -1
  17. package/dist/features/magic-context/message-index.d.ts.map +1 -1
  18. package/dist/features/magic-context/migrations.d.ts.map +1 -1
  19. package/dist/features/magic-context/pi-content-decisions.d.ts +7 -1
  20. package/dist/features/magic-context/pi-content-decisions.d.ts.map +1 -1
  21. package/dist/features/magic-context/search.d.ts +29 -0
  22. package/dist/features/magic-context/search.d.ts.map +1 -1
  23. package/dist/features/magic-context/storage-clone.d.ts +2 -0
  24. package/dist/features/magic-context/storage-clone.d.ts.map +1 -1
  25. package/dist/features/magic-context/storage-compartment-history-version.d.ts +4 -0
  26. package/dist/features/magic-context/storage-compartment-history-version.d.ts.map +1 -0
  27. package/dist/features/magic-context/storage-db.d.ts +1 -1
  28. package/dist/features/magic-context/storage-db.d.ts.map +1 -1
  29. package/dist/features/magic-context/storage-historian-runs.d.ts.map +1 -1
  30. package/dist/features/magic-context/storage-meta-persisted.d.ts +23 -5
  31. package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
  32. package/dist/features/magic-context/storage-meta-session.d.ts +7 -1
  33. package/dist/features/magic-context/storage-meta-session.d.ts.map +1 -1
  34. package/dist/features/magic-context/storage-session-tables.d.ts +5 -1
  35. package/dist/features/magic-context/storage-session-tables.d.ts.map +1 -1
  36. package/dist/features/magic-context/storage-tag-identity-revision.d.ts +50 -0
  37. package/dist/features/magic-context/storage-tag-identity-revision.d.ts.map +1 -0
  38. package/dist/features/magic-context/storage-tags.d.ts +155 -5
  39. package/dist/features/magic-context/storage-tags.d.ts.map +1 -1
  40. package/dist/features/magic-context/tagger.d.ts +2 -0
  41. package/dist/features/magic-context/tagger.d.ts.map +1 -1
  42. package/dist/features/magic-context/transform-decision-log.d.ts +3 -0
  43. package/dist/features/magic-context/transform-decision-log.d.ts.map +1 -1
  44. package/dist/hooks/magic-context/active-anthropic-turn.d.ts +2 -0
  45. package/dist/hooks/magic-context/active-anthropic-turn.d.ts.map +1 -1
  46. package/dist/hooks/magic-context/apply-operations.d.ts +7 -0
  47. package/dist/hooks/magic-context/apply-operations.d.ts.map +1 -1
  48. package/dist/hooks/magic-context/auto-search-runner.d.ts +2 -2
  49. package/dist/hooks/magic-context/auto-search-runner.d.ts.map +1 -1
  50. package/dist/hooks/magic-context/heuristic-cleanup.d.ts.map +1 -1
  51. package/dist/hooks/magic-context/history-boundary-repair.d.ts +11 -2
  52. package/dist/hooks/magic-context/history-boundary-repair.d.ts.map +1 -1
  53. package/dist/hooks/magic-context/hook-handlers.d.ts.map +1 -1
  54. package/dist/hooks/magic-context/lkg-measured-request.d.ts +3 -1
  55. package/dist/hooks/magic-context/lkg-measured-request.d.ts.map +1 -1
  56. package/dist/hooks/magic-context/lkg-persist.d.ts +10 -13
  57. package/dist/hooks/magic-context/lkg-persist.d.ts.map +1 -1
  58. package/dist/hooks/magic-context/lkg-recovery-fence.d.ts +62 -0
  59. package/dist/hooks/magic-context/lkg-recovery-fence.d.ts.map +1 -0
  60. package/dist/hooks/magic-context/lkg-recovery-prefix.d.ts +38 -0
  61. package/dist/hooks/magic-context/lkg-recovery-prefix.d.ts.map +1 -0
  62. package/dist/hooks/magic-context/lkg-replay.d.ts +16 -9
  63. package/dist/hooks/magic-context/lkg-replay.d.ts.map +1 -1
  64. package/dist/hooks/magic-context/lkg-slot.d.ts +141 -2
  65. package/dist/hooks/magic-context/lkg-slot.d.ts.map +1 -1
  66. package/dist/hooks/magic-context/opaque-thinking.d.ts +12 -0
  67. package/dist/hooks/magic-context/opaque-thinking.d.ts.map +1 -0
  68. package/dist/hooks/magic-context/pass-outcome.d.ts +1 -0
  69. package/dist/hooks/magic-context/pass-outcome.d.ts.map +1 -1
  70. package/dist/hooks/magic-context/pending-materialization.d.ts +8 -0
  71. package/dist/hooks/magic-context/pending-materialization.d.ts.map +1 -0
  72. package/dist/hooks/magic-context/postprocess-read-cache.d.ts.map +1 -1
  73. package/dist/hooks/magic-context/provider-visible-parts.d.ts +45 -0
  74. package/dist/hooks/magic-context/provider-visible-parts.d.ts.map +1 -0
  75. package/dist/hooks/magic-context/read-session-chunk.d.ts +22 -1
  76. package/dist/hooks/magic-context/read-session-chunk.d.ts.map +1 -1
  77. package/dist/hooks/magic-context/read-session-db.d.ts +3 -0
  78. package/dist/hooks/magic-context/read-session-db.d.ts.map +1 -1
  79. package/dist/hooks/magic-context/read-session-formatting.d.ts +5 -0
  80. package/dist/hooks/magic-context/read-session-formatting.d.ts.map +1 -1
  81. package/dist/hooks/magic-context/read-session-raw.d.ts +123 -1
  82. package/dist/hooks/magic-context/read-session-raw.d.ts.map +1 -1
  83. package/dist/hooks/magic-context/read-session-true-raw-tokens.d.ts.map +1 -1
  84. package/dist/hooks/magic-context/reasoning-budget.d.ts.map +1 -1
  85. package/dist/hooks/magic-context/sentinel.d.ts +41 -1
  86. package/dist/hooks/magic-context/sentinel.d.ts.map +1 -1
  87. package/dist/hooks/magic-context/strict-thinking-prefix.d.ts +8 -0
  88. package/dist/hooks/magic-context/strict-thinking-prefix.d.ts.map +1 -0
  89. package/dist/hooks/magic-context/strip-content.d.ts +38 -24
  90. package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
  91. package/dist/hooks/magic-context/system-date-refresh.d.ts +29 -0
  92. package/dist/hooks/magic-context/system-date-refresh.d.ts.map +1 -0
  93. package/dist/hooks/magic-context/system-prompt-dates.d.ts +6 -0
  94. package/dist/hooks/magic-context/system-prompt-dates.d.ts.map +1 -0
  95. package/dist/hooks/magic-context/system-prompt-delivery.d.ts +47 -0
  96. package/dist/hooks/magic-context/system-prompt-delivery.d.ts.map +1 -0
  97. package/dist/hooks/magic-context/system-prompt-hash.d.ts +18 -5
  98. package/dist/hooks/magic-context/system-prompt-hash.d.ts.map +1 -1
  99. package/dist/hooks/magic-context/tag-messages.d.ts +19 -0
  100. package/dist/hooks/magic-context/tag-messages.d.ts.map +1 -1
  101. package/dist/hooks/magic-context/tag-owner-warmup.d.ts +14 -0
  102. package/dist/hooks/magic-context/tag-owner-warmup.d.ts.map +1 -0
  103. package/dist/hooks/magic-context/token-count-exact.d.ts +35 -0
  104. package/dist/hooks/magic-context/token-count-exact.d.ts.map +1 -0
  105. package/dist/hooks/magic-context/tool-drop-target.d.ts +2 -0
  106. package/dist/hooks/magic-context/tool-drop-target.d.ts.map +1 -1
  107. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +7 -0
  108. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
  109. package/dist/hooks/magic-context/transform.d.ts +21 -4
  110. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  111. package/dist/index-18yrysyj.js +1477 -0
  112. package/dist/{index-g2z2k5m4.js → index-3gj1ak4c.js} +2 -2
  113. package/dist/{index-5cep2r9n.js → index-5ty9rnbx.js} +60 -258
  114. package/dist/{index-amycb7b1.js → index-a5mw0xp3.js} +3 -3
  115. package/dist/{index-1j34a56j.js → index-czba8tw3.js} +266 -32
  116. package/dist/{index-qbkzdkk0.js → index-k59af64t.js} +6 -2
  117. package/dist/{index-53g38ggt.js → index-kmbme1v1.js} +1 -1
  118. package/dist/{index-5b7x80ec.js → index-kw21z5pe.js} +6055 -3973
  119. package/dist/{index-33gprt9g.js → index-yjv8dpht.js} +1 -1
  120. package/dist/{index-btp5tdc8.js → index-z2w1eq3y.js} +840 -456
  121. package/dist/index.d.ts.map +1 -1
  122. package/dist/index.js +114 -2
  123. package/dist/{message-time-backfill-phrwp2fz.js → message-time-backfill-sykevdax.js} +3 -3
  124. package/dist/plugin/rpc-handlers.d.ts.map +1 -1
  125. package/dist/{read-session-chunk-2xm4sc4y.js → read-session-chunk-863d6c06.js} +6 -4
  126. package/dist/recomp-orchestrator-wrjy3qym.js +21 -0
  127. package/dist/{safe-notification-target-ph3xd1ve.js → safe-notification-target-1jkf0m82.js} +2 -2
  128. package/dist/{send-session-notification-rnc4nrsr.js → send-session-notification-cz6ev088.js} +5 -5
  129. package/dist/shared/auto-search-lifecycle.d.ts +14 -0
  130. package/dist/shared/auto-search-lifecycle.d.ts.map +1 -1
  131. package/dist/shared/logger.d.ts.map +1 -1
  132. package/dist/shared/models-dev-cache.d.ts.map +1 -1
  133. package/dist/shared/models-dev-temperature.d.ts +18 -0
  134. package/dist/shared/models-dev-temperature.d.ts.map +1 -0
  135. package/dist/shared/processed-image-eligibility.d.ts +22 -0
  136. package/dist/shared/processed-image-eligibility.d.ts.map +1 -0
  137. package/dist/shared/project-directory-key.d.ts +16 -0
  138. package/dist/shared/project-directory-key.d.ts.map +1 -1
  139. package/dist/shared/recovery-reasoning-prefix.d.ts +9 -0
  140. package/dist/shared/recovery-reasoning-prefix.d.ts.map +1 -0
  141. package/dist/shared/sqlite.d.ts +33 -1
  142. package/dist/shared/sqlite.d.ts.map +1 -1
  143. package/dist/shared/stable-json.d.ts.map +1 -1
  144. package/dist/shared/storage-file-mutex.d.ts +9 -0
  145. package/dist/shared/storage-file-mutex.d.ts.map +1 -0
  146. package/dist/shared/storage-permissions.d.ts +6 -0
  147. package/dist/shared/storage-permissions.d.ts.map +1 -1
  148. package/dist/shared/subagent-temperature.d.ts +46 -0
  149. package/dist/shared/subagent-temperature.d.ts.map +1 -0
  150. package/dist/shared/tag-owner-id.d.ts +1 -0
  151. package/dist/shared/tag-owner-id.d.ts.map +1 -1
  152. package/dist/shared/tag-transcript.d.ts +9 -0
  153. package/dist/shared/tag-transcript.d.ts.map +1 -1
  154. package/dist/shared/window-geometry.d.ts +9 -66
  155. package/dist/shared/window-geometry.d.ts.map +1 -1
  156. package/dist/standalone-plugin-marker.d.ts +8 -0
  157. package/dist/standalone-plugin-marker.d.ts.map +1 -0
  158. package/dist/tools/ctx-expand/search-archive.d.ts +4 -0
  159. package/dist/tools/ctx-expand/search-archive.d.ts.map +1 -0
  160. package/dist/tools/ctx-expand/tools.d.ts.map +1 -1
  161. package/dist/tools/ctx-reduce/constants.d.ts +1 -2
  162. package/dist/tools/ctx-reduce/constants.d.ts.map +1 -1
  163. package/dist/tools/ctx-reduce/tools.d.ts +1 -1
  164. package/dist/tools/ctx-reduce/tools.d.ts.map +1 -1
  165. package/dist/v2/hooks/context.d.ts +1 -1
  166. package/dist/v2/hooks/context.d.ts.map +1 -1
  167. package/dist/v2/hooks/copied-tag-notation.d.ts +19 -0
  168. package/dist/v2/hooks/copied-tag-notation.d.ts.map +1 -0
  169. package/dist/v2/hooks/generate.d.ts +9 -2
  170. package/dist/v2/hooks/generate.d.ts.map +1 -1
  171. package/dist/v2/hooks/payload.d.ts.map +1 -1
  172. package/dist/v2/hooks/system-prompt-response.d.ts +5 -0
  173. package/dist/v2/hooks/system-prompt-response.d.ts.map +1 -0
  174. package/dist/v2/server.d.ts.map +1 -1
  175. package/dist/v2/server.js +34550 -30855
  176. package/dist/web/main.js +373 -19
  177. package/dist/{wrapup-orchestrator-yknks3ad.js → wrapup-orchestrator-bmybc5p5.js} +10 -10
  178. package/package.json +1 -1
  179. package/src/config/deprecated-config.ts +18 -0
  180. package/src/config/index.test.ts +13 -37
  181. package/src/config/index.ts +2 -2
  182. package/src/config/project-security.ts +3 -3
  183. package/src/config/schema/magic-context.ts +10 -6
  184. package/src/shared/auto-search-lifecycle.ts +43 -0
  185. package/src/shared/logger.ts +10 -2
  186. package/src/shared/models-dev-cache.ts +1 -7
  187. package/src/shared/models-dev-temperature.ts +107 -0
  188. package/src/shared/processed-image-eligibility.test.ts +68 -0
  189. package/src/shared/processed-image-eligibility.ts +41 -0
  190. package/src/shared/processed-image-position-review.json +20 -0
  191. package/src/shared/processed-image-position-review.test.ts +19 -0
  192. package/src/shared/project-directory-key.ts +20 -0
  193. package/src/shared/recovery-reasoning-prefix.test.ts +34 -0
  194. package/src/shared/recovery-reasoning-prefix.ts +35 -0
  195. package/src/shared/sqlite-acquisition-routing.test.ts +4 -3
  196. package/src/shared/sqlite-foreground-wait.test.ts +68 -0
  197. package/src/shared/sqlite-pass-scope.test.ts +4 -4
  198. package/src/shared/sqlite-writer-diagnostics.test.ts +52 -12
  199. package/src/shared/sqlite.ts +380 -39
  200. package/src/shared/stable-json.ts +37 -7
  201. package/src/shared/storage-file-mutex.test.ts +50 -0
  202. package/src/shared/storage-file-mutex.ts +130 -0
  203. package/src/shared/storage-permissions.ts +46 -0
  204. package/src/shared/subagent-temperature.ts +110 -0
  205. package/src/shared/tag-owner-id.ts +13 -0
  206. package/src/shared/tag-transcript.ts +118 -0
  207. package/src/shared/window-geometry.test.ts +102 -236
  208. package/src/shared/window-geometry.ts +23 -413
  209. package/dist/index-ramz5kkm.js +0 -514
  210. package/dist/recomp-orchestrator-frmwj9hz.js +0 -21
  211. package/src/shared/__fixtures__/window-overlay.v1.json +0 -337
@@ -0,0 +1,68 @@
1
+ import { expect, test } from "bun:test";
2
+ import { rmSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { log } from "./logger";
6
+ import {
7
+ Database,
8
+ guardSqliteTransformPass,
9
+ registerSqliteDiagnosticSink,
10
+ SqliteAcquisitionBusyError,
11
+ withPrivilegedWriter,
12
+ withSqliteTransformPass,
13
+ } from "./sqlite";
14
+ import { createTestTempDirFromPath } from "./test-temp-dir";
15
+
16
+ function fixture() {
17
+ const dir = createTestTempDirFromPath(join(tmpdir(), "mc-foreground-wait-"));
18
+ const path = join(dir, "context.db");
19
+ const db = new Database(path);
20
+ db.exec(`
21
+ PRAGMA journal_mode=WAL;
22
+ PRAGMA busy_timeout=5000;
23
+ CREATE TABLE context_privilege_state(id INTEGER PRIMARY KEY, enabled INTEGER);
24
+ CREATE TABLE result(value TEXT);
25
+ `);
26
+ return {
27
+ db,
28
+ path,
29
+ close() {
30
+ db.close();
31
+ rmSync(dir, { recursive: true, force: true });
32
+ },
33
+ };
34
+ }
35
+
36
+ // Upstream also drives two cases through the OC1 messages-transform handler
37
+ // (1 s hold served, 2.5 s hold refused with StorageBusyRefusalError). That
38
+ // adapter is deleted in this fork; the in-pass acquisition-wait behavior those
39
+ // cases cover is exercised here against the core primitives instead, and the
40
+ // 1 s/2.5 s boundaries are additionally covered by scripts/smoke-node-sqlite.ts.
41
+ for (const mode of ["BEGIN", "autocommit"]) {
42
+ test(`foreground ${mode} wait is capped by the pass deadline`, () => {
43
+ const { db, path, close } = fixture();
44
+ const blocker = new Database(path);
45
+ blocker.exec("BEGIN IMMEDIATE");
46
+ const diagnostics: string[] = [];
47
+ registerSqliteDiagnosticSink((line) => diagnostics.push(line));
48
+ try {
49
+ const started = performance.now();
50
+ withSqliteTransformPass(() => {
51
+ guardSqliteTransformPass({ assert() {}, remainingMs: () => 100 });
52
+ expect(() => {
53
+ if (mode === "BEGIN") withPrivilegedWriter(db, () => undefined);
54
+ else db.prepare("INSERT INTO result VALUES ('late')").run();
55
+ }).toThrow(SqliteAcquisitionBusyError);
56
+ });
57
+ expect(performance.now() - started).toBeGreaterThanOrEqual(75);
58
+ expect(performance.now() - started).toBeLessThan(700);
59
+ expect(db.prepare("SELECT * FROM result").all()).toEqual([]);
60
+ expect(db.prepare("PRAGMA busy_timeout").get()).toEqual({ timeout: 5000 });
61
+ } finally {
62
+ registerSqliteDiagnosticSink(log);
63
+ blocker.exec("ROLLBACK");
64
+ blocker.close();
65
+ close();
66
+ }
67
+ });
68
+ }
@@ -100,7 +100,7 @@ test("foreground admission keeps the loop ticking while a separate writer holds
100
100
  }
101
101
  }, 30000);
102
102
 
103
- test("a foreground in-pass writer has bounded 250ms tolerance and restores its timeout", () => {
103
+ test("a foreground in-pass writer has bounded 2000ms tolerance and restores its timeout", () => {
104
104
  const { db, path, close } = fixture();
105
105
  const blocker = new Database(path);
106
106
  blocker.exec("BEGIN IMMEDIATE");
@@ -109,15 +109,15 @@ test("a foreground in-pass writer has bounded 250ms tolerance and restores its t
109
109
  expect(() =>
110
110
  withSqliteTransformPass(() => withPrivilegedWriter(db, () => undefined)),
111
111
  ).toThrow(SqliteAcquisitionBusyError);
112
- expect(performance.now() - start).toBeGreaterThanOrEqual(200);
113
- expect(performance.now() - start).toBeLessThan(400);
112
+ expect(performance.now() - start).toBeGreaterThanOrEqual(1900);
113
+ expect(performance.now() - start).toBeLessThan(2500);
114
114
  expect(db.prepare("PRAGMA busy_timeout").get()).toEqual({ timeout: 5000 });
115
115
  } finally {
116
116
  blocker.exec("ROLLBACK");
117
117
  blocker.close();
118
118
  close();
119
119
  }
120
- });
120
+ }, 30000);
121
121
 
122
122
  test("ready background publication retries on a timer and retains its write", async () => {
123
123
  const { db, path, close } = fixture();
@@ -7,11 +7,13 @@ import { createTestTempDirFromPath } from "./test-temp-dir";
7
7
  // The scenario runs in a child process with NODE_ENV=production because the
8
8
  // shared logger is silent under test. It imports storage-db for its side effect
9
9
  // of registering the real log sink, then drives three privileged writes and one
10
- // background writer through two connections on a throwaway database:
11
- // wait - another connection holds the lock for 300 ms, the hold is short
12
- // hold - the lock comes at once, the operation keeps it for 300 ms
13
- // fast - neither crosses the threshold, so nothing is logged
14
- // async - beginSqliteWriterAsync acquires at once and holds for 300 ms
10
+ // background writer through two connections on a throwaway database.
11
+ // Acquisition measures waiting for BEGIN to succeed; hold measures time from
12
+ // successful BEGIN through COMMIT. The injected clock separates those costs:
13
+ // wait - 300 ms before BEGIN and 0 ms held: log a foreground acquisition
14
+ // hold - 0 ms before BEGIN and 300 ms held: log a foreground hold
15
+ // fast - 0 ms for both: do not log anything
16
+ // async - 0 ms before BEGIN and 300 ms held: log the background writer's own site
15
17
  const scenario = `
16
18
  await import(${JSON.stringify(new URL("../features/magic-context/storage-db.ts", import.meta.url).href)});
17
19
  const sqlite = await import(${JSON.stringify(new URL("./sqlite.ts", import.meta.url).href)});
@@ -20,19 +22,38 @@ const scenario = `
20
22
  const holder = new sqlite.Database(dbPath);
21
23
  holder.exec("PRAGMA journal_mode=WAL; CREATE TABLE context_privilege_state(id INTEGER PRIMARY KEY, enabled INTEGER)");
22
24
  const writer = new sqlite.Database(dbPath);
23
- const spin = (ms) => { const end = performance.now() + ms; while (performance.now() < end) {} };
25
+ let now = 0;
26
+ performance.now = () => now;
27
+ const hold = () => { now += 300; };
24
28
 
25
29
  holder.exec("BEGIN IMMEDIATE");
26
- setTimeout(() => holder.exec("COMMIT"), 300);
27
- await sqlite.withAsyncPrivilegedWriter(writer, () => undefined);
30
+ await sqlite.withAsyncPrivilegedWriter(writer, () => undefined, {
31
+ beforeRetry: () => { now += 300; holder.exec("COMMIT"); },
32
+ });
28
33
 
29
- await sqlite.withAsyncPrivilegedWriter(writer, () => spin(300));
34
+ await sqlite.withAsyncPrivilegedWriter(writer, hold);
30
35
 
31
36
  await sqlite.withAsyncPrivilegedWriter(writer, () => undefined);
32
37
 
33
38
  await sqlite.beginSqliteWriterAsync(writer, "historian-publish");
34
- spin(300);
39
+ hold();
40
+ writer.exec("COMMIT");
41
+
42
+ writer.exec("BEGIN IMMEDIATE");
43
+ now += 101;
44
+ writer.exec("ROLLBACK");
45
+
46
+ writer.exec("CREATE TABLE result(value)");
47
+ const statementTimes = [now, now + 101];
48
+ performance.now = () => statementTimes.shift() ?? now;
49
+ writer.prepare("INSERT INTO result VALUES (1)").run();
50
+ now += 101;
51
+
52
+ let clockReads = 0;
53
+ performance.now = () => { clockReads++; return now; };
54
+ writer.exec("BEGIN IMMEDIATE");
35
55
  writer.exec("COMMIT");
56
+ if (clockReads !== 2) throw new Error("fast unscoped transaction clock reads=" + clockReads);
36
57
 
37
58
  logger.flushLogger();
38
59
  writer.close();
@@ -61,8 +82,6 @@ describe("sqlite writer diagnostics", () => {
61
82
  SCENARIO_DB_PATH: join(root, "writer.db"),
62
83
  };
63
84
  for (const key of [
64
- "HOME",
65
- "OPENCODE_CONFIG_DIR",
66
85
  "XDG_DATA_HOME",
67
86
  "XDG_CONFIG_HOME",
68
87
  "XDG_STATE_HOME",
@@ -103,6 +122,21 @@ describe("sqlite writer diagnostics", () => {
103
122
  expect(consoleOutput).not.toContain("sqlite writer");
104
123
  });
105
124
 
125
+ test("unscoped rollback names the host, pid and caller at the 100ms threshold", () => {
126
+ const rolledBack = logLines.find((line) => line.includes("outcome=rolled_back"));
127
+ expect(rolledBack).toContain("lane=background");
128
+ expect(rolledBack).toContain("hold_ms=101");
129
+ expect(rolledBack).toMatch(/pid=\d+ host=opencode/);
130
+ expect(rolledBack).toContain("caller=");
131
+ });
132
+
133
+ test("native autocommit time is labelled as including acquisition, not lock ownership", () => {
134
+ const statement = logLines.find((line) => line.includes("site=autocommit"));
135
+ expect(statement).toContain("elapsed_ms=101");
136
+ expect(statement).toContain("timing=statement_including_acquisition");
137
+ expect(statement).not.toContain("hold_ms=");
138
+ });
139
+
106
140
  test("a writer that waited for the lock reports the wait, not its short hold", () => {
107
141
  const waited = logLines.find((line) => line.includes("attempts=2"));
108
142
  if (!waited) throw new Error(`no retried acquisition logged:\n${logLines.join("\n")}`);
@@ -111,6 +145,8 @@ describe("sqlite writer diagnostics", () => {
111
145
  expect(waited).toContain("outcome=committed");
112
146
  expect(field(waited, "acquire_ms")).toBeGreaterThanOrEqual(250);
113
147
  expect(field(waited, "hold_ms")).toBeLessThan(250);
148
+ expect(field(waited, "acquire_ms")).toBe(300);
149
+ expect(field(waited, "hold_ms")).toBe(0);
114
150
  });
115
151
 
116
152
  test("a writer that held the lock reports the hold, not its quick acquisition", () => {
@@ -120,6 +156,8 @@ describe("sqlite writer diagnostics", () => {
120
156
  expect(held).toHaveLength(1);
121
157
  expect(field(held[0], "acquire_ms")).toBeLessThan(250);
122
158
  expect(field(held[0], "hold_ms")).toBeGreaterThanOrEqual(300);
159
+ expect(field(held[0], "acquire_ms")).toBe(0);
160
+ expect(field(held[0], "hold_ms")).toBe(300);
123
161
  });
124
162
 
125
163
  test("an async background writer reports its hold under its own site", () => {
@@ -128,6 +166,8 @@ describe("sqlite writer diagnostics", () => {
128
166
  expect(held[0]).toContain("lane=background");
129
167
  expect(field(held[0], "acquire_ms")).toBeLessThan(250);
130
168
  expect(field(held[0], "hold_ms")).toBeGreaterThanOrEqual(300);
169
+ expect(field(held[0], "acquire_ms")).toBe(0);
170
+ expect(field(held[0], "hold_ms")).toBe(300);
131
171
  });
132
172
 
133
173
  test("a fast writer stays out of the log", () => {