@orkestrel/study 0.0.2 → 0.0.3

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.
@@ -84,8 +84,8 @@ export declare function computeStudyOutputStop(bytes: number, limit: number, out
84
84
  export declare function computeStudyScores(manifest: StudyManifest, rubric: StudyRubric, grading: StudyGrading): readonly StudyScore[];
85
85
 
86
86
  /**
87
- * Records an error that escaped admitted work as the failure of its ticket.
88
- * @param ticket - The admitted request whose work escaped
87
+ * Records an error thrown by admitted work as the failure of its ticket.
88
+ * @param ticket - The admitted request whose work threw
89
89
  * @param error - The caught value, read through `readStudyFailure`
90
90
  * @param role - The responsibility that replaces any role the reading carries; Default: the carried role, else the ticket's admitted role
91
91
  * @remarks
@@ -94,7 +94,7 @@ export declare function computeStudyScores(manifest: StudyManifest, rubric: Stud
94
94
  * so the parent's ownership is respected rather than inferred as local settlement. A refusal
95
95
  * thrown by the ticket's own settlement propagates.
96
96
  * @throws Thrown when the ticket's settlement refuses the failure.
97
- * @example Fail an escaped request
97
+ * @example Fail an admitted request
98
98
  * ```ts
99
99
  * import type { StudyTicket } from '@orkestrel/study'
100
100
  * import { failStudyTicket } from '@orkestrel/study'
@@ -158,6 +158,16 @@ export declare const isStudyCause: Guard<StudyCause>;
158
158
  */
159
159
  export declare function isStudyCriterion(value: unknown): value is StudyCriterion;
160
160
 
161
+ /** Checks whether an owned daemon declaration pins an absolute executable and distinct models.
162
+ * @param value - The candidate declaration.
163
+ * @returns True if executable, version, environment, and model identities are valid; false otherwise.
164
+ * @example
165
+ * ```ts
166
+ * isStudyDaemon({ executable: 'ollama' }) // false
167
+ * ```
168
+ */
169
+ export declare function isStudyDaemon(value: unknown): value is StudyDaemon;
170
+
161
171
  /**
162
172
  * Checks whether a study freezes a complete arm order for every repetition.
163
173
  * @param value - The candidate definition
@@ -187,9 +197,9 @@ export declare function isStudyDigest(value: unknown): value is string;
187
197
  * valid optional failure members, and an own {@link STUDY_ERROR_BRAND} descriptor whose value is
188
198
  * the error itself. Subclasses can replace the name.
189
199
  * The global key is recognized across package, ESM, CommonJS and captured module copies; it is
190
- * not unforgeable provenance. A transparent proxy is refused because its forwarded descriptor
200
+ * not proof of origin. A transparent proxy is refused because its forwarded descriptor
191
201
  * stores the target, and an unbranded copy, an inherited brand, or a property-only lookalike
192
- * stays outside the type. Hostile property access returns false instead of throwing.
202
+ * stays outside the type. Throwing property access returns false instead of throwing.
193
203
  * @param value - The caught value
194
204
  * @returns True if the value is a study error; false otherwise
195
205
  * @example
@@ -312,6 +322,12 @@ export declare function isStudyProfile(value: unknown): value is StudyProfile;
312
322
  */
313
323
  export declare function isStudyProvider(value: unknown): value is StudyProvider;
314
324
 
325
+ /** Checks whether a recording declaration bounds every serialized sidecar byte.
326
+ * @param value - The candidate declaration.
327
+ * @returns True if its byte allowance is a positive safe integer; false otherwise.
328
+ */
329
+ export declare function isStudyRecord(value: unknown): value is StudyRecord;
330
+
315
331
  /** Checks whether a value names a measured request role. */
316
332
  export declare const isStudyRole: Guard<StudyRole>;
317
333
 
@@ -533,8 +549,8 @@ export declare const STUDY_CAUSES: readonly StudyCause[];
533
549
  * Keys the own self-identity every study error stamps on itself.
534
550
  * @remarks
535
551
  * The global symbol registry lets every package, ESM, CommonJS and captured copy read the same
536
- * key. The registry makes the key discoverable; it is a recognition mechanism, not an
537
- * unforgeable provenance marker.
552
+ * key. The registry makes the key discoverable; it recognizes the contract without proving
553
+ * its origin.
538
554
  */
539
555
  export declare const STUDY_ERROR_BRAND: unique symbol;
540
556
 
@@ -755,6 +771,32 @@ export declare interface StudyCriterion {
755
771
  readonly assertion: JSONValue;
756
772
  }
757
773
 
774
+ /** Pins an executable and the model population an execution owns until cleanup.
775
+ * @remarks
776
+ * Study owns this declaration independently of Ollama's server API. The executable is an absolute
777
+ * canonical path; digest is its SHA-256 and version is the exact expected version reply. The
778
+ * provider URL must be a dedicated loopback origin outside port 11434. Environment entries are
779
+ * explicit non-secret overrides, merged by case-folded name; the host owns OLLAMA_HOST. Models
780
+ * have distinct names and SHA-256 digests equal to the provider's model and judge pins; shared
781
+ * pins require one entry. Execution
782
+ * verifies identity before use, unloads this population before each trial, and stops and joins
783
+ * on every ending. Offline replay never creates this daemon.
784
+ */
785
+ export declare interface StudyDaemon {
786
+ readonly executable: string;
787
+ readonly digest: string;
788
+ readonly version: string;
789
+ /** Holds explicit overrides with distinct case-folded names and no NUL characters. */
790
+ readonly environment?: Readonly<Record<string, string>> | undefined;
791
+ readonly models: readonly StudyDaemonModel[];
792
+ }
793
+
794
+ /** Pins one model the declared daemon verifies and unloads before each trial. */
795
+ export declare interface StudyDaemonModel {
796
+ readonly name: string;
797
+ readonly digest: string;
798
+ }
799
+
758
800
  /** Bounds each independent operation with a fresh failure owner and deadline. */
759
801
  export declare class StudyDeadline implements StudyDeadlineInterface {
760
802
  #private;
@@ -934,6 +976,8 @@ export declare interface StudyManifest {
934
976
  readonly bounds: StudyBounds;
935
977
  readonly qualification: string | undefined;
936
978
  readonly host: Readonly<Record<string, string>>;
979
+ /** Copies the profile's recording declaration before any trial starts. */
980
+ readonly record?: StudyRecord | undefined;
937
981
  }
938
982
 
939
983
  /** Admits request ceilings across every enclosing scope before dispatch. */
@@ -1001,6 +1045,8 @@ export declare interface StudyProfile {
1001
1045
  readonly qualification: string | undefined;
1002
1046
  /** Holds driver-owned JSON; the driver owns its interpretation and Study only validates and owns the object. */
1003
1047
  readonly settings: Readonly<Record<string, JSONValue>> | undefined;
1048
+ /** Requires one bounded exchange sidecar for every trial when present. */
1049
+ readonly record?: StudyRecord | undefined;
1004
1050
  }
1005
1051
 
1006
1052
  /** Holds caller-selected provider artifacts and model identities. */
@@ -1016,11 +1062,24 @@ export declare interface StudyProvider {
1016
1062
  /** Pins the judge model; declared together with its name or both absent. */
1017
1063
  readonly judge: string | undefined;
1018
1064
  };
1065
+ /** Declares an execution-owned daemon at this provider's dedicated URL. */
1066
+ readonly daemon?: StudyDaemon;
1019
1067
  }
1020
1068
 
1021
1069
  /** Names the evidence a study can establish. */
1022
1070
  export declare type StudyPurpose = 'mechanism' | 'quality' | 'capacity';
1023
1071
 
1072
+ /** Bounds the serialized exchange sidecar retained for each scheduled trial.
1073
+ * @remarks
1074
+ * Bytes is a positive safe integer counting all UTF-8 JSONL bytes, including declarations,
1075
+ * base64 expansion, terminal records, and LF separators. Recording is independent of passive
1076
+ * observation. Sidecars have their own trial allowance and also consume series custody capacity;
1077
+ * they do not consume the goal evidence allowance or the model's request quota.
1078
+ */
1079
+ export declare interface StudyRecord {
1080
+ readonly bytes: number;
1081
+ }
1082
+
1024
1083
  /** Settles an admitted request once and propagates operational failure to its owner. */
1025
1084
  export declare class StudyRequest implements StudyTicket {
1026
1085
  #private;
@@ -1051,7 +1110,7 @@ export declare interface StudyRequestOptions {
1051
1110
  }
1052
1111
 
1053
1112
  /** Names a request's measured responsibility. */
1054
- export declare type StudyRole = 'agent' | 'judge' | 'summary' | 'metadata' | 'tool';
1113
+ export declare type StudyRole = 'agent' | 'judge' | 'summary' | 'metadata' | 'maintenance' | 'tool';
1055
1114
 
1056
1115
  /** Holds the model context and generation allowance as separate measurements. */
1057
1116
  export declare interface StudyRoom {
@@ -84,8 +84,8 @@ export declare function computeStudyOutputStop(bytes: number, limit: number, out
84
84
  export declare function computeStudyScores(manifest: StudyManifest, rubric: StudyRubric, grading: StudyGrading): readonly StudyScore[];
85
85
 
86
86
  /**
87
- * Records an error that escaped admitted work as the failure of its ticket.
88
- * @param ticket - The admitted request whose work escaped
87
+ * Records an error thrown by admitted work as the failure of its ticket.
88
+ * @param ticket - The admitted request whose work threw
89
89
  * @param error - The caught value, read through `readStudyFailure`
90
90
  * @param role - The responsibility that replaces any role the reading carries; Default: the carried role, else the ticket's admitted role
91
91
  * @remarks
@@ -94,7 +94,7 @@ export declare function computeStudyScores(manifest: StudyManifest, rubric: Stud
94
94
  * so the parent's ownership is respected rather than inferred as local settlement. A refusal
95
95
  * thrown by the ticket's own settlement propagates.
96
96
  * @throws Thrown when the ticket's settlement refuses the failure.
97
- * @example Fail an escaped request
97
+ * @example Fail an admitted request
98
98
  * ```ts
99
99
  * import type { StudyTicket } from '@orkestrel/study'
100
100
  * import { failStudyTicket } from '@orkestrel/study'
@@ -158,6 +158,16 @@ export declare const isStudyCause: Guard<StudyCause>;
158
158
  */
159
159
  export declare function isStudyCriterion(value: unknown): value is StudyCriterion;
160
160
 
161
+ /** Checks whether an owned daemon declaration pins an absolute executable and distinct models.
162
+ * @param value - The candidate declaration.
163
+ * @returns True if executable, version, environment, and model identities are valid; false otherwise.
164
+ * @example
165
+ * ```ts
166
+ * isStudyDaemon({ executable: 'ollama' }) // false
167
+ * ```
168
+ */
169
+ export declare function isStudyDaemon(value: unknown): value is StudyDaemon;
170
+
161
171
  /**
162
172
  * Checks whether a study freezes a complete arm order for every repetition.
163
173
  * @param value - The candidate definition
@@ -187,9 +197,9 @@ export declare function isStudyDigest(value: unknown): value is string;
187
197
  * valid optional failure members, and an own {@link STUDY_ERROR_BRAND} descriptor whose value is
188
198
  * the error itself. Subclasses can replace the name.
189
199
  * The global key is recognized across package, ESM, CommonJS and captured module copies; it is
190
- * not unforgeable provenance. A transparent proxy is refused because its forwarded descriptor
200
+ * not proof of origin. A transparent proxy is refused because its forwarded descriptor
191
201
  * stores the target, and an unbranded copy, an inherited brand, or a property-only lookalike
192
- * stays outside the type. Hostile property access returns false instead of throwing.
202
+ * stays outside the type. Throwing property access returns false instead of throwing.
193
203
  * @param value - The caught value
194
204
  * @returns True if the value is a study error; false otherwise
195
205
  * @example
@@ -312,6 +322,12 @@ export declare function isStudyProfile(value: unknown): value is StudyProfile;
312
322
  */
313
323
  export declare function isStudyProvider(value: unknown): value is StudyProvider;
314
324
 
325
+ /** Checks whether a recording declaration bounds every serialized sidecar byte.
326
+ * @param value - The candidate declaration.
327
+ * @returns True if its byte allowance is a positive safe integer; false otherwise.
328
+ */
329
+ export declare function isStudyRecord(value: unknown): value is StudyRecord;
330
+
315
331
  /** Checks whether a value names a measured request role. */
316
332
  export declare const isStudyRole: Guard<StudyRole>;
317
333
 
@@ -533,8 +549,8 @@ export declare const STUDY_CAUSES: readonly StudyCause[];
533
549
  * Keys the own self-identity every study error stamps on itself.
534
550
  * @remarks
535
551
  * The global symbol registry lets every package, ESM, CommonJS and captured copy read the same
536
- * key. The registry makes the key discoverable; it is a recognition mechanism, not an
537
- * unforgeable provenance marker.
552
+ * key. The registry makes the key discoverable; it recognizes the contract without proving
553
+ * its origin.
538
554
  */
539
555
  export declare const STUDY_ERROR_BRAND: unique symbol;
540
556
 
@@ -755,6 +771,32 @@ export declare interface StudyCriterion {
755
771
  readonly assertion: JSONValue;
756
772
  }
757
773
 
774
+ /** Pins an executable and the model population an execution owns until cleanup.
775
+ * @remarks
776
+ * Study owns this declaration independently of Ollama's server API. The executable is an absolute
777
+ * canonical path; digest is its SHA-256 and version is the exact expected version reply. The
778
+ * provider URL must be a dedicated loopback origin outside port 11434. Environment entries are
779
+ * explicit non-secret overrides, merged by case-folded name; the host owns OLLAMA_HOST. Models
780
+ * have distinct names and SHA-256 digests equal to the provider's model and judge pins; shared
781
+ * pins require one entry. Execution
782
+ * verifies identity before use, unloads this population before each trial, and stops and joins
783
+ * on every ending. Offline replay never creates this daemon.
784
+ */
785
+ export declare interface StudyDaemon {
786
+ readonly executable: string;
787
+ readonly digest: string;
788
+ readonly version: string;
789
+ /** Holds explicit overrides with distinct case-folded names and no NUL characters. */
790
+ readonly environment?: Readonly<Record<string, string>> | undefined;
791
+ readonly models: readonly StudyDaemonModel[];
792
+ }
793
+
794
+ /** Pins one model the declared daemon verifies and unloads before each trial. */
795
+ export declare interface StudyDaemonModel {
796
+ readonly name: string;
797
+ readonly digest: string;
798
+ }
799
+
758
800
  /** Bounds each independent operation with a fresh failure owner and deadline. */
759
801
  export declare class StudyDeadline implements StudyDeadlineInterface {
760
802
  #private;
@@ -934,6 +976,8 @@ export declare interface StudyManifest {
934
976
  readonly bounds: StudyBounds;
935
977
  readonly qualification: string | undefined;
936
978
  readonly host: Readonly<Record<string, string>>;
979
+ /** Copies the profile's recording declaration before any trial starts. */
980
+ readonly record?: StudyRecord | undefined;
937
981
  }
938
982
 
939
983
  /** Admits request ceilings across every enclosing scope before dispatch. */
@@ -1001,6 +1045,8 @@ export declare interface StudyProfile {
1001
1045
  readonly qualification: string | undefined;
1002
1046
  /** Holds driver-owned JSON; the driver owns its interpretation and Study only validates and owns the object. */
1003
1047
  readonly settings: Readonly<Record<string, JSONValue>> | undefined;
1048
+ /** Requires one bounded exchange sidecar for every trial when present. */
1049
+ readonly record?: StudyRecord | undefined;
1004
1050
  }
1005
1051
 
1006
1052
  /** Holds caller-selected provider artifacts and model identities. */
@@ -1016,11 +1062,24 @@ export declare interface StudyProvider {
1016
1062
  /** Pins the judge model; declared together with its name or both absent. */
1017
1063
  readonly judge: string | undefined;
1018
1064
  };
1065
+ /** Declares an execution-owned daemon at this provider's dedicated URL. */
1066
+ readonly daemon?: StudyDaemon;
1019
1067
  }
1020
1068
 
1021
1069
  /** Names the evidence a study can establish. */
1022
1070
  export declare type StudyPurpose = 'mechanism' | 'quality' | 'capacity';
1023
1071
 
1072
+ /** Bounds the serialized exchange sidecar retained for each scheduled trial.
1073
+ * @remarks
1074
+ * Bytes is a positive safe integer counting all UTF-8 JSONL bytes, including declarations,
1075
+ * base64 expansion, terminal records, and LF separators. Recording is independent of passive
1076
+ * observation. Sidecars have their own trial allowance and also consume series custody capacity;
1077
+ * they do not consume the goal evidence allowance or the model's request quota.
1078
+ */
1079
+ export declare interface StudyRecord {
1080
+ readonly bytes: number;
1081
+ }
1082
+
1024
1083
  /** Settles an admitted request once and propagates operational failure to its owner. */
1025
1084
  export declare class StudyRequest implements StudyTicket {
1026
1085
  #private;
@@ -1051,7 +1110,7 @@ export declare interface StudyRequestOptions {
1051
1110
  }
1052
1111
 
1053
1112
  /** Names a request's measured responsibility. */
1054
- export declare type StudyRole = 'agent' | 'judge' | 'summary' | 'metadata' | 'tool';
1113
+ export declare type StudyRole = 'agent' | 'judge' | 'summary' | 'metadata' | 'maintenance' | 'tool';
1055
1114
 
1056
1115
  /** Holds the model context and generation allowance as separate measurements. */
1057
1116
  export declare interface StudyRoom {
@@ -1,4 +1,4 @@
1
- import { INTRINSICS, attempt, canonicalStringify, cloneJSONValue, holds, isArray, isError, isFiniteNumber, isJSONValue, isNonEmptyString, isObject, isRecord, literalOf } from "@orkestrel/contract";
1
+ import { INTRINSICS, attempt, canonicalStringify, cloneJSONValue, holds, isArray, isError, isFiniteNumber, isJSONValue, isNonEmptyString, isNumber, isObject, isRecord, literalOf } from "@orkestrel/contract";
2
2
  import { MAX_TIMEOUT_MS, Timeout } from "@orkestrel/timeout";
3
3
  import { Budget, isBudgetError } from "@orkestrel/budget";
4
4
  import { Abort, linkSignal } from "@orkestrel/abort";
@@ -7,8 +7,8 @@ import { Abort, linkSignal } from "@orkestrel/abort";
7
7
  * Keys the own self-identity every study error stamps on itself.
8
8
  * @remarks
9
9
  * The global symbol registry lets every package, ESM, CommonJS and captured copy read the same
10
- * key. The registry makes the key discoverable; it is a recognition mechanism, not an
11
- * unforgeable provenance marker.
10
+ * key. The registry makes the key discoverable; it recognizes the contract without proving
11
+ * its origin.
12
12
  */
13
13
  var STUDY_ERROR_BRAND = Symbol.for("@orkestrel/study.error");
14
14
  /** Caps a declared trial population before schedule allocation. */
@@ -23,6 +23,7 @@ var STUDY_ROLES = Object.freeze([
23
23
  "judge",
24
24
  "summary",
25
25
  "metadata",
26
+ "maintenance",
26
27
  "tool"
27
28
  ]);
28
29
  /** Lists every ceiling a run can stop at in report order. */
@@ -66,16 +67,18 @@ function omitStudyAbsent(value) {
66
67
  * ```
67
68
  */
68
69
  function normalizeStudyProfile(profile) {
69
- const { provider, qualification, settings, room, ...required } = profile;
70
+ const { provider, qualification, settings, record, room, ...required } = profile;
70
71
  return {
71
72
  ...required,
72
73
  room: omitStudyAbsent(room),
73
74
  ...provider === void 0 ? {} : { provider: {
74
75
  ...omitStudyAbsent(provider),
75
- digest: omitStudyAbsent(provider.digest)
76
+ digest: omitStudyAbsent(provider.digest),
77
+ ...provider.daemon === void 0 ? {} : { daemon: omitStudyAbsent(provider.daemon) }
76
78
  } },
77
79
  ...qualification === void 0 ? {} : { qualification },
78
- ...settings === void 0 ? {} : { settings }
80
+ ...settings === void 0 ? {} : { settings },
81
+ ...record === void 0 ? {} : { record }
79
82
  };
80
83
  }
81
84
  /**
@@ -417,15 +420,46 @@ function isStudyProvider(value) {
417
420
  "url",
418
421
  "model",
419
422
  "digest"
420
- ], ["judge"]) || ![
423
+ ], ["judge", "daemon"]) || ![
421
424
  "entry",
422
425
  "url",
423
426
  "model"
424
427
  ].every((key) => isNonEmptyString(value[key])) || typeof value.url !== "string" || !isRecord(value.digest) || !matchesStudyKeys(value.digest, ["model"], ["judge"]) || !isStudyDigest(value.digest.model) || value.judge === void 0 !== (value.digest.judge === void 0) || value.judge !== void 0 && (!isNonEmptyString(value.judge) || !isStudyDigest(value.digest.judge))) return false;
425
428
  const url = new URL(value.url);
429
+ if (value.daemon !== void 0) {
430
+ if (!isStudyDaemon(value.daemon) || !/^http:\/\/127\.0\.0\.1:[0-9]+\/?$/.test(value.url) || Number(url.port) <= 0 || Number(url.port) === 11434) return false;
431
+ const daemon = value.daemon;
432
+ const digest = value.digest;
433
+ if (!daemon.models.every((model) => model.name === value.model && model.digest === digest.model || value.judge !== void 0 && model.name === value.judge && model.digest === digest.judge) || !daemon.models.some((model) => model.name === value.model && model.digest === digest.model) || value.judge !== void 0 && !daemon.models.some((model) => model.name === value.judge && model.digest === digest.judge)) return false;
434
+ }
426
435
  return ["http:", "https:"].includes(url.protocol) && url.username === "" && url.password === "" && url.search === "" && url.hash === "" && url.pathname === "/";
427
436
  });
428
437
  }
438
+ /** Checks whether an owned daemon declaration pins an absolute executable and distinct models.
439
+ * @param value - The candidate declaration.
440
+ * @returns True if executable, version, environment, and model identities are valid; false otherwise.
441
+ * @example
442
+ * ```ts
443
+ * isStudyDaemon({ executable: 'ollama' }) // false
444
+ * ```
445
+ */
446
+ function isStudyDaemon(value) {
447
+ return holds(() => {
448
+ if (!isRecord(value) || !matchesStudyKeys(value, [
449
+ "executable",
450
+ "digest",
451
+ "version",
452
+ "models"
453
+ ], ["environment"]) || typeof value.executable !== "string" || !/^(?:[A-Za-z]:[\\/]|\/(?!\/)|\\\\[^\\]+\\[^\\]+\\)/.test(value.executable) || value.executable.includes("\0") || value.executable.split(/[\\/]/).some((part) => part === "." || part === "..") || !isStudyDigest(value.digest) || !isNonEmptyString(value.version) || value.version.trim().length === 0 || value.version.includes("\0") || !isArray(value.models) || value.models.length === 0 || !value.models.every((model) => isRecord(model) && matchesStudyKeys(model, ["name", "digest"]) && isNonEmptyString(model.name) && model.name.trim().length > 0 && !model.name.includes("\0") && isStudyDigest(model.digest))) return false;
454
+ if (new Set(value.models.map((model) => isRecord(model) ? model.name : void 0)).size !== value.models.length) return false;
455
+ if (value.environment !== void 0) {
456
+ if (!isRecord(value.environment) || !matchesStudyKeys(value.environment, [], Object.keys(value.environment))) return false;
457
+ const entries = Object.entries(value.environment);
458
+ if (new Set(entries.map(([name]) => name.toLowerCase())).size !== entries.length || entries.some(([name, entry]) => name.length === 0 || /[=\0]/.test(name) || typeof entry !== "string" || entry.includes("\0"))) return false;
459
+ }
460
+ return true;
461
+ });
462
+ }
429
463
  /**
430
464
  * Checks whether a profile carries finite bounds and an optional explicit provider.
431
465
  * @param value - The candidate profile
@@ -447,10 +481,18 @@ function isStudyProfile(value) {
447
481
  ], [
448
482
  "provider",
449
483
  "qualification",
450
- "settings"
451
- ]) && isStudyName(value.name) && isNonEmptyString(value.agent) && isArray(value.roots) && isJSONValue(value.roots) && value.roots.length > 0 && value.roots.every((root) => typeof root === "string" && root.trim().length > 0) && new Set(value.roots).size === value.roots.length && isNonEmptyString(value.subject) && isStudyBounds(value.bounds) && isStudyRoom(value.room) && (value.provider === void 0 || isStudyProvider(value.provider)) && (value.qualification === void 0 || isNonEmptyString(value.qualification)) && (value.settings === void 0 || isRecord(value.settings) && isJSONValue(value.settings));
484
+ "settings",
485
+ "record"
486
+ ]) && isStudyName(value.name) && isNonEmptyString(value.agent) && isArray(value.roots) && isJSONValue(value.roots) && value.roots.length > 0 && value.roots.every((root) => typeof root === "string" && root.trim().length > 0) && new Set(value.roots).size === value.roots.length && isNonEmptyString(value.subject) && isStudyBounds(value.bounds) && isStudyRoom(value.room) && (value.provider === void 0 || isStudyProvider(value.provider)) && (value.record === void 0 || isStudyRecord(value.record)) && (value.qualification === void 0 || isNonEmptyString(value.qualification)) && (value.settings === void 0 || isRecord(value.settings) && isJSONValue(value.settings));
452
487
  });
453
488
  }
489
+ /** Checks whether a recording declaration bounds every serialized sidecar byte.
490
+ * @param value - The candidate declaration.
491
+ * @returns True if its byte allowance is a positive safe integer; false otherwise.
492
+ */
493
+ function isStudyRecord(value) {
494
+ return holds(() => isRecord(value) && matchesStudyKeys(value, ["bytes"]) && typeof value.bytes === "number" && Number.isSafeInteger(value.bytes) && value.bytes > 0);
495
+ }
454
496
  /**
455
497
  * Checks whether a byte identity is a lowercase SHA-256 digest.
456
498
  * @param value - The candidate identity
@@ -649,9 +691,9 @@ var StudyError = class extends Error {
649
691
  * valid optional failure members, and an own {@link STUDY_ERROR_BRAND} descriptor whose value is
650
692
  * the error itself. Subclasses can replace the name.
651
693
  * The global key is recognized across package, ESM, CommonJS and captured module copies; it is
652
- * not unforgeable provenance. A transparent proxy is refused because its forwarded descriptor
694
+ * not proof of origin. A transparent proxy is refused because its forwarded descriptor
653
695
  * stores the target, and an unbranded copy, an inherited brand, or a property-only lookalike
654
- * stays outside the type. Hostile property access returns false instead of throwing.
696
+ * stays outside the type. Throwing property access returns false instead of throwing.
655
697
  * @param value - The caught value
656
698
  * @returns True if the value is a study error; false otherwise
657
699
  * @example
@@ -661,11 +703,12 @@ var StudyError = class extends Error {
661
703
  */
662
704
  function isStudyError(value) {
663
705
  return holds(() => {
664
- if (!isError(value) || INTRINSICS.prototype(value) === INTRINSICS.error.prototype) return false;
706
+ if (!(value instanceof INTRINSICS.error) || INTRINSICS.prototype(value) === INTRINSICS.error.prototype) return false;
665
707
  const brand = INTRINSICS.describe(value, STUDY_ERROR_BRAND)?.value;
666
708
  const code = INTRINSICS.reflect.read(value, "code");
667
709
  const failure = INTRINSICS.reflect.read(value, "failure");
668
- if (brand !== value || !isStudyErrorCode(code) || typeof INTRINSICS.reflect.read(value, "name") !== "string" || typeof INTRINSICS.reflect.read(value, "message") !== "string") return false;
710
+ const stack = INTRINSICS.reflect.read(value, "stack");
711
+ if (brand !== value || !isStudyErrorCode(code) || typeof INTRINSICS.reflect.read(value, "name") !== "string" || typeof INTRINSICS.reflect.read(value, "message") !== "string" || stack !== void 0 && typeof stack !== "string") return false;
669
712
  if (failure === void 0) return true;
670
713
  if (!isObject(failure)) return false;
671
714
  const cause = INTRINSICS.reflect.read(failure, "cause");
@@ -679,7 +722,7 @@ function isStudyError(value) {
679
722
  const id = INTRINSICS.reflect.read(stop, "id");
680
723
  const max = INTRINSICS.reflect.read(stop, "max");
681
724
  const consumed = INTRINSICS.reflect.read(stop, "consumed");
682
- return isStudyLayer(layer) && typeof id === "string" && (max === void 0 || isFiniteNumber(max)) && (consumed === void 0 || isFiniteNumber(consumed));
725
+ return isStudyLayer(layer) && typeof id === "string" && (max === void 0 || isNumber(max)) && (consumed === void 0 || isNumber(consumed));
683
726
  });
684
727
  }
685
728
  //#endregion
@@ -944,8 +987,8 @@ function computeStudyOutputStop(bytes, limit, outputs) {
944
987
  };
945
988
  }
946
989
  /**
947
- * Records an error that escaped admitted work as the failure of its ticket.
948
- * @param ticket - The admitted request whose work escaped
990
+ * Records an error thrown by admitted work as the failure of its ticket.
991
+ * @param ticket - The admitted request whose work threw
949
992
  * @param error - The caught value, read through `readStudyFailure`
950
993
  * @param role - The responsibility that replaces any role the reading carries; Default: the carried role, else the ticket's admitted role
951
994
  * @remarks
@@ -954,7 +997,7 @@ function computeStudyOutputStop(bytes, limit, outputs) {
954
997
  * so the parent's ownership is respected rather than inferred as local settlement. A refusal
955
998
  * thrown by the ticket's own settlement propagates.
956
999
  * @throws Thrown when the ticket's settlement refuses the failure.
957
- * @example Fail an escaped request
1000
+ * @example Fail an admitted request
958
1001
  * ```ts
959
1002
  * import type { StudyTicket } from '@orkestrel/study'
960
1003
  * import { failStudyTicket } from '@orkestrel/study'
@@ -2035,6 +2078,6 @@ var StudyAgentJudge = class {
2035
2078
  }
2036
2079
  };
2037
2080
  //#endregion
2038
- export { STUDY_CAUSES, STUDY_ERROR_BRAND, STUDY_GOALS, STUDY_LAYERS, STUDY_NAME, STUDY_ROLES, STUDY_TRIALS, StudyAgentJudge, StudyAgentObserver, StudyControl, StudyDeadline, StudyError, StudyMeter, StudyRequest, StudyScope, StudySeries, buildStudyAgentDispatch, buildStudyAgentParts, buildStudySchedule, computeStudyAdmittedStop, computeStudyOutputStop, computeStudyScores, failStudyTicket, failureToStudyError, isStudyAgentGauge, isStudyAgentRuntime, isStudyBounds, isStudyCause, isStudyCriterion, isStudyDefinition, isStudyDigest, isStudyError, isStudyErrorCode, isStudyGoal, isStudyGrading, isStudyJudgment, isStudyLabeling, isStudyLayer, isStudyLimit, isStudyName, isStudyProduct, isStudyProfile, isStudyProvider, isStudyRole, isStudyRoom, isStudyRubric, isStudyScenario, isStudyStop, isStudyTimestamp, isStudyUsage, matchesStudyKeys, normalizeStudyProfile, omitStudyAbsent, parseStudyDefinition, parseStudyGrading, parseStudyLabeling, parseStudyProfile, parseStudyReading, parseStudyRubric, parseStudyScenario, projectStudyAgentRequest, readStudyFailure, studyStopToJSON, translateStudyAgentError, translateStudyAgentStop, validateStudyAgentUsage, validateStudyInputs };
2081
+ export { STUDY_CAUSES, STUDY_ERROR_BRAND, STUDY_GOALS, STUDY_LAYERS, STUDY_NAME, STUDY_ROLES, STUDY_TRIALS, StudyAgentJudge, StudyAgentObserver, StudyControl, StudyDeadline, StudyError, StudyMeter, StudyRequest, StudyScope, StudySeries, buildStudyAgentDispatch, buildStudyAgentParts, buildStudySchedule, computeStudyAdmittedStop, computeStudyOutputStop, computeStudyScores, failStudyTicket, failureToStudyError, isStudyAgentGauge, isStudyAgentRuntime, isStudyBounds, isStudyCause, isStudyCriterion, isStudyDaemon, isStudyDefinition, isStudyDigest, isStudyError, isStudyErrorCode, isStudyGoal, isStudyGrading, isStudyJudgment, isStudyLabeling, isStudyLayer, isStudyLimit, isStudyName, isStudyProduct, isStudyProfile, isStudyProvider, isStudyRecord, isStudyRole, isStudyRoom, isStudyRubric, isStudyScenario, isStudyStop, isStudyTimestamp, isStudyUsage, matchesStudyKeys, normalizeStudyProfile, omitStudyAbsent, parseStudyDefinition, parseStudyGrading, parseStudyLabeling, parseStudyProfile, parseStudyReading, parseStudyRubric, parseStudyScenario, projectStudyAgentRequest, readStudyFailure, studyStopToJSON, translateStudyAgentError, translateStudyAgentStop, validateStudyAgentUsage, validateStudyInputs };
2039
2082
 
2040
2083
  //# sourceMappingURL=index.js.map