@manyoars/oars 0.2.0 → 0.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 (241) hide show
  1. package/README.md +132 -28
  2. package/dist/api.js +1 -1
  3. package/dist/bin.js +6 -29
  4. package/dist/cli.d.ts +6 -3
  5. package/dist/cli.js +367 -88
  6. package/dist/commands/keys.d.ts +31 -0
  7. package/dist/commands/keys.js +65 -0
  8. package/dist/commands/login.js +2 -2
  9. package/dist/errors.d.ts +13 -13
  10. package/dist/errors.js +28 -11
  11. package/dist/gate/checks.d.ts +68 -0
  12. package/dist/gate/checks.js +584 -0
  13. package/dist/gate/command.d.ts +14 -0
  14. package/dist/gate/command.js +61 -0
  15. package/dist/gate/hook-fill-main.d.ts +1 -0
  16. package/dist/gate/hook-fill-main.js +16 -0
  17. package/dist/gate/hook-fill.d.ts +38 -0
  18. package/dist/gate/hook-fill.js +94 -0
  19. package/dist/gate/hook-main.d.ts +1 -0
  20. package/dist/gate/hook-main.js +35 -0
  21. package/dist/gate/hook-process.d.ts +14 -0
  22. package/dist/gate/hook-process.js +64 -0
  23. package/dist/gate/hook.d.ts +46 -0
  24. package/dist/gate/hook.js +256 -0
  25. package/dist/gate/install.d.ts +25 -0
  26. package/dist/gate/install.js +126 -0
  27. package/dist/gate/local-bin.d.ts +8 -0
  28. package/dist/gate/local-bin.js +36 -0
  29. package/dist/gate/notes.d.ts +19 -0
  30. package/dist/gate/notes.js +27 -0
  31. package/dist/gate/novelty.d.ts +35 -0
  32. package/dist/gate/novelty.js +99 -0
  33. package/dist/gate/policy.d.ts +25 -0
  34. package/dist/gate/policy.js +73 -0
  35. package/dist/gate/record.d.ts +120 -0
  36. package/dist/gate/record.js +125 -0
  37. package/dist/gate/registry-state.d.ts +10 -0
  38. package/dist/gate/registry-state.js +30 -0
  39. package/dist/gate/registry.d.ts +56 -0
  40. package/dist/gate/registry.js +94 -0
  41. package/dist/gate/replay.d.ts +20 -0
  42. package/dist/gate/replay.js +257 -0
  43. package/dist/gate/report.d.ts +50 -0
  44. package/dist/gate/report.js +89 -0
  45. package/dist/gate/scope.d.ts +7 -0
  46. package/dist/gate/scope.js +27 -0
  47. package/dist/gate/transcripts.d.ts +68 -0
  48. package/dist/gate/transcripts.js +232 -0
  49. package/dist/hook-bin.d.ts +1 -0
  50. package/dist/hook-bin.js +25 -0
  51. package/dist/issuer.d.ts +7 -1
  52. package/dist/issuer.js +18 -1
  53. package/dist/main.d.ts +1 -0
  54. package/dist/main.js +64 -0
  55. package/dist/oidc.js +1 -1
  56. package/dist/sessions/actions.d.ts +35 -0
  57. package/dist/sessions/actions.js +351 -0
  58. package/dist/sessions/allow.d.ts +14 -0
  59. package/dist/sessions/allow.js +51 -0
  60. package/dist/sessions/archive.d.ts +11 -0
  61. package/dist/sessions/archive.js +38 -0
  62. package/dist/sessions/audit.d.ts +42 -0
  63. package/dist/sessions/audit.js +104 -0
  64. package/dist/sessions/baseline.d.ts +62 -0
  65. package/dist/sessions/baseline.js +188 -0
  66. package/dist/sessions/capture-runtime.d.ts +10 -0
  67. package/dist/sessions/capture-runtime.js +12 -0
  68. package/dist/sessions/cell-increments.d.ts +43 -0
  69. package/dist/sessions/cell-increments.js +209 -0
  70. package/dist/sessions/cells.d.ts +79 -0
  71. package/dist/sessions/cells.js +181 -0
  72. package/dist/sessions/codex.d.ts +53 -0
  73. package/dist/sessions/codex.js +679 -0
  74. package/dist/sessions/command.d.ts +117 -0
  75. package/dist/sessions/command.js +373 -0
  76. package/dist/sessions/copilot.d.ts +40 -0
  77. package/dist/sessions/copilot.js +432 -0
  78. package/dist/sessions/cursor.d.ts +24 -0
  79. package/dist/sessions/cursor.js +324 -0
  80. package/dist/sessions/derive.d.ts +57 -0
  81. package/dist/sessions/derive.js +320 -0
  82. package/dist/sessions/exclusion.d.ts +81 -0
  83. package/dist/sessions/exclusion.js +353 -0
  84. package/dist/sessions/extract.d.ts +179 -0
  85. package/dist/sessions/extract.js +1774 -0
  86. package/dist/sessions/fold.d.ts +17 -0
  87. package/dist/sessions/fold.js +71 -0
  88. package/dist/sessions/frame.d.ts +24 -0
  89. package/dist/sessions/frame.js +95 -0
  90. package/dist/sessions/gemini.d.ts +37 -0
  91. package/dist/sessions/gemini.js +613 -0
  92. package/dist/sessions/gold-arms.d.ts +67 -0
  93. package/dist/sessions/gold-arms.js +172 -0
  94. package/dist/sessions/gold.d.ts +197 -0
  95. package/dist/sessions/gold.js +463 -0
  96. package/dist/sessions/graph.d.ts +12 -0
  97. package/dist/sessions/graph.js +34 -0
  98. package/dist/sessions/grok.d.ts +36 -0
  99. package/dist/sessions/grok.js +421 -0
  100. package/dist/sessions/hook-calls.d.ts +3 -0
  101. package/dist/sessions/hook-calls.js +47 -0
  102. package/dist/sessions/hook-index.d.ts +53 -0
  103. package/dist/sessions/hook-index.js +157 -0
  104. package/dist/sessions/hooks.d.ts +13 -0
  105. package/dist/sessions/hooks.js +170 -0
  106. package/dist/sessions/identity.d.ts +22 -0
  107. package/dist/sessions/identity.js +101 -0
  108. package/dist/sessions/install.d.ts +54 -0
  109. package/dist/sessions/install.js +201 -0
  110. package/dist/sessions/lineage.d.ts +19 -0
  111. package/dist/sessions/lineage.js +118 -0
  112. package/dist/sessions/mcp.d.ts +6 -0
  113. package/dist/sessions/mcp.js +153 -0
  114. package/dist/sessions/merkle.d.ts +7 -0
  115. package/dist/sessions/merkle.js +28 -0
  116. package/dist/sessions/motif.d.ts +43 -0
  117. package/dist/sessions/motif.js +59 -0
  118. package/dist/sessions/package-resolve.d.ts +30 -0
  119. package/dist/sessions/package-resolve.js +183 -0
  120. package/dist/sessions/pending.d.ts +34 -0
  121. package/dist/sessions/pending.js +142 -0
  122. package/dist/sessions/pool-command.d.ts +44 -0
  123. package/dist/sessions/pool-command.js +542 -0
  124. package/dist/sessions/pool-delta.d.ts +54 -0
  125. package/dist/sessions/pool-delta.js +190 -0
  126. package/dist/sessions/pool-outbox.d.ts +63 -0
  127. package/dist/sessions/pool-outbox.js +260 -0
  128. package/dist/sessions/pool-print.d.ts +26 -0
  129. package/dist/sessions/pool-print.js +68 -0
  130. package/dist/sessions/pool-wire.d.ts +225 -0
  131. package/dist/sessions/pool-wire.js +620 -0
  132. package/dist/sessions/pool.d.ts +32 -0
  133. package/dist/sessions/pool.js +164 -0
  134. package/dist/sessions/projection.d.ts +75 -0
  135. package/dist/sessions/projection.js +165 -0
  136. package/dist/sessions/public-vocabulary.d.ts +73 -0
  137. package/dist/sessions/public-vocabulary.js +302 -0
  138. package/dist/sessions/query.d.ts +111 -0
  139. package/dist/sessions/query.js +246 -0
  140. package/dist/sessions/read-adoption.d.ts +2 -0
  141. package/dist/sessions/read-adoption.js +27 -0
  142. package/dist/sessions/read-call-shape.d.ts +2 -0
  143. package/dist/sessions/read-call-shape.js +25 -0
  144. package/dist/sessions/read-change.d.ts +2 -0
  145. package/dist/sessions/read-change.js +30 -0
  146. package/dist/sessions/read-cost.d.ts +4 -0
  147. package/dist/sessions/read-cost.js +37 -0
  148. package/dist/sessions/read-day-move.d.ts +3 -0
  149. package/dist/sessions/read-day-move.js +65 -0
  150. package/dist/sessions/read-fit.d.ts +7 -0
  151. package/dist/sessions/read-fit.js +41 -0
  152. package/dist/sessions/read-interaction.d.ts +5 -0
  153. package/dist/sessions/read-interaction.js +62 -0
  154. package/dist/sessions/read-lead-lag.d.ts +15 -0
  155. package/dist/sessions/read-lead-lag.js +81 -0
  156. package/dist/sessions/read-live.d.ts +6 -0
  157. package/dist/sessions/read-live.js +38 -0
  158. package/dist/sessions/read-method.d.ts +2 -0
  159. package/dist/sessions/read-method.js +29 -0
  160. package/dist/sessions/read-next-step.d.ts +2 -0
  161. package/dist/sessions/read-next-step.js +35 -0
  162. package/dist/sessions/read-order.d.ts +54 -0
  163. package/dist/sessions/read-order.js +62 -0
  164. package/dist/sessions/read-readable.d.ts +8 -0
  165. package/dist/sessions/read-readable.js +24 -0
  166. package/dist/sessions/read-recovery.d.ts +20 -0
  167. package/dist/sessions/read-recovery.js +97 -0
  168. package/dist/sessions/read-rows.d.ts +13 -0
  169. package/dist/sessions/read-rows.js +47 -0
  170. package/dist/sessions/read-substitution.d.ts +5 -0
  171. package/dist/sessions/read-substitution.js +51 -0
  172. package/dist/sessions/read-support.d.ts +48 -0
  173. package/dist/sessions/read-support.js +73 -0
  174. package/dist/sessions/read-tool-day.d.ts +2 -0
  175. package/dist/sessions/read-tool-day.js +26 -0
  176. package/dist/sessions/read-version-boundary.d.ts +31 -0
  177. package/dist/sessions/read-version-boundary.js +95 -0
  178. package/dist/sessions/read.d.ts +79 -0
  179. package/dist/sessions/read.js +84 -0
  180. package/dist/sessions/recall-consequence.d.ts +45 -0
  181. package/dist/sessions/recall-consequence.js +179 -0
  182. package/dist/sessions/recall-object.d.ts +137 -0
  183. package/dist/sessions/recall-object.js +77 -0
  184. package/dist/sessions/recall-pool.d.ts +19 -0
  185. package/dist/sessions/recall-pool.js +96 -0
  186. package/dist/sessions/recall-push.d.ts +26 -0
  187. package/dist/sessions/recall-push.js +95 -0
  188. package/dist/sessions/recall-reads.d.ts +117 -0
  189. package/dist/sessions/recall-reads.js +410 -0
  190. package/dist/sessions/recall-render.d.ts +19 -0
  191. package/dist/sessions/recall-render.js +168 -0
  192. package/dist/sessions/redact.d.ts +6 -0
  193. package/dist/sessions/redact.js +54 -0
  194. package/dist/sessions/repo-root.d.ts +10 -0
  195. package/dist/sessions/repo-root.js +101 -0
  196. package/dist/sessions/schema.d.ts +644 -0
  197. package/dist/sessions/schema.js +148 -0
  198. package/dist/sessions/session-shape.d.ts +73 -0
  199. package/dist/sessions/session-shape.js +342 -0
  200. package/dist/sessions/sources.d.ts +50 -0
  201. package/dist/sessions/sources.js +159 -0
  202. package/dist/sessions/steps.d.ts +105 -0
  203. package/dist/sessions/steps.js +250 -0
  204. package/dist/sessions/store.d.ts +297 -0
  205. package/dist/sessions/store.js +1478 -0
  206. package/dist/sessions/strata.d.ts +26 -0
  207. package/dist/sessions/strata.js +77 -0
  208. package/dist/sessions/survival.d.ts +25 -0
  209. package/dist/sessions/survival.js +230 -0
  210. package/dist/sessions/verify.d.ts +53 -0
  211. package/dist/sessions/verify.js +286 -0
  212. package/dist/sessions/vocab-load.d.ts +26 -0
  213. package/dist/sessions/vocab-load.js +61 -0
  214. package/dist/sessions/vscode-copilot.d.ts +38 -0
  215. package/dist/sessions/vscode-copilot.js +477 -0
  216. package/package.json +4 -5
  217. package/vocab/crates.json +1 -0
  218. package/vocab/go.json +1 -0
  219. package/vocab/harness.json +1 -0
  220. package/vocab/homebrew.json +1 -0
  221. package/vocab/manifest.json +1 -0
  222. package/vocab/maven.json +1 -0
  223. package/vocab/models.json +1 -0
  224. package/vocab/npm.json +1 -0
  225. package/vocab/programs.json +1 -0
  226. package/vocab/pypi.json +1 -0
  227. package/vocab/rubygems.json +1 -0
  228. package/dist/commands/deploy-input.d.ts +0 -38
  229. package/dist/commands/deploy-input.js +0 -150
  230. package/dist/commands/deploy.d.ts +0 -22
  231. package/dist/commands/deploy.js +0 -124
  232. package/dist/commands/status.d.ts +0 -7
  233. package/dist/commands/status.js +0 -18
  234. package/dist/commands/templates.d.ts +0 -11
  235. package/dist/commands/templates.js +0 -32
  236. package/dist/commands/test-charge.d.ts +0 -11
  237. package/dist/commands/test-charge.js +0 -34
  238. package/dist/journal.d.ts +0 -26
  239. package/dist/journal.js +0 -58
  240. package/parity/deploy-plans/paid-api.json +0 -34
  241. package/parity/deploy-plans/per-task-agent.json +0 -34
@@ -0,0 +1,62 @@
1
+ import { type Harness, type PromptObservation, type SessionObservation, type ToolObservation } from './schema.js';
2
+ /** A week needs this many tool calls to be a unit or a baseline week (spec 5.4 item 1). */
3
+ export declare const BASELINE_MIN_CALLS = 1000;
4
+ /** A baseline needs this many other qualifying weeks (spec 5.4 item 1). */
5
+ export declare const BASELINE_MIN_WEEKS = 4;
6
+ /** F's bounds: a median distance at or under the low is F 1, at or over the high is F 0. */
7
+ export declare const FIT_LOW: Fraction;
8
+ export declare const FIT_HIGH: Fraction;
9
+ /**
10
+ * The fixed bin edges of the numeric dimensions: a value goes in the first bin whose edge it is under, else the last, so a value exactly on an edge
11
+ * lands in the upper bin. The cache-read share's edges are tenths (k / 10) and compare as integers: `cacheRead * 10 < k * input`.
12
+ */
13
+ export declare const DURATION_EDGES_MS: readonly number[];
14
+ export declare const WALL_EDGES_SEC: readonly number[];
15
+ export declare const CACHE_SHARE_TENTHS: readonly number[];
16
+ export declare const OUTPUT_PER_TURN_EDGES: readonly number[];
17
+ export declare const DIMENSIONS: readonly ["tool mix", "status", "durationMs", "wallSec", "cache-read share", "output per turn", "prompt class"];
18
+ export type Dimension = (typeof DIMENSIONS)[number];
19
+ /** The fields each record needs, so a caller passes the store's records and a test passes small ones. */
20
+ export type BaselineSession = Pick<SessionObservation, 'session' | 'tsFirst' | 'tools' | 'harness'>;
21
+ export type BaselineTool = Pick<ToolObservation, 'session' | 'status' | 'durationMs'>;
22
+ export type BaselinePrompt = Pick<PromptObservation, 'session' | 'wallSec' | 'cls' | 'turns' | 'usage'>;
23
+ export interface DimensionDistance {
24
+ dimension: Dimension;
25
+ distance: number;
26
+ }
27
+ /** One session's layer-2 result: `scored` with `m` and `f`, or `not run` with the reason and nothing scored. */
28
+ export interface SelfBaselineResult {
29
+ session: string;
30
+ harness: Harness;
31
+ /** The UTC Monday of the session's `tsFirst` (YYYY-MM-DD); null when `tsFirst` is null. */
32
+ week: string | null;
33
+ status: 'scored' | 'not run';
34
+ reason: string | null;
35
+ /** Tool calls in the session's week on its harness. */
36
+ calls: number;
37
+ /** The other qualifying weeks the baseline pools. */
38
+ baselineWeeks: number;
39
+ distances: DimensionDistance[];
40
+ m: number | null;
41
+ f: number | null;
42
+ }
43
+ /** An exact rational with a positive denominator: the distances, `m`, and `F` stay in this form until the one rounding at the end (spec 2.2, no float). */
44
+ export interface Fraction {
45
+ n: bigint;
46
+ d: bigint;
47
+ }
48
+ /** The UTC Monday 00:00 (epoch ms) of the week holding `ts`; null for a null or unparsable timestamp. */
49
+ export declare const weekOf: (ts: string | null) => number | null;
50
+ /** The median; the mean of the middle two for an even count. Never called with an empty list. */
51
+ export declare const medianOf: (values: readonly Fraction[]) => Fraction;
52
+ /** F = 1 - clamp((m - 1/10) / (2/5 - 1/10)), exact, rounded half-even to 4 places. */
53
+ export declare const fitOf: (m: Fraction) => number;
54
+ /**
55
+ * Every session's result. A session belongs wholly to the UTC week of its `tsFirst` and takes the F of its harness and week. A week qualifies at
56
+ * {@link BASELINE_MIN_CALLS} tool calls with its last UTC day no later than `asOf`; the baseline is a harness's other qualifying weeks, their counts
57
+ * added per bin so each week weighs what its calls weigh. A dimension with no counts on one side or both is dropped; `m` is the median of the
58
+ * rest. Mechanics only: no signature, no record content.
59
+ */
60
+ export declare const selfBaseline: (sessions: readonly BaselineSession[], tools: readonly BaselineTool[], prompts: readonly BaselinePrompt[], asOf: number) => SelfBaselineResult[];
61
+ /** A count line for the scored sessions, then one per reason a session was not run. Counts only. */
62
+ export declare const formatSelfBaseline: (results: readonly SelfBaselineResult[]) => string;
@@ -0,0 +1,188 @@
1
+ // Layer 2's self-baseline (spec 2.2): a session's week against the operator's own other weeks on the same harness. Local only: no pooled field,
2
+ // no network, no account step. The peer baseline waits for T2.
3
+ import { harnessOf } from './schema.js';
4
+ /** A week needs this many tool calls to be a unit or a baseline week (spec 5.4 item 1). */
5
+ export const BASELINE_MIN_CALLS = 1000;
6
+ /** A baseline needs this many other qualifying weeks (spec 5.4 item 1). */
7
+ export const BASELINE_MIN_WEEKS = 4;
8
+ /** F's bounds: a median distance at or under the low is F 1, at or over the high is F 0. */
9
+ export const FIT_LOW = { n: 1n, d: 10n };
10
+ export const FIT_HIGH = { n: 2n, d: 5n };
11
+ /**
12
+ * The fixed bin edges of the numeric dimensions: a value goes in the first bin whose edge it is under, else the last, so a value exactly on an edge
13
+ * lands in the upper bin. The cache-read share's edges are tenths (k / 10) and compare as integers: `cacheRead * 10 < k * input`.
14
+ */
15
+ export const DURATION_EDGES_MS = [100, 1000, 10_000, 60_000];
16
+ export const WALL_EDGES_SEC = [5, 30, 120, 600];
17
+ export const CACHE_SHARE_TENTHS = [1, 2, 3, 4, 5, 6, 7, 8, 9];
18
+ export const OUTPUT_PER_TURN_EDGES = [50, 200, 500, 1000, 2000];
19
+ const DAY_MS = 86_400_000;
20
+ const WEEK_MS = 7 * DAY_MS;
21
+ export const DIMENSIONS = ['tool mix', 'status', 'durationMs', 'wallSec', 'cache-read share', 'output per turn', 'prompt class'];
22
+ /** The UTC Monday 00:00 (epoch ms) of the week holding `ts`; null for a null or unparsable timestamp. */
23
+ export const weekOf = (ts) => {
24
+ const at = ts === null ? Number.NaN : Date.parse(ts);
25
+ if (Number.isNaN(at))
26
+ return null;
27
+ const day = Math.floor(at / DAY_MS) * DAY_MS;
28
+ // 1970-01-01 was a Thursday: three days after the Monday of its week.
29
+ return day - ((((day / DAY_MS) + 3) % 7) * DAY_MS);
30
+ };
31
+ const compare = (a, b) => {
32
+ const diff = a.n * b.d - b.n * a.d;
33
+ return diff < 0n ? -1 : diff > 0n ? 1 : 0;
34
+ };
35
+ const minus = (a, b) => ({ n: a.n * b.d - b.n * a.d, d: a.d * b.d });
36
+ /** Rounds half-even to 4 places, once, at the end (spec 3.2). A value is never negative here. */
37
+ const roundTo4 = (value) => {
38
+ const scaled = value.n * 10000n;
39
+ const quotient = scaled / value.d;
40
+ const twiceRest = 2n * (scaled - quotient * value.d);
41
+ const up = twiceRest > value.d || (twiceRest === value.d && quotient % 2n === 1n);
42
+ return Number(up ? quotient + 1n : quotient) / 10_000;
43
+ };
44
+ /** The median; the mean of the middle two for an even count. Never called with an empty list. */
45
+ export const medianOf = (values) => {
46
+ const sorted = [...values].sort(compare);
47
+ const mid = sorted.length >> 1;
48
+ const upper = sorted[mid] ?? { n: 0n, d: 1n };
49
+ if (sorted.length % 2 === 1)
50
+ return upper;
51
+ const lower = sorted[mid - 1] ?? { n: 0n, d: 1n };
52
+ return { n: lower.n * upper.d + upper.n * lower.d, d: 2n * lower.d * upper.d };
53
+ };
54
+ /** F = 1 - clamp((m - 1/10) / (2/5 - 1/10)), exact, rounded half-even to 4 places. */
55
+ export const fitOf = (m) => {
56
+ const span = minus(FIT_HIGH, FIT_LOW);
57
+ if (compare(m, FIT_LOW) <= 0)
58
+ return 1;
59
+ if (compare(m, FIT_HIGH) >= 0)
60
+ return 0;
61
+ const over = minus(m, FIT_LOW);
62
+ // 1 - (over.n / over.d) / (span.n / span.d) = (over.d * span.n - over.n * span.d) / (over.d * span.n)
63
+ return roundTo4({ n: over.d * span.n - over.n * span.d, d: over.d * span.n });
64
+ };
65
+ /** The first bin whose edge the value is under, else the last: `value < edge * scale` with integers, so the comparison is exact. */
66
+ const binOf = (value, edges, scale = 1) => {
67
+ const index = edges.findIndex((edge) => value < edge * scale);
68
+ return String(index === -1 ? edges.length : index);
69
+ };
70
+ const add = (histogram, key, count = 1) => {
71
+ histogram.set(key, (histogram.get(key) ?? 0) + count);
72
+ };
73
+ const emptyHistograms = () => new Map(DIMENSIONS.map((dimension) => [dimension, new Map()]));
74
+ const histogramOf = (histograms, dimension) => histograms.get(dimension) ?? new Map();
75
+ const callsOf = (session) => Object.values(session.tools).reduce((total, count) => total + count, 0);
76
+ const addInto = (into, from) => {
77
+ for (const dimension of DIMENSIONS)
78
+ for (const [key, count] of histogramOf(from, dimension))
79
+ add(histogramOf(into, dimension), key, count);
80
+ };
81
+ const total = (histogram) => [...histogram.values()].reduce((sum, count) => sum + count, 0);
82
+ /** Half the sum of absolute differences of the two normalized histograms, over integer counts: sum |a * sumB - b * sumA| / (2 * sumA * sumB). */
83
+ const distanceBetween = (a, b) => {
84
+ const sumA = BigInt(total(a));
85
+ const sumB = BigInt(total(b));
86
+ let sum = 0n;
87
+ for (const key of new Set([...a.keys(), ...b.keys()])) {
88
+ const diff = BigInt(a.get(key) ?? 0) * sumB - BigInt(b.get(key) ?? 0) * sumA;
89
+ sum += diff < 0n ? -diff : diff;
90
+ }
91
+ return { n: sum, d: 2n * sumA * sumB };
92
+ };
93
+ /** One pass, pushing into each session's list: a copy per row is quadratic in a session's rows. */
94
+ const bySession = (records) => {
95
+ const grouped = new Map();
96
+ for (const record of records) {
97
+ const list = grouped.get(record.session);
98
+ if (list === undefined)
99
+ grouped.set(record.session, [record]);
100
+ else
101
+ list.push(record);
102
+ }
103
+ return grouped;
104
+ };
105
+ const isoDay = (ms) => new Date(ms).toISOString().slice(0, 10);
106
+ /**
107
+ * Every session's result. A session belongs wholly to the UTC week of its `tsFirst` and takes the F of its harness and week. A week qualifies at
108
+ * {@link BASELINE_MIN_CALLS} tool calls with its last UTC day no later than `asOf`; the baseline is a harness's other qualifying weeks, their counts
109
+ * added per bin so each week weighs what its calls weigh. A dimension with no counts on one side or both is dropped; `m` is the median of the
110
+ * rest. Mechanics only: no signature, no record content.
111
+ */
112
+ export const selfBaseline = (sessions, tools, prompts, asOf) => {
113
+ const toolsOf = bySession(tools);
114
+ const promptsOf = bySession(prompts);
115
+ const groups = new Map();
116
+ const keyOf = new Map();
117
+ for (const session of sessions) {
118
+ const start = weekOf(session.tsFirst);
119
+ if (start === null) {
120
+ keyOf.set(session.session, null);
121
+ continue;
122
+ }
123
+ const harness = harnessOf(session);
124
+ const key = `${harness}:${String(start)}`;
125
+ keyOf.set(session.session, key);
126
+ const group = groups.get(key) ?? { harness, start, calls: 0, histograms: emptyHistograms() };
127
+ groups.set(key, group);
128
+ group.calls += callsOf(session);
129
+ for (const [tool, count] of Object.entries(session.tools))
130
+ add(histogramOf(group.histograms, 'tool mix'), tool, count);
131
+ for (const tool of toolsOf.get(session.session) ?? []) {
132
+ add(histogramOf(group.histograms, 'status'), tool.status);
133
+ if (tool.durationMs !== null)
134
+ add(histogramOf(group.histograms, 'durationMs'), binOf(tool.durationMs, DURATION_EDGES_MS));
135
+ }
136
+ for (const prompt of promptsOf.get(session.session) ?? []) {
137
+ add(histogramOf(group.histograms, 'prompt class'), prompt.cls);
138
+ if (prompt.wallSec !== null)
139
+ add(histogramOf(group.histograms, 'wallSec'), binOf(prompt.wallSec, WALL_EDGES_SEC));
140
+ const input = prompt.usage.input + prompt.usage.cacheWrite + prompt.usage.cacheRead;
141
+ if (input > 0)
142
+ add(histogramOf(group.histograms, 'cache-read share'), binOf(prompt.usage.cacheRead * 10, CACHE_SHARE_TENTHS, input));
143
+ if (prompt.turns > 0)
144
+ add(histogramOf(group.histograms, 'output per turn'), binOf(prompt.usage.output, OUTPUT_PER_TURN_EDGES, prompt.turns));
145
+ }
146
+ }
147
+ const scored = new Map();
148
+ for (const [key, group] of groups) {
149
+ const base = { calls: group.calls, baselineWeeks: 0, distances: [], m: null, f: null };
150
+ if (group.calls < BASELINE_MIN_CALLS) {
151
+ scored.set(key, { ...base, status: 'not run', reason: `week under ${String(BASELINE_MIN_CALLS)} tool calls` });
152
+ continue;
153
+ }
154
+ const others = [...groups.values()].filter((other) => other.harness === group.harness && other.start !== group.start && other.calls >= BASELINE_MIN_CALLS && other.start + WEEK_MS - DAY_MS <= Math.floor(asOf / DAY_MS) * DAY_MS);
155
+ if (others.length < BASELINE_MIN_WEEKS) {
156
+ scored.set(key, { ...base, baselineWeeks: others.length, status: 'not run', reason: `under ${String(BASELINE_MIN_WEEKS)} qualifying baseline weeks` });
157
+ continue;
158
+ }
159
+ const pooled = emptyHistograms();
160
+ for (const other of others)
161
+ addInto(pooled, other.histograms);
162
+ const exact = DIMENSIONS.filter((dimension) => total(histogramOf(group.histograms, dimension)) > 0 && total(histogramOf(pooled, dimension)) > 0).map((dimension) => ({ dimension, distance: distanceBetween(histogramOf(group.histograms, dimension), histogramOf(pooled, dimension)) }));
163
+ // The tool mix has counts on both sides whenever the week and the baseline each hold 1,000 calls, so at least one dimension always remains.
164
+ const m = medianOf(exact.map((entry) => entry.distance));
165
+ const distances = exact.map((entry) => ({ dimension: entry.dimension, distance: roundTo4(entry.distance) }));
166
+ scored.set(key, { ...base, baselineWeeks: others.length, distances, m: roundTo4(m), f: fitOf(m), status: 'scored', reason: null });
167
+ }
168
+ return sessions.map((session) => {
169
+ const key = keyOf.get(session.session) ?? null;
170
+ const harness = harnessOf(session);
171
+ const found = key === null ? undefined : scored.get(key);
172
+ if (key === null || found === undefined)
173
+ return { session: session.session, harness, week: null, status: 'not run', reason: 'no first timestamp', calls: 0, baselineWeeks: 0, distances: [], m: null, f: null };
174
+ return { session: session.session, harness, week: isoDay(groups.get(key)?.start ?? 0), ...found };
175
+ });
176
+ };
177
+ /** A count line for the scored sessions, then one per reason a session was not run. Counts only. */
178
+ export const formatSelfBaseline = (results) => {
179
+ const reasons = new Map();
180
+ for (const entry of results)
181
+ if (entry.reason !== null)
182
+ add(reasons, entry.reason);
183
+ return [
184
+ `${String(results.length)} sessions against their own other weeks (layer 2, local; a low F lowers W and rejects nothing)`,
185
+ ` scored: ${String(results.filter((entry) => entry.status === 'scored').length)}`,
186
+ ...[...reasons].sort().map(([reason, count]) => ` not run, ${reason}: ${String(count)}`),
187
+ ].join('\n');
188
+ };
@@ -0,0 +1,10 @@
1
+ /** The `version` in a package.json text, or `0.0.0` when it has none. */
2
+ export declare const versionIn: (manifestText: string) => string;
3
+ /** This package's version: `package.json` sits two folders above both `src/sessions/` and `dist/sessions/`. */
4
+ export declare const oarsVersion: () => string;
5
+ export interface CaptureRuntime {
6
+ oars: string;
7
+ node: string;
8
+ os: string;
9
+ }
10
+ export declare const captureRuntime: () => CaptureRuntime;
@@ -0,0 +1,12 @@
1
+ // What a capture records about the machine it ran on (`env` on the session record, class P): the oars package version, Node.js, and OS.
2
+ // Names and versions only; a library version is recorded only when a transcript states it, and none is read here.
3
+ import { readFileSync } from 'node:fs';
4
+ /** The `version` in a package.json text, or `0.0.0` when it has none. */
5
+ export const versionIn = (manifestText) => {
6
+ const manifest = JSON.parse(manifestText);
7
+ const version = typeof manifest === 'object' && manifest !== null ? Reflect.get(manifest, 'version') : undefined;
8
+ return typeof version === 'string' ? version : '0.0.0';
9
+ };
10
+ /** This package's version: `package.json` sits two folders above both `src/sessions/` and `dist/sessions/`. */
11
+ export const oarsVersion = () => versionIn(readFileSync(new URL('../../package.json', import.meta.url), 'utf8'));
12
+ export const captureRuntime = () => ({ oars: oarsVersion(), node: process.version, os: process.platform });
@@ -0,0 +1,43 @@
1
+ import { type CellSet, type Grain } from './cells.js';
2
+ import type { Motif } from './motif.js';
3
+ import { type SessionSkeleton, type ShellReader } from './pool-wire.js';
4
+ import { type RecallGraph } from './recall-consequence.js';
5
+ import type { Store } from './store.js';
6
+ export interface CellRow {
7
+ key: string;
8
+ harnessBucket: string;
9
+ motifId: string;
10
+ frameBucket: string;
11
+ grain: Grain;
12
+ day: string;
13
+ exposed: number;
14
+ sessions: number;
15
+ ok: number;
16
+ failed: number;
17
+ tokens: number;
18
+ durationMs: number;
19
+ }
20
+ export interface StratumRow {
21
+ key: string;
22
+ dimension: string;
23
+ value: string;
24
+ sessions: number;
25
+ }
26
+ /** What one batch adds to the index: its cells, the motifs they name, and rule 3's strata. */
27
+ export interface Increments {
28
+ cells: Map<string, CellRow>;
29
+ motifs: Motif[];
30
+ strata: Map<string, StratumRow>;
31
+ }
32
+ /**
33
+ * Rule 5: exact tokens and durations never reach `index`. A cell counts a bucketed value as the bucket's lowest value, so a derivation reads only what a deposit carries, and a
34
+ * summed window is a sum of those. A value already at a bucket's floor stays there, which is why a skeleton's store and a local store of the same session derive the same cells.
35
+ * Rules 1 and 2: a window names a shell step by its program and subcommand alone, and each flag and each pull of it adds a one-step cell of its own.
36
+ */
37
+ export declare const cellsOf: (store: Store, graph?: RecallGraph) => CellSet;
38
+ /** A local store with each shell step read as a deposit reads it (`projectedSteps`), so its named motifs are the ones the pool holds. */
39
+ export declare const projectedStore: (store: Store, read: ShellReader) => Store;
40
+ /** One account's deposits as a store. A session's id is its skeleton's, which the client hashes per account, so it joins nothing across accounts. */
41
+ export declare const storeOf: (skeletons: readonly SessionSkeleton[]) => Store;
42
+ /** Whole cells and strata, summed over a batch's own sessions: the same cells a store of that batch alone derives. */
43
+ export declare const incrementsOf: (skeleton: SessionSkeleton) => Increments;
@@ -0,0 +1,209 @@
1
+ // The cell computation a deposit and a local read share (pool vocabulary spec Section 2, "the deposit form", and Section 4, rules 1 to 6). The pool's ingest turns a batch's skeleton into
2
+ // increments with `incrementsOf`, and a local read derives its cells with `cellsOf` over the store it holds, projected the way a deposit projects it (`projectedStore`). Both end in the
3
+ // one `cellsOf`, so a session gives the same cells at the pool and on the machine. This file reads no file system: the pool's Worker bundles it.
4
+ import { bucketFields, deriveCells, ENVIRONMENT_BUCKET, isEnvironmentCell } from './cells.js';
5
+ import { bucketOf, floorOf, projectedSteps } from './pool-wire.js';
6
+ import { NO_RECALLS } from './recall-consequence.js';
7
+ import { harnessOf } from './schema.js';
8
+ const floorTokens = (value) => floorOf('tokens', bucketOf('tokens', value));
9
+ const floorUsage = ({ input, cacheWrite, cacheRead, output, thinking }) => ({
10
+ input: floorTokens(input),
11
+ cacheWrite: floorTokens(cacheWrite),
12
+ cacheRead: floorTokens(cacheRead),
13
+ output: floorTokens(output),
14
+ thinking: floorTokens(thinking),
15
+ });
16
+ const floorPrompt = (prompt) => ({
17
+ ...prompt,
18
+ errors: floorOf('count', bucketOf('count', prompt.errors)),
19
+ wallSec: prompt.wallSec === null ? null : floorOf('seconds', bucketOf('seconds', prompt.wallSec)),
20
+ usage: floorUsage(prompt.usage),
21
+ });
22
+ const floorTool = (tool) => ({
23
+ ...tool,
24
+ durationMs: tool.durationMs === null ? null : floorOf('ms', bucketOf('ms', tool.durationMs)),
25
+ steps: tool.steps?.map((step) => ({
26
+ ...step,
27
+ cost: { tokens: step.cost.tokens === null ? null : floorTokens(step.cost.tokens), durationMs: step.cost.durationMs === null ? null : floorOf('ms', bucketOf('ms', step.cost.durationMs)) },
28
+ })),
29
+ });
30
+ const flooredStore = (store) => ({ ...store, prompts: store.prompts.map(floorPrompt), tools: store.tools.map(floorTool) });
31
+ /** A step's shell identity without the two lists it carries: a named motif keys on the program and its subcommand, and each flag and each pull is a cell of its own (rule 1). */
32
+ const withoutLists = (step) => (step.identity !== null && 'program' in step.identity ? { ...step, identity: { program: step.identity.program, sub: step.identity.sub, flags: [] } } : step);
33
+ /** One step per flag and one per pull of each shell step, each holding that one name and nothing else (rule 2: no cell has two set-valued dimensions). */
34
+ const marginalSteps = (step) => {
35
+ if (step.identity === null || !('program' in step.identity))
36
+ return [];
37
+ const { program, sub, flags, pulls } = step.identity;
38
+ return [...flags.map((flag) => ({ ...step, identity: { program, sub, flags: [flag] } })), ...(pulls ?? []).map((pull) => ({ ...step, identity: { program, sub, flags: [], pulls: [pull] } }))];
39
+ };
40
+ /** The tools of a store as one-step calls, one per flag and per pull: read at window length 1, each is one marginal cell. */
41
+ const marginalStore = (store) => ({ ...store, tools: store.tools.flatMap((tool) => (tool.steps ?? []).flatMap(marginalSteps).map((marginal) => ({ ...tool, steps: [marginal] }))) });
42
+ /**
43
+ * Rule 5: exact tokens and durations never reach `index`. A cell counts a bucketed value as the bucket's lowest value, so a derivation reads only what a deposit carries, and a
44
+ * summed window is a sum of those. A value already at a bucket's floor stays there, which is why a skeleton's store and a local store of the same session derive the same cells.
45
+ * Rules 1 and 2: a window names a shell step by its program and subcommand alone, and each flag and each pull of it adds a one-step cell of its own.
46
+ */
47
+ export const cellsOf = (store, graph = NO_RECALLS) => {
48
+ const floored = flooredStore(store);
49
+ const whole = deriveCells({ ...floored, tools: floored.tools.map((tool) => (tool.steps === undefined ? tool : { ...tool, steps: tool.steps.map(withoutLists) })) }, graph);
50
+ // Only the one-step named motifs of the marginal pass that hold a shell identity (a recall step the graph places there has none, and `whole` already counts it): its prompts, its shape motifs, and its longer windows repeat or splice what `whole` holds.
51
+ const marginal = deriveCells(marginalStore(floored), graph);
52
+ const named = new Set(marginal.motifs.filter(({ level, steps }) => level === 'named' && steps.length === 1 && steps[0]?.identity !== null).map(({ motifId }) => motifId));
53
+ const motifs = new Map([...whole.motifs, ...marginal.motifs.filter(({ motifId }) => named.has(motifId))].map((motif) => [motif.motifId, motif]));
54
+ const order = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
55
+ const cells = [...whole.cells, ...marginal.cells.filter(({ motifId }) => named.has(motifId))].sort((a, b) => order(a.motifId, b.motifId) || order(a.frameBucket, b.frameBucket) || order(a.grain, b.grain) || order(a.day, b.day) || Number(a.exposed) - Number(b.exposed));
56
+ return { cells, motifs: [...motifs.values()].sort((a, b) => order(a.motifId, b.motifId)) };
57
+ };
58
+ /** A local store with each shell step read as a deposit reads it (`projectedSteps`), so its named motifs are the ones the pool holds. */
59
+ export const projectedStore = (store, read) => {
60
+ const harnesses = new Map([...store.sessions, ...store.subagents].map((session) => [session.session, harnessOf(session)]));
61
+ return { ...store, tools: store.tools.map((tool) => (tool.steps === undefined ? tool : { ...tool, steps: projectedSteps(tool, read, harnesses.get(tool.session) ?? 'claude-code') })) };
62
+ };
63
+ const NO_USAGE = { input: 0, cacheWrite: 0, cacheRead: 0, output: 0, thinking: 0 };
64
+ /** A skeleton's transcript as the record `deriveCells` reads. Everything a skeleton leaves out stays empty, so no derivation here can come to depend on it unnoticed. */
65
+ const sessionRecord = (id, parent, { harness, frame }) => ({
66
+ kind: 'session',
67
+ cv: '',
68
+ sid: '',
69
+ session: id,
70
+ project: '',
71
+ parent,
72
+ agentType: null,
73
+ spawnedBy: null,
74
+ spawnedIn: null,
75
+ rev: 1,
76
+ at: '',
77
+ bytes: 0,
78
+ lines: 0,
79
+ badLines: 0,
80
+ userTurns: 0,
81
+ assistantTurns: 0,
82
+ tsFirst: null,
83
+ tsLast: null,
84
+ versions: [],
85
+ models: {},
86
+ branches: [],
87
+ tools: {},
88
+ files: [],
89
+ fileHashes: [],
90
+ repo: null,
91
+ urls: [],
92
+ searches: [],
93
+ bashCmds: 0,
94
+ tldrs: [],
95
+ confidences: [],
96
+ compactSummaries: 0,
97
+ promptFingerprint: null,
98
+ env: { harness: frame.harnessVersion ?? '', oars: '', node: '', os: frame.platform },
99
+ costUsd: null,
100
+ usage: NO_USAGE,
101
+ harness,
102
+ frame,
103
+ });
104
+ const promptRecords = (id, { prompts }) => prompts.map(({ cls, errors, wallSec, usage, explore, frame }, index) => ({
105
+ kind: 'prompt',
106
+ v: '',
107
+ session: id,
108
+ rev: 1,
109
+ index,
110
+ ts: null,
111
+ wallSec: wallSec === null ? null : floorOf('seconds', wallSec),
112
+ head: '',
113
+ cls,
114
+ tools: [],
115
+ shape: '',
116
+ errors: floorOf('count', errors),
117
+ thinking: 0,
118
+ sideTools: 0,
119
+ turns: 0,
120
+ model: frame.model ?? '',
121
+ efforts: frame.effort === null ? [] : [frame.effort],
122
+ skills: [],
123
+ usage: {
124
+ input: floorOf('tokens', usage.input),
125
+ cacheWrite: floorOf('tokens', usage.cacheWrite),
126
+ cacheRead: floorOf('tokens', usage.cacheRead),
127
+ output: floorOf('tokens', usage.output),
128
+ thinking: floorOf('tokens', usage.thinking),
129
+ },
130
+ recovery: [],
131
+ // The lowest value of the bucket, as every other bucketed value counts; a prompt that sent none counts none.
132
+ explore: explore === null ? 0 : floorOf('calls', explore),
133
+ act: 0,
134
+ bigrams: {},
135
+ contextFingerprint: null,
136
+ frame,
137
+ }));
138
+ const toolRecords = (id, main, { tools }) => tools.map(({ side, prompt, status, durationMs, steps }, n) => ({
139
+ kind: 'tool',
140
+ cv: '',
141
+ sid: '',
142
+ session: id,
143
+ rev: 1,
144
+ parent: main,
145
+ agentType: null,
146
+ n,
147
+ ts: null,
148
+ tool: steps[0]?.native ?? '',
149
+ status,
150
+ target: null,
151
+ targetBase: null,
152
+ keys: [],
153
+ durationMs,
154
+ toolUseId: '',
155
+ harnessVersion: null,
156
+ side,
157
+ prompt,
158
+ steps: steps.map(({ action, native, identity, status: reported, parent, tokens, ms }, seg) => ({
159
+ action,
160
+ native,
161
+ identity,
162
+ status: reported,
163
+ parent,
164
+ seg,
165
+ edges: [],
166
+ cost: { tokens: tokens === null ? null : floorOf('tokens', tokens), durationMs: ms === null ? null : floorOf('ms', ms) },
167
+ n,
168
+ frameId: '',
169
+ })),
170
+ }));
171
+ /** One account's deposits as a store. A session's id is its skeleton's, which the client hashes per account, so it joins nothing across accounts. */
172
+ export const storeOf = (skeletons) => {
173
+ const store = { sessions: [], prompts: [], errors: [], subagents: [], tools: [], newerCaptures: 0 };
174
+ for (const { id, transcripts } of skeletons) {
175
+ const nameOf = (index) => `${id}#${String(index)}`;
176
+ const main = nameOf(0);
177
+ transcripts.forEach((transcript, index) => {
178
+ const session = nameOf(index);
179
+ (index === 0 ? store.sessions : store.subagents).push(sessionRecord(session, index === 0 ? null : main, transcript));
180
+ store.prompts.push(...promptRecords(session, transcript));
181
+ store.tools.push(...toolRecords(session, main, transcript));
182
+ });
183
+ }
184
+ return store;
185
+ };
186
+ /** Whole cells and strata, summed over a batch's own sessions: the same cells a store of that batch alone derives. */
187
+ export const incrementsOf = (skeleton) => {
188
+ const derived = cellsOf(storeOf([skeleton]));
189
+ const cells = new Map();
190
+ for (const cell of derived.cells) {
191
+ const exposed = cell.exposed ? 1 : 0;
192
+ const key = `${cell.motifId}/${cell.frameBucket}/${cell.grain}/${cell.day}/${String(exposed)}`;
193
+ cells.set(key, { key, harnessBucket: isEnvironmentCell(cell) ? ENVIRONMENT_BUCKET : bucketFields(cell.frameBucket).harnessBucket, motifId: cell.motifId, frameBucket: cell.frameBucket, grain: cell.grain, day: cell.day, exposed, sessions: cell.sessions, ok: cell.ok, failed: cell.failed, tokens: cell.tokens, durationMs: cell.durationMs });
194
+ }
195
+ // A session counts once per value, so each value is one session of this batch.
196
+ const strata = new Map();
197
+ const frames = skeleton.transcripts.flatMap(({ frame, prompts }) => [frame, ...prompts.map((prompt) => prompt.frame)]);
198
+ const named = [
199
+ ...frames.flatMap(({ stack }) => stack.languages.map((value) => ['language', value])),
200
+ ...frames.flatMap(({ stack }) => stack.frameworks.map((value) => ['framework', value])),
201
+ ...frames.flatMap(({ stack }) => (stack.size === null ? [] : [['repoFiles', stack.size]])),
202
+ // The two pairs (rule 3): each is a stratum of its own, never a cell and never keyed by the stack's other values. A language with no share, and a prompt with no count, make none.
203
+ ...frames.flatMap(({ stack }) => stack.languageShares.map(({ language, languageShare }) => ['languageShare', `${language}/${languageShare}`])),
204
+ ...skeleton.transcripts.flatMap(({ prompts }) => prompts.flatMap(({ explore, frame }) => (explore === null || frame.stack.size === null ? [] : [['repoFilesExplore', `${frame.stack.size}/${String(explore)}`]]))),
205
+ ];
206
+ for (const [dimension, value] of named)
207
+ strata.set(`${dimension}/${value}`, { key: `${dimension}/${value}`, dimension, value, sessions: 1 });
208
+ return { cells, motifs: derived.motifs, strata };
209
+ };
@@ -0,0 +1,79 @@
1
+ import { type Motif } from './motif.js';
2
+ import { type RecallGraph } from './recall-consequence.js';
3
+ import { type ErrorObservation, type Frame } from './schema.js';
4
+ import type { Store } from './store.js';
5
+ /** Rule 6: an `index` cell is weekly, except a live cell, which stays at the UTC day. */
6
+ export type Grain = 'day' | 'week';
7
+ export interface Cell {
8
+ motifId: string;
9
+ /** `harness/versionBucket/model`, public names (`frameBucketOf`). Effort is not in it (rule 3). */
10
+ frameBucket: string;
11
+ /** What `day` names: a UTC day, or a UTC week. A read takes the grain it answers from and never the other. */
12
+ grain: Grain;
13
+ /** The UTC day of the step's frame for a day cell, the Monday of its UTC week for a week cell, or `unknown`. */
14
+ day: string;
15
+ /** Distinct main sessions: a subagent's steps count under the session that spawned it. */
16
+ sessions: number;
17
+ /** Distinct accounts. One store is one operator, so 1 here (spec Section 6: one operator stays labeled). */
18
+ clusters: number;
19
+ /** Windows whose last step came back clean; a `class` cell: prompts with no error. */
20
+ ok: number;
21
+ /** Windows whose last step failed; a `class` cell: prompts with an error. A step with no result is in neither. */
22
+ failed: number;
23
+ /** Summed over each window's steps; a `class` cell: the prompts' whole usage. */
24
+ tokens: number;
25
+ durationMs: number;
26
+ /** Whether these counts sit inside a shown recall's consequence window, wholly or in part. It is part of the cell's key, so a motif's exposed and unexposed counts stay apart. */
27
+ exposed: boolean;
28
+ }
29
+ export declare const UNKNOWN = "unknown";
30
+ /** The class name an effort cell carries: a class motif with no prompt class in it, so a read of prompt classes skips it by this prefix. */
31
+ export declare const EFFORT_CLASS = "effort:";
32
+ /**
33
+ * Rule 3: effort is not in a cell's key. The one cell that holds it pairs it with the bucket of the prompt's thinking tokens, which is the fact an effort level explains, and it
34
+ * carries `week` and `exposure` but no frame axis: rule 3 bars joining `effort` to `model` or `harnessVersion`, and the pair would be four dimensions over `q_dims`.
35
+ */
36
+ export declare const effortClass: (effort: string, thinking: number) => string;
37
+ /** The frame bucket of a cell that joins no axis. */
38
+ export declare const NO_AXES = "-";
39
+ /**
40
+ * A harness version's bucket: its patch by tens. 70.5 percent of one operator's later prompts ran a version not yet seen, so an
41
+ * exact-version cell goes stale in days; a reader compares buckets by distance instead. A version with no patch stands for itself.
42
+ */
43
+ export declare const harnessBucket: (version: string | null) => string;
44
+ /** Only what most comparisons cross: platform, shell, and stack are on the frame and in the records, so a later extractor can add them without new capture. */
45
+ export declare const frameBucketOf: (frame: Frame) => string;
46
+ export declare const bucketFields: (bucket: string) => {
47
+ harness: string;
48
+ harnessBucket: string;
49
+ model: string;
50
+ };
51
+ /** The frame bucket of a (name, `platform`, `shell`) cell: fit's third pool form. Rule 3 lets the two join each other and a name within `q_dims`, but not a version or a model beside them, so the cell is version-free and model-free. */
52
+ export declare const ENVIRONMENT_BUCKET = "env:";
53
+ export declare const environmentBucketOf: (frame: Frame) => string;
54
+ export declare const isEnvironmentCell: (cell: Pick<Cell, "frameBucket">) => boolean;
55
+ export declare const environmentFieldsOf: (bucket: string) => {
56
+ platform: string;
57
+ shell: string;
58
+ };
59
+ /** The Monday of the UTC week a UTC day falls in, as a day: the week's name in a cell. An unknown day is an unknown week. */
60
+ export declare const weekStartOf: (day: string) => string;
61
+ /** The last UTC day a cell can hold: its day, or the Sunday that closes its week. A check that evidence predates a day reads this, since a week's cell may hold any of its seven days. */
62
+ export declare const lastDayOfCell: (cell: Cell) => string;
63
+ /** `cell:<contentHash>`: a cell that counts differently is a different cell, so a read's inputs pin what it read. */
64
+ export declare const cellIdOf: (cell: Cell) => string;
65
+ export interface CellSet {
66
+ cells: Cell[];
67
+ motifs: Motif[];
68
+ }
69
+ export declare const deriveCells: (store: Store, graph?: RecallGraph) => CellSet;
70
+ export declare const serializeCells: (cells: readonly Cell[]) => string;
71
+ /** What every read of one derive pass reads, and the snapshot it answers from. */
72
+ export interface Pool {
73
+ cells: readonly Cell[];
74
+ motifs: ReadonlyMap<string, Motif>;
75
+ /** The hash of the extractor version, the family methods, every cell, and the error rows recovery reads: new data or a new rule is a new pool, and every read of it a new read. */
76
+ version: string;
77
+ lastDay: string | null;
78
+ }
79
+ export declare const poolOf: (set: CellSet, errors: readonly ErrorObservation[]) => Pool;