@orkestrel/probe 0.0.26 → 0.0.28

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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/core/constants.ts","../../../src/core/errors.ts","../../../src/core/validators.ts","../../../src/core/helpers.ts","../../../src/core/shapers.ts"],"sourcesContent":["/**\n * Lists the stages a claim passes through, in the order a verdict reports them:\n * `['type', 'lint', 'runtime']`.\n *\n * @remarks\n * One list feeds the `Stage` type, the wire shape, the stage guard, and the receipt computation, so\n * a stage cannot be admitted by one and refused by another. The type derives from this list rather\n * than standing beside it, so a stage the list omits is a stage no member of the union names.\n *\n * @example\n * ```ts\n * PROBE_STAGES // ['type', 'lint', 'runtime']\n * ```\n */\nexport const PROBE_STAGES = Object.freeze(['type', 'lint', 'runtime'] as const)\n\n/**\n * Lists the parties that can own action on an issue or probe failure:\n * `['claimant', 'workspace', 'instrument']`.\n *\n * @remarks\n * One list feeds the `Party` type and its guard, so a party cannot be admitted by one and refused\n * by another.\n *\n * @example\n * ```ts\n * PROBE_PARTIES // ['claimant', 'workspace', 'instrument']\n * ```\n */\nexport const PROBE_PARTIES = Object.freeze(['claimant', 'workspace', 'instrument'] as const)\n\n/**\n * Lists the conditions that can end a probe operation:\n * `['refused', 'missing', 'malformed', 'destroyed', 'deadline']`.\n *\n * @remarks\n * One list feeds the code union and the error guard, so a condition cannot be constructed by one\n * and refused by the other. The guard reads this list rather than the class, which is what lets it\n * refuse a lookalike carrying a condition this package never declared.\n *\n * @example\n * ```ts\n * PROBE_ERROR_CODES // ['refused', 'missing', 'malformed', 'destroyed', 'deadline']\n * ```\n */\nexport const PROBE_ERROR_CODES = Object.freeze([\n\t'refused',\n\t'missing',\n\t'malformed',\n\t'destroyed',\n\t'deadline',\n] as const)\n\n/**\n * Names the leading token every receipt carries, `'probe'`.\n *\n * @remarks\n * A receipt travels away from the verdict that minted it — an agent pastes it into the promotion\n * action — so it names itself rather than relying on where it was found.\n *\n * @example\n * ```ts\n * const receipt =\n * \t'probe:6ca20c3bff623031d3955b9d1a76d71d:type:typescript@6.0.3:oxlint@1.79.0:vitest@4.1.11:configs/src/tsconfig.core.json@d61f11b52460b1c6707cfac2c6078d59'\n * receipt.startsWith(RECEIPT_PREFIX) // true\n * ```\n */\nexport const RECEIPT_PREFIX = 'probe'\n\n/**\n * Names the character joining a receipt's tokens, `':'`.\n *\n * @remarks\n * A project path can contain this character, so the project field goes last and a reader rejoins\n * every field from index 6 onward with this separator rather than expecting a fixed count.\n *\n * @example\n * ```ts\n * const receipt =\n * \t'probe:6ca20c3bff623031d3955b9d1a76d71d:type:typescript@6.0.3:oxlint@1.79.0:vitest@4.1.11:configs/src/tsconfig.core.json@d61f11b52460b1c6707cfac2c6078d59'\n * receipt.split(RECEIPT_SEPARATOR).length // 7\n * ```\n */\nexport const RECEIPT_SEPARATOR = ':'\n\n/**\n * Names the default inspection deadline a `Probe` applies when its construction omits one,\n * 30,000 ms.\n *\n * @remarks\n * `ProbeOptions.deadline` overrides this value per instance.\n *\n * @example\n * ```ts\n * PROBE_DEADLINE // 30_000\n * ```\n */\nexport const PROBE_DEADLINE = 30_000\n\n/**\n * Bounds the default type warm at 90,000 ms, independently of active inspections.\n *\n * @remarks\n * `ProbeOptions.warm` overrides this value. Veneer's slowest measured warm on 2026-10-05 took\n * 50,000 ms; this bound leaves 40,000 ms for host contention without changing inspection budgets.\n *\n * @example\n * ```ts\n * PROBE_WARM // 90_000\n * ```\n */\nexport const PROBE_WARM = 90_000\n\n/**\n * Bounds consecutive failed warms or idle losses before a stage's floor is spent.\n *\n * @example\n * ```ts\n * PROBE_RESTARTS // 1\n * ```\n */\nexport const PROBE_RESTARTS = 1\n\n/**\n * Names the 2,000 ms bound the lint stage holds over the lifecycle exchanges the protocol leaves\n * to the server: the `initialize` reply warming waits for and the `shutdown` reply ending waits for.\n *\n * @remarks\n * It does not reach the diagnostics an inspection waits for, which the caller's own signal bounds.\n * The transport's cooperative window is half of it; `LINT_TEARDOWN` also accounts for process\n * termination and exit confirmation. Measured 2026-08-27: the workspace `oxlint --lsp` answers\n * `initialize` in 155 ms, so\n * this bound is more than ten times that reply.\n *\n * @example\n * ```ts\n * LINT_DEADLINE // 2_000\n * ```\n */\nexport const LINT_DEADLINE = 2_000\n\n/**\n * Bounds lint teardown at 16,000 ms, including protocol exchanges and process termination.\n *\n * @remarks\n * Shutdown and exit each have `LINT_DEADLINE`; transport grace and stream drain each have half\n * that bound. The installed process transport permits 5,000 ms for Windows tree termination and\n * another 5,000 ms for exit confirmation. This also covers POSIX escalation's shorter grace.\n * The process confirmation constant is not exposed by the declared LSP dependency.\n *\n * @example\n * ```ts\n * LINT_TEARDOWN // 16_000\n * ```\n */\nexport const LINT_TEARDOWN = LINT_DEADLINE * 3 + 5_000 * 2\n\n/**\n * Names the total enumerable key bound `ProbeServer` applies to inbound metadata and to produced\n * tool content alike, 4096.\n *\n * @remarks\n * `@orkestrel/mcp`'s default leaf is sized for metadata: a verdict costs 38 keys empty and 11 more\n * for each issue a stage reports, so the default carries a verdict whose control refuses one\n * declaration and stops carrying the next one. A whole tool-call result carrying the record beside\n * its rendering costs 44 keys plus 11 for each issue, so this bound carries a record reporting up\n * to 368 issues. Past that the reply falls back to the rendered text, and past the 4 MiB content\n * bound to the receipt block.\n *\n * @example\n * ```ts\n * PROBE_KEYS // 4096\n * ```\n */\nexport const PROBE_KEYS = 4096\n\n/**\n * Names the specification lifetime the runtime stage replaces its resident Vitest service at,\n * 64 specifications.\n *\n * @remarks\n * Vite retains one unresolved URL for every fresh specification path, so an inspection that writes\n * a specification grows a map the resident service never releases. The stage replaces that service\n * rather than deleting from each map it owns, and this is the count of specifications one service\n * carries before the replacement. Any bound holds the retention flat, because the replacement\n * releases everything the instance held; this value trades the cost of the replacement against how\n * far the map grows between them. On 2026-08-20, over this package's own workspace on the host\n * `guides/probe.md` § Cost names, two runs of 66 inspections put the crossing inspection at 480 ms\n * and 269 ms against median inspections of 156 ms and 155 ms.\n *\n * @example\n * ```ts\n * PROBE_SPECIFICATIONS // 64\n * ```\n */\nexport const PROBE_SPECIFICATIONS = 64\n\n/**\n * Names the Vite plugin the runtime stage installs into a target workspace's Vitest\n * configuration, `'orkestrel-runtime-overlay'`.\n *\n * @remarks\n * The stage declares the plugin under this name and reads a configured project's plugin list for\n * the same name to decide whether that project carries the overlay. One name feeds both, so a\n * project the stage instrumented is never reported as a workspace project it could not reach.\n *\n * @example\n * ```ts\n * RUNTIME_PLUGIN // 'orkestrel-runtime-overlay'\n * ```\n */\nexport const RUNTIME_PLUGIN = 'orkestrel-runtime-overlay'\n\n/**\n * Names the workspace-relative directory the type stage keeps its workspace mirror under,\n * `'tmp/type'`.\n *\n * @remarks\n * Each stage owns one directory beneath this one, named for the writing host's process id and a\n * fresh UUID, and deletes it at teardown. The directory sits under `tmp/` so nothing the stage\n * writes reaches a version-controlled path, and beside `tmp/probes/` rather than inside it because\n * the workbench Vitest project collects `tmp/probes/**` and would otherwise collect a mirrored test.\n * A target that runs its own `check`, `lint:check`, or `format:check` while a probe is resident\n * reads whatever it does not exclude, so exclude this directory there.\n *\n * @example\n * ```ts\n * TYPE_MIRROR // 'tmp/type'\n * ```\n */\nexport const TYPE_MIRROR = 'tmp/type'\n","import type { Party, ProbeErrorCode, ProbeErrorContext, ProbeErrorOptions } from './types.js'\nimport {\n\tINTRINSICS,\n\tholds,\n\tisError,\n\tisFunction,\n\tisNumber,\n\tisObject,\n\tisString,\n} from '@orkestrel/contract'\nimport { PROBE_ERROR_CODES, PROBE_PARTIES } from './constants.js'\n\n/**\n * Reports one probe failure under stable ownership and condition axes.\n *\n * @remarks\n * Every failure raised while serving a claim arrives as one of these, so a consumer catching from\n * `prove`, from a stage, or from a workspace leaf branches on {@link Party} and\n * {@link ProbeErrorCode} rather than on message text. Narrow a caught value with\n * {@link isProbeError} before reading either member. A pure leaf called directly with a value\n * `isClaim` would refuse raises the host's own fault unchanged, because those leaves are written to\n * be total over a validated claim rather than over every value.\n *\n * @example\n * ```ts\n * const error = new ProbeError('Path escapes the workspace: ../secrets.env', {\n * \torigin: 'claimant',\n * \tcode: 'refused',\n * \tcontext: { path: '../secrets.env' },\n * })\n * error.origin // 'claimant'\n * error.code // 'refused'\n * error.context?.path // '../secrets.env'\n * ```\n */\nexport class ProbeError extends Error {\n\toverride readonly name = 'ProbeError'\n\treadonly origin: Party\n\treadonly code: ProbeErrorCode\n\treadonly context?: ProbeErrorContext\n\n\t/**\n\t * Creates one classified probe failure.\n\t *\n\t * @param message - Human-readable description of what failed\n\t * @param options - The ownership, condition, optional structured context, and optional cause\n\t */\n\tconstructor(message: string, options: ProbeErrorOptions) {\n\t\tconst cause = options.cause\n\t\tsuper(message, cause === undefined ? undefined : { cause })\n\t\tObject.defineProperty(this, Symbol.for('@orkestrel/probe.error'), { value: this })\n\t\tthis.origin = options.origin\n\t\tthis.code = options.code\n\t\tif (options.context !== undefined) this.context = options.context\n\t}\n}\n\n/**\n * Checks whether an unknown value is a {@link ProbeError}.\n *\n * @remarks\n * Recognition requires the native `Error` base, a non-base prototype, an own global brand holding\n * the error itself, a declared party and condition, and context members of their declared types.\n * The brand carries recognition across duplicate installations and module formats. A subclass may\n * replace `name`. A transparent proxy, a plain `Error`, a property-only lookalike, and an error with\n * invalid members stay outside the type.\n *\n * @param value - The value to inspect\n * @returns True if the value is a `ProbeError` instance; false otherwise\n *\n * @example\n * ```ts\n * isProbeError(createDestroyedError('probe')) // true\n * isProbeError(new Error('The probe has been destroyed')) // false\n * ```\n */\nexport function isProbeError(value: unknown): value is ProbeError {\n\tif (!isError(value)) return false\n\treturn holds(() => {\n\t\tif (INTRINSICS.prototype(value) === Error.prototype) return false\n\t\tconst descriptor = INTRINSICS.describe(value, Symbol.for('@orkestrel/probe.error'))\n\t\tif (descriptor?.value !== value) return false\n\t\tconst origin: unknown = INTRINSICS.reflect.read(value, 'origin')\n\t\tconst code: unknown = INTRINSICS.reflect.read(value, 'code')\n\t\tconst context: unknown = INTRINSICS.reflect.read(value, 'context')\n\t\tif (context !== undefined) {\n\t\t\tif (!isObject(context) && !isFunction(context)) return false\n\t\t\tconst stage: unknown = INTRINSICS.reflect.read(context, 'stage')\n\t\t\tif (stage !== undefined && stage !== 'type' && stage !== 'lint' && stage !== 'runtime')\n\t\t\t\treturn false\n\t\t\tfor (const key of ['path', 'project', 'name']) {\n\t\t\t\tconst member: unknown = INTRINSICS.reflect.read(context, key)\n\t\t\t\tif (member !== undefined && !isString(member)) return false\n\t\t\t}\n\t\t\tconst deadline: unknown = INTRINSICS.reflect.read(context, 'deadline')\n\t\t\tif (deadline !== undefined && !isNumber(deadline)) return false\n\t\t}\n\t\treturn (\n\t\t\tPROBE_PARTIES.some((declared) => declared === origin) &&\n\t\t\tPROBE_ERROR_CODES.some((declared) => declared === code)\n\t\t)\n\t})\n}\n\n/**\n * Creates the failure raised when an instrument is used after it was torn down.\n *\n * @remarks\n * Every resident entity in this package refuses this way, so the subject names which one in the\n * message and the condition is the same for all of them. A caller that meets this builds a\n * replacement rather than retrying: teardown is permanent.\n *\n * @param subject - The torn-down entity, named as the message names it, such as `type stage`\n * @returns A typed teardown failure\n *\n * @example\n * ```ts\n * createDestroyedError('type stage').message // 'The type stage has been destroyed'\n * createDestroyedError('probe').code // 'destroyed'\n * ```\n */\nexport function createDestroyedError(subject: string): ProbeError {\n\treturn new ProbeError(`The ${subject} has been destroyed`, {\n\t\torigin: 'claimant',\n\t\tcode: 'destroyed',\n\t})\n}\n","import type { Guard } from '@orkestrel/contract'\nimport type {\n\tCase,\n\tCheck,\n\tClaim,\n\tControl,\n\tDraft,\n\tIssue,\n\tParty,\n\tProject,\n\tStage,\n\tToolchain,\n\tVerdict,\n} from './types.js'\nimport {\n\tandOf,\n\tarrayOf,\n\tisNonEmptyString,\n\tisNumber,\n\tisString,\n\tliteralOf,\n\trecordOf,\n} from '@orkestrel/contract'\nimport { isLSPRange } from '@orkestrel/lsp'\nimport { PROBE_PARTIES, PROBE_STAGES } from './constants.js'\n\n/**\n * Checks whether a value names a stage.\n *\n * @param value - The value to check\n * @returns True if the value is a stage name; false otherwise\n *\n * @example\n * ```ts\n * isStage('lint') // true\n * isStage('typecheck') // false\n * ```\n */\nexport const isStage: Guard<Stage> = literalOf(PROBE_STAGES)\n\n/**\n * Checks whether a value carries a proposed file's path and text.\n *\n * @remarks\n * Rejects an empty, absolute, or workspace-escaping path. A contained relative path may contain\n * `.` or may traverse to a parent that remains inside the workspace. Every stage resolves a file\n * by that path, so admitting an escape here would defer the refusal until a stage had already\n * accepted the draft. An `Issue` carries no such minimum because this package produces issues\n * rather than admits them.\n *\n * @param value - The value to check\n * @returns True if the value is a draft; false otherwise\n *\n * @example\n * ```ts\n * const text = \"export function createGreeting(): string {\\n\\treturn 'hi'\\n}\\n\"\n * isDraft({ path: 'src/core/factories.ts', text }) // true\n * isDraft({ path: '../../etc/hosts', text: '' }) // false\n * isDraft({ path: 'src/core/factories.ts' }) // false\n * ```\n */\nexport const isDraft: Guard<Draft> = recordOf({\n\tpath: andOf<string>(isNonEmptyString, (path) => {\n\t\tif (/^(?:[\\\\/]|[A-Za-z]:)/.test(path)) return false\n\t\treturn (\n\t\t\tpath.split(/[\\\\/]+/).reduce((depth, segment) => {\n\t\t\t\tif (depth < 0 || segment === '' || segment === '.') return depth\n\t\t\t\treturn segment === '..' ? depth - 1 : depth + 1\n\t\t\t}, 0) >= 0\n\t\t)\n\t}),\n\ttext: isString,\n})\n\n/**\n * Checks whether a value carries a claim's candidate drafts and its test.\n *\n * @param value - The value to check\n * @returns True if the value is a case; false otherwise\n *\n * @example\n * ```ts\n * const test = { path: 'tests/src/core/greeting.test.ts', text: 'test(\"greets\", () => {})\\n' }\n * isCase({ files: [], test }) // true\n * ```\n */\nexport const isCase: Guard<Case> = recordOf({ files: arrayOf(isDraft), test: isDraft })\n\n/**\n * Checks whether a value carries a case plus the stage and reason it must fail for.\n *\n * @param value - The value to check\n * @returns True if the value is a control; false otherwise\n *\n * @example\n * ```ts\n * const test = { path: 'tests/src/core/greeting.test.ts', text: 'test(\"greets\", () => {})\\n' }\n * isControl({ files: [], test, stage: 'type', reason: 'must not compile' }) // true\n * isControl({ files: [], test }) // false\n * ```\n */\nexport const isControl: Guard<Control> = recordOf({\n\tfiles: arrayOf(isDraft),\n\ttest: isDraft,\n\tstage: isStage,\n\treason: isNonEmptyString,\n})\n\n/**\n * Checks whether a value carries a project, a case, and a control.\n *\n * @remarks\n * Exact rather than open: a claim is this package's own record, so an unknown member is a caller\n * sending a contract this service does not implement rather than a wider caller it must tolerate.\n *\n * `CLAIM_SHAPE` is the wire contract's shape and this is the admission rule the tool enforces, and\n * the rule is strictly narrower than the shape. They agree on every member but `Draft.path`: the\n * shape constrains it to a non-empty string, while `isDraft` also refuses an absolute path\n * and one that traverses out of the workspace, so `../../etc/hosts` satisfies\n * `compileGuard(CLAIM_SHAPE)` and is refused here. `ProbeServer` advertises the shape and enforces\n * this rule, so it names the refused member rather than reporting only that the claim was invalid.\n *\n * @param value - The value to check\n * @returns True if the value is a claim; false otherwise\n *\n * @example\n * ```ts\n * isClaim({ project: 'tsconfig.json', case: subject, control }) // true\n * isClaim({ project: 'tsconfig.json', case: subject }) // false\n * ```\n */\nexport const isClaim: Guard<Claim> = recordOf({\n\tproject: isNonEmptyString,\n\tcase: isCase,\n\tcontrol: isControl,\n})\n\n/**\n * Checks whether a value names a party an issue carries.\n *\n * @param value - The value to check\n * @returns True if the value is a party; false otherwise\n *\n * @example\n * ```ts\n * isParty('claimant') // true\n * isParty('workspace') // true\n * isParty('instrument') // true\n * isParty('stage') // false\n * ```\n */\nexport const isParty: Guard<Party> = literalOf(PROBE_PARTIES)\n\n/**\n * Checks whether a value carries one message, its location, and the origin of the fault it names.\n *\n * @remarks\n * `origin` is required rather than optional. Every issue this package produces sets it, and a\n * receipt turns on it: a control's issue disproves the claim only when its origin names the\n * claimant.\n *\n * @param value - The value to check\n * @returns True if the value is an issue; false otherwise\n *\n * @example\n * ```ts\n * isIssue({ origin: 'claimant', path: 'src/core/greeting.ts', message: 'not assignable' }) // true\n * isIssue({ path: 'src/core/greeting.ts', message: 'not assignable' }) // false\n * ```\n */\nexport const isIssue: Guard<Issue> = recordOf(\n\t{ origin: isParty, path: isString, message: isString, range: isLSPRange },\n\t['range'],\n)\n\n/**\n * Checks whether a value carries one stage's outcome.\n *\n * @param value - The value to check\n * @returns True if the value is a check; false otherwise\n *\n * @example\n * ```ts\n * isCheck({ stage: 'lint', elapsed: 17, issues: [] }) // true\n * ```\n */\nexport const isCheck: Guard<Check> = recordOf({\n\tstage: isStage,\n\telapsed: isNumber,\n\tissues: arrayOf(isIssue),\n})\n\n/**\n * Checks whether a value names every resolved tool version.\n *\n * @param value - The value to check\n * @returns True if the value is a toolchain; false otherwise\n *\n * @example\n * ```ts\n * isToolchain({ typescript: '6.0.3', oxlint: '1.79.0', vitest: '4.1.11' }) // true\n * isToolchain({ typescript: '6.0.3' }) // false\n * ```\n */\nexport const isToolchain: Guard<Toolchain> = recordOf({\n\ttypescript: isString,\n\toxlint: isString,\n\tvitest: isString,\n})\n\n/**\n * Checks whether a value names one resolved TypeScript project and what it contained.\n *\n * @remarks\n * Rejects an empty path and an empty digest. A verdict names the project that judged it, so a\n * blank member here would report a project the type stage never applied.\n *\n * @param value - The value to check\n * @returns True if the value is a project; false otherwise\n *\n * @example\n * ```ts\n * isProject({ path: 'configs/src/tsconfig.core.json', digest: '3b674fdf121c85ef' }) // true\n * isProject({ path: 'configs/src/tsconfig.core.json', digest: '' }) // false\n * ```\n */\nexport const isProject: Guard<Project> = recordOf({\n\tpath: isNonEmptyString,\n\tdigest: isNonEmptyString,\n})\n\n/**\n * Checks whether a value carries a complete verdict.\n *\n * @remarks\n * Accepts any check count, because arity is the coordinator's obligation rather than this guard's:\n * a stage that could not start throws before a verdict is built, so a short list can only arrive\n * from a caller that assembled one by hand.\n *\n * @param value - The value to check\n * @returns True if the value is a verdict; false otherwise\n *\n * @example\n * ```ts\n * const basis = { id: '01J8Z0', digest: '6ca20c3b', toolchain, project, elapsed: 337 }\n * isVerdict({ ...basis, case: [], control: [] }) // true\n * isVerdict({ ...basis, case: [] }) // false\n * ```\n */\nexport const isVerdict: Guard<Verdict> = recordOf(\n\t{\n\t\tid: isString,\n\t\tdigest: isString,\n\t\ttoolchain: isToolchain,\n\t\tproject: isProject,\n\t\treason: isNonEmptyString,\n\t\tcase: arrayOf(isCheck),\n\t\tcontrol: arrayOf(isCheck),\n\t\telapsed: isNumber,\n\t\treceipt: isString,\n\t},\n\t['reason', 'receipt'],\n)\n","import type { Check, Issue, Stage, Verdict } from './types.js'\nimport { PROBE_STAGES, RECEIPT_PREFIX, RECEIPT_SEPARATOR } from './constants.js'\n\n/**\n * Renders one tool message as a single line an agent can classify and locate.\n *\n * @remarks\n * `Issue.range` is stored zero-based, and this is the only place it is rendered, so the one-based\n * line an editor shows is derived here and nowhere else. The rendered location carries the line\n * alone: a reader opens a file at a line, and the column would widen every issue line in every\n * verdict for a precision that address does not use.\n *\n * @param issue - The message and location a stage reported\n * @returns The bracketed origin, location, and message, separated by spaces\n *\n * @example\n * ```ts\n * const located: Issue = {\n * \torigin: 'claimant',\n * \tpath: 'src/core/greeting.ts',\n * \tmessage: 'not assignable',\n * \trange: { start: { line: 0, character: 6 }, end: { line: 0, character: 13 } },\n * }\n * const whole: Issue = {\n * \torigin: 'claimant',\n * \tpath: 'src/core/greeting.ts',\n * \tmessage: 'not assignable',\n * }\n * formatIssue(located) // '[claimant] src/core/greeting.ts:1 not assignable'\n * formatIssue(whole) // '[claimant] src/core/greeting.ts not assignable'\n * ```\n */\nexport function formatIssue(issue: Issue): string {\n\tconst range = issue.range\n\tconst where = range === undefined ? issue.path : `${issue.path}:${range.start.line + 1}`\n\treturn `[${issue.origin}] ${where} ${issue.message}`\n}\n\n/**\n * Renders one stage's outcome as its summary line followed by every message it reported.\n *\n * @remarks\n * A clean stage renders one line. The count appears even when it is zero, because that line is\n * what shows a reader the stage ran; a silent stage and a skipped stage read the same.\n *\n * @param check - The stage outcome to render\n * @returns The summary line, then one indented line per issue\n *\n * @example\n * ```ts\n * formatCheck({ stage: 'lint', elapsed: 17, issues: [] })\n * // 'lint: 0 issues (17 ms)'\n * ```\n */\nexport function formatCheck(check: Check): string {\n\tconst count = check.issues.length\n\tconst noun = count === 1 ? 'issue' : 'issues'\n\tconst summary = `${check.stage}: ${count} ${noun} (${check.elapsed} ms)`\n\tconst lines = check.issues.map((issue) => ` ${formatIssue(issue)}`)\n\treturn [summary, ...lines].join('\\n')\n}\n\n/**\n * Renders the closing line a rendered verdict ends with: the receipt it earned, or its absence.\n *\n * @remarks\n * One implementation serves every rendering of that line, so the line a reader quotes off a whole\n * verdict and the line a bounded reply falls back to cannot drift apart.\n *\n * @param verdict - The verdict whose `receipt` decides the line\n * @returns `receipt <token>` when the verdict carries a receipt, and `no receipt` when it does not\n *\n * @example\n * ```ts\n * formatProof(proven)\n * // 'receipt probe:6ca20c3bff623031d3955b9d1a76d71d:type:typescript@6.0.3:oxlint@1.79.0:vitest@4.1.11:configs/src/tsconfig.core.json@d61f11b52460b1c6707cfac2c6078d59'\n * formatProof(disproven) // 'no receipt'\n * ```\n */\nexport function formatProof(verdict: Verdict): string {\n\treturn verdict.receipt === undefined ? 'no receipt' : `receipt ${verdict.receipt}`\n}\n\n/**\n * Renders the smallest text a verdict can travel as: what it judged, and how it ended.\n *\n * @remarks\n * A reply whose bounds refuse both the record and the whole rendering answers with this text, so it\n * carries what a reader cannot recompute — the call's identity, the claim's digest, and the\n * control's reason when the verdict carries one — and closes with the same line `formatVerdict`\n * closes with. Every line here is a line `formatVerdict` renders at the same position relative to\n * the ones around it, so a reader meets one shape whichever answer arrived.\n *\n * @param verdict - The verdict to render\n * @returns The identity and claim lines, the reason when present, then the closing receipt line\n *\n * @example\n * ```ts\n * formatReceipt(disproven).split('\\n')\n * // ['probe 88a5addc-7d33-40dc-9a5a-104b71f8787d (549 ms)', 'claim 6ca20c3bff623031d3955b9d1a76d71d', 'no receipt']\n * ```\n */\nexport function formatReceipt(verdict: Verdict): string {\n\tconst reason = verdict.reason === undefined ? [] : [`reason ${verdict.reason}`]\n\treturn [\n\t\t`probe ${verdict.id} (${verdict.elapsed} ms)`,\n\t\t`claim ${verdict.digest}`,\n\t\t...reason,\n\t\tformatProof(verdict),\n\t].join('\\n')\n}\n\n/**\n * Renders a whole verdict as the text an agent reads.\n *\n * @remarks\n * Every stage appears for both the case and the control, so no failing stage can be masked by an\n * earlier one. The closing line always states the receipt or its absence, so a reader never has to\n * infer from silence whether the claim was proven.\n *\n * The heading lines carry everything the receipt binds, each in the name-then-value form the\n * toolchain line established: the call's identity, the claim it answered, the tools that ran, and\n * the project that judged the candidates. A verdict carrying the control's reason renders it next,\n * before either phase's checks. A reader comparing two verdicts therefore reads what differed\n * without recomputing anything.\n *\n * @param verdict - The verdict to render\n * @returns The identity, claim, toolchain, project, and reason when present, then every case and\n * control stage, then the receipt line\n *\n * @example\n * ```ts\n * formatVerdict(verdict).split('\\n')[1] // 'claim 6ca20c3bff623031d3955b9d1a76d71d'\n * ```\n */\nexport function formatVerdict(verdict: Verdict): string {\n\tconst { typescript, oxlint, vitest } = verdict.toolchain\n\tconst tools = `toolchain typescript ${typescript}, oxlint ${oxlint}, vitest ${vitest}`\n\tconst project = `project ${verdict.project.path} ${verdict.project.digest}`\n\tconst reason = verdict.reason === undefined ? [] : [`reason ${verdict.reason}`]\n\treturn [\n\t\t`probe ${verdict.id} (${verdict.elapsed} ms)`,\n\t\t`claim ${verdict.digest}`,\n\t\ttools,\n\t\tproject,\n\t\t...reason,\n\t\t...verdict.case.map((check) => `case ${formatCheck(check)}`),\n\t\t...verdict.control.map((check) => `control ${formatCheck(check)}`),\n\t\tformatProof(verdict),\n\t].join('\\n')\n}\n\n/**\n * Computes the proof token a verdict carries, or returns nothing when the claim was not proven.\n *\n * @remarks\n * A receipt is minted on these conditions together: both phases report one check per stage, the\n * case carries no issue, the control carries at least one `origin: 'claimant'` issue\n * at the stage it declared, and every other control stage carries no claimant issue. A control\n * that fails somewhere else has falsified the instrument rather than the claim, so no receipt is\n * minted for it.\n *\n * Both phases owe every stage, not the case alone. `stayed` reads the control entries a verdict\n * carries, so a control that omits a stage entirely would otherwise read as a stage that stayed\n * clean, and the receipt would certify an inspection that never ran. `prove` records every stage\n * for both phases, so this condition refuses only a verdict a caller assembled by hand.\n *\n * The token names every condition the verdict was reached under, so a receipt read away from its\n * verdict still says what was judged and what judged it: the claim's digest, the stage the control\n * broke at, the tool versions, and the project the candidate drafts were checked against.\n * The call's identity is deliberately absent. It carries no integrity and it is the only value\n * stopping two honest runs of one claim from producing one comparable string, so leaving it out is\n * what makes \"re-run `prove` and compare tokens\" a verification a reader can perform.\n *\n * The project goes last, and its own path and digest are joined by `@`. Split the token on\n * `RECEIPT_SEPARATOR`, take fields 0 through 5, and rejoin the remainder with that separator to\n * read the project field; inside it the digest is everything after the final `@`. That rule stays\n * total for a workspace-relative project path containing either character.\n *\n * An `origin: 'instrument'` issue in either phase means the inspection did not complete and\n * refuses the receipt. A workspace issue in the control does not decide whether the claimant's\n * candidate broke, while one in the case still means that the case did not run clean.\n *\n * @param verdict - The verdict whose case and control checks decide the outcome\n * @param stage - The stage the claim's control declared it must fail at\n * @returns The receipt token, or `undefined` when any condition fails\n *\n * @example\n * ```ts\n * computeReceipt(proven, 'type')\n * // 'probe:6ca20c3bff623031d3955b9d1a76d71d:type:typescript@6.0.3:oxlint@1.79.0:vitest@4.1.11:configs/src/tsconfig.core.json@d61f11b52460b1c6707cfac2c6078d59'\n * computeReceipt(proven, 'lint') // undefined\n * ```\n */\nexport function computeReceipt(verdict: Verdict, stage: Stage): string | undefined {\n\tconst ran = PROBE_STAGES.every(\n\t\t(name) =>\n\t\t\tverdict.case.filter((check) => check.stage === name).length === 1 &&\n\t\t\tverdict.control.filter((check) => check.stage === name).length === 1,\n\t)\n\tconst clean = verdict.case.every((check) => check.issues.length === 0)\n\tconst declared = verdict.control.find((check) => check.stage === stage)\n\tconst broke = declared?.issues.some((issue) => issue.origin === 'claimant') ?? false\n\tconst faulted =\n\t\tverdict.case.some((check) => check.issues.some((issue) => issue.origin === 'instrument')) ||\n\t\tverdict.control.some((check) => check.issues.some((issue) => issue.origin === 'instrument'))\n\tconst stayed = verdict.control.every(\n\t\t(check) => check.stage === stage || check.issues.every((issue) => issue.origin !== 'claimant'),\n\t)\n\tif (!ran || !clean || !broke || faulted || !stayed) return undefined\n\tconst { typescript, oxlint, vitest } = verdict.toolchain\n\treturn [\n\t\tRECEIPT_PREFIX,\n\t\tverdict.digest,\n\t\tstage,\n\t\t`typescript@${typescript}`,\n\t\t`oxlint@${oxlint}`,\n\t\t`vitest@${vitest}`,\n\t\t`${verdict.project.path}@${verdict.project.digest}`,\n\t].join(RECEIPT_SEPARATOR)\n}\n\n/**\n * Renders one generated specification: the caller's own test text, then the marker naming the\n * revision that wrote it.\n *\n * @remarks\n * The marker is what makes a file in a target workspace attributable to this package. A generated\n * specification carries text the caller supplied, so its name alone cannot separate it from a\n * developer's own file, and a sweep reading the name alone deletes that file. The marker goes last\n * so every line of the caller's text keeps the number a reported stack frame gives it, and the text\n * gains a terminating newline first so the marker is never appended to a line of the test.\n *\n * @param text - The caller's own test text\n * @param revision - The writing host's process id and a fresh UUID, joined by `-`\n * @returns The bytes the runtime stage writes for that revision\n *\n * @example\n * ```ts\n * formatSpecification(\"test('greets', () => {})\\n\", '4821-9f0c')\n * // \"test('greets', () => {})\\n// @orkestrel/probe generated specification 4821-9f0c\\n\"\n * ```\n */\nexport function formatSpecification(text: string, revision: string): string {\n\tconst body = text === '' || text.endsWith('\\n') ? text : `${text}\\n`\n\treturn `${body}// @orkestrel/probe generated specification ${revision}\\n`\n}\n\n/**\n * Checks whether one file's text is the generated specification written for one revision.\n *\n * @remarks\n * Reads the marker `formatSpecification` writes, taken from that function rather than restated, so\n * the writer and the reader cannot drift apart. An empty body renders the marker alone, which is\n * why the comparison is built from a call rather than from a second copy of the text.\n *\n * @param text - The file's full contents\n * @param revision - The revision the file's name declares\n * @returns True if the text ends with this package's marker for that revision; false otherwise\n *\n * @example\n * ```ts\n * matchesSpecification(formatSpecification('', '4821-9f0c'), '4821-9f0c') // true\n * matchesSpecification('export const NOTE = 1\\n', '4821-9f0c') // false\n * ```\n */\nexport function matchesSpecification(text: string, revision: string): boolean {\n\treturn text.endsWith(formatSpecification('', revision))\n}\n","import { arrayShape, literalShape, objectShape, stringShape } from '@orkestrel/contract'\nimport { PROBE_STAGES } from './constants.js'\n\n/**\n * Describes one proposed file a claim carries.\n *\n * @remarks\n * `path` is constrained to a non-empty string because every stage resolves a file by it, so an\n * empty path names no file. That minimum is the whole of the constraint here, and it is the one\n * place the wire contract is wider than what this package admits: `isDraft` enforces the same\n * minimum and also refuses an absolute path and one that traverses out of the workspace, which no\n * JSON Schema keyword can express. A caller that satisfies this shape can still be refused, and\n * `ProbeServer` names the member when it is.\n *\n * @example\n * ```ts\n * compileGuard(DRAFT_SHAPE)({ path: 'src/core/greeting.ts', text: '' }) // true\n * ```\n */\nexport const DRAFT_SHAPE = objectShape(\n\t{\n\t\tpath: stringShape({ min: 1, description: 'Workspace-relative path, which need not exist.' }),\n\t\ttext: stringShape({ description: \"The file's full contents.\" }),\n\t},\n\t{ description: 'One proposed file a claim carries.' },\n)\n\n/**\n * Describes the drafts a claim asserts about and the test that exercises them.\n *\n * @example\n * ```ts\n * const test = { path: 'tests/src/core/greeting.test.ts', text: '' }\n * compileGuard(CASE_SHAPE)({ files: [], test }) // true\n * ```\n */\nexport const CASE_SHAPE = objectShape(\n\t{\n\t\tfiles: arrayShape(DRAFT_SHAPE, { description: 'The candidate drafts the test imports.' }),\n\t\ttest: DRAFT_SHAPE,\n\t},\n\t{ description: 'The drafts a claim asserts about, and the test that exercises them.' },\n)\n\n/**\n * Describes the negative control, which is a case plus where and why it must break.\n *\n * @remarks\n * Built from `CASE_SHAPE`'s own properties rather than a second copy of them, so the control and\n * the case cannot describe a draft differently.\n *\n * @example\n * ```ts\n * const test = { path: 'tests/src/core/greeting.test.ts', text: '' }\n * compileGuard(CONTROL_SHAPE)({ files: [], test, stage: 'type', reason: 'must not compile' }) // true\n * ```\n */\nexport const CONTROL_SHAPE = objectShape(\n\t{\n\t\t...CASE_SHAPE.properties,\n\t\tstage: literalShape(PROBE_STAGES, { description: 'The stage this control must fail at.' }),\n\t\treason: stringShape({ min: 1, description: 'Why the control fails at that stage.' }),\n\t},\n\t{ description: 'A case that must fail, naming the stage where it must fail and why.' },\n)\n\n/**\n * Describes one claim and is the sole source of both the published tool schema and the guard\n * applied to an arriving claim.\n *\n * @remarks\n * The Model Context Protocol tool publishes `compileSchema(CLAIM_SHAPE)` and admits a call with\n * `isClaim`. Deriving the advertised schema from this one value is what stops the two from drifting\n * apart across a release on any member either can express. `isClaim` is narrower on the one member\n * neither a schema keyword nor this shape can carry — the containment rule on `Draft.path` — and\n * `validators.ts` names where.\n *\n * @example\n * ```ts\n * const schema = compileSchema(CLAIM_SHAPE)\n * const admits = compileGuard(CLAIM_SHAPE)\n * schema.type // 'object'\n * admits({ project: 'tsconfig.json' }) // false\n * ```\n */\nexport const CLAIM_SHAPE = objectShape(\n\t{\n\t\tproject: stringShape({\n\t\t\tmin: 1,\n\t\t\tdescription:\n\t\t\t\t'Workspace-relative TypeScript project the candidate drafts are checked against.',\n\t\t}),\n\t\tcase: CASE_SHAPE,\n\t\tcontrol: CONTROL_SHAPE,\n\t},\n\t{ description: 'One claim: the project, the case, and the control that must break.' },\n)\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/core/constants.ts","../../../src/core/errors.ts","../../../src/core/validators.ts","../../../src/core/helpers.ts","../../../src/core/shapers.ts"],"sourcesContent":["/**\n * Lists the stages a claim passes through, in the order a verdict reports them:\n * `['type', 'lint', 'runtime']`.\n *\n * @remarks\n * One list feeds the `Stage` type, the wire shape, the stage guard, and the receipt computation, so\n * a stage cannot be admitted by one and refused by another. The type derives from this list rather\n * than standing beside it, so a stage the list omits is a stage no member of the union names.\n *\n * @example\n * ```ts\n * PROBE_STAGES // ['type', 'lint', 'runtime']\n * ```\n */\nexport const PROBE_STAGES = Object.freeze(['type', 'lint', 'runtime'] as const)\n\n/**\n * Lists the parties that can own action on an issue or probe failure:\n * `['claimant', 'workspace', 'instrument']`.\n *\n * @remarks\n * One list feeds the `Party` type and its guard, so a party cannot be admitted by one and refused\n * by another.\n *\n * @example\n * ```ts\n * PROBE_PARTIES // ['claimant', 'workspace', 'instrument']\n * ```\n */\nexport const PROBE_PARTIES = Object.freeze(['claimant', 'workspace', 'instrument'] as const)\n\n/**\n * Lists the conditions that can end a probe operation:\n * `['refused', 'missing', 'malformed', 'destroyed', 'deadline']`.\n *\n * @remarks\n * One list feeds the code union and the error guard, so a condition cannot be constructed by one\n * and refused by the other. The guard reads this list rather than the class, which is what lets it\n * refuse a lookalike carrying a condition this package never declared.\n *\n * @example\n * ```ts\n * PROBE_ERROR_CODES // ['refused', 'missing', 'malformed', 'destroyed', 'deadline']\n * ```\n */\nexport const PROBE_ERROR_CODES = Object.freeze([\n\t'refused',\n\t'missing',\n\t'malformed',\n\t'destroyed',\n\t'deadline',\n] as const)\n\n/**\n * Names the leading token every receipt carries, `'probe'`.\n *\n * @remarks\n * A receipt travels away from the verdict that minted it — an agent pastes it into the promotion\n * action — so it names itself rather than relying on where it was found.\n *\n * @example\n * ```ts\n * const receipt =\n * \t'probe:6ca20c3bff623031d3955b9d1a76d71d:type:typescript@6.0.3:oxlint@1.79.0:vitest@4.1.11:configs/src/tsconfig.core.json@d61f11b52460b1c6707cfac2c6078d59'\n * receipt.startsWith(RECEIPT_PREFIX) // true\n * ```\n */\nexport const RECEIPT_PREFIX = 'probe'\n\n/**\n * Names the character joining a receipt's tokens, `':'`.\n *\n * @remarks\n * A project path can contain this character, so the project field goes last and a reader rejoins\n * every field from index 6 onward with this separator rather than expecting a fixed count.\n *\n * @example\n * ```ts\n * const receipt =\n * \t'probe:6ca20c3bff623031d3955b9d1a76d71d:type:typescript@6.0.3:oxlint@1.79.0:vitest@4.1.11:configs/src/tsconfig.core.json@d61f11b52460b1c6707cfac2c6078d59'\n * receipt.split(RECEIPT_SEPARATOR).length // 7\n * ```\n */\nexport const RECEIPT_SEPARATOR = ':'\n\n/**\n * Names the default inspection deadline a `Probe` applies when its construction omits one,\n * 30,000 ms.\n *\n * @remarks\n * `ProbeOptions.deadline` overrides this value per instance.\n *\n * @example\n * ```ts\n * PROBE_DEADLINE // 30_000\n * ```\n */\nexport const PROBE_DEADLINE = 30_000\n\n/**\n * Bounds the default type warm at 90,000 ms, independently of active inspections.\n *\n * @remarks\n * `ProbeOptions.warm` overrides this value. Veneer's slowest measured warm on 2026-10-05 took\n * 50,000 ms; this bound leaves 40,000 ms for host contention without changing inspection budgets.\n *\n * @example\n * ```ts\n * PROBE_WARM // 90_000\n * ```\n */\nexport const PROBE_WARM = 90_000\n\n/**\n * Bounds consecutive failed warms or idle losses before a stage's floor is spent.\n *\n * @example\n * ```ts\n * PROBE_RESTARTS // 1\n * ```\n */\nexport const PROBE_RESTARTS = 1\n\n/**\n * Names the 2,000 ms bound the lint stage holds over the lifecycle exchanges the protocol leaves\n * to the server: the `initialize` reply warming waits for and the `shutdown` reply ending waits for.\n *\n * @remarks\n * It does not reach the diagnostics an inspection waits for, which the caller's own signal bounds.\n * The transport's cooperative window is half of it; `LINT_TEARDOWN` also accounts for process\n * termination and exit confirmation. Measured 2026-08-27: the workspace `oxlint --lsp` answers\n * `initialize` in 155 ms, so\n * this bound is more than ten times that reply.\n *\n * @example\n * ```ts\n * LINT_DEADLINE // 2_000\n * ```\n */\nexport const LINT_DEADLINE = 2_000\n\n/**\n * Bounds lint teardown at 16,000 ms, including protocol exchanges and process termination.\n *\n * @remarks\n * Shutdown and exit each have `LINT_DEADLINE`; transport grace and stream drain each have half\n * that bound. The installed process transport permits 5,000 ms for Windows tree termination and\n * another 5,000 ms for exit confirmation. This also covers POSIX escalation's shorter grace.\n * The process confirmation constant is not exposed by the declared LSP dependency.\n *\n * @example\n * ```ts\n * LINT_TEARDOWN // 16_000\n * ```\n */\nexport const LINT_TEARDOWN = LINT_DEADLINE * 3 + 5_000 * 2\n\n/**\n * Names the total enumerable key bound `ProbeServer` applies to inbound metadata and to produced\n * tool content alike, 4096.\n *\n * @remarks\n * `@orkestrel/mcp`'s default leaf is sized for metadata: a verdict costs 38 keys empty and 11 more\n * for each issue a stage reports, so the default carries a verdict whose control refuses one\n * declaration and stops carrying the next one. A whole tool-call result carrying the record beside\n * its rendering costs 44 keys plus 11 for each issue, so this bound carries a record reporting up\n * to 368 issues. Past that the reply falls back to the rendered text, and past the 4 MiB content\n * bound to the receipt block.\n *\n * @example\n * ```ts\n * PROBE_KEYS // 4096\n * ```\n */\nexport const PROBE_KEYS = 4096\n\n/**\n * Names the specification lifetime the runtime stage replaces its resident Vitest service at,\n * 64 specifications.\n *\n * @remarks\n * Vite retains one unresolved URL for every fresh specification path, so an inspection that writes\n * a specification grows a map the resident service never releases. The stage replaces that service\n * rather than deleting from each map it owns, and this is the count of specifications one service\n * carries before the replacement. Any bound holds the retention flat, because the replacement\n * releases everything the instance held; this value trades the cost of the replacement against how\n * far the map grows between them. On 2026-08-20, over this package's own workspace on the host\n * `guides/probe.md` § Cost names, two runs of 66 inspections put the crossing inspection at 480 ms\n * and 269 ms against median inspections of 156 ms and 155 ms.\n *\n * @example\n * ```ts\n * PROBE_SPECIFICATIONS // 64\n * ```\n */\nexport const PROBE_SPECIFICATIONS = 64\n\n/**\n * Names the Vite plugin the runtime stage installs into a target workspace's Vitest\n * configuration, `'orkestrel-runtime-overlay'`.\n *\n * @remarks\n * The stage declares the plugin under this name and reads a configured project's plugin list for\n * the same name to decide whether that project carries the overlay. One name feeds both, so a\n * project the stage instrumented is never reported as a workspace project it could not reach.\n *\n * @example\n * ```ts\n * RUNTIME_PLUGIN // 'orkestrel-runtime-overlay'\n * ```\n */\nexport const RUNTIME_PLUGIN = 'orkestrel-runtime-overlay'\n\n/**\n * Names the workspace-relative directory the type stage keeps its workspace mirror under,\n * `'tmp/type'`.\n *\n * @remarks\n * Each stage owns one directory beneath this one, named for the writing host's process id and a\n * fresh UUID, and deletes it at teardown. The directory sits under `tmp/` so nothing the stage\n * writes reaches a version-controlled path, and beside `tmp/probes/` rather than inside it because\n * the workbench Vitest project collects `tmp/probes/**` and would otherwise collect a mirrored test.\n * A target that runs its own `check`, `lint:check`, or `format:check` while a probe is resident\n * reads whatever it does not exclude, so exclude this directory there.\n *\n * @example\n * ```ts\n * TYPE_MIRROR // 'tmp/type'\n * ```\n */\nexport const TYPE_MIRROR = 'tmp/type'\n","import type { Party, ProbeErrorCode, ProbeErrorContext, ProbeErrorOptions } from './types.js'\nimport {\n\tINTRINSICS,\n\tholds,\n\tisError,\n\tisFunction,\n\tisNumber,\n\tisObject,\n\tisString,\n} from '@orkestrel/contract'\nimport { PROBE_ERROR_CODES, PROBE_PARTIES } from './constants.js'\n\n/**\n * Reports one probe failure under stable ownership and condition axes.\n *\n * @remarks\n * Every failure raised while serving a claim arrives as one of these, so a consumer catching from\n * `prove`, from a stage, or from a workspace leaf branches on {@link Party} and\n * {@link ProbeErrorCode} rather than on message text. Narrow a caught value with\n * {@link isProbeError} before reading either member. A pure leaf called directly with a value\n * `isClaim` would refuse raises the host's own fault unchanged, because those leaves are written to\n * be total over a validated claim rather than over every value.\n *\n * @example\n * ```ts\n * const error = new ProbeError('Path escapes the workspace: ../secrets.env', {\n * \torigin: 'claimant',\n * \tcode: 'refused',\n * \tcontext: { path: '../secrets.env' },\n * })\n * error.origin // 'claimant'\n * error.code // 'refused'\n * error.context?.path // '../secrets.env'\n * ```\n */\nexport class ProbeError extends Error {\n\toverride readonly name = 'ProbeError'\n\treadonly origin: Party\n\treadonly code: ProbeErrorCode\n\treadonly context?: ProbeErrorContext\n\n\t/**\n\t * Creates one classified probe failure.\n\t *\n\t * @param message - Human-readable description of what failed\n\t * @param options - The ownership, condition, optional structured context, and optional cause\n\t */\n\tconstructor(message: string, options: ProbeErrorOptions) {\n\t\tconst cause = options.cause\n\t\tsuper(message, cause === undefined ? undefined : { cause })\n\t\tObject.defineProperty(this, Symbol.for('@orkestrel/probe.ProbeError'), { value: this })\n\t\tthis.origin = options.origin\n\t\tthis.code = options.code\n\t\tif (options.context !== undefined) this.context = options.context\n\t}\n}\n\n/**\n * Checks whether an unknown value is a {@link ProbeError}.\n *\n * @remarks\n * Recognition requires the native `Error` base, a non-base prototype, an own global brand holding\n * the error itself, a declared party and condition, and context members of their declared types.\n * The brand carries recognition across duplicate installations and module formats. A subclass may\n * replace `name`. A transparent proxy, a plain `Error`, a property-only lookalike, and an error with\n * invalid members stay outside the type.\n *\n * @param value - The value to inspect\n * @returns True if the value is a `ProbeError` instance; false otherwise\n *\n * @example\n * ```ts\n * isProbeError(createDestroyedError('probe')) // true\n * isProbeError(new Error('The probe has been destroyed')) // false\n * ```\n */\nexport function isProbeError(value: unknown): value is ProbeError {\n\tif (!isError(value)) return false\n\treturn holds(() => {\n\t\tif (INTRINSICS.prototype(value) === Error.prototype) return false\n\t\tconst descriptor = INTRINSICS.describe(value, Symbol.for('@orkestrel/probe.ProbeError'))\n\t\tif (descriptor?.value !== value) return false\n\t\tconst origin: unknown = INTRINSICS.reflect.read(value, 'origin')\n\t\tconst code: unknown = INTRINSICS.reflect.read(value, 'code')\n\t\tconst context: unknown = INTRINSICS.reflect.read(value, 'context')\n\t\tif (context !== undefined) {\n\t\t\tif (!isObject(context) && !isFunction(context)) return false\n\t\t\tconst stage: unknown = INTRINSICS.reflect.read(context, 'stage')\n\t\t\tif (stage !== undefined && stage !== 'type' && stage !== 'lint' && stage !== 'runtime')\n\t\t\t\treturn false\n\t\t\tfor (const key of ['path', 'project', 'name']) {\n\t\t\t\tconst member: unknown = INTRINSICS.reflect.read(context, key)\n\t\t\t\tif (member !== undefined && !isString(member)) return false\n\t\t\t}\n\t\t\tconst deadline: unknown = INTRINSICS.reflect.read(context, 'deadline')\n\t\t\tif (deadline !== undefined && !isNumber(deadline)) return false\n\t\t}\n\t\treturn (\n\t\t\tPROBE_PARTIES.some((declared) => declared === origin) &&\n\t\t\tPROBE_ERROR_CODES.some((declared) => declared === code)\n\t\t)\n\t})\n}\n\n/**\n * Creates the failure raised when an instrument is used after it was torn down.\n *\n * @remarks\n * Every resident entity in this package refuses this way, so the subject names which one in the\n * message and the condition is the same for all of them. A caller that meets this builds a\n * replacement rather than retrying: teardown is permanent.\n *\n * @param subject - The torn-down entity, named as the message names it, such as `type stage`\n * @returns A typed teardown failure\n *\n * @example\n * ```ts\n * createDestroyedError('type stage').message // 'The type stage has been destroyed'\n * createDestroyedError('probe').code // 'destroyed'\n * ```\n */\nexport function createDestroyedError(subject: string): ProbeError {\n\treturn new ProbeError(`The ${subject} has been destroyed`, {\n\t\torigin: 'claimant',\n\t\tcode: 'destroyed',\n\t})\n}\n","import type { Guard } from '@orkestrel/contract'\nimport type {\n\tCase,\n\tStageResult,\n\tClaim,\n\tControl,\n\tDraft,\n\tIssue,\n\tParty,\n\tProject,\n\tStage,\n\tToolchain,\n\tVerdict,\n} from './types.js'\nimport {\n\tandOf,\n\tarrayOf,\n\tisNonEmptyString,\n\tisNumber,\n\tisString,\n\tliteralOf,\n\trecordOf,\n} from '@orkestrel/contract'\nimport { isLSPRange } from '@orkestrel/lsp'\nimport { PROBE_PARTIES, PROBE_STAGES } from './constants.js'\n\n/**\n * Checks whether a value names a stage.\n *\n * @param value - The value to check\n * @returns True if the value is a stage name; false otherwise\n *\n * @example\n * ```ts\n * isStage('lint') // true\n * isStage('typecheck') // false\n * ```\n */\nexport const isStage: Guard<Stage> = literalOf(PROBE_STAGES)\n\n/**\n * Checks whether a value carries a proposed file's path and text.\n *\n * @remarks\n * Rejects an empty path, an absolute path, or a path that resolves outside the workspace. A\n * contained relative path may contain `.` or may traverse to a parent that remains inside the\n * workspace. Every stage resolves a file by that path, so admitting such a path here would defer\n * the refusal until a stage had already accepted the draft. An `Issue` carries no such minimum\n * because this package produces issues rather than admits them.\n *\n * @param value - The value to check\n * @returns True if the value is a draft; false otherwise\n *\n * @example\n * ```ts\n * const text = \"export function createGreeting(): string {\\n\\treturn 'hi'\\n}\\n\"\n * isDraft({ path: 'src/core/factories.ts', text }) // true\n * isDraft({ path: '../../etc/hosts', text: '' }) // false\n * isDraft({ path: 'src/core/factories.ts' }) // false\n * ```\n */\nexport const isDraft: Guard<Draft> = recordOf({\n\tpath: andOf<string>(isNonEmptyString, (path) => {\n\t\tif (/^(?:[\\\\/]|[A-Za-z]:)/.test(path)) return false\n\t\treturn (\n\t\t\tpath.split(/[\\\\/]+/).reduce((depth, segment) => {\n\t\t\t\tif (depth < 0 || segment === '' || segment === '.') return depth\n\t\t\t\treturn segment === '..' ? depth - 1 : depth + 1\n\t\t\t}, 0) >= 0\n\t\t)\n\t}),\n\ttext: isString,\n})\n\n/**\n * Checks whether a value carries a claim's candidate drafts and its test.\n *\n * @param value - The value to check\n * @returns True if the value is a case; false otherwise\n *\n * @example\n * ```ts\n * const test = { path: 'tests/src/core/greeting.test.ts', text: 'test(\"greets\", () => {})\\n' }\n * isCase({ files: [], test }) // true\n * ```\n */\nexport const isCase: Guard<Case> = recordOf({ files: arrayOf(isDraft), test: isDraft })\n\n/**\n * Checks whether a value carries a case plus the stage and reason it must fail for.\n *\n * @param value - The value to check\n * @returns True if the value is a control; false otherwise\n *\n * @example\n * ```ts\n * const test = { path: 'tests/src/core/greeting.test.ts', text: 'test(\"greets\", () => {})\\n' }\n * isControl({ files: [], test, stage: 'type', reason: 'must not compile' }) // true\n * isControl({ files: [], test }) // false\n * ```\n */\nexport const isControl: Guard<Control> = recordOf({\n\tfiles: arrayOf(isDraft),\n\ttest: isDraft,\n\tstage: isStage,\n\treason: isNonEmptyString,\n})\n\n/**\n * Checks whether a value carries a project, a case, and a control.\n *\n * @remarks\n * Exact rather than open: a claim is this package's own record, so an unknown member is a caller\n * sending a contract this service does not implement rather than a wider caller it must tolerate.\n *\n * `CLAIM_SHAPE` is the wire contract's shape and this is the admission rule the tool enforces, and\n * the rule is strictly narrower than the shape. They agree on every member but `Draft.path`: the\n * shape constrains it to a non-empty string, while `isDraft` also refuses an absolute path\n * and one that traverses out of the workspace, so `../../etc/hosts` satisfies\n * `compileGuard(CLAIM_SHAPE)` and is refused here. `ProbeServer` advertises the shape and enforces\n * this rule, so it names the refused member rather than reporting only that the claim was invalid.\n *\n * @param value - The value to check\n * @returns True if the value is a claim; false otherwise\n *\n * @example\n * ```ts\n * isClaim({ project: 'tsconfig.json', case: subject, control }) // true\n * isClaim({ project: 'tsconfig.json', case: subject }) // false\n * ```\n */\nexport const isClaim: Guard<Claim> = recordOf({\n\tproject: isNonEmptyString,\n\tcase: isCase,\n\tcontrol: isControl,\n})\n\n/**\n * Checks whether a value names a party an issue carries.\n *\n * @param value - The value to check\n * @returns True if the value is a party; false otherwise\n *\n * @example\n * ```ts\n * isParty('claimant') // true\n * isParty('workspace') // true\n * isParty('instrument') // true\n * isParty('stage') // false\n * ```\n */\nexport const isParty: Guard<Party> = literalOf(PROBE_PARTIES)\n\n/**\n * Checks whether a value carries one message, its location, and the origin of the fault it names.\n *\n * @remarks\n * `origin` is required rather than optional. Every issue this package produces sets it, and a\n * receipt turns on it: a control's issue disproves the claim only when its origin names the\n * claimant.\n *\n * @param value - The value to check\n * @returns True if the value is an issue; false otherwise\n *\n * @example\n * ```ts\n * isIssue({ origin: 'claimant', path: 'src/core/greeting.ts', message: 'not assignable' }) // true\n * isIssue({ path: 'src/core/greeting.ts', message: 'not assignable' }) // false\n * ```\n */\nexport const isIssue: Guard<Issue> = recordOf(\n\t{ origin: isParty, path: isString, message: isString, range: isLSPRange },\n\t['range'],\n)\n\n/**\n * Checks whether a value carries one stage's outcome.\n *\n * @param value - The value to check\n * @returns True if the value is a stage result; false otherwise\n *\n * @example\n * ```ts\n * isStageResult({ stage: 'lint', elapsed: 17, issues: [] }) // true\n * ```\n */\nexport const isStageResult: Guard<StageResult> = recordOf({\n\tstage: isStage,\n\telapsed: isNumber,\n\tissues: arrayOf(isIssue),\n})\n\n/**\n * Checks whether a value names every resolved tool version.\n *\n * @param value - The value to check\n * @returns True if the value is a toolchain; false otherwise\n *\n * @example\n * ```ts\n * isToolchain({ typescript: '6.0.3', oxlint: '1.79.0', vitest: '4.1.11' }) // true\n * isToolchain({ typescript: '6.0.3' }) // false\n * ```\n */\nexport const isToolchain: Guard<Toolchain> = recordOf({\n\ttypescript: isString,\n\toxlint: isString,\n\tvitest: isString,\n})\n\n/**\n * Checks whether a value names one resolved TypeScript project and what it contained.\n *\n * @remarks\n * Rejects an empty path and an empty digest. A verdict names the project that judged it, so a\n * blank member here would report a project the type stage never applied.\n *\n * @param value - The value to check\n * @returns True if the value is a project; false otherwise\n *\n * @example\n * ```ts\n * isProject({ path: 'configs/src/tsconfig.core.json', digest: '3b674fdf121c85ef' }) // true\n * isProject({ path: 'configs/src/tsconfig.core.json', digest: '' }) // false\n * ```\n */\nexport const isProject: Guard<Project> = recordOf({\n\tpath: isNonEmptyString,\n\tdigest: isNonEmptyString,\n})\n\n/**\n * Checks whether a value carries a complete verdict.\n *\n * @remarks\n * Accepts any stage result count, because arity is the coordinator's obligation rather than this\n * guard's: a stage that could not start throws before a verdict is built, so a short list can only\n * arrive from a caller that assembled one by hand.\n *\n * @param value - The value to check\n * @returns True if the value is a verdict; false otherwise\n *\n * @example\n * ```ts\n * const basis = { id: '01J8Z0', digest: '6ca20c3b', toolchain, project, elapsed: 337 }\n * isVerdict({ ...basis, case: [], control: [] }) // true\n * isVerdict({ ...basis, case: [] }) // false\n * ```\n */\nexport const isVerdict: Guard<Verdict> = recordOf(\n\t{\n\t\tid: isString,\n\t\tdigest: isString,\n\t\ttoolchain: isToolchain,\n\t\tproject: isProject,\n\t\treason: isNonEmptyString,\n\t\tcase: arrayOf(isStageResult),\n\t\tcontrol: arrayOf(isStageResult),\n\t\telapsed: isNumber,\n\t\treceipt: isString,\n\t},\n\t['reason', 'receipt'],\n)\n","import type { StageResult, Issue, Stage, Verdict } from './types.js'\nimport { PROBE_STAGES, RECEIPT_PREFIX, RECEIPT_SEPARATOR } from './constants.js'\n\n/**\n * Renders one tool message as a single line an agent can classify and locate.\n *\n * @remarks\n * `Issue.range` is stored zero-based, and this is the only place it is rendered, so the one-based\n * line an editor shows is derived here and nowhere else. The rendered location carries the line\n * alone: a reader opens a file at a line, and the column would widen every issue line in every\n * verdict for a precision that address does not use.\n *\n * @param issue - The message and location a stage reported\n * @returns The bracketed origin, location, and message, separated by spaces\n *\n * @example\n * ```ts\n * const located: Issue = {\n * \torigin: 'claimant',\n * \tpath: 'src/core/greeting.ts',\n * \tmessage: 'not assignable',\n * \trange: { start: { line: 0, character: 6 }, end: { line: 0, character: 13 } },\n * }\n * const whole: Issue = {\n * \torigin: 'claimant',\n * \tpath: 'src/core/greeting.ts',\n * \tmessage: 'not assignable',\n * }\n * formatIssue(located) // '[claimant] src/core/greeting.ts:1 not assignable'\n * formatIssue(whole) // '[claimant] src/core/greeting.ts not assignable'\n * ```\n */\nexport function formatIssue(issue: Issue): string {\n\tconst range = issue.range\n\tconst where = range === undefined ? issue.path : `${issue.path}:${range.start.line + 1}`\n\treturn `[${issue.origin}] ${where} ${issue.message}`\n}\n\n/**\n * Renders one stage's outcome as its summary line followed by every message it reported.\n *\n * @remarks\n * A clean stage renders one line. The count appears even when it is zero, because that line is\n * what shows a reader the stage ran; a silent stage and a skipped stage read the same.\n *\n * @param result - The stage outcome to render\n * @returns The summary line, then one indented line per issue\n *\n * @example\n * ```ts\n * formatStageResult({ stage: 'lint', elapsed: 17, issues: [] })\n * // 'lint: 0 issues (17 ms)'\n * ```\n */\nexport function formatStageResult(result: StageResult): string {\n\tconst count = result.issues.length\n\tconst noun = count === 1 ? 'issue' : 'issues'\n\tconst summary = `${result.stage}: ${count} ${noun} (${result.elapsed} ms)`\n\tconst lines = result.issues.map((issue) => ` ${formatIssue(issue)}`)\n\treturn [summary, ...lines].join('\\n')\n}\n\n/**\n * Renders the closing line a rendered verdict ends with: the receipt it earned, or its absence.\n *\n * @remarks\n * One implementation serves every rendering of that line, so the line a reader quotes off a whole\n * verdict and the line a bounded reply falls back to cannot drift apart.\n *\n * @param verdict - The verdict whose `receipt` decides the line\n * @returns `receipt <token>` when the verdict carries a receipt, and `no receipt` when it does not\n *\n * @example\n * ```ts\n * formatProof(proven)\n * // 'receipt probe:6ca20c3bff623031d3955b9d1a76d71d:type:typescript@6.0.3:oxlint@1.79.0:vitest@4.1.11:configs/src/tsconfig.core.json@d61f11b52460b1c6707cfac2c6078d59'\n * formatProof(disproven) // 'no receipt'\n * ```\n */\nexport function formatProof(verdict: Verdict): string {\n\treturn verdict.receipt === undefined ? 'no receipt' : `receipt ${verdict.receipt}`\n}\n\n/**\n * Renders the smallest text a verdict can travel as: what it judged, and how it ended.\n *\n * @remarks\n * A reply whose bounds refuse both the record and the whole rendering answers with this text, so it\n * carries what a reader cannot recompute — the call's identity, the claim's digest, and the\n * control's reason when the verdict carries one — and closes with the same line `formatVerdict`\n * closes with. Every line here is a line `formatVerdict` renders at the same position relative to\n * the ones around it, so a reader meets one shape whichever answer arrived.\n *\n * @param verdict - The verdict to render\n * @returns The identity and claim lines, the reason when present, then the closing receipt line\n *\n * @example\n * ```ts\n * formatReceipt(disproven).split('\\n')\n * // ['probe 88a5addc-7d33-40dc-9a5a-104b71f8787d (549 ms)', 'claim 6ca20c3bff623031d3955b9d1a76d71d', 'no receipt']\n * ```\n */\nexport function formatReceipt(verdict: Verdict): string {\n\tconst reason = verdict.reason === undefined ? [] : [`reason ${verdict.reason}`]\n\treturn [\n\t\t`probe ${verdict.id} (${verdict.elapsed} ms)`,\n\t\t`claim ${verdict.digest}`,\n\t\t...reason,\n\t\tformatProof(verdict),\n\t].join('\\n')\n}\n\n/**\n * Renders a whole verdict as the text an agent reads.\n *\n * @remarks\n * Every stage appears for both the case and the control, so no failing stage can be masked by an\n * earlier one. The closing line always states the receipt or its absence, so a reader never has to\n * infer from silence whether the claim was proven.\n *\n * The heading lines carry everything the receipt binds, each in the name-then-value form the\n * toolchain line established: the call's identity, the claim it answered, the tools that ran, and\n * the project that judged the candidates. A verdict carrying the control's reason renders it next,\n * before either phase's stage results. A reader comparing two verdicts therefore reads what\n * differed without recomputing anything.\n *\n * @param verdict - The verdict to render\n * @returns The identity, claim, toolchain, project, and reason when present, then every case and\n * control stage, then the receipt line\n *\n * @example\n * ```ts\n * formatVerdict(verdict).split('\\n')[1] // 'claim 6ca20c3bff623031d3955b9d1a76d71d'\n * ```\n */\nexport function formatVerdict(verdict: Verdict): string {\n\tconst { typescript, oxlint, vitest } = verdict.toolchain\n\tconst tools = `toolchain typescript ${typescript}, oxlint ${oxlint}, vitest ${vitest}`\n\tconst project = `project ${verdict.project.path} ${verdict.project.digest}`\n\tconst reason = verdict.reason === undefined ? [] : [`reason ${verdict.reason}`]\n\treturn [\n\t\t`probe ${verdict.id} (${verdict.elapsed} ms)`,\n\t\t`claim ${verdict.digest}`,\n\t\ttools,\n\t\tproject,\n\t\t...reason,\n\t\t...verdict.case.map((result) => `case ${formatStageResult(result)}`),\n\t\t...verdict.control.map((result) => `control ${formatStageResult(result)}`),\n\t\tformatProof(verdict),\n\t].join('\\n')\n}\n\n/**\n * Computes the proof token a verdict carries, or returns nothing when the claim was not proven.\n *\n * @remarks\n * A receipt is minted on these conditions together: both phases report one stage result per stage,\n * the case carries no issue, the control carries at least one `origin: 'claimant'` issue\n * at the stage it declared, and every other control stage carries no claimant issue. A control\n * that fails somewhere else has falsified the instrument rather than the claim, so no receipt is\n * minted for it.\n *\n * Both phases owe every stage, not the case alone. `stayed` reads the control entries a verdict\n * carries, so a control that omits a stage entirely would otherwise read as a stage that stayed\n * clean, and the receipt would certify an inspection that never ran. `prove` records every stage\n * for both phases, so this condition refuses only a verdict a caller assembled by hand.\n *\n * The token names every condition the verdict was reached under, so a receipt read away from its\n * verdict still says what was judged and what judged it: the claim's digest, the stage the control\n * broke at, the tool versions, and the project the candidate drafts were checked against.\n * The call's identity is deliberately absent. It carries no integrity and it is the only value\n * stopping two honest runs of one claim from producing one comparable string, so leaving it out is\n * what makes \"re-run `prove` and compare tokens\" a verification a reader can perform.\n *\n * The project goes last, and its own path and digest are joined by `@`. Split the token on\n * `RECEIPT_SEPARATOR`, take fields 0 through 5, and rejoin the remainder with that separator to\n * read the project field; inside it the digest is everything after the final `@`. That rule stays\n * total for a workspace-relative project path containing either character.\n *\n * An `origin: 'instrument'` issue in either phase means the inspection did not complete and\n * refuses the receipt. A workspace issue in the control does not decide whether the claimant's\n * candidate broke, while one in the case still means that the case did not run clean.\n *\n * @param verdict - The verdict whose case and control stage results decide the outcome\n * @param stage - The stage the claim's control declared it must fail at\n * @returns The receipt token, or `undefined` when any condition fails\n *\n * @example\n * ```ts\n * computeReceipt(proven, 'type')\n * // 'probe:6ca20c3bff623031d3955b9d1a76d71d:type:typescript@6.0.3:oxlint@1.79.0:vitest@4.1.11:configs/src/tsconfig.core.json@d61f11b52460b1c6707cfac2c6078d59'\n * computeReceipt(proven, 'lint') // undefined\n * ```\n */\nexport function computeReceipt(verdict: Verdict, stage: Stage): string | undefined {\n\tconst ran = PROBE_STAGES.every(\n\t\t(name) =>\n\t\t\tverdict.case.filter((result) => result.stage === name).length === 1 &&\n\t\t\tverdict.control.filter((result) => result.stage === name).length === 1,\n\t)\n\tconst clean = verdict.case.every((result) => result.issues.length === 0)\n\tconst declared = verdict.control.find((result) => result.stage === stage)\n\tconst broke = declared?.issues.some((issue) => issue.origin === 'claimant') ?? false\n\tconst faulted =\n\t\tverdict.case.some((result) => result.issues.some((issue) => issue.origin === 'instrument')) ||\n\t\tverdict.control.some((result) => result.issues.some((issue) => issue.origin === 'instrument'))\n\tconst stayed = verdict.control.every(\n\t\t(result) =>\n\t\t\tresult.stage === stage || result.issues.every((issue) => issue.origin !== 'claimant'),\n\t)\n\tif (!ran || !clean || !broke || faulted || !stayed) return undefined\n\tconst { typescript, oxlint, vitest } = verdict.toolchain\n\treturn [\n\t\tRECEIPT_PREFIX,\n\t\tverdict.digest,\n\t\tstage,\n\t\t`typescript@${typescript}`,\n\t\t`oxlint@${oxlint}`,\n\t\t`vitest@${vitest}`,\n\t\t`${verdict.project.path}@${verdict.project.digest}`,\n\t].join(RECEIPT_SEPARATOR)\n}\n\n/**\n * Renders one generated specification: the caller's own test text, then the marker naming the\n * revision that wrote it.\n *\n * @remarks\n * The marker is what makes a file in a target workspace attributable to this package. A generated\n * specification carries text the caller supplied, so its name alone cannot separate it from a\n * developer's own file, and a sweep reading the name alone deletes that file. The marker goes last\n * so every line of the caller's text keeps the number a reported stack frame gives it, and the text\n * gains a terminating newline first so the marker is never appended to a line of the test.\n *\n * @param text - The caller's own test text\n * @param revision - The writing host's process id and a fresh UUID, joined by `-`\n * @returns The bytes the runtime stage writes for that revision\n *\n * @example\n * ```ts\n * formatSpecification(\"test('greets', () => {})\\n\", '4821-9f0c')\n * // \"test('greets', () => {})\\n// @orkestrel/probe generated specification 4821-9f0c\\n\"\n * ```\n */\nexport function formatSpecification(text: string, revision: string): string {\n\tconst body = text === '' || text.endsWith('\\n') ? text : `${text}\\n`\n\treturn `${body}// @orkestrel/probe generated specification ${revision}\\n`\n}\n\n/**\n * Checks whether one file's text is the generated specification written for one revision.\n *\n * @remarks\n * Reads the marker `formatSpecification` writes, taken from that function rather than restated, so\n * the writer and the reader cannot drift apart. An empty body renders the marker alone, which is\n * why the comparison is built from a call rather than from a second copy of the text.\n *\n * @param text - The file's full contents\n * @param revision - The revision the file's name declares\n * @returns True if the text ends with this package's marker for that revision; false otherwise\n *\n * @example\n * ```ts\n * matchesSpecification(formatSpecification('', '4821-9f0c'), '4821-9f0c') // true\n * matchesSpecification('export const NOTE = 1\\n', '4821-9f0c') // false\n * ```\n */\nexport function matchesSpecification(text: string, revision: string): boolean {\n\treturn text.endsWith(formatSpecification('', revision))\n}\n","import { arrayShape, literalShape, objectShape, stringShape } from '@orkestrel/contract'\nimport { PROBE_STAGES } from './constants.js'\n\n/**\n * Describes one proposed file a claim carries.\n *\n * @remarks\n * `path` is constrained to a non-empty string because every stage resolves a file by it, so an\n * empty path names no file. That minimum is the whole of the constraint here, and it is the one\n * place the wire contract is wider than what this package admits: `isDraft` enforces the same\n * minimum and also refuses an absolute path and one that traverses out of the workspace, which no\n * JSON Schema keyword can express. A caller that satisfies this shape can still be refused, and\n * `ProbeServer` names the member when it is.\n *\n * @example\n * ```ts\n * compileGuard(DRAFT_SHAPE)({ path: 'src/core/greeting.ts', text: '' }) // true\n * ```\n */\nexport const DRAFT_SHAPE = objectShape(\n\t{\n\t\tpath: stringShape({ min: 1, description: 'Workspace-relative path, which need not exist.' }),\n\t\ttext: stringShape({ description: \"The file's full contents.\" }),\n\t},\n\t{ description: 'One proposed file a claim carries.' },\n)\n\n/**\n * Describes the drafts a claim asserts about and the test that exercises them.\n *\n * @example\n * ```ts\n * const test = { path: 'tests/src/core/greeting.test.ts', text: '' }\n * compileGuard(CASE_SHAPE)({ files: [], test }) // true\n * ```\n */\nexport const CASE_SHAPE = objectShape(\n\t{\n\t\tfiles: arrayShape(DRAFT_SHAPE, { description: 'The candidate drafts the test imports.' }),\n\t\ttest: DRAFT_SHAPE,\n\t},\n\t{ description: 'The drafts a claim asserts about, and the test that exercises them.' },\n)\n\n/**\n * Describes the negative control, which is a case plus where and why it must break.\n *\n * @remarks\n * Built from `CASE_SHAPE`'s own properties rather than a second copy of them, so the control and\n * the case cannot describe a draft differently.\n *\n * @example\n * ```ts\n * const test = { path: 'tests/src/core/greeting.test.ts', text: '' }\n * compileGuard(CONTROL_SHAPE)({ files: [], test, stage: 'type', reason: 'must not compile' }) // true\n * ```\n */\nexport const CONTROL_SHAPE = objectShape(\n\t{\n\t\t...CASE_SHAPE.properties,\n\t\tstage: literalShape(PROBE_STAGES, { description: 'The stage this control must fail at.' }),\n\t\treason: stringShape({ min: 1, description: 'Why the control fails at that stage.' }),\n\t},\n\t{ description: 'A case that must fail, naming the stage where it must fail and why.' },\n)\n\n/**\n * Describes one claim and is the sole source of both the published tool schema and the guard\n * applied to an arriving claim.\n *\n * @remarks\n * The Model Context Protocol tool publishes `compileSchema(CLAIM_SHAPE)` and admits a call with\n * `isClaim`. Deriving the advertised schema from this one value is what stops the two from drifting\n * apart across a release on any member either can express. `isClaim` is narrower on the one member\n * neither a schema keyword nor this shape can carry — the containment rule on `Draft.path` — and\n * `validators.ts` names where.\n *\n * @example\n * ```ts\n * const schema = compileSchema(CLAIM_SHAPE)\n * const admits = compileGuard(CLAIM_SHAPE)\n * schema.type // 'object'\n * admits({ project: 'tsconfig.json' }) // false\n * ```\n */\nexport const CLAIM_SHAPE = objectShape(\n\t{\n\t\tproject: stringShape({\n\t\t\tmin: 1,\n\t\t\tdescription:\n\t\t\t\t'Workspace-relative TypeScript project the candidate drafts are checked against.',\n\t\t}),\n\t\tcase: CASE_SHAPE,\n\t\tcontrol: CONTROL_SHAPE,\n\t},\n\t{ description: 'One claim: the project, the case, and the control that must break.' },\n)\n"],"mappings":"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"}
@@ -730,8 +730,12 @@ function matchesLiveProcess(id) {
730
730
  * Collects every regular file a target workspace holds, skipping the trees no inspection reads.
731
731
  *
732
732
  * @remarks
733
- * Skips a directory named `.git`, `dist`, or `node_modules` at any depth, and the directory
734
- * {@link TYPE_MIRROR} names, which would otherwise carry this package's own mirror into the walk. A
733
+ * Skips a directory named `.git`, `dist`, or `node_modules` at any depth. Of the workspace-root
734
+ * `tmp` directory it walks only `tmp/probes`: the scaffold tmp layout holds the runtime probes there
735
+ * and nothing else an inspection reads, so a consumer's ignored data under `tmp/` is never read or
736
+ * copied. The match is the lowercase `tmp` spelling exactly, and a `tmp` directory below the root is
737
+ * walked like any other. The directory {@link TYPE_MIRROR} names is skipped as well, so this
738
+ * package's own mirror stays out of the walk if it ever moves outside `tmp`. A
735
739
  * directory this host cannot list is skipped rather than raised, so one unreadable directory in a
736
740
  * consumer's tree is not a reason to refuse the whole walk. A symbolic link is not carried, whether
737
741
  * it names a file or a directory, so a file reached only through one is absent from the result; a
@@ -748,7 +752,9 @@ function matchesLiveProcess(id) {
748
752
  function collectWorkspaceFiles(workspace) {
749
753
  const excluded = normalizePath(resolveWorkspaceFile(workspace, _src_core.TYPE_MIRROR));
750
754
  const files = [];
751
- const directories = [(0, node_path.resolve)(workspace)];
755
+ const root = (0, node_path.resolve)(workspace);
756
+ const scratch = (0, node_path.join)(root, "tmp");
757
+ const directories = [root];
752
758
  while (directories.length > 0) {
753
759
  const directory = directories.pop();
754
760
  if (directory === void 0) break;
@@ -756,6 +762,7 @@ function collectWorkspaceFiles(workspace) {
756
762
  const entries = reading.success ? reading.value : [];
757
763
  for (const entry of entries) {
758
764
  if (entry.name === ".git" || entry.name === "dist" || entry.name === "node_modules") continue;
765
+ if (directory === scratch && entry.name !== "probes") continue;
759
766
  const path = (0, node_path.join)(directory, entry.name);
760
767
  if (entry.isDirectory()) {
761
768
  if (normalizePath(path) !== excluded) directories.push(path);
@@ -815,12 +822,12 @@ async function guardStage(stage, operation) {
815
822
  *
816
823
  * @remarks
817
824
  * The published claim schema constrains `Draft.path` to a non-empty string and nothing else, while
818
- * `isDraft` also refuses an absolute path and one that escapes the workspace. That one member is
819
- * the whole of the difference between the advertised contract and the enforced one, so a caller
820
- * refused after satisfying the schema is refused here and nowhere else. The whole input must first
821
- * satisfy that schema. Each draft is then tested with `isDraft` itself rather than with a second
822
- * copy of its rule. Reports nothing for a value carrying no draft member, including one refused for
823
- * a member this contract does not declare.
825
+ * `isDraft` also refuses an absolute path and one that resolves outside the workspace. That one
826
+ * member is the whole of the difference between the advertised contract and the enforced one, so a
827
+ * caller refused after satisfying the schema is refused here and nowhere else. The whole input must
828
+ * first satisfy that schema. Each draft is then tested with `isDraft` itself rather than with a
829
+ * second copy of its rule. Reports nothing for a value carrying no draft member, including one
830
+ * refused for a member this contract does not declare.
824
831
  *
825
832
  * This function lives in the server helpers rather than in core: core's leaf pair, `helpers.ts` and
826
833
  * `validators.ts`, may not consume the shapes module this function reads `CLAIM_SHAPE` from, and
@@ -855,10 +862,11 @@ function findRefusedPaths(value) {
855
862
  * `tsc --showConfig` spells a path relative to the project file, so a printed record carries an
856
863
  * absolute path only where a project itself declares one. Rewriting a contained absolute path to
857
864
  * its relative form keeps one commit checked out at two roots reading to one digest. A path that
858
- * escapes the workspace is left as it stands, because rewriting it would name a file outside the
859
- * tree by a relative spelling. Key order is the compiler's own and it differs between the supported
860
- * majors, so sorting removes a difference that means nothing. A `__proto__` key is dropped, as
861
- * the earlier assignment-built record dropped it, so a digest stays the same across releases.
865
+ * resolves outside the workspace is left as it stands, because rewriting it would name a file
866
+ * outside the tree by a relative spelling. Key order is the compiler's own and it differs between
867
+ * the supported majors, so sorting removes a difference that means nothing. A `__proto__` key is
868
+ * dropped, as the earlier assignment-built record dropped it, so a digest stays the same across
869
+ * releases.
862
870
  *
863
871
  * @param workspace - The target workspace root
864
872
  * @param value - The value to rewrite
@@ -897,10 +905,10 @@ function normalizeValue(workspace, value) {
897
905
  *
898
906
  * @example
899
907
  * ```ts
900
- * computeDigest('/srv/checkout', { strict: true }).length // 32
908
+ * computeWorkspaceDigest('/srv/checkout', { strict: true }).length // 32
901
909
  * ```
902
910
  */
903
- function computeDigest(workspace, value) {
911
+ function computeWorkspaceDigest(workspace, value) {
904
912
  return (0, node_crypto.createHash)("sha256").update(JSON.stringify(normalizeValue(workspace, value))).digest("hex").slice(0, 32);
905
913
  }
906
914
  /**
@@ -1094,7 +1102,7 @@ var Overlay = class {
1094
1102
  }
1095
1103
  };
1096
1104
  // package.json
1097
- var version = "0.0.26";
1105
+ var version = "0.0.28";
1098
1106
  var peerDependencies = {
1099
1107
  "typescript": "^6.0.3 || ^7.0.2",
1100
1108
  "vitest": "^4.1.12 || ^5.0.5"
@@ -1105,7 +1113,7 @@ var peerDependencies = {
1105
1113
  *
1106
1114
  * @remarks
1107
1115
  * Starting launches the target workspace's Oxlint binary with its Language Server Protocol mode
1108
- * and hands the conversation to `@orkestrel/lsp`: `createStdioClientTransport` owns the child
1116
+ * and hands the conversation to `@orkestrel/lsp`: `createLSPStdioTransport` owns the child
1109
1117
  * process and its bytes, and `createLSPClient` owns the framing, the request correlation, the
1110
1118
  * capabilities this stage advertises, and the choice between pulled and pushed diagnostics. This
1111
1119
  * stage owns the workspace, the candidate identity, and the projection from a diagnostic to an
@@ -1133,7 +1141,7 @@ var peerDependencies = {
1133
1141
  * @example
1134
1142
  * ```ts
1135
1143
  * const stage = new LintStage('/srv/checkout')
1136
- * const check = await stage.inspect(subject, { signal: AbortSignal.timeout(30_000) })
1144
+ * const result = await stage.inspect(subject, { signal: AbortSignal.timeout(30_000) })
1137
1145
  * await stage.destroy()
1138
1146
  * ```
1139
1147
  */
@@ -1206,7 +1214,7 @@ var LintStage = class {
1206
1214
  }
1207
1215
  async #warm() {
1208
1216
  const binary = resolveWorkspaceBinary(this.#workspace, "oxlint");
1209
- const transport = (0, _orkestrel_lsp_server.createStdioClientTransport)({
1217
+ const transport = (0, _orkestrel_lsp_server.createLSPStdioTransport)({
1210
1218
  server: {
1211
1219
  command: [
1212
1220
  process.execPath,
@@ -1346,10 +1354,10 @@ var LintStage = class {
1346
1354
  * Vitest service after 64 specifications rather than deleting from each map that service owns. Any
1347
1355
  * bound holds the retention flat, because the replacement releases everything the instance held,
1348
1356
  * and 64 is the value chosen. The inspection that crosses the bound pays the replacement
1349
- * synchronously: it closes the resident service and warms a new one before it runs. On 2026-08-20,
1350
- * over this package's own workspace on the host `guides/probe.md` § Cost names, two runs of 66
1351
- * inspections put that one at 480 ms and 269 ms against median inspections of 156 ms and 155 ms, so
1352
- * budget one call in 64 at 110 ms to 330 ms above the rest.
1357
+ * synchronously: it closes the resident service and warms a fresh service before it runs. On
1358
+ * 2026-08-20, over this package's own workspace on the host `guides/probe.md` § Cost names, two
1359
+ * runs of 66 inspections put that one at 480 ms and 269 ms against median inspections of 156 ms and
1360
+ * 155 ms, so budget one call in 64 at 110 ms to 330 ms above the rest.
1353
1361
  *
1354
1362
  * Every issue this stage emits names its party, and the stage emits an issue for every party
1355
1363
  * `Party` declares. An `origin: 'claimant'` issue is a failure Vitest reported about the candidate.
@@ -1374,7 +1382,7 @@ var LintStage = class {
1374
1382
  * @example
1375
1383
  * ```ts
1376
1384
  * const stage = new RuntimeStage('/srv/checkout')
1377
- * const check = await stage.inspect(subject)
1385
+ * const result = await stage.inspect(subject)
1378
1386
  * await stage.destroy()
1379
1387
  * ```
1380
1388
  */
@@ -2020,7 +2028,7 @@ var RuntimeStage = class {
2020
2028
  * @example
2021
2029
  * ```ts
2022
2030
  * const stage = new TypeStage('/srv/checkout')
2023
- * const check = await stage.inspect(subject, 'configs/src/tsconfig.core.json')
2031
+ * const result = await stage.inspect(subject, 'configs/src/tsconfig.core.json')
2024
2032
  * await stage.destroy()
2025
2033
  * ```
2026
2034
  */
@@ -2089,8 +2097,8 @@ var TypeStage = class {
2089
2097
  *
2090
2098
  * @param project - The workspace-relative TypeScript project to resolve
2091
2099
  * @returns The resolved workspace-relative path and the digest of its compiler options
2092
- * @throws When the project escapes the workspace, when the compiler refuses it, or when the
2093
- * stage has already been destroyed
2100
+ * @throws When the project resolves outside the workspace, when the compiler refuses it, or when
2101
+ * the stage has already been destroyed
2094
2102
  */
2095
2103
  resolve(project) {
2096
2104
  return guardStage(this.stage, this.#resolve(project));
@@ -2159,7 +2167,7 @@ var TypeStage = class {
2159
2167
  }
2160
2168
  return {
2161
2169
  path: contained,
2162
- digest: computeDigest(this.#workspace, config.compilerOptions)
2170
+ digest: computeWorkspaceDigest(this.#workspace, config.compilerOptions)
2163
2171
  };
2164
2172
  }
2165
2173
  async #destroy() {
@@ -2610,7 +2618,7 @@ var Probe = class {
2610
2618
  const started = performance.now();
2611
2619
  const id = (0, node_crypto.randomUUID)();
2612
2620
  const project = await this.#resolve(claim);
2613
- const digest = computeDigest(this.#workspace, {
2621
+ const digest = computeWorkspaceDigest(this.#workspace, {
2614
2622
  case: claim.case,
2615
2623
  control: claim.control
2616
2624
  });
@@ -2774,21 +2782,21 @@ var Probe = class {
2774
2782
  const beforeType = await this.#inspect(typeClaim.case, typeClaim);
2775
2783
  const beforeRuntime = await this.#inspect(runtimeClaim.case, runtimeClaim);
2776
2784
  const before = [...beforeType, ...beforeRuntime];
2777
- if (before.some((check) => check.issues.length > 0)) throw new _src_core.ProbeError(`The probe boot control did not begin clean\n${before.map(_src_core.formatCheck).join("\n")}`, {
2785
+ if (before.some((result) => result.issues.length > 0)) throw new _src_core.ProbeError(`The probe boot control did not begin clean\n${before.map(_src_core.formatStageResult).join("\n")}`, {
2778
2786
  origin: "instrument",
2779
2787
  code: "malformed"
2780
2788
  });
2781
2789
  resolveWorkspaceFile(this.#workspace, (0, node_path.relative)(this.#workspace, typeDependency), true);
2782
2790
  overwriteFile(typeDependency, (0, _src_core.formatSpecification)("export type Signal = number\n", revision));
2783
2791
  const afterType = await this.#inspect(typeClaim.control, typeClaim);
2784
- const type = afterType.find((check) => check.stage === typeClaim.control.stage);
2785
- const tolerant = afterType.find((check) => check.stage === "runtime");
2786
- if (type === void 0 || type.issues.length === 0) throw new _src_core.ProbeError(`The probe boot type control did not detect a mutated dependency\n${afterType.map(_src_core.formatCheck).join("\n")}`, {
2792
+ const type = afterType.find((result) => result.stage === typeClaim.control.stage);
2793
+ const tolerant = afterType.find((result) => result.stage === "runtime");
2794
+ if (type === void 0 || type.issues.length === 0) throw new _src_core.ProbeError(`The probe boot type control did not detect a mutated dependency\n${afterType.map(_src_core.formatStageResult).join("\n")}`, {
2787
2795
  origin: "instrument",
2788
2796
  code: "malformed",
2789
2797
  context: { stage: typeClaim.control.stage }
2790
2798
  });
2791
- if (tolerant === void 0 || tolerant.issues.length > 0) throw new _src_core.ProbeError(`The probe boot type control did not remain runtime-clean\n${afterType.map(_src_core.formatCheck).join("\n")}`, {
2799
+ if (tolerant === void 0 || tolerant.issues.length > 0) throw new _src_core.ProbeError(`The probe boot type control did not remain runtime-clean\n${afterType.map(_src_core.formatStageResult).join("\n")}`, {
2792
2800
  origin: "instrument",
2793
2801
  code: "malformed",
2794
2802
  context: { stage: "runtime" }
@@ -2796,8 +2804,8 @@ var Probe = class {
2796
2804
  resolveWorkspaceFile(this.#workspace, (0, node_path.relative)(this.#workspace, runtimeDependency), true);
2797
2805
  overwriteFile(runtimeDependency, (0, _src_core.formatSpecification)("export const SIGNAL = 'after'\n", revision));
2798
2806
  const afterRuntime = await this.#inspect(runtimeClaim.control, runtimeClaim);
2799
- const runtime = afterRuntime.find((check) => check.stage === runtimeClaim.control.stage);
2800
- if (runtime === void 0 || runtime.issues.length === 0) throw new _src_core.ProbeError(`The probe boot runtime control did not detect a mutated dependency\n${afterRuntime.map(_src_core.formatCheck).join("\n")}`, {
2807
+ const runtime = afterRuntime.find((result) => result.stage === runtimeClaim.control.stage);
2808
+ if (runtime === void 0 || runtime.issues.length === 0) throw new _src_core.ProbeError(`The probe boot runtime control did not detect a mutated dependency\n${afterRuntime.map(_src_core.formatStageResult).join("\n")}`, {
2801
2809
  origin: "instrument",
2802
2810
  code: "malformed",
2803
2811
  context: { stage: runtimeClaim.control.stage }
@@ -3261,7 +3269,7 @@ exports.TypeStage = TypeStage;
3261
3269
  exports.buildRevisionPath = buildRevisionPath;
3262
3270
  exports.captureListeners = captureListeners;
3263
3271
  exports.collectWorkspaceFiles = collectWorkspaceFiles;
3264
- exports.computeDigest = computeDigest;
3272
+ exports.computeWorkspaceDigest = computeWorkspaceDigest;
3265
3273
  exports.createHandshakeError = createHandshakeError;
3266
3274
  exports.describeUnknown = describeUnknown;
3267
3275
  exports.escapesRoot = escapesRoot;