diff --git a/hypaware-core/plugins-workspace/claude-account/src/index.js b/hypaware-core/plugins-workspace/claude-account/src/index.js index 121713e8..23a2c4b0 100644 --- a/hypaware-core/plugins-workspace/claude-account/src/index.js +++ b/hypaware-core/plugins-workspace/claude-account/src/index.js @@ -4,6 +4,7 @@ import { spawn } from 'node:child_process' import http from 'node:http' import readline from 'node:readline/promises' +import { askLineOnce } from '../../../../src/core/cli/line_asker.js' import { CLAUDE_ACCOUNT_CONFIG_SECTION, resolveMode, validateClaudeAccountConfig } from './config.js' import { resolveCredential } from './credential.js' import { @@ -22,6 +23,7 @@ import { } from './store.js' /** + * @import { Interface } from 'node:readline/promises' * @import { PluginActivationContext, CommandRunContext } from '../../../../hypaware-plugin-kernel-types.js' * @import { AnthropicCredentialCapability } from './types.js' */ @@ -131,6 +133,43 @@ async function runCredential(cmdCtx, config, stateDir) { } } +/** + * The `Code: ` paste fallback as its own lane, so the rule it encodes for + * a stdin that can no longer answer is readable and testable without a + * browser. + * + * `rl.question` leaves its promise permanently unsettled at EOF, so with + * no loopback listener to race - the port could not be bound, or the + * consumer flow fell back to the hosted callback - a login on a stdin + * that dried up waited forever on a paste that could never arrive. + * `askLineOnce` settles that as `null`, and the two cases part here: + * + * - No listener: nothing else can finish the login, so EOF is a real + * failure and says so, and `runLogin` prints it and exits 1. + * - A listener is up: the browser can still land on it, so the paste lane + * deliberately stays pending rather than losing the race for it. An EOF + * on the fallback input is not evidence about the primary flow, and a + * rejection here would settle `Promise.race` and abort a sign-in that + * was still on its way. + * + * `null` is not folded into the empty line the way the wizard's prompts + * fold it: `parsePastedAuthorization('')` throws `empty authorization + * code`, so an unanswerable prompt would report itself as a malformed + * paste the user never made. + * + * @ref LLP 0190#eof-everywhere [implements]: a spent stdin settles the prompt instead of waiting on an answer that can never come; this prompt has no default, so it settles as a failure rather than an answer + * + * @param {{ rl: Interface, stdin: NodeJS.ReadableStream, hasCallback: boolean }} args + * @returns {Promise<{ code: string, state: string }>} + */ +export function pasteAuthorizationLane({ rl, stdin, hasCallback }) { + return askLineOnce(rl, stdin, 'Code: ').then((pasted) => { + if (pasted !== null) return parsePastedAuthorization(pasted) + if (hasCallback) return new Promise(() => {}) + throw new Error('stdin ended before an authorization code was pasted') + }) +} + /** * @param {CommandRunContext} cmdCtx * @param {'org_key' | 'subscription'} mode @@ -164,9 +203,15 @@ async function runLogin(cmdCtx, mode, stateDir) { output: /** @type {NodeJS.WritableStream} */ (/** @type {unknown} */ (cmdCtx.stdout)), }) try { - const pastePromise = rl.question('Code: ').then((pasted) => parsePastedAuthorization(pasted)) - // A settled race leaves the loser pending; readline close (finally) - // rejects a pending question, so keep that rejection handled. + const pastePromise = pasteAuthorizationLane({ + rl, + stdin: /** @type {NodeJS.ReadableStream} */ (cmdCtx.stdin), + hasCallback: callback !== null && callback !== undefined, + }) + // A settled race leaves the loser pending. Closing the interface does + // not settle it either way (that is the defect `askLineOnce` works + // around), so what is guarded here is a malformed paste landing after + // the callback already won: keep that rejection handled. pastePromise.catch(() => {}) const { code, state } = await (callback ? Promise.race([callback.result, pastePromise]) diff --git a/llp/0190-wizard-defaults-gate.decision.md b/llp/0190-wizard-defaults-gate.decision.md index 3613e4df..5149bb3b 100644 --- a/llp/0190-wizard-defaults-gate.decision.md +++ b/llp/0190-wizard-defaults-gate.decision.md @@ -125,6 +125,38 @@ everything behind the prompt - the store schema, editor semantics over shown candidates, seam enforcement, corrupt-store fail-closed - is LLP 0188's and is unchanged. +**The EOF rule is the tree's, not this +lane's, and the asker that implements it is shared.** The defect is +`node:readline`'s, so it is at every readline prompt HypAware has, and +the rule above answers all of them: **a stdin that can no longer answer +takes the default the prompt printed; a prompt whose enter has no +default settles as a cancel or a failure instead, never as an invented +answer.** `queuedLineAsker` moves out of `walkthrough.js` into +`src/core/cli/line_asker.js`, beside `stdio.js` and `flush-streams.js`, +where a plugin workspace can import it too; `askLineOnce` joins it for +the prompts that ask once on an interface that may be a real terminal, +keeping `rl.question` as the thing that writes the query (readline +redraws a terminal line from its own bookkeeping, so a query it never +saw is a query it cannot redraw) and replacing only its promise. + +Applied outward, the rule settles the exit code the class kept raising. +The wizard's fork menu prints `default 3`, and LLP 0129 #fork already +settled that the default is Quit, so EOF there is Quit and the wizard +exits 0 having written nothing: the same result the fork's TUI path +already returns for a real ctrl+c at that screen. 130 is not the answer +for a dropped terminal at a prompt that advertised a default, because a +prompt with a default cannot tell a dropped terminal from a bare enter +and should not pretend to; 130 stays where LLP 0135's cancel put it, at +the prompts whose enter answers nothing. The two `[y/N]` confirms +(`src/core/cli/confirm.js`, `src/core/plugin_install/confirm.js`) take +their printed no, which is the safe direction for the irreversible verbs +behind them, and their callers' exit codes are unchanged: an EOF decline +is reported exactly as a typed `n` is. The one prompt with no default is +`claude-account login`'s `Code: ` paste, and it does not invent one: with +no loopback listener left to finish the sign-in, EOF is a failure that +says so, and with a listener up the paste lane stays pending rather than +losing a race the browser may still win. + **One gate prompt shape for both lanes.** The gate is a `ConfirmSelectQuestion` asked through `defaultConfirmSelectPromptFactory`: a TUI select on a real TTY, a diff --git a/src/core/cli/confirm.js b/src/core/cli/confirm.js index 47b50866..b1785b17 100644 --- a/src/core/cli/confirm.js +++ b/src/core/cli/confirm.js @@ -3,6 +3,7 @@ import process from 'node:process' import readline from 'node:readline/promises' +import { askLineOnce } from './line_asker.js' import { isTty } from './stdio.js' /** @@ -17,18 +18,28 @@ import { isTty } from './stdio.js' * Anything other than `y`/`yes` is a no: the default has to be the safe * one for a verb nobody can undo. * + * That includes a terminal that stops being able to answer. `rl.question` + * leaves its promise permanently unsettled at EOF, so a ctrl+D or a + * dropped session hung the irreversible verb on its own confirmation + * instead of declining it. `askLineOnce` settles that case as `null`, + * read here as the empty line the `[y/N]` already treats as a no - so + * the EOF answer is the printed default, and cannot drift from it. + * + * @ref LLP 0190#eof-everywhere [implements]: a spent stdin lands on the prompt's stated default rather than waiting on an answer that can never come + * * @param {CommandRunContext} ctx * @param {string} question rendered verbatim, including its `[y/N]` suffix * @returns {Promise} */ export async function askYesNo(ctx, question) { + const input = /** @type {NodeJS.ReadableStream} */ (ctx.stdin ?? process.stdin) const rl = readline.createInterface({ - input: /** @type {NodeJS.ReadableStream} */ (ctx.stdin ?? process.stdin), + input, output: /** @type {NodeJS.WritableStream} */ (/** @type {unknown} */ (ctx.stderr)), }) try { - const answer = await rl.question(question) - return /^y(es)?$/i.test(answer.trim()) + const answer = await askLineOnce(rl, input, question) + return /^y(es)?$/i.test((answer ?? '').trim()) } finally { rl.close() } diff --git a/src/core/cli/line_asker.js b/src/core/cli/line_asker.js new file mode 100644 index 00000000..1627dafb --- /dev/null +++ b/src/core/cli/line_asker.js @@ -0,0 +1,160 @@ +// @ts-check + +/** + * @import { Interface } from 'node:readline/promises' + */ + +/** + * Ask one question and settle even when the stream can no longer answer. + * + * The same EOF defect `queuedLineAsker` documents below, for the prompts + * that ask exactly once and are not built with `terminal: false`. There, + * writing the query directly to the output stream is not equivalent to + * asking: on a real terminal readline redraws the line it is editing from + * its own cursor bookkeeping, and a query it never saw is a query it + * cannot redraw. So this keeps `rl.question` as the thing that writes and + * reads, and only replaces the promise, which is the part that is broken. + * + * Two settlements are added to it: `close`, for a stream that ends while + * the question is on screen (a terminal that dropped, a ctrl+D), and the + * stream's own `readableEnded`, for a stream that was already spent when + * the interface was built. Readline registers its `end` listener at + * construction, so the second case never emits `close` at all and a + * `close`-only guard still hangs on it. Both resolve `null`, which callers + * read as the empty line - the answer a bare enter would have given. + * + * Neither settlement may outrun an answer that did arrive, and both can: + * `close` fires synchronously, while `rl.question` hands its answer back + * through a promise, a microtask later. A stream that delivers the line + * and EOF in one burst (`Readable.from(['y\n'])`, any readable that + * pushes data and `null` together) would have `close` win that race and + * discard a real `y`, so the EOF settlements are deferred a turn and the + * delivered line settles first. Readline also routes the last line of a + * stream that ends without a trailing newline to `line` rather than to + * the pending question, which is a second answer `question` alone never + * sees, so that one is taken too - but only at EOF, and only if the + * question never got a line of its own. Readline emits `line` while no + * question is pending, so the same event also carries anything typed or + * pasted PAST the answer ("n\ny\n" in one burst), and settling on that + * directly would answer with the last line of the burst rather than the + * first: a typed `n` at an irreversible confirm would read as `y`. + * + * `rl.question`'s own promise is left permanently unsettled at EOF rather + * than rejected, so nothing here can leak an unhandled rejection; the + * handler attached to it is only there for the abort/close rejections + * newer readline versions may raise. + * + * @ref LLP 0190#eof-everywhere [implements]: a spent stdin settles on the prompt's stated default instead of waiting on an answer that can never come + * + * @param {Interface} rl + * @param {NodeJS.ReadableStream} input + * @param {string} prompt + * @returns {Promise} the answer line, `null` once the stream is spent + */ +export function askLineOnce(rl, input, prompt) { + return new Promise((resolve) => { + let settled = false + /** @param {string | null} line */ + const done = (line) => { + if (settled) return + settled = true + resolve(line) + } + /** + * The last line readline emitted outside the question, held rather + * than settled on. Only read if the question itself never answered. + * @type {string | null} + */ + let unaskedLine = null + // A turn behind the answer, never ahead of it: an answer delivered in + // the same burst as the EOF has to settle first. + const endOfInput = () => setImmediate(() => done(unaskedLine)) + rl.once('close', endOfInput) + // Readline emits `line` only while no question is pending, so this is + // either the unterminated last line of the stream (an answer the + // question never sees) or a line that arrived past the answer. Holding + // it for EOF takes the first without letting the second overwrite an + // answer the question already has. + rl.on('line', (line) => { + unaskedLine = line + }) + // Writes the query synchronously, on a spent stream too. + rl.question(prompt).then(done, () => done(null)) + if (/** @type {{ readableEnded?: boolean }} */ (input).readableEnded === true) endOfInput() + }) +} + +/** + * Ceiling on the answer lines held for a still-unasked prompt. A prompt + * interface asks a small fixed number of questions and is closed straight + * after, so anything past a handful is unreadable backlog: a pipe that + * floods stdin (`yes |`) must not grow an array for as long as the prompt + * is on screen. + */ +const MAX_QUEUED_LINES = 4 + +/** + * Read answer lines off a readline interface without losing one and + * without ever waiting on a stream that can no longer answer. + * + * `rl.question()` cannot do either job here. It registers its `line` + * listener only when it is called, so a second answer line arriving in + * the same chunk as the first ("y\n3\n" from a pipe) is emitted and + * dropped before a re-ask can ask: readline emits both synchronously + * and the next `question()` is a microtask away. And at EOF its promise + * is left permanently unsettled - the interface closes, `question` + * neither resolves nor rejects - which is a hang, or a silent + * "unsettled top-level await" exit. Queueing every line from + * construction fixes the first; resolving the pending ask as `null` on + * `close` fixes the second. + * + * `close` alone is not enough for the second job. Readline registers its + * `end` listener when the interface is built, so an interface built over + * a stream that has ALREADY ended never sees an `end` and never emits + * `close` - the second prompt asked on a spent stdin would wait forever + * even though the first resolved. The stream's own `readableEnded` is + * the answer readline can no longer give, so it seeds `closed` here. + * + * This lives beside `stdio.js` and `flush-streams.js` rather than inside + * any one prompt: every readline prompt in the tree has the same EOF + * defect, and a shared asker is what keeps the answer to it in one place. + * + * @ref LLP 0190#eof-everywhere [implements]: a spent stdin settles on the prompt's stated default instead of waiting on an answer that can never come + * + * @param {Interface} rl + * @param {NodeJS.ReadableStream} input + * @param {NodeJS.WritableStream} output + * @returns {(prompt: string) => Promise} writes one prompt and takes the next line, `null` once the stream is spent + */ +export function queuedLineAsker(rl, input, output) { + /** @type {string[]} */ + const queued = [] + /** @type {((line: string | null) => void) | null} */ + let waiting = null + let closed = /** @type {{ readableEnded?: boolean }} */ (input).readableEnded === true + const take = () => { + const resolve = waiting + waiting = null + return resolve + } + rl.on('line', (line) => { + const resolve = take() + if (resolve) resolve(line) + else if (queued.length < MAX_QUEUED_LINES) queued.push(line) + }) + rl.on('close', () => { + closed = true + const resolve = take() + if (resolve) resolve(null) + }) + return function askLine(prompt) { + // Byte-identical to what `rl.question` writes: with `terminal: false` + // readline puts the query straight on the output stream. + output.write(prompt) + if (queued.length > 0) return Promise.resolve(/** @type {string} */ (queued.shift())) + if (closed) return Promise.resolve(null) + return new Promise((resolve) => { + waiting = resolve + }) + } +} diff --git a/src/core/cli/walkthrough.js b/src/core/cli/walkthrough.js index 5ced86ff..920d9e1b 100644 --- a/src/core/cli/walkthrough.js +++ b/src/core/cli/walkthrough.js @@ -15,6 +15,7 @@ import { materializeClientAssets } from '../runtime/client_assets.js' import { clientAssetStateRoot } from '../runtime/client_asset_ledger.js' import { buildPluginCatalog } from '../plugin_catalog.js' import { detectPickerSources } from './detect.js' +import { queuedLineAsker } from './line_asker.js' import { withSpinner } from './spinner.js' import { multiselect, select } from './tui/index.js' import { PromptBackRequestedError, PromptCancelledError, isPromptCancelledError } from './tui/runtime.js' @@ -29,7 +30,6 @@ import { shouldUseTui } from './tui-router.js' export const WALKTHROUGH_CANCEL_EXIT_CODE = 130 /** - * @import { Interface } from 'node:readline/promises' * @import { AiGatewayCapability, CapabilityRegistry, HypAwareV2Config, PluginConfigInstance, PluginName, SinkConfigInstance } from '../../../hypaware-plugin-kernel-types.js' * @import { ClientDescriptor, PickerDescriptor } from '../../../src/core/types.js' * @import { DaemonInstallOptions } from '../../../src/core/daemon/types.js' @@ -84,74 +84,11 @@ export function resolveHypHome(env) { */ const MAX_MALFORMED_REASKS = 1 -/** - * Ceiling on the answer lines held for a still-unasked prompt. An - * interface asks at most `1 + MAX_MALFORMED_REASKS` questions and is - * closed straight after, so anything past a handful is unreadable - * backlog: a pipe that floods stdin (`yes |`) must not grow an array - * for as long as the prompt is on screen. - */ -const MAX_QUEUED_LINES = 4 - -/** - * Read answer lines off a readline interface without losing one and - * without ever waiting on a stream that can no longer answer. - * - * `rl.question()` cannot do either job here. It registers its `line` - * listener only when it is called, so a second answer line arriving in - * the same chunk as the first ("y\n3\n" from a pipe) is emitted and - * dropped before a re-ask can ask: readline emits both synchronously - * and the next `question()` is a microtask away. And at EOF its promise - * is left permanently unsettled - the interface closes, `question` - * neither resolves nor rejects - which is a hang, or a silent - * "unsettled top-level await" exit. Queueing every line from - * construction fixes the first; resolving the pending ask as `null` on - * `close` fixes the second. - * - * `close` alone is not enough for the second job. Readline registers its - * `end` listener when the interface is built, so an interface built over - * a stream that has ALREADY ended never sees an `end` and never emits - * `close` - the second prompt asked on a spent stdin would wait forever - * even though the first resolved. The stream's own `readableEnded` is - * the answer readline can no longer give, so it seeds `closed` here. - * - * @param {Interface} rl - * @param {NodeJS.ReadableStream} input - * @param {NodeJS.WritableStream} output - * @returns {(prompt: string) => Promise} writes one prompt and takes the next line, `null` once the stream is spent - */ -function queuedLineAsker(rl, input, output) { - /** @type {string[]} */ - const queued = [] - /** @type {((line: string | null) => void) | null} */ - let waiting = null - let closed = /** @type {{ readableEnded?: boolean }} */ (input).readableEnded === true - const take = () => { - const resolve = waiting - waiting = null - return resolve - } - rl.on('line', (line) => { - const resolve = take() - if (resolve) resolve(line) - else if (queued.length < MAX_QUEUED_LINES) queued.push(line) - }) - rl.on('close', () => { - closed = true - const resolve = take() - if (resolve) resolve(null) - }) - return function askLine(prompt) { - // Byte-identical to what `rl.question` writes: with `terminal: false` - // readline puts the query straight on the output stream. - output.write(prompt) - if (queued.length > 0) return Promise.resolve(/** @type {string} */ (queued.shift())) - if (closed) return Promise.resolve(null) - return new Promise((resolve) => { - waiting = resolve - }) - } -} +// `queuedLineAsker` used to live here, module-private. It is now shared +// from `./line_asker.js`: the same EOF defect it was written for exists at +// every readline prompt in the tree (the wizard's fork menu, the y/N +// confirms, a plugin's OAuth paste fallback), and one asker they all read +// through is what keeps the answer in one place. /** * Build the default interactive prompt. Uses Node's `readline` against diff --git a/src/core/cli/wizard/fork.js b/src/core/cli/wizard/fork.js index 45c4373d..d067d03c 100644 --- a/src/core/cli/wizard/fork.js +++ b/src/core/cli/wizard/fork.js @@ -11,6 +11,7 @@ import readline from 'node:readline/promises' import { Attr, getLogger, withSpan } from '../../observability/index.js' import { collectHypAwareStatus } from '../../daemon/status.js' +import { queuedLineAsker } from '../line_asker.js' import { select } from '../tui/index.js' import { isPromptBackError, isPromptCancelledError } from '../tui/runtime.js' import { shouldUseTui } from '../tui-router.js' @@ -366,6 +367,22 @@ const FRIENDLY_CLIENT_LABELS = /** @type {Record} */ ({ * `allowBack`, a `b` answer resolves to `back` (the readline form of the * TUI's escape, LLP 0191); any other stray answer still quits. * + * A stdin that ends without a line is read through `queuedLineAsker` + * rather than `rl.question`, whose promise is left permanently unsettled + * at EOF - which on the wizard's first screen is the whole wizard + * hanging, or dying on an unsettled top-level await, before it has asked + * anything else. The EOF `null` is coalesced into the empty line rather + * than branched on, so a spent stdin takes exactly the default the + * prompt just printed (`default 3`, Quit) and the EOF answer cannot + * drift from the advertised one. Quit here means exit 0 with nothing + * written, which is also what the TUI path returns for a real ctrl+c at + * this screen (`isPromptCancelledError` -> `quit` above), so the + * fallback does not judge a dropped terminal more harshly than the TUI + * judges a deliberate cancel. + * + * @ref LLP 0190#eof-everywhere [implements]: a spent stdin lands on the prompt's stated default; 130 is for prompts whose enter has no default, and this one prints its own + * @ref LLP 0129#fork [constrained-by]: quit is the safe default at the fork, so the EOF answer is quit and the wizard reconfigures nothing by accident + * * @param {{ stdin?: NodeJS.ReadableStream, stdout: RunWizardForkOptions['stdout'] }} opts * @param {ConfiguredMenuOption[]} options * @param {string} title @@ -377,16 +394,17 @@ async function legacyMenuPrompt(opts, options, title, allowBack = false) { const output = /** @type {NodeJS.WritableStream} */ (/** @type {any} */ (opts.stdout)) const defaultIdx = Math.max(0, options.findIndex((o) => o.value === 'quit')) const rl = readline.createInterface({ input, output, terminal: false }) + const askLine = queuedLineAsker(rl, input, output) try { output.write(`${title}\n`) options.forEach((opt, i) => { output.write(` ${i + 1}) ${opt.label}\n`) if (opt.summary) output.write(` ${opt.summary}\n`) }) - const answer = await rl.question( + const answer = await askLine( `Choose [1-${options.length}, default ${defaultIdx + 1}${allowBack ? ', b back' : ''}]: ` ) - const trimmed = answer.trim() + const trimmed = (answer ?? '').trim() if (allowBack && trimmed.toLowerCase() === 'b') return 'back' if (trimmed === '') return options[defaultIdx]?.value ?? 'quit' const n = Number.parseInt(trimmed, 10) diff --git a/src/core/plugin_install/confirm.js b/src/core/plugin_install/confirm.js index dc413a6b..d3ba32ef 100644 --- a/src/core/plugin_install/confirm.js +++ b/src/core/plugin_install/confirm.js @@ -2,6 +2,8 @@ import readline from 'node:readline/promises' +import { queuedLineAsker } from '../cli/line_asker.js' + /** * @import { PluginLockEntry, PluginSourceSpec } from '../../../hypaware-plugin-kernel-types.js' * @import { ConfirmDecision, StagedArtifact } from '../../../src/core/plugin_install/types.js' @@ -136,6 +138,16 @@ export async function decideConfirmation({ yes, tty, ask }) { * counts as rejection. Closes the readline interface once an answer * comes back so the dispatcher doesn't leak file descriptors. * + * "Anything else" includes a stdin that ends without a line. + * `rl.question` leaves its promise permanently unsettled at EOF, so a + * terminal that dropped mid-install hung on the confirm rather than + * declining it. The asker's EOF `null` is coalesced into the empty line, + * which the printed `[y/N]` already reads as a no: the unanswerable + * question takes the default it advertised, and `decideConfirmation` + * turns that into the same `rejected` a typed `n` gives. + * + * @ref LLP 0190#eof-everywhere [implements]: a spent stdin lands on the prompt's stated default rather than waiting on an answer that can never come + * * @param {{ * stdin: NodeJS.ReadableStream, * stdout: NodeJS.WritableStream, @@ -146,9 +158,10 @@ export async function decideConfirmation({ yes, tty, ask }) { export function buildTtyPrompt({ stdin, stdout, promptText }) { return async function ask() { const rl = readline.createInterface({ input: stdin, output: stdout, terminal: false }) + const askLine = queuedLineAsker(rl, stdin, stdout) try { - const answer = await rl.question(promptText ?? 'Proceed? [y/N] ') - const trimmed = answer.trim().toLowerCase() + const answer = await askLine(promptText ?? 'Proceed? [y/N] ') + const trimmed = (answer ?? '').trim().toLowerCase() return trimmed === 'y' || trimmed === 'yes' } finally { rl.close() diff --git a/test/core/readline-prompt-eof.test.js b/test/core/readline-prompt-eof.test.js new file mode 100644 index 00000000..b6a2dc89 --- /dev/null +++ b/test/core/readline-prompt-eof.test.js @@ -0,0 +1,243 @@ +// @ts-check + +// The readline prompts outside `walkthrough.js` on a stdin that can no +// longer answer. `rl.question()` leaves its promise permanently unsettled +// at EOF, so each of these waited forever on an answer that could never +// arrive instead of taking the default it had just printed. +// +// The wizard's fork menu is the exposed one: it is the first screen `hyp +// init` shows, so a terminal whose stdin dries up or drops never got past +// it. The two `[y/N]` confirms are behind a TTY gate (LLP 0104, +// LLP 0155#delete-confirm), so what lands on them is a ctrl+D or a dropped +// session rather than a pipe. +// +// Every case is raced against a timer, because the pre-fix failure mode is +// a hang rather than a wrong value and an unraced assertion would never +// run at all. +// +// @ref LLP 0190#eof-everywhere [tests]: a spent stdin lands on the prompt's stated default rather than waiting on an answer that can never come +// @ref LLP 0129#fork [tests]: quit stays the fork's answer when the terminal stops answering, so nothing is reconfigured by accident + +import test from 'node:test' +import assert from 'node:assert/strict' +import { PassThrough, Readable } from 'node:stream' + +import { askYesNo } from '../../src/core/cli/confirm.js' +import { + buildForkOptions, + buildReturningGateOptions, + legacyForkPrompt, + legacyReturningGatePrompt, +} from '../../src/core/cli/wizard/fork.js' +import { buildTtyPrompt } from '../../src/core/plugin_install/confirm.js' + +/** Long enough to be unambiguous, short enough that a hang fails fast. */ +const SETTLE_MS = 500 + +/** Sentinel the race resolves to when the prompt never answers. */ +const HUNG = Symbol('hung') + +/** + * Resolve `promise` or fail the test with the hang it is guarding against. + * + * @template T + * @param {Promise} promise + * @param {string} what + * @returns {Promise} + */ +async function settles(promise, what) { + /** @type {NodeJS.Timeout | undefined} */ + let timer + const timeout = new Promise((resolve) => { + timer = setTimeout(() => resolve(HUNG), SETTLE_MS) + }) + try { + const result = await Promise.race([promise, timeout]) + assert.notEqual(result, HUNG, `${what} never settled within ${SETTLE_MS}ms - the prompt hung on EOF`) + return /** @type {T} */ (result) + } finally { + if (timer) clearTimeout(timer) + } +} + +function makeBuf() { + /** @type {string[]} */ + const chunks = [] + return { + write(/** @type {string} */ chunk) { + chunks.push(chunk) + return true + }, + text() { + return chunks.join('') + }, + } +} + +/** + * A stream that has already ended before the readline interface is built. + * Readline registers its `end` listener at construction, so this one never + * emits `close` - the case a `close`-only guard still hangs on. + * + * @returns {Promise} + */ +async function spentStdin() { + const stdin = new PassThrough() + stdin.resume() + stdin.end() + await new Promise((resolve) => setImmediate(resolve)) + return stdin +} + +test('fork menu takes its printed default on a stdin that ends without a line', async () => { + const stdin = new PassThrough() + const stdout = makeBuf() + const choice = legacyForkPrompt({ stdout: /** @type {any} */ (stdout), stderr: /** @type {any} */ (makeBuf()), stdin, env: {} }, buildForkOptions()) + stdin.end() + + // Quit, the default the menu printed, and the answer a bare enter gives: + // `runInitWizard` turns it into exit 0 with nothing written, which is + // also what the TUI path returns for a real ctrl+c at this screen. + assert.equal(await settles(choice, 'fork menu at EOF'), 'quit') + // The question is still asked, byte for byte: the default is taken + // because it was advertised, not instead of advertising it. + assert.match(stdout.text(), /Choose \[1-3, default 3\]: $/) +}) + +test('fork menu takes its printed default on a stdin that was already spent', async () => { + const stdin = await spentStdin() + const choice = legacyForkPrompt({ stdout: /** @type {any} */ (makeBuf()), stderr: /** @type {any} */ (makeBuf()), stdin, env: {} }, buildForkOptions()) + + assert.equal(await settles(choice, 'fork menu on a spent stdin'), 'quit') +}) + +test('fork menu still honours an explicit pick', async () => { + const stdin = new PassThrough() + const choice = legacyForkPrompt({ stdout: /** @type {any} */ (makeBuf()), stderr: /** @type {any} */ (makeBuf()), stdin, env: {} }, buildForkOptions()) + stdin.write('1\n') + + assert.equal(await settles(choice, 'fork menu with an answer'), 'team') +}) + +test('returning gate menu takes its printed default on a stdin that ends without a line', async () => { + const stdin = new PassThrough() + const stdout = makeBuf() + const choice = legacyReturningGatePrompt( + { stdout: /** @type {any} */ (stdout), stderr: /** @type {any} */ (makeBuf()), stdin, env: {} }, + buildReturningGateOptions() + ) + stdin.end() + + assert.equal(await settles(choice, 'returning gate at EOF'), 'quit') + assert.match(stdout.text(), /Choose \[1-3, default 3\]: $/) +}) + +test('askYesNo declines on a stdin that ends without a line', async () => { + const stdin = new PassThrough() + const stderr = makeBuf() + const answered = askYesNo(/** @type {any} */ ({ stdin, stderr }), 'Delete everything? [y/N] ') + stdin.end() + + // The `[y/N]` says no is the default, and an irreversible verb has to + // land there when the terminal stops being able to say otherwise. + assert.equal(await settles(answered, 'askYesNo at EOF'), false) + assert.match(stderr.text(), /\[y\/N\] $/) +}) + +test('askYesNo declines on a stdin that was already spent', async () => { + const stdin = await spentStdin() + const answered = askYesNo( + /** @type {any} */ ({ stdin, stderr: makeBuf() }), + 'Delete everything? [y/N] ' + ) + + assert.equal(await settles(answered, 'askYesNo on a spent stdin'), false) +}) + +test('askYesNo still honours an explicit yes', async () => { + const stdin = new PassThrough() + const answered = askYesNo( + /** @type {any} */ ({ stdin, stderr: makeBuf() }), + 'Delete everything? [y/N] ' + ) + stdin.write('yes\n') + + assert.equal(await settles(answered, 'askYesNo with an answer'), true) +}) + +test('askYesNo takes an answer delivered in the same burst as the EOF', async () => { + // `Readable.from` pushes the line and the EOF together, which is how a + // caller that injects a stdin usually builds one. The answer is there, + // so only a settlement that outran it could turn this `y` into a no. + const answered = askYesNo( + /** @type {any} */ ({ stdin: Readable.from(['y\n']), stderr: makeBuf() }), + 'Delete everything? [y/N] ' + ) + + assert.equal(await settles(answered, 'askYesNo on a one-burst stdin'), true) +}) + +test('askYesNo takes a final answer with no trailing newline', async () => { + const stdin = new PassThrough() + const answered = askYesNo( + /** @type {any} */ ({ stdin, stderr: makeBuf() }), + 'Delete everything? [y/N] ' + ) + // Readline hands this one to `line` rather than to the pending + // question, so it is an answer `rl.question` alone never sees. + stdin.end('y') + + assert.equal(await settles(answered, 'askYesNo on an unterminated answer'), true) +}) + +test('askYesNo answers with the line that answered it, not one that arrived after', async () => { + // Readline gives the first line to the pending question and emits every + // later one as `line`, so a burst carrying more than one line (a paste, + // or type-ahead for the next prompt) offers a second candidate answer. + // Taking it would read a typed `n` at an irreversible confirm as `y`, + // which is the one direction a `[y/N]` must never drift in. + const answered = askYesNo( + /** @type {any} */ ({ stdin: Readable.from(['n\ny\n']), stderr: makeBuf() }), + 'Delete everything? [y/N] ' + ) + + assert.equal(await settles(answered, 'askYesNo on a two-line burst'), false) +}) + +test('askYesNo keeps its answer when an unterminated line follows it', async () => { + // Same shape, with the trailing line arriving through the end-of-stream + // flush rather than as a line of its own. + const answered = askYesNo( + /** @type {any} */ ({ stdin: Readable.from(['n\ny']), stderr: makeBuf() }), + 'Delete everything? [y/N] ' + ) + + assert.equal(await settles(answered, 'askYesNo on a burst with an unterminated tail'), false) +}) + +test('plugin install confirm declines on a stdin that ends without a line', async () => { + const stdin = new PassThrough() + const stdout = makeBuf() + const ask = buildTtyPrompt({ stdin, stdout: /** @type {any} */ (stdout) }) + const answered = ask() + stdin.end() + + assert.equal(await settles(answered, 'plugin install confirm at EOF'), false) + assert.match(stdout.text(), /Proceed\? \[y\/N\] $/) +}) + +test('plugin install confirm declines on a stdin that was already spent', async () => { + const stdin = await spentStdin() + const ask = buildTtyPrompt({ stdin, stdout: /** @type {any} */ (makeBuf()) }) + + assert.equal(await settles(ask(), 'plugin install confirm on a spent stdin'), false) +}) + +test('plugin install confirm still honours an explicit yes', async () => { + const stdin = new PassThrough() + const ask = buildTtyPrompt({ stdin, stdout: /** @type {any} */ (makeBuf()) }) + const answered = ask() + stdin.write('y\n') + + assert.equal(await settles(answered, 'plugin install confirm with an answer'), true) +}) diff --git a/test/plugins/claude-account-paste-eof.test.js b/test/plugins/claude-account-paste-eof.test.js new file mode 100644 index 00000000..a294fca5 --- /dev/null +++ b/test/plugins/claude-account-paste-eof.test.js @@ -0,0 +1,156 @@ +// @ts-check + +// The `Code: ` paste fallback in `claude-account login` on a stdin that can +// no longer answer. `rl.question()` leaves its promise permanently unsettled +// at EOF, so a login with no loopback listener to race waited forever on a +// paste that could never arrive. +// +// The two halves are asymmetric on purpose, and that is what these cases +// pin: with no listener there is nothing else that can finish the login, so +// EOF is a failure and says so; with a listener up the browser can still +// land on it, so the paste lane must not settle and take the race away from +// a sign-in that was still on its way. +// +// Raced against a timer, because the pre-fix failure mode is a hang. +// +// @ref LLP 0190#eof-everywhere [tests]: a spent stdin settles the prompt instead of waiting on an answer that can never come + +import test from 'node:test' +import assert from 'node:assert/strict' +import readline from 'node:readline/promises' +import { PassThrough, Readable } from 'node:stream' + +import { pasteAuthorizationLane } from '../../hypaware-core/plugins-workspace/claude-account/src/index.js' + +const SETTLE_MS = 500 +const HUNG = Symbol('hung') + +/** + * @template T + * @param {Promise} promise + * @returns {Promise} + */ +async function raceSettle(promise) { + /** @type {NodeJS.Timeout | undefined} */ + let timer + const timeout = new Promise((resolve) => { + timer = setTimeout(() => resolve(HUNG), SETTLE_MS) + }) + try { + return await Promise.race([promise.then((v) => /** @type {any} */ ({ ok: v })), timeout]) + } catch (err) { + return /** @type {any} */ ({ err }) + } finally { + if (timer) clearTimeout(timer) + } +} + +function makeBuf() { + /** @type {string[]} */ + const chunks = [] + return { + write(/** @type {string} */ chunk) { + chunks.push(chunk) + return true + }, + text() { + return chunks.join('') + }, + } +} + +/** + * @param {PassThrough} stdin + * @param {{ write(chunk: string): unknown }} stdout + */ +function makeRl(stdin, stdout) { + return readline.createInterface({ + input: stdin, + output: /** @type {any} */ (stdout), + terminal: false, + }) +} + +test('paste lane fails, rather than hanging, when stdin ends with no listener to wait on', async () => { + const stdin = new PassThrough() + const stdout = makeBuf() + const rl = makeRl(stdin, stdout) + const lane = pasteAuthorizationLane({ rl, stdin, hasCallback: false }) + stdin.end() + + const settled = /** @type {any} */ (await raceSettle(lane)) + assert.notEqual(settled, HUNG, 'the paste lane never settled - it hung on EOF') + assert.ok(settled.err instanceof Error, 'EOF with no listener is a login failure, not a silent wait') + assert.match(settled.err.message, /stdin ended/) + assert.equal(stdout.text(), 'Code: ') + rl.close() +}) + +test('paste lane fails, rather than hanging, on a stdin that was already spent', async () => { + const stdin = new PassThrough() + stdin.resume() + stdin.end() + await new Promise((resolve) => setImmediate(resolve)) + + const rl = makeRl(stdin, makeBuf()) + const settled = /** @type {any} */ (await raceSettle(pasteAuthorizationLane({ rl, stdin, hasCallback: false }))) + assert.notEqual(settled, HUNG, 'the paste lane never settled on an already-spent stdin') + assert.ok(settled.err instanceof Error) + rl.close() +}) + +test('paste lane leaves the loopback listener to finish when stdin ends under it', async () => { + const stdin = new PassThrough() + const rl = makeRl(stdin, makeBuf()) + const lane = pasteAuthorizationLane({ rl, stdin, hasCallback: true }) + lane.catch(() => {}) + stdin.end() + + // The race a real login runs. An EOF on the fallback input is not + // evidence about the browser flow, so the listener still wins it. + const callbackResult = new Promise((resolve) => { + setTimeout(() => resolve({ code: 'from-browser', state: 'st' }), 20) + }) + const settled = /** @type {any} */ (await raceSettle(Promise.race([callbackResult, lane]))) + assert.deepEqual(settled.ok, { code: 'from-browser', state: 'st' }) + rl.close() +}) + +test('paste lane parses a code delivered in the same burst as the EOF', async () => { + // The paste and the EOF arrive together, which is what a stdin built + // from a string does. The EOF must not take the race off a paste that + // landed. + const stdin = Readable.from(['the-code#the-state\n']) + const rl = readline.createInterface({ input: stdin, output: /** @type {any} */ (makeBuf()), terminal: false }) + + const settled = /** @type {any} */ (await raceSettle( + pasteAuthorizationLane({ rl, stdin: /** @type {any} */ (stdin), hasCallback: false }) + )) + assert.deepEqual(settled.ok, { code: 'the-code', state: 'the-state' }) + rl.close() +}) + +test('paste lane parses the pasted code, not a line that followed it', async () => { + // Readline hands the first line to the pending question and emits every + // later one as `line`. Settling on `line` directly would exchange the + // last line of the burst for the code that actually answered the prompt. + const stdin = Readable.from(['the-code#the-state\nnoise#noise\n']) + const rl = readline.createInterface({ input: stdin, output: /** @type {any} */ (makeBuf()), terminal: false }) + + const settled = /** @type {any} */ (await raceSettle( + pasteAuthorizationLane({ rl, stdin: /** @type {any} */ (stdin), hasCallback: false }) + )) + assert.deepEqual(settled.ok, { code: 'the-code', state: 'the-state' }) + rl.close() +}) + +test('paste lane still parses a pasted code', async () => { + const stdin = new PassThrough() + const rl = makeRl(stdin, makeBuf()) + const lane = pasteAuthorizationLane({ rl, stdin, hasCallback: true }) + stdin.write('the-code#the-state\n') + + const settled = /** @type {any} */ (await raceSettle(lane)) + assert.deepEqual(settled.ok, { code: 'the-code', state: 'the-state' }) + rl.close() +}) diff --git a/x/burst.mjs b/x/burst.mjs new file mode 100644 index 00000000..9f69931e --- /dev/null +++ b/x/burst.mjs @@ -0,0 +1,39 @@ +import readline from 'node:readline/promises' +import { Readable } from 'node:stream' +import { askLineOnce, queuedLineAsker } from '../src/core/cli/line_asker.js' + +async function viaAskLineOnce(chunks, terminal) { + const input = Readable.from(chunks) + const out = { write: () => true } + const rl = readline.createInterface({ input, output: out, ...(terminal === undefined ? {} : { terminal }) }) + try { return await askLineOnce(rl, input, 'Proceed? [y/N] ') } finally { rl.close() } +} +async function viaPlainQuestion(chunks, terminal) { + const input = Readable.from(chunks) + const out = { write: () => true } + const rl = readline.createInterface({ input, output: out, ...(terminal === undefined ? {} : { terminal }) }) + try { + return await Promise.race([rl.question('Proceed? [y/N] '), new Promise(r => setTimeout(() => r(''), 300))]) + } finally { rl.close() } +} +async function viaQueued(chunks, terminal) { + const input = Readable.from(chunks) + const out = { write: () => true } + const rl = readline.createInterface({ input, output: out, ...(terminal === undefined ? {} : { terminal }) }) + try { return await queuedLineAsker(rl, input, out)('Proceed? [y/N] ') } finally { rl.close() } +} + +const cases = [ + ['single line', ['y\n']], + ['two lines one burst', ['y\n3\n']], + ['two lines separate chunks', ['y\n', '3\n']], + ['line + trailing junk no newline', ['y\nzzz']], + ['no newline only', ['y']], + ['empty EOF', []], +] +for (const [name, chunks] of cases) { + const a = await viaAskLineOnce(chunks) + const p = await viaPlainQuestion(chunks) + const q = await viaQueued(chunks) + console.log(`${name.padEnd(32)} askLineOnce=${JSON.stringify(a)} rl.question=${JSON.stringify(p)} queued=${JSON.stringify(q)}`) +} diff --git a/x/invert.mjs b/x/invert.mjs new file mode 100644 index 00000000..7a04ef89 --- /dev/null +++ b/x/invert.mjs @@ -0,0 +1,20 @@ +import { Readable, PassThrough } from 'node:stream' +import { askYesNo } from '../src/core/cli/confirm.js' + +const mk = (chunks) => { + const s = Readable.from(chunks) + Object.defineProperty(s, 'isTTY', { value: true }) + return s +} +const buf = () => { let v=''; return { write(c){ v+=String(c); return true }, text(){return v} } } + +for (const [name, chunks] of [ + ['typed n then y', ['n\ny\n']], + ['typed n then y, 2 chunks', ['n\n','y\n']], + ['typed y then n', ['y\nn\n']], + ['typed n, junk no newline', ['n\ny']], +]) { + const stderr = buf() + const res = await askYesNo({ stdin: mk(chunks), stderr }, 'Delete everything? [y/N] ') + console.log(`${name.padEnd(28)} -> askYesNo=${res}`) +} diff --git a/x/line_asker.fixed.js b/x/line_asker.fixed.js new file mode 100644 index 00000000..1627dafb --- /dev/null +++ b/x/line_asker.fixed.js @@ -0,0 +1,160 @@ +// @ts-check + +/** + * @import { Interface } from 'node:readline/promises' + */ + +/** + * Ask one question and settle even when the stream can no longer answer. + * + * The same EOF defect `queuedLineAsker` documents below, for the prompts + * that ask exactly once and are not built with `terminal: false`. There, + * writing the query directly to the output stream is not equivalent to + * asking: on a real terminal readline redraws the line it is editing from + * its own cursor bookkeeping, and a query it never saw is a query it + * cannot redraw. So this keeps `rl.question` as the thing that writes and + * reads, and only replaces the promise, which is the part that is broken. + * + * Two settlements are added to it: `close`, for a stream that ends while + * the question is on screen (a terminal that dropped, a ctrl+D), and the + * stream's own `readableEnded`, for a stream that was already spent when + * the interface was built. Readline registers its `end` listener at + * construction, so the second case never emits `close` at all and a + * `close`-only guard still hangs on it. Both resolve `null`, which callers + * read as the empty line - the answer a bare enter would have given. + * + * Neither settlement may outrun an answer that did arrive, and both can: + * `close` fires synchronously, while `rl.question` hands its answer back + * through a promise, a microtask later. A stream that delivers the line + * and EOF in one burst (`Readable.from(['y\n'])`, any readable that + * pushes data and `null` together) would have `close` win that race and + * discard a real `y`, so the EOF settlements are deferred a turn and the + * delivered line settles first. Readline also routes the last line of a + * stream that ends without a trailing newline to `line` rather than to + * the pending question, which is a second answer `question` alone never + * sees, so that one is taken too - but only at EOF, and only if the + * question never got a line of its own. Readline emits `line` while no + * question is pending, so the same event also carries anything typed or + * pasted PAST the answer ("n\ny\n" in one burst), and settling on that + * directly would answer with the last line of the burst rather than the + * first: a typed `n` at an irreversible confirm would read as `y`. + * + * `rl.question`'s own promise is left permanently unsettled at EOF rather + * than rejected, so nothing here can leak an unhandled rejection; the + * handler attached to it is only there for the abort/close rejections + * newer readline versions may raise. + * + * @ref LLP 0190#eof-everywhere [implements]: a spent stdin settles on the prompt's stated default instead of waiting on an answer that can never come + * + * @param {Interface} rl + * @param {NodeJS.ReadableStream} input + * @param {string} prompt + * @returns {Promise} the answer line, `null` once the stream is spent + */ +export function askLineOnce(rl, input, prompt) { + return new Promise((resolve) => { + let settled = false + /** @param {string | null} line */ + const done = (line) => { + if (settled) return + settled = true + resolve(line) + } + /** + * The last line readline emitted outside the question, held rather + * than settled on. Only read if the question itself never answered. + * @type {string | null} + */ + let unaskedLine = null + // A turn behind the answer, never ahead of it: an answer delivered in + // the same burst as the EOF has to settle first. + const endOfInput = () => setImmediate(() => done(unaskedLine)) + rl.once('close', endOfInput) + // Readline emits `line` only while no question is pending, so this is + // either the unterminated last line of the stream (an answer the + // question never sees) or a line that arrived past the answer. Holding + // it for EOF takes the first without letting the second overwrite an + // answer the question already has. + rl.on('line', (line) => { + unaskedLine = line + }) + // Writes the query synchronously, on a spent stream too. + rl.question(prompt).then(done, () => done(null)) + if (/** @type {{ readableEnded?: boolean }} */ (input).readableEnded === true) endOfInput() + }) +} + +/** + * Ceiling on the answer lines held for a still-unasked prompt. A prompt + * interface asks a small fixed number of questions and is closed straight + * after, so anything past a handful is unreadable backlog: a pipe that + * floods stdin (`yes |`) must not grow an array for as long as the prompt + * is on screen. + */ +const MAX_QUEUED_LINES = 4 + +/** + * Read answer lines off a readline interface without losing one and + * without ever waiting on a stream that can no longer answer. + * + * `rl.question()` cannot do either job here. It registers its `line` + * listener only when it is called, so a second answer line arriving in + * the same chunk as the first ("y\n3\n" from a pipe) is emitted and + * dropped before a re-ask can ask: readline emits both synchronously + * and the next `question()` is a microtask away. And at EOF its promise + * is left permanently unsettled - the interface closes, `question` + * neither resolves nor rejects - which is a hang, or a silent + * "unsettled top-level await" exit. Queueing every line from + * construction fixes the first; resolving the pending ask as `null` on + * `close` fixes the second. + * + * `close` alone is not enough for the second job. Readline registers its + * `end` listener when the interface is built, so an interface built over + * a stream that has ALREADY ended never sees an `end` and never emits + * `close` - the second prompt asked on a spent stdin would wait forever + * even though the first resolved. The stream's own `readableEnded` is + * the answer readline can no longer give, so it seeds `closed` here. + * + * This lives beside `stdio.js` and `flush-streams.js` rather than inside + * any one prompt: every readline prompt in the tree has the same EOF + * defect, and a shared asker is what keeps the answer to it in one place. + * + * @ref LLP 0190#eof-everywhere [implements]: a spent stdin settles on the prompt's stated default instead of waiting on an answer that can never come + * + * @param {Interface} rl + * @param {NodeJS.ReadableStream} input + * @param {NodeJS.WritableStream} output + * @returns {(prompt: string) => Promise} writes one prompt and takes the next line, `null` once the stream is spent + */ +export function queuedLineAsker(rl, input, output) { + /** @type {string[]} */ + const queued = [] + /** @type {((line: string | null) => void) | null} */ + let waiting = null + let closed = /** @type {{ readableEnded?: boolean }} */ (input).readableEnded === true + const take = () => { + const resolve = waiting + waiting = null + return resolve + } + rl.on('line', (line) => { + const resolve = take() + if (resolve) resolve(line) + else if (queued.length < MAX_QUEUED_LINES) queued.push(line) + }) + rl.on('close', () => { + closed = true + const resolve = take() + if (resolve) resolve(null) + }) + return function askLine(prompt) { + // Byte-identical to what `rl.question` writes: with `terminal: false` + // readline puts the query straight on the output stream. + output.write(prompt) + if (queued.length > 0) return Promise.resolve(/** @type {string} */ (queued.shift())) + if (closed) return Promise.resolve(null) + return new Promise((resolve) => { + waiting = resolve + }) + } +} diff --git a/x/line_asker.orig.js b/x/line_asker.orig.js new file mode 100644 index 00000000..04df8425 --- /dev/null +++ b/x/line_asker.orig.js @@ -0,0 +1,144 @@ +// @ts-check + +/** + * @import { Interface } from 'node:readline/promises' + */ + +/** + * Ask one question and settle even when the stream can no longer answer. + * + * The same EOF defect `queuedLineAsker` documents below, for the prompts + * that ask exactly once and are not built with `terminal: false`. There, + * writing the query directly to the output stream is not equivalent to + * asking: on a real terminal readline redraws the line it is editing from + * its own cursor bookkeeping, and a query it never saw is a query it + * cannot redraw. So this keeps `rl.question` as the thing that writes and + * reads, and only replaces the promise, which is the part that is broken. + * + * Two settlements are added to it: `close`, for a stream that ends while + * the question is on screen (a terminal that dropped, a ctrl+D), and the + * stream's own `readableEnded`, for a stream that was already spent when + * the interface was built. Readline registers its `end` listener at + * construction, so the second case never emits `close` at all and a + * `close`-only guard still hangs on it. Both resolve `null`, which callers + * read as the empty line - the answer a bare enter would have given. + * + * Neither settlement may outrun an answer that did arrive, and both can: + * `close` fires synchronously, while `rl.question` hands its answer back + * through a promise, a microtask later. A stream that delivers the line + * and EOF in one burst (`Readable.from(['y\n'])`, any readable that + * pushes data and `null` together) would have `close` win that race and + * discard a real `y`, so the EOF settlements are deferred a turn and the + * delivered line settles first. Readline also routes the last line of a + * stream that ends without a trailing newline to `line` rather than to + * the pending question, which is a second answer `question` alone never + * sees, so that one is taken too. + * + * `rl.question`'s own promise is left permanently unsettled at EOF rather + * than rejected, so nothing here can leak an unhandled rejection; the + * handler attached to it is only there for the abort/close rejections + * newer readline versions may raise. + * + * @ref LLP 0190#eof-everywhere [implements]: a spent stdin settles on the prompt's stated default instead of waiting on an answer that can never come + * + * @param {Interface} rl + * @param {NodeJS.ReadableStream} input + * @param {string} prompt + * @returns {Promise} the answer line, `null` once the stream is spent + */ +export function askLineOnce(rl, input, prompt) { + return new Promise((resolve) => { + let settled = false + /** @param {string | null} line */ + const done = (line) => { + if (settled) return + settled = true + resolve(line) + } + // A turn behind the answer, never ahead of it: an answer delivered in + // the same burst as the EOF has to settle first. + const endOfInput = () => setImmediate(() => done(null)) + rl.once('close', endOfInput) + // The last line of a stream that ends without a trailing newline goes + // here rather than to the pending question. + rl.on('line', done) + // Writes the query synchronously, on a spent stream too. + rl.question(prompt).then(done, () => done(null)) + if (/** @type {{ readableEnded?: boolean }} */ (input).readableEnded === true) endOfInput() + }) +} + +/** + * Ceiling on the answer lines held for a still-unasked prompt. A prompt + * interface asks a small fixed number of questions and is closed straight + * after, so anything past a handful is unreadable backlog: a pipe that + * floods stdin (`yes |`) must not grow an array for as long as the prompt + * is on screen. + */ +const MAX_QUEUED_LINES = 4 + +/** + * Read answer lines off a readline interface without losing one and + * without ever waiting on a stream that can no longer answer. + * + * `rl.question()` cannot do either job here. It registers its `line` + * listener only when it is called, so a second answer line arriving in + * the same chunk as the first ("y\n3\n" from a pipe) is emitted and + * dropped before a re-ask can ask: readline emits both synchronously + * and the next `question()` is a microtask away. And at EOF its promise + * is left permanently unsettled - the interface closes, `question` + * neither resolves nor rejects - which is a hang, or a silent + * "unsettled top-level await" exit. Queueing every line from + * construction fixes the first; resolving the pending ask as `null` on + * `close` fixes the second. + * + * `close` alone is not enough for the second job. Readline registers its + * `end` listener when the interface is built, so an interface built over + * a stream that has ALREADY ended never sees an `end` and never emits + * `close` - the second prompt asked on a spent stdin would wait forever + * even though the first resolved. The stream's own `readableEnded` is + * the answer readline can no longer give, so it seeds `closed` here. + * + * This lives beside `stdio.js` and `flush-streams.js` rather than inside + * any one prompt: every readline prompt in the tree has the same EOF + * defect, and a shared asker is what keeps the answer to it in one place. + * + * @ref LLP 0190#eof-everywhere [implements]: a spent stdin settles on the prompt's stated default instead of waiting on an answer that can never come + * + * @param {Interface} rl + * @param {NodeJS.ReadableStream} input + * @param {NodeJS.WritableStream} output + * @returns {(prompt: string) => Promise} writes one prompt and takes the next line, `null` once the stream is spent + */ +export function queuedLineAsker(rl, input, output) { + /** @type {string[]} */ + const queued = [] + /** @type {((line: string | null) => void) | null} */ + let waiting = null + let closed = /** @type {{ readableEnded?: boolean }} */ (input).readableEnded === true + const take = () => { + const resolve = waiting + waiting = null + return resolve + } + rl.on('line', (line) => { + const resolve = take() + if (resolve) resolve(line) + else if (queued.length < MAX_QUEUED_LINES) queued.push(line) + }) + rl.on('close', () => { + closed = true + const resolve = take() + if (resolve) resolve(null) + }) + return function askLine(prompt) { + // Byte-identical to what `rl.question` writes: with `terminal: false` + // readline puts the query straight on the output stream. + output.write(prompt) + if (queued.length > 0) return Promise.resolve(/** @type {string} */ (queued.shift())) + if (closed) return Promise.resolve(null) + return new Promise((resolve) => { + waiting = resolve + }) + } +} diff --git a/x/msg.txt b/x/msg.txt new file mode 100644 index 00000000..42092e7d --- /dev/null +++ b/x/msg.txt @@ -0,0 +1,27 @@ +askLineOnce must answer with the line that answered it (review 2 of #785) + +The `rl.on('line', done)` added in 2843b78 takes the LAST line of a +burst rather than the first. Readline emits `line` only while no +question is pending, so the handler fires for everything typed or +pasted past the answer, and it fires synchronously while the question +hands its own answer back a microtask later. A stdin delivering +"n\ny\n" in one chunk therefore answered `y`. + +That is the one direction an irreversible `[y/N]` must never drift in: +`hyp purge`, `hyp report delete` and `hyp attach`'s enable prompt read +a typed `n` as a confirmation. Reproduced on a real pty, where a paced +`n` is still correct but a pasted "n\ny\n" proceeded with the delete; +both `rl.question` and the pre-2843b78 asker returned "n" there, so it +was a regression introduced by the fix rather than a pre-existing gap. + +The line is now held rather than settled on, and read only at EOF and +only if the question never answered. That keeps 2843b78's two cases +(an answer delivered in the same burst as the EOF, and the +unterminated last line readline routes past the question) and drops +the overwrite. On every input where `rl.question` answers at all, +`askLineOnce` now returns exactly what it returns. + +Three regression tests, each verified to fail against 2843b78's body +and pass against this one. + +Co-Authored-By: Claude diff --git a/x/npm-test.log b/x/npm-test.log new file mode 100644 index 00000000..c84e550d --- /dev/null +++ b/x/npm-test.log @@ -0,0 +1,24648 @@ + +> hypaware@1.22.0 test +> node scripts/run-tests.js + +TAP version 13 +# Subtest: the default attachHandler is an attach-kind, reversible ActionHandler +ok 1 - the default attachHandler is an attach-kind, reversible ActionHandler + --- + duration_ms: 0.706344 + type: 'test' + ... +# Subtest: desired() emits one action per enabled client descriptor with a registered client +ok 2 - desired() emits one action per enabled client descriptor with a registered client + --- + duration_ms: 0.757632 + type: 'test' + ... +# Subtest: desired() emits an action per enabled descriptor across two client plugins +ok 3 - desired() emits an action per enabled descriptor across two client plugins + --- + duration_ms: 0.22394 + type: 'test' + ... +# Subtest: desired() excludes a descriptor whose owning plugin is disabled or absent +ok 4 - desired() excludes a descriptor whose owning plugin is disabled or absent + --- + duration_ms: 0.274437 + type: 'test' + ... +# Subtest: desired() honors an explicit attach.on_join:false opt-out (no action) +ok 5 - desired() honors an explicit attach.on_join:false opt-out (no action) + --- + duration_ms: 0.237191 + type: 'test' + ... +# Subtest: desired() does not fail open on a non-boolean on_join (treats it as opt-out) +ok 6 - desired() does not fail open on a non-boolean on_join (treats it as opt-out) + --- + duration_ms: 0.228517 + type: 'test' + ... +# Subtest: desired() excludes a probe-less descriptor - attach-eligibility requires reverse-capability (\#212) +ok 7 - desired() excludes a probe-less descriptor - attach-eligibility requires reverse-capability (\#212) + --- + duration_ms: 0.231262 + type: 'test' + ... +# Subtest: desired() guards on the runtime registry actually having the client +ok 8 - desired() guards on the runtime registry actually having the client + --- + duration_ms: 0.178061 + type: 'test' + ... +# Subtest: desired() is daemon-only: inert with no clientDescriptors and with no clients (a plain CLI boot) +ok 9 - desired() is daemon-only: inert with no clientDescriptors and with no clients (a plain CLI boot) + --- + duration_ms: 0.450044 + type: 'test' + ... +# Subtest: perform() attaches via the registry (endpoint + json mode) and records settings_path + prev_value +ok 10 - perform() attaches via the registry (endpoint + json mode) and records settings_path + prev_value + --- + duration_ms: 0.720715 + type: 'test' + ... +# Subtest: perform() records done with only settings_path when the attach had no prior value to back up +ok 11 - perform() records done with only settings_path when the attach had no prior value to back up + --- + duration_ms: 0.290642 + type: 'test' + ... +# Subtest: perform() records done (endpoint only) on an idempotent re-attach (changed:false) +ok 12 - perform() records done (endpoint only) on an idempotent re-attach (changed:false) + --- + duration_ms: 0.205262 + type: 'test' + ... +# Subtest: perform() records done (endpoint only) when the adapter emits an unparseable payload +ok 13 - perform() records done (endpoint only) when the adapter emits an unparseable payload + --- + duration_ms: 0.217711 + type: 'test' + ... +# Subtest: perform() parses the last non-empty line when prose precedes the JSON +ok 14 - perform() parses the last non-empty line when prose precedes the JSON + --- + duration_ms: 0.2307 + type: 'test' + ... +# Subtest: perform() returns failed when the adapter throws (file not writable) +ok 15 - perform() returns failed when the adapter throws (file not writable) + --- + duration_ms: 0.680734 + type: 'test' + ... +# Subtest: perform() returns refused when the adapter throws a marked refusal +ok 16 - perform() returns refused when the adapter throws a marked refusal + --- + duration_ms: 0.188567 + type: 'test' + ... +# Subtest: perform() returns failed when the registry has no such client +ok 17 - perform() returns failed when the registry has no such client + --- + duration_ms: 0.154755 + type: 'test' + ... +# Subtest: perform() returns failed when no gateway endpoint is set +ok 18 - perform() returns failed when no gateway endpoint is set + --- + duration_ms: 0.152412 + type: 'test' + ... +# Subtest: perform() guards against a missing client name +ok 19 - perform() guards against a missing client name + --- + duration_ms: 0.142026 + type: 'test' + ... +# Subtest: perform() materializes the client assets and records them as the undo record +ok 20 - perform() materializes the client assets and records them as the undo record + --- + duration_ms: 11.296129 + type: 'test' + ... +# Subtest: perform() stays done when an asset copy fails - the attach itself applied +ok 21 - perform() stays done when an asset copy fails - the attach itself applied + --- + duration_ms: 2.077297 + type: 'test' + ... +# Subtest: perform() installs no assets when the daemon threaded no registries +ok 22 - perform() installs no assets when the daemon threaded no registries + --- + duration_ms: 1.242819 + type: 'test' + ... +# Subtest: reverse() removes exactly the assets its own marker recorded +ok 23 - reverse() removes exactly the assets its own marker recorded + --- + duration_ms: 4.023305 + type: 'test' + ... +# Subtest: reverse() refuses to remove a marker path outside the client asset dirs +ok 24 - reverse() refuses to remove a marker path outside the client asset dirs + --- + duration_ms: 1.841188 + type: 'test' + ... +# Subtest: reverse() of a marker with no installed_assets touches no files +ok 25 - reverse() of a marker with no installed_assets touches no files + --- + duration_ms: 1.209208 + type: 'test' + ... +# Subtest: reverse() invokes the disk-driven undo once and never consults ctx.clients +ok 26 - reverse() invokes the disk-driven undo once and never consults ctx.clients + --- + duration_ms: 0.338324 + type: 'test' + ... +# Subtest: reverse() reports a replayed malformed-block backup by path, and never its contents +ok 27 - reverse() reports a replayed malformed-block backup by path, and never its contents + --- + duration_ms: 0.291664 + type: 'test' + ... +# Subtest: reverse() says nothing about a restore that did not happen +ok 28 - reverse() says nothing about a restore that did not happen + --- + duration_ms: 0.22997 + type: 'test' + ... +# Subtest: reverse() replays the real core undo from disk with no adapter loaded (fs round-trip) +ok 29 - reverse() replays the real core undo from disk with no adapter loaded (fs round-trip) + --- + duration_ms: 7.245873 + type: 'test' + ... +# Subtest: reverse() of a probe-less descriptor fails - never silently drops the marker, orphaning settings (\#212) +ok 30 - reverse() of a probe-less descriptor fails - never silently drops the marker, orphaning settings (\#212) + --- + duration_ms: 0.340878 + type: 'test' + ... +# Subtest: reverse() returns failed (retry next pass) when the descriptor is gone from the catalog +ok 31 - reverse() returns failed (retry next pass) when the descriptor is gone from the catalog + --- + duration_ms: 0.231011 + type: 'test' + ... +# Subtest: reverse() returns failed when the disk undo throws (concurrent edit) +ok 32 - reverse() returns failed when the disk undo throws (concurrent edit) + --- + duration_ms: 0.255799 + type: 'test' + ... +# Subtest: the reverse-gap contract: a dropped client falls out of desired() and reverse() then undoes it +ok 33 - the reverse-gap contract: a dropped client falls out of desired() and reverse() then undoes it + --- + duration_ms: 0.576717 + type: 'test' + ... +# Subtest: the default backfillHandler is a backfill-kind ActionHandler +ok 34 - the default backfillHandler is a backfill-kind ActionHandler + --- + duration_ms: 0.890914 + type: 'test' + ... +# Subtest: desired() emits one action per enabled provider (default on_join, plugin->provider mapping) +ok 35 - desired() emits one action per enabled provider (default on_join, plugin->provider mapping) + --- + duration_ms: 3.106291 + type: 'test' + ... +# Subtest: desired() honors an explicit on_join:false opt-out (no action) +ok 36 - desired() honors an explicit on_join:false opt-out (no action) + --- + duration_ms: 0.320017 + type: 'test' + ... +# Subtest: desired() does not fail open on a non-boolean on_join (treats it as opt-out) +ok 37 - desired() does not fail open on a non-boolean on_join (treats it as opt-out) + --- + duration_ms: 0.426699 + type: 'test' + ... +# Subtest: desired() carries window_days through to params when present +ok 38 - desired() carries window_days through to params when present + --- + duration_ms: 0.40754 + type: 'test' + ... +# Subtest: desired() excludes a provider whose owning plugin is not enabled +ok 39 - desired() excludes a provider whose owning plugin is not enabled + --- + duration_ms: 0.457666 + type: 'test' + ... +# Subtest: perform() resolves window_days to a --since flag (assert spawned argv) +ok 40 - perform() resolves window_days to a --since flag (assert spawned argv) + --- + duration_ms: 0.760937 + type: 'test' + ... +# Subtest: perform() spawns with the daemon-resolved env (HYP_HOME), not process.env +ok 41 - perform() spawns with the daemon-resolved env (HYP_HOME), not process.env + --- + duration_ms: 0.351575 + type: 'test' + ... +# Subtest: perform() omits --since when window_days is absent (retention fallback) +ok 42 - perform() omits --since when window_days is absent (retention fallback) + --- + duration_ms: 0.510246 + type: 'test' + ... +# Subtest: perform() sums rows_written across providers in the --json payload +ok 43 - perform() sums rows_written across providers in the --json payload + --- + duration_ms: 0.57912 + type: 'test' + ... +# Subtest: perform() records done (without rows) on exit 0 with an unparseable payload +ok 44 - perform() records done (without rows) on exit 0 with an unparseable payload + --- + duration_ms: 0.337874 + type: 'test' + ... +# Subtest: perform() returns failed on a non-zero exit +ok 45 - perform() returns failed on a non-zero exit + --- + duration_ms: 0.35558 + type: 'test' + ... +# Subtest: perform() returns failed on a spawn error +ok 46 - perform() returns failed on a spawn error + --- + duration_ms: 0.29578 + type: 'test' + ... +# Subtest: perform() guards against a missing provider name +ok 47 - perform() guards against a missing provider name + --- + duration_ms: 0.247406 + type: 'test' + ... +# Subtest: driven through the reconciler: a failed perform writes a failed marker, then a retry flips to done +ok 48 - driven through the reconciler: a failed perform writes a failed marker, then a retry flips to done + --- + duration_ms: 22.257589 + type: 'test' + ... +# Subtest: reconcile runs a desired action once and short-circuits on the done marker +ok 49 - reconcile runs a desired action once and short-circuits on the done marker + --- + duration_ms: 5.773525 + type: 'test' + ... +# Subtest: a missed pass (no marker yet) runs on the next reconcile call +ok 50 - a missed pass (no marker yet) runs on the next reconcile call + --- + duration_ms: 1.421812 + type: 'test' + ... +# Subtest: atomic marker read/write round-trips through readClientActionStatus and readStatus +ok 51 - atomic marker read/write round-trips through readClientActionStatus and readStatus + --- + duration_ms: 1.467049 + type: 'test' + ... +# Subtest: a failed perform writes a failed marker (not done) and retries with bumped attempts +ok 52 - a failed perform writes a failed marker (not done) and retries with bumped attempts + --- + duration_ms: 3.221456 + type: 'test' + ... +# Subtest: a thrown perform is normalized to a failed marker +ok 53 - a thrown perform is normalized to a failed marker + --- + duration_ms: 1.629777 + type: 'test' + ... +# Subtest: a corrupt marker file does not wedge reconcile (treated as empty, pass still runs) +ok 54 - a corrupt marker file does not wedge reconcile (treated as empty, pass still runs) + --- + duration_ms: 1.463794 + type: 'test' + ... +# Subtest: a handler whose desired() throws does not wedge other handlers +ok 55 - a handler whose desired() throws does not wedge other handlers + --- + duration_ms: 1.364413 + type: 'test' + ... +# Subtest: a reversible handler undoes a previously-applied key the config no longer names +ok 56 - a reversible handler undoes a previously-applied key the config no longer names + --- + duration_ms: 1.437605 + type: 'test' + ... +# Subtest: a failed reverse keeps the marker in the store (probe-less attach is never orphaned) (\#212) +ok 57 - a failed reverse keeps the marker in the store (probe-less attach is never orphaned) (\#212) + --- + duration_ms: 1.646182 + type: 'test' + ... +# Subtest: a failed marker that recorded an effect is reversed, not dropped (LLP 0138 marker-undo) +ok 58 - a failed marker that recorded an effect is reversed, not dropped (LLP 0138 marker-undo) + --- + duration_ms: 1.912637 + type: 'test' + ... +# Subtest: a run-once handler never reverses a no-longer-desired done marker +ok 59 - a run-once handler never reverses a no-longer-desired done marker + --- + duration_ms: 1.593642 + type: 'test' + ... +# Subtest: clearClientActionMarker is a no-op (returns false, writes nothing) when the file, bucket, or key is missing +ok 60 - clearClientActionMarker is a no-op (returns false, writes nothing) when the file, bucket, or key is missing + --- + duration_ms: 1.517347 + type: 'test' + ... +# Subtest: clearClientActionMarker drops an emptied bucket but preserves sibling buckets and keys +ok 61 - clearClientActionMarker drops an emptied bucket but preserves sibling buckets and keys + --- + duration_ms: 1.468863 + type: 'test' + ... +# Subtest: rearmRefusedActionMarker only touches a refused marker, and drops it only when it records no assets +ok 62 - rearmRefusedActionMarker only touches a refused marker, and drops it only when it records no assets + --- + duration_ms: 2.60526 + type: 'test' + ... +# Subtest: a refused marker short-circuits unconditionally, unlike a done marker the handler reports stale (LLP 0186) +ok 63 - a refused marker short-circuits unconditionally, unlike a done marker the handler reports stale (LLP 0186) + --- + duration_ms: 3.80299 + type: 'test' + ... +# Subtest: a refused outcome writes a terminal marker (reason + at, no attempts) that carries installed_assets forward (LLP 0186) +ok 64 - a refused outcome writes a terminal marker (reason + at, no attempts) that carries installed_assets forward (LLP 0186) + --- + duration_ms: 1.640924 + type: 'test' + ... +# Subtest: the reverse gap drops an assetless refused marker and reverses one that recorded an effect (LLP 0186) +ok 65 - the reverse gap drops an assetless refused marker and reverses one that recorded an effect (LLP 0186) + --- + duration_ms: 1.75002 + type: 'test' + ... +# Subtest: a thrown, marked Error round-trips through isActionRefused as true +ok 66 - a thrown, marked Error round-trips through isActionRefused as true + --- + duration_ms: 0.527401 + type: 'test' + ... +# Subtest: a plain Error reads as not refused +ok 67 - a plain Error reads as not refused + --- + duration_ms: 0.090307 + type: 'test' + ... +# Subtest: a non-Error throw reads as not refused +ok 68 - a non-Error throw reads as not refused + --- + duration_ms: 0.082515 + type: 'test' + ... +# Subtest: ai-gateway registers cache partitioning for source columns and iceberg fields +ok 69 - ai-gateway registers cache partitioning for source columns and iceberg fields + --- + duration_ms: 1.30236 + type: 'test' + ... +# Subtest: ai-gateway registers sourceSignal proxy so rows forward under a known ingest signal +ok 70 - ai-gateway registers sourceSignal proxy so rows forward under a known ingest signal + --- + duration_ms: 0.10569 + type: 'test' + ... +# Subtest: registry rejects cachePartitioning with source column absent from schema +ok 71 - registry rejects cachePartitioning with source column absent from schema + --- + duration_ms: 0.321869 + type: 'test' + ... +# Subtest: registry rejects cachePartitioning with required Iceberg field absent from schema +ok 72 - registry rejects cachePartitioning with required Iceberg field absent from schema + --- + duration_ms: 0.120172 + type: 'test' + ... +# Subtest: registry accepts cachePartitioning with optional Iceberg field absent from schema +ok 73 - registry accepts cachePartitioning with optional Iceberg field absent from schema + --- + duration_ms: 0.095224 + type: 'test' + ... +# Subtest: ai-gateway createDataSource honors scope when re-discovering fresh partitions +ok 74 - ai-gateway createDataSource honors scope when re-discovering fresh partitions + --- + duration_ms: 33.144436 + type: 'test' + ... +# Subtest: ai-gateway discoverParts unions legacy and source-table partitions without duplicates +ok 75 - ai-gateway discoverParts unions legacy and source-table partitions without duplicates + --- + duration_ms: 18.02771 + type: 'test' + ... +# Subtest: ai-gateway createDataSource unions legacy and source-table data +ok 76 - ai-gateway createDataSource unions legacy and source-table data + --- + duration_ms: 16.202336 + type: 'test' + ... +# Subtest: ai-gateway createDataSource pads declared schema columns absent from an old partition +ok 77 - ai-gateway createDataSource pads declared schema columns absent from an old partition + --- + duration_ms: 20.535032 + type: 'test' + ... +# Subtest: ai-gateway createDataSource streams scanColumn with nulls for a physically absent column +ok 78 - ai-gateway createDataSource streams scanColumn with nulls for a physically absent column + --- + duration_ms: 9.835749 + type: 'test' + ... +# Subtest: ai-gateway scanColumn pushes a convertible WHERE down to the parquet read and reports it applied +ok 79 - ai-gateway scanColumn pushes a convertible WHERE down to the parquet read and reports it applied + --- + duration_ms: 22.659661 + type: 'test' + ... +# Subtest: hyp attach all: a catalog-known client missing from the live registry gets a note, the live ones attach +ok 80 - hyp attach all: a catalog-known client missing from the live registry gets a note, the live ones attach + --- + duration_ms: 38.193761 + type: 'test' + ... +# Subtest: hyp attach all: the note does not change the exit code, only real attach failures among the live set do +ok 81 - hyp attach all: the note does not change the exit code, only real attach failures among the live set do + --- + duration_ms: 23.818962 + type: 'test' + ... +# Subtest: accept-then-yes: the provider runs with the expected shape and its result is reported +ok 82 - accept-then-yes: the provider runs with the expected shape and its result is reported + --- + duration_ms: 134.170586 + type: 'test' + ... +# Subtest: accept-then-no: declining the backfill question runs no import +ok 83 - accept-then-no: declining the backfill question runs no import + --- + duration_ms: 24.148142 + type: 'test' + ... +# Subtest: client with no registered backfill provider: the question is not asked at all +ok 84 - client with no registered backfill provider: the question is not asked at all + --- + duration_ms: 29.355687 + type: 'test' + ... +# Subtest: OpenClaw never reaches the backfill question, even with a registered provider +ok 85 - OpenClaw never reaches the backfill question, even with a registered provider + --- + duration_ms: 19.229156 + type: 'test' + ... +# Subtest: decline: exits 1 with zero side effects, no config or backup file written +ok 86 - decline: exits 1 with zero side effects, no config or backup file written + --- + duration_ms: 95.43364 + type: 'test' + ... +# Subtest: decline via a bare Enter (empty line) is treated as no +ok 87 - decline via a bare Enter (empty line) is treated as no + --- + duration_ms: 10.443593 + type: 'test' + ... +# Subtest: accept: enables the adapter and dispatches attach() in the same invocation +ok 88 - accept: enables the adapter and dispatches attach() in the same invocation + --- + duration_ms: 34.4827 + type: 'test' + ... +# Subtest: OpenClaw prompt names the periodic sweep import; Claude/Codex does not +ok 89 - OpenClaw prompt names the periodic sweep import; Claude/Codex does not + --- + duration_ms: 23.517985 + type: 'test' + ... +# Subtest: bootstrap floor: no local config file at all skips the prompt entirely +ok 90 - bootstrap floor: no local config file at all skips the prompt entirely + --- + duration_ms: 4.455232 + type: 'test' + ... +# Subtest: an entry already present but disabled skips the prompt: the additive write cannot enable it +ok 91 - an entry already present but disabled skips the prompt: the additive write cannot enable it + --- + duration_ms: 9.644369 + type: 'test' + ... +# Subtest: a disabled @hypaware/ai-gateway entry also skips the prompt, not just the adapter entry +ok 92 - a disabled @hypaware/ai-gateway entry also skips the prompt, not just the adapter entry + --- + duration_ms: 16.689927 + type: 'test' + ... +# Subtest: every requested plugin already present and enabled skips the prompt: the write would append nothing +ok 93 - every requested plugin already present and enabled skips the prompt: the write would append nothing + --- + duration_ms: 20.275507 + type: 'test' + ... +# Subtest: an adapter absent but its gateway present-and-enabled still prompts: the write has something to append +ok 94 - an adapter absent but its gateway present-and-enabled still prompts: the write has something to append + --- + duration_ms: 23.589674 + type: 'test' + ... +# Subtest: non-interactive (no stdin/TTY) keeps the unchanged not_enabled refusal +ok 95 - non-interactive (no stdin/TTY) keeps the unchanged not_enabled refusal + --- + duration_ms: 9.40858 + type: 'test' + ... +# Subtest: --dry-run never prompts: no config write, no backup, no attach +ok 96 - --dry-run never prompts: no config write, no backup, no attach + --- + duration_ms: 8.502062 + type: 'test' + ... +# Subtest: --json never prompts even with a TTY +ok 97 - --json never prompts even with a TTY + --- + duration_ms: 5.307697 + type: 'test' + ... +# Subtest: accept, write succeeds, restart fails: names the restart step, the backup path, and the resume instruction; does not retry the enable +ok 98 - accept, write succeeds, restart fails: names the restart step, the backup path, and the resume instruction; does not retry the enable + --- + duration_ms: 119.412696 + type: 'test' + ... +# Subtest: second invocation after a partial failure: config already carries the entry, so attach never re-prompts +ok 99 - second invocation after a partial failure: config already carries the entry, so attach never re-prompts + --- + duration_ms: 5.951456 + type: 'test' + ... +# Subtest: capability gate: a catalog-known client reports not_enabled, not cap_missing +ok 100 - capability gate: a catalog-known client reports not_enabled, not cap_missing + --- + duration_ms: 98.006711 + type: 'test' + ... +# Subtest: capability gate: bare `hyp attach` defaults to claude and takes the same path +ok 101 - capability gate: bare `hyp attach` defaults to claude and takes the same path + --- + duration_ms: 5.858174 + type: 'test' + ... +# Subtest: capability gate: --json carries error_kind adapter_not_enabled in the same payload shape +ok 102 - capability gate: --json carries error_kind adapter_not_enabled in the same payload shape + --- + duration_ms: 6.03343 + type: 'test' + ... +# Subtest: capability gate: a name no plugin contributes keeps the cap_missing wording +ok 103 - capability gate: a name no plugin contributes keeps the cap_missing wording + --- + duration_ms: 7.34293 + type: 'test' + ... +# Subtest: registry miss: another gateway plugin is live but the requested adapter is not enabled +ok 104 - registry miss: another gateway plugin is live but the requested adapter is not enabled + --- + duration_ms: 10.387118 + type: 'test' + ... +# Subtest: registry miss: --json reports adapter_not_enabled +ok 105 - registry miss: --json reports adapter_not_enabled + --- + duration_ms: 6.130428 + type: 'test' + ... +# Subtest: registry miss: a genuinely unrecognized name still gets the plain unknown client text +ok 106 - registry miss: a genuinely unrecognized name still gets the plain unknown client text + --- + duration_ms: 12.281066 + type: 'test' + ... +# Subtest: fleet-disabled adapter refuses with the central-managed explanation, not the local remedy +ok 107 - fleet-disabled adapter refuses with the central-managed explanation, not the local remedy + --- + duration_ms: 9.712221 + type: 'test' + ... +# Subtest: fleet-disabled adapter reports adapter_disabled_central through the capability gate too +ok 108 - fleet-disabled adapter reports adapter_disabled_central through the capability gate too + --- + duration_ms: 6.0064 + type: 'test' + ... +# Subtest: a locally disabled adapter is fixable, so it renders the not_enabled remedy +ok 109 - a locally disabled adapter is fixable, so it renders the not_enabled remedy + --- + duration_ms: 4.242208 + type: 'test' + ... +# Subtest: a registered client still attaches unchanged +ok 110 - a registered client still attaches unchanged + --- + duration_ms: 6.373166 + type: 'test' + ... +# Subtest: a rebind (new endpoint) re-attaches instead of short-circuiting on the done marker (\#277 Gap 2) +ok 111 - a rebind (new endpoint) re-attaches instead of short-circuiting on the done marker (\#277 Gap 2) + --- + duration_ms: 5.876181 + type: 'test' + ... +# Subtest: a legacy done attach marker with no recorded endpoint re-attaches once (backward compatible) (\#277 Gap 2) +ok 112 - a legacy done attach marker with no recorded endpoint re-attaches once (backward compatible) (\#277 Gap 2) + --- + duration_ms: 2.346967 + type: 'test' + ... +# Subtest: a changed asset set re-attaches at an unchanged endpoint (LLP 0107 currency) +ok 113 - a changed asset set re-attaches at an unchanged endpoint (LLP 0107 currency) + --- + duration_ms: 17.475721 + type: 'test' + ... +# Subtest: an unresolvable endpoint this pass leaves the existing done attach untouched (\#277 Gap 2) +ok 114 - an unresolvable endpoint this pass leaves the existing done attach untouched (\#277 Gap 2) + --- + duration_ms: 2.024397 + type: 'test' + ... +# Subtest: attach without endpoint: already-attached client is a no-op success +ok 115 - attach without endpoint: already-attached client is a no-op success + --- + duration_ms: 27.316288 + type: 'test' + ... +# Subtest: attach without endpoint: already-attached --json reports ok/unchanged +ok 116 - attach without endpoint: already-attached --json reports ok/unchanged + --- + duration_ms: 7.795719 + type: 'test' + ... +# Subtest: attach without endpoint: no daemon installed names hyp daemon install/start +ok 117 - attach without endpoint: no daemon installed names hyp daemon install/start + --- + duration_ms: 9.990875 + type: 'test' + ... +# Subtest: attach without endpoint: installed-but-unreachable daemon keeps the existing hyp start message +ok 118 - attach without endpoint: installed-but-unreachable daemon keeps the existing hyp start message + --- + duration_ms: 15.271151 + type: 'test' + ... +# Subtest: attach without endpoint: not-attached --json reports failed/no_endpoint +ok 119 - attach without endpoint: not-attached --json reports failed/no_endpoint + --- + duration_ms: 8.700414 + type: 'test' + ... +# Subtest: attach with configured listen still uses the config fallback +ok 120 - attach with configured listen still uses the config fallback + --- + duration_ms: 9.569785 + type: 'test' + ... +# Subtest: attach discovers the daemon live port from status.json and attaches there (\#277 Gap 1) +ok 121 - attach discovers the daemon live port from status.json and attaches there (\#277 Gap 1) + --- + duration_ms: 25.175435 + type: 'test' + ... +# Subtest: attach re-attaches when the recorded marker port is stale vs the live port (\#277 Gap 1/2) +ok 122 - attach re-attaches when the recorded marker port is stale vs the live port (\#277 Gap 1/2) + --- + duration_ms: 7.231601 + type: 'test' + ... +# Subtest: attach reports already-attached (no-op) when the recorded port matches the live port (\#277 Gap 2) +ok 123 - attach reports already-attached (no-op) when the recorded port matches the live port (\#277 Gap 2) + --- + duration_ms: 11.00049 + type: 'test' + ... +# Subtest: attach installs client assets even when the settings are already attached (LLP 0107 every-attach) +ok 124 - attach installs client assets even when the settings are already attached (LLP 0107 every-attach) + --- + duration_ms: 12.354677 + type: 'test' + ... +# Subtest: attach does NOT trust a dead daemon status.json (liveness gate) (\#277 Gap 1) +ok 125 - attach does NOT trust a dead daemon status.json (liveness gate) (\#277 Gap 1) + --- + duration_ms: 12.761566 + type: 'test' + ... +# Subtest: readAttachPolicy: no entry → default-on (onJoin undefined) +ok 126 - readAttachPolicy: no entry → default-on (onJoin undefined) + --- + duration_ms: 1.034022 + type: 'test' + ... +# Subtest: readAttachPolicy: entry with no config → default-on +ok 127 - readAttachPolicy: entry with no config → default-on + --- + duration_ms: 0.804183 + type: 'test' + ... +# Subtest: readAttachPolicy: absent attach block → default-on (onJoin undefined) +ok 128 - readAttachPolicy: absent attach block → default-on (onJoin undefined) + --- + duration_ms: 0.106322 + type: 'test' + ... +# Subtest: readAttachPolicy: present block with no on_join → default-on +ok 129 - readAttachPolicy: present block with no on_join → default-on + --- + duration_ms: 0.089717 + type: 'test' + ... +# Subtest: readAttachPolicy: on_join: true → opt-in +ok 130 - readAttachPolicy: on_join: true → opt-in + --- + duration_ms: 0.080533 + type: 'test' + ... +# Subtest: readAttachPolicy: on_join: false → opt-out +ok 131 - readAttachPolicy: on_join: false → opt-out + --- + duration_ms: 0.110828 + type: 'test' + ... +# Subtest: readAttachPolicy: present-but-malformed on_join → fail-safe opt-out +ok 132 - readAttachPolicy: present-but-malformed on_join → fail-safe opt-out + --- + duration_ms: 0.168246 + type: 'test' + ... +# Subtest: readAttachPolicy: attach block that is an array → default-on (ignored) +ok 133 - readAttachPolicy: attach block that is an array → default-on (ignored) + --- + duration_ms: 0.081765 + type: 'test' + ... +# Subtest: readAttachPolicy: attach block that is a scalar → default-on (ignored) +ok 134 - readAttachPolicy: attach block that is a scalar → default-on (ignored) + --- + duration_ms: 0.201356 + type: 'test' + ... +# Subtest: readAttachPolicy: off switch reads as onJoin !== false +ok 135 - readAttachPolicy: off switch reads as onJoin !== false + --- + duration_ms: 0.280556 + type: 'test' + ... +# Subtest: a successful manual hyp attach clears a refused marker, and the next reconcile re-performs (LLP 0186 re-arm) +ok 136 - a successful manual hyp attach clears a refused marker, and the next reconcile re-performs (LLP 0186 re-arm) + --- + duration_ms: 46.826852 + type: 'test' + ... +# Subtest: a failed manual hyp attach leaves the refused marker in place (never cleared on failure) +ok 137 - a failed manual hyp attach leaves the refused marker in place (never cleared on failure) + --- + duration_ms: 3.528723 + type: 'test' + ... +# Subtest: a successful manual hyp attach leaves a done marker (and its installed_assets) alone +ok 138 - a successful manual hyp attach leaves a done marker (and its installed_assets) alone + --- + duration_ms: 9.483765 + type: 'test' + ... +# Subtest: a successful manual hyp attach re-arms an asset-bearing refused marker without dropping its undo record +ok 139 - a successful manual hyp attach re-arms an asset-bearing refused marker without dropping its undo record + --- + duration_ms: 13.197318 + type: 'test' + ... +# Subtest: hyp attach --dry-run never clears a refused marker +ok 140 - hyp attach --dry-run never clears a refused marker + --- + duration_ms: 9.575124 + type: 'test' + ... +# Subtest: parseRunArgv collects providers and flags in space and = forms +ok 141 - parseRunArgv collects providers and flags in space and = forms + --- + duration_ms: 2.542184 + type: 'test' + ... +# Subtest: parseRunArgv defaults flags off and keeps date window unset +ok 142 - parseRunArgv defaults flags off and keeps date window unset + --- + duration_ms: 0.165872 + type: 'test' + ... +# Subtest: parseRunArgv reports usage errors for bad input +ok 143 - parseRunArgv reports usage errors for bad input + --- + duration_ms: 0.309841 + type: 'test' + ... +# Subtest: parseRunArgv accepts an equal since/until boundary +ok 144 - parseRunArgv accepts an equal since/until boundary + --- + duration_ms: 0.101184 + type: 'test' + ... +# Subtest: parsePlanArgv accepts retention-days but rejects since/until +ok 145 - parsePlanArgv accepts retention-days but rejects since/until + --- + duration_ms: 0.244201 + type: 'test' + ... +# Subtest: selectProviders intersects explicit names and reports unknowns +ok 146 - selectProviders intersects explicit names and reports unknowns + --- + duration_ms: 0.235217 + type: 'test' + ... +# Subtest: selectProviders defaults to providers whose plugin is enabled in config +ok 147 - selectProviders defaults to providers whose plugin is enabled in config + --- + duration_ms: 0.161956 + type: 'test' + ... +# Subtest: resolveRetentionDays prefers the flag, then config, then the default +ok 148 - resolveRetentionDays prefers the flag, then config, then the default + --- + duration_ms: 0.169067 + type: 'test' + ... +# Subtest: runBackfill materializes rows, appends to the dataset path, and flushes +ok 149 - runBackfill materializes rows, appends to the dataset path, and flushes + --- + duration_ms: 24.187422 + type: 'test' + ... +# Subtest: runBackfill --dry-run scans without appending or flushing any rows +ok 150 - runBackfill --dry-run scans without appending or flushing any rows + --- + duration_ms: 8.04632 + type: 'test' + ... +# Subtest: runBackfill --json reports per-provider counts +ok 151 - runBackfill --json reports per-provider counts + --- + duration_ms: 10.912025 + type: 'test' + ... +# Subtest: runBackfill --json marks provider failed for materializer_missing +ok 152 - runBackfill --json marks provider failed for materializer_missing + --- + duration_ms: 10.450093 + type: 'test' + ... +# Subtest: runBackfill --json marks provider failed for dataset_mismatch +ok 153 - runBackfill --json marks provider failed for dataset_mismatch + --- + duration_ms: 5.292485 + type: 'test' + ... +# Subtest: runBackfill --json marks provider failed for dataset_not_registered +ok 154 - runBackfill --json marks provider failed for dataset_not_registered + --- + duration_ms: 5.840137 + type: 'test' + ... +# Subtest: runBackfill fails with exit 1 for an unknown explicit provider +ok 155 - runBackfill fails with exit 1 for an unknown explicit provider + --- + duration_ms: 0.312375 + type: 'test' + ... +# Subtest: runBackfill fails with exit 2 for invalid --since +ok 156 - runBackfill fails with exit 2 for invalid --since + --- + duration_ms: 0.176929 + type: 'test' + ... +# Subtest: runBackfill fails with exit 2 for invalid --until +ok 157 - runBackfill fails with exit 2 for invalid --until + --- + duration_ms: 0.223349 + type: 'test' + ... +# Subtest: runBackfill fails with exit 2 for --since after --until +ok 158 - runBackfill fails with exit 2 for --since after --until + --- + duration_ms: 0.153934 + type: 'test' + ... +# Subtest: runBackfillProvider runs one provider and returns compact counts + appends +ok 159 - runBackfillProvider runs one provider and returns compact counts + appends + --- + duration_ms: 7.163308 + type: 'test' + ... +# Subtest: runBackfillProvider dry-run scans without appending or flushing +ok 160 - runBackfillProvider dry-run scans without appending or flushing + --- + duration_ms: 8.981481 + type: 'test' + ... +# Subtest: runBackfillProvider reports a failed result for an unknown provider +ok 161 - runBackfillProvider reports a failed result for an unknown provider + --- + duration_ms: 0.22976 + type: 'test' + ... +# Subtest: the entrypoint gate counts a config-listed plugin as configured even when this process never activated it +ok 162 - the entrypoint gate counts a config-listed plugin as configured even when this process never activated it + --- + duration_ms: 11.357232 + type: 'test' + ... +# Subtest: the entrypoint gate stays closed for a plugin in neither the config nor the activation set +ok 163 - the entrypoint gate stays closed for a plugin in neither the config nor the activation set + --- + duration_ms: 4.655986 + type: 'test' + ... +# Subtest: a plugin listed with enabled:false is not configured +ok 164 - a plugin listed with enabled:false is not configured + --- + duration_ms: 4.402311 + type: 'test' + ... +# Subtest: hyp backfill plan hands providers the same entrypoint owner map the run gets +ok 165 - hyp backfill plan hands providers the same entrypoint owner map the run gets + --- + duration_ms: 4.390553 + type: 'test' + ... +# Subtest: resolveEntrypointOwners maps each declared entrypoint to its owning client +ok 166 - resolveEntrypointOwners maps each declared entrypoint to its owning client + --- + duration_ms: 1.001653 + type: 'test' + ... +# Subtest: resolveEntrypointOwners records whether each owning plugin is configured +ok 167 - resolveEntrypointOwners records whether each owning plugin is configured + --- + duration_ms: 0.150158 + type: 'test' + ... +# Subtest: resolveEntrypointOwners ignores clients declaring no entrypoints +ok 168 - resolveEntrypointOwners ignores clients declaring no entrypoints + --- + duration_ms: 0.074052 + type: 'test' + ... +# Subtest: resolveEntrypointOwners is first-declaration-wins on a contested entrypoint +ok 169 - resolveEntrypointOwners is first-declaration-wins on a contested entrypoint + --- + duration_ms: 0.127905 + type: 'test' + ... +# Subtest: classifyTranscriptEntrypoint skips an entrypoint owned by an unconfigured client +ok 170 - classifyTranscriptEntrypoint skips an entrypoint owned by an unconfigured client + --- + duration_ms: 0.107133 + type: 'test' + ... +# Subtest: classifyTranscriptEntrypoint attributes an owned+configured entrypoint to its owner, not the scanner +ok 171 - classifyTranscriptEntrypoint attributes an owned+configured entrypoint to its owner, not the scanner + --- + duration_ms: 0.120133 + type: 'test' + ... +# Subtest: classifyTranscriptEntrypoint fails open on an unclaimed entrypoint +ok 172 - classifyTranscriptEntrypoint fails open on an unclaimed entrypoint + --- + duration_ms: 0.081955 + type: 'test' + ... +# Subtest: classifyTranscriptEntrypoint fails open on an absent entrypoint +ok 173 - classifyTranscriptEntrypoint fails open on an absent entrypoint + --- + duration_ms: 0.090307 + type: 'test' + ... +# Subtest: classifyTranscriptEntrypoint with an empty owner map imports everything (pre-gate behavior) +ok 174 - classifyTranscriptEntrypoint with an empty owner map imports everything (pre-gate behavior) + --- + duration_ms: 0.186453 + type: 'test' + ... +# Subtest: classifyContainerSession imports as the owner when its plugin is configured +ok 175 - classifyContainerSession imports as the owner when its plugin is configured + --- + duration_ms: 0.260295 + type: 'test' + ... +# Subtest: classifyContainerSession gates when the owning plugin is unconfigured +ok 176 - classifyContainerSession gates when the owning plugin is unconfigured + --- + duration_ms: 0.150799 + type: 'test' + ... +# Subtest: classifyContainerSession gates without a predicate, unlike the value classifier +ok 177 - classifyContainerSession gates without a predicate, unlike the value classifier + --- + duration_ms: 0.062134 + type: 'test' + ... +# Subtest: classifyContainerSession is independent of entrypoint value declarations +ok 178 - classifyContainerSession is independent of entrypoint value declarations + --- + duration_ms: 0.584819 + type: 'test' + ... +# Subtest: sessionEntrypoint takes the first non-empty value across a session +ok 179 - sessionEntrypoint takes the first non-empty value across a session + --- + duration_ms: 0.077197 + type: 'test' + ... +# Subtest: the real claude-desktop manifest claims every observed entrypoint value +ok 180 - the real claude-desktop manifest claims every observed entrypoint value + --- + duration_ms: 17.798192 + type: 'test' + ... +# Subtest: the real claude manifest claims cli and sdk-cli +ok 181 - the real claude manifest claims cli and sdk-cli + --- + duration_ms: 8.365615 + type: 'test' + ... +# Subtest: real manifests: Desktop entrypoints gate off when only @hypaware/claude is configured +ok 182 - real manifests: Desktop entrypoints gate off when only @hypaware/claude is configured + --- + duration_ms: 9.491747 + type: 'test' + ... +# Subtest: real manifests: Desktop entrypoints import as claude-desktop once it is configured +ok 183 - real manifests: Desktop entrypoints import as claude-desktop once it is configured + --- + duration_ms: 5.108264 + type: 'test' + ... +# Subtest: the 3p container gates off until claude-desktop is configured, whatever the tag +ok 184 - the 3p container gates off until claude-desktop is configured, whatever the tag + --- + duration_ms: 0.176889 + type: 'test' + ... +# Subtest: every known real-world shared-tree entrypoint value is claimed by a bundled client +ok 185 - every known real-world shared-tree entrypoint value is claimed by a bundled client + --- + duration_ms: 5.840097 + type: 'test' + ... +# Subtest: BackfillRegistry registers, gets, and lists providers sorted by name +ok 186 - BackfillRegistry registers, gets, and lists providers sorted by name + --- + duration_ms: 5.94084 + type: 'test' + ... +# Subtest: BackfillRegistry rejects a duplicate provider name +ok 187 - BackfillRegistry rejects a duplicate provider name + --- + duration_ms: 0.327287 + type: 'test' + ... +# Subtest: BackfillRegistry validates the contribution shape +ok 188 - BackfillRegistry validates the contribution shape + --- + duration_ms: 0.163509 + type: 'test' + ... +# Subtest: BackfillRegistry accepts a provider with an optional plan() hook +ok 189 - BackfillRegistry accepts a provider with an optional plan() hook + --- + duration_ms: 0.192883 + type: 'test' + ... +# Subtest: BackfillMaterializerRegistry registers, gets, lists, and rejects duplicate kinds +ok 190 - BackfillMaterializerRegistry registers, gets, lists, and rejects duplicate kinds + --- + duration_ms: 0.253285 + type: 'test' + ... +# Subtest: BackfillMaterializerRegistry validates the contribution shape +ok 191 - BackfillMaterializerRegistry validates the contribution shape + --- + duration_ms: 0.198442 + type: 'test' + ... +# Subtest: resolveExportsBaseDir prefers explicit pluginConfig.exports_dir +ok 192 - resolveExportsBaseDir prefers explicit pluginConfig.exports_dir + --- + duration_ms: 0.84211 + type: 'test' + ... +# Subtest: resolveExportsBaseDir falls back to HYP_HOME +ok 193 - resolveExportsBaseDir falls back to HYP_HOME + --- + duration_ms: 0.187364 + type: 'test' + ... +# Subtest: resolveExportsBaseDir falls back to homedir/.hyp when HYP_HOME unset +ok 194 - resolveExportsBaseDir falls back to homedir/.hyp when HYP_HOME unset + --- + duration_ms: 0.153944 + type: 'test' + ... +# Subtest: local-fs BlobStore puts and gets bytes by key +ok 195 - local-fs BlobStore puts and gets bytes by key + --- + duration_ms: 9.468441 + type: 'test' + ... +# Subtest: local-fs BlobStore getObject returns null for missing keys +ok 196 - local-fs BlobStore getObject returns null for missing keys + --- + duration_ms: 0.823551 + type: 'test' + ... +# Subtest: local-fs BlobStore lists objects under a prefix in deterministic order +ok 197 - local-fs BlobStore lists objects under a prefix in deterministic order + --- + duration_ms: 12.334157 + type: 'test' + ... +# Subtest: local-fs BlobStore listObjects with empty prefix returns all entries +ok 198 - local-fs BlobStore listObjects with empty prefix returns all entries + --- + duration_ms: 6.041262 + type: 'test' + ... +# Subtest: local-fs BlobStore deleteObject removes the key and is idempotent +ok 199 - local-fs BlobStore deleteObject removes the key and is idempotent + --- + duration_ms: 6.194876 + type: 'test' + ... +# Subtest: local-fs BlobStore getObject body survives a concurrent unlink +ok 200 - local-fs BlobStore getObject body survives a concurrent unlink + --- + duration_ms: 5.369151 + type: 'test' + ... +# Subtest: local-fs BlobStore putObject honours ifNoneMatch="*" by failing on existing keys +ok 201 - local-fs BlobStore putObject honours ifNoneMatch="*" by failing on existing keys + --- + duration_ms: 4.682738 + type: 'test' + ... +# Subtest: local-fs BlobStore ifNoneMatch="*" succeeds when the key is new +ok 202 - local-fs BlobStore ifNoneMatch="*" succeeds when the key is new + --- + duration_ms: 3.119801 + type: 'test' + ... +# Subtest: local-fs BlobStore rejects keys that escape the configured root +ok 203 - local-fs BlobStore rejects keys that escape the configured root + --- + duration_ms: 2.742809 + type: 'test' + ... +# Subtest: local-fs BlobStore accepts a Readable stream as body +ok 204 - local-fs BlobStore accepts a Readable stream as body + --- + duration_ms: 8.208866 + type: 'test' + ... +# Subtest: in-memory BlobStore satisfies the BlobStore contract end-to-end +ok 205 - in-memory BlobStore satisfies the BlobStore contract end-to-end + --- + duration_ms: 1.215197 + type: 'test' + ... +# Subtest: discoverInstalledPlugins returns loaded manifests from the lock +ok 206 - discoverInstalledPlugins returns loaded manifests from the lock + --- + duration_ms: 15.791862 + type: 'test' + ... +# Subtest: discoverInstalledPlugins flags manifest/lock name mismatch as failed +ok 207 - discoverInstalledPlugins flags manifest/lock name mismatch as failed + --- + duration_ms: 3.958357 + type: 'test' + ... +# Subtest: bootKernel merges bundled and installed manifest pools +ok 208 - bootKernel merges bundled and installed manifest pools + --- + duration_ms: 12.662356 + type: 'test' + ... +# Subtest: bootKernel does not activate installed plugins under all-bundled +ok 209 - bootKernel does not activate installed plugins under all-bundled + --- + duration_ms: 8.161985 + type: 'test' + ... +# Subtest: bootKernel rejects installed plugins that shadow bundled first-party names +ok 210 - bootKernel rejects installed plugins that shadow bundled first-party names + --- + duration_ms: 6.821058 + type: 'test' + ... +# Subtest: bootKernel lets installed plugins replace excluded bundled skeletons and exposes their init presets +ok 211 - bootKernel lets installed plugins replace excluded bundled skeletons and exposes their init presets + --- + duration_ms: 8.2769 + type: 'test' + ... +# Subtest: dispatch routes hyp init even when preset args include --yes +ok 212 - dispatch routes hyp init even when preset args include --yes + --- + duration_ms: 6.725572 + type: 'test' + ... +# Subtest: resolveDependencies works over a merged bundled+installed manifest pool +ok 213 - resolveDependencies works over a merged bundled+installed manifest pool + --- + duration_ms: 1.092531 + type: 'test' + ... +# Subtest: mergeInstalledManifestsIntoKnown propagates capability provides/requires +ok 214 - mergeInstalledManifestsIntoKnown propagates capability provides/requires + --- + duration_ms: 0.425296 + type: 'test' + ... +# Subtest: mergeInstalledManifestsIntoKnown does not override first-party metadata +ok 215 - mergeInstalledManifestsIntoKnown does not override first-party metadata + --- + duration_ms: 0.394409 + type: 'test' + ... +# Subtest: the all-available boot (hyp init profile) registers bundled backfill providers including codex +ok 216 - the all-available boot (hyp init profile) registers bundled backfill providers including codex + --- + duration_ms: 130.213582 + type: 'test' + ... +# Subtest: validateConfig does not flag installed plugin names as plugin_unknown +ok 217 - validateConfig does not flag installed plugin names as plugin_unknown + --- + duration_ms: 0.731582 + type: 'test' + ... +# Subtest: a host with only a local layer boots it verbatim (effective = local) +ok 218 - a host with only a local layer boots it verbatim (effective = local) + --- + duration_ms: 21.342919 + type: 'test' + ... +# Subtest: central seed + local layer: effective is the merge, collisions drop, central query ignored +ok 219 - central seed + local layer: effective is the merge, collisions drop, central query ignored + --- + duration_ms: 25.081862 + type: 'test' + ... +# Subtest: a local plugin that invalidates the merge (capability tie) is dropped; central boots +ok 220 - a local plugin that invalidates the merge (capability tie) is dropped; central boots + --- + duration_ms: 26.812192 + type: 'test' + ... +# Subtest: a config-enabled plugin the boot profile drops is reported unavailable +ok 221 - a config-enabled plugin the boot profile drops is reported unavailable + --- + duration_ms: 21.221494 + type: 'test' + ... +# Subtest: an ordinary config-profile boot reports nothing unavailable +ok 222 - an ordinary config-profile boot reports nothing unavailable + --- + duration_ms: 23.385154 + type: 'test' + ... +# Subtest: a plugin the dep graph eliminated is reported unavailable +ok 223 - a plugin the dep graph eliminated is reported unavailable + --- + duration_ms: 5.421951 + type: 'test' + ... +# Subtest: a manifest that would not load is reported unavailable +ok 224 - a manifest that would not load is reported unavailable + --- + duration_ms: 15.890912 + type: 'test' + ... +# Subtest: a boot that selected nothing still reports what it could not load +ok 225 - a boot that selected nothing still reports what it could not load + --- + duration_ms: 4.406627 + type: 'test' + ... +# Subtest: the default-activation allowlist and the excluded set are disjoint +ok 226 - the default-activation allowlist and the excluded set are disjoint + --- + duration_ms: 1.042254 + type: 'test' + ... +# Subtest: a compaction that cannot reduce the file count records that it did not +ok 227 - a compaction that cannot reduce the file count records that it did not + --- + duration_ms: 74.512071 + type: 'test' + ... +# Subtest: a partition frozen by an ineffective compaction is retried once under a new compaction writer +ok 228 - a partition frozen by an ineffective compaction is retried once under a new compaction writer + --- + duration_ms: 78.434833 + type: 'test' + ... +# Subtest: a compaction that did reduce the file count is never retried +ok 229 - a compaction that did reduce the file count is never retried + --- + duration_ms: 68.323796 + type: 'test' + ... +# Subtest: a retry whose rewrite throws still spends its writer generation +ok 230 - a retry whose rewrite throws still spends its writer generation + --- + duration_ms: 23.208575 + type: 'test' + ... +# Subtest: a partition frozen by a failed retry is reported as skipped on every later tick +ok 231 - a partition frozen by a failed retry is reported as skipped on every later tick + --- + duration_ms: 39.649584 + type: 'test' + ... +# Subtest: a committed ineffective verdict outranks the failed attempt that followed it +ok 232 - a committed ineffective verdict outranks the failed attempt that followed it + --- + duration_ms: 41.316447 + type: 'test' + ... +# Subtest: a rewrite that throws after committing its cursor keeps the generation it committed +ok 233 - a rewrite that throws after committing its cursor keeps the generation it committed + --- + duration_ms: 34.616394 + type: 'test' + ... +# Subtest: a partition already at one data file is not reported as an ineffective compaction +ok 234 - a partition already at one data file is not reported as an ineffective compaction + --- + duration_ms: 14.989192 + type: 'test' + ... +# Subtest: a spent-attempt stamp with no usable timestamp is not reported +ok 235 - a spent-attempt stamp with no usable timestamp is not reported + --- + duration_ms: 32.807133 + type: 'test' + ... +# Subtest: compaction sizes output files by bytes written, not the in-memory batch estimate +ok 236 - compaction sizes output files by bytes written, not the in-memory batch estimate + --- + duration_ms: 308.810241 + type: 'test' + ... +# Subtest: compaction rolls to a new data file once target_file_bytes is written +ok 237 - compaction rolls to a new data file once target_file_bytes is written + --- + duration_ms: 243.45645 + type: 'test' + ... +# Subtest: compaction converges past the descriptor cap when a partition holds more tuples than it +ok 238 - compaction converges past the descriptor cap when a partition holds more tuples than it + --- + duration_ms: 1263.803397 + type: 'test' + ... +# Subtest: a streaming append holds one file per tuple open while capping descriptors +ok 239 - a streaming append holds one file per tuple open while capping descriptors + --- + duration_ms: 290.844064 + type: 'test' + ... +# Subtest: a streaming append rolls on retained row-group metadata, not only on target_file_bytes +ok 240 - a streaming append rolls on retained row-group metadata, not only on target_file_bytes + --- + duration_ms: 296.925698 + type: 'test' + ... +# Subtest: aborting a streaming append releases every open descriptor and temp file +ok 241 - aborting a streaming append releases every open descriptor and temp file + --- + duration_ms: 2.266605 + type: 'test' + ... +# Subtest: additive nullable column evolves the cache schema in place - new column queryable, no recreate +ok 242 - additive nullable column evolves the cache schema in place - new column queryable, no recreate + --- + duration_ms: 36.582351 + type: 'test' + ... +# Subtest: backfill works against the evolved schema - repeated V2 appends keep the new column populated +ok 243 - backfill works against the evolved schema - repeated V2 appends keep the new column populated + --- + duration_ms: 16.233704 + type: 'test' + ... +# Subtest: schema evolution is a no-op when columns are unchanged - only one schema persists +ok 244 - schema evolution is a no-op when columns are unchanged - only one schema persists + --- + duration_ms: 8.120913 + type: 'test' + ... +# Subtest: breaking changes still reject after the table exists (no in-place evolution for them) +ok 245 - breaking changes still reject after the table exists (no in-place evolution for them) + --- + duration_ms: 8.425756 + type: 'test' + ... +# Subtest: required→nullable widening evolves the table in place - a later null append lands +ok 246 - required→nullable widening evolves the table in place - a later null append lands + --- + duration_ms: 12.890773 + type: 'test' + ... +# Subtest: a rejected append does not advance the table schema (coerce before any commit) +ok 247 - a rejected append does not advance the table schema (coerce before any commit) + --- + duration_ms: 10.828288 + type: 'test' + ... +# Subtest: partitionSpecForDeclaration builds spec for ai_gateway_messages +ok 248 - partitionSpecForDeclaration builds spec for ai_gateway_messages + --- + duration_ms: 0.785794 + type: 'test' + ... +# Subtest: partitionSpecForDeclaration skips optional column not in schema +ok 249 - partitionSpecForDeclaration skips optional column not in schema + --- + duration_ms: 0.093552 + type: 'test' + ... +# Subtest: partitionSpecForDeclaration throws when required column not in schema +ok 250 - partitionSpecForDeclaration throws when required column not in schema + --- + duration_ms: 0.308339 + type: 'test' + ... +# Subtest: partitionSpecForDeclaration supports non-identity transforms +ok 251 - partitionSpecForDeclaration supports non-identity transforms + --- + duration_ms: 0.093673 + type: 'test' + ... +# Subtest: mergeFieldIdsFromTable preserves existing field IDs +ok 252 - mergeFieldIdsFromTable preserves existing field IDs + --- + duration_ms: 0.546841 + type: 'test' + ... +# Subtest: mergeFieldIdsFromTable appends nullable additions with fresh IDs +ok 253 - mergeFieldIdsFromTable appends nullable additions with fresh IDs + --- + duration_ms: 0.209779 + type: 'test' + ... +# Subtest: mergeFieldIdsFromTable rejects type changes +ok 254 - mergeFieldIdsFromTable rejects type changes + --- + duration_ms: 0.160584 + type: 'test' + ... +# Subtest: mergeFieldIdsFromTable rejects new required columns +ok 255 - mergeFieldIdsFromTable rejects new required columns + --- + duration_ms: 0.162016 + type: 'test' + ... +# Subtest: mergeFieldIdsFromTable rejects column removal +ok 256 - mergeFieldIdsFromTable rejects column removal + --- + duration_ms: 0.352917 + type: 'test' + ... +# Subtest: mergeFieldIdsFromTable rejects nullable → required tightening +ok 257 - mergeFieldIdsFromTable rejects nullable → required tightening + --- + duration_ms: 0.389412 + type: 'test' + ... +# Subtest: mergeFieldIdsFromTable uses specific error for partition column type change +ok 258 - mergeFieldIdsFromTable uses specific error for partition column type change + --- + duration_ms: 0.215919 + type: 'test' + ... +# Subtest: mergeFieldIdsFromTable uses specific error for partition column removal +ok 259 - mergeFieldIdsFromTable uses specific error for partition column removal + --- + duration_ms: 0.15123 + type: 'test' + ... +# Subtest: validatePartitionSpecStability passes when spec matches declaration +ok 260 - validatePartitionSpecStability passes when spec matches declaration + --- + duration_ms: 0.277833 + type: 'test' + ... +# Subtest: validatePartitionSpecStability rejects new partition field +ok 261 - validatePartitionSpecStability rejects new partition field + --- + duration_ms: 0.180554 + type: 'test' + ... +# Subtest: validatePartitionSpecStability rejects removed partition field +ok 262 - validatePartitionSpecStability rejects removed partition field + --- + duration_ms: 0.179022 + type: 'test' + ... +# Subtest: validatePartitionSpecStability compares effective optional fields against schema +ok 263 - validatePartitionSpecStability compares effective optional fields against schema + --- + duration_ms: 0.129196 + type: 'test' + ... +# Subtest: validatePartitionSpecStability rejects transform changes +ok 264 - validatePartitionSpecStability rejects transform changes + --- + duration_ms: 0.130989 + type: 'test' + ... +# Subtest: appendRowsToTable creates table with partition spec from declaration +ok 265 - appendRowsToTable creates table with partition spec from declaration + --- + duration_ms: 28.217728 + type: 'test' + ... +# Subtest: appendRowsToTable without declaration creates unpartitioned table +ok 266 - appendRowsToTable without declaration creates unpartitioned table + --- + duration_ms: 4.615505 + type: 'test' + ... +# Subtest: appendRowsToTable validates schema on existing table with declaration +ok 267 - appendRowsToTable validates schema on existing table with declaration + --- + duration_ms: 29.351161 + type: 'test' + ... +# Subtest: basicTypeForIcebergType round-trips known types +ok 268 - basicTypeForIcebergType round-trips known types + --- + duration_ms: 0.199122 + type: 'test' + ... +# Subtest: basicTypeForIcebergType defaults unknown types to STRING +ok 269 - basicTypeForIcebergType defaults unknown types to STRING + --- + duration_ms: 0.11226 + type: 'test' + ... +# Subtest: columnsFromIcebergSchema preserves types and nullability +ok 270 - columnsFromIcebergSchema preserves types and nullability + --- + duration_ms: 0.137128 + type: 'test' + ... +# Subtest: a partition whose compaction throws does not abort the rest of the walk +ok 271 - a partition whose compaction throws does not abort the rest of the walk + --- + duration_ms: 118.096675 + type: 'test' + ... +# Subtest: the partition that threw still spends its writer generation +ok 272 - the partition that threw still spends its writer generation + --- + duration_ms: 55.246529 + type: 'test' + ... +# Subtest: snapshot expiry still runs for the partitions behind the one that threw +ok 273 - snapshot expiry still runs for the partitions behind the one that threw + --- + duration_ms: 34.707853 + type: 'test' + ... +# Subtest: a tick that lost a partition reports the loss rather than swallowing it +ok 274 - a tick that lost a partition reports the loss rather than swallowing it + --- + duration_ms: 39.770257 + type: 'test' + ... +# Subtest: a zero budget does not let a failed first partition starve the healthy partition behind it +ok 275 - a zero budget does not let a failed first partition starve the healthy partition behind it + --- + duration_ms: 38.721854 + type: 'test' + ... +# Subtest: an all-failing cache does not walk past its budget unbounded: it stops at the failure cap +ok 276 - an all-failing cache does not walk past its budget unbounded: it stops at the failure cap + --- + duration_ms: 52.131274 + type: 'test' + ... +# Subtest: migrateLegacyPartitions dry-run reports legacy partitions without modifying +ok 277 - migrateLegacyPartitions dry-run reports legacy partitions without modifying + --- + duration_ms: 28.766112 + type: 'test' + ... +# Subtest: migrateLegacyPartitions --force moves rows to source-table layout and retires legacy dir +ok 278 - migrateLegacyPartitions --force moves rows to source-table layout and retires legacy dir + --- + duration_ms: 23.96834 + type: 'test' + ... +# Subtest: migrateLegacyPartitions is idempotent +ok 279 - migrateLegacyPartitions is idempotent + --- + duration_ms: 25.974289 + type: 'test' + ... +# Subtest: migrateLegacyPartitions skips source-table partitions +ok 280 - migrateLegacyPartitions skips source-table partitions + --- + duration_ms: 6.667954 + type: 'test' + ... +# Subtest: migrateLegacyPartitions migrates client=/date= epoch partitions +ok 281 - migrateLegacyPartitions migrates client=/date= epoch partitions + --- + duration_ms: 39.347895 + type: 'test' + ... +# Subtest: migrateLegacyPartitions dry-run on epoch partitions reports without modifying +ok 282 - migrateLegacyPartitions dry-run on epoch partitions reports without modifying + --- + duration_ms: 8.352054 + type: 'test' + ... +# Subtest: migrateLegacyPartitions retires only after successful append +ok 283 - migrateLegacyPartitions retires only after successful append + --- + duration_ms: 25.870671 + type: 'test' + ... +# Subtest: appendRowsToPartition creates epoch directory and cursor on first write +ok 284 - appendRowsToPartition creates epoch directory and cursor on first write + --- + duration_ms: 32.910801 + type: 'test' + ... +# Subtest: appendRowsToPartition with single partition segment +ok 285 - appendRowsToPartition with single partition segment + --- + duration_ms: 9.117698 + type: 'test' + ... +# Subtest: appendRowsToPartition with multi-segment path +ok 286 - appendRowsToPartition with multi-segment path + --- + duration_ms: 15.178881 + type: 'test' + ... +# Subtest: appendRowsToPartition updates cursor rowCount on subsequent writes +ok 287 - appendRowsToPartition updates cursor rowCount on subsequent writes + --- + duration_ms: 8.728957 + type: 'test' + ... +# Subtest: appendRowsToPartition returns early for empty rows +ok 288 - appendRowsToPartition returns early for empty rows + --- + duration_ms: 0.797712 + type: 'test' + ... +# Subtest: readCursorSync returns default cursor for missing file +ok 289 - readCursorSync returns default cursor for missing file + --- + duration_ms: 0.602746 + type: 'test' + ... +# Subtest: writeCursor creates the file and readCursorSync reads it back +ok 290 - writeCursor creates the file and readCursorSync reads it back + --- + duration_ms: 1.620413 + type: 'test' + ... +# Subtest: discoverCachePartitions returns empty for empty cache +ok 291 - discoverCachePartitions returns empty for empty cache + --- + duration_ms: 0.807898 + type: 'test' + ... +# Subtest: discoverCachePartitions finds a single dataset partition +ok 292 - discoverCachePartitions finds a single dataset partition + --- + duration_ms: 18.471705 + type: 'test' + ... +# Subtest: discoverCachePartitions discovers multiple dates +ok 293 - discoverCachePartitions discovers multiple dates + --- + duration_ms: 23.280895 + type: 'test' + ... +# Subtest: discoverCachePartitions filters by scope.datasets +ok 294 - discoverCachePartitions filters by scope.datasets + --- + duration_ms: 18.642134 + type: 'test' + ... +# Subtest: discoverCachePartitions filters by scope.date +ok 295 - discoverCachePartitions filters by scope.date + --- + duration_ms: 14.52686 + type: 'test' + ... +# Subtest: discoverCachePartitions filters by scope.from and scope.to +ok 296 - discoverCachePartitions filters by scope.from and scope.to + --- + duration_ms: 23.059618 + type: 'test' + ... +# Subtest: resolveClientName returns client_name when present +ok 297 - resolveClientName returns client_name when present + --- + duration_ms: 0.17739 + type: 'test' + ... +# Subtest: resolveClientName falls back to conversation_source +ok 298 - resolveClientName falls back to conversation_source + --- + duration_ms: 0.065379 + type: 'test' + ... +# Subtest: resolveClientName falls back to provider +ok 299 - resolveClientName falls back to provider + --- + duration_ms: 0.065459 + type: 'test' + ... +# Subtest: resolveClientName falls back to "unknown" +ok 300 - resolveClientName falls back to "unknown" + --- + duration_ms: 0.04607 + type: 'test' + ... +# Subtest: resolveClientName skips empty strings in the fallback chain +ok 301 - resolveClientName skips empty strings in the fallback chain + --- + duration_ms: 0.043887 + type: 'test' + ... +# Subtest: resolvePartitionDate extracts date from ISO timestamp string +ok 302 - resolvePartitionDate extracts date from ISO timestamp string + --- + duration_ms: 0.104829 + type: 'test' + ... +# Subtest: resolvePartitionDate extracts date from Date object +ok 303 - resolvePartitionDate extracts date from Date object + --- + duration_ms: 0.247075 + type: 'test' + ... +# Subtest: resolvePartitionDate extracts date from epoch ms number +ok 304 - resolvePartitionDate extracts date from epoch ms number + --- + duration_ms: 0.082084 + type: 'test' + ... +# Subtest: resolvePartitionDate extracts date from created_at field +ok 305 - resolvePartitionDate extracts date from created_at field + --- + duration_ms: 0.089667 + type: 'test' + ... +# Subtest: resolvePartitionDate extracts date from date field +ok 306 - resolvePartitionDate extracts date from date field + --- + duration_ms: 0.061042 + type: 'test' + ... +# Subtest: resolvePartitionDate returns undefined when no timestamp field present +ok 307 - resolvePartitionDate returns undefined when no timestamp field present + --- + duration_ms: 0.05248 + type: 'test' + ... +# Subtest: resolvePartitionSegments returns client+date for rows with both +ok 308 - resolvePartitionSegments returns client+date for rows with both + --- + duration_ms: 0.108996 + type: 'test' + ... +# Subtest: resolvePartitionSegments returns client+date using fallback chain +ok 309 - resolvePartitionSegments returns client+date using fallback chain + --- + duration_ms: 0.085019 + type: 'test' + ... +# Subtest: resolvePartitionSegments falls back to ["all"] when no partition keys +ok 310 - resolvePartitionSegments falls back to ["all"] when no partition keys + --- + duration_ms: 0.104518 + type: 'test' + ... +# Subtest: resolvePartitionSegments returns client=unknown+date when only date present +ok 311 - resolvePartitionSegments returns client=unknown+date when only date present + --- + duration_ms: 0.07851 + type: 'test' + ... +# Subtest: discoverCachePartitions detects a legacy Iceberg table without cursor.json +ok 312 - discoverCachePartitions detects a legacy Iceberg table without cursor.json + --- + duration_ms: 9.259754 + type: 'test' + ... +# Subtest: discoverCachePartitions finds both legacy and new-style partitions +ok 313 - discoverCachePartitions finds both legacy and new-style partitions + --- + duration_ms: 14.90255 + type: 'test' + ... +# Subtest: discoverCachePartitions skips .retired directories +ok 314 - discoverCachePartitions skips .retired directories + --- + duration_ms: 6.380658 + type: 'test' + ... +# Subtest: sanitizePathSegment passes through normal values +ok 315 - sanitizePathSegment passes through normal values + --- + duration_ms: 0.235718 + type: 'test' + ... +# Subtest: sanitizePathSegment replaces path separators and special chars +ok 316 - sanitizePathSegment replaces path separators and special chars + --- + duration_ms: 0.081794 + type: 'test' + ... +# Subtest: sanitizePathSegment escapes dot and dotdot +ok 317 - sanitizePathSegment escapes dot and dotdot + --- + duration_ms: 0.069436 + type: 'test' + ... +# Subtest: sanitizePathSegment handles empty string +ok 318 - sanitizePathSegment handles empty string + --- + duration_ms: 0.051438 + type: 'test' + ... +# Subtest: sanitizePathSegment replaces control characters +ok 319 - sanitizePathSegment replaces control characters + --- + duration_ms: 0.11819 + type: 'test' + ... +# Subtest: resolveSourceSegments uses first non-empty source column +ok 320 - resolveSourceSegments uses first non-empty source column + --- + duration_ms: 0.175366 + type: 'test' + ... +# Subtest: resolveSourceSegments falls through source columns +ok 321 - resolveSourceSegments falls through source columns + --- + duration_ms: 0.112711 + type: 'test' + ... +# Subtest: resolveSourceSegments uses fallback when no columns match +ok 322 - resolveSourceSegments uses fallback when no columns match + --- + duration_ms: 0.071378 + type: 'test' + ... +# Subtest: resolveSourceSegments skips empty string values +ok 323 - resolveSourceSegments skips empty string values + --- + duration_ms: 0.061443 + type: 'test' + ... +# Subtest: resolveSourceSegments sanitizes path-unsafe source values +ok 324 - resolveSourceSegments sanitizes path-unsafe source values + --- + duration_ms: 0.07251 + type: 'test' + ... +# Subtest: validateIcebergPartitionFields passes when required fields present +ok 325 - validateIcebergPartitionFields passes when required fields present + --- + duration_ms: 0.10535 + type: 'test' + ... +# Subtest: validateIcebergPartitionFields fails when required fields missing +ok 326 - validateIcebergPartitionFields fails when required fields missing + --- + duration_ms: 0.080743 + type: 'test' + ... +# Subtest: validateIcebergPartitionFields ignores optional fields +ok 327 - validateIcebergPartitionFields ignores optional fields + --- + duration_ms: 0.069656 + type: 'test' + ... +# Subtest: validateIcebergPartitionFields treats empty strings as missing +ok 328 - validateIcebergPartitionFields treats empty strings as missing + --- + duration_ms: 0.068214 + type: 'test' + ... +# Subtest: validateIcebergPartitionFields accepts non-string required fields +ok 329 - validateIcebergPartitionFields accepts non-string required fields + --- + duration_ms: 0.083106 + type: 'test' + ... +# Subtest: validateIcebergPartitionFields rejects null and undefined required fields +ok 330 - validateIcebergPartitionFields rejects null and undefined required fields + --- + duration_ms: 0.109807 + type: 'test' + ... +# Subtest: readCursorSync preserves layout and retention fields +ok 331 - readCursorSync preserves layout and retention fields + --- + duration_ms: 11.739062 + type: 'test' + ... +# Subtest: readCursorSync returns default cursor without new fields when missing +ok 332 - readCursorSync returns default cursor without new fields when missing + --- + duration_ms: 0.195818 + type: 'test' + ... +# Subtest: resolveIcebergDir returns tablePath/table for source-table layout +ok 333 - resolveIcebergDir returns tablePath/table for source-table layout + --- + duration_ms: 1.424846 + type: 'test' + ... +# Subtest: resolveIcebergDir uses custom tableDir for source-table layout +ok 334 - resolveIcebergDir uses custom tableDir for source-table layout + --- + duration_ms: 0.98694 + type: 'test' + ... +# Subtest: resolveIcebergDir returns epoch path for legacy layout +ok 335 - resolveIcebergDir returns epoch path for legacy layout + --- + duration_ms: 1.775849 + type: 'test' + ... +# Subtest: resolveIcebergDir returns tablePath unchanged when no cursor +ok 336 - resolveIcebergDir returns tablePath unchanged when no cursor + --- + duration_ms: 0.110498 + type: 'test' + ... +# Subtest: appendRowsToSourceTable creates source-table layout with table subdirectory +ok 337 - appendRowsToSourceTable creates source-table layout with table subdirectory + --- + duration_ms: 6.556626 + type: 'test' + ... +# Subtest: appendRowsToSourceTable accumulates rowCount across multiple writes +ok 338 - appendRowsToSourceTable accumulates rowCount across multiple writes + --- + duration_ms: 6.874238 + type: 'test' + ... +# Subtest: appendRowsToSourceTable with multiple dates creates one source table +ok 339 - appendRowsToSourceTable with multiple dates creates one source table + --- + duration_ms: 4.833666 + type: 'test' + ... +# Subtest: appendRowsToSourceTable with two sources creates two source tables +ok 340 - appendRowsToSourceTable with two sources creates two source tables + --- + duration_ms: 15.386656 + type: 'test' + ... +# Subtest: appendRowsToSourceTable returns early for empty rows +ok 341 - appendRowsToSourceTable returns early for empty rows + --- + duration_ms: 1.310422 + type: 'test' + ... +# Subtest: discoverCachePartitions finds source-table partitions +ok 342 - discoverCachePartitions finds source-table partitions + --- + duration_ms: 5.242749 + type: 'test' + ... +# Subtest: discoverCachePartitions does not filter source-table partitions by date scope +ok 343 - discoverCachePartitions does not filter source-table partitions by date scope + --- + duration_ms: 11.567392 + type: 'test' + ... +# Subtest: discoverCachePartitions finds both source-table and legacy partitions +ok 344 - discoverCachePartitions finds both source-table and legacy partitions + --- + duration_ms: 5.713023 + type: 'test' + ... +# Subtest: re-settle sweep collapses a split fallback/uuid twin pair after the fact +ok 345 - re-settle sweep collapses a split fallback/uuid twin pair after the fact + --- + duration_ms: 160.169842 + type: 'test' + ... +# Subtest: re-settle sweep upgrades a lone fallback row even when its twin never arrived +ok 346 - re-settle sweep upgrades a lone fallback row even when its twin never arrived + --- + duration_ms: 47.368736 + type: 'test' + ... +# Subtest: re-settle sweep leaves the fallback row untouched when the transcript is unavailable +ok 347 - re-settle sweep leaves the fallback row untouched when the transcript is unavailable + --- + duration_ms: 47.951912 + type: 'test' + ... +# Subtest: a fallback marker auto-triggers compaction without force; a no-marker partition does not +ok 348 - a fallback marker auto-triggers compaction without force; a no-marker partition does not + --- + duration_ms: 55.890699 + type: 'test' + ... +# Subtest: an unmatchable fallback does not force a rewrite every tick - only on new data +ok 349 - an unmatchable fallback does not force a rewrite every tick - only on new data + --- + duration_ms: 85.727727 + type: 'test' + ... +# Subtest: default retention days is 90 +ok 350 - default retention days is 90 + --- + duration_ms: 1.162087 + type: 'test' + ... +# Subtest: retention normalizeConfig applies defaults +ok 351 - retention normalizeConfig applies defaults + --- + duration_ms: 0.694756 + type: 'test' + ... +# Subtest: retention tick on empty cache returns empty results +ok 352 - retention tick on empty cache returns empty results + --- + duration_ms: 12.662736 + type: 'test' + ... +# Subtest: retention commits Iceberg deletes on source-table rows older than cutoff +ok 353 - retention commits Iceberg deletes on source-table rows older than cutoff + --- + duration_ms: 38.35126 + type: 'test' + ... +# Subtest: retention skips source tables when all rows are within retention +ok 354 - retention skips source tables when all rows are within retention + --- + duration_ms: 11.681535 + type: 'test' + ... +# Subtest: retention respects per-dataset override +ok 355 - retention respects per-dataset override + --- + duration_ms: 40.112627 + type: 'test' + ... +# Subtest: cacheStatus reports source-table layout with source field +ok 356 - cacheStatus reports source-table layout with source field + --- + duration_ms: 16.671389 + type: 'test' + ... +# Subtest: cacheStatus reports lastRetentionCutoffDate after retention runs +ok 357 - cacheStatus reports lastRetentionCutoffDate after retention runs + --- + duration_ms: 15.537946 + type: 'test' + ... +# Subtest: normalizeMaintenanceConfig fills defaults +ok 358 - normalizeMaintenanceConfig fills defaults + --- + duration_ms: 0.403994 + type: 'test' + ... +# Subtest: maintenance counts data files and snapshots for source tables +ok 359 - maintenance counts data files and snapshots for source tables + --- + duration_ms: 35.533007 + type: 'test' + ... +# Subtest: maintenance expires snapshots on source tables +ok 360 - maintenance expires snapshots on source tables + --- + duration_ms: 56.885891 + type: 'test' + ... +# Subtest: compaction preserves source-table layout +ok 361 - compaction preserves source-table layout + --- + duration_ms: 197.712203 + type: 'test' + ... +# Subtest: compaction retires empty source table and advances cursor +ok 362 - compaction retires empty source table and advances cursor + --- + duration_ms: 9.64543 + type: 'test' + ... +# Subtest: compaction preserves partition spec and column types from declaration +ok 363 - compaction preserves partition spec and column types from declaration + --- + duration_ms: 282.70272 + type: 'test' + ... +# Subtest: retention second tick reports zero newly deleted rows (no duplicate deletes) +ok 364 - retention second tick reports zero newly deleted rows (no duplicate deletes) + --- + duration_ms: 19.052237 + type: 'test' + ... +# Subtest: retention re-scans unchanged source table when cutoff advances +ok 365 - retention re-scans unchanged source table when cutoff advances + --- + duration_ms: 24.075673 + type: 'test' + ... +# Subtest: retention uses dataset primaryTimestampColumn for source tables +ok 366 - retention uses dataset primaryTimestampColumn for source tables + --- + duration_ms: 14.202086 + type: 'test' + ... +# Subtest: retention evicts source table by mtime when no timestamp column is resolvable +ok 367 - retention evicts source table by mtime when no timestamp column is resolvable + --- + duration_ms: 6.794116 + type: 'test' + ... +# Subtest: retention cursor stays accurate after new data arrives between ticks +ok 368 - retention cursor stays accurate after new data arrives between ticks + --- + duration_ms: 20.17886 + type: 'test' + ... +# Subtest: source-table directory remains intact after retention +ok 369 - source-table directory remains intact after retention + --- + duration_ms: 12.176257 + type: 'test' + ... +# Subtest: normalizeMaintenanceConfig fills compact_batch_bytes default +ok 370 - normalizeMaintenanceConfig fills compact_batch_bytes default + --- + duration_ms: 0.147745 + type: 'test' + ... +# Subtest: normalizeMaintenanceConfig honours an explicit compact_batch_bytes +ok 371 - normalizeMaintenanceConfig honours an explicit compact_batch_bytes + --- + duration_ms: 0.076967 + type: 'test' + ... +# Subtest: compaction flushes by byte budget so a fat column cannot blow up one batch +ok 372 - compaction flushes by byte budget so a fat column cannot blow up one batch + --- + duration_ms: 144.444582 + type: 'test' + ... +# Subtest: a generous byte budget compacts the same input into a single row group +ok 373 - a generous byte budget compacts the same input into a single row group + --- + duration_ms: 114.955471 + type: 'test' + ... +# Subtest: maintenance reclaims a stale cursor-orphaned table dir with no .retired marker +ok 374 - maintenance reclaims a stale cursor-orphaned table dir with no .retired marker + --- + duration_ms: 8.238342 + type: 'test' + ... +# Subtest: orphan sweep never deletes the live table when cursor.json is unreadable +ok 375 - orphan sweep never deletes the live table when cursor.json is unreadable + --- + duration_ms: 10.850662 + type: 'test' + ... +# Subtest: orphan sweep reclaims a stale epoch generation and keeps the live one +ok 376 - orphan sweep reclaims a stale epoch generation and keeps the live one + --- + duration_ms: 9.954971 + type: 'test' + ... +# Subtest: legacy epoch compaction preserves the table sort order +ok 377 - legacy epoch compaction preserves the table sort order + --- + duration_ms: 17.058027 + type: 'test' + ... +# Subtest: an already-compacted partition is not recompacted until new data flushes +ok 378 - an already-compacted partition is not recompacted until new data flushes + --- + duration_ms: 29.371472 + type: 'test' + ... +# Subtest: a foreign sorted replace re-baselines the cursor instead of being rewritten +ok 379 - a foreign sorted replace re-baselines the cursor instead of being rewritten + --- + duration_ms: 29.34419 + type: 'test' + ... +# Subtest: a rebaseline that fails to persist is not reported as rebaselined, and is not counted +ok 380 - a rebaseline that fails to persist is not reported as rebaselined, and is not counted + --- + duration_ms: 24.378012 + type: 'test' + ... +# Subtest: foreign sorted replace recognition works on the source-table layout +ok 381 - foreign sorted replace recognition works on the source-table layout + --- + duration_ms: 14.806014 + type: 'test' + ... +# Subtest: recognition outranks the re-settle force: a fallback row does not undo the sorted layout +ok 382 - recognition outranks the re-settle force: a fallback row does not undo the sorted layout + --- + duration_ms: 10.764861 + type: 'test' + ... +# Subtest: a foreign replace without a declared sort order is not blessed +ok 383 - a foreign replace without a declared sort order is not blessed + --- + duration_ms: 28.891653 + type: 'test' + ... +# Subtest: force still rewrites a foreign sorted replace +ok 384 - force still rewrites a foreign sorted replace + --- + duration_ms: 24.31029 + type: 'test' + ... +# Subtest: a foreign sorted replace tags the maintenance.partition span with rebaselined +ok 385 - a foreign sorted replace tags the maintenance.partition span with rebaselined + --- + duration_ms: 17.208075 + type: 'test' + ... +# Subtest: a partition already due for compaction skips the resettle-candidate row scan +ok 386 - a partition already due for compaction skips the resettle-candidate row scan + --- + duration_ms: 11.154644 + type: 'test' + ... +# Subtest: maintenance walks partitions neediest-first, not directory order +ok 387 - maintenance walks partitions neediest-first, not directory order + --- + duration_ms: 12.986648 + type: 'test' + ... +# Subtest: neediest-first order reads the live table dir for source-table partitions +ok 388 - neediest-first order reads the live table dir for source-table partitions + --- + duration_ms: 14.322378 + type: 'test' + ... +# Subtest: default spool threshold is Iceberg-sized to avoid frequent small commits +ok 389 - default spool threshold is Iceberg-sized to avoid frequent small commits + --- + duration_ms: 0.965087 + type: 'test' + ... +# Subtest: storage.appendRowsToPartition writes data without error +ok 390 - storage.appendRowsToPartition writes data without error + --- + duration_ms: 22.89633 + type: 'test' + ... +# Subtest: spool flush groups rows by source and creates source-table layout +ok 391 - spool flush groups rows by source and creates source-table layout + --- + duration_ms: 38.186129 + type: 'test' + ... +# Subtest: spool flush falls back to source=unknown when no client columns present +ok 392 - spool flush falls back to source=unknown when no client columns present + --- + duration_ms: 17.248405 + type: 'test' + ... +# Subtest: storage.dataSourceForTable keeps columns and cells aligned after internal-field filtering +ok 393 - storage.dataSourceForTable keeps columns and cells aligned after internal-field filtering + --- + duration_ms: 14.241937 + type: 'test' + ... +# Subtest: spool flush creates Iceberg table with partition spec when declaration is provided +ok 394 - spool flush creates Iceberg table with partition spec when declaration is provided + --- + duration_ms: 31.980948 + type: 'test' + ... +# Subtest: spool flush reports rows dropped by required partition validation +ok 395 - spool flush reports rows dropped by required partition validation + --- + duration_ms: 19.165119 + type: 'test' + ... +# Subtest: spool flush uses resolveSourceSegments when declaration is provided +ok 396 - spool flush uses resolveSourceSegments when declaration is provided + --- + duration_ms: 35.032476 + type: 'test' + ... +# Subtest: readSpooledRows yields unflushed rows and goes empty after flush +ok 397 - readSpooledRows yields unflushed rows and goes empty after flush + --- + duration_ms: 14.333776 + type: 'test' + ... +# Subtest: readSpooledRows projects to requested columns and filters by dataset +ok 398 - readSpooledRows projects to requested columns and filters by dataset + --- + duration_ms: 23.197348 + type: 'test' + ... +# Subtest: readSpooledRows skips a parseable envelope missing columns, matching what flush drops +ok 399 - readSpooledRows skips a parseable envelope missing columns, matching what flush drops + --- + duration_ms: 6.935992 + type: 'test' + ... +# Subtest: readSpooledRows on an unknown dataset is an empty stream +ok 400 - readSpooledRows on an unknown dataset is an empty stream + --- + duration_ms: 2.28335 + type: 'test' + ... +# Subtest: readSpooledRows streams a large spool file rather than reading it whole (bounded memory, issue \#280) +ok 401 - readSpooledRows streams a large spool file rather than reading it whole (bounded memory, issue \#280) + --- + duration_ms: 448.008209 + type: 'test' + ... +# Subtest: classifyInactiveState returns absent when the entry is missing from the effective config +ok 402 - classifyInactiveState returns absent when the entry is missing from the effective config + --- + duration_ms: 0.804422 + type: 'test' + ... +# Subtest: classifyInactiveState returns disabled-local when the disabled entry exists only in the local layer +ok 403 - classifyInactiveState returns disabled-local when the disabled entry exists only in the local layer + --- + duration_ms: 0.15153 + type: 'test' + ... +# Subtest: classifyInactiveState returns disabled-central when the disabled entry also names the plugin centrally +ok 404 - classifyInactiveState returns disabled-central when the disabled entry also names the plugin centrally + --- + duration_ms: 0.129286 + type: 'test' + ... +# Subtest: hyp --version prints version and exits 0 +ok 405 - hyp --version prints version and exits 0 + --- + duration_ms: 2.745163 + type: 'test' + ... +# Subtest: hyp -V prints version and exits 0 +ok 406 - hyp -V prints version and exits 0 + --- + duration_ms: 0.602405 + type: 'test' + ... +# Subtest: hyp version prints version info and exits 0 +ok 407 - hyp version prints version info and exits 0 + --- + duration_ms: 1.675877 + type: 'test' + ... +# Subtest: version string matches package.json +ok 408 - version string matches package.json + --- + duration_ms: 0.565028 + type: 'test' + ... +# Subtest: init help documents every flag that selects the non-interactive path +ok 409 - init help documents every flag that selects the non-interactive path + --- + duration_ms: 1.833616 + type: 'test' + ... +# Subtest: init usage does not imply --yes gates the other flags +ok 410 - init usage does not imply --yes gates the other flags + --- + duration_ms: 0.288909 + type: 'test' + ... +# Subtest: renderSelect: an option summary lands on its own indented line under its row +ok 411 - renderSelect: an option summary lands on its own indented line under its row + --- + duration_ms: 0.821258 + type: 'test' + ... +# Subtest: renderSelect: the summary line is dim, not the row colour +ok 412 - renderSelect: the summary line is dim, not the row colour + --- + duration_ms: 0.233174 + type: 'test' + ... +# Subtest: TTY gate: the accept disclosure reaches the screen through the real select prompt +ok 413 - TTY gate: the accept disclosure reaches the screen through the real select prompt + --- + duration_ms: 35.229235 + type: 'test' + ... +# Subtest: non-TTY gate: the numbered fallback prints the accept disclosure under its row +ok 414 - non-TTY gate: the numbered fallback prints the accept disclosure under its row + --- + duration_ms: 1.36203 + type: 'test' + ... +# Subtest: withSpinner off a TTY prints the label once and nothing else +ok 415 - withSpinner off a TTY prints the label once and nothing else + --- + duration_ms: 0.950815 + type: 'test' + ... +# Subtest: withSpinner under HYP_NO_TUI=1 stays on the plain path even on a TTY +ok 416 - withSpinner under HYP_NO_TUI=1 stays on the plain path even on a TTY + --- + duration_ms: 0.188096 + type: 'test' + ... +# Subtest: withSpinner on a TTY animates in place and clears the line when done +ok 417 - withSpinner on a TTY animates in place and clears the line when done + --- + duration_ms: 32.164547 + type: 'test' + ... +# Subtest: withSpinner clears the line and rethrows when the work fails +ok 418 - withSpinner clears the line and rethrows when the work fails + --- + duration_ms: 0.590047 + type: 'test' + ... +# Subtest: installStreamErrorHandlers: EPIPE is swallowed without a word +ok 419 - installStreamErrorHandlers: EPIPE is swallowed without a word + --- + duration_ms: 1.090177 + type: 'test' + ... +# Subtest: installStreamErrorHandlers: any other failure is reported once +ok 420 - installStreamErrorHandlers: any other failure is reported once + --- + duration_ms: 0.993199 + type: 'test' + ... +# Subtest: installStreamErrorHandlers: the returned detach removes the listeners +ok 421 - installStreamErrorHandlers: the returned detach removes the listeners + --- + duration_ms: 0.236059 + type: 'test' + ... +# Subtest: a write past the pipe buffer survives a reader that walked away +ok 422 - a write past the pipe buffer survives a reader that walked away + --- + duration_ms: 236.786262 + type: 'test' + ... +# Subtest: the same write without the handler is what we are protecting against +ok 423 - the same write without the handler is what we are protecting against + --- + duration_ms: 36.039917 + type: 'test' + ... +# Subtest: the palette maps severities to the standard SGR codes, pinned as bytes +ok 424 - the palette maps severities to the standard SGR codes, pinned as bytes + --- + duration_ms: 1.19076 + type: 'test' + ... +# Subtest: rule order is observable: the continuation rule outranks failed: on an indented line +ok 425 - rule order is observable: the continuation rule outranks failed: on an indented line + --- + duration_ms: 0.169417 + type: 'test' + ... +# Subtest: a severity word mid-line is not a prefix, so the line stays plain +ok 426 - a severity word mid-line is not a prefix, so the line stays plain + --- + duration_ms: 0.237881 + type: 'test' + ... +# Subtest: errors are red and warnings are yellow +ok 427 - errors are red and warnings are yellow + --- + duration_ms: 0.169368 + type: 'test' + ... +# Subtest: the shouted and thrown spellings classify the same as the lowercase ones +ok 428 - the shouted and thrown spellings classify the same as the lowercase ones + --- + duration_ms: 0.11168 + type: 'test' + ... +# Subtest: note, tip and usage are dim, not coloured +ok 429 - note, tip and usage are dim, not coloured + --- + duration_ms: 0.104088 + type: 'test' + ... +# Subtest: a `hyp :` diagnostic is red, at every depth of subcommand +ok 430 - a `hyp :` diagnostic is red, at every depth of subcommand + --- + duration_ms: 0.20448 + type: 'test' + ... +# Subtest: a cancellation is not an error +ok 431 - a cancellation is not an error + --- + duration_ms: 0.114333 + type: 'test' + ... +# Subtest: `... failed:` diagnostics without a hyp prefix are red too +ok 432 - `... failed:` diagnostics without a hyp prefix are red too + --- + duration_ms: 0.379667 + type: 'test' + ... +# Subtest: only the prefix is painted, never the message body +ok 433 - only the prefix is painted, never the message body + --- + duration_ms: 0.38814 + type: 'test' + ... +# Subtest: continuation lines keep the severity of the line above by staying plain +ok 434 - continuation lines keep the severity of the line above by staying plain + --- + duration_ms: 0.183038 + type: 'test' + ... +# Subtest: unclassified output is left alone rather than guessed at +ok 435 - unclassified output is left alone rather than guessed at + --- + duration_ms: 0.103197 + type: 'test' + ... +# Subtest: every line of a multi-line chunk is classified +ok 436 - every line of a multi-line chunk is classified + --- + duration_ms: 0.147253 + type: 'test' + ... +# Subtest: a chunk that resumes mid-line is not re-classified as a new diagnostic +ok 437 - a chunk that resumes mid-line is not re-classified as a new diagnostic + --- + duration_ms: 0.086481 + type: 'test' + ... +# Subtest: a non-TTY stream is returned untouched, byte for byte +ok 438 - a non-TTY stream is returned untouched, byte for byte + --- + duration_ms: 0.184581 + type: 'test' + ... +# Subtest: NO_COLOR wins over a TTY +ok 439 - NO_COLOR wins over a TTY + --- + duration_ms: 0.111069 + type: 'test' + ... +# Subtest: a TTY stream is wrapped, and paints across separate writes +ok 440 - a TTY stream is wrapped, and paints across separate writes + --- + duration_ms: 0.167425 + type: 'test' + ... +# Subtest: a write that does not end in a newline leaves the next write mid-line +ok 441 - a write that does not end in a newline leaves the next write mid-line + --- + duration_ms: 0.130228 + type: 'test' + ... +# Subtest: the wrap preserves the rest of the stream surface +ok 442 - the wrap preserves the rest of the stream surface + --- + duration_ms: 0.0921 + type: 'test' + ... +# Subtest: non-string chunks pass through unexamined +ok 443 - non-string chunks pass through unexamined + --- + duration_ms: 0.109356 + type: 'test' + ... +# Subtest: reduce: ctrl+c cancels any active state +ok 444 - reduce: ctrl+c cancels any active state + --- + duration_ms: 1.277201 + type: 'test' + ... +# Subtest: reduce: escape cancels any active state +ok 445 - reduce: escape cancels any active state + --- + duration_ms: 0.214816 + type: 'test' + ... +# Subtest: reduce: terminal states ignore further input +ok 446 - reduce: terminal states ignore further input + --- + duration_ms: 0.140864 + type: 'test' + ... +# Subtest: multiselect: arrow down moves cursor through the submit row and wraps +ok 447 - multiselect: arrow down moves cursor through the submit row and wraps + --- + duration_ms: 0.240996 + type: 'test' + ... +# Subtest: multiselect: arrow up wraps to the submit row +ok 448 - multiselect: arrow up wraps to the submit row + --- + duration_ms: 0.204601 + type: 'test' + ... +# Subtest: multiselect: j and k are aliases for down and up +ok 449 - multiselect: j and k are aliases for down and up + --- + duration_ms: 0.142737 + type: 'test' + ... +# Subtest: multiselect: space toggles current option +ok 450 - multiselect: space toggles current option + --- + duration_ms: 0.207005 + type: 'test' + ... +# Subtest: multiselect: a toggles all on then all off +ok 451 - multiselect: a toggles all on then all off + --- + duration_ms: 0.766745 + type: 'test' + ... +# Subtest: multiselect: a with mixed selection checks all +ok 452 - multiselect: a with mixed selection checks all + --- + duration_ms: 0.356993 + type: 'test' + ... +# Subtest: multiselect: space on a disabled row is a no-op +ok 453 - multiselect: space on a disabled row is a no-op + --- + duration_ms: 0.372687 + type: 'test' + ... +# Subtest: multiselect: a leaves disabled rows fixed and toggles the rest +ok 454 - multiselect: a leaves disabled rows fixed and toggles the rest + --- + duration_ms: 0.235188 + type: 'test' + ... +# Subtest: multiselect: digit keys 1-3 jump to in-range index +ok 455 - multiselect: digit keys 1-3 jump to in-range index + --- + duration_ms: 0.227847 + type: 'test' + ... +# Subtest: multiselect: digit out of range is a no-op +ok 456 - multiselect: digit out of range is a no-op + --- + duration_ms: 0.118961 + type: 'test' + ... +# Subtest: multiselect: space on the submit row resolves +ok 457 - multiselect: space on the submit row resolves + --- + duration_ms: 0.174535 + type: 'test' + ... +# Subtest: multiselect: space on the submit row below bounds.min sets error and stays active +ok 458 - multiselect: space on the submit row below bounds.min sets error and stays active + --- + duration_ms: 1.366648 + type: 'test' + ... +# Subtest: multiselect: digit keys never jump to the submit row +ok 459 - multiselect: digit keys never jump to the submit row + --- + duration_ms: 0.094143 + type: 'test' + ... +# Subtest: multiselect: enter without bounds resolves +ok 460 - multiselect: enter without bounds resolves + --- + duration_ms: 0.083346 + type: 'test' + ... +# Subtest: multiselect: enter below bounds.min sets error and stays active +ok 461 - multiselect: enter below bounds.min sets error and stays active + --- + duration_ms: 0.088265 + type: 'test' + ... +# Subtest: multiselect: enter above bounds.max sets error and stays active +ok 462 - multiselect: enter above bounds.max sets error and stays active + --- + duration_ms: 0.119972 + type: 'test' + ... +# Subtest: multiselect: bounds error clears on next cursor move +ok 463 - multiselect: bounds error clears on next cursor move + --- + duration_ms: 0.141485 + type: 'test' + ... +# Subtest: multiselect: empty options + enter with bounds.min still rejects +ok 464 - multiselect: empty options + enter with bounds.min still rejects + --- + duration_ms: 0.09839 + type: 'test' + ... +# Subtest: select: cursor moves and wraps; enter resolves +ok 465 - select: cursor moves and wraps; enter resolves + --- + duration_ms: 0.117289 + type: 'test' + ... +# Subtest: select: space does not toggle (single-select has no toggle) +ok 466 - select: space does not toggle (single-select has no toggle) + --- + duration_ms: 0.074473 + type: 'test' + ... +# Subtest: text: printable characters append to value +ok 467 - text: printable characters append to value + --- + duration_ms: 0.139352 + type: 'test' + ... +# Subtest: text: backspace removes the last char and stops at empty +ok 468 - text: backspace removes the last char and stops at empty + --- + duration_ms: 0.093642 + type: 'test' + ... +# Subtest: text: control characters are ignored as input +ok 469 - text: control characters are ignored as input + --- + duration_ms: 0.083597 + type: 'test' + ... +# Subtest: text: enter without validate resolves with current value +ok 470 - text: enter without validate resolves with current value + --- + duration_ms: 0.086932 + type: 'test' + ... +# Subtest: text: enter on empty value applies default +ok 471 - text: enter on empty value applies default + --- + duration_ms: 0.085129 + type: 'test' + ... +# Subtest: text: enter when validate rejects sets error and stays active +ok 472 - text: enter when validate rejects sets error and stays active + --- + duration_ms: 0.098229 + type: 'test' + ... +# Subtest: text: mask flag does not change the value field; only render relies on it +ok 473 - text: mask flag does not change the value field; only render relies on it + --- + duration_ms: 0.083076 + type: 'test' + ... +# Subtest: non-TTY stdin rejects multiselect with the documented error +ok 474 - non-TTY stdin rejects multiselect with the documented error + --- + duration_ms: 1.338915 + type: 'test' + ... +# Subtest: non-TTY stdin rejects select with the documented error +ok 475 - non-TTY stdin rejects select with the documented error + --- + duration_ms: 0.459639 + type: 'test' + ... +# Subtest: non-TTY stdin rejects text with the documented error +ok 476 - non-TTY stdin rejects text with the documented error + --- + duration_ms: 0.327998 + type: 'test' + ... +# Subtest: HYP_NO_TUI=1 forces the same TTY error even for fake-TTY streams +ok 477 - HYP_NO_TUI=1 forces the same TTY error even for fake-TTY streams + --- + duration_ms: 1.545309 + type: 'test' + ... +# Subtest: injected env.HYP_NO_TUI=1 forces the TTY error even when process.env is clean +ok 478 - injected env.HYP_NO_TUI=1 forces the TTY error even when process.env is clean + --- + duration_ms: 0.325925 + type: 'test' + ... +# Subtest: package export "hypaware/tui" resolves to the same module +ok 479 - package export "hypaware/tui" resolves to the same module + --- + duration_ms: 0.678942 + type: 'test' + ... +# Subtest: multiselect: NO_COLOR frame contains no SGR escapes +ok 480 - multiselect: NO_COLOR frame contains no SGR escapes + --- + duration_ms: 1.012529 + type: 'test' + ... +# Subtest: multiselect: colored frame contains at least one SGR escape +ok 481 - multiselect: colored frame contains at least one SGR escape + --- + duration_ms: 0.181115 + type: 'test' + ... +# Subtest: multiselect: cursor row uses pointer ">", others use space +ok 482 - multiselect: cursor row uses pointer ">", others use space + --- + duration_ms: 0.147685 + type: 'test' + ... +# Subtest: multiselect: a disabled row renders its label and checkbox +ok 483 - multiselect: a disabled row renders its label and checkbox + --- + duration_ms: 0.115396 + type: 'test' + ... +# Subtest: multiselect: a disabled row under the cursor renders dim, not the cyan cursor color +ok 484 - multiselect: a disabled row under the cursor renders dim, not the cyan cursor color + --- + duration_ms: 0.143839 + type: 'test' + ... +# Subtest: multiselect: summary lines appear under labels when set +ok 485 - multiselect: summary lines appear under labels when set + --- + duration_ms: 0.096446 + type: 'test' + ... +# Subtest: multiselect: empty options renders title + hint + submit row without option rows +ok 486 - multiselect: empty options renders title + hint + submit row without option rows + --- + duration_ms: 0.185452 + type: 'test' + ... +# Subtest: multiselect: submit row renders below the options, pointer-free when not focused +ok 487 - multiselect: submit row renders below the options, pointer-free when not focused + --- + duration_ms: 0.133123 + type: 'test' + ... +# Subtest: multiselect: submit row carries the pointer when the cursor is on it +ok 488 - multiselect: submit row carries the pointer when the cursor is on it + --- + duration_ms: 0.271172 + type: 'test' + ... +# Subtest: multiselect: error line is included when set +ok 489 - multiselect: error line is included when set + --- + duration_ms: 0.280536 + type: 'test' + ... +# Subtest: multiselect: frame ends with a single trailing newline +ok 490 - multiselect: frame ends with a single trailing newline + --- + duration_ms: 0.131109 + type: 'test' + ... +# Subtest: select: renders pointer-and-label rows +ok 491 - select: renders pointer-and-label rows + --- + duration_ms: 0.152562 + type: 'test' + ... +# Subtest: text: render shows "> " followed by the value +ok 492 - text: render shows "> " followed by the value + --- + duration_ms: 0.113792 + type: 'test' + ... +# Subtest: text: render masks value when mask is true +ok 493 - text: render masks value when mask is true + --- + duration_ms: 0.075484 + type: 'test' + ... +# Subtest: text: default hint shown when value is empty +ok 494 - text: default hint shown when value is empty + --- + duration_ms: 0.937896 + type: 'test' + ... +# Subtest: text: default hint disappears once value is typed +ok 495 - text: default hint disappears once value is typed + --- + duration_ms: 0.07206 + type: 'test' + ... +# Subtest: render: hint override replaces default hint line +ok 496 - render: hint override replaces default hint line + --- + duration_ms: 0.086241 + type: 'test' + ... +# Subtest: render: items render verbatim between the title and the hint +ok 497 - render: items render verbatim between the title and the hint + --- + duration_ms: 0.076006 + type: 'test' + ... +# Subtest: box: frame is wrapped in a border, every row padded to one width +ok 498 - box: frame is wrapped in a border, every row padded to one width + --- + duration_ms: 0.245233 + type: 'test' + ... +# Subtest: box: a frame wider than the terminal is dropped, not soft-wrapped +ok 499 - box: a frame wider than the terminal is dropped, not soft-wrapped + --- + duration_ms: 0.102746 + type: 'test' + ... +# Subtest: box: border width measures visible columns, not style escapes +ok 500 - box: border width measures visible columns, not style escapes + --- + duration_ms: 0.117188 + type: 'test' + ... +# Subtest: box: an unboxed state renders exactly as it did before the field existed +ok 501 - box: an unboxed state renders exactly as it did before the field existed + --- + duration_ms: 0.116948 + type: 'test' + ... +# Subtest: render: a state without items renders exactly as it does today +ok 502 - render: a state without items renders exactly as it does today + --- + duration_ms: 0.06595 + type: 'test' + ... +# Subtest: runtime: multiselect happy path returns selected values in order +ok 503 - runtime: multiselect happy path returns selected values in order + --- + duration_ms: 8.348108 + type: 'test' + ... +# Subtest: runtime: multiselect cancel via ctrl+c throws PromptCancelledError +ok 504 - runtime: multiselect cancel via ctrl+c throws PromptCancelledError + --- + duration_ms: 2.355019 + type: 'test' + ... +# Subtest: runtime: escape on an allowBack multiselect throws PromptBackRequestedError +ok 505 - runtime: escape on an allowBack multiselect throws PromptBackRequestedError + --- + duration_ms: 501.343883 + type: 'test' + ... +# Subtest: runtime: escape on an allowBack select throws PromptBackRequestedError +ok 506 - runtime: escape on an allowBack select throws PromptBackRequestedError + --- + duration_ms: 503.920369 + type: 'test' + ... +# Subtest: runtime: ctrl+c on an allowBack prompt still cancels +ok 507 - runtime: ctrl+c on an allowBack prompt still cancels + --- + duration_ms: 0.939348 + type: 'test' + ... +# Subtest: runtime: escape on a prompt without allowBack still cancels +ok 508 - runtime: escape on a prompt without allowBack still cancels + --- + duration_ms: 500.925848 + type: 'test' + ... +# Subtest: runtime: prompt cancellation predicate recognizes PromptCancelledError +ok 509 - runtime: prompt cancellation predicate recognizes PromptCancelledError + --- + duration_ms: 0.242939 + type: 'test' + ... +# Subtest: runtime: multiselect bounds rejection retains active state until satisfied +ok 510 - runtime: multiselect bounds rejection retains active state until satisfied + --- + duration_ms: 1.074653 + type: 'test' + ... +# Subtest: runtime: select returns the value at the cursor on enter +ok 511 - runtime: select returns the value at the cursor on enter + --- + duration_ms: 0.821218 + type: 'test' + ... +# Subtest: runtime: select cancel throws PromptCancelledError +ok 512 - runtime: select cancel throws PromptCancelledError + --- + duration_ms: 501.843423 + type: 'test' + ... +# Subtest: runtime: text returns the typed buffer on enter +ok 513 - runtime: text returns the typed buffer on enter + --- + duration_ms: 0.920309 + type: 'test' + ... +# Subtest: runtime: text empty + default returns default on enter +ok 514 - runtime: text empty + default returns default on enter + --- + duration_ms: 0.390654 + type: 'test' + ... +# Subtest: runtime: text validate-rejected enter stays active until valid +ok 515 - runtime: text validate-rejected enter stays active until valid + --- + duration_ms: 0.592761 + type: 'test' + ... +# Subtest: runtime: cursor-hide is written on entry and cursor-show on resolve +ok 516 - runtime: cursor-hide is written on entry and cursor-show on resolve + --- + duration_ms: 0.46748 + type: 'test' + ... +# Subtest: runtime: cursor-show is written even on cancel +ok 517 - runtime: cursor-show is written even on cancel + --- + duration_ms: 0.463354 + type: 'test' + ... +# Subtest: runtime: cleanup restores paused stdin after prompt completion +ok 518 - runtime: cleanup restores paused stdin after prompt completion + --- + duration_ms: 0.36203 + type: 'test' + ... +# Subtest: runtime: cleanup preserves previously flowing stdin after prompt completion +ok 519 - runtime: cleanup preserves previously flowing stdin after prompt completion + --- + duration_ms: 1.046841 + type: 'test' + ... +# Subtest: runtime: render failures during keypress reject and clean up +ok 520 - runtime: render failures during keypress reject and clean up + --- + duration_ms: 0.431335 + type: 'test' + ... +# Subtest: countPhysicalRows: counts each logical line once when nothing wraps +ok 521 - countPhysicalRows: counts each logical line once when nothing wraps + --- + duration_ms: 0.073662 + type: 'test' + ... +# Subtest: countPhysicalRows: empty lines still occupy one row +ok 522 - countPhysicalRows: empty lines still occupy one row + --- + duration_ms: 0.042224 + type: 'test' + ... +# Subtest: countPhysicalRows: a line wider than the terminal counts its wrapped rows +ok 523 - countPhysicalRows: a line wider than the terminal counts its wrapped rows + --- + duration_ms: 0.0528 + type: 'test' + ... +# Subtest: countPhysicalRows: a line exactly the terminal width stays one row +ok 524 - countPhysicalRows: a line exactly the terminal width stays one row + --- + duration_ms: 0.043436 + type: 'test' + ... +# Subtest: countPhysicalRows: ANSI style codes do not inflate the width +ok 525 - countPhysicalRows: ANSI style codes do not inflate the width + --- + duration_ms: 0.040972 + type: 'test' + ... +# Subtest: countPhysicalRows: defaults to 80 columns for non-TTY widths +ok 526 - countPhysicalRows: defaults to 80 columns for non-TTY widths + --- + duration_ms: 0.035424 + type: 'test' + ... +# Subtest: runtime: redraw moves up by physical (wrapped) rows on a narrow terminal +ok 527 - runtime: redraw moves up by physical (wrapped) rows on a narrow terminal + --- + duration_ms: 1.578629 + type: 'test' + ... +# Subtest: runtime: clearOnResolve erases the settled frame on resolve +ok 528 - runtime: clearOnResolve erases the settled frame on resolve + --- + duration_ms: 0.691481 + type: 'test' + ... +# Subtest: runtime: clearOnResolve erases the settled frame on cancel +ok 529 - runtime: clearOnResolve erases the settled frame on cancel + --- + duration_ms: 0.605661 + type: 'test' + ... +# Subtest: runtime: without clearOnResolve the settled frame is left in place +ok 530 - runtime: without clearOnResolve the settled frame is left in place + --- + duration_ms: 0.335811 + type: 'test' + ... +# Subtest: runtime: overlapping prompts are rejected +ok 531 - runtime: overlapping prompts are rejected + --- + duration_ms: 0.403994 + type: 'test' + ... +# Subtest: reduce: escape settles an allowBack prompt as backed, not cancelled +ok 532 - reduce: escape settles an allowBack prompt as backed, not cancelled + --- + duration_ms: 1.156979 + type: 'test' + ... +# Subtest: reduce: without allowBack escape keeps meaning cancel +ok 533 - reduce: without allowBack escape keeps meaning cancel + --- + duration_ms: 0.158681 + type: 'test' + ... +# Subtest: reduce: ctrl+c cancels even on an allowBack prompt +ok 534 - reduce: ctrl+c cancels even on an allowBack prompt + --- + duration_ms: 0.153162 + type: 'test' + ... +# Subtest: render: the default hint tells the truth about escape +ok 535 - render: the default hint tells the truth about escape + --- + duration_ms: 0.474922 + type: 'test' + ... +# Subtest: isPromptBackError recognises the error and name-preserving copies +ok 536 - isPromptBackError recognises the error and name-preserving copies + --- + duration_ms: 0.200225 + type: 'test' + ... +# Subtest: runWizardFork: with allowBack the legacy prompt accepts b and resolves back +ok 537 - runWizardFork: with allowBack the legacy prompt accepts b and resolves back + --- + duration_ms: 3.564889 + type: 'test' + ... +# Subtest: runWizardFork: without allowBack a stray b quits, and the prompt never mentions back +ok 538 - runWizardFork: without allowBack a stray b quits, and the prompt never mentions back + --- + duration_ms: 1.298193 + type: 'test' + ... +# Subtest: runWizardPick: back at the gate propagates only when the orchestrator allowed it +ok 539 - runWizardPick: back at the gate propagates only when the orchestrator allowed it + --- + duration_ms: 38.595751 + type: 'test' + ... +# Subtest: runWizardPick: back at the menu returns to the gate, not out of the lane +ok 540 - runWizardPick: back at the menu returns to the gate, not out of the lane + --- + duration_ms: 10.321247 + type: 'test' + ... +# Subtest: runWizardPick: without a gate and without allowBack the menu offers no back +ok 541 - runWizardPick: without a gate and without allowBack the menu offers no back + --- + duration_ms: 13.271481 + type: 'test' + ... +# Subtest: runWizardPick: initialSelection seeds the boxes and skips detection +ok 542 - runWizardPick: initialSelection seeds the boxes and skips detection + --- + duration_ms: 9.624699 + type: 'test' + ... +# Subtest: runWizardSyncScope: back at the gate propagates and leaves the store unwritten +ok 543 - runWizardSyncScope: back at the gate propagates and leaves the store unwritten + --- + duration_ms: 1.136347 + type: 'test' + ... +# Subtest: runWizardSyncScope: back at the menu returns to the gate +ok 544 - runWizardSyncScope: back at the menu returns to the gate + --- + duration_ms: 1.143448 + type: 'test' + ... +# Subtest: runInitWizard: a back from pick re-presents the fork +ok 545 - runInitWizard: a back from pick re-presents the fork + --- + duration_ms: 1.8491 + type: 'test' + ... +# Subtest: runInitWizard: a back from pick re-presents the express gate, not the fork +ok 546 - runInitWizard: a back from pick re-presents the express gate, not the fork + --- + duration_ms: 10.174655 + type: 'test' + ... +# Subtest: runInitWizard: a back from sync re-presents pick without re-asking the express gate +ok 547 - runInitWizard: a back from sync re-presents pick without re-asking the express gate + --- + duration_ms: 12.708356 + type: 'test' + ... +# Subtest: runInitWizard: with nothing to accept there is no gate, and pick backs straight to the fork +ok 548 - runInitWizard: with nothing to accept there is no gate, and pick backs straight to the fork + --- + duration_ms: 15.436972 + type: 'test' + ... +# Subtest: runInitWizard: a back from folders skips a sync lane that asked nothing and reaches pick +ok 549 - runInitWizard: a back from folders skips a sync lane that asked nothing and reaches pick + --- + duration_ms: 4.520901 + type: 'test' + ... +# Subtest: runInitWizard: a back from folders re-presents a sync lane that did ask +ok 550 - runInitWizard: a back from folders re-presents a sync lane that did ask + --- + duration_ms: 5.361098 + type: 'test' + ... +# Subtest: runInitWizard: a back from pick reaches the fork when the confirmed picks leave the gate empty +ok 551 - runInitWizard: a back from pick reaches the fork when the confirmed picks leave the gate empty + --- + duration_ms: 9.976173 + type: 'test' + ... +# Subtest: runInitWizard: back past a completed join reuses it instead of re-running the login +ok 552 - runInitWizard: back past a completed join reuses it instead of re-running the login + --- + duration_ms: 16.374067 + type: 'test' + ... +# Subtest: runInitWizard: a back from sync re-runs pick seeded with the confirmed selection +ok 553 - runInitWizard: a back from sync re-runs pick seeded with the confirmed selection + --- + duration_ms: 15.591969 + type: 'test' + ... +# Subtest: runInitWizard: a back from the fork re-presents the returning gate +ok 554 - runInitWizard: a back from the fork re-presents the returning gate + --- + duration_ms: 4.687084 + type: 'test' + ... +# Subtest: runInitWizard: a first-run fork has no gate screen and offers no back +ok 555 - runInitWizard: a first-run fork has no gate screen and offers no back + --- + duration_ms: 4.385375 + type: 'test' + ... +# Subtest: runInitWizard: interactive pick lanes are offered back; non-interactive are not +ok 556 - runInitWizard: interactive pick lanes are offered back; non-interactive are not + --- + duration_ms: 6.372536 + type: 'test' + ... +# Subtest: runInitWizard: a join that locked nothing still counts as enrolled when backing to local +ok 557 - runInitWizard: a join that locked nothing still counts as enrolled when backing to local + --- + duration_ms: 10.174114 + type: 'test' + ... +# Subtest: runInitWizard: disconnecting after a join that locked nothing drops the sync lane +ok 558 - runInitWizard: disconnecting after a join that locked nothing drops the sync lane + --- + duration_ms: 7.025418 + type: 'test' + ... +# Subtest: runInitWizard: ctrl+c at the disconnect question cancels the run instead of re-presenting the fork +ok 559 - runInitWizard: ctrl+c at the disconnect question cancels the run instead of re-presenting the fork + --- + duration_ms: 4.343051 + type: 'test' + ... +# Subtest: runInitWizard end-to-end: join, back to the fork, local, and the enrolled machine is still asked what syncs +ok 560 - runInitWizard end-to-end: join, back to the fork, local, and the enrolled machine is still asked what syncs + --- + duration_ms: 13.29678 + type: 'test' + ... +# Subtest: runConfigurePhase: a zero exit keeps the source and records ok +ok 561 - runConfigurePhase: a zero exit keeps the source and records ok + --- + duration_ms: 2.547523 + type: 'test' + ... +# Subtest: runConfigurePhase: only needs_setup descriptors with a configure_command run +ok 562 - runConfigurePhase: only needs_setup descriptors with a configure_command run + --- + duration_ms: 0.4934 + type: 'test' + ... +# Subtest: runConfigurePhase: a non-zero exit drops the source and prints the catch-up hint +ok 563 - runConfigurePhase: a non-zero exit drops the source and prints the catch-up hint + --- + duration_ms: 0.422632 + type: 'test' + ... +# Subtest: runConfigurePhase: a drop does not abort the phase; later sources still run +ok 564 - runConfigurePhase: a drop does not abort the phase; later sources still run + --- + duration_ms: 0.564789 + type: 'test' + ... +# Subtest: runConfigurePhase: a thrown configure drops the source without rethrowing +ok 565 - runConfigurePhase: a thrown configure drops the source without rethrowing + --- + duration_ms: 0.464766 + type: 'test' + ... +# Subtest: runConfigurePhase: --print-commands threads onto the invoked command argv +ok 566 - runConfigurePhase: --print-commands threads onto the invoked command argv + --- + duration_ms: 0.275058 + type: 'test' + ... +# Subtest: runConfigurePhase: never runs off a non-interactive opts.picks path +ok 567 - runConfigurePhase: never runs off a non-interactive opts.picks path + --- + duration_ms: 0.24334 + type: 'test' + ... +# Subtest: a needs_setup row the existing config already composed is skipped, not re-run +ok 568 - a needs_setup row the existing config already composed is skipped, not re-run + --- + duration_ms: 0.184099 + type: 'test' + ... +# Subtest: a newly picked needs_setup row still runs when another row is carried +ok 569 - a newly picked needs_setup row still runs when another row is carried + --- + duration_ms: 0.556846 + type: 'test' + ... +# Subtest: the gate lists the rows it will record, and the accept row names the act on them +ok 570 - the gate lists the rows it will record, and the accept row names the act on them + --- + duration_ms: 4.469454 + type: 'test' + ... +# Subtest: the gate claims a server only when told it has one +ok 571 - the gate claims a server only when told it has one + --- + duration_ms: 3.295258 + type: 'test' + ... +# Subtest: declining runs the lanes as they are; back and cancel are their own answers +ok 572 - declining runs the lanes as they are; back and cancel are their own answers + --- + duration_ms: 7.341338 + type: 'test' + ... +# Subtest: the sync lane auto-accepts by narrating the same split and writing the same store +ok 573 - the sync lane auto-accepts by narrating the same split and writing the same store + --- + duration_ms: 8.124358 + type: 'test' + ... +# Subtest: the new-folder lane auto-accepts to the default and records it +ok 574 - the new-folder lane auto-accepts to the default and records it + --- + duration_ms: 1.791112 + type: 'test' + ... +# Subtest: narrateAcceptedGate prints the gate title and its items verbatim, led by a blank line +ok 575 - narrateAcceptedGate prints the gate title and its items verbatim, led by a blank line + --- + duration_ms: 0.119522 + type: 'test' + ... +# Subtest: resolveOnPath: finds an executable on PATH, ignores a non-executable match +ok 576 - resolveOnPath: finds an executable on PATH, ignores a non-executable match + --- + duration_ms: 6.372476 + type: 'test' + ... +# Subtest: resolveLaunchers: picked and resolvable only; a launch-less client is never offered +ok 577 - resolveLaunchers: picked and resolvable only; a launch-less client is never offered + --- + duration_ms: 0.935261 + type: 'test' + ... +# Subtest: resolveLaunchers: an unpicked client is not offered even when it resolves +ok 578 - resolveLaunchers: an unpicked client is not offered even when it resolves + --- + duration_ms: 0.167144 + type: 'test' + ... +# Subtest: runWizardFirstAsk: a pick spawns the client with the question as argv +ok 579 - runWizardFirstAsk: a pick spawns the client with the question as argv + --- + duration_ms: 1.977636 + type: 'test' + ... +# Subtest: runWizardFirstAsk: every `{prompt}` slot in a manifest arg template is filled +ok 580 - runWizardFirstAsk: every `{prompt}` slot in a manifest arg template is filled + --- + duration_ms: 0.404785 + type: 'test' + ... +# Subtest: runWizardFirstAsk: no launchable client prints the list and launches nothing +ok 581 - runWizardFirstAsk: no launchable client prints the list and launches nothing + --- + duration_ms: 0.503665 + type: 'test' + ... +# Subtest: runWizardFirstAsk: the ask is framed, so it reads as a screen and not as more output +ok 582 - runWizardFirstAsk: the ask is framed, so it reads as a screen and not as more output + --- + duration_ms: 0.443625 + type: 'test' + ... +# Subtest: runWizardFirstAsk: an empty cache suppresses the launch and says why +ok 583 - runWizardFirstAsk: an empty cache suppresses the launch and says why + --- + duration_ms: 0.425016 + type: 'test' + ... +# Subtest: runWizardFirstAsk: an unknown row count never withholds the offer +ok 584 - runWizardFirstAsk: an unknown row count never withholds the offer + --- + duration_ms: 0.529705 + type: 'test' + ... +# Subtest: runWizardFirstAsk: "Not now" and a cancelled prompt both decline, and keep the list +ok 585 - runWizardFirstAsk: "Not now" and a cancelled prompt both decline, and keep the list + --- + duration_ms: 0.89508 + type: 'test' + ... +# Subtest: runWizardFirstAsk: a non-interactive run prints the list and never prompts +ok 586 - runWizardFirstAsk: a non-interactive run prints the list and never prompts + --- + duration_ms: 0.474801 + type: 'test' + ... +# Subtest: runWizardFirstAsk: a spawn failure degrades to the list, never a throw +ok 587 - runWizardFirstAsk: a spawn failure degrades to the list, never a throw + --- + duration_ms: 0.469744 + type: 'test' + ... +# Subtest: runWizardFirstAsk: an unforeseen error is contained +ok 588 - runWizardFirstAsk: an unforeseen error is contained + --- + duration_ms: 0.359677 + type: 'test' + ... +# Subtest: runWizardFirstAsk: two launchable clients ask which one answers +ok 589 - runWizardFirstAsk: two launchable clients ask which one answers + --- + duration_ms: 0.380979 + type: 'test' + ... +# Subtest: the suggested prompts are a short list of distinct, routable questions +ok 590 - the suggested prompts are a short list of distinct, routable questions + --- + duration_ms: 0.351454 + type: 'test' + ... +# Subtest: every suggested label fits a narrow terminal without wrapping +ok 591 - every suggested label fits a narrow terminal without wrapping + --- + duration_ms: 0.138471 + type: 'test' + ... +# Subtest: runWizardFirstLook: writes the two setup sections, names the fuller command, reports row counts +ok 592 - runWizardFirstLook: writes the two setup sections, names the fuller command, reports row counts + --- + duration_ms: 3.960459 + type: 'test' + ... +# Subtest: runWizardFirstLook: an expired deadline keeps the sections that finished +ok 593 - runWizardFirstLook: an expired deadline keeps the sections that finished + --- + duration_ms: 123.35644 + type: 'test' + ... +# Subtest: runWizardFirstLook: a slow cache skips within budget and says what to run +ok 594 - runWizardFirstLook: a slow cache skips within budget and says what to run + --- + duration_ms: 40.055961 + type: 'test' + ... +# Subtest: runWizardFirstLook: a cache inside the budget still renders +ok 595 - runWizardFirstLook: a cache inside the budget still renders + --- + duration_ms: 18.696297 + type: 'test' + ... +# Subtest: runWizardFirstLook: an unregistered dataset skips silently +ok 596 - runWizardFirstLook: an unregistered dataset skips silently + --- + duration_ms: 0.555344 + type: 'test' + ... +# Subtest: runWizardFirstLook: a query failure degrades to a skipped step, not a throw +ok 597 - runWizardFirstLook: a query failure degrades to a skipped step, not a throw + --- + duration_ms: 0.4016 + type: 'test' + ... +# Subtest: runWizardFirstLook: a synchronous write failure cannot escape and fail a finished install +ok 598 - runWizardFirstLook: a synchronous write failure cannot escape and fail a finished install + --- + duration_ms: 0.56592 + type: 'test' + ... +# Subtest: runWizardFirstLook: a render failure is contained too +ok 599 - runWizardFirstLook: a render failure is contained too + --- + duration_ms: 0.460119 + type: 'test' + ... +# Subtest: firstLookNoticeSink: discloses withheld rows, drops the freshness line +ok 600 - firstLookNoticeSink: discloses withheld rows, drops the freshness line + --- + duration_ms: 3.382671 + type: 'test' + ... +# Subtest: firstLookNoticeSink: a closed sink drops a late disclosure +ok 601 - firstLookNoticeSink: a closed sink drops a late disclosure + --- + duration_ms: 0.415462 + type: 'test' + ... +# Subtest: runWizardFirstLook: no runner (no query registry) skips +ok 602 - runWizardFirstLook: no runner (no query registry) skips + --- + duration_ms: 0.409242 + type: 'test' + ... +# Subtest: sync leads, is the bare-enter default, and both rows state their consequence +ok 603 - sync leads, is the bare-enter default, and both rows state their consequence + --- + duration_ms: 12.41538 + type: 'test' + ... +# Subtest: choosing the ask buys the per-folder question and says how to undo it +ok 604 - choosing the ask buys the per-folder question and says how to undo it + --- + duration_ms: 1.588865 + type: 'test' + ... +# Subtest: the answer is recorded even when it matches the default, so status can read it back +ok 605 - the answer is recorded even when it matches the default, so status can read it back + --- + duration_ms: 1.906428 + type: 'test' + ... +# Subtest: a re-run defaults to the standing answer instead of resetting it +ok 606 - a re-run defaults to the standing answer instead of resetting it + --- + duration_ms: 20.708404 + type: 'test' + ... +# Subtest: cancel and back leave the standing answer untouched +ok 607 - cancel and back leave the standing answer untouched + --- + duration_ms: 3.673884 + type: 'test' + ... +# Subtest: autoAccept states the question and records the default without prompting (LLP 0201) +ok 608 - autoAccept states the question and records the default without prompting (LLP 0201) + --- + duration_ms: 1.945527 + type: 'test' + ... +# Subtest: an unwritable preference warns and leaves the previous mode standing +ok 609 - an unwritable preference warns and leaves the previous mode standing + --- + duration_ms: 3.148916 + type: 'test' + ... +# Subtest: the two options are exactly sync and ask +ok 610 - the two options are exactly sync and ask + --- + duration_ms: 0.23005 + type: 'test' + ... +# Subtest: buildForkOptions: team, local, quit, in that order +ok 611 - buildForkOptions: team, local, quit, in that order + --- + duration_ms: 1.312165 + type: 'test' + ... +# Subtest: runWizardFork: a bare enter takes the default (quit) +ok 612 - runWizardFork: a bare enter takes the default (quit) + --- + duration_ms: 3.985938 + type: 'test' + ... +# Subtest: runWizardFork: choosing 1 forks to the team pathway +ok 613 - runWizardFork: choosing 1 forks to the team pathway + --- + duration_ms: 1.182708 + type: 'test' + ... +# Subtest: runWizardFork: choosing 2 forks to the local pathway +ok 614 - runWizardFork: choosing 2 forks to the local pathway + --- + duration_ms: 0.721306 + type: 'test' + ... +# Subtest: runWizardFork: an out-of-range answer quits rather than guessing +ok 615 - runWizardFork: an out-of-range answer quits rather than guessing + --- + duration_ms: 0.55321 + type: 'test' + ... +# Subtest: legacyForkPrompt: matches runWizardFork on the same input (direct call, no TUI routing) +ok 616 - legacyForkPrompt: matches runWizardFork on the same input (direct call, no TUI routing) + --- + duration_ms: 0.505789 + type: 'test' + ... +# Subtest: buildReturningGateOptions: one Reconfigure, the same three rows for every machine +ok 617 - buildReturningGateOptions: one Reconfigure, the same three rows for every machine + --- + duration_ms: 0.147834 + type: 'test' + ... +# Subtest: evaluateReturningGate: no config yet is the first-run path, not the gate +ok 618 - evaluateReturningGate: no config yet is the first-run path, not the gate + --- + duration_ms: 0.266445 + type: 'test' + ... +# Subtest: evaluateReturningGate: an invalid config is also first-run +ok 619 - evaluateReturningGate: an invalid config is also first-run + --- + duration_ms: 0.376472 + type: 'test' + ... +# Subtest: evaluateReturningGate: a managed machine with an invalid config is still managed on the first-run path +ok 620 - evaluateReturningGate: a managed machine with an invalid config is still managed on the first-run path + --- + duration_ms: 0.477987 + type: 'test' + ... +# Subtest: evaluateReturningGate: a managed machine with no config at all is still managed +ok 621 - evaluateReturningGate: a managed machine with no config at all is still managed + --- + duration_ms: 0.226104 + type: 'test' + ... +# Subtest: evaluateReturningGate: managed machine, Reconfigure is the same row it is on a solo machine +ok 622 - evaluateReturningGate: managed machine, Reconfigure is the same row it is on a solo machine + --- + duration_ms: 0.780135 + type: 'test' + ... +# Subtest: evaluateReturningGate: managed machine, a bare enter still quits (never reconfigures by accident) +ok 623 - evaluateReturningGate: managed machine, a bare enter still quits (never reconfigures by accident) + --- + duration_ms: 0.456083 + type: 'test' + ... +# Subtest: evaluateReturningGate: solo machine, Reconfigure re-enters the full fork +ok 624 - evaluateReturningGate: solo machine, Reconfigure re-enters the full fork + --- + duration_ms: 0.496574 + type: 'test' + ... +# Subtest: evaluateReturningGate: solo machine, a bare enter takes the default (quit) +ok 625 - evaluateReturningGate: solo machine, a bare enter takes the default (quit) + --- + duration_ms: 0.667564 + type: 'test' + ... +# Subtest: evaluateReturningGate: either machine kind can still choose status +ok 626 - evaluateReturningGate: either machine kind can still choose status + --- + duration_ms: 0.871414 + type: 'test' + ... +# Subtest: legacyReturningGatePrompt: default title is the plain "what would you like to do" prompt +ok 627 - legacyReturningGatePrompt: default title is the plain "what would you like to do" prompt + --- + duration_ms: 0.333226 + type: 'test' + ... +# Subtest: runInitWizard: gate quit exits 0 without running any phase +ok 628 - runInitWizard: gate quit exits 0 without running any phase + --- + duration_ms: 25.313254 + type: 'test' + ... +# Subtest: runInitWizard: gate status delegates to runStatus and returns its code +ok 629 - runInitWizard: gate status delegates to runStatus and returns its code + --- + duration_ms: 9.71789 + type: 'test' + ... +# Subtest: runInitWizard: a managed machine reconfigures through the fork, carrying managed into the picker +ok 630 - runInitWizard: a managed machine reconfigures through the fork, carrying managed into the picker + --- + duration_ms: 25.860736 + type: 'test' + ... +# Subtest: runInitWizard: a managed machine on the local pathway keeps the 90-day default, not the local 120 +ok 631 - runInitWizard: a managed machine on the local pathway keeps the 90-day default, not the local 120 + --- + duration_ms: 11.736027 + type: 'test' + ... +# Subtest: runInitWizard: managed + local + stay connected keeps the locked rows and the sync lane +ok 632 - runInitWizard: managed + local + stay connected keeps the locked rows and the sync lane + --- + duration_ms: 7.938365 + type: 'test' + ... +# Subtest: runInitWizard: managed + local + disconnect runs hyp leave and continues as a solo install +ok 633 - runInitWizard: managed + local + disconnect runs hyp leave and continues as a solo install + --- + duration_ms: 1.205673 + type: 'test' + ... +# Subtest: runInitWizard: a failed hyp leave returns to the fork still connected +ok 634 - runInitWizard: a failed hyp leave returns to the fork still connected + --- + duration_ms: 0.956313 + type: 'test' + ... +# Subtest: runInitWizard: cancelling the disconnect question ends the run without disconnecting +ok 635 - runInitWizard: cancelling the disconnect question ends the run without disconnecting + --- + duration_ms: 1.049005 + type: 'test' + ... +# Subtest: runInitWizard: an unmanaged machine choosing local is never asked about disconnecting +ok 636 - runInitWizard: an unmanaged machine choosing local is never asked about disconnecting + --- + duration_ms: 1.577057 + type: 'test' + ... +# Subtest: runInitWizard: a managed machine can still re-join a team from the gate +ok 637 - runInitWizard: a managed machine can still re-join a team from the gate + --- + duration_ms: 14.273174 + type: 'test' + ... +# Subtest: runInitWizard: a managed first run locks the org rows from the on-disk central layer +ok 638 - runInitWizard: a managed first run locks the org rows from the on-disk central layer + --- + duration_ms: 7.747025 + type: 'test' + ... +# Subtest: runInitWizard: accepting the express gate auto-accepts every lane and states no positions +ok 639 - runInitWizard: accepting the express gate auto-accepts every lane and states no positions + --- + duration_ms: 15.352384 + type: 'test' + ... +# Subtest: runInitWizard: with nothing detected and nothing locked, no express gate is shown +ok 640 - runInitWizard: with nothing detected and nothing locked, no express gate is shown + --- + duration_ms: 4.78956 + type: 'test' + ... +# Subtest: runInitWizard: declining the express gate leaves the lanes prompting, positions and all +ok 641 - runInitWizard: declining the express gate leaves the lanes prompting, positions and all + --- + duration_ms: 9.148585 + type: 'test' + ... +# Subtest: runInitWizard: a cancelled express gate exits 130 before any lane runs +ok 642 - runInitWizard: a cancelled express gate exits 130 before any lane runs + --- + duration_ms: 1.86831 + type: 'test' + ... +# Subtest: runInitWizard: back at the express gate re-presents the fork +ok 643 - runInitWizard: back at the express gate re-presents the fork + --- + duration_ms: 5.851594 + type: 'test' + ... +# Subtest: runInitWizard: the unenrolled local pathway shows no express gate; the pick gate is the one question +ok 644 - runInitWizard: the unenrolled local pathway shows no express gate; the pick gate is the one question + --- + duration_ms: 0.938627 + type: 'test' + ... +# Subtest: runInitWizard: a managed machine reconfiguring down the local pathway still gets the express gate +ok 645 - runInitWizard: a managed machine reconfiguring down the local pathway still gets the express gate + --- + duration_ms: 1.271392 + type: 'test' + ... +# Subtest: runInitWizard: the team pathway runs the sync-scope and new-folder steps between pick and configure +ok 646 - runInitWizard: the team pathway runs the sync-scope and new-folder steps between pick and configure + --- + duration_ms: 12.762257 + type: 'test' + ... +# Subtest: runInitWizard: the sync-scope step receives the locked descriptors so it can state the whole sync picture +ok 647 - runInitWizard: the sync-scope step receives the locked descriptors so it can state the whole sync picture + --- + duration_ms: 18.226623 + type: 'test' + ... +# Subtest: runInitWizard: a managed machine on the local pathway also runs the sync-scope step +ok 648 - runInitWizard: a managed machine on the local pathway also runs the sync-scope step + --- + duration_ms: 12.169977 + type: 'test' + ... +# Subtest: runInitWizard: an unmanaged local run never sees the sync-scope step +ok 649 - runInitWizard: an unmanaged local run never sees the sync-scope step + --- + duration_ms: 0.758843 + type: 'test' + ... +# Subtest: runInitWizard: non-interactive picks skip the sync-scope step (default-sync is the scripted outcome) +ok 650 - runInitWizard: non-interactive picks skip the sync-scope step (default-sync is the scripted outcome) + --- + duration_ms: 0.686995 + type: 'test' + ... +# Subtest: runInitWizard: a cancelled sync-scope step exits 130 and runs nothing further +ok 651 - runInitWizard: a cancelled sync-scope step exits 130 and runs nothing further + --- + duration_ms: 5.115004 + type: 'test' + ... +# Subtest: runInitWizard: local pathway runs pick -> configure -> finale, no join +ok 652 - runInitWizard: local pathway runs pick -> configure -> finale, no join + --- + duration_ms: 0.757401 + type: 'test' + ... +# Subtest: runInitWizard: fork quit exits 0 before the pick phase +ok 653 - runInitWizard: fork quit exits 0 before the pick phase + --- + duration_ms: 0.691641 + type: 'test' + ... +# Subtest: runInitWizard: team pathway threads locked + managed into the pick phase +ok 654 - runInitWizard: team pathway threads locked + managed into the pick phase + --- + duration_ms: 10.363281 + type: 'test' + ... +# Subtest: runInitWizard: a failed join explains and returns to the fork +ok 655 - runInitWizard: a failed join explains and returns to the fork + --- + duration_ms: 1.028042 + type: 'test' + ... +# Subtest: runInitWizard: a multi-org join failure points at hyp remote login --org +ok 656 - runInitWizard: a multi-org join failure points at hyp remote login --org + --- + duration_ms: 0.712022 + type: 'test' + ... +# Subtest: runInitWizard: an abandoned join is retriable and re-presents the fork +ok 657 - runInitWizard: an abandoned join is retriable and re-presents the fork + --- + duration_ms: 9.987309 + type: 'test' + ... +# Subtest: runInitWizard: pre-baked picks skip gate, fork, and join entirely +ok 658 - runInitWizard: pre-baked picks skip gate, fork, and join entirely + --- + duration_ms: 0.55268 + type: 'test' + ... +# Subtest: runInitWizard: a cancelled pick returns 130 and runs nothing further +ok 659 - runInitWizard: a cancelled pick returns 130 and runs nothing further + --- + duration_ms: 0.477706 + type: 'test' + ... +# Subtest: runInitWizard: an overwrite refusal returns the pick phase exit 1 +ok 660 - runInitWizard: an overwrite refusal returns the pick phase exit 1 + --- + duration_ms: 0.438677 + type: 'test' + ... +# Subtest: runInitWizard: a pending config lands on disk after the sync lane, before configure +ok 661 - runInitWizard: a pending config lands on disk after the sync lane, before configure + --- + duration_ms: 16.380066 + type: 'test' + ... +# Subtest: runInitWizard: a declined commit exits 1, runs nothing further, and narrates on the team pathway +ok 662 - runInitWizard: a declined commit exits 1, runs nothing further, and narrates on the team pathway + --- + duration_ms: 8.506549 + type: 'test' + ... +# Subtest: runInitWizard: a scripted pick result without configPending is never committed by the orchestrator +ok 663 - runInitWizard: a scripted pick result without configPending is never committed by the orchestrator + --- + duration_ms: 0.850763 + type: 'test' + ... +# Subtest: runInitWizard: a team-path overwrite refusal narrates the enrolled state and the deadline +ok 664 - runInitWizard: a team-path overwrite refusal narrates the enrolled state and the deadline + --- + duration_ms: 18.495992 + type: 'test' + ... +# Subtest: runInitWizard: a team-path pick cancel narrates the enrolled state; no hold means no deadline claim +ok 665 - runInitWizard: a team-path pick cancel narrates the enrolled state; no hold means no deadline claim + --- + duration_ms: 5.226393 + type: 'test' + ... +# Subtest: runInitWizard: a team-path sync-scope cancel narrates that default-sync stands +ok 666 - runInitWizard: a team-path sync-scope cancel narrates that default-sync stands + --- + duration_ms: 9.155336 + type: 'test' + ... +# Subtest: runInitWizard: a local-path abort stays quiet - nothing enrolled this run +ok 667 - runInitWizard: a local-path abort stays quiet - nothing enrolled this run + --- + duration_ms: 2.341549 + type: 'test' + ... +# Subtest: runInitWizard: a cancelled finale returns 130 with the cancel notice +ok 668 - runInitWizard: a cancelled finale returns 130 with the cancel notice + --- + duration_ms: 1.163919 + type: 'test' + ... +# Subtest: runInitWizard: prints the run summary with the written config path +ok 669 - runInitWizard: prints the run summary with the written config path + --- + duration_ms: 0.672542 + type: 'test' + ... +# Subtest: runInitWizard: an attended run ends on the first look, before the privacy narration +ok 670 - runInitWizard: an attended run ends on the first look, before the privacy narration + --- + duration_ms: 21.219361 + type: 'test' + ... +# Subtest: runInitWizard: a non-interactive or dry run skips the first look +ok 671 - runInitWizard: a non-interactive or dry run skips the first look + --- + duration_ms: 1.044257 + type: 'test' + ... +# Subtest: runInitWizard: the first ask comes last, after the privacy narration +ok 672 - runInitWizard: the first ask comes last, after the privacy narration + --- + duration_ms: 30.620741 + type: 'test' + ... +# Subtest: runInitWizard: an enrolled run is offered the first sync, after the narration and before the first ask +ok 673 - runInitWizard: an enrolled run is offered the first sync, after the narration and before the first ask + --- + duration_ms: 12.392606 + type: 'test' + ... +# Subtest: runInitWizard: a local install with no hold is never offered a sync +ok 674 - runInitWizard: a local install with no hold is never offered a sync + --- + duration_ms: 0.787728 + type: 'test' + ... +# Subtest: runInitWizard: a first look with no rows suppresses the launch +ok 675 - runInitWizard: a first look with no rows suppresses the launch + --- + duration_ms: 0.775839 + type: 'test' + ... +# Subtest: runInitWizard: a first look with no gateway dataset suppresses the launch too +ok 676 - runInitWizard: a first look with no gateway dataset suppresses the launch too + --- + duration_ms: 0.625541 + type: 'test' + ... +# Subtest: firstLookHadRows: a slow first look still reports hasRows true, so the launch is not suppressed +ok 677 - firstLookHadRows: a slow first look still reports hasRows true, so the launch is not suppressed + --- + duration_ms: 0.084028 + type: 'test' + ... +# Subtest: firstLookHadRows: an absent or errored first look reports hasRows undefined, not false, so the offer is never withheld +ok 678 - firstLookHadRows: an absent or errored first look reports hasRows undefined, not false, so the offer is never withheld + --- + duration_ms: 0.060452 + type: 'test' + ... +# Subtest: runInitWizard: a launched client does not change the wizard exit code +ok 679 - runInitWizard: a launched client does not change the wizard exit code + --- + duration_ms: 1.434961 + type: 'test' + ... +# Subtest: runInitWizard: a non-interactive or dry run never launches anything +ok 680 - runInitWizard: a non-interactive or dry run never launches anything + --- + duration_ms: 1.272724 + type: 'test' + ... +# Subtest: runInitWizard: team pathway with a live first-sync hold narrates the deadline +ok 681 - runInitWizard: team pathway with a live first-sync hold narrates the deadline + --- + duration_ms: 10.297671 + type: 'test' + ... +# Subtest: runInitWizard: local pathway never narrates the first-sync hold +ok 682 - runInitWizard: local pathway never narrates the first-sync hold + --- + duration_ms: 1.46727 + type: 'test' + ... +# Subtest: classifyLoginFailure: no_membership is a definitive rejection -> failed +ok 683 - classifyLoginFailure: no_membership is a definitive rejection -> failed + --- + duration_ms: 0.929984 + type: 'test' + ... +# Subtest: classifyLoginFailure: org_not_permitted is a definitive rejection -> failed +ok 684 - classifyLoginFailure: org_not_permitted is a definitive rejection -> failed + --- + duration_ms: 0.122737 + type: 'test' + ... +# Subtest: classifyLoginFailure: org_selection_required (multi-org account) -> failed +ok 685 - classifyLoginFailure: org_selection_required (multi-org account) -> failed + --- + duration_ms: 0.116698 + type: 'test' + ... +# Subtest: classifyLoginFailure: retriable reasons -> abandoned +ok 686 - classifyLoginFailure: retriable reasons -> abandoned + --- + duration_ms: 0.136187 + type: 'test' + ... +# Subtest: classifyLoginFailure: the message text is not consulted +ok 687 - classifyLoginFailure: the message text is not consulted + --- + duration_ms: 0.188667 + type: 'test' + ... +# Subtest: classifyLoginFailure: a missing reason defaults to abandoned +ok 688 - classifyLoginFailure: a missing reason defaults to abandoned + --- + duration_ms: 0.113412 + type: 'test' + ... +# Subtest: runWizardJoin: a non-zero login exit returns the classified failure and never waits +ok 689 - runWizardJoin: a non-zero login exit returns the classified failure and never waits + --- + duration_ms: 2.079942 + type: 'test' + ... +# Subtest: runWizardJoin: a transient login failure returns abandoned +ok 690 - runWizardJoin: a transient login failure returns abandoned + --- + duration_ms: 0.343532 + type: 'test' + ... +# Subtest: runWizardJoin: on convergence, locks exactly the central-layer picker rows +ok 691 - runWizardJoin: on convergence, locks exactly the central-layer picker rows + --- + duration_ms: 0.877684 + type: 'test' + ... +# Subtest: runWizardJoin: convergence with no central-owned rows locks nothing +ok 692 - runWizardJoin: convergence with no central-owned rows locks nothing + --- + duration_ms: 0.488863 + type: 'test' + ... +# Subtest: runWizardJoin: a convergence timeout narrates and returns an empty lock set +ok 693 - runWizardJoin: a convergence timeout narrates and returns an empty lock set + --- + duration_ms: 0.346136 + type: 'test' + ... +# Subtest: runWizardJoin: passes the org-config wait budget through to the converge helper +ok 694 - runWizardJoin: passes the org-config wait budget through to the converge helper + --- + duration_ms: 0.40763 + type: 'test' + ... +# Subtest: runWizardPick: pre-baked picks skip prompting and compose the same config +ok 695 - runWizardPick: pre-baked picks skip prompting and compose the same config + --- + duration_ms: 29.174763 + type: 'test' + ... +# Subtest: runWizardPick: non-interactive path does not run detection +ok 696 - runWizardPick: non-interactive path does not run detection + --- + duration_ms: 13.156767 + type: 'test' + ... +# Subtest: runWizardPick: interactive prompt options pre-check detected sources +ok 697 - runWizardPick: interactive prompt options pre-check detected sources + --- + duration_ms: 14.822428 + type: 'test' + ... +# Subtest: runWizardPick: a detected needs_setup row arrives unchecked, labeled detected +ok 698 - runWizardPick: a detected needs_setup row arrives unchecked, labeled detected + --- + duration_ms: 8.691981 + type: 'test' + ... +# Subtest: runWizardPick: the defaults gate omits a detected needs_setup row, and accept does not pick it +ok 699 - runWizardPick: the defaults gate omits a detected needs_setup row, and accept does not pick it + --- + duration_ms: 5.608194 + type: 'test' + ... +# Subtest: runWizardPick: a reconfigure reports carried picks in previouslyConfigured +ok 700 - runWizardPick: a reconfigure reports carried picks in previouslyConfigured + --- + duration_ms: 9.453188 + type: 'test' + ... +# Subtest: runWizardPick: a fresh pick reports nothing as previously configured +ok 701 - runWizardPick: a fresh pick reports nothing as previously configured + --- + duration_ms: 9.298173 + type: 'test' + ... +# Subtest: runWizardPick: a seeded needs_setup row on the gate carries the needs-extra-setup suffix +ok 702 - runWizardPick: a seeded needs_setup row on the gate carries the needs-extra-setup suffix + --- + duration_ms: 4.76322 + type: 'test' + ... +# Subtest: runWizardPick: accepting the defaults gate picks exactly the detected sources, no menu +ok 703 - runWizardPick: accepting the defaults gate picks exactly the detected sources, no menu + --- + duration_ms: 11.905635 + type: 'test' + ... +# Subtest: runWizardPick: the gate names locked sources as fleet-managed and accept keeps them +ok 704 - runWizardPick: the gate names locked sources as fleet-managed and accept keeps them + --- + duration_ms: 6.400788 + type: 'test' + ... +# Subtest: runWizardPick: autoAccept takes the gate rows and prints what the gate would have said +ok 705 - runWizardPick: autoAccept takes the gate rows and prints what the gate would have said + --- + duration_ms: 2.888941 + type: 'test' + ... +# Subtest: runWizardPick: autoAccept with no default to take still opens the menu +ok 706 - runWizardPick: autoAccept with no default to take still opens the menu + --- + duration_ms: 3.932717 + type: 'test' + ... +# Subtest: runWizardPick: no gate when nothing is detected and nothing is locked +ok 707 - runWizardPick: no gate when nothing is detected and nothing is locked + --- + duration_ms: 7.208035 + type: 'test' + ... +# Subtest: runWizardPick: menu back with allowBack and no gate propagates to the caller, never loops +ok 708 - runWizardPick: menu back with allowBack and no gate propagates to the caller, never loops + --- + duration_ms: 3.807457 + type: 'test' + ... +# Subtest: runWizardPick: a cancelled gate returns the deterministic cancel result +ok 709 - runWizardPick: a cancelled gate returns the deterministic cancel result + --- + duration_ms: 3.400729 + type: 'test' + ... +# Subtest: runWizardPick: interactive runs take the 90-day default without a retention prompt +ok 710 - runWizardPick: interactive runs take the 90-day default without a retention prompt + --- + duration_ms: 8.917945 + type: 'test' + ... +# Subtest: runWizardPick: retentionDefault (the local pathway) lands in the composed config +ok 711 - runWizardPick: retentionDefault (the local pathway) lands in the composed config + --- + duration_ms: 4.925036 + type: 'test' + ... +# Subtest: runWizardPick: pre-baked picks override retentionDefault +ok 712 - runWizardPick: pre-baked picks override retentionDefault + --- + duration_ms: 3.863012 + type: 'test' + ... +# Subtest: runWizardPick: options come from catalog.pickerDescriptors, not a hardcoded table +ok 713 - runWizardPick: options come from catalog.pickerDescriptors, not a hardcoded table + --- + duration_ms: 7.685401 + type: 'test' + ... +# Subtest: runWizardPick: a locked row renders checked, disabled, and fleet-labeled +ok 714 - runWizardPick: a locked row renders checked, disabled, and fleet-labeled + --- + duration_ms: 4.274387 + type: 'test' + ... +# Subtest: runWizardPick: a locked source is filtered out of the returned picks and composition +ok 715 - runWizardPick: a locked source is filtered out of the returned picks and composition + --- + duration_ms: 6.038197 + type: 'test' + ... +# Subtest: runWizardPick: an unknown locked id is ignored, not surfaced as a row +ok 716 - runWizardPick: an unknown locked id is ignored, not surfaced as a row + --- + duration_ms: 4.297472 + type: 'test' + ... +# Subtest: runWizardPick: a fully fleet-managed machine still reports its locked clients as picked so the finale installs skills/agents +ok 717 - runWizardPick: a fully fleet-managed machine still reports its locked clients as picked so the finale installs skills/agents + --- + duration_ms: 3.122836 + type: 'test' + ... +# Subtest: runWizardPick: a managed machine no longer labels non-locked rows "stays on this machine" +ok 718 - runWizardPick: a managed machine no longer labels non-locked rows "stays on this machine" + --- + duration_ms: 3.102015 + type: 'test' + ... +# Subtest: runWizardPick: an unmanaged (solo) machine never shows a local-only suffix either +ok 719 - runWizardPick: an unmanaged (solo) machine never shows a local-only suffix either + --- + duration_ms: 5.418235 + type: 'test' + ... +# Subtest: runWizardPick: refuses to clobber an existing config without --force (exit 1, not cancelled) +ok 720 - runWizardPick: refuses to clobber an existing config without --force (exit 1, not cancelled) + --- + duration_ms: 5.51998 + type: 'test' + ... +# Subtest: runWizardPick: --force overwrites an existing config after backing it up +ok 721 - runWizardPick: --force overwrites an existing config after backing it up + --- + duration_ms: 10.173913 + type: 'test' + ... +# Subtest: runWizardPick: deferWrite composes but never writes, guards, or prompts to overwrite +ok 722 - runWizardPick: deferWrite composes but never writes, guards, or prompts to overwrite + --- + duration_ms: 3.104789 + type: 'test' + ... +# Subtest: commitWizardPickedConfig: writes the config, backing up an existing one first +ok 723 - commitWizardPickedConfig: writes the config, backing up an existing one first + --- + duration_ms: 1.208577 + type: 'test' + ... +# Subtest: commitWizardPickedConfig: a declined overwrite refuses without touching the config +ok 724 - commitWizardPickedConfig: a declined overwrite refuses without touching the config + --- + duration_ms: 0.73724 + type: 'test' + ... +# Subtest: runWizardPick: a cancelled prompt returns the deterministic cancel result +ok 725 - runWizardPick: a cancelled prompt returns the deterministic cancel result + --- + duration_ms: 3.167203 + type: 'test' + ... +# Subtest: runWizardPick: a picked openclaw reaches clientsPicked; a clientless pick does not +ok 726 - runWizardPick: a picked openclaw reaches clientsPicked; a clientless pick does not + --- + duration_ms: 4.141575 + type: 'test' + ... +# Subtest: derivePickedClients: the derived set over every bundled picker row is pinned +ok 727 - derivePickedClients: the derived set over every bundled picker row is pinned + --- + duration_ms: 6.635115 + type: 'test' + ... +# Subtest: runWizardPick: a reconfigure pre-checks the undetectable otel row it already collects +ok 728 - runWizardPick: a reconfigure pre-checks the undetectable otel row it already collects + --- + duration_ms: 8.433839 + type: 'test' + ... +# Subtest: runWizardPick: a reconfigure leaves a deliberately excluded client unchecked even when it is detected +ok 729 - runWizardPick: a reconfigure leaves a deliberately excluded client unchecked even when it is detected + --- + duration_ms: 3.90188 + type: 'test' + ... +# Subtest: runWizardPick: a 120-day retention survives a team-path reconfigure +ok 730 - runWizardPick: a 120-day retention survives a team-path reconfigure + --- + duration_ms: 6.721577 + type: 'test' + ... +# Subtest: runWizardPick: a first run still seeds from detection and takes the pathway retention default +ok 731 - runWizardPick: a first run still seeds from detection and takes the pathway retention default + --- + duration_ms: 5.512098 + type: 'test' + ... +# Subtest: runWizardPick: a reconfigure carries forward plugins and sink edits the picker does not own +ok 732 - runWizardPick: a reconfigure carries forward plugins and sink edits the picker does not own + --- + duration_ms: 14.519759 + type: 'test' + ... +# Subtest: runWizardPick: a reconfigure of a cache-only install does not silently add an export sink +ok 733 - runWizardPick: a reconfigure of a cache-only install does not silently add an export sink + --- + duration_ms: 4.407038 + type: 'test' + ... +# Subtest: runWizardPick: unchecking a row still removes its plugin and its gateway upstream +ok 734 - runWizardPick: unchecking a row still removes its plugin and its gateway upstream + --- + duration_ms: 4.789009 + type: 'test' + ... +# Subtest: runWizardPick: a disabled plugin reads as an off row, and re-picking it turns it back on +ok 735 - runWizardPick: a disabled plugin reads as an off row, and re-picking it turns it back on + --- + duration_ms: 7.374779 + type: 'test' + ... +# Subtest: runWizardPick: a reconfigure does not add a second export sink beside a renamed one +ok 736 - runWizardPick: a reconfigure does not add a second export sink beside a renamed one + --- + duration_ms: 5.465958 + type: 'test' + ... +# Subtest: runWizardPick: a request sink parked on the composer sink id is not folded into a mixed shape +ok 737 - runWizardPick: a request sink parked on the composer sink id is not folded into a mixed shape + --- + duration_ms: 5.878805 + type: 'test' + ... +# Subtest: runWizardPick: a differently written blob sink parked on the composer sink id is not rewritten +ok 738 - runWizardPick: a differently written blob sink parked on the composer sink id is not rewritten + --- + duration_ms: 13.468781 + type: 'test' + ... +# Subtest: defaultOverwriteConfirmFactory: the prompt says the config is regenerated from the picks +ok 739 - defaultOverwriteConfirmFactory: the prompt says the config is regenerated from the picks + --- + duration_ms: 1.764191 + type: 'test' + ... +# Subtest: defaultOverwriteConfirmFactory: bare enter proceeds, an explicit no declines +ok 740 - defaultOverwriteConfirmFactory: bare enter proceeds, an explicit no declines + --- + duration_ms: 0.646683 + type: 'test' + ... +# Subtest: runWizardPick: a hidden row is absent from the defaults gate as well as the menu +ok 741 - runWizardPick: a hidden row is absent from the defaults gate as well as the menu + --- + duration_ms: 7.284151 + type: 'test' + ... +# Subtest: runWizardPick: a raw-only config survives a reconfigure that picks nothing new +ok 742 - runWizardPick: a raw-only config survives a reconfigure that picks nothing new + --- + duration_ms: 6.95476 + type: 'test' + ... +# Subtest: runWizardPick: the express path carries a raw-only config and never states the hidden row +ok 743 - runWizardPick: the express path carries a raw-only config and never states the hidden row + --- + duration_ms: 13.590045 + type: 'test' + ... +# Subtest: runWizardPick: a hidden row seeded only derivatively does not resurrect an unchecked upstream +ok 744 - runWizardPick: a hidden row seeded only derivatively does not resurrect an unchecked upstream + --- + duration_ms: 8.745212 + type: 'test' + ... +# Subtest: runWizardPick: --source still composes a hidden row (no prompt involved) +ok 745 - runWizardPick: --source still composes a hidden row (no prompt involved) + --- + duration_ms: 7.424515 + type: 'test' + ... +# Subtest: runWizardPick: a hidden row that is merely detected is not carried on a first run +ok 746 - runWizardPick: a hidden row that is merely detected is not carried on a first run + --- + duration_ms: 6.807216 + type: 'test' + ... +# Subtest: runWizardPick: a carried hidden row survives a re-entry that adds a visible row +ok 747 - runWizardPick: a carried hidden row survives a re-entry that adds a visible row + --- + duration_ms: 8.454209 + type: 'test' + ... +# Subtest: wizardStepProgress: the team pathway counts join, pick, sync, folders and finale +ok 748 - wizardStepProgress: the team pathway counts join, pick, sync, folders and finale + --- + duration_ms: 1.560201 + type: 'test' + ... +# Subtest: wizardStepProgress: the local pathway counts two steps +ok 749 - wizardStepProgress: the local pathway counts two steps + --- + duration_ms: 0.165982 + type: 'test' + ... +# Subtest: wizardStepProgress: a managed machine on the local pathway gains both enrolled lanes (LLP 0188, LLP 0200) +ok 750 - wizardStepProgress: a managed machine on the local pathway gains both enrolled lanes (LLP 0188, LLP 0200) + --- + duration_ms: 0.236589 + type: 'test' + ... +# Subtest: wizardStepProgress: an uncommitted pathway has no denominator +ok 751 - wizardStepProgress: an uncommitted pathway has no denominator + --- + duration_ms: 0.133352 + type: 'test' + ... +# Subtest: runInitWizard: the local pathway reads step 1 of 2 then step 2 of 2 +ok 752 - runInitWizard: the local pathway reads step 1 of 2 then step 2 of 2 + --- + duration_ms: 10.043846 + type: 'test' + ... +# Subtest: runInitWizard: the team pathway reads step 1/2/3/4/5 across join, pick, sync, folders and finale +ok 753 - runInitWizard: the team pathway reads step 1/2/3/4/5 across join, pick, sync, folders and finale + --- + duration_ms: 40.529801 + type: 'test' + ... +# Subtest: runInitWizard: the fork never carries a counter, before or after a failed join +ok 754 - runInitWizard: the fork never carries a counter, before or after a failed join + --- + duration_ms: 0.983925 + type: 'test' + ... +# Subtest: runInitWizard: a managed re-entry counts the pathway the fork returns, plus both enrolled lanes +ok 755 - runInitWizard: a managed re-entry counts the pathway the fork returns, plus both enrolled lanes + --- + duration_ms: 8.551878 + type: 'test' + ... +# Subtest: runInitWizard: a non-interactive run carries no breadcrumb anywhere +ok 756 - runInitWizard: a non-interactive run carries no breadcrumb anywhere + --- + duration_ms: 0.886818 + type: 'test' + ... +# Subtest: runWizardJoin: prints its position above the joining narration +ok 757 - runWizardJoin: prints its position above the joining narration + --- + duration_ms: 0.711842 + type: 'test' + ... +# Subtest: runWizardJoin: without a position it narrates exactly as it does today +ok 758 - runWizardJoin: without a position it narrates exactly as it does today + --- + duration_ms: 0.219494 + type: 'test' + ... +# Subtest: runPickerFinale: states its position once, where the lane starts +ok 759 - runPickerFinale: states its position once, where the lane starts + --- + duration_ms: 8.284281 + type: 'test' + ... +# Subtest: runPickerFinale: without a position it writes exactly what it writes today +ok 760 - runPickerFinale: without a position it writes exactly what it writes today + --- + duration_ms: 13.866786 + type: 'test' + ... +# Subtest: the legacy numbered picker prompt prints the breadcrumb as plain text +ok 761 - the legacy numbered picker prompt prints the breadcrumb as plain text + --- + duration_ms: 0.914039 + type: 'test' + ... +# Subtest: render: the breadcrumb is its own line above the title, never folded into it +ok 762 - render: the breadcrumb is its own line above the title, never folded into it + --- + duration_ms: 0.222819 + type: 'test' + ... +# Subtest: render: a spec without a breadcrumb renders exactly as it does today +ok 763 - render: a spec without a breadcrumb renders exactly as it does today + --- + duration_ms: 0.110959 + type: 'test' + ... +# Subtest: no hold means no question: an unenrolled install is never asked to sync +ok 764 - no hold means no question: an unenrolled install is never asked to sync + --- + duration_ms: 2.283811 + type: 'test' + ... +# Subtest: a non-interactive run is never asked, and never sends +ok 765 - a non-interactive run is never asked, and never sends + --- + duration_ms: 0.374509 + type: 'test' + ... +# Subtest: the question offers wait first, as the default, and declining spawns nothing +ok 766 - the question offers wait first, as the default, and declining spawns nothing + --- + duration_ms: 16.015442 + type: 'test' + ... +# Subtest: send now spawns `hyp sync` on the inherited terminal and reports the release +ok 767 - send now spawns `hyp sync` on the inherited terminal and reports the release + --- + duration_ms: 1.031578 + type: 'test' + ... +# Subtest: a child that exits 0 without releasing is reported as not sent +ok 768 - a child that exits 0 without releasing is reported as not sent + --- + duration_ms: 0.814298 + type: 'test' + ... +# Subtest: a re-read that throws is reported as not sent, not as a release +ok 769 - a re-read that throws is reported as not sent, not as a release + --- + duration_ms: 0.727506 + type: 'test' + ... +# Subtest: a spawn failure never fails the install, and restates the wait +ok 770 - a spawn failure never fails the install, and restates the wait + --- + duration_ms: 0.605671 + type: 'test' + ... +# Subtest: the read-back resolves a real hold marker from the environment: left in place +ok 771 - the read-back resolves a real hold marker from the environment: left in place + --- + duration_ms: 11.700935 + type: 'test' + ... +# Subtest: the read-back resolves a real hold marker from the environment: cleared by the child +ok 772 - the read-back resolves a real hold marker from the environment: cleared by the child + --- + duration_ms: 19.452456 + type: 'test' + ... +# Subtest: a cancelled prompt is a wait, not an error +ok 773 - a cancelled prompt is a wait, not an error + --- + duration_ms: 1.097428 + type: 'test' + ... +# Subtest: a fresh enrolled run: the gate states what will sync and accepting opts nothing out +ok 774 - a fresh enrolled run: the gate states what will sync and accepting opts nothing out + --- + duration_ms: 13.375249 + type: 'test' + ... +# Subtest: the menu checks what syncs: everything checked by default on a fresh run +ok 775 - the menu checks what syncs: everything checked by default on a fresh run + --- + duration_ms: 12.693042 + type: 'test' + ... +# Subtest: non-TTY: opening the menu and pressing enter keeps the defaults, not opt-everything-out +ok 776 - non-TTY: opening the menu and pressing enter keeps the defaults, not opt-everything-out + --- + duration_ms: 3.459598 + type: 'test' + ... +# Subtest: non-TTY: a bare enter at the menu round-trips a standing opt-out instead of resetting it +ok 777 - non-TTY: a bare enter at the menu round-trips a standing opt-out instead of resetting it + --- + duration_ms: 5.670579 + type: 'test' + ... +# Subtest: unchecking a source writes its opt-out and names the follow-up command +ok 778 - unchecking a source writes its opt-out and names the follow-up command + --- + duration_ms: 1.580111 + type: 'test' + ... +# Subtest: a re-entry states the split on the gate and accepting keeps it +ok 779 - a re-entry states the split on the gate and accepting keeps it + --- + duration_ms: 3.872806 + type: 'test' + ... +# Subtest: a re-entry renders existing opt-outs unchecked and re-checking removes them +ok 780 - a re-entry renders existing opt-outs unchecked and re-checking removes them + --- + duration_ms: 2.926267 + type: 'test' + ... +# Subtest: editor semantics: an entry for a source not shown this run is kept +ok 781 - editor semantics: an entry for a source not shown this run is kept + --- + duration_ms: 1.645431 + type: 'test' + ... +# Subtest: locked sources lead the gate list fleet-suffixed and the menu as read-only rows +ok 782 - locked sources lead the gate list fleet-suffixed and the menu as read-only rows + --- + duration_ms: 1.744461 + type: 'test' + ... +# Subtest: a locked source never enters the opt-out computation +ok 783 - a locked source never enters the opt-out computation + --- + duration_ms: 3.235959 + type: 'test' + ... +# Subtest: zero candidates: prints the position and the fleet line, prompts nothing, writes nothing +ok 784 - zero candidates: prints the position and the fleet line, prompts nothing, writes nothing + --- + duration_ms: 0.889212 + type: 'test' + ... +# Subtest: a cancelled gate returns cancelled and writes nothing +ok 785 - a cancelled gate returns cancelled and writes nothing + --- + duration_ms: 0.976804 + type: 'test' + ... +# Subtest: a cancelled menu returns cancelled and writes nothing +ok 786 - a cancelled menu returns cancelled and writes nothing + --- + duration_ms: 1.640203 + type: 'test' + ... +# Subtest: a corrupt store skips the step with a warning and is never overwritten +ok 787 - a corrupt store skips the step with a warning and is never overwritten + --- + duration_ms: 3.367198 + type: 'test' + ... +# Subtest: agents.register validates contribution shape +ok 788 - agents.register validates contribution shape + --- + duration_ms: 3.530456 + type: 'test' + ... +# Subtest: agents.register rejects path-traversal names +ok 789 - agents.register rejects path-traversal names + --- + duration_ms: 0.553822 + type: 'test' + ... +# Subtest: skills.register rejects path-traversal names +ok 790 - skills.register rejects path-traversal names + --- + duration_ms: 0.428961 + type: 'test' + ... +# Subtest: hyp skills install materializes skills and subagents in one command +ok 791 - hyp skills install materializes skills and subagents in one command + --- + duration_ms: 44.107199 + type: 'test' + ... +# Subtest: hyp agents install is gone: agents is not a command +ok 792 - hyp agents install is gone: agents is not a command + --- + duration_ms: 10.114273 + type: 'test' + ... +# Subtest: hyp skills install skips a client with no directory for that asset kind +ok 793 - hyp skills install skips a client with no directory for that asset kind + --- + duration_ms: 12.531907 + type: 'test' + ... +# Subtest: hyp skills install warns when a contribution names an unknown client +ok 794 - hyp skills install warns when a contribution names an unknown client + --- + duration_ms: 6.994782 + type: 'test' + ... +# Subtest: hyp skills install respects --client filtering +ok 795 - hyp skills install respects --client filtering + --- + duration_ms: 7.097127 + type: 'test' + ... +# Subtest: bundled @hypaware/claude manifest declares the hypaware-analyst agent +ok 796 - bundled @hypaware/claude manifest declares the hypaware-analyst agent + --- + duration_ms: 1.008192 + type: 'test' + ... +# Subtest: hyp skills install removes a skill the current manifests no longer declare +ok 797 - hyp skills install removes a skill the current manifests no longer declare + --- + duration_ms: 48.802355 + type: 'test' + ... +# Subtest: a retired subagent file is removed the same way a skill directory is +ok 798 - a retired subagent file is removed the same way a skill directory is + --- + duration_ms: 43.482328 + type: 'test' + ... +# Subtest: a user's own skill in the same directory is never removed +ok 799 - a user's own skill in the same directory is never removed + --- + duration_ms: 18.194855 + type: 'test' + ... +# Subtest: a retired skill the user edited is left in place and named, never deleted +ok 800 - a retired skill the user edited is left in place and named, never deleted + --- + duration_ms: 22.861086 + type: 'test' + ... +# Subtest: an asset only the attach marker records is named, never removed (pre-ledger installs) +ok 801 - an asset only the attach marker records is named, never removed (pre-ledger installs) + --- + duration_ms: 17.346034 + type: 'test' + ... +# Subtest: a skill the user authored at a path the attach marker still names is never removed +ok 802 - a skill the user authored at a path the attach marker still names is never removed + --- + duration_ms: 19.94197 + type: 'test' + ... +# Subtest: a ledger record whose digest is not a string never becomes an unconditional delete +ok 803 - a ledger record whose digest is not a string never becomes an unconditional delete + --- + duration_ms: 17.374938 + type: 'test' + ... +# Subtest: a ledger record with no digest at all is withheld and reported +ok 804 - a ledger record with no digest at all is withheld and reported + --- + duration_ms: 21.155284 + type: 'test' + ... +# Subtest: a retired asset that cannot be read is named and kept on the books +ok 805 - a retired asset that cannot be read is named and kept on the books + --- + duration_ms: 24.723007 + type: 'test' + ... +# Subtest: an ENOENT raised reading inside a retired asset, not at dest itself, is reported as unreadable +ok 806 - an ENOENT raised reading inside a retired asset, not at dest itself, is reported as unreadable + --- + duration_ms: 25.807715 + type: 'test' + ... +# Subtest: a destination another client in the same run planned is never pruned +ok 807 - a destination another client in the same run planned is never pruned + --- + duration_ms: 26.108332 + type: 'test' + ... +# Subtest: a recorded destination outside the client asset directories is refused out loud +ok 808 - a recorded destination outside the client asset directories is refused out loud + --- + duration_ms: 19.98838 + type: 'test' + ... +# Subtest: a recorded destination deeper than a direct child is refused, digest or no digest +ok 809 - a recorded destination deeper than a direct child is refused, digest or no digest + --- + duration_ms: 16.276679 + type: 'test' + ... +# Subtest: a recorded destination whose basename begins with ".." is refused, not read as a direct child +ok 810 - a recorded destination whose basename begins with ".." is refused, not read as a direct child + --- + duration_ms: 26.42298 + type: 'test' + ... +# Subtest: removeClientAssets refuses a basename beginning with ".." on posix and win32 alike +ok 811 - removeClientAssets refuses a basename beginning with ".." on posix and win32 alike + --- + duration_ms: 2.955462 + type: 'test' + ... +# Subtest: nothing is removed when this run installs nothing +ok 812 - nothing is removed when this run installs nothing + --- + duration_ms: 10.740965 + type: 'test' + ... +# Subtest: a boot where a plugin failed to activate prunes nothing +ok 813 - a boot where a plugin failed to activate prunes nothing + --- + duration_ms: 22.432605 + type: 'test' + ... +# Subtest: a skill the boot profile withheld is never read as retired +ok 814 - a skill the boot profile withheld is never read as retired + --- + duration_ms: 24.811491 + type: 'test' + ... +# Subtest: a config-enabled plugin that is no longer installed is retired, and its skill prunes +ok 815 - a config-enabled plugin that is no longer installed is retired, and its skill prunes + --- + duration_ms: 25.647632 + type: 'test' + ... +# Subtest: a directory and a file never share a content digest +ok 816 - a directory and a file never share a content digest + --- + duration_ms: 2.946028 + type: 'test' + ... +# Subtest: a user's file that collides with a retired skill's digest is left in place +ok 817 - a user's file that collides with a retired skill's digest is left in place + --- + duration_ms: 18.568482 + type: 'test' + ... +# Subtest: a user's empty file at an empty retired skill's path is left in place +ok 818 - a user's empty file at an empty retired skill's path is left in place + --- + duration_ms: 22.58064 + type: 'test' + ... +# Subtest: the wizard finale says how many retired assets it removed +ok 819 - the wizard finale says how many retired assets it removed + --- + duration_ms: 19.459587 + type: 'test' + ... +# Subtest: a record survives a dest moving to a client whose copy failed +ok 820 - a record survives a dest moving to a client whose copy failed + --- + duration_ms: 23.774666 + type: 'test' + ... +# Subtest: claude undo restores a pre-existing foreign base URL byte-for-byte +ok 821 - claude undo restores a pre-existing foreign base URL byte-for-byte + --- + duration_ms: 32.798771 + type: 'test' + ... +# Subtest: claude undo of a no-pre-existing-URL attach round-trips to empty +ok 822 - claude undo of a no-pre-existing-URL attach round-trips to empty + --- + duration_ms: 9.71102 + type: 'test' + ... +# Subtest: claude undo removes managed ENABLE_TOOL_SEARCH without stamping the restored base URL onto it +ok 823 - claude undo removes managed ENABLE_TOOL_SEARCH without stamping the restored base URL onto it + --- + duration_ms: 13.401158 + type: 'test' + ... +# Subtest: claude attach + undo leave a non-string user-owned env value byte-for-byte intact +ok 824 - claude attach + undo leave a non-string user-owned env value byte-for-byte intact + --- + duration_ms: 9.4284 + type: 'test' + ... +# Subtest: claude undo removes the managed _CLAUDE_CODE_ASSUME_FIRST_PARTY_BASE_URL +ok 825 - claude undo removes the managed _CLAUDE_CODE_ASSUME_FIRST_PARTY_BASE_URL + --- + duration_ms: 9.739553 + type: 'test' + ... +# Subtest: claude undo leaves a user-owned ENABLE_TOOL_SEARCH in place +ok 826 - claude undo leaves a user-owned ENABLE_TOOL_SEARCH in place + --- + duration_ms: 12.13877 + type: 'test' + ... +# Subtest: claude undo strips marker + managed keys/hooks from a hand-written fixture (no plugin loaded) +ok 827 - claude undo strips marker + managed keys/hooks from a hand-written fixture (no plugin loaded) + --- + duration_ms: 6.092079 + type: 'test' + ... +# Subtest: claude undo of a LEGACY pre-upgrade marker (no managed record) detaches fully +ok 828 - claude undo of a LEGACY pre-upgrade marker (no managed record) detaches fully + --- + duration_ms: 5.685622 + type: 'test' + ... +# Subtest: claude undo of a LEGACY marker preserves a user hook and an externally-overridden base URL +ok 829 - claude undo of a LEGACY marker preserves a user hook and an externally-overridden base URL + --- + duration_ms: 7.287006 + type: 'test' + ... +# Subtest: claude undo leaves an externally-overridden base URL in place with a warning +ok 830 - claude undo leaves an externally-overridden base URL in place with a warning + --- + duration_ms: 17.156075 + type: 'test' + ... +# Subtest: claude undo preserves a user-owned non-managed hook for a managed event +ok 831 - claude undo preserves a user-owned non-managed hook for a managed event + --- + duration_ms: 10.749968 + type: 'test' + ... +# Subtest: claude undo is a no-op when the marker is absent +ok 832 - claude undo is a no-op when the marker is absent + --- + duration_ms: 2.777311 + type: 'test' + ... +# Subtest: claude undo is a no-op when the settings file is absent +ok 833 - claude undo is a no-op when the settings file is absent + --- + duration_ms: 3.394449 + type: 'test' + ... +# Subtest: codex undo strips the managed blocks and restores model_provider byte-for-byte +ok 834 - codex undo strips the managed blocks and restores model_provider byte-for-byte + --- + duration_ms: 8.965066 + type: 'test' + ... +# Subtest: codex undo of a no-previous-provider attach round-trips to empty +ok 835 - codex undo of a no-previous-provider attach round-trips to empty + --- + duration_ms: 5.453249 + type: 'test' + ... +# Subtest: codex undo preserves unrelated config alongside the restored provider +ok 836 - codex undo preserves unrelated config alongside the restored provider + --- + duration_ms: 6.492038 + type: 'test' + ... +# Subtest: codex undo strips a hand-written marked block (no plugin loaded) +ok 837 - codex undo strips a hand-written marked block (no plugin loaded) + --- + duration_ms: 9.075023 + type: 'test' + ... +# Subtest: codex undo is a no-op when no managed block is present +ok 838 - codex undo is a no-op when no managed block is present + --- + duration_ms: 2.022163 + type: 'test' + ... +# Subtest: codex undo warning carries the user value verbatim, so `warning` is never splittable +ok 839 - codex undo warning carries the user value verbatim, so `warning` is never splittable + --- + duration_ms: 7.445056 + type: 'test' + ... +# Subtest: undo clears exactly what probeClientAttached detects, for both formats +ok 840 - undo clears exactly what probeClientAttached detects, for both formats + --- + duration_ms: 15.551296 + type: 'test' + ... +# Subtest: undo is a no-op for a descriptor without an attachProbe +ok 841 - undo is a no-op for a descriptor without an attachProbe + --- + duration_ms: 0.440189 + type: 'test' + ... +# Subtest: the atomic write unlinks the temp file on a partial write - no orphaned .tmp +ok 842 - the atomic write unlinks the temp file on a partial write - no orphaned .tmp + --- + duration_ms: 10.535303 + type: 'test' + ... +# Subtest: claude undo names EVERY externally-overridden managed key in the warning, not just the last +ok 843 - claude undo names EVERY externally-overridden managed key in the warning, not just the last + --- + duration_ms: 18.321057 + type: 'test' + ... +# Subtest: claude undo reports a single overridden key without the join separator +ok 844 - claude undo reports a single overridden key without the join separator + --- + duration_ms: 9.130767 + type: 'test' + ... +# Subtest: claude undo reports a managed key the user overrode with a non-string +ok 845 - claude undo reports a managed key the user overrode with a non-string + --- + duration_ms: 14.095384 + type: 'test' + ... +# Subtest: claude undo stays silent about a managed key the user deleted outright +ok 846 - claude undo stays silent about a managed key the user deleted outright + --- + duration_ms: 12.67852 + type: 'test' + ... +# Subtest: claude undo of a LEGACY marker reports a base URL the user overrode with a non-string +ok 847 - claude undo of a LEGACY marker reports a base URL the user overrode with a non-string + --- + duration_ms: 9.090978 + type: 'test' + ... +# Subtest: claude undo of a LEGACY marker stays silent when the base URL is absent +ok 848 - claude undo of a LEGACY marker stays silent when the base URL is absent + --- + duration_ms: 14.222046 + type: 'test' + ... +# Subtest: claude undo does not report an Object.prototype-named managed key that is absent from settings +ok 849 - claude undo does not report an Object.prototype-named managed key that is absent from settings + --- + duration_ms: 6.545629 + type: 'test' + ... +# Subtest: claude attach + undo restore a 8080 base URL byte-for-byte +ok 850 - claude attach + undo restore a 8080 base URL byte-for-byte + --- + duration_ms: 14.450092 + type: 'test' + ... +# Subtest: claude attach + undo restore a false base URL byte-for-byte +ok 851 - claude attach + undo restore a false base URL byte-for-byte + --- + duration_ms: 14.262607 + type: 'test' + ... +# Subtest: claude attach + undo restore a null base URL byte-for-byte +ok 852 - claude attach + undo restore a null base URL byte-for-byte + --- + duration_ms: 8.166212 + type: 'test' + ... +# Subtest: claude re-attach carries a non-string base URL backup forward, and undo restores it +ok 853 - claude re-attach carries a non-string base URL backup forward, and undo restores it + --- + duration_ms: 26.240933 + type: 'test' + ... +# Subtest: \#500 finding 3: a restored malformed-block backup is reported by path, not silently +ok 854 - \#500 finding 3: a restored malformed-block backup is reported by path, not silently + --- + duration_ms: 19.635624 + type: 'test' + ... +# Subtest: \#500 finding 2: the delete-then-detach resurrection is announced, not silent +ok 855 - \#500 finding 2: the delete-then-detach resurrection is announced, not silent + --- + duration_ms: 12.439336 + type: 'test' + ... +# Subtest: \#500 finding 1: the legacy branch replays a prev_malformed backup instead of dropping it +ok 856 - \#500 finding 1: the legacy branch replays a prev_malformed backup instead of dropping it + --- + duration_ms: 20.518947 + type: 'test' + ... +# Subtest: \#500 finding 1: the legacy branch reports a prev_malformed backup it cannot put back +ok 857 - \#500 finding 1: the legacy branch reports a prev_malformed backup it cannot put back + --- + duration_ms: 11.698921 + type: 'test' + ... +# Subtest: \#500 finding 1: the legacy branch restores prev_base_url rather than deleting the key +ok 858 - \#500 finding 1: the legacy branch restores prev_base_url rather than deleting the key + --- + duration_ms: 14.828267 + type: 'test' + ... +# Subtest: \#500 finding 1: a GENUINE pre-record marker is reversed exactly as before, and says nothing new +ok 859 - \#500 finding 1: a GENUINE pre-record marker is reversed exactly as before, and says nothing new + --- + duration_ms: 8.726834 + type: 'test' + ... +# Subtest: \#500 finding 3: `hyp detach` prints the restored block, and never its contents +ok 860 - \#500 finding 3: `hyp detach` prints the restored block, and never its contents + --- + duration_ms: 29.066178 + type: 'test' + ... +# Subtest: \#500 finding 3: `hyp detach --json` echoes restored_paths, and never the contents +ok 861 - \#500 finding 3: `hyp detach --json` echoes restored_paths, and never the contents + --- + duration_ms: 25.923641 + type: 'test' + ... +# Subtest: json_path undo deletes the two entries the gateway wrote +ok 862 - json_path undo deletes the two entries the gateway wrote + --- + duration_ms: 13.735186 + type: 'test' + ... +# Subtest: json_path undo leaves a present-but-not-ours entry in place and warns by path +ok 863 - json_path undo leaves a present-but-not-ours entry in place and warns by path + --- + duration_ms: 6.15787 + type: 'test' + ... +# Subtest: json_path undo does not touch or remark on a config that was never attached +ok 864 - json_path undo does not touch or remark on a config that was never attached + --- + duration_ms: 7.383121 + type: 'test' + ... +# Subtest: json_path undo rerun finishes a cache purge the first pass left behind +ok 865 - json_path undo rerun finishes a cache purge the first pass left behind + --- + duration_ms: 18.063675 + type: 'test' + ... +# Subtest: json_path undo rerun purges marked residue beside a user entry it leaves alone +ok 866 - json_path undo rerun purges marked residue beside a user entry it leaves alone + --- + duration_ms: 14.867928 + type: 'test' + ... +# Subtest: json_path undo leaves a never-attached machine with no managed settings keys and marker-less cache rows untouched +ok 867 - json_path undo leaves a never-attached machine with no managed settings keys and marker-less cache rows untouched + --- + duration_ms: 4.070748 + type: 'test' + ... +# Subtest: json_path undo deletes a stale entry from an old port: the signature is the whole test +ok 868 - json_path undo deletes a stale entry from an old port: the signature is the whole test + --- + duration_ms: 5.88233 + type: 'test' + ... +# Subtest: json_path undo is a no-op when the settings file is absent +ok 869 - json_path undo is a no-op when the settings file is absent + --- + duration_ms: 0.935472 + type: 'test' + ... +# Subtest: json_path undo keeps the cache row for a user entry it left in the settings +ok 870 - json_path undo keeps the cache row for a user entry it left in the settings + --- + duration_ms: 11.452246 + type: 'test' + ... +# Subtest: json_path undo purges the derived caches, skipping one that will not parse +ok 871 - json_path undo purges the derived caches, skipping one that will not parse + --- + duration_ms: 15.806524 + type: 'test' + ... +# Subtest: json_path undo purges the caches under a nested $OPENCLAW_HOME +ok 872 - json_path undo purges the caches under a nested $OPENCLAW_HOME + --- + duration_ms: 13.338513 + type: 'test' + ... +# Subtest: the enable write is additive: unrelated plugins and keys survive, and the prior config is backed up +ok 873 - the enable write is additive: unrelated plugins and keys survive, and the prior config is backed up + --- + duration_ms: 22.968219 + type: 'test' + ... +# Subtest: an entry the central layer already names is not duplicated into the local layer +ok 874 - an entry the central layer already names is not duplicated into the local layer + --- + duration_ms: 3.876883 + type: 'test' + ... +# Subtest: an entry already in the local layer is not appended a second time +ok 875 - an entry already in the local layer is not appended a second time + --- + duration_ms: 6.100843 + type: 'test' + ... +# Subtest: with no daemon service installed, restart and the bind wait are both skipped +ok 876 - with no daemon service installed, restart and the bind wait are both skipped + --- + duration_ms: 3.008052 + type: 'test' + ... +# Subtest: with a daemon installed, the write is followed by a restart and a wait that sees the bound port +ok 877 - with a daemon installed, the write is followed by a restart and a wait that sees the bound port + --- + duration_ms: 5.55956 + type: 'test' + ... +# Subtest: a restart failure is reported as the restart step, with the write and its backup intact +ok 878 - a restart failure is reported as the restart step, with the write and its backup intact + --- + duration_ms: 2.487962 + type: 'test' + ... +# Subtest: a gateway that never binds times out into a reported wait failure, not a throw +ok 879 - a gateway that never binds times out into a reported wait failure, not a throw + --- + duration_ms: 31.943841 + type: 'test' + ... +# Subtest: waitForGatewayBind returns as soon as status.json reports a bound port +ok 880 - waitForGatewayBind returns as soon as status.json reports a bound port + --- + duration_ms: 1.945978 + type: 'test' + ... +# Subtest: waitForGatewayBind polls until the port appears +ok 881 - waitForGatewayBind polls until the port appears + --- + duration_ms: 1.057778 + type: 'test' + ... +# Subtest: waitForGatewayBind returns { bound: false } on timeout instead of throwing +ok 882 - waitForGatewayBind returns { bound: false } on timeout instead of throwing + --- + duration_ms: 26.979226 + type: 'test' + ... +# Subtest: waitForGatewayBind treats a throwing probe as "not yet", not as a failure +ok 883 - waitForGatewayBind treats a throwing probe as "not yet", not as a failure + --- + duration_ms: 2.529935 + type: 'test' + ... +# Subtest: the bundled CLI clients declare a launch spec; each carries {prompt} +ok 884 - the bundled CLI clients declare a launch spec; each carries {prompt} + --- + duration_ms: 26.859214 + type: 'test' + ... +# Subtest: a launch spec without {prompt} is dropped: launchable and mute is worse than not launchable +ok 885 - a launch spec without {prompt} is dropped: launchable and mute is worse than not launchable + --- + duration_ms: 0.260697 + type: 'test' + ... +# Subtest: a malformed launch spec is dropped, and never fails catalog construction +ok 886 - a malformed launch spec is dropped, and never fails catalog construction + --- + duration_ms: 0.226544 + type: 'test' + ... +# Subtest: a client with no launch spec stays unlaunchable (Claude Desktop has no prompt argument) +ok 887 - a client with no launch spec stays unlaunchable (Claude Desktop has no prompt argument) + --- + duration_ms: 11.834528 + type: 'test' + ... +# Subtest: the bundled CLI clients' launch args never carry a permission-widening flag +ok 888 - the bundled CLI clients' launch args never carry a permission-widening flag + --- + duration_ms: 6.857503 + type: 'test' + ... +# Subtest: the permission-widening denylist covers the documented equivalents +ok 889 - the permission-widening denylist covers the documented equivalents + --- + duration_ms: 0.26981 + type: 'test' + ... +# Subtest: attach Claude (capitalized) resolves to the lowercase claude adapter +ok 890 - attach Claude (capitalized) resolves to the lowercase claude adapter + --- + duration_ms: 21.845363 + type: 'test' + ... +# Subtest: attach ALL (uppercase sentinel) expands to every registered client +ok 891 - attach ALL (uppercase sentinel) expands to every registered client + --- + duration_ms: 19.447599 + type: 'test' + ... +# Subtest: attach claude (already lowercase) is unchanged +ok 892 - attach claude (already lowercase) is unchanged + --- + duration_ms: 7.219513 + type: 'test' + ... +# Subtest: detach Claude (capitalized) resolves via the client-descriptor map +ok 893 - detach Claude (capitalized) resolves via the client-descriptor map + --- + duration_ms: 7.483024 + type: 'test' + ... +# Subtest: 'central': the owning plugin is declared by the central layer +ok 894 - 'central': the owning plugin is declared by the central layer + --- + duration_ms: 1.003175 + type: 'test' + ... +# Subtest: 'local': in the effective config but not in the central layer +ok 895 - 'local': in the effective config but not in the central layer + --- + duration_ms: 0.182958 + type: 'test' + ... +# Subtest: 'absent': the owning plugin is not in the effective config at all +ok 896 - 'absent': the owning plugin is not in the effective config at all + --- + duration_ms: 0.892857 + type: 'test' + ... +# Subtest: 'absent': a source id that resolves to no plugin (conservative default) +ok 897 - 'absent': a source id that resolves to no plugin (conservative default) + --- + duration_ms: 0.124109 + type: 'test' + ... +# Subtest: resolution falls back to clientDescriptors when no picker row matches +ok 898 - resolution falls back to clientDescriptors when no picker row matches + --- + duration_ms: 0.127173 + type: 'test' + ... +# Subtest: picker descriptors are authoritative over a same-named client descriptor +ok 899 - picker descriptors are authoritative over a same-named client descriptor + --- + duration_ms: 0.167645 + type: 'test' + ... +# Subtest: a solo host (no central layer) classifies every present source as local +ok 900 - a solo host (no central layer) classifies every present source as local + --- + duration_ms: 0.125711 + type: 'test' + ... +# Subtest: a present-but-central source stays central even when central also enables it +ok 901 - a present-but-central source stays central even when central also enables it + --- + duration_ms: 0.121094 + type: 'test' + ... +# Subtest: empty layered config: an in-catalog source with no config is absent +ok 902 - empty layered config: an in-catalog source with no config is absent + --- + duration_ms: 0.372976 + type: 'test' + ... +# Subtest: clientSyncListPath derives /usage-policy/client-sync.json +ok 903 - clientSyncListPath derives /usage-policy/client-sync.json + --- + duration_ms: 0.916012 + type: 'test' + ... +# Subtest: clientSyncListPath requires a stateDir +ok 904 - clientSyncListPath requires a stateDir + --- + duration_ms: 0.283651 + type: 'test' + ... +# Subtest: readClientSyncEntries returns null when the store has never been written +ok 905 - readClientSyncEntries returns null when the store has never been written + --- + duration_ms: 4.785604 + type: 'test' + ... +# Subtest: an empty stamped store reads as [], distinct from absent +ok 906 - an empty stamped store reads as [], distinct from absent + --- + duration_ms: 3.100652 + type: 'test' + ... +# Subtest: write then read round-trips the entry set +ok 907 - write then read round-trips the entry set + --- + duration_ms: 1.784041 + type: 'test' + ... +# Subtest: writeClientSyncEntries persists the version-1 shape +ok 908 - writeClientSyncEntries persists the version-1 shape + --- + duration_ms: 2.4481 + type: 'test' + ... +# Subtest: writeClientSyncEntries dedupes by source (later wins) and sorts +ok 909 - writeClientSyncEntries dedupes by source (later wins) and sorts + --- + duration_ms: 1.517176 + type: 'test' + ... +# Subtest: optedOutClientSourceIds lists sources from entries and treats null as none +ok 910 - optedOutClientSourceIds lists sources from entries and treats null as none + --- + duration_ms: 0.200234 + type: 'test' + ... +# Subtest: readClientSyncEntries throws ClientSyncListUnreadableError on unparseable JSON +ok 911 - readClientSyncEntries throws ClientSyncListUnreadableError on unparseable JSON + --- + duration_ms: 2.088274 + type: 'test' + ... +# Subtest: readClientSyncEntries throws on a wrong-shape file +ok 912 - readClientSyncEntries throws on a wrong-shape file + --- + duration_ms: 3.451816 + type: 'test' + ... +# Subtest: readClientSyncEntries throws when an entry has an unknown class or empty source +ok 913 - readClientSyncEntries throws when an entry has an unknown class or empty source + --- + duration_ms: 1.78251 + type: 'test' + ... +# Subtest: seedClientSyncStoreIfAbsent stamps an empty store once and is idempotent +ok 914 - seedClientSyncStoreIfAbsent stamps an empty store once and is idempotent + --- + duration_ms: 1.659783 + type: 'test' + ... +# Subtest: seedClientSyncStoreIfAbsent never clobbers an existing entry list +ok 915 - seedClientSyncStoreIfAbsent never clobbers an existing entry list + --- + duration_ms: 1.742668 + type: 'test' + ... +# Subtest: writeClientSyncEntries is atomic write-rename and leaves no temp files +ok 916 - writeClientSyncEntries is atomic write-rename and leaves no temp files + --- + duration_ms: 1.28334 + type: 'test' + ... +# Subtest: a legacy rollout with no session_id reports none: the thread id is never back-filled +ok 917 - a legacy rollout with no session_id reports none: the thread id is never back-filled + --- + duration_ms: 1.128585 + type: 'test' + ... +# Subtest: a subagent rollout keeps the container and the thread apart +ok 918 - a subagent rollout keeps the container and the thread apart + --- + duration_ms: 0.212834 + type: 'test' + ... +# Subtest: a line that is not JSON at all resolves nothing +ok 919 - a line that is not JSON at all resolves nothing + --- + duration_ms: 0.14479 + type: 'test' + ... +# Subtest: a JSON line that is not an object resolves nothing +ok 920 - a JSON line that is not an object resolves nothing + --- + duration_ms: 0.129046 + type: 'test' + ... +# Subtest: another envelope type carrying id/session_id/cwd is not the header +ok 921 - another envelope type carrying id/session_id/cwd is not the header + --- + duration_ms: 1.485588 + type: 'test' + ... +# Subtest: a missing, blank, or non-string envelope type is not session_meta either +ok 922 - a missing, blank, or non-string envelope type is not session_meta either + --- + duration_ms: 0.160424 + type: 'test' + ... +# Subtest: a blank or non-string field is absent, never an empty value passed on +ok 923 - a blank or non-string field is absent, never an empty value passed on + --- + duration_ms: 0.272394 + type: 'test' + ... +# Subtest: a session_meta header with no payload resolves no field +ok 924 - a session_meta header with no payload resolves no field + --- + duration_ms: 0.668967 + type: 'test' + ... +# Subtest: a field that survives the blank test is returned byte-identical +ok 925 - a field that survives the blank test is returned byte-identical + --- + duration_ms: 0.303031 + type: 'test' + ... +# Subtest: a relative session_meta.cwd is no cwd, not a path resolved against the daemon +ok 926 - a relative session_meta.cwd is no cwd, not a path resolved against the daemon + --- + duration_ms: 0.450194 + type: 'test' + ... +# Subtest: an absolute cwd still resolves, and the ids are never path-tested +ok 927 - an absolute cwd still resolves, and the ids are never path-tested + --- + duration_ms: 0.13785 + type: 'test' + ... +# Subtest: sessionMetaCwd is the one cwd predicate, usable by a caller that cannot delegate +ok 928 - sessionMetaCwd is the one cwd predicate, usable by a caller that cannot delegate + --- + duration_ms: 0.081363 + type: 'test' + ... +# Subtest: readRolloutSessionMeta reads the first line and ignores the rest of the rollout +ok 929 - readRolloutSessionMeta reads the first line and ignores the rest of the rollout + --- + duration_ms: 1.716249 + type: 'test' + ... +# Subtest: a rollout whose first line is longer than the read bound resolves nothing rather than half a line +ok 930 - a rollout whose first line is longer than the read bound resolves nothing rather than half a line + --- + duration_ms: 0.598349 + type: 'test' + ... +# Subtest: an unreadable, empty, or absent rollout resolves nothing +ok 931 - an unreadable, empty, or absent rollout resolves nothing + --- + duration_ms: 0.537407 + type: 'test' + ... +# Subtest: a rollout with no trailing newline is still one whole first line +ok 932 - a rollout with no trailing newline is still one whole first line + --- + duration_ms: 0.305404 + type: 'test' + ... +# Subtest: Claude session-context hook exits 0 without --state-file +ok 933 - Claude session-context hook exits 0 without --state-file + --- + duration_ms: 15.63254 + type: 'test' + ... +# Subtest: Claude session-context hook appends one JSONL record per event to --state-file +ok 934 - Claude session-context hook appends one JSONL record per event to --state-file + --- + duration_ms: 43.404811 + type: 'test' + ... +# Subtest: Claude session-context hook ignores events without session context +ok 935 - Claude session-context hook ignores events without session context + --- + duration_ms: 6.283481 + type: 'test' + ... +# Subtest: legacy Claude session-context hook --port writes the default plugin state file +ok 936 - legacy Claude session-context hook --port writes the default plugin state file + --- + duration_ms: 15.737339 + type: 'test' + ... +# Subtest: hidden Claude hook command is omitted from top-level help +ok 937 - hidden Claude hook command is omitted from top-level help + --- + duration_ms: 13.2904 + type: 'test' + ... +# Subtest: top-level help lists commands declared by config-active plugins +ok 938 - top-level help lists commands declared by config-active plugins + --- + duration_ms: 6.909061 + type: 'test' + ... +# Subtest: top-level help lists a local plugin addition on a fleet-joined host +ok 939 - top-level help lists a local plugin addition on a fleet-joined host + --- + duration_ms: 9.024767 + type: 'test' + ... +# Subtest: top-level help omits plugin commands when the plugin is disabled +ok 940 - top-level help omits plugin commands when the plugin is disabled + --- + duration_ms: 6.079741 + type: 'test' + ... +# Subtest: top-level help lists the installed plugin that replaces an excluded bundled skeleton, not the skeleton it shadows +ok 941 - top-level help lists the installed plugin that replaces an excluded bundled skeleton, not the skeleton it shadows + --- + duration_ms: 7.530476 + type: 'test' + ... +# Subtest: top-level help advertises no commands for an installed plugin that shadows a bundled first-party name +ok 942 - top-level help advertises no commands for an installed plugin that shadows a bundled first-party name + --- + duration_ms: 12.450223 + type: 'test' + ... +# Subtest: bare group with no command of its own renders synthesized group help +ok 943 - bare group with no command of its own renders synthesized group help + --- + duration_ms: 0.687876 + type: 'test' + ... +# Subtest: group --help renders synthesized group help +ok 944 - group --help renders synthesized group help + --- + duration_ms: 0.253005 + type: 'test' + ... +# Subtest: group with an unknown subcommand reports it and exits 2 +ok 945 - group with an unknown subcommand reports it and exits 2 + --- + duration_ms: 0.323271 + type: 'test' + ... +# Subtest: top-level help collapses subcommands into one row per group +ok 946 - top-level help collapses subcommands into one row per group + --- + duration_ms: 16.623816 + type: 'test' + ... +# Subtest: group --help lists subcommands with their registry summaries +ok 947 - group --help lists subcommands with their registry summaries + --- + duration_ms: 0.759926 + type: 'test' + ... +# Subtest: an action command with subcommands gets group help on --help +ok 948 - an action command with subcommands gets group help on --help + --- + duration_ms: 0.64445 + type: 'test' + ... +# Subtest: leaf command --help renders summary, usage, and long help +ok 949 - leaf command --help renders summary, usage, and long help + --- + duration_ms: 0.425076 + type: 'test' + ... +# Subtest: a leaf subcommand --help documents every flag the command accepts +ok 950 - a leaf subcommand --help documents every flag the command accepts + --- + duration_ms: 3.494041 + type: 'test' + ... +# Subtest: bare group command with an unknown subcommand reports the registry children +ok 951 - bare group command with an unknown subcommand reports the registry children + --- + duration_ms: 0.527542 + type: 'test' + ... +# Subtest: a token that is neither a command nor a group prefix still errors +ok 952 - a token that is neither a command nor a group prefix still errors + --- + duration_ms: 8.143437 + type: 'test' + ... +# Subtest: dispatch surfaces boot-path sink materialization warnings +ok 953 - dispatch surfaces boot-path sink materialization warnings + --- + duration_ms: 12.008201 + type: 'test' + ... +# Subtest: zero-plugin lifecycle commands skip sink materialization warnings; config-profile commands still warn +ok 954 - zero-plugin lifecycle commands skip sink materialization warnings; config-profile commands still warn + --- + duration_ms: 20.682556 + type: 'test' + ... +# Subtest: walkthrough boot skips sink warnings for config-named plugins its profile excludes; unnamed ones still warn +ok 955 - walkthrough boot skips sink warnings for config-named plugins its profile excludes; unnamed ones still warn + --- + duration_ms: 133.775225 + type: 'test' + ... +# Subtest: walkthrough boot still warns when a config-named sink plugin is uninstalled or unresolvable +ok 956 - walkthrough boot still warns when a config-named sink plugin is uninstalled or unresolvable + --- + duration_ms: 41.796847 + type: 'test' + ... +# Subtest: attach accepts a positional client name +ok 957 - attach accepts a positional client name + --- + duration_ms: 7.545188 + type: 'test' + ... +# Subtest: unattach alias routes a positional client through the core disk undo +ok 958 - unattach alias routes a positional client through the core disk undo + --- + duration_ms: 12.482613 + type: 'test' + ... +# Subtest: attach rejects conflicting positional and flag client names +ok 959 - attach rejects conflicting positional and flag client names + --- + duration_ms: 1.201787 + type: 'test' + ... +# Subtest: dispatch forwards a real stdin to command run when the caller omits opts.stdin +ok 960 - dispatch forwards a real stdin to command run when the caller omits opts.stdin + --- + duration_ms: 0.281458 + type: 'test' + ... +# Subtest: ctx.commands.run dispatches a registered command with the same exit code and output as the CLI path +ok 961 - ctx.commands.run dispatches a registered command with the same exit code and output as the CLI path + --- + duration_ms: 0.644199 + type: 'test' + ... +# Subtest: ctx.commands.run activates a config-enabled plugin the boot profile skipped, so its command dispatches +ok 962 - ctx.commands.run activates a config-enabled plugin the boot profile skipped, so its command dispatches + --- + duration_ms: 20.703357 + type: 'test' + ... +# Subtest: ctx.commands.run does not activate a plugin the effective config leaves out +ok 963 - ctx.commands.run does not activate a plugin the effective config leaves out + --- + duration_ms: 12.031187 + type: 'test' + ... +# Subtest: askableClients returns only attached clients when the probe succeeds +ok 964 - askableClients returns only attached clients when the probe succeeds + --- + duration_ms: 0.891625 + type: 'test' + ... +# Subtest: askableClients returns an empty list when the probe succeeds with nothing attached, rather than falling back +ok 965 - askableClients returns an empty list when the probe succeeds with nothing attached, rather than falling back + --- + duration_ms: 0.109776 + type: 'test' + ... +# Subtest: askableClients falls back to launchable clients only when the probe throws +ok 966 - askableClients falls back to launchable clients only when the probe throws + --- + duration_ms: 24.86991 + type: 'test' + ... +# Subtest: runAsk: no-launcher exits 1 on a fresh install with nothing attached +ok 967 - runAsk: no-launcher exits 1 on a fresh install with nothing attached + --- + duration_ms: 20.566119 + type: 'test' + ... +# Subtest: runAsk: --list exits 0 regardless of launchability +ok 968 - runAsk: --list exits 0 regardless of launchability + --- + duration_ms: 17.078517 + type: 'test' + ... +# Subtest: runAsk: --list on a host with nothing launchable prints the manual fallback, not a launch promise +ok 969 - runAsk: --list on a host with nothing launchable prints the manual fallback, not a launch promise + --- + duration_ms: 12.92157 + type: 'test' + ... +# Subtest: claude alone composes the gateway + anthropic upstream + claude adapter +ok 970 - claude alone composes the gateway + anthropic upstream + claude adapter + --- + duration_ms: 20.900967 + type: 'test' + ... +# Subtest: codex alone composes the gateway + openai + chatgpt upstreams + codex adapter +ok 971 - codex alone composes the gateway + openai + chatgpt upstreams + codex adapter + --- + duration_ms: 11.862881 + type: 'test' + ... +# Subtest: raw-anthropic alone composes only the gateway + anthropic upstream (no adapter plugin) +ok 972 - raw-anthropic alone composes only the gateway + anthropic upstream (no adapter plugin) + --- + duration_ms: 8.381639 + type: 'test' + ... +# Subtest: raw-openai alone composes only the gateway + openai upstream (no chatgpt, no adapter plugin) +ok 973 - raw-openai alone composes only the gateway + openai upstream (no chatgpt, no adapter plugin) + --- + duration_ms: 4.955512 + type: 'test' + ... +# Subtest: otel alone composes the otel receiver, no gateway +ok 974 - otel alone composes the otel receiver, no gateway + --- + duration_ms: 4.432707 + type: 'test' + ... +# Subtest: openclaw alone composes the gateway + anthropic upstream + openclaw adapter +ok 975 - openclaw alone composes the gateway + anthropic upstream + openclaw adapter + --- + duration_ms: 6.203589 + type: 'test' + ... +# Subtest: hermes alone composes the gateway (no upstreams) + hermes adapter +ok 976 - hermes alone composes the gateway (no upstreams) + hermes adapter + --- + duration_ms: 2.856821 + type: 'test' + ... +# Subtest: claude + hermes share the gateway; hermes adds no upstream +ok 977 - claude + hermes share the gateway; hermes adds no upstream + --- + duration_ms: 2.735648 + type: 'test' + ... +# Subtest: claude + codex union the anthropic/openai/chatgpt upstreams and both adapters +ok 978 - claude + codex union the anthropic/openai/chatgpt upstreams and both adapters + --- + duration_ms: 4.476393 + type: 'test' + ... +# Subtest: all five sources dedupe upstreams by name and order otel before the export sinks +ok 979 - all five sources dedupe upstreams by name and order otel before the export sinks + --- + duration_ms: 10.069796 + type: 'test' + ... +# Subtest: no sources picked still writes a valid config with just the export sinks +ok 980 - no sources picked still writes a valid config with just the export sinks + --- + duration_ms: 11.641554 + type: 'test' + ... +# Subtest: keep-local export omits the sink plugins and sinks block +ok 981 - keep-local export omits the sink plugins and sinks block + --- + duration_ms: 4.400629 + type: 'test' + ... +# Subtest: configure-later export behaves like keep-local (no sinks block) +ok 982 - configure-later export behaves like keep-local (no sinks block) + --- + duration_ms: 3.980801 + type: 'test' + ... +# Subtest: real claude/codex picker rows carry the settings_file detect probe +ok 983 - real claude/codex picker rows carry the settings_file detect probe + --- + duration_ms: 8.161224 + type: 'test' + ... +# Subtest: real openclaw/hermes picker rows carry the settings_file detect probe +ok 984 - real openclaw/hermes picker rows carry the settings_file detect probe + --- + duration_ms: 4.077017 + type: 'test' + ... +# Subtest: no bundled plugin manifest fails validation +ok 985 - no bundled plugin manifest fails validation + --- + duration_ms: 5.749088 + type: 'test' + ... +# Subtest: claude-desktop composes the gateway, the credential plugin, and its own adapter +ok 986 - claude-desktop composes the gateway, the credential plugin, and its own adapter + --- + duration_ms: 8.627603 + type: 'test' + ... +# Subtest: the claude-desktop row composes its required-capability provider, not just its adapter +ok 987 - the claude-desktop row composes its required-capability provider, not just its adapter + --- + duration_ms: 8.360728 + type: 'test' + ... +# Subtest: claude + claude-desktop share one anthropic upstream and one gateway +ok 988 - claude + claude-desktop share one anthropic upstream and one gateway + --- + duration_ms: 5.464305 + type: 'test' + ... +# Subtest: every needs_setup picker row composes the plugin that owns its configure_command +ok 989 - every needs_setup picker row composes the plugin that owns its configure_command + --- + duration_ms: 6.610558 + type: 'test' + ... +# Subtest: picking a gateway client composes the graph pair with it +ok 990 - picking a gateway client composes the graph pair with it + --- + duration_ms: 12.66549 + type: 'test' + ... +# Subtest: a gateway-free pick composes neither graph plugin +ok 991 - a gateway-free pick composes neither graph plugin + --- + duration_ms: 4.66475 + type: 'test' + ... +# Subtest: a reconfigure that unpicks the gateway drops the graph pair +ok 992 - a reconfigure that unpicks the gateway drops the graph pair + --- + duration_ms: 3.774286 + type: 'test' + ... +# Subtest: a hand-added non-rider plugin survives a reconfigure that composes riders +ok 993 - a hand-added non-rider plugin survives a reconfigure that composes riders + --- + duration_ms: 5.012188 + type: 'test' + ... +# Subtest: a rider that rides another rider still composes +ok 994 - a rider that rides another rider still composes + --- + duration_ms: 4.456664 + type: 'test' + ... +# Subtest: no composeWith map means no riders +ok 995 - no composeWith map means no riders + --- + duration_ms: 4.178581 + type: 'test' + ... +# Subtest: a user's `enabled: false` on a rider survives a reconfigure +ok 996 - a user's `enabled: false` on a rider survives a reconfigure + --- + duration_ms: 4.117018 + type: 'test' + ... +# Subtest: a picked plugin still loses a stale `enabled: false` +ok 997 - a picked plugin still loses a stale `enabled: false` + --- + duration_ms: 4.231681 + type: 'test' + ... +# Subtest: an excluded plugin declaring compose_with is not composed +ok 998 - an excluded plugin declaring compose_with is not composed + --- + duration_ms: 2.402982 + type: 'test' + ... +# Subtest: an injected catalog cannot smuggle an excluded rider through resolvePickSeeding +ok 999 - an injected catalog cannot smuggle an excluded rider through resolvePickSeeding + --- + duration_ms: 3.215227 + type: 'test' + ... +# Subtest: no excluded bundled manifest declares compose_with +ok 1000 - no excluded bundled manifest declares compose_with + --- + duration_ms: 10.262678 + type: 'test' + ... +# Subtest: bundled plugin: pinned version mismatch is a bundled_version_mismatch failure +ok 1001 - bundled plugin: pinned version mismatch is a bundled_version_mismatch failure + --- + duration_ms: 12.208506 + type: 'test' + ... +# Subtest: bundled plugin: matching version pin is satisfied without an install; hash is not checked +ok 1002 - bundled plugin: matching version pin is satisfied without an install; hash is not checked + --- + duration_ms: 7.239944 + type: 'test' + ... +# Subtest: bundled plugin: an unpinned entry is satisfied by any bundled version +ok 1003 - bundled plugin: an unpinned entry is satisfied by any bundled version + --- + duration_ms: 4.784252 + type: 'test' + ... +# Subtest: disabled entries are skipped entirely +ok 1004 - disabled entries are skipped entirely + --- + duration_ms: 2.968192 + type: 'test' + ... +# Subtest: an installed lock entry matching version + hash is satisfied without re-install +ok 1005 - an installed lock entry matching version + hash is satisfied without re-install + --- + duration_ms: 28.114722 + type: 'test' + ... +# Subtest: a fetched artifact failing its hash pin is an artifact_hash_mismatch and nothing is installed +ok 1006 - a fetched artifact failing its hash pin is an artifact_hash_mismatch and nothing is installed + --- + duration_ms: 146.804769 + type: 'test' + ... +# Subtest: a correct hash pin installs, and validation then sees the plugin it could not know before +ok 1007 - a correct hash pin installs, and validation then sees the plugin it could not know before + --- + duration_ms: 224.221554 + type: 'test' + ... +# Subtest: an expired first apply with no distinct previous slot does not mark the active etag bad +ok 1008 - an expired first apply with no distinct previous slot does not mark the active etag bad + --- + duration_ms: 7.664079 + type: 'test' + ... +# Subtest: the watchdog does not loop-restart when a no-op rollback has no target +ok 1009 - the watchdog does not loop-restart when a no-op rollback has no target + --- + duration_ms: 103.075625 + type: 'test' + ... +# Subtest: boot recovers an already-wedged active slot by clearing the contradictory bad_etag (re-pull) +ok 1010 - boot recovers an already-wedged active slot by clearing the contradictory bad_etag (re-pull) + --- + duration_ms: 2.483935 + type: 'test' + ... +# Subtest: boot recovers a wedged active slot by falling back to the seed when one survives +ok 1011 - boot recovers a wedged active slot by falling back to the seed when one survives + --- + duration_ms: 2.575254 + type: 'test' + ... +# Subtest: apply validation rejects a malformed plugin backfill block via the live section validator +ok 1012 - apply validation rejects a malformed plugin backfill block via the live section validator + --- + duration_ms: 113.143918 + type: 'test' + ... +# Subtest: apply validates a backfill block for a plugin the document INTRODUCES but is not active yet +ok 1013 - apply validates a backfill block for a plugin the document INTRODUCES but is not active yet + --- + duration_ms: 37.832632 + type: 'test' + ... +# Subtest: introduced-plugin discovery rejects a malformed block even without the live registry +ok 1014 - introduced-plugin discovery rejects a malformed block even without the live registry + --- + duration_ms: 19.709056 + type: 'test' + ... +# Subtest: stage applies a document: slot persisted, pointer flipped, etag staged, probation armed, restart requested +ok 1015 - stage applies a document: slot persisted, pointer flipped, etag staged, probation armed, restart requested + --- + duration_ms: 14.722967 + type: 'test' + ... +# Subtest: probation window is max(3 Ă— poll interval, floor) from the staged document +ok 1016 - probation window is max(3 Ă— poll interval, floor) from the staged document + --- + duration_ms: 3.550777 + type: 'test' + ... +# Subtest: stage before attachApplyDeps fails closed +ok 1017 - stage before attachApplyDeps fails closed + --- + duration_ms: 1.445156 + type: 'test' + ... +# Subtest: validation failure remembers the bad etag and leaves the central layer untouched +ok 1018 - validation failure remembers the bad etag and leaves the central layer untouched + --- + duration_ms: 2.337783 + type: 'test' + ... +# Subtest: pinned plugins install before full validation, so a config can name a not-yet-installed plugin +ok 1019 - pinned plugins install before full validation, so a config can name a not-yet-installed plugin + --- + duration_ms: 2.141194 + type: 'test' + ... +# Subtest: a shape-invalid document is rejected before any install runs +ok 1020 - a shape-invalid document is rejected before any install runs + --- + duration_ms: 2.017226 + type: 'test' + ... +# Subtest: a remembered bad etag backs off re-apply until the etag changes +ok 1021 - a remembered bad etag backs off re-apply until the etag changes + --- + duration_ms: 3.056155 + type: 'test' + ... +# Subtest: pinned install hash mismatch is an apply failure with a structured reason +ok 1022 - pinned install hash mismatch is an apply failure with a structured reason + --- + duration_ms: 3.46715 + type: 'test' + ... +# Subtest: oversized documents are rejected before validation +ok 1023 - oversized documents are rejected before validation + --- + duration_ms: 12.515412 + type: 'test' + ... +# Subtest: staging the running etag is a no-op +ok 1024 - staging the running etag is a no-op + --- + duration_ms: 4.537116 + type: 'test' + ... +# Subtest: a second stage in the same process is refused while a restart is pending +ok 1025 - a second stage in the same process is refused while a restart is pending + --- + duration_ms: 5.536244 + type: 'test' + ... +# Subtest: confirmPoll clears probation +ok 1026 - confirmPoll clears probation + --- + duration_ms: 3.114233 + type: 'test' + ... +# Subtest: onConfirmed fires exactly on the probation active→cleared edge, not on a no-probation poll +ok 1027 - onConfirmed fires exactly on the probation active→cleared edge, not on a no-probation poll + --- + duration_ms: 4.021202 + type: 'test' + ... +# Subtest: chained applies alternate slots and roll back one revision +ok 1028 - chained applies alternate slots and roll back one revision + --- + duration_ms: 3.200614 + type: 'test' + ... +# Subtest: evaluateAtBoot rolls an expired first apply back onto the seed +ok 1029 - evaluateAtBoot rolls an expired first apply back onto the seed + --- + duration_ms: 2.472157 + type: 'test' + ... +# Subtest: evaluateAtBoot keeps an unexpired probation marker +ok 1030 - evaluateAtBoot keeps an unexpired probation marker + --- + duration_ms: 1.581063 + type: 'test' + ... +# Subtest: evaluateAtBoot discards a probation marker whose flip never committed +ok 1031 - evaluateAtBoot discards a probation marker whose flip never committed + --- + duration_ms: 1.8686 + type: 'test' + ... +# Subtest: the probation watchdog rolls back and requests a restart on expiry +ok 1032 - the probation watchdog rolls back and requests a restart on expiry + --- + duration_ms: 102.666563 + type: 'test' + ... +# Subtest: a confirmed poll disarms the watchdog before it fires +ok 1033 - a confirmed poll disarms the watchdog before it fires + --- + duration_ms: 107.563166 + type: 'test' + ... +# Subtest: readConfigControlStatus reads without an engine and tolerates a fresh install +ok 1034 - readConfigControlStatus reads without an engine and tolerates a fresh install + --- + duration_ms: 3.614193 + type: 'test' + ... +# Subtest: parseConfigShape accepts and validates plugin pin fields +ok 1035 - parseConfigShape accepts and validates plugin pin fields + --- + duration_ms: 0.238743 + type: 'test' + ... +# Subtest: no central layer: effective is the local layer verbatim +ok 1036 - no central layer: effective is the local layer verbatim + --- + duration_ms: 0.920378 + type: 'test' + ... +# Subtest: both layers absent: effective is an empty v2 config +ok 1037 - both layers absent: effective is an empty v2 config + --- + duration_ms: 0.219554 + type: 'test' + ... +# Subtest: local adds plugins/sinks the central layer does not name +ok 1038 - local adds plugins/sinks the central layer does not name + --- + duration_ms: 0.157319 + type: 'test' + ... +# Subtest: central wins and locks: a colliding local plugin/sink is dropped +ok 1039 - central wins and locks: a colliding local plugin/sink is dropped + --- + duration_ms: 5.110808 + type: 'test' + ... +# Subtest: query is local-only: the local block wins, a central query block is ignored +ok 1040 - query is local-only: the local block wins, a central query block is ignored + --- + duration_ms: 0.141415 + type: 'test' + ... +# Subtest: resolveLayeredConfig: a valid-in-isolation local addition that invalidates the merge is dropped +ok 1041 - resolveLayeredConfig: a valid-in-isolation local addition that invalidates the merge is dropped + --- + duration_ms: 0.319054 + type: 'test' + ... +# Subtest: resolveLayeredConfig: an error the central layer carries alone never drops a local entry +ok 1042 - resolveLayeredConfig: an error the central layer carries alone never drops a local entry + --- + duration_ms: 0.166483 + type: 'test' + ... +# Subtest: resolveLayeredConfig: collisions and invalid additions both surface as drops +ok 1043 - resolveLayeredConfig: collisions and invalid additions both surface as drops + --- + duration_ms: 0.153503 + type: 'test' + ... +# Subtest: resolveLayeredConfig: no central layer is a pure passthrough (never validated) +ok 1044 - resolveLayeredConfig: no central layer is a pure passthrough (never validated) + --- + duration_ms: 0.265263 + type: 'test' + ... +# Subtest: defaultConfigPath resolves the v2 config basename under HYP_HOME +ok 1045 - defaultConfigPath resolves the v2 config basename under HYP_HOME + --- + duration_ms: 1.890574 + type: 'test' + ... +# Subtest: parseConfigShape accepts the supported v2 config shape +ok 1046 - parseConfigShape accepts the supported v2 config shape + --- + duration_ms: 0.759254 + type: 'test' + ... +# Subtest: parseConfigShape reports stable pointers for malformed config +ok 1047 - parseConfigShape reports stable pointers for malformed config + --- + duration_ms: 0.575414 + type: 'test' + ... +# Subtest: parseConfigShape preserves query.cache.maintenance.compact_batch_bytes +ok 1048 - parseConfigShape preserves query.cache.maintenance.compact_batch_bytes + --- + duration_ms: 0.224591 + type: 'test' + ... +# Subtest: parseConfigShape rejects a negative compact_batch_bytes +ok 1049 - parseConfigShape rejects a negative compact_batch_bytes + --- + duration_ms: 0.138661 + type: 'test' + ... +# Subtest: validateConfig catches cross-plugin and schedule errors +ok 1050 - validateConfig catches cross-plugin and schedule errors + --- + duration_ms: 1.890063 + type: 'test' + ... +# Subtest: validateConfig dispatches plugin-specific section validators +ok 1051 - validateConfig dispatches plugin-specific section validators + --- + duration_ms: 0.464286 + type: 'test' + ... +# Subtest: isCronExpression accepts narrow standard cron and rejects aliases +ok 1052 - isCronExpression accepts narrow standard cron and rejects aliases + --- + duration_ms: 0.356371 + type: 'test' + ... +# Subtest: diagnoseV1Config reports advisory product wiring gaps +ok 1053 - diagnoseV1Config reports advisory product wiring gaps + --- + duration_ms: 32.220322 + type: 'test' + ... +# Subtest: diagnoseV1Config falls back to first-party client descriptors +ok 1054 - diagnoseV1Config falls back to first-party client descriptors + --- + duration_ms: 0.612681 + type: 'test' + ... +# Subtest: diagnoseV1Config emits descriptor-derived upstream diagnostic kinds +ok 1055 - diagnoseV1Config emits descriptor-derived upstream diagnostic kinds + --- + duration_ms: 0.295289 + type: 'test' + ... +# Subtest: buildPluginCatalog derives capability metadata from bundled manifests +ok 1056 - buildPluginCatalog derives capability metadata from bundled manifests + --- + duration_ms: 6.831553 + type: 'test' + ... +# Subtest: buildPluginCatalog extracts client descriptors from manifests +ok 1057 - buildPluginCatalog extracts client descriptors from manifests + --- + duration_ms: 12.824932 + type: 'test' + ... +# Subtest: buildPluginCatalog reads contributes.picker into pickerDescriptors, keyed by row name +ok 1058 - buildPluginCatalog reads contributes.picker into pickerDescriptors, keyed by row name + --- + duration_ms: 0.527572 + type: 'test' + ... +# Subtest: buildPluginCatalog picker descriptors are first-manifest-wins on a name collision +ok 1059 - buildPluginCatalog picker descriptors are first-manifest-wins on a name collision + --- + duration_ms: 0.117369 + type: 'test' + ... +# Subtest: buildPluginCatalog collects known datasets from manifest contributions +ok 1060 - buildPluginCatalog collects known datasets from manifest contributions + --- + duration_ms: 8.608383 + type: 'test' + ... +# Subtest: buildPluginCatalog includes excluded gascity plugin as a catalog entry +ok 1061 - buildPluginCatalog includes excluded gascity plugin as a catalog entry + --- + duration_ms: 3.018478 + type: 'test' + ... +# Subtest: validateConfig uses catalog-derived metadata for sink validation +ok 1062 - validateConfig uses catalog-derived metadata for sink validation + --- + duration_ms: 6.166302 + type: 'test' + ... +# Subtest: validateConfig with catalog rejects @hypaware/central as blob-sink writer +ok 1063 - validateConfig with catalog rejects @hypaware/central as blob-sink writer + --- + duration_ms: 8.501181 + type: 'test' + ... +# Subtest: validateConfig with catalog accepts @hypaware/central as request sink +ok 1064 - validateConfig with catalog accepts @hypaware/central as request sink + --- + duration_ms: 3.313677 + type: 'test' + ... +# Subtest: buildPluginCatalog merges installed manifests with bundled +ok 1065 - buildPluginCatalog merges installed manifests with bundled + --- + duration_ms: 0.179633 + type: 'test' + ... +# Subtest: buildPluginCatalog bundled wins over installed on name collision +ok 1066 - buildPluginCatalog bundled wins over installed on name collision + --- + duration_ms: 0.093822 + type: 'test' + ... +# Subtest: diagnoseV1Config treats ChatGPT as a valid Codex upstream +ok 1067 - diagnoseV1Config treats ChatGPT as a valid Codex upstream + --- + duration_ms: 2.902221 + type: 'test' + ... +# Subtest: isSafeContributionName accepts plain basenames +ok 1068 - isSafeContributionName accepts plain basenames + --- + duration_ms: 0.799585 + type: 'test' + ... +# Subtest: isSafeContributionName rejects traversal and separators +ok 1069 - isSafeContributionName rejects traversal and separators + --- + duration_ms: 0.214155 + type: 'test' + ... +# Subtest: isSafeContributionName rejects non-strings +ok 1070 - isSafeContributionName rejects non-strings + --- + duration_ms: 0.117959 + type: 'test' + ... +# Subtest: isWithinDir accepts the base dir and paths beneath it +ok 1071 - isWithinDir accepts the base dir and paths beneath it + --- + duration_ms: 1.253445 + type: 'test' + ... +# Subtest: isWithinDir rejects paths that escape the base dir +ok 1072 - isWithinDir rejects paths that escape the base dir + --- + duration_ms: 0.295339 + type: 'test' + ... +# Subtest: seam records the proven-bound localEndpoint as the daemon attach endpoint +ok 1073 - seam records the proven-bound localEndpoint as the daemon attach endpoint + --- + duration_ms: 0.873407 + type: 'test' + ... +# Subtest: seam does NOT fall back to the configured listen when localEndpoint() throws (no URL for an unbound port) +ok 1074 - seam does NOT fall back to the configured listen when localEndpoint() throws (no URL for an unbound port) + --- + duration_ms: 0.179052 + type: 'test' + ... +# Subtest: seam is inert (no clients/endpoint) when the ai-gateway capability is absent +ok 1075 - seam is inert (no clients/endpoint) when the ai-gateway capability is absent + --- + duration_ms: 0.142556 + type: 'test' + ... +# Subtest: the daemon tick runs a sweep-bearing backfill contribution +ok 1076 - the daemon tick runs a sweep-bearing backfill contribution + --- + duration_ms: 410.27635 + type: 'test' + ... +# Subtest: tick fires only the sweep-bearing contributions that are cron-due +ok 1077 - tick fires only the sweep-bearing contributions that are cron-due + --- + duration_ms: 2.080883 + type: 'test' + ... +# Subtest: tick fires nothing when no contribution is due, and both when both are +ok 1078 - tick fires nothing when no contribution is due, and both when both are + --- + duration_ms: 0.489925 + type: 'test' + ... +# Subtest: the fired run gets the narrowed runner context, built from the daemon runtime fields +ok 1079 - the fired run gets the narrowed runner context, built from the daemon runtime fields + --- + duration_ms: 0.257402 + type: 'test' + ... +# Subtest: a rejected sweep run neither throws out of tick nor becomes an unhandled rejection +ok 1080 - a rejected sweep run neither throws out of tick nor becomes an unhandled rejection + --- + duration_ms: 13.015212 + type: 'test' + ... +# Subtest: tick does not block on a run that never settles +ok 1081 - tick does not block on a run that never settles + --- + duration_ms: 0.369992 + type: 'test' + ... +# Subtest: a second tick fires nothing while the first run is still in flight +ok 1082 - a second tick fires nothing while the first run is still in flight + --- + duration_ms: 0.425076 + type: 'test' + ... +# Subtest: a rejected run clears the in-flight guard so the next due tick still fires +ok 1083 - a rejected run clears the in-flight guard so the next due tick still fires + --- + duration_ms: 4.22405 + type: 'test' + ... +# Subtest: a malformed sweep cron is skipped, not thrown, and later providers still fire +ok 1084 - a malformed sweep cron is skipped, not thrown, and later providers still fire + --- + duration_ms: 0.36816 + type: 'test' + ... +# Subtest: createBackfillSweepDriver refuses to build without the registries it fires through +ok 1085 - createBackfillSweepDriver refuses to build without the registries it fires through + --- + duration_ms: 0.629497 + type: 'test' + ... +# Subtest: installDaemon upgrades an _npx binPath to a durable global bin before writing the service unit +ok 1086 - installDaemon upgrades an _npx binPath to a durable global bin before writing the service unit + --- + duration_ms: 8.231191 + type: 'test' + ... +# Subtest: explicit --bin bypasses the durable upgrade even for an _npx-looking path +ok 1087 - explicit --bin bypasses the durable upgrade even for an _npx-looking path + --- + duration_ms: 1.10536 + type: 'test' + ... +# Subtest: dry-run render never triggers the durable upgrade (renders the _npx path as-is) +ok 1088 - dry-run render never triggers the durable upgrade (renders the _npx path as-is) + --- + duration_ms: 0.37449 + type: 'test' + ... +# Subtest: hyp daemon install (no --bin) upgrades the process argv _npx bin to a durable global bin +ok 1089 - hyp daemon install (no --bin) upgrades the process argv _npx bin to a durable global bin + --- + duration_ms: 2.849721 + type: 'test' + ... +# Subtest: runDaemonInstall dry-run still surfaces the _npx bin without a global install (escape hatch) +ok 1090 - runDaemonInstall dry-run still surfaces the _npx bin without a global install (escape hatch) + --- + duration_ms: 1.170459 + type: 'test' + ... +# Subtest: reinstall over a loaded agent boots out, waits for release, then bootstraps once +ok 1091 - reinstall over a loaded agent boots out, waits for release, then bootstraps once + --- + duration_ms: 5.443564 + type: 'test' + ... +# Subtest: transient EIO (error 5) on bootstrap is retried until it succeeds +ok 1092 - transient EIO (error 5) on bootstrap is retried until it succeeds + --- + duration_ms: 0.820648 + type: 'test' + ... +# Subtest: a persistent EIO still throws after a bounded number of retries +ok 1093 - a persistent EIO still throws after a bounded number of retries + --- + duration_ms: 1.25671 + type: 'test' + ... +# Subtest: a genuine (non-transient) bootstrap failure fails fast without retry +ok 1094 - a genuine (non-transient) bootstrap failure fails fast without retry + --- + duration_ms: 0.820186 + type: 'test' + ... +# Subtest: a degraded maintenance tick sets the span status code, not just the attribute +ok 1095 - a degraded maintenance tick sets the span status code, not just the attribute + --- + duration_ms: 154.822565 + type: 'test' + ... +# Subtest: createReconcilePassScheduler runs exactly one pass per idle edge +ok 1096 - createReconcilePassScheduler runs exactly one pass per idle edge + --- + duration_ms: 0.969233 + type: 'test' + ... +# Subtest: createReconcilePassScheduler is single-flight and coalesces concurrent edges into one rerun +ok 1097 - createReconcilePassScheduler is single-flight and coalesces concurrent edges into one rerun + --- + duration_ms: 6.014161 + type: 'test' + ... +# Subtest: createReconcilePassScheduler.settle resolves immediately when no pass is in flight +ok 1098 - createReconcilePassScheduler.settle resolves immediately when no pass is in flight + --- + duration_ms: 0.22991 + type: 'test' + ... +# Subtest: createReconcilePassScheduler keeps scheduling after a pass throws +ok 1099 - createReconcilePassScheduler keeps scheduling after a pass throws + --- + duration_ms: 0.229439 + type: 'test' + ... +# Subtest: runDaemon runs the boot already-confirmed pass when a central layer is present and no probation is active +ok 1100 - runDaemon runs the boot already-confirmed pass when a central layer is present and no probation is active + --- + duration_ms: 45.238068 + type: 'test' + ... +# Subtest: runDaemon does not run the boot pass on a non-joined host (no central layer) +ok 1101 - runDaemon does not run the boot pass on a non-joined host (no central layer) + --- + duration_ms: 17.611708 + type: 'test' + ... +# Subtest: runDaemon does not run the boot pass while probation is still active (fresh-join case) +ok 1102 - runDaemon does not run the boot pass while probation is still active (fresh-join case) + --- + duration_ms: 18.557185 + type: 'test' + ... +# Subtest: the confirmation edge during active probation drives exactly one reconcile pass (fresh-join path) +ok 1103 - the confirmation edge during active probation drives exactly one reconcile pass (fresh-join path) + --- + duration_ms: 24.078467 + type: 'test' + ... +# Subtest: the daemon registers [attachHandler, backfillHandler] - attach first (LLP 0045 §Module / seam breakdown item 7) +ok 1104 - the daemon registers [attachHandler, backfillHandler] - attach first (LLP 0045 §Module / seam breakdown item 7) + --- + duration_ms: 1.647134 + type: 'test' + ... +# Subtest: the daemon resolves clientDescriptors/clients/endpoint from boot when the gateway is enabled +ok 1105 - the daemon resolves clientDescriptors/clients/endpoint from boot when the gateway is enabled + --- + duration_ms: 72.909616 + type: 'test' + ... +# Subtest: the daemon refreshes source details on every tick, so accruing details reach status.json +ok 1106 - the daemon refreshes source details on every tick, so accruing details reach status.json + --- + duration_ms: 153.614769 + type: 'test' + ... +# Subtest: a daemon that never reached a tick still refreshes source details before it stops +ok 1107 - a daemon that never reached a tick still refreshes source details before it stops + --- + duration_ms: 29.009182 + type: 'test' + ... +# Subtest: a source whose status() never settles cannot freeze the status file or the shutdown +ok 1108 - a source whose status() never settles cannot freeze the status file or the shutdown + --- + duration_ms: 123.231649 + type: 'test' + ... +# Subtest: the uninstall sweep reverses every attached client, claude and codex alike +ok 1109 - the uninstall sweep reverses every attached client, claude and codex alike + --- + duration_ms: 54.389937 + type: 'test' + ... +# Subtest: the uninstall sweep reports nothing on a machine with no client attached +ok 1110 - the uninstall sweep reports nothing on a machine with no client attached + --- + duration_ms: 9.873397 + type: 'test' + ... +# Subtest: one client attached is detached without the untouched one being reported +ok 1111 - one client attached is detached without the untouched one being reported + --- + duration_ms: 25.980989 + type: 'test' + ... +# Subtest: the sweep detaches openclaw like any other client, with nothing resolved from a daemon +ok 1112 - the sweep detaches openclaw like any other client, with nothing resolved from a daemon + --- + duration_ms: 13.147022 + type: 'test' + ... +# Subtest: a never-attached openclaw config with its own providers is an honest no-op, not a failure +ok 1113 - a never-attached openclaw config with its own providers is an honest no-op, not a failure + --- + duration_ms: 9.167804 + type: 'test' + ... +# Subtest: uninstall detaches openclaw on a machine whose daemon was already stopped +ok 1114 - uninstall detaches openclaw on a machine whose daemon was already stopped + --- + duration_ms: 20.390972 + type: 'test' + ... +# Subtest: a failed teardown detaches nothing: the gateway still answers, so the attaches stay +ok 1115 - a failed teardown detaches nothing: the gateway still answers, so the attaches stay + --- + duration_ms: 19.723478 + type: 'test' + ... +# Subtest: one wedged client is a collected failure with a remedy; the rest still detach +ok 1116 - one wedged client is a collected failure with a remedy; the rest still detach + --- + duration_ms: 21.868187 + type: 'test' + ... +# Subtest: the undo warnings the quiet sweep collects reach the uninstall output +ok 1117 - the undo warnings the quiet sweep collects reach the uninstall output + --- + duration_ms: 21.134753 + type: 'test' + ... +# Subtest: writeStatusFile writes an atomic readable status snapshot +ok 1118 - writeStatusFile writes an atomic readable status snapshot + --- + duration_ms: 4.728307 + type: 'test' + ... +# Subtest: readStatusFile returns null before the daemon has written status +ok 1119 - readStatusFile returns null before the daemon has written status + --- + duration_ms: 1.805634 + type: 'test' + ... +# Subtest: resolveClientSettingsPath sanitizes client env override names +ok 1120 - resolveClientSettingsPath sanitizes client env override names + --- + duration_ms: 0.338895 + type: 'test' + ... +# Subtest: resolveClientSettingsPath rejects an absolute settings_file rather than re-anchoring it +ok 1121 - resolveClientSettingsPath rejects an absolute settings_file rather than re-anchoring it + --- + duration_ms: 0.586241 + type: 'test' + ... +# Subtest: resolveClientSettingsPath rejects a settings_file that climbs out of its base +ok 1122 - resolveClientSettingsPath rejects a settings_file that climbs out of its base + --- + duration_ms: 0.338935 + type: 'test' + ... +# Subtest: resolveClientSettingsPath does not mistake a prefix-sharing sibling for the base +ok 1123 - resolveClientSettingsPath does not mistake a prefix-sharing sibling for the base + --- + duration_ms: 0.404014 + type: 'test' + ... +# Subtest: resolveClientSettingsPath returns the absolute path it checked +ok 1124 - resolveClientSettingsPath returns the absolute path it checked + --- + duration_ms: 0.188386 + type: 'test' + ... +# Subtest: probeClientAttachFromDescriptor errors on an absolute settings_file instead of probing $HOME +ok 1125 - probeClientAttachFromDescriptor errors on an absolute settings_file instead of probing $HOME + --- + duration_ms: 9.199262 + type: 'test' + ... +# Subtest: probeClientAttachFromDescriptor reads JSON attach markers +ok 1126 - probeClientAttachFromDescriptor reads JSON attach markers + --- + duration_ms: 2.993419 + type: 'test' + ... +# Subtest: probeClientAttachFromDescriptor honors sanitized TOML home overrides +ok 1127 - probeClientAttachFromDescriptor honors sanitized TOML home overrides + --- + duration_ms: 1.596146 + type: 'test' + ... +# Subtest: probeClientAttachFromDescriptor reads json_path attach markers when the entry is present +ok 1128 - probeClientAttachFromDescriptor reads json_path attach markers when the entry is present + --- + duration_ms: 4.920088 + type: 'test' + ... +# Subtest: probeClientAttachFromDescriptor reports json_path as not attached when the entry is absent +ok 1129 - probeClientAttachFromDescriptor reports json_path as not attached when the entry is absent + --- + duration_ms: 1.362431 + type: 'test' + ... +# Subtest: probeClientAttachFromDescriptor reports json_path as not attached when the marker header is wrong +ok 1130 - probeClientAttachFromDescriptor reports json_path as not attached when the marker header is wrong + --- + duration_ms: 1.44702 + type: 'test' + ... +# Subtest: renderDaemonInstall renders a deterministic systemd dry-run payload +ok 1131 - renderDaemonInstall renders a deterministic systemd dry-run payload + --- + duration_ms: 0.828489 + type: 'test' + ... +# Subtest: renderDaemonInstall renders a deterministic LaunchAgent dry-run payload +ok 1132 - renderDaemonInstall renders a deterministic LaunchAgent dry-run payload + --- + duration_ms: 0.491397 + type: 'test' + ... +# Subtest: installers default to relaunch-on-exit (staged restart requirement, LLP 0017) +ok 1133 - installers default to relaunch-on-exit (staged restart requirement, LLP 0017) + --- + duration_ms: 3.46726 + type: 'test' + ... +# Subtest: serviceDaemonStatus reports "not installed" without probing the service manager +ok 1134 - serviceDaemonStatus reports "not installed" without probing the service manager + --- + duration_ms: 2.328109 + type: 'test' + ... +# Subtest: serviceDaemonStatus degrades to "not loaded" when the service manager cannot run +ok 1135 - serviceDaemonStatus degrades to "not loaded" when the service manager cannot run + --- + duration_ms: 2.550276 + type: 'test' + ... +# Subtest: the staged-restart exit code is distinct from success and error exits +ok 1136 - the staged-restart exit code is distinct from success and error exits + --- + duration_ms: 0.255679 + type: 'test' + ... +# Subtest: runDaemon reload refreshes plugin config before source.reload +ok 1137 - runDaemon reload refreshes plugin config before source.reload + --- + duration_ms: 58.64195 + type: 'test' + ... +# Subtest: runDaemon reload re-merges the central layer (does not re-read local alone) - \#111 regression +ok 1138 - runDaemon reload re-merges the central layer (does not re-read local alone) - \#111 regression + --- + duration_ms: 30.141082 + type: 'test' + ... +# Subtest: runDaemon health event derives from aggregate state and excludes failed sources (\#138) +ok 1139 - runDaemon health event derives from aggregate state and excludes failed sources (\#138) + --- + duration_ms: 27.866544 + type: 'test' + ... +# Subtest: manual `hyp detach` retracts the attach marker so a later join re-attaches (\#217) +ok 1140 - manual `hyp detach` retracts the attach marker so a later join re-attaches (\#217) + --- + duration_ms: 56.761332 + type: 'test' + ... +# Subtest: detach of a probe-HAVING client with already-clean settings (changed:false) still clears its stale marker +ok 1141 - detach of a probe-HAVING client with already-clean settings (changed:false) still clears its stale marker + --- + duration_ms: 9.194124 + type: 'test' + ... +# Subtest: detach of a probe-LESS client does NOT clear its marker (mirrors reverse()'s \#212 exception) +ok 1142 - detach of a probe-LESS client does NOT clear its marker (mirrors reverse()'s \#212 exception) + --- + duration_ms: 19.762757 + type: 'test' + ... +# Subtest: detach still succeeds when the marker retraction throws (best-effort, not a detach failure) +ok 1143 - detach still succeeds when the marker retraction throws (best-effort, not a detach failure) + --- + duration_ms: 17.04716 + type: 'test' + ... +# Subtest: detach removes the assets its attach marker records, and leaves manual copies alone +ok 1144 - detach removes the assets its attach marker records, and leaves manual copies alone + --- + duration_ms: 9.068223 + type: 'test' + ... +# Subtest: detach names the assets it refuses to remove, and does not keep the marker for them +ok 1145 - detach names the assets it refuses to remove, and does not keep the marker for them + --- + duration_ms: 5.834398 + type: 'test' + ... +# Subtest: detects claude when ~/.claude exists +ok 1146 - detects claude when ~/.claude exists + --- + duration_ms: 5.994501 + type: 'test' + ... +# Subtest: detects codex when ~/.codex exists +ok 1147 - detects codex when ~/.codex exists + --- + duration_ms: 5.596046 + type: 'test' + ... +# Subtest: detects both when both config homes exist +ok 1148 - detects both when both config homes exist + --- + duration_ms: 1.593662 + type: 'test' + ... +# Subtest: detects nothing in an empty home +ok 1149 - detects nothing in an empty home + --- + duration_ms: 0.719664 + type: 'test' + ... +# Subtest: honors $CODEX_HOME override for codex detection +ok 1150 - honors $CODEX_HOME override for codex detection + --- + duration_ms: 1.036916 + type: 'test' + ... +# Subtest: a plain file (not a directory) at the config-home path does not count +ok 1151 - a plain file (not a directory) at the config-home path does not count + --- + duration_ms: 2.404364 + type: 'test' + ... +# Subtest: a picker row with no detect probe is never detected +ok 1152 - a picker row with no detect probe is never detected + --- + duration_ms: 0.606922 + type: 'test' + ... +# Subtest: an app_bundle probe detects presence by stat +ok 1153 - an app_bundle probe detects presence by stat + --- + duration_ms: 0.862541 + type: 'test' + ... +# Subtest: an app_bundle probe is not present when the path does not exist +ok 1154 - an app_bundle probe is not present when the path does not exist + --- + duration_ms: 1.650599 + type: 'test' + ... +# Subtest: a path probe detects a literal directory +ok 1155 - a path probe detects a literal directory + --- + duration_ms: 1.152652 + type: 'test' + ... +# Subtest: a path probe honors a $FOO_HOME-style env override of its literal path +ok 1156 - a path probe honors a $FOO_HOME-style env override of its literal path + --- + duration_ms: 0.865325 + type: 'test' + ... +# Subtest: a probe failure does not surface, and other rows still detect +ok 1157 - a probe failure does not surface, and other rows still detect + --- + duration_ms: 3.222397 + type: 'test' + ... +# Subtest: dispatch miss on an inactive bundled plugin command reports unavailable + repair, not unknown +ok 1158 - dispatch miss on an inactive bundled plugin command reports unavailable + repair, not unknown + --- + duration_ms: 26.586379 + type: 'test' + ... +# Subtest: dispatch miss on a genuine typo still gets the generic unknown-command message +ok 1159 - dispatch miss on a genuine typo still gets the generic unknown-command message + --- + duration_ms: 4.80312 + type: 'test' + ... +# Subtest: dispatch miss on a plugin present-but-disabled in the local config advises enabling it, not adding it +ok 1160 - dispatch miss on a plugin present-but-disabled in the local config advises enabling it, not adding it + --- + duration_ms: 4.397204 + type: 'test' + ... +# Subtest: dispatch miss on a plugin disabled by the central layer says it cannot be enabled locally +ok 1161 - dispatch miss on a plugin disabled by the central layer says it cannot be enabled locally + --- + duration_ms: 7.541663 + type: 'test' + ... +# Subtest: a command whose plugin IS active is unaffected (renders group help, no availability error) +ok 1162 - a command whose plugin IS active is unaffected (renders group help, no availability error) + --- + duration_ms: 6.898114 + type: 'test' + ... +# Subtest: deadline is the next local 11:59pm when that is comfortably away +ok 1163 - deadline is the next local 11:59pm when that is comfortably away + --- + duration_ms: 1.090998 + type: 'test' + ... +# Subtest: deadline rolls to the following day when same-day 11:59pm is under the 4-hour floor +ok 1164 - deadline rolls to the following day when same-day 11:59pm is under the 4-hour floor + --- + duration_ms: 1.048714 + type: 'test' + ... +# Subtest: deadline exactly at the 4-hour boundary does not roll (floor is strict "less than") +ok 1165 - deadline exactly at the 4-hour boundary does not roll (floor is strict "less than") + --- + duration_ms: 0.162427 + type: 'test' + ... +# Subtest: an enrollment after 11:59pm rolls to the next day (past same-day deadline is under the floor) +ok 1166 - an enrollment after 11:59pm rolls to the next day (past same-day deadline is under the floor) + --- + duration_ms: 0.122566 + type: 'test' + ... +# Subtest: the deadline always lands on a local 23:59 and is strictly in the future (DST-agnostic invariant) +ok 1167 - the deadline always lands on a local 23:59 and is strictly in the future (DST-agnostic invariant) + --- + duration_ms: 0.234456 + type: 'test' + ... +# Subtest: the rendered deadline names its time zone, never a bare wall-clock time (LLP 0100 R1) +ok 1168 - the rendered deadline names its time zone, never a bare wall-clock time (LLP 0100 R1) + --- + duration_ms: 17.056955 + type: 'test' + ... +# Subtest: the rendered deadline reads as a full local date, time and zone in a pinned zone +ok 1169 - the rendered deadline reads as a full local date, time and zone in a pinned zone + --- + duration_ms: 0.905897 + type: 'test' + ... +# Subtest: a written marker reads back its future deadline; the deadline is stored, not derived from mtime +ok 1170 - a written marker reads back its future deadline; the deadline is stored, not derived from mtime + --- + duration_ms: 6.997636 + type: 'test' + ... +# Subtest: a touched marker (mtime bumped) keeps its original deadline - incidental writes cannot shorten or extend the hold +ok 1171 - a touched marker (mtime bumped) keeps its original deadline - incidental writes cannot shorten or extend the hold + --- + duration_ms: 1.647313 + type: 'test' + ... +# Subtest: a past deadline reads as absent and is opportunistically unlinked (bounded hold, LLP 0101) +ok 1172 - a past deadline reads as absent and is opportunistically unlinked (bounded hold, LLP 0101) + --- + duration_ms: 2.199473 + type: 'test' + ... +# Subtest: an unreadable marker reads as absent, never a wedge (fail-open by design) +ok 1173 - an unreadable marker reads as absent, never a wedge (fail-open by design) + --- + duration_ms: 0.768137 + type: 'test' + ... +# Subtest: a malformed marker (bad JSON or missing deadline) reads as absent (fail-open) +ok 1174 - a malformed marker (bad JSON or missing deadline) reads as absent (fail-open) + --- + duration_ms: 1.896632 + type: 'test' + ... +# Subtest: a live first-sync hold holds the whole sink tick (no sink exports); the tick after the deadline exports +ok 1175 - a live first-sync hold holds the whole sink tick (no sink exports); the tick after the deadline exports + --- + duration_ms: 8.505858 + type: 'test' + ... +# Subtest: an expired marker does not hold the tick (a bounded hold can never stall exports past its deadline) +ok 1176 - an expired marker does not hold the tick (a bounded hold can never stall exports past its deadline) + --- + duration_ms: 11.682797 + type: 'test' + ... +# Subtest: no marker means no hold: exports proceed on the first tick +ok 1177 - no marker means no hold: exports proceed on the first tick + --- + duration_ms: 1.016605 + type: 'test' + ... +# Subtest: resolves immediately when nothing is buffered +ok 1178 - resolves immediately when nothing is buffered + --- + duration_ms: 0.718792 + type: 'test' + ... +# Subtest: resolves via the write callback once buffered output drains +ok 1179 - resolves via the write callback once buffered output drains + --- + duration_ms: 0.203088 + type: 'test' + ... +# Subtest: resolves on error (EPIPE) instead of hanging +ok 1180 - resolves on error (EPIPE) instead of hanging + --- + duration_ms: 0.141886 + type: 'test' + ... +# Subtest: does not double-resolve when both error and write callback fire +ok 1181 - does not double-resolve when both error and write callback fire + --- + duration_ms: 0.247626 + type: 'test' + ... +# Subtest: the preference lives beside its sibling stores under HYP_HOME state +ok 1182 - the preference lives beside its sibling stores under HYP_HOME state + --- + duration_ms: 5.178722 + type: 'test' + ... +# Subtest: an absent preference reads as the product default: new folders sync, nobody is asked +ok 1183 - an absent preference reads as the product default: new folders sync, nobody is asked + --- + duration_ms: 2.527562 + type: 'test' + ... +# Subtest: a written preference round-trips, and both modes are writable +ok 1184 - a written preference round-trips, and both modes are writable + --- + duration_ms: 4.668215 + type: 'test' + ... +# Subtest: an unknown mode is refused at the write, never persisted +ok 1185 - an unknown mode is refused at the write, never persisted + --- + duration_ms: 0.498168 + type: 'test' + ... +# Subtest: a corrupt or unrecognized file throws rather than resolving to a mode by accident +ok 1186 - a corrupt or unrecognized file throws rather than resolving to a mode by accident + --- + duration_ms: 10.077536 + type: 'test' + ... +# Subtest: the unreadable error carries the file path and an error_kind for telemetry +ok 1187 - the unreadable error carries the file path and an error_kind for telemetry + --- + duration_ms: 1.119772 + type: 'test' + ... +# Subtest: resolveEncodeSettings defaults to SNAPPY with no explicit compressors +ok 1188 - resolveEncodeSettings defaults to SNAPPY with no explicit compressors + --- + duration_ms: 0.770331 + type: 'test' + ... +# Subtest: resolveEncodeSettings honours codec=ZSTD (case-insensitive) when zstd is available +ok 1189 - resolveEncodeSettings honours codec=ZSTD (case-insensitive) when zstd is available + --- + duration_ms: 1.269219 + type: 'test' + ... +# Subtest: resolveEncodeSettings falls back to SNAPPY and warns when ZSTD is unavailable +ok 1190 - resolveEncodeSettings falls back to SNAPPY and warns when ZSTD is unavailable # SKIP + --- + duration_ms: 0.082626 + type: 'test' + ... +# Subtest: resolveEncodeSettings warns and falls back on an unknown codec +ok 1191 - resolveEncodeSettings warns and falls back on an unknown codec + --- + duration_ms: 0.200876 + type: 'test' + ... +# Subtest: resolveEncodeSettings passes through a positive page_size and ignores invalid ones +ok 1192 - resolveEncodeSettings passes through a positive page_size and ignores invalid ones + --- + duration_ms: 0.287848 + type: 'test' + ... +# Subtest: isNpxBinPath detects npm _npx cache entries +ok 1193 - isNpxBinPath detects npm _npx cache entries + --- + duration_ms: 0.775539 + type: 'test' + ... +# Subtest: ensureDurableBinForNpx installs the current package globally and returns the global bin +ok 1194 - ensureDurableBinForNpx installs the current package globally and returns the global bin + --- + duration_ms: 8.793885 + type: 'test' + ... +# Subtest: ensureDurableBinForNpx leaves stable bin paths untouched +ok 1195 - ensureDurableBinForNpx leaves stable bin paths untouched + --- + duration_ms: 0.259965 + type: 'test' + ... +# Subtest: ensureDurableBinForNpx reports npm install failures with a repair command +ok 1196 - ensureDurableBinForNpx reports npm install failures with a repair command + --- + duration_ms: 7.598288 + type: 'test' + ... +# Subtest: a verb with help projects it onto the command registration +ok 1197 - a verb with help projects it onto the command registration + --- + duration_ms: 0.884024 + type: 'test' + ... +# Subtest: a verb without help contributes no help key at all +ok 1198 - a verb without help contributes no help key at all + --- + duration_ms: 0.17768 + type: 'test' + ... +# Subtest: the verb help passthrough does not disturb summary or usage +ok 1199 - the verb help passthrough does not disturb summary or usage + --- + duration_ms: 0.176268 + type: 'test' + ... +# Subtest: registerGroup stores a description without adding a command +ok 1200 - registerGroup stores a description without adding a command + --- + duration_ms: 0.182267 + type: 'test' + ... +# Subtest: registerGroup rejects a missing name and non-string prose +ok 1201 - registerGroup rejects a missing name and non-string prose + --- + duration_ms: 0.348219 + type: 'test' + ... +# Subtest: re-registering a group replaces it rather than throwing +ok 1202 - re-registering a group replaces it rather than throwing + --- + duration_ms: 0.127524 + type: 'test' + ... +# Subtest: group help renders the header and paragraph above the subcommand table +ok 1203 - group help renders the header and paragraph above the subcommand table + --- + duration_ms: 0.283911 + type: 'test' + ... +# Subtest: a group with help but no summary renders no header line +ok 1204 - a group with help but no summary renders no header line + --- + duration_ms: 0.133793 + type: 'test' + ... +# Subtest: an undescribed group still renders its table, as before +ok 1205 - an undescribed group still renders its table, as before + --- + duration_ms: 0.278453 + type: 'test' + ... +# Subtest: no tracked file carries an em dash +ok 1206 - no tracked file carries an em dash + --- + duration_ms: 67.684795 + type: 'test' + ... +# Subtest: the scan actually reads the tree +ok 1207 - the scan actually reads the tree + --- + duration_ms: 3.381449 + type: 'test' + ... +# Subtest: cache and parquet backends answer the corpus identically, and answer it the way SQL does +ok 1208 - cache and parquet backends answer the corpus identically, and answer it the way SQL does + --- + duration_ms: 150.165617 + type: 'test' + ... +# Subtest: filtered aggregates take the same NULL semantics as the row scan +ok 1209 - filtered aggregates take the same NULL semantics as the row scan + --- + duration_ms: 10.974249 + type: 'test' + ... +# Subtest: the cache column stream reports appliedWhere honestly +ok 1210 - the cache column stream reports appliedWhere honestly + --- + duration_ms: 6.900027 + type: 'test' + ... +# Subtest: both backends agree on which predicates are converted and which are declined +ok 1211 - both backends agree on which predicates are converted and which are declined + --- + duration_ms: 5.120373 + type: 'test' + ... +# Subtest: LIMIT and OFFSET are held back under a WHERE and the slice still lands on the matching rows +ok 1212 - LIMIT and OFFSET are held back under a WHERE and the slice still lands on the matching rows + --- + duration_ms: 12.121864 + type: 'test' + ... +# Subtest: a filtered cache scan still prunes whole data files +ok 1213 - a filtered cache scan still prunes whole data files + --- + duration_ms: 8.635765 + type: 'test' + ... +# Subtest: hyp ignore writes a self-documenting .hypignore at the git repo root +ok 1214 - hyp ignore writes a self-documenting .hypignore at the git repo root + --- + duration_ms: 9.326376 + type: 'test' + ... +# Subtest: hyp ignore without a repo writes .hypignore at the cwd +ok 1215 - hyp ignore without a repo writes .hypignore at the cwd + --- + duration_ms: 2.698431 + type: 'test' + ... +# Subtest: hyp ignore [path] writes exactly at the explicit path, overriding the repo root +ok 1216 - hyp ignore [path] writes exactly at the explicit path, overriding the repo root + --- + duration_ms: 12.094893 + type: 'test' + ... +# Subtest: hyp ignore is idempotent: re-ignoring an already-ignored path is a no-op success +ok 1217 - hyp ignore is idempotent: re-ignoring an already-ignored path is a no-op success + --- + duration_ms: 2.663068 + type: 'test' + ... +# Subtest: hyp unignore removes the governing .hypignore and is idempotent +ok 1218 - hyp unignore removes the governing .hypignore and is idempotent + --- + duration_ms: 2.494782 + type: 'test' + ... +# Subtest: hyp ignore --check reports an ignored path, its governor, and residual count +ok 1219 - hyp ignore --check reports an ignored path, its governor, and residual count + --- + duration_ms: 5.636407 + type: 'test' + ... +# Subtest: hyp ignore --check reports a clean path as not ignored with zero residue +ok 1220 - hyp ignore --check reports a clean path as not ignored with zero residue + --- + duration_ms: 1.241548 + type: 'test' + ... +# Subtest: hyp ignore --check --json emits a machine-readable status +ok 1221 - hyp ignore --check --json emits a machine-readable status + --- + duration_ms: 2.024107 + type: 'test' + ... +# Subtest: hyp ignore --check counts already-cached rows under the scope (LIKE superset, refined exactly) +ok 1222 - hyp ignore --check counts already-cached rows under the scope (LIKE superset, refined exactly) + --- + duration_ms: 6.823261 + type: 'test' + ... +# Subtest: hyp ignore --local-only adds the git repo root to the machine-local list, never touching the repo +ok 1223 - hyp ignore --local-only adds the git repo root to the machine-local list, never touching the repo + --- + duration_ms: 28.289377 + type: 'test' + ... +# Subtest: hyp ignore --local-only [path] overrides the repo-root default +ok 1224 - hyp ignore --local-only [path] overrides the repo-root default + --- + duration_ms: 6.511978 + type: 'test' + ... +# Subtest: hyp ignore --local-only accepts a nonexistent, non-repo path (R4) +ok 1225 - hyp ignore --local-only accepts a nonexistent, non-repo path (R4) + --- + duration_ms: 2.919467 + type: 'test' + ... +# Subtest: hyp ignore --local-only is idempotent: adding the same directory twice is a no-op success +ok 1226 - hyp ignore --local-only is idempotent: adding the same directory twice is a no-op success + --- + duration_ms: 5.78296 + type: 'test' + ... +# Subtest: hyp ignore --local-only on a directory under an already-listed ancestor is a no-op (ancestor-governed) +ok 1227 - hyp ignore --local-only on a directory under an already-listed ancestor is a no-op (ancestor-governed) + --- + duration_ms: 4.905897 + type: 'test' + ... +# Subtest: hyp ignore --local-only on a path already governed by a stricter .hypignore is a no-op naming the dotfile +ok 1228 - hyp ignore --local-only on a path already governed by a stricter .hypignore is a no-op naming the dotfile + --- + duration_ms: 1.926769 + type: 'test' + ... +# Subtest: hyp unignore --local-only removes an exact entry and is idempotent +ok 1229 - hyp unignore --local-only removes an exact entry and is idempotent + --- + duration_ms: 4.856331 + type: 'test' + ... +# Subtest: hyp unignore --local-only [path] removes every governing (equal-or-ancestor) entry +ok 1230 - hyp unignore --local-only [path] removes every governing (equal-or-ancestor) entry + --- + duration_ms: 3.885395 + type: 'test' + ... +# Subtest: hyp unignore --local-only does not remove a sibling that merely shares a string prefix +ok 1231 - hyp unignore --local-only does not remove a sibling that merely shares a string prefix + --- + duration_ms: 2.945958 + type: 'test' + ... +# Subtest: hyp ignore --private on the real path upgrades an entry declared by its symlink spelling, not duplicating it +ok 1232 - hyp ignore --private on the real path upgrades an entry declared by its symlink spelling, not duplicating it + --- + duration_ms: 5.445016 + type: 'test' + ... +# Subtest: hyp unignore --local-only by symlink spelling removes an entry declared canonically +ok 1233 - hyp unignore --local-only by symlink spelling removes an entry declared canonically + --- + duration_ms: 5.762248 + type: 'test' + ... +# Subtest: hyp ignore --check reports the local-only class and the list file as the governor +ok 1234 - hyp ignore --check reports the local-only class and the list file as the governor + --- + duration_ms: 6.709218 + type: 'test' + ... +# Subtest: hyp ignore --check --json reports local-only class + governedBy pointing at the list file +ok 1235 - hyp ignore --check --json reports local-only class + governedBy pointing at the list file + --- + duration_ms: 5.938185 + type: 'test' + ... +# Subtest: hyp ignore --check still reports the dotfile ignore class + its governing file (unaffected by an empty list) +ok 1236 - hyp ignore --check still reports the dotfile ignore class + its governing file (unaffected by an empty list) + --- + duration_ms: 1.567052 + type: 'test' + ... +# Subtest: hyp ignore --check counts residual cached rows for a local-only scope (recorded, withheld from forwarding) +ok 1237 - hyp ignore --check counts residual cached rows for a local-only scope (recorded, withheld from forwarding) + --- + duration_ms: 9.886206 + type: 'test' + ... +# Subtest: hyp ignore --check on a clean path with a populated-but-non-matching list reports full/no residue +ok 1238 - hyp ignore --check on a clean path with a populated-but-non-matching list reports full/no residue + --- + duration_ms: 4.325314 + type: 'test' + ... +# Subtest: hyp ignore --check scopes the residual count to the entry the gate used, not the longest declared string +ok 1239 - hyp ignore --check scopes the residual count to the entry the gate used, not the longest declared string + --- + duration_ms: 5.056445 + type: 'test' + ... +# Subtest: hyp ignore --private marks the repo root ignore in the machine-local store, never touching the repo +ok 1240 - hyp ignore --private marks the repo root ignore in the machine-local store, never touching the repo + --- + duration_ms: 23.263979 + type: 'test' + ... +# Subtest: hyp ignore --private is idempotent: marking twice is a no-op success +ok 1241 - hyp ignore --private is idempotent: marking twice is a no-op success + --- + duration_ms: 3.468292 + type: 'test' + ... +# Subtest: hyp ignore --private upgrades an existing local-only entry to ignore +ok 1242 - hyp ignore --private upgrades an existing local-only entry to ignore + --- + duration_ms: 4.027311 + type: 'test' + ... +# Subtest: hyp ignore --private on a path already governed by a stricter .hypignore is a no-op naming the dotfile +ok 1243 - hyp ignore --private on a path already governed by a stricter .hypignore is a no-op naming the dotfile + --- + duration_ms: 1.750891 + type: 'test' + ... +# Subtest: hyp ignore --sync writes an explicit full entry (the "asked; syncs" marker) +ok 1244 - hyp ignore --sync writes an explicit full entry (the "asked; syncs" marker) + --- + duration_ms: 6.355821 + type: 'test' + ... +# Subtest: hyp ignore --sync is idempotent against an existing explicit full entry, but not against the mere implicit default +ok 1245 - hyp ignore --sync is idempotent against an existing explicit full entry, but not against the mere implicit default + --- + duration_ms: 4.586911 + type: 'test' + ... +# Subtest: hyp ignore --sync downgrades an existing ignore entry back to full (re-marking is not destructive of cached rows) +ok 1246 - hyp ignore --sync downgrades an existing ignore entry back to full (re-marking is not destructive of cached rows) + --- + duration_ms: 6.979628 + type: 'test' + ... +# Subtest: hyp ignore --sync keeps its exact deprecated-alias confirmation, internal class and store path included +ok 1247 - hyp ignore --sync keeps its exact deprecated-alias confirmation, internal class and store path included + --- + duration_ms: 2.989343 + type: 'test' + ... +# Subtest: hyp ignore --check keeps its exact deprecated-alias human output (resolver class, real store path) +ok 1248 - hyp ignore --check keeps its exact deprecated-alias human output (resolver class, real store path) + --- + duration_ms: 3.46743 + type: 'test' + ... +# Subtest: hyp ignore --check on an unmarked directory keeps its exact bare class line, no implicit-default suffix +ok 1249 - hyp ignore --check on an unmarked directory keeps its exact bare class line, no implicit-default suffix + --- + duration_ms: 1.366577 + type: 'test' + ... +# Subtest: hyp ignore rejects combining --local-only, --private, and --sync +ok 1250 - hyp ignore rejects combining --local-only, --private, and --sync + --- + duration_ms: 0.556136 + type: 'test' + ... +# Subtest: hyp unignore --private removes an ignore entry and is idempotent, leaving other classes untouched +ok 1251 - hyp unignore --private removes an ignore entry and is idempotent, leaving other classes untouched + --- + duration_ms: 10.448341 + type: 'test' + ... +# Subtest: hyp unignore --sync removes an explicit full entry and is idempotent +ok 1252 - hyp unignore --sync removes an explicit full entry and is idempotent + --- + duration_ms: 4.647774 + type: 'test' + ... +# Subtest: hyp ignore --check names the machine-local source for a --private mark +ok 1253 - hyp ignore --check names the machine-local source for a --private mark + --- + duration_ms: 7.712482 + type: 'test' + ... +# Subtest: hyp ignore --check names the dotfile source when a .hypignore governs +ok 1254 - hyp ignore --check names the dotfile source when a .hypignore governs + --- + duration_ms: 6.179312 + type: 'test' + ... +# Subtest: hyp ignore --check names no source when nothing governs (the implicit default) +ok 1255 - hyp ignore --check names no source when nothing governs (the implicit default) + --- + duration_ms: 0.734316 + type: 'test' + ... +# Subtest: bare hyp ignore [path] (no flags) still writes the LLP 0049 dotfile, unaffected by --private/--sync existing +ok 1256 - bare hyp ignore [path] (no flags) still writes the LLP 0049 dotfile, unaffected by --private/--sync existing + --- + duration_ms: 2.308659 + type: 'test' + ... +# Subtest: allocator hands out a strictly increasing run from 1 +ok 1257 - allocator hands out a strictly increasing run from 1 + --- + duration_ms: 14.923062 + type: 'test' + ... +# Subtest: reserve-before-stamp: persisted nextSeq is always ahead of the last issued seq +ok 1258 - reserve-before-stamp: persisted nextSeq is always ahead of the last issued seq + --- + duration_ms: 6.092319 + type: 'test' + ... +# Subtest: allocator never regresses across a restart and skips the abandoned block tail +ok 1259 - allocator never regresses across a restart and skips the abandoned block tail + --- + duration_ms: 3.2108 + type: 'test' + ... +# Subtest: concurrent next() calls never collide (single allocator, parallel flushes) +ok 1260 - concurrent next() calls never collide (single allocator, parallel flushes) + --- + duration_ms: 24.871362 + type: 'test' + ... +# Subtest: default block size is a positive integer and rejects bad input +ok 1261 - default block size is a positive integer and rejects bad input + --- + duration_ms: 0.938657 + type: 'test' + ... +# Subtest: streamFlushFile stamps a monotonic _hyp_ingest_seq and adds the column +ok 1262 - streamFlushFile stamps a monotonic _hyp_ingest_seq and adds the column + --- + duration_ms: 5.611549 + type: 'test' + ... +# Subtest: streamFlushFile leaves seq null and still declares the column when no allocator is wired +ok 1263 - streamFlushFile leaves seq null and still declares the column when no allocator is wired + --- + duration_ms: 4.340897 + type: 'test' + ... +# Subtest: seq survives a flush into Iceberg, increases per row, and is stripped from readRows +ok 1264 - seq survives a flush into Iceberg, increases per row, and is stripped from readRows + --- + duration_ms: 46.683885 + type: 'test' + ... +# Subtest: renderConfigSummary: a local install reads as set up, not fleet-managed +ok 1265 - renderConfigSummary: a local install reads as set up, not fleet-managed + --- + duration_ms: 1.004366 + type: 'test' + ... +# Subtest: renderConfigSummary: a fleet-managed install marks each client synced vs local only +ok 1266 - renderConfigSummary: a fleet-managed install marks each client synced vs local only + --- + duration_ms: 0.222198 + type: 'test' + ... +# Subtest: hyp init on a configured install fronts the picker with the summary menu +ok 1267 - hyp init on a configured install fronts the picker with the summary menu + --- + duration_ms: 166.028587 + type: 'test' + ... +# Subtest: hyp init first run presents the pathway fork; a bare enter quits untouched +ok 1268 - hyp init first run presents the pathway fork; a bare enter quits untouched + --- + duration_ms: 28.187442 + type: 'test' + ... +# Subtest: hyp init: choosing "See full status" renders the status report and exits 0 +ok 1269 - hyp init: choosing "See full status" renders the status report and exits 0 + --- + duration_ms: 37.924282 + type: 'test' + ... +# Subtest: omitting --export defaults to local-parquet (origin=default) +ok 1270 - omitting --export defaults to local-parquet (origin=default) + --- + duration_ms: 1.113182 + type: 'test' + ... +# Subtest: --yes no longer changes the omitted-export default +ok 1271 - --yes no longer changes the omitted-export default + --- + duration_ms: 0.128345 + type: 'test' + ... +# Subtest: explicit --export keep-local is honored (origin=user) +ok 1272 - explicit --export keep-local is honored (origin=user) + --- + duration_ms: 0.09852 + type: 'test' + ... +# Subtest: explicit --export configure-later is honored (origin=user) +ok 1273 - explicit --export configure-later is honored (origin=user) + --- + duration_ms: 0.091449 + type: 'test' + ... +# Subtest: explicit --export local-parquet still reports origin=user +ok 1274 - explicit --export local-parquet still reports origin=user + --- + duration_ms: 0.154375 + type: 'test' + ... +# Subtest: core's default gateway endpoint tracks the ai-gateway plugin's DEFAULT_LISTEN +ok 1275 - core's default gateway endpoint tracks the ai-gateway plugin's DEFAULT_LISTEN + --- + duration_ms: 0.756911 + type: 'test' + ... +# Subtest: the picker writer leaves the gateway listen unset so the fixed default applies +ok 1276 - the picker writer leaves the gateway listen unset so the fixed default applies + --- + duration_ms: 24.53448 + type: 'test' + ... +# Subtest: the claude-and-otel-local preset leaves the gateway listen unset so the fixed default applies +ok 1277 - the claude-and-otel-local preset leaves the gateway listen unset so the fixed default applies + --- + duration_ms: 4.144148 + type: 'test' + ... +# Subtest: init --from-file into a fresh home writes the config +ok 1278 - init --from-file into a fresh home writes the config + --- + duration_ms: 177.157271 + type: 'test' + ... +# Subtest: init --from-file refuses to clobber an existing local config without --force +ok 1279 - init --from-file refuses to clobber an existing local config without --force + --- + duration_ms: 22.882509 + type: 'test' + ... +# Subtest: init --from-file --force backs up then overwrites +ok 1280 - init --from-file --force backs up then overwrites + --- + duration_ms: 33.151667 + type: 'test' + ... +# Subtest: init --yes refuses to clobber an existing local config without --force +ok 1281 - init --yes refuses to clobber an existing local config without --force + --- + duration_ms: 29.911282 + type: 'test' + ... +# Subtest: interactive init: declining the overwrite prompt aborts with the config intact +ok 1282 - interactive init: declining the overwrite prompt aborts with the config intact + --- + duration_ms: 18.806995 + type: 'test' + ... +# Subtest: interactive init: confirming the overwrite prompt backs up then writes +ok 1283 - interactive init: confirming the overwrite prompt backs up then writes + --- + duration_ms: 10.290781 + type: 'test' + ... +# Subtest: init rejects an unrecognized flag as a flag, not a preset +ok 1284 - init rejects an unrecognized flag as a flag, not a preset + --- + duration_ms: 15.280135 + type: 'test' + ... +# Subtest: buildPickerBackfillRunner: sweeping derives from the real provider contributions +ok 1285 - buildPickerBackfillRunner: sweeping derives from the real provider contributions + --- + duration_ms: 7.705742 + type: 'test' + ... +# Subtest: the claude-and-otel-local preset composes the graph pair beside its gateway +ok 1286 - the claude-and-otel-local preset composes the graph pair beside its gateway + --- + duration_ms: 22.085839 + type: 'test' + ... +# Subtest: the preset composes the graph engine before the connector that requires it +ok 1287 - the preset composes the graph engine before the connector that requires it + --- + duration_ms: 2.360478 + type: 'test' + ... +# Subtest: attach returns the parsed structured result +ok 1288 - attach returns the parsed structured result + --- + duration_ms: 32.89055 + type: 'test' + ... +# Subtest: detach returns the parsed structured result (core disk undo) +ok 1289 - detach returns the parsed structured result (core disk undo) + --- + duration_ms: 11.498747 + type: 'test' + ... +# Subtest: detach reverses a client whose adapter was dropped from the live gateway (LLP 0045 §Part 3) +ok 1290 - detach reverses a client whose adapter was dropped from the live gateway (LLP 0045 §Part 3) + --- + duration_ms: 9.314877 + type: 'test' + ... +# Subtest: detach resolves an INSTALLED (non-bundled) client adapter from the bundled+installed descriptor map (LLP 0045 §Part 3) +ok 1291 - detach resolves an INSTALLED (non-bundled) client adapter from the bundled+installed descriptor map (LLP 0045 §Part 3) + --- + duration_ms: 11.866005 + type: 'test' + ... +# Subtest: detach works with the @hypaware/ai-gateway capability absent (disk-driven undo, LLP 0045 §Part 3) +ok 1292 - detach works with the @hypaware/ai-gateway capability absent (disk-driven undo, LLP 0045 §Part 3) + --- + duration_ms: 7.992018 + type: 'test' + ... +# Subtest: attach stays gated on the @hypaware/ai-gateway capability (adapter_not_enabled) +ok 1293 - attach stays gated on the @hypaware/ai-gateway capability (adapter_not_enabled) + --- + duration_ms: 5.907088 + type: 'test' + ... +# Subtest: attach throws HypAwareCommandError for an unknown client +ok 1294 - attach throws HypAwareCommandError for an unknown client + --- + duration_ms: 6.443734 + type: 'test' + ... +# Subtest: join writes the central seed and returns success +ok 1295 - join writes the central seed and returns success + --- + duration_ms: 26.374406 + type: 'test' + ... +# Subtest: join throws HypAwareCommandError for an invalid url +ok 1296 - join throws HypAwareCommandError for an invalid url + --- + duration_ms: 6.92166 + type: 'test' + ... +# Subtest: join rejects dryRun instead of silently writing the seed +ok 1297 - join rejects dryRun instead of silently writing the seed + --- + duration_ms: 1.027342 + type: 'test' + ... +# Subtest: attach rejects the "all" target instead of dropping results +ok 1298 - attach rejects the "all" target instead of dropping results + --- + duration_ms: 0.908911 + type: 'test' + ... +# Subtest: detach rejects the "all" target instead of dropping results +ok 1299 - detach rejects the "all" target instead of dropping results + --- + duration_ms: 1.01932 + type: 'test' + ... +# Subtest: run is the escape hatch for multi-client attach (every client surfaces) +ok 1300 - run is the escape hatch for multi-client attach (every client surfaces) + --- + duration_ms: 9.497436 + type: 'test' + ... +# Subtest: run().json recovers the JSON object past trailing non-JSON prose +ok 1301 - run().json recovers the JSON object past trailing non-JSON prose + --- + duration_ms: 16.160492 + type: 'test' + ... +# Subtest: run exposes raw code and captured streams +ok 1302 - run exposes raw code and captured streams + --- + duration_ms: 12.489533 + type: 'test' + ... +# Subtest: join writes the central seed (mode 0600) and skips daemon install with --no-daemon +ok 1303 - join writes the central seed (mode 0600) and skips daemon install with --no-daemon + --- + duration_ms: 46.247291 + type: 'test' + ... +# Subtest: join never touches an existing local config (\#111 regression) +ok 1304 - join never touches an existing local config (\#111 regression) + --- + duration_ms: 68.490278 + type: 'test' + ... +# Subtest: join supersedes a stale active slot so the fresh token is honored (\#139) +ok 1305 - join supersedes a stale active slot so the fresh token is honored (\#139) + --- + duration_ms: 21.386796 + type: 'test' + ... +# Subtest: join reads the token from --token-file +ok 1306 - join reads the token from --token-file + --- + duration_ms: 17.202205 + type: 'test' + ... +# Subtest: join reads the token from stdin when piped +ok 1307 - join reads the token from stdin when piped + --- + duration_ms: 19.821186 + type: 'test' + ... +# Subtest: join rejects missing url, bad url, missing token, and conflicting token sources +ok 1308 - join rejects missing url, bad url, missing token, and conflicting token sources + --- + duration_ms: 27.897952 + type: 'test' + ... +# Subtest: join help exits 0 and documents token sources +ok 1309 - join help exits 0 and documents token sources + --- + duration_ms: 6.664059 + type: 'test' + ... +# Subtest: leave when not connected is a friendly no-op +ok 1310 - leave when not connected is a friendly no-op + --- + duration_ms: 32.578155 + type: 'test' + ... +# Subtest: leave after join removes the seed and reports the server +ok 1311 - leave after join removes the seed and reports the server + --- + duration_ms: 205.975893 + type: 'test' + ... +# Subtest: leave clears an applied central slot, not just the seed +ok 1312 - leave clears an applied central slot, not just the seed + --- + duration_ms: 20.368498 + type: 'test' + ... +# Subtest: leave reverses org-driven attaches and drops the forward identity +ok 1313 - leave reverses org-driven attaches and drops the forward identity + --- + duration_ms: 60.061498 + type: 'test' + ... +# Subtest: leave never edits the local layer, and says so when a local central sink exists +ok 1314 - leave never edits the local layer, and says so when a local central sink exists + --- + duration_ms: 13.034341 + type: 'test' + ... +# Subtest: leave after join also warns about a local central sink that keeps forwarding +ok 1315 - leave after join also warns about a local central sink that keeps forwarding + --- + duration_ms: 41.993787 + type: 'test' + ... +# Subtest: leave is idempotent: a second leave is the not-connected no-op +ok 1316 - leave is idempotent: a second leave is the not-connected no-op + --- + duration_ms: 31.9625 + type: 'test' + ... +# Subtest: leave tears down a central layer whose active-slot pointer does not resolve (\#623) +ok 1317 - leave tears down a central layer whose active-slot pointer does not resolve (\#623) + --- + duration_ms: 25.817099 + type: 'test' + ... +# Subtest: leave reports a central layer it cannot remove instead of throwing (\#623) +ok 1318 - leave reports a central layer it cannot remove instead of throwing (\#623) + --- + duration_ms: 6.15216 + type: 'test' + ... +# Subtest: leave still tears down when only a stale attach marker survives a prior partial leave +ok 1319 - leave still tears down when only a stale attach marker survives a prior partial leave + --- + duration_ms: 20.335328 + type: 'test' + ... +# Subtest: leave removes the assets its attach marker records, and leaves manual copies alone +ok 1320 - leave removes the assets its attach marker records, and leaves manual copies alone + --- + duration_ms: 36.147461 + type: 'test' + ... +# Subtest: leave self-heals an org attach whose plugin is gone: drops the marker, warns, stays clean +ok 1321 - leave self-heals an org attach whose plugin is gone: drops the marker, warns, stays clean + --- + duration_ms: 42.898762 + type: 'test' + ... +# Subtest: leave help exits 0 and rejects unknown arguments +ok 1322 - leave help exits 0 and rejects unknown arguments + --- + duration_ms: 9.407058 + type: 'test' + ... +# Subtest: the scan finds the corpus and its annotations +ok 1323 - the scan finds the corpus and its annotations + --- + duration_ms: 0.895611 + type: 'test' + ... +# Subtest: every @ref resolves to a live LLP document and one of its anchors +ok 1324 - every @ref resolves to a live LLP document and one of its anchors + --- + duration_ms: 2.462633 + type: 'test' + ... +# Subtest: every tolerated reference forgives no more than is still broken +ok 1325 - every tolerated reference forgives no more than is still broken + --- + duration_ms: 1.40777 + type: 'test' + ... +# Subtest: a tolerated reference is tolerated only as often as it is listed +ok 1326 - a tolerated reference is tolerated only as often as it is listed + --- + duration_ms: 0.211422 + type: 'test' + ... +# Subtest: no @ref annotation carries an em dash, on its first line or a later one +ok 1327 - no @ref annotation carries an em dash, on its first line or a later one + --- + duration_ms: 0.635596 + type: 'test' + ... +# Subtest: a gloss spans its continuation lines, and stops where the gloss stops +ok 1328 - a gloss spans its continuation lines, and stops where the gloss stops + --- + duration_ms: 2.256991 + type: 'test' + ... +# Subtest: no LLP number is claimed by two documents +ok 1329 - no LLP number is claimed by two documents + --- + duration_ms: 0.228647 + type: 'test' + ... +# Subtest: the ignore markers hide illustrative annotations without hiding live ones +ok 1330 - the ignore markers hide illustrative annotations without hiding live ones + --- + duration_ms: 0.213525 + type: 'test' + ... +# Subtest: documenting a marker does not activate it +ok 1331 - documenting a marker does not activate it + --- + duration_ms: 0.275649 + type: 'test' + ... +# Subtest: a marker shown as documentation or as a code sample does not activate it +ok 1332 - a marker shown as documentation or as a code sample does not activate it + --- + duration_ms: 0.568665 + type: 'test' + ... +# Subtest: suppression is confined to the syntax documentation and every region closes +ok 1333 - suppression is confined to the syntax documentation and every region closes + --- + duration_ms: 170.031922 + type: 'test' + ... +# Subtest: listCapturedDirectories: groups by cwd, carries repo_root/rows/last_seen, most-recent first +ok 1334 - listCapturedDirectories: groups by cwd, carries repo_root/rows/last_seen, most-recent first + --- + duration_ms: 11.42857 + type: 'test' + ... +# Subtest: listCapturedDirectories: a cwd is not offered when it has never been captured +ok 1335 - listCapturedDirectories: a cwd is not offered when it has never been captured + --- + duration_ms: 1.324634 + type: 'test' + ... +# Subtest: listCapturedDirectories: best-effort - a broken registry resolves to null, never throws +ok 1336 - listCapturedDirectories: best-effort - a broken registry resolves to null, never throws + --- + duration_ms: 0.773375 + type: 'test' + ... +# Subtest: compose_with is optional +ok 1337 - compose_with is optional + --- + duration_ms: 0.798674 + type: 'test' + ... +# Subtest: compose_with survives validation as a plugin name array +ok 1338 - compose_with survives validation as a plugin name array + --- + duration_ms: 0.545959 + type: 'test' + ... +# Subtest: compose_with rejects non-arrays, non-strings, and the empty array +ok 1339 - compose_with rejects non-arrays, non-strings, and the empty array + --- + duration_ms: 0.203369 + type: 'test' + ... +# Subtest: the empty array is rejected rather than treated as no condition +ok 1340 - the empty array is rejected rather than treated as no condition + --- + duration_ms: 0.107894 + type: 'test' + ... +# Subtest: compose_with rejects a self-reference +ok 1341 - compose_with rejects a self-reference + --- + duration_ms: 0.155256 + type: 'test' + ... +# Subtest: a self-reference is rejected even alongside a real condition +ok 1342 - a self-reference is rejected even alongside a real condition + --- + duration_ms: 0.090027 + type: 'test' + ... +# Subtest: a mutual compose_with pair each validate on their own +ok 1343 - a mutual compose_with pair each validate on their own + --- + duration_ms: 0.172132 + type: 'test' + ... +# Subtest: a typo in a compose_with name is a silent no-op at every layer +ok 1344 - a typo in a compose_with name is a silent no-op at every layer + --- + duration_ms: 16.887357 + type: 'test' + ... +# Subtest: the catalog surfaces compose_with from the shipped graph manifests +ok 1345 - the catalog surfaces compose_with from the shipped graph manifests + --- + duration_ms: 9.437323 + type: 'test' + ... +# Subtest: plugins without the field are absent from the rider map +ok 1346 - plugins without the field are absent from the rider map + --- + duration_ms: 10.98066 + type: 'test' + ... +# Subtest: validateManifest accepts the plugin manifest fields the kernel consumes +ok 1347 - validateManifest accepts the plugin manifest fields the kernel consumes + --- + duration_ms: 0.951136 + type: 'test' + ... +# Subtest: validateManifest rejects malformed nested maps +ok 1348 - validateManifest rejects malformed nested maps + --- + duration_ms: 0.175597 + type: 'test' + ... +# Subtest: validateManifest accepts a picker array with all three probe variants +ok 1349 - validateManifest accepts a picker array with all three probe variants + --- + duration_ms: 0.162436 + type: 'test' + ... +# Subtest: validateManifest accepts a picker row without a detect probe +ok 1350 - validateManifest accepts a picker row without a detect probe + --- + duration_ms: 0.097999 + type: 'test' + ... +# Subtest: validateManifest keeps unknown picker fields opaque (e.g. compose) +ok 1351 - validateManifest keeps unknown picker fields opaque (e.g. compose) + --- + duration_ms: 0.156287 + type: 'test' + ... +# Subtest: validateManifest rejects a non-array picker +ok 1352 - validateManifest rejects a non-array picker + --- + duration_ms: 0.095465 + type: 'test' + ... +# Subtest: validateManifest rejects a picker row missing a name +ok 1353 - validateManifest rejects a picker row missing a name + --- + duration_ms: 0.144089 + type: 'test' + ... +# Subtest: validateManifest rejects a picker row missing a label +ok 1354 - validateManifest rejects a picker row missing a label + --- + duration_ms: 0.096277 + type: 'test' + ... +# Subtest: validateManifest rejects a detect probe with no recognized variant +ok 1355 - validateManifest rejects a detect probe with no recognized variant + --- + duration_ms: 0.251633 + type: 'test' + ... +# Subtest: validateManifest rejects a detect probe with more than one variant +ok 1356 - validateManifest rejects a detect probe with more than one variant + --- + duration_ms: 0.36189 + type: 'test' + ... +# Subtest: validateManifest rejects a non-string probe path +ok 1357 - validateManifest rejects a non-string probe path + --- + duration_ms: 0.150008 + type: 'test' + ... +# Subtest: validateManifest rejects a non-boolean needs_setup +ok 1358 - validateManifest rejects a non-boolean needs_setup + --- + duration_ms: 0.083808 + type: 'test' + ... +# Subtest: validateManifest rejects a non-boolean hidden +ok 1359 - validateManifest rejects a non-boolean hidden + --- + duration_ms: 0.071048 + type: 'test' + ... +# Subtest: validateManifest accepts a hidden picker row +ok 1360 - validateManifest accepts a hidden picker row + --- + duration_ms: 0.068474 + type: 'test' + ... +# Subtest: matchesSemverRange covers exact, wildcard, caret, tilde, and comparisons +ok 1361 - matchesSemverRange covers exact, wildcard, caret, tilde, and comparisons + --- + duration_ms: 0.280016 + type: 'test' + ... +# Subtest: matchesSemverRange preserves zero-major caret behavior +ok 1362 - matchesSemverRange preserves zero-major caret behavior + --- + duration_ms: 0.068715 + type: 'test' + ... +# Subtest: initialize advertises serverInfo + tool/resource capabilities, echoing the client protocol +ok 1363 - initialize advertises serverInfo + tool/resource capabilities, echoing the client protocol + --- + duration_ms: 1.127123 + type: 'test' + ... +# Subtest: notifications get no response +ok 1364 - notifications get no response + --- + duration_ms: 1.274036 + type: 'test' + ... +# Subtest: tools/list on stdio shows read + operator + local-only (local-user trust) +ok 1365 - tools/list on stdio shows read + operator + local-only (local-user trust) + --- + duration_ms: 0.569175 + type: 'test' + ... +# Subtest: tools/list on an http transport without operator scope hides operator + local-only +ok 1366 - tools/list on an http transport without operator scope hides operator + local-only + --- + duration_ms: 0.126372 + type: 'test' + ... +# Subtest: tools/call runs the operation and sanitizes BigInt for JSON transport +ok 1367 - tools/call runs the operation and sanitizes BigInt for JSON transport + --- + duration_ms: 0.493089 + type: 'test' + ... +# Subtest: tools/call on an operator-gated server cannot reach an operator tool +ok 1368 - tools/call on an operator-gated server cannot reach an operator tool + --- + duration_ms: 0.161456 + type: 'test' + ... +# Subtest: tools/call with bad arguments is an invalid-params error +ok 1369 - tools/call with bad arguments is an invalid-params error + --- + duration_ms: 0.17765 + type: 'test' + ... +# Subtest: a thrown operation becomes an isError tool result, not a protocol error +ok 1370 - a thrown operation becomes an isError tool result, not a protocol error + --- + duration_ms: 0.218692 + type: 'test' + ... +# Subtest: resources expose dataset schemas; read returns the columns +ok 1371 - resources expose dataset schemas; read returns the columns + --- + duration_ms: 0.421661 + type: 'test' + ... +# Subtest: unknown method and unknown resource are proper errors +ok 1372 - unknown method and unknown resource are proper errors + --- + duration_ms: 0.290472 + type: 'test' + ... +# Subtest: query seam: a null-cwd ai_gateway_messages row is visible to the fail-closed unknown caller +ok 1373 - query seam: a null-cwd ai_gateway_messages row is visible to the fail-closed unknown caller + --- + duration_ms: 10.351373 + type: 'test' + ... +# Subtest: query seam: a null-cwd ai_gateway_messages row is visible to a restricted full-class caller +ok 1374 - query seam: a null-cwd ai_gateway_messages row is visible to a restricted full-class caller + --- + duration_ms: 1.48054 + type: 'test' + ... +# Subtest: export seam: a null-cwd ai_gateway_messages row is forwarded (full by construction) +ok 1375 - export seam: a null-cwd ai_gateway_messages row is forwarded (full by construction) + --- + duration_ms: 50.499454 + type: 'test' + ... +# Subtest: whereToParquetFilter converts simple comparisons +ok 1376 - whereToParquetFilter converts simple comparisons + --- + duration_ms: 3.802269 + type: 'test' + ... +# Subtest: whereToParquetFilter mirrors flipped operands (literal on the left) +ok 1377 - whereToParquetFilter mirrors flipped operands (literal on the left) + --- + duration_ms: 0.326997 + type: 'test' + ... +# Subtest: whereToParquetFilter handles AND / OR / NOT +ok 1378 - whereToParquetFilter handles AND / OR / NOT + --- + duration_ms: 0.838875 + type: 'test' + ... +# Subtest: whereToParquetFilter handles IN / NOT IN / IS NULL +ok 1379 - whereToParquetFilter handles IN / NOT IN / IS NULL + --- + duration_ms: 0.352155 + type: 'test' + ... +# Subtest: whereToParquetFilter folds typed literals (TIMESTAMP casts) +ok 1380 - whereToParquetFilter folds typed literals (TIMESTAMP casts) + --- + duration_ms: 0.477706 + type: 'test' + ... +# Subtest: whereToParquetFilter only unwraps truthiness-preserving casts +ok 1381 - whereToParquetFilter only unwraps truthiness-preserving casts + --- + duration_ms: 0.206704 + type: 'test' + ... +# Subtest: whereToParquetFilter returns undefined for non-convertible predicates +ok 1382 - whereToParquetFilter returns undefined for non-convertible predicates + --- + duration_ms: 0.209749 + type: 'test' + ... +# Subtest: whereToParquetFilter declines NULL-literal comparisons to the engine +ok 1383 - whereToParquetFilter declines NULL-literal comparisons to the engine + --- + duration_ms: 0.337513 + type: 'test' + ... +# Subtest: whereToParquetFilter handles NULL members of an IN list +ok 1384 - whereToParquetFilter handles NULL members of an IN list + --- + duration_ms: 0.412266 + type: 'test' + ... +# Subtest: pushed-down comparisons do not leak NULL rows (issue \#728) +ok 1385 - pushed-down comparisons do not leak NULL rows (issue \#728) + --- + duration_ms: 63.237875 + type: 'test' + ... +# Subtest: comparison against a NULL literal matches no rows (issue \#728) +ok 1386 - comparison against a NULL literal matches no rows (issue \#728) + --- + duration_ms: 7.34276 + type: 'test' + ... +# Subtest: negated comparisons against a NULL literal match no rows (issue \#734) +ok 1387 - negated comparisons against a NULL literal match no rows (issue \#734) + --- + duration_ms: 31.99494 + type: 'test' + ... +# Subtest: predicates that are not always-UNKNOWN keep their ordinary handling (issue \#734) +ok 1388 - predicates that are not always-UNKNOWN keep their ordinary handling (issue \#734) + --- + duration_ms: 8.462262 + type: 'test' + ... +# Subtest: a NULL member in an IN list does not cost row-group pruning (issue \#734) +ok 1389 - a NULL member in an IN list does not cost row-group pruning (issue \#734) + --- + duration_ms: 2.850553 + type: 'test' + ... +# Subtest: parquetDataSource exposes schema columns and row count +ok 1390 - parquetDataSource exposes schema columns and row count + --- + duration_ms: 0.633623 + type: 'test' + ... +# Subtest: SELECT * returns every row across row groups +ok 1391 - SELECT * returns every row across row groups + --- + duration_ms: 0.850373 + type: 'test' + ... +# Subtest: WHERE with pushed-down filter returns matching rows +ok 1392 - WHERE with pushed-down filter returns matching rows + --- + duration_ms: 0.851795 + type: 'test' + ... +# Subtest: WHERE on a non-projected column still filters correctly +ok 1393 - WHERE on a non-projected column still filters correctly + --- + duration_ms: 0.524597 + type: 'test' + ... +# Subtest: a pushed-down filter does not widen the projection +ok 1394 - a pushed-down filter does not widen the projection + --- + duration_ms: 0.816561 + type: 'test' + ... +# Subtest: range WHERE (AND) returns the inclusive window +ok 1395 - range WHERE (AND) returns the inclusive window + --- + duration_ms: 0.563166 + type: 'test' + ... +# Subtest: timestamp day bounds filter correctly through the pushed-down scan +ok 1396 - timestamp day bounds filter correctly through the pushed-down scan + --- + duration_ms: 0.615165 + type: 'test' + ... +# Subtest: a folded timestamp bound is actually pushed down, not left to the engine +ok 1397 - a folded timestamp bound is actually pushed down, not left to the engine + --- + duration_ms: 0.244943 + type: 'test' + ... +# Subtest: a timestamp bound matching no rows returns none (and one matching all returns all) +ok 1398 - a timestamp bound matching no rows returns none (and one matching all returns all) + --- + duration_ms: 1.357183 + type: 'test' + ... +# Subtest: LIKE falls back to engine filtering (not pushed down) +ok 1399 - LIKE falls back to engine filtering (not pushed down) + --- + duration_ms: 0.560552 + type: 'test' + ... +# Subtest: LIMIT/OFFSET without WHERE is pushed down +ok 1400 - LIMIT/OFFSET without WHERE is pushed down + --- + duration_ms: 1.306246 + type: 'test' + ... +# Subtest: ORDER BY ... LIMIT sees all rows before limiting +ok 1401 - ORDER BY ... LIMIT sees all rows before limiting + --- + duration_ms: 1.260766 + type: 'test' + ... +# Subtest: WHERE + LIMIT applies the limit over the filtered stream +ok 1402 - WHERE + LIMIT applies the limit over the filtered stream + --- + duration_ms: 0.720776 + type: 'test' + ... +# Subtest: aggregate over the source +ok 1403 - aggregate over the source + --- + duration_ms: 1.040281 + type: 'test' + ... +# Subtest: decideConfirmation: --yes returns auto_yes proceed +ok 1404 - decideConfirmation: --yes returns auto_yes proceed + --- + duration_ms: 0.653663 + type: 'test' + ... +# Subtest: decideConfirmation: --yes wins even with a tty (no prompt) +ok 1405 - decideConfirmation: --yes wins even with a tty (no prompt) + --- + duration_ms: 0.135286 + type: 'test' + ... +# Subtest: decideConfirmation: non-tty without --yes returns non_tty_no_yes +ok 1406 - decideConfirmation: non-tty without --yes returns non_tty_no_yes + --- + duration_ms: 0.077317 + type: 'test' + ... +# Subtest: decideConfirmation: tty + ask returning true => confirmed/proceed +ok 1407 - decideConfirmation: tty + ask returning true => confirmed/proceed + --- + duration_ms: 0.081925 + type: 'test' + ... +# Subtest: decideConfirmation: tty + ask returning false => rejected/abort +ok 1408 - decideConfirmation: tty + ask returning false => rejected/abort + --- + duration_ms: 0.13191 + type: 'test' + ... +# Subtest: buildTtyPrompt: real readline returns the trimmed answer (yes) +ok 1409 - buildTtyPrompt: real readline returns the trimmed answer (yes) + --- + duration_ms: 1.204861 + type: 'test' + ... +# Subtest: buildTtyPrompt: real readline returns false when the user types anything else +ok 1410 - buildTtyPrompt: real readline returns false when the user types anything else + --- + duration_ms: 0.440119 + type: 'test' + ... +# Subtest: buildTtyPrompt: real readline trims whitespace before deciding +ok 1411 - buildTtyPrompt: real readline trims whitespace before deciding + --- + duration_ms: 0.318865 + type: 'test' + ... +# Subtest: sourceIsUnpinnedBranch: missing ref counts as unpinned +ok 1412 - sourceIsUnpinnedBranch: missing ref counts as unpinned + --- + duration_ms: 0.333507 + type: 'test' + ... +# Subtest: sourceIsUnpinnedBranch: branch name counts as unpinned +ok 1413 - sourceIsUnpinnedBranch: branch name counts as unpinned + --- + duration_ms: 0.433289 + type: 'test' + ... +# Subtest: sourceIsUnpinnedBranch: semver tag counts as pinned +ok 1414 - sourceIsUnpinnedBranch: semver tag counts as pinned + --- + duration_ms: 0.198021 + type: 'test' + ... +# Subtest: sourceIsUnpinnedBranch: commit sha counts as pinned +ok 1415 - sourceIsUnpinnedBranch: commit sha counts as pinned + --- + duration_ms: 0.09248 + type: 'test' + ... +# Subtest: sourceIsUnpinnedBranch: local-dir is not a branch +ok 1416 - sourceIsUnpinnedBranch: local-dir is not a branch + --- + duration_ms: 0.075585 + type: 'test' + ... +# Subtest: buildWarnings: broad permission `network` triggers a warning +ok 1417 - buildWarnings: broad permission `network` triggers a warning + --- + duration_ms: 0.185412 + type: 'test' + ... +# Subtest: buildWarnings: unpinned branch triggers a warning +ok 1418 - buildWarnings: unpinned branch triggers a warning + --- + duration_ms: 0.120553 + type: 'test' + ... +# Subtest: buildWarnings: pinned tag emits no warning +ok 1419 - buildWarnings: pinned tag emits no warning + --- + duration_ms: 0.536365 + type: 'test' + ... +# Subtest: renderConfirmationSummary: install header lists permissions, entrypoint, content_hash, resolved_ref +ok 1420 - renderConfirmationSummary: install header lists permissions, entrypoint, content_hash, resolved_ref + --- + duration_ms: 0.279094 + type: 'test' + ... +# Subtest: renderConfirmationSummary: update header shows diff arrows when version/ref/hash change +ok 1421 - renderConfirmationSummary: update header shows diff arrows when version/ref/hash change + --- + duration_ms: 0.20399 + type: 'test' + ... +# Subtest: installPlugin (git): non-tty without confirm callback succeeds (legacy direct caller) +ok 1422 - installPlugin (git): non-tty without confirm callback succeeds (legacy direct caller) + --- + duration_ms: 171.84001 + type: 'test' + ... +# Subtest: installPlugin (git): confirm returning non_tty_no_yes fails with remote_install_confirmation_required +ok 1423 - installPlugin (git): confirm returning non_tty_no_yes fails with remote_install_confirmation_required + --- + duration_ms: 141.537864 + type: 'test' + ... +# Subtest: installPlugin (git): confirm returning auto_yes proceeds and stamps confirmation +ok 1424 - installPlugin (git): confirm returning auto_yes proceeds and stamps confirmation + --- + duration_ms: 153.513966 + type: 'test' + ... +# Subtest: installPlugin (git): confirm sees manifest, source, hashes, resolved_ref before commit +ok 1425 - installPlugin (git): confirm sees manifest, source, hashes, resolved_ref before commit + --- + duration_ms: 150.071725 + type: 'test' + ... +# Subtest: installPlugin (local-dir): does not invoke the confirm callback +ok 1426 - installPlugin (local-dir): does not invoke the confirm callback + --- + duration_ms: 5.892336 + type: 'test' + ... +# Subtest: updatePlugin: returns plugin_not_installed for an unknown name +ok 1427 - updatePlugin: returns plugin_not_installed for an unknown name + --- + duration_ms: 3.964255 + type: 'test' + ... +# Subtest: updatePlugin (git): re-fetches with same source, exposes previous to confirm +ok 1428 - updatePlugin (git): re-fetches with same source, exposes previous to confirm + --- + duration_ms: 200.689579 + type: 'test' + ... +# Subtest: updatePlugin (git): rejection leaves the prior install untouched +ok 1429 - updatePlugin (git): rejection leaves the prior install untouched + --- + duration_ms: 187.3612 + type: 'test' + ... +# Subtest: parseGitSource accepts https GitHub URLs with and without .git suffix +ok 1430 - parseGitSource accepts https GitHub URLs with and without .git suffix + --- + duration_ms: 1.945617 + type: 'test' + ... +# Subtest: parseGitSource extracts ref from URL fragment +ok 1431 - parseGitSource extracts ref from URL fragment + --- + duration_ms: 0.136368 + type: 'test' + ... +# Subtest: parseGitSource normalizes github: shorthand to HTTPS clone URL +ok 1432 - parseGitSource normalizes github: shorthand to HTTPS clone URL + --- + duration_ms: 0.156337 + type: 'test' + ... +# Subtest: parseGitSource normalizes git@github.com SSH shorthand to HTTPS clone URL +ok 1433 - parseGitSource normalizes git@github.com SSH shorthand to HTTPS clone URL + --- + duration_ms: 0.195698 + type: 'test' + ... +# Subtest: parseGitSource passes through non-GitHub git URLs untouched +ok 1434 - parseGitSource passes through non-GitHub git URLs untouched + --- + duration_ms: 0.390974 + type: 'test' + ... +# Subtest: applyGitSourceFlags rejects --ref when a URL fragment was already supplied +ok 1435 - applyGitSourceFlags rejects --ref when a URL fragment was already supplied + --- + duration_ms: 0.398075 + type: 'test' + ... +# Subtest: applyGitSourceFlags adopts --ref when the URL has no fragment +ok 1436 - applyGitSourceFlags adopts --ref when the URL has no fragment + --- + duration_ms: 0.116337 + type: 'test' + ... +# Subtest: applyGitSourceFlags rejects --path subdir with git_subdir_unsupported +ok 1437 - applyGitSourceFlags rejects --path subdir with git_subdir_unsupported + --- + duration_ms: 0.153544 + type: 'test' + ... +# Subtest: resolveSource forwards --ref into the resolved git source spec +ok 1438 - resolveSource forwards --ref into the resolved git source spec + --- + duration_ms: 0.50637 + type: 'test' + ... +# Subtest: resolveSource throws git_subdir_unsupported when --path is provided +ok 1439 - resolveSource throws git_subdir_unsupported when --path is provided + --- + duration_ms: 0.44064 + type: 'test' + ... +# Subtest: resolveSource throws source_ambiguous when --ref conflicts with URL fragment +ok 1440 - resolveSource throws source_ambiguous when --ref conflicts with URL fragment + --- + duration_ms: 0.180945 + type: 'test' + ... +# Subtest: resolveSource still routes github: shorthand through the git path +ok 1441 - resolveSource still routes github: shorthand through the git path + --- + duration_ms: 0.103126 + type: 'test' + ... +# Subtest: provenanceFromUrl extracts host/owner/repo from HTTPS clone URL +ok 1442 - provenanceFromUrl extracts host/owner/repo from HTTPS clone URL + --- + duration_ms: 0.166923 + type: 'test' + ... +# Subtest: validateEntrypoint rejects absolute paths +ok 1443 - validateEntrypoint rejects absolute paths + --- + duration_ms: 0.113753 + type: 'test' + ... +# Subtest: validateEntrypoint rejects parent-directory traversal +ok 1444 - validateEntrypoint rejects parent-directory traversal + --- + duration_ms: 0.120853 + type: 'test' + ... +# Subtest: validateEntrypoint accepts a relative path that stays inside the artifact root +ok 1445 - validateEntrypoint accepts a relative path that stays inside the artifact root + --- + duration_ms: 0.093763 + type: 'test' + ... +# Subtest: findSymlink reports the first symlink encountered in the tree +ok 1446 - findSymlink reports the first symlink encountered in the tree + --- + duration_ms: 16.777159 + type: 'test' + ... +# Subtest: findSymlink returns null for a tree with no symlinks +ok 1447 - findSymlink returns null for a tree with no symlinks + --- + duration_ms: 2.233334 + type: 'test' + ... +# Subtest: hashArtifactTree is stable across two equal trees +ok 1448 - hashArtifactTree is stable across two equal trees + --- + duration_ms: 7.049685 + type: 'test' + ... +# Subtest: hashArtifactTree changes when file content changes +ok 1449 - hashArtifactTree changes when file content changes + --- + duration_ms: 2.393898 + type: 'test' + ... +# Subtest: parseGitSource rejects raw input that begins with a dash +ok 1450 - parseGitSource rejects raw input that begins with a dash + --- + duration_ms: 0.183809 + type: 'test' + ... +# Subtest: parseGitSource rejects URL fragment refs that begin with a dash +ok 1451 - parseGitSource rejects URL fragment refs that begin with a dash + --- + duration_ms: 0.115295 + type: 'test' + ... +# Subtest: parseGitSource strips userinfo from passthrough URLs +ok 1452 - parseGitSource strips userinfo from passthrough URLs + --- + duration_ms: 0.141185 + type: 'test' + ... +# Subtest: parseGitSource strips userinfo from passthrough URLs with a fragment +ok 1453 - parseGitSource strips userinfo from passthrough URLs with a fragment + --- + duration_ms: 0.087874 + type: 'test' + ... +# Subtest: parseGitSource accepts GitHub HTTPS URLs that carry userinfo and ignores it +ok 1454 - parseGitSource accepts GitHub HTTPS URLs that carry userinfo and ignores it + --- + duration_ms: 0.148616 + type: 'test' + ... +# Subtest: applyGitSourceFlags rejects --ref values that start with a dash +ok 1455 - applyGitSourceFlags rejects --ref values that start with a dash + --- + duration_ms: 0.139301 + type: 'test' + ... +# Subtest: applyGitSourceFlags rejects --path values that start with a dash before reporting unsupported +ok 1456 - applyGitSourceFlags rejects --path values that start with a dash before reporting unsupported + --- + duration_ms: 0.127824 + type: 'test' + ... +# Subtest: redactGitUrl strips user:pass@ userinfo +ok 1457 - redactGitUrl strips user:pass@ userinfo + --- + duration_ms: 0.102426 + type: 'test' + ... +# Subtest: redactGitUrl is a no-op on URLs without userinfo +ok 1458 - redactGitUrl is a no-op on URLs without userinfo + --- + duration_ms: 0.084519 + type: 'test' + ... +# Subtest: redactGitUrl preserves port and path through the redaction +ok 1459 - redactGitUrl preserves port and path through the redaction + --- + duration_ms: 0.084709 + type: 'test' + ... +# Subtest: redactRawSource strips userinfo while preserving the \#ref fragment +ok 1460 - redactRawSource strips userinfo while preserving the \#ref fragment + --- + duration_ms: 0.083978 + type: 'test' + ... +# Subtest: resolveSource persists redacted raw + gitUrl for a passthrough URL with credentials +ok 1461 - resolveSource persists redacted raw + gitUrl for a passthrough URL with credentials + --- + duration_ms: 0.11124 + type: 'test' + ... +# Subtest: resolveSource rejects rawSource that begins with a dash +ok 1462 - resolveSource rejects rawSource that begins with a dash + --- + duration_ms: 0.167104 + type: 'test' + ... +# Subtest: pickLsRemoteSha prefers the peeled commit for an annotated tag +ok 1463 - pickLsRemoteSha prefers the peeled commit for an annotated tag + --- + duration_ms: 0.221837 + type: 'test' + ... +# Subtest: pickLsRemoteSha returns the lightweight tag SHA when no peeled line exists +ok 1464 - pickLsRemoteSha returns the lightweight tag SHA when no peeled line exists + --- + duration_ms: 0.128465 + type: 'test' + ... +# Subtest: pickLsRemoteSha prefers the HEAD line when HEAD was requested +ok 1465 - pickLsRemoteSha prefers the HEAD line when HEAD was requested + --- + duration_ms: 0.092881 + type: 'test' + ... +# Subtest: pickLsRemoteSha returns undefined when no commit-shaped SHA is present +ok 1466 - pickLsRemoteSha returns undefined when no commit-shaped SHA is present + --- + duration_ms: 0.068093 + type: 'test' + ... +# Subtest: hyp policy set ignore marks the path ignore in the machine-local store, never touching the repo +ok 1467 - hyp policy set ignore marks the path ignore in the machine-local store, never touching the repo + --- + duration_ms: 27.88319 + type: 'test' + ... +# Subtest: hyp policy set ignore is idempotent: marking twice is a no-op success +ok 1468 - hyp policy set ignore is idempotent: marking twice is a no-op success + --- + duration_ms: 3.844363 + type: 'test' + ... +# Subtest: hyp policy set ignore upgrades an existing local-only entry to ignore +ok 1469 - hyp policy set ignore upgrades an existing local-only entry to ignore + --- + duration_ms: 5.187654 + type: 'test' + ... +# Subtest: hyp policy set ignore on a path already governed by a stricter .hypignore is a no-op naming the dotfile +ok 1470 - hyp policy set ignore on a path already governed by a stricter .hypignore is a no-op naming the dotfile + --- + duration_ms: 1.937595 + type: 'test' + ... +# Subtest: hyp policy set sync writes an explicit full entry (the sync -> full token mapping) +ok 1471 - hyp policy set sync writes an explicit full entry (the sync -> full token mapping) + --- + duration_ms: 6.740175 + type: 'test' + ... +# Subtest: hyp policy set sync is idempotent against an existing explicit full entry, but not against the mere implicit default +ok 1472 - hyp policy set sync is idempotent against an existing explicit full entry, but not against the mere implicit default + --- + duration_ms: 3.34888 + type: 'test' + ... +# Subtest: hyp policy set sync confirms in the public vocabulary and never names the backing store file +ok 1473 - hyp policy set sync confirms in the public vocabulary and never names the backing store file + --- + duration_ms: 2.0326 + type: 'test' + ... +# Subtest: hyp policy set ignore also names the marking as machine-local +ok 1474 - hyp policy set ignore also names the marking as machine-local + --- + duration_ms: 1.886668 + type: 'test' + ... +# Subtest: hyp policy set sync twice reports the existing mark in the public vocabulary, naming the store neutrally +ok 1475 - hyp policy set sync twice reports the existing mark in the public vocabulary, naming the store neutrally + --- + duration_ms: 4.213414 + type: 'test' + ... +# Subtest: hyp policy set local-only marks the path local-only +ok 1476 - hyp policy set local-only marks the path local-only + --- + duration_ms: 3.4938 + type: 'test' + ... +# Subtest: hyp policy set rejects an unknown class token with a usage error naming the three valid tokens +ok 1477 - hyp policy set rejects an unknown class token with a usage error naming the three valid tokens + --- + duration_ms: 1.32963 + type: 'test' + ... +# Subtest: hyp policy set requires a path (bare class token alone is ambiguous, so it is rejected) +ok 1478 - hyp policy set requires a path (bare class token alone is ambiguous, so it is rejected) + --- + duration_ms: 0.632171 + type: 'test' + ... +# Subtest: hyp policy show [path] --json is byte-compatible with hyp ignore --check --json for a machine-local mark +ok 1479 - hyp policy show [path] --json is byte-compatible with hyp ignore --check --json for a machine-local mark + --- + duration_ms: 8.982512 + type: 'test' + ... +# Subtest: hyp policy show defaults to cwd and names the dotfile source when a .hypignore governs +ok 1480 - hyp policy show defaults to cwd and names the dotfile source when a .hypignore governs + --- + duration_ms: 1.254897 + type: 'test' + ... +# Subtest: hyp policy show (human) renders a stored full entry as sync and names the store neutrally +ok 1481 - hyp policy show (human) renders a stored full entry as sync and names the store neutrally + --- + duration_ms: 3.035603 + type: 'test' + ... +# Subtest: hyp policy show (human) keeps naming a governing dotfile by its real path +ok 1482 - hyp policy show (human) keeps naming a governing dotfile by its real path + --- + duration_ms: 1.317713 + type: 'test' + ... +# Subtest: hyp policy show --json keeps the stored vocabulary and the store path (unchanged machine contract) +ok 1483 - hyp policy show --json keeps the stored vocabulary and the store path (unchanged machine contract) + --- + duration_ms: 9.012197 + type: 'test' + ... +# Subtest: hyp policy show names no source when nothing governs (the implicit default) +ok 1484 - hyp policy show names no source when nothing governs (the implicit default) + --- + duration_ms: 0.865095 + type: 'test' + ... +# Subtest: hyp policy show (human) flags the implicit default so it cannot be mistaken for an explicit sync mark +ok 1485 - hyp policy show (human) flags the implicit default so it cannot be mistaken for an explicit sync mark + --- + duration_ms: 0.878876 + type: 'test' + ... +# Subtest: hyp policy unset ignore (scoped) removes an ignore entry and is idempotent, leaving other classes untouched +ok 1486 - hyp policy unset ignore (scoped) removes an ignore entry and is idempotent, leaving other classes untouched + --- + duration_ms: 7.008152 + type: 'test' + ... +# Subtest: hyp policy unset sync (scoped) removes an explicit full entry and is idempotent +ok 1487 - hyp policy unset sync (scoped) removes an explicit full entry and is idempotent + --- + duration_ms: 3.227786 + type: 'test' + ... +# Subtest: hyp policy unset sync reports removal and the no-op in the public vocabulary +ok 1488 - hyp policy unset sync reports removal and the no-op in the public vocabulary + --- + duration_ms: 3.232523 + type: 'test' + ... +# Subtest: hyp policy unset (class-neutral) names each removed entry class in the public vocabulary +ok 1489 - hyp policy unset (class-neutral) names each removed entry class in the public vocabulary + --- + duration_ms: 2.607833 + type: 'test' + ... +# Subtest: hyp policy unset (no trailing class token) is class-neutral: removes every machine-local entry governing the target +ok 1490 - hyp policy unset (no trailing class token) is class-neutral: removes every machine-local entry governing the target + --- + duration_ms: 3.196358 + type: 'test' + ... +# Subtest: hyp policy unset (no trailing class token) removes multiple entries of different classes governing the same target +ok 1491 - hyp policy unset (no trailing class token) removes multiple entries of different classes governing the same target + --- + duration_ms: 4.529164 + type: 'test' + ... +# Subtest: hyp policy unset (no trailing class token) on an already-clean path is a class-neutral no-op success +ok 1492 - hyp policy unset (no trailing class token) on an already-clean path is a class-neutral no-op success + --- + duration_ms: 0.766325 + type: 'test' + ... +# Subtest: hyp policy unset rejects an unknown trailing class token with a usage error naming the three valid tokens +ok 1493 - hyp policy unset rejects an unknown trailing class token with a usage error naming the three valid tokens + --- + duration_ms: 0.42129 + type: 'test' + ... +# Subtest: hyp policy unset requires a path +ok 1494 - hyp policy unset requires a path + --- + duration_ms: 0.43413 + type: 'test' + ... +# Subtest: hyp policy list --json enumerates every machine-local entry with the store path +ok 1495 - hyp policy list --json enumerates every machine-local entry with the store path + --- + duration_ms: 5.630418 + type: 'test' + ... +# Subtest: hyp policy list --json on an empty store lists zero entries successfully +ok 1496 - hyp policy list --json on an empty store lists zero entries successfully + --- + duration_ms: 1.22338 + type: 'test' + ... +# Subtest: hyp policy list (human) renders a full entry as sync and labels the store path +ok 1497 - hyp policy list (human) renders a full entry as sync and labels the store path + --- + duration_ms: 2.030867 + type: 'test' + ... +# Subtest: hyp policy list (human) on an empty store reports no entries without error, labelling the store path +ok 1498 - hyp policy list (human) on an empty store reports no entries without error, labelling the store path + --- + duration_ms: 1.024257 + type: 'test' + ... +# Subtest: hyp policy show on a corrupt store fails with the policy-store wording, not "local-only list" +ok 1499 - hyp policy show on a corrupt store fails with the policy-store wording, not "local-only list" + --- + duration_ms: 1.275378 + type: 'test' + ... +# Subtest: hyp policy list on a corrupt store fails with the policy-store wording, not "local-only list" +ok 1500 - hyp policy list on a corrupt store fails with the policy-store wording, not "local-only list" + --- + duration_ms: 6.727986 + type: 'test' + ... +# Subtest: hyp policy set on a corrupt store fails with the policy-store wording, not "local-only list" +ok 1501 - hyp policy set on a corrupt store fails with the policy-store wording, not "local-only list" + --- + duration_ms: 1.284071 + type: 'test' + ... +# Subtest: hyp policy unset on a corrupt store fails with the policy-store wording, not "local-only list" +ok 1502 - hyp policy unset on a corrupt store fails with the policy-store wording, not "local-only list" + --- + duration_ms: 2.858915 + type: 'test' + ... +# Subtest: hyp policy on a corrupt store tags the command span with the local_only_list_unreadable error_kind +ok 1503 - hyp policy on a corrupt store tags the command span with the local_only_list_unreadable error_kind + --- + duration_ms: 11.46774 + type: 'test' + ... +# Subtest: hyp policy (bare) renders group help listing set/show/unset/list +ok 1504 - hyp policy (bare) renders group help listing set/show/unset/list + --- + duration_ms: 0.983304 + type: 'test' + ... +# Subtest: hyp policy client: empty store lists nothing opted out and names the store +ok 1505 - hyp policy client: empty store lists nothing opted out and names the store + --- + duration_ms: 14.818613 + type: 'test' + ... +# Subtest: hyp policy client local-only writes the opt-out; show and list reflect it; sync removes it +ok 1506 - hyp policy client local-only writes the opt-out; show and list reflect it; sync removes it + --- + duration_ms: 25.737869 + type: 'test' + ... +# Subtest: hyp policy client --json: both shapes are stable, before and after an opt-out +ok 1507 - hyp policy client --json: both shapes are stable, before and after an opt-out + --- + duration_ms: 28.623726 + type: 'test' + ... +# Subtest: hyp policy client with an unknown name exits 2 and names known ids +ok 1508 - hyp policy client with an unknown name exits 2 and names known ids + --- + duration_ms: 5.154144 + type: 'test' + ... +# Subtest: hyp policy client refuses to opt out a central-configured source (LLP 0188 \#locked) +ok 1509 - hyp policy client refuses to opt out a central-configured source (LLP 0188 \#locked) + --- + duration_ms: 14.767546 + type: 'test' + ... +# Subtest: hyp policy client fails loudly on a corrupt store +ok 1510 - hyp policy client fails loudly on a corrupt store + --- + duration_ms: 4.49998 + type: 'test' + ... +# Subtest: hyp policy list includes the clients section, text and --json +ok 1511 - hyp policy list includes the clients section, text and --json + --- + duration_ms: 15.123466 + type: 'test' + ... +# Subtest: hyp policy folders: with nothing set, reports the sync default and names the store +ok 1512 - hyp policy folders: with nothing set, reports the sync default and names the store + --- + duration_ms: 0.990596 + type: 'test' + ... +# Subtest: hyp policy folders ask buys the per-folder question; folders sync retires it again +ok 1513 - hyp policy folders ask buys the per-folder question; folders sync retires it again + --- + duration_ms: 2.832285 + type: 'test' + ... +# Subtest: hyp policy folders is idempotent and never touches the directory store +ok 1514 - hyp policy folders is idempotent and never touches the directory store + --- + duration_ms: 3.554462 + type: 'test' + ... +# Subtest: hyp policy folders --json emits the mode and the store path, both shapes +ok 1515 - hyp policy folders --json emits the mode and the store path, both shapes + --- + duration_ms: 3.051218 + type: 'test' + ... +# Subtest: hyp policy folders rejects an unknown mode with a usage error +ok 1516 - hyp policy folders rejects an unknown mode with a usage error + --- + duration_ms: 0.570607 + type: 'test' + ... +# Subtest: hyp policy folders fails loudly on a corrupt preference and names the repair +ok 1517 - hyp policy folders fails loudly on a corrupt preference and names the repair + --- + duration_ms: 2.476233 + type: 'test' + ... +# Subtest: hyp policy list surfaces the per-folder ask when it is on, text and --json +ok 1518 - hyp policy list surfaces the per-folder ask when it is on, text and --json + --- + duration_ms: 6.53979 + type: 'test' + ... +# Subtest: purge subtree deletes rows equal-or-descendant, leaves siblings +ok 1519 - purge subtree deletes rows equal-or-descendant, leaves siblings + --- + duration_ms: 49.201872 + type: 'test' + ... +# Subtest: purge subtree is segment-aware: /home/u/repoA does not match /home/u/repoA-other +ok 1520 - purge subtree is segment-aware: /home/u/repoA does not match /home/u/repoA-other + --- + duration_ms: 41.295706 + type: 'test' + ... +# Subtest: purge subtree matches a row recorded under a symlink spelling of the target +ok 1521 - purge subtree matches a row recorded under a symlink spelling of the target + --- + duration_ms: 23.806343 + type: 'test' + ... +# Subtest: purge --session deletes only that session +ok 1522 - purge --session deletes only that session + --- + duration_ms: 28.220132 + type: 'test' + ... +# Subtest: purge --ignored deletes only rows whose cwd resolves ignore +ok 1523 - purge --ignored deletes only rows whose cwd resolves ignore + --- + duration_ms: 16.502032 + type: 'test' + ... +# Subtest: purge --all deletes every row +ok 1524 - purge --all deletes every row + --- + duration_ms: 18.991035 + type: 'test' + ... +# Subtest: purge reports distinct purged cwds for the resurrection warning +ok 1525 - purge reports distinct purged cwds for the resurrection warning + --- + duration_ms: 11.832595 + type: 'test' + ... +# Subtest: purge preserves surviving rows' part_id (dedupe identity) and does not resurrect on re-scan +ok 1526 - purge preserves surviving rows' part_id (dedupe identity) and does not resurrect on re-scan + --- + duration_ms: 15.905625 + type: 'test' + ... +# Subtest: purge updates the partition cursor rowCount to the live count +ok 1527 - purge updates the partition cursor rowCount to the live count + --- + duration_ms: 11.087011 + type: 'test' + ... +# Subtest: purge is idempotent: re-purging the same target deletes nothing new +ok 1528 - purge is idempotent: re-purging the same target deletes nothing new + --- + duration_ms: 11.031127 + type: 'test' + ... +# Subtest: deleteMatchingRows on an empty/absent table is a no-op +ok 1529 - deleteMatchingRows on an empty/absent table is a no-op + --- + duration_ms: 0.795018 + type: 'test' + ... +# Subtest: runPurge: bare purge (no target) errors +ok 1530 - runPurge: bare purge (no target) errors + --- + duration_ms: 1.686393 + type: 'test' + ... +# Subtest: runPurge: two targets error +ok 1531 - runPurge: two targets error + --- + duration_ms: 2.51952 + type: 'test' + ... +# Subtest: runPurge: non-TTY without --yes refuses +ok 1532 - runPurge: non-TTY without --yes refuses + --- + duration_ms: 5.972608 + type: 'test' + ... +# Subtest: runPurge --all --yes deletes everything and reports counts +ok 1533 - runPurge --all --yes deletes everything and reports counts + --- + duration_ms: 14.846595 + type: 'test' + ... +# Subtest: runPurge subtree warns about resurrection when the dir still resolves full +ok 1534 - runPurge subtree warns about resurrection when the dir still resolves full + --- + duration_ms: 12.269157 + type: 'test' + ... +# Subtest: runPurge --ignored is durable: an ignored dir does not warn +ok 1535 - runPurge --ignored is durable: an ignored dir does not warn + --- + duration_ms: 17.680913 + type: 'test' + ... +# Subtest: runPurge --json emits machine-readable counts and resurrectable dirs +ok 1536 - runPurge --json emits machine-readable counts and resurrectable dirs + --- + duration_ms: 19.148544 + type: 'test' + ... +# Subtest: \#485 premise: the two fixture spellings are different strings that NFC folds together +ok 1537 - \#485 premise: the two fixture spellings are different strings that NFC folds together + --- + duration_ms: 0.199704 + type: 'test' + ... +# Subtest: purge subtree deletes a row recorded NFD when the target is typed NFC, on a volume that folds them +ok 1538 - purge subtree deletes a row recorded NFD when the target is typed NFC, on a volume that folds them + --- + duration_ms: 18.786513 + type: 'test' + ... +# Subtest: purge subtree deletes a row recorded NFC when the target is typed NFD, on a volume that folds them +ok 1539 - purge subtree deletes a row recorded NFC when the target is typed NFD, on a volume that folds them + --- + duration_ms: 20.86344 + type: 'test' + ... +# Subtest: purge subtree deletes a row recorded under a case variant, on a case-insensitive volume +ok 1540 - purge subtree deletes a row recorded under a case variant, on a case-insensitive volume + --- + duration_ms: 20.191569 + type: 'test' + ... +# Subtest: purge subtree never widens onto a spelling the volume says is a different directory +ok 1541 - purge subtree never widens onto a spelling the volume says is a different directory + --- + duration_ms: 11.311612 + type: 'test' + ... +# Subtest: purge subtree agrees with the real filesystem about an NFC/NFD pair, and never over-deletes +ok 1542 - purge subtree agrees with the real filesystem about an NFC/NFD pair, and never over-deletes + --- + duration_ms: 14.28324 + type: 'test' + ... +# Subtest: purge subtree agrees with the real filesystem about a case variant, and never over-deletes +ok 1543 - purge subtree agrees with the real filesystem about a case variant, and never over-deletes + --- + duration_ms: 9.80968 + type: 'test' + ... +# Subtest: runPurge says so when it leaves a lookalike spelling in place, instead of exiting 0 in silence +ok 1544 - runPurge says so when it leaves a lookalike spelling in place, instead of exiting 0 in silence + --- + duration_ms: 8.101463 + type: 'test' + ... +# Subtest: scopeGovernance stays unwidened for callers that do not opt in +ok 1545 - scopeGovernance stays unwidened for callers that do not opt in + --- + duration_ms: 0.610928 + type: 'test' + ... +# Subtest: the near-miss note does not claim a verdict an ENOENT never gave +ok 1546 - the near-miss note does not claim a verdict an ENOENT never gave + --- + duration_ms: 7.423683 + type: 'test' + ... +# Subtest: the near-miss note agrees in number with the rows it is counting +ok 1547 - the near-miss note agrees in number with the rows it is counting + --- + duration_ms: 7.905786 + type: 'test' + ... +# Subtest: the near-miss note does not say "no longer on disk" of an alias that is on disk but unreadable +ok 1548 - the near-miss note does not say "no longer on disk" of an alias that is on disk but unreadable + --- + duration_ms: 15.462241 + type: 'test' + ... +# Subtest: no stat errno makes an unprovable alias deletable +ok 1549 - no stat errno makes an unprovable alias deletable + --- + duration_ms: 1.168566 + type: 'test' + ... +# Subtest: cell truncation clips long strings with a sized marker, leaves short ones +ok 1550 - cell truncation clips long strings with a sized marker, leaves short ones + --- + duration_ms: 1.021613 + type: 'test' + ... +# Subtest: cell truncation recurses into nested objects/arrays and preserves JSON validity +ok 1551 - cell truncation recurses into nested objects/arrays and preserves JSON validity + --- + duration_ms: 0.360998 + type: 'test' + ... +# Subtest: cell truncation counts code points, never splitting a multibyte char +ok 1552 - cell truncation counts code points, never splitting a multibyte char + --- + duration_ms: 0.147584 + type: 'test' + ... +# Subtest: maxCell = 0 disables truncation +ok 1553 - maxCell = 0 disables truncation + --- + duration_ms: 0.142196 + type: 'test' + ... +# Subtest: byte budget drops trailing rows and emits a notice naming the counts +ok 1554 - byte budget drops trailing rows and emits a notice naming the counts + --- + duration_ms: 0.430174 + type: 'test' + ... +# Subtest: byte budget always keeps at least one row even if it alone exceeds the budget +ok 1555 - byte budget always keeps at least one row even if it alone exceeds the budget + --- + duration_ms: 0.195818 + type: 'test' + ... +# Subtest: no notice when nothing is dropped +ok 1556 - no notice when nothing is dropped + --- + duration_ms: 0.201577 + type: 'test' + ... +# Subtest: input result is not mutated +ok 1557 - input result is not mutated + --- + duration_ms: 0.15776 + type: 'test' + ... +# Subtest: truncation is lazy: rows past the budget are never touched +ok 1558 - truncation is lazy: rows past the budget are never touched + --- + duration_ms: 0.409663 + type: 'test' + ... +# Subtest: table format escapes every control and bidi character in a cell +ok 1559 - table format escapes every control and bidi character in a cell + --- + duration_ms: 1.132842 + type: 'test' + ... +# Subtest: markdown format escapes control and bidi, and still escapes pipes +ok 1560 - markdown format escapes control and bidi, and still escapes pipes + --- + duration_ms: 0.289921 + type: 'test' + ... +# Subtest: a newline in a table cell cannot forge a row +ok 1561 - a newline in a table cell cannot forge a row + --- + duration_ms: 0.166503 + type: 'test' + ... +# Subtest: tab and carriage return get their familiar spellings +ok 1562 - tab and carriage return get their familiar spellings + --- + duration_ms: 0.200085 + type: 'test' + ... +# Subtest: json and jsonl stay byte-exact, control characters included +ok 1563 - json and jsonl stay byte-exact, control characters included + --- + duration_ms: 1.037016 + type: 'test' + ... +# Subtest: ordinary non-ASCII text is not touched by either human format +ok 1564 - ordinary non-ASCII text is not touched by either human format + --- + duration_ms: 0.244962 + type: 'test' + ... +# Subtest: column widths and alignment survive an escaped cell +ok 1565 - column widths and alignment survive an escaped cell + --- + duration_ms: 0.189628 + type: 'test' + ... +# Subtest: table column width is measured on the escaped header, not the raw column name +ok 1566 - table column width is measured on the escaped header, not the raw column name + --- + duration_ms: 0.156768 + type: 'test' + ... +# Subtest: the spill receipt escapes its preview but the file it wrote does not +ok 1567 - the spill receipt escapes its preview but the file it wrote does not + --- + duration_ms: 0.600443 + type: 'test' + ... +# Subtest: caller-class lattice truth table: include iff caller class >= row class +ok 1568 - caller-class lattice truth table: include iff caller class >= row class + --- + duration_ms: 13.095875 + type: 'test' + ... +# Subtest: an omitted callerCwd behaves exactly like null: the fail-closed backstop +ok 1569 - an omitted callerCwd behaves exactly like null: the fail-closed backstop + --- + duration_ms: 1.376402 + type: 'test' + ... +# Subtest: includeLocalOnly bypasses the filter and reports it unfiltered +ok 1570 - includeLocalOnly bypasses the filter and reports it unfiltered + --- + duration_ms: 2.106151 + type: 'test' + ... +# Subtest: a projection that omits cwd cannot blind the filter, and the forced cwd never leaks out +ok 1571 - a projection that omits cwd cannot blind the filter, and the forced cwd never leaks out + --- + duration_ms: 0.815009 + type: 'test' + ... +# Subtest: rows without a cwd value in a plain cwd-bearing dataset pass through (export-seam parity) +ok 1572 - rows without a cwd value in a plain cwd-bearing dataset pass through (export-seam parity) + --- + duration_ms: 0.92709 + type: 'test' + ... +# Subtest: COUNT(*) cannot use numRows around the filter and counts only visible rows +ok 1573 - COUNT(*) cannot use numRows around the filter and counts only visible rows + --- + duration_ms: 1.751452 + type: 'test' + ... +# Subtest: the scanColumn fast path is withheld from a filtered caller and stays lit for a top-of-lattice one +ok 1574 - the scanColumn fast path is withheld from a filtered caller and stays lit for a top-of-lattice one + --- + duration_ms: 2.672502 + type: 'test' + ... +# Subtest: a source that eagerly applies LIMIT cannot under-return visible rows +ok 1575 - a source that eagerly applies LIMIT cannot under-return visible rows + --- + duration_ms: 0.959338 + type: 'test' + ... +# Subtest: suppression: unprovenanced rows in a content-declaring dataset expose structure, never content +ok 1576 - suppression: unprovenanced rows in a content-declaring dataset expose structure, never content + --- + duration_ms: 1.24988 + type: 'test' + ... +# Subtest: suppression is skipped when the scan touches no declared content column +ok 1577 - suppression is skipped when the scan touches no declared content column + --- + duration_ms: 0.725343 + type: 'test' + ... +# Subtest: a WHERE over a suppressed column cannot reveal content by row presence +ok 1578 - a WHERE over a suppressed column cannot reveal content by row presence + --- + duration_ms: 3.28325 + type: 'test' + ... +# Subtest: an ignore-classed caller sees unprovenanced content unsuppressed +ok 1579 - an ignore-classed caller sees unprovenanced content unsuppressed + --- + duration_ms: 0.51272 + type: 'test' + ... +# Subtest: a resolver failure propagates: the query fails loudly, never silently unfiltered +ok 1580 - a resolver failure propagates: the query fails loudly, never silently unfiltered + --- + duration_ms: 1.051469 + type: 'test' + ... +# Subtest: datasets with neither cwd nor a content declaration are untouched +ok 1581 - datasets with neither cwd nor a content declaration are untouched + --- + duration_ms: 0.53977 + type: 'test' + ... +# Subtest: hyp query maintain writes a stderr line when the walk loses a partition +ok 1582 - hyp query maintain writes a stderr line when the walk loses a partition + --- + duration_ms: 60.253349 + type: 'test' + ... +# Subtest: hyp query maintain writes nothing to stderr on a clean tick +ok 1583 - hyp query maintain writes nothing to stderr on a clean tick + --- + duration_ms: 39.829416 + type: 'test' + ... +# Subtest: renderOverview: aligns columns, groups thousands, and bars the largest row widest +ok 1584 - renderOverview: aligns columns, groups thousands, and bars the largest row widest + --- + duration_ms: 1.969534 + type: 'test' + ... +# Subtest: buildOverviewSql: ranked tables sort by exactly what their bar charts +ok 1585 - buildOverviewSql: ranked tables sort by exactly what their bar charts + --- + duration_ms: 0.123718 + type: 'test' + ... +# Subtest: renderOverview: bars descend with the row order in ranked tables +ok 1586 - renderOverview: bars descend with the row order in ranked tables + --- + duration_ms: 0.192302 + type: 'test' + ... +# Subtest: renderOverview: token bars split input from output and ignore cache +ok 1587 - renderOverview: token bars split input from output and ignore cache + --- + duration_ms: 0.33554 + type: 'test' + ... +# Subtest: renderOverview: a component that exists never vanishes from its bar +ok 1588 - renderOverview: a component that exists never vanishes from its bar + --- + duration_ms: 3.286284 + type: 'test' + ... +# Subtest: renderOverview: SQL is hidden behind --sql, and pointed at when hidden +ok 1589 - renderOverview: SQL is hidden behind --sql, and pointed at when hidden + --- + duration_ms: 0.277902 + type: 'test' + ... +# Subtest: renderOverview: a labelled model with no usage is counted out loud +ok 1590 - renderOverview: a labelled model with no usage is counted out loud + --- + duration_ms: 0.158531 + type: 'test' + ... +# Subtest: renderOverview: unlabelled groups are omitted silently, never called models +ok 1591 - renderOverview: unlabelled groups are omitted silently, never called models + --- + duration_ms: 0.102025 + type: 'test' + ... +# Subtest: renderOverview: an unlabelled group that DID carry tokens is named, not dropped +ok 1592 - renderOverview: an unlabelled group that DID carry tokens is named, not dropped + --- + duration_ms: 0.329972 + type: 'test' + ... +# Subtest: renderOverview: traffic with no token counts at all says so instead of an empty table +ok 1593 - renderOverview: traffic with no token counts at all says so instead of an empty table + --- + duration_ms: 0.301328 + type: 'test' + ... +# Subtest: renderOverview: nothing but unlabelled groups reports no counts, not "0 models" +ok 1594 - renderOverview: nothing but unlabelled groups reports no counts, not "0 models" + --- + duration_ms: 0.124038 + type: 'test' + ... +# Subtest: renderOverview: a cache-only model counts as measured, not as missing usage +ok 1595 - renderOverview: a cache-only model counts as measured, not as missing usage + --- + duration_ms: 0.133262 + type: 'test' + ... +# Subtest: renderOverview: the caller names the heading (setup milestone vs standing command) +ok 1596 - renderOverview: the caller names the heading (setup milestone vs standing command) + --- + duration_ms: 0.093722 + type: 'test' + ... +# Subtest: renderOverview: no rows renders the empty state with what to do next +ok 1597 - renderOverview: no rows renders the empty state with what to do next + --- + duration_ms: 0.058158 + type: 'test' + ... +# Subtest: renderOverview: folds the tail of a long provider list into a count line +ok 1598 - renderOverview: folds the tail of a long provider list into a count line + --- + duration_ms: 0.122116 + type: 'test' + ... +# Subtest: renderOverview: the caption and key wear the shades they describe +ok 1599 - renderOverview: the caption and key wear the shades they describe + --- + duration_ms: 0.142947 + type: 'test' + ... +# Subtest: renderOverview: color=true wraps in ANSI, the default emits none +ok 1600 - renderOverview: color=true wraps in ANSI, the default emits none + --- + duration_ms: 0.124479 + type: 'test' + ... +# Subtest: renderRepoMix: shortens paths, ranks by token volume, and counts repo-less sessions +ok 1601 - renderRepoMix: shortens paths, ranks by token volume, and counts repo-less sessions + --- + duration_ms: 0.372015 + type: 'test' + ... +# Subtest: renderRepoMix: no repo at all says so rather than printing an empty table +ok 1602 - renderRepoMix: no repo at all says so rather than printing an empty table + --- + duration_ms: 0.100643 + type: 'test' + ... +# Subtest: renderToolMix: ranks by calls and shows the session spread +ok 1603 - renderToolMix: ranks by calls and shows the session spread + --- + duration_ms: 0.265944 + type: 'test' + ... +# Subtest: buildOverviewSql: every section carries the same window, and tools filters tool_call +ok 1604 - buildOverviewSql: every section carries the same window, and tools filters tool_call + --- + duration_ms: 0.072971 + type: 'test' + ... +# Subtest: chooseOverviewWindow: takes the widest span that fits the row budget +ok 1605 - chooseOverviewWindow: takes the widest span that fits the row budget + --- + duration_ms: 0.518378 + type: 'test' + ... +# Subtest: chooseOverviewWindow: everything fits when the cache is small +ok 1606 - chooseOverviewWindow: everything fits when the cache is small + --- + duration_ms: 0.075225 + type: 'test' + ... +# Subtest: chooseOverviewWindow: one oversized day is still shown, never nothing +ok 1607 - chooseOverviewWindow: one oversized day is still shown, never nothing + --- + duration_ms: 0.068293 + type: 'test' + ... +# Subtest: chooseOverviewWindow: the same data yields a smaller window on a slower machine +ok 1608 - chooseOverviewWindow: the same data yields a smaller window on a slower machine + --- + duration_ms: 0.117799 + type: 'test' + ... +# Subtest: chooseOverviewWindow: the probe is charged to the budget it shares +ok 1609 - chooseOverviewWindow: the probe is charged to the budget it shares + --- + duration_ms: 0.162317 + type: 'test' + ... +# Subtest: chooseOverviewWindow: a probe that overran the budget still yields a window +ok 1610 - chooseOverviewWindow: a probe that overran the budget still yields a window + --- + duration_ms: 0.091619 + type: 'test' + ... +# Subtest: chooseOverviewWindow: the row cap still binds a fast machine +ok 1611 - chooseOverviewWindow: the row cap still binds a fast machine + --- + duration_ms: 0.075634 + type: 'test' + ... +# Subtest: chooseOverviewWindow: with no probe timing, the row cap decides alone +ok 1612 - chooseOverviewWindow: with no probe timing, the row cap decides alone + --- + duration_ms: 0.070677 + type: 'test' + ... +# Subtest: chooseOverviewWindow: the plan charges for the sections it will run, not all four +ok 1613 - chooseOverviewWindow: the plan charges for the sections it will run, not all four + --- + duration_ms: 0.126983 + type: 'test' + ... +# Subtest: chooseOverviewWindow: an explicit --days request outranks both caps +ok 1614 - chooseOverviewWindow: an explicit --days request outranks both caps + --- + duration_ms: 0.086482 + type: 'test' + ... +# Subtest: chooseOverviewWindow: unordered probe rows and an empty cache +ok 1615 - chooseOverviewWindow: unordered probe rows and an empty cache + --- + duration_ms: 0.092411 + type: 'test' + ... +# Subtest: describeWindow: states the period, and the lever - never the reason +ok 1616 - describeWindow: states the period, and the lever - never the reason + --- + duration_ms: 0.101284 + type: 'test' + ... +# Subtest: renderOverview: the window is stated under the title, always +ok 1617 - renderOverview: the window is stated under the title, always + --- + duration_ms: 0.11216 + type: 'test' + ... +# Subtest: collectOverview: probes first, then runs only the requested sections +ok 1618 - collectOverview: probes first, then runs only the requested sections + --- + duration_ms: 0.301758 + type: 'test' + ... +# Subtest: collectOverview: the window is planned for the sections actually requested +ok 1619 - collectOverview: the window is planned for the sections actually requested + --- + duration_ms: 0.23684 + type: 'test' + ... +# Subtest: missingSections: a section nobody asked for is not reported as unfinished +ok 1620 - missingSections: a section nobody asked for is not reported as unfinished + --- + duration_ms: 0.26324 + type: 'test' + ... +# Subtest: collectOverview: runs all four sections by default, in display order +ok 1621 - collectOverview: runs all four sections by default, in display order + --- + duration_ms: 0.207725 + type: 'test' + ... +# Subtest: collectOverview: an empty cache runs no section at all +ok 1622 - collectOverview: an empty cache runs no section at all + --- + duration_ms: 0.163889 + type: 'test' + ... +# Subtest: overviewRunnerFromCtx: aggregates through the real query seam +ok 1623 - overviewRunnerFromCtx: aggregates through the real query seam + --- + duration_ms: 24.27791 + type: 'test' + ... +# Subtest: overviewRunnerFromCtx: a withheld row is reported, once, not once per section +ok 1624 - overviewRunnerFromCtx: a withheld row is reported, once, not once per section + --- + duration_ms: 3.824773 + type: 'test' + ... +# Subtest: overviewRunnerFromCtx: nothing to withhold says nothing +ok 1625 - overviewRunnerFromCtx: nothing to withhold says nothing + --- + duration_ms: 12.117528 + type: 'test' + ... +# Subtest: overviewRunnerFromCtx: no query registry yields no runner +ok 1626 - overviewRunnerFromCtx: no query registry yields no runner + --- + duration_ms: 0.11843 + type: 'test' + ... +# Subtest: hyp query overview: renders all four sections from real rows +ok 1627 - hyp query overview: renders all four sections from real rows + --- + duration_ms: 11.332814 + type: 'test' + ... +# Subtest: hyp query overview: a withheld row is disclosed on stderr, off the block +ok 1628 - hyp query overview: a withheld row is disclosed on stderr, off the block + --- + duration_ms: 1.932037 + type: 'test' + ... +# Subtest: hyp query overview --sql: prints the statement above each table +ok 1629 - hyp query overview --sql: prints the statement above each table + --- + duration_ms: 6.675036 + type: 'test' + ... +# Subtest: hyp query overview --json: emits both result sets for scripting +ok 1630 - hyp query overview --json: emits both result sets for scripting + --- + duration_ms: 9.111488 + type: 'test' + ... +# Subtest: hyp query overview: no capture configured reports that, not a schema name +ok 1631 - hyp query overview: no capture configured reports that, not a schema name + --- + duration_ms: 0.239043 + type: 'test' + ... +# Subtest: hyp query overview: an empty dataset renders the empty state, not an error +ok 1632 - hyp query overview: an empty dataset renders the empty state, not an error + --- + duration_ms: 0.429983 + type: 'test' + ... +# Subtest: formatCount: groups thousands and passes non-numbers through +ok 1633 - formatCount: groups thousands and passes non-numbers through + --- + duration_ms: 0.073141 + type: 'test' + ... +# Subtest: renderRepoMix: the fold count is the real tail, and the repo-less line survives it +ok 1634 - renderRepoMix: the fold count is the real tail, and the repo-less line survives it + --- + duration_ms: 0.346827 + type: 'test' + ... +# Subtest: buildOverviewSql: no LIMIT on the sections whose tails are counted +ok 1635 - buildOverviewSql: no LIMIT on the sections whose tails are counted + --- + duration_ms: 0.115706 + type: 'test' + ... +# Subtest: renderDailyActivity: a window longer than the table says how many days it folded +ok 1636 - renderDailyActivity: a window longer than the table says how many days it folded + --- + duration_ms: 0.202748 + type: 'test' + ... +# Subtest: renderDailyActivity: a window that fits says nothing +ok 1637 - renderDailyActivity: a window that fits says nothing + --- + duration_ms: 0.122447 + type: 'test' + ... +# Subtest: buildOverviewSql: rejects a since that is not a plain date +ok 1638 - buildOverviewSql: rejects a since that is not a plain date + --- + duration_ms: 0.346647 + type: 'test' + ... +# Subtest: hyp query overview: --days=7 is honored, not silently ignored +ok 1639 - hyp query overview: --days=7 is honored, not silently ignored + --- + duration_ms: 3.872415 + type: 'test' + ... +# Subtest: hyp query overview: an unknown flag is refused, not ignored +ok 1640 - hyp query overview: an unknown flag is refused, not ignored + --- + duration_ms: 0.216689 + type: 'test' + ... +# Subtest: hyp query overview: --days rejects non-positive and non-integer values +ok 1641 - hyp query overview: --days rejects non-positive and non-integer values + --- + duration_ms: 0.372557 + type: 'test' + ... +# Subtest: hyp query overview: --help prints usage and exits 0 +ok 1642 - hyp query overview: --help prints usage and exits 0 + --- + duration_ms: 0.163178 + type: 'test' + ... +# Subtest: hyp query overview: NO_COLOR suppresses ANSI even on a TTY +ok 1643 - hyp query overview: NO_COLOR suppresses ANSI even on a TTY + --- + duration_ms: 5.200434 + type: 'test' + ... +# Subtest: hyp query overview: --include-local-only is accepted, and does what the disclosure promises +ok 1644 - hyp query overview: --include-local-only is accepted, and does what the disclosure promises + --- + duration_ms: 6.811633 + type: 'test' + ... +# Subtest: hyp query overview: the usage line lists every flag the codec accepts +ok 1645 - hyp query overview: the usage line lists every flag the codec accepts + --- + duration_ms: 0.227285 + type: 'test' + ... +# Subtest: renderOverview escapes captured columns and keeps its own colour +ok 1646 - renderOverview escapes captured columns and keeps its own colour + --- + duration_ms: 6.810861 + type: 'test' + ... +# Subtest: valid query.remotes + default_remote parses through +ok 1647 - valid query.remotes + default_remote parses through + --- + duration_ms: 1.3026 + type: 'test' + ... +# Subtest: remotes coexists with cache in the same query block +ok 1648 - remotes coexists with cache in the same query block + --- + duration_ms: 0.268939 + type: 'test' + ... +# Subtest: a non-http(s) url is rejected +ok 1649 - a non-http(s) url is rejected + --- + duration_ms: 0.177099 + type: 'test' + ... +# Subtest: a remote target without a url is rejected +ok 1650 - a remote target without a url is rejected + --- + duration_ms: 0.121134 + type: 'test' + ... +# Subtest: default_remote must name a defined target +ok 1651 - default_remote must name a defined target + --- + duration_ms: 0.12529 + type: 'test' + ... +# Subtest: a query whose heap growth exceeds the execution budget refuses with the typed error +ok 1652 - a query whose heap growth exceeds the execution budget refuses with the typed error + --- + duration_ms: 74.373691 + type: 'test' + ... +# Subtest: resolveHeapBudgetBytes resolves the effective ceiling and never disables on a blank env +ok 1653 - resolveHeapBudgetBytes resolves the effective ceiling and never disables on a blank env + --- + duration_ms: 0.359606 + type: 'test' + ... +# Subtest: maxHeapBytes 0 disables the budget entirely +ok 1654 - maxHeapBytes 0 disables the budget entirely + --- + duration_ms: 1.775819 + type: 'test' + ... +# Subtest: a pre-aborted caller signal aborts execution before rows flow +ok 1655 - a pre-aborted caller signal aborts execution before rows flow + --- + duration_ms: 0.978427 + type: 'test' + ... +# Subtest: the streaming-aggregate scanColumn fast path stays lit through the budget decoration +ok 1656 - the streaming-aggregate scanColumn fast path stays lit through the budget decoration + --- + duration_ms: 2.060031 + type: 'test' + ... +# Subtest: transient scan garbage does not trip the budget; only retained growth refuses +ok 1657 - transient scan garbage does not trip the budget; only retained growth refuses + --- + duration_ms: 587.597931 + type: 'test' + ... +# Subtest: the budget decoration forwards WHERE to scanColumn and preserves the applied flags +ok 1658 - the budget decoration forwards WHERE to scanColumn and preserves the applied flags + --- + duration_ms: 1.59871 + type: 'test' + ... +# Subtest: parse errors surface the squirreling message verbatim, unwrapped +ok 1659 - parse errors surface the squirreling message verbatim, unwrapped + --- + duration_ms: 4.479349 + type: 'test' + ... +# Subtest: non-SELECT statements surface the parser message without extra framing +ok 1660 - non-SELECT statements surface the parser message without extra framing + --- + duration_ms: 0.68443 + type: 'test' + ... +# Subtest: empty SQL is reported as required +ok 1661 - empty SQL is reported as required + --- + duration_ms: 0.341169 + type: 'test' + ... +# Subtest: spill mode: file content is the full lossless result, stdout is a receipt +ok 1662 - spill mode: file content is the full lossless result, stdout is a receipt + --- + duration_ms: 1.314437 + type: 'test' + ... +# Subtest: spill receipt preview clips cells even though the file does not +ok 1663 - spill receipt preview clips cells even though the file does not + --- + duration_ms: 0.18408 + type: 'test' + ... +# Subtest: inline mode: small result renders in full to stdout, no notice +ok 1664 - inline mode: small result renders in full to stdout, no notice + --- + duration_ms: 0.649838 + type: 'test' + ... +# Subtest: inline mode: over-budget result caps rows, stdout stays valid JSON, notice to stderr +ok 1665 - inline mode: over-budget result caps rows, stdout stays valid JSON, notice to stderr + --- + duration_ms: 1.708957 + type: 'test' + ... +# Subtest: inline mode: long cells are truncated in stdout with a marker +ok 1666 - inline mode: long cells are truncated in stdout with a marker + --- + duration_ms: 0.220826 + type: 'test' + ... +# Subtest: fork menu takes its printed default on a stdin that ends without a line +ok 1667 - fork menu takes its printed default on a stdin that ends without a line + --- + duration_ms: 3.290672 + type: 'test' + ... +# Subtest: fork menu takes its printed default on a stdin that was already spent +ok 1668 - fork menu takes its printed default on a stdin that was already spent + --- + duration_ms: 3.437034 + type: 'test' + ... +# Subtest: fork menu still honours an explicit pick +ok 1669 - fork menu still honours an explicit pick + --- + duration_ms: 0.687725 + type: 'test' + ... +# Subtest: returning gate menu takes its printed default on a stdin that ends without a line +ok 1670 - returning gate menu takes its printed default on a stdin that ends without a line + --- + duration_ms: 0.581864 + type: 'test' + ... +# Subtest: askYesNo declines on a stdin that ends without a line +ok 1671 - askYesNo declines on a stdin that ends without a line + --- + duration_ms: 1.663448 + type: 'test' + ... +# Subtest: askYesNo declines on a stdin that was already spent +ok 1672 - askYesNo declines on a stdin that was already spent + --- + duration_ms: 0.628785 + type: 'test' + ... +# Subtest: askYesNo still honours an explicit yes +ok 1673 - askYesNo still honours an explicit yes + --- + duration_ms: 0.359146 + type: 'test' + ... +# Subtest: askYesNo takes an answer delivered in the same burst as the EOF +ok 1674 - askYesNo takes an answer delivered in the same burst as the EOF + --- + duration_ms: 1.234787 + type: 'test' + ... +# Subtest: askYesNo takes a final answer with no trailing newline +ok 1675 - askYesNo takes a final answer with no trailing newline + --- + duration_ms: 1.405807 + type: 'test' + ... +# Subtest: askYesNo answers with the line that answered it, not one that arrived after +ok 1676 - askYesNo answers with the line that answered it, not one that arrived after + --- + duration_ms: 0.816411 + type: 'test' + ... +# Subtest: askYesNo keeps its answer when an unterminated line follows it +ok 1677 - askYesNo keeps its answer when an unterminated line follows it + --- + duration_ms: 0.457585 + type: 'test' + ... +# Subtest: plugin install confirm declines on a stdin that ends without a line +ok 1678 - plugin install confirm declines on a stdin that ends without a line + --- + duration_ms: 0.706514 + type: 'test' + ... +# Subtest: plugin install confirm declines on a stdin that was already spent +ok 1679 - plugin install confirm declines on a stdin that was already spent + --- + duration_ms: 3.584428 + type: 'test' + ... +# Subtest: plugin install confirm still honours an explicit yes +ok 1680 - plugin install confirm still honours an explicit yes + --- + duration_ms: 1.096467 + type: 'test' + ... +# Subtest: a second read with no change skips re-reading the file (parse cache) +ok 1681 - a second read with no change skips re-reading the file (parse cache) + --- + duration_ms: 10.057326 + type: 'test' + ... +# Subtest: a write is visible to the very next read (cache is busted on write) +ok 1682 - a write is visible to the very next read (cache is busted on write) + --- + duration_ms: 5.838994 + type: 'test' + ... +# Subtest: isRefreshable is true only for an oidc record read from the file +ok 1683 - isRefreshable is true only for an oidc record read from the file + --- + duration_ms: 0.253195 + type: 'test' + ... +# Subtest: an oidc session round-trips with kind: oidc +ok 1684 - an oidc session round-trips with kind: oidc + --- + duration_ms: 2.721536 + type: 'test' + ... +# Subtest: a record with a refreshToken but no accessJwt still yields its usable static token +ok 1685 - a record with a refreshToken but no accessJwt still yields its usable static token + --- + duration_ms: 1.410384 + type: 'test' + ... +# Subtest: an oidc record with a refresh token but no cached accessJwt is kept (refreshable) +ok 1686 - an oidc record with a refresh token but no cached accessJwt is kept (refreshable) + --- + duration_ms: 2.946969 + type: 'test' + ... +# Subtest: a static record with an empty token is dropped on read (not reported as stored) +ok 1687 - a static record with an empty token is dropped on read (not reported as stored) + --- + duration_ms: 1.404755 + type: 'test' + ... +# Subtest: an oidc record with neither a refresh token nor a static token is dropped on read +ok 1688 - an oidc record with neither a refresh token nor a static token is dropped on read + --- + duration_ms: 1.759574 + type: 'test' + ... +# Subtest: resolveToken returns an oidc record cached access JWT as-is +ok 1689 - resolveToken returns an oidc record cached access JWT as-is + --- + duration_ms: 7.202347 + type: 'test' + ... +# Subtest: resolveAccessJwt refreshes a JWT inside the skew window (not yet past) +ok 1690 - resolveAccessJwt refreshes a JWT inside the skew window (not yet past) + --- + duration_ms: 7.715898 + type: 'test' + ... +# Subtest: a legacy token-only record reads as kind: static +ok 1691 - a legacy token-only record reads as kind: static + --- + duration_ms: 1.183228 + type: 'test' + ... +# Subtest: writeToken now stamps kind: static +ok 1692 - writeToken now stamps kind: static + --- + duration_ms: 8.216798 + type: 'test' + ... +# Subtest: writing one target preserves a sibling record that does not normalize +ok 1693 - writing one target preserves a sibling record that does not normalize + --- + duration_ms: 3.203519 + type: 'test' + ... +# Subtest: removeToken drops a record that does not normalize and keeps the rest +ok 1694 - removeToken drops a record that does not normalize and keeps the rest + --- + duration_ms: 4.03334 + type: 'test' + ... +# Subtest: removeToken clears an oidc record too +ok 1695 - removeToken clears an oidc record too + --- + duration_ms: 2.884524 + type: 'test' + ... +# Subtest: resolveAccessJwt returns a static token unchanged +ok 1696 - resolveAccessJwt returns a static token unchanged + --- + duration_ms: 4.899907 + type: 'test' + ... +# Subtest: resolveAccessJwt honors the per-target env override over the file +ok 1697 - resolveAccessJwt honors the per-target env override over the file + --- + duration_ms: 1.649497 + type: 'test' + ... +# Subtest: resolveAccessJwt returns a fresh oidc JWT without calling refresh +ok 1698 - resolveAccessJwt returns a fresh oidc JWT without calling refresh + --- + duration_ms: 1.512248 + type: 'test' + ... +# Subtest: resolveAccessJwt refreshes a stale oidc JWT and persists the new one +ok 1699 - resolveAccessJwt refreshes a stale oidc JWT and persists the new one + --- + duration_ms: 6.267566 + type: 'test' + ... +# Subtest: resolveAccessJwt persists a rotated refresh token from the refresh response +ok 1700 - resolveAccessJwt persists a rotated refresh token from the refresh response + --- + duration_ms: 3.537887 + type: 'test' + ... +# Subtest: resolveAccessJwt keeps the stored refresh token when the server does not rotate +ok 1701 - resolveAccessJwt keeps the stored refresh token when the server does not rotate + --- + duration_ms: 4.299474 + type: 'test' + ... +# Subtest: concurrent resolveAccessJwt is single-flight: the loser adopts the winner with no second token call +ok 1702 - concurrent resolveAccessJwt is single-flight: the loser adopts the winner with no second token call + --- + duration_ms: 29.928759 + type: 'test' + ... +# Subtest: resolveAccessJwt does not resurrect a session removed before the refresh +ok 1703 - resolveAccessJwt does not resurrect a session removed before the refresh + --- + duration_ms: 3.045559 + type: 'test' + ... +# Subtest: a refresh does not resurrect a record removed during the network call (commit is a compare-and-swap) +ok 1704 - a refresh does not resurrect a record removed during the network call (commit is a compare-and-swap) + --- + duration_ms: 9.318393 + type: 'test' + ... +# Subtest: a refresh does not clobber a record a concurrent login replaced during the network call +ok 1705 - a refresh does not clobber a record a concurrent login replaced during the network call + --- + duration_ms: 7.88847 + type: 'test' + ... +# Subtest: resolveAccessJwt keeps the stored org when a refresh response omits it +ok 1706 - resolveAccessJwt keeps the stored org when a refresh response omits it + --- + duration_ms: 3.557106 + type: 'test' + ... +# Subtest: resolveAccessJwt errors with login guidance when no record exists +ok 1707 - resolveAccessJwt errors with login guidance when no record exists + --- + duration_ms: 0.542314 + type: 'test' + ... +# Subtest: resolveAccessJwt with forceRefresh refreshes even when the cached JWT is still clock-fresh +ok 1708 - resolveAccessJwt with forceRefresh refreshes even when the cached JWT is still clock-fresh + --- + duration_ms: 3.038839 + type: 'test' + ... +# Subtest: resolveAccessJwt refreshes with the freshest stored refresh token in one shot +ok 1709 - resolveAccessJwt refreshes with the freshest stored refresh token in one shot + --- + duration_ms: 5.505257 + type: 'test' + ... +# Subtest: a write breaks a lock left stale by a crashed holder +ok 1710 - a write breaks a lock left stale by a crashed holder + --- + duration_ms: 28.854696 + type: 'test' + ... +# Subtest: resolveAccessJwt propagates a refresh failure (invalid_grant) +ok 1711 - resolveAccessJwt propagates a refresh failure (invalid_grant) + --- + duration_ms: 3.054733 + type: 'test' + ... +# Subtest: env var name is per-target and sanitized +ok 1712 - env var name is per-target and sanitized + --- + duration_ms: 0.641555 + type: 'test' + ... +# Subtest: missing file reads as an empty map (not an error) +ok 1713 - missing file reads as an empty map (not an error) + --- + duration_ms: 4.442402 + type: 'test' + ... +# Subtest: writeToken persists, is 0600, and round-trips +ok 1714 - writeToken persists, is 0600, and round-trips + --- + duration_ms: 4.078229 + type: 'test' + ... +# Subtest: writeToken merges, removeToken drops only the named target +ok 1715 - writeToken merges, removeToken drops only the named target + --- + duration_ms: 9.871704 + type: 'test' + ... +# Subtest: resolveToken order: env overrides file +ok 1716 - resolveToken order: env overrides file + --- + duration_ms: 3.142146 + type: 'test' + ... +# Subtest: resolveToken errors with guidance when neither env nor file has a token +ok 1717 - resolveToken errors with guidance when neither env nor file has a token + --- + duration_ms: 1.444896 + type: 'test' + ... +# Subtest: a corrupt credentials file throws rather than silently masking +ok 1718 - a corrupt credentials file throws rather than silently masking + --- + duration_ms: 1.795279 + type: 'test' + ... +# Subtest: sessionExpiredMessage names the target for re-login +ok 1719 - sessionExpiredMessage names the target for re-login + --- + duration_ms: 0.84821 + type: 'test' + ... +# Subtest: exchangeCode posts the authorization_code grant and maps the response +ok 1720 - exchangeCode posts the authorization_code grant and maps the response + --- + duration_ms: 1.350072 + type: 'test' + ... +# Subtest: exchangeCode sends the host label with the authorization_code grant when given +ok 1721 - exchangeCode sends the host label with the authorization_code grant when given + --- + duration_ms: 0.23038 + type: 'test' + ... +# Subtest: exchangeCode omits host from the body when none is given +ok 1722 - exchangeCode omits host from the body when none is given + --- + duration_ms: 0.25701 + type: 'test' + ... +# Subtest: exchangeCode captures the login-minted gateway credential (LLP 0061 D1) +ok 1723 - exchangeCode captures the login-minted gateway credential (LLP 0061 D1) + --- + duration_ms: 0.345445 + type: 'test' + ... +# Subtest: exchangeCode against a server without login-gateway support carries no gateway +ok 1724 - exchangeCode against a server without login-gateway support carries no gateway + --- + duration_ms: 0.212943 + type: 'test' + ... +# Subtest: a partial gateway_* set is a contract violation, not a silent drop +ok 1725 - a partial gateway_* set is a contract violation, not a silent drop + --- + duration_ms: 0.567883 + type: 'test' + ... +# Subtest: an ISO gateway_expires_at converts down to an epoch second (identity.json stores epochs) +ok 1726 - an ISO gateway_expires_at converts down to an epoch second (identity.json stores epochs) + --- + duration_ms: 0.197229 + type: 'test' + ... +# Subtest: exchangeCode accepts the server epoch-second expires_at and normalizes it to ISO +ok 1727 - exchangeCode accepts the server epoch-second expires_at and normalizes it to ISO + --- + duration_ms: 0.614184 + type: 'test' + ... +# Subtest: refreshSession accepts the server epoch-second expires_at and normalizes it to ISO +ok 1728 - refreshSession accepts the server epoch-second expires_at and normalizes it to ISO + --- + duration_ms: 0.690279 + type: 'test' + ... +# Subtest: refreshSession posts the refresh_token grant and maps the response +ok 1729 - refreshSession posts the refresh_token grant and maps the response + --- + duration_ms: 0.322329 + type: 'test' + ... +# Subtest: refreshSession returns a rotated refresh_token when the server issues one +ok 1730 - refreshSession returns a rotated refresh_token when the server issues one + --- + duration_ms: 1.255729 + type: 'test' + ... +# Subtest: refreshSession tolerates a response that omits org (returns org: "") +ok 1731 - refreshSession tolerates a response that omits org (returns org: "") + --- + duration_ms: 0.215878 + type: 'test' + ... +# Subtest: a 401 invalid_grant surfaces a typed InvalidGrantError +ok 1732 - a 401 invalid_grant surfaces a typed InvalidGrantError + --- + duration_ms: 0.260336 + type: 'test' + ... +# Subtest: a 401 on the authorization_code grant does not borrow the refresh-token wording +ok 1733 - a 401 on the authorization_code grant does not borrow the refresh-token wording + --- + duration_ms: 0.319596 + type: 'test' + ... +# Subtest: a 401 with an empty body still surfaces InvalidGrantError (re-login guidance) +ok 1734 - a 401 with an empty body still surfaces InvalidGrantError (re-login guidance) + --- + duration_ms: 0.196729 + type: 'test' + ... +# Subtest: a 401 with a non-JSON body still surfaces InvalidGrantError +ok 1735 - a 401 with a non-JSON body still surfaces InvalidGrantError + --- + duration_ms: 0.265434 + type: 'test' + ... +# Subtest: a non-invalid_grant error throws a generic error, not InvalidGrantError +ok 1736 - a non-invalid_grant error throws a generic error, not InvalidGrantError + --- + duration_ms: 0.194495 + type: 'test' + ... +# Subtest: a response missing access_jwt is rejected +ok 1737 - a response missing access_jwt is rejected + --- + duration_ms: 0.156628 + type: 'test' + ... +# Subtest: a 2xx with an empty body fails as transient, not a misleading missing-field error +ok 1738 - a 2xx with an empty body fails as transient, not a misleading missing-field error + --- + duration_ms: 0.201176 + type: 'test' + ... +# Subtest: a non-date expires_at string is rejected at refresh time, not stored to loop forever +ok 1739 - a non-date expires_at string is rejected at refresh time, not stored to loop forever + --- + duration_ms: 0.225372 + type: 'test' + ... +# Subtest: describeRefreshError maps invalid_grant to session-expired re-login guidance +ok 1740 - describeRefreshError maps invalid_grant to session-expired re-login guidance + --- + duration_ms: 0.161966 + type: 'test' + ... +# Subtest: describeRefreshError passes a non-invalid_grant error through as a generic message +ok 1741 - describeRefreshError passes a non-invalid_grant error through as a generic message + --- + duration_ms: 0.095935 + type: 'test' + ... +# Subtest: deriveIdentityBase yields /v1/identity +ok 1742 - deriveIdentityBase yields /v1/identity + --- + duration_ms: 1.433729 + type: 'test' + ... +# Subtest: browser mode forwards --org and the derived identity base, then stores the session +ok 1743 - browser mode forwards --org and the derived identity base, then stores the session + --- + duration_ms: 25.986397 + type: 'test' + ... +# Subtest: a successful sign-in whose session write fails reports a store failure, not a login failure +ok 1744 - a successful sign-in whose session write fails reports a store failure, not a login failure + --- + duration_ms: 2.072661 + type: 'test' + ... +# Subtest: a server refusal is reported as its own reason, not just an exit code +ok 1745 - a server refusal is reported as its own reason, not just an exit code + --- + duration_ms: 6.975643 + type: 'test' + ... +# Subtest: a local failure with no server code is retriable, and a success is ok +ok 1746 - a local failure with no server code is retriable, and a success is ok + --- + duration_ms: 8.635976 + type: 'test' + ... +# Subtest: post-auth failures name their step rather than collapsing into the login failure +ok 1747 - post-auth failures name their step rather than collapsing into the login failure + --- + duration_ms: 25.284731 + type: 'test' + ... +# Subtest: a usage error and the exclusivity gate are distinguishable, both exit 2 +ok 1748 - a usage error and the exclusivity gate are distinguishable, both exit 2 + --- + duration_ms: 5.590477 + type: 'test' + ... +# Subtest: runRemoteLogin stays the exit-code adapter over the same run +ok 1749 - runRemoteLogin stays the exit-code adapter over the same run + --- + duration_ms: 3.335529 + type: 'test' + ... +# Subtest: --no-browser passes noBrowser through to the flow +ok 1750 - --no-browser passes noBrowser through to the flow + --- + duration_ms: 4.555314 + type: 'test' + ... +# Subtest: --no-browser still uses browser mode when stdin is non-TTY +ok 1751 - --no-browser still uses browser mode when stdin is non-TTY + --- + duration_ms: 5.557357 + type: 'test' + ... +# Subtest: a login-minted gateway credential seeds the matching central sink (LLP 0061 D2/D5) +ok 1752 - a login-minted gateway credential seeds the matching central sink (LLP 0061 D2/D5) + --- + duration_ms: 4.518007 + type: 'test' + ... +# Subtest: a configured persisted_path is honored and non-matching central sinks are not seeded +ok 1753 - a configured persisted_path is honored and non-matching central sinks are not seeded + --- + duration_ms: 4.491867 + type: 'test' + ... +# Subtest: a gateway credential with no matching central sink provisions one, forwarding from one command (LLP 0063 D2) +ok 1754 - a gateway credential with no matching central sink provisions one, forwarding from one command (LLP 0063 D2) + --- + duration_ms: 5.554292 + type: 'test' + ... +# Subtest: an enrolling login waits for the reconcile and reports the clients that actually attached +ok 1755 - an enrolling login waits for the reconcile and reports the clients that actually attached + --- + duration_ms: 8.549505 + type: 'test' + ... +# Subtest: an enrolling login into an org with no config times out the wait and points at hyp status +ok 1756 - an enrolling login into an org with no config times out the wait and points at hyp status + --- + duration_ms: 3.707906 + type: 'test' + ... +# Subtest: a failed daemon install reports it and does not wait for attach +ok 1757 - a failed daemon install reports it and does not wait for attach + --- + duration_ms: 7.999218 + type: 'test' + ... +# Subtest: an enrolling login whose attach poll throws still reports the timeout fallback, not a failure (Major 1) +ok 1758 - an enrolling login whose attach poll throws still reports the timeout fallback, not a failure (Major 1) + --- + duration_ms: 3.715397 + type: 'test' + ... +# Subtest: the enrolling login announces the attach wait on stderr before polling (Major 2) +ok 1759 - the enrolling login announces the attach wait on stderr before polling (Major 2) + --- + duration_ms: 3.234326 + type: 'test' + ... +# Subtest: waitForClientAttach returns attached client names as soon as the reconcile lands +ok 1760 - waitForClientAttach returns attached client names as soon as the reconcile lands + --- + duration_ms: 0.213985 + type: 'test' + ... +# Subtest: waitForClientAttach returns empty on timeout without hanging +ok 1761 - waitForClientAttach returns empty on timeout without hanging + --- + duration_ms: 0.110949 + type: 'test' + ... +# Subtest: waitForClientAttach swallows a probe that throws mid-poll and still times out to empty (Major 1) +ok 1762 - waitForClientAttach swallows a probe that throws mid-poll and still times out to empty (Major 1) + --- + duration_ms: 0.151581 + type: 'test' + ... +# Subtest: waitForCentralConverge: the applied slot is convergence (ok:true) +ok 1763 - waitForCentralConverge: the applied slot is convergence (ok:true) + --- + duration_ms: 0.227296 + type: 'test' + ... +# Subtest: waitForCentralConverge: a timeout is the no-org-config steady state (ok:false) +ok 1764 - waitForCentralConverge: a timeout is the no-org-config steady state (ok:false) + --- + duration_ms: 0.101805 + type: 'test' + ... +# Subtest: waitForCentralConverge: a throwing probe is "not converged this tick", polled to timeout +ok 1765 - waitForCentralConverge: a throwing probe is "not converged this tick", polled to timeout + --- + duration_ms: 0.162186 + type: 'test' + ... +# Subtest: waitForCentralConverge: default probe converges on the active slot, never the seed +ok 1766 - waitForCentralConverge: default probe converges on the active slot, never the seed + --- + duration_ms: 3.149447 + type: 'test' + ... +# Subtest: waitForCentralConverge: an unreadable active pointer reaches the probe-error branch +ok 1767 - waitForCentralConverge: an unreadable active pointer reaches the probe-error branch + --- + duration_ms: 1.426118 + type: 'test' + ... +# Subtest: --no-forward signs in for queries only and provisions nothing (LLP 0063 D3) +ok 1768 - --no-forward signs in for queries only and provisions nothing (LLP 0063 D3) + --- + duration_ms: 2.748788 + type: 'test' + ... +# Subtest: login to a different server than the one this machine is enrolled to is rejected before the browser (LLP 0063 D4) +ok 1769 - login to a different server than the one this machine is enrolled to is rejected before the browser (LLP 0063 D4) + --- + duration_ms: 1.960069 + type: 'test' + ... +# Subtest: an unreadable central layer fails the D4 gate CLOSED: login to a different server is rejected (LLP 0063 D4) +ok 1770 - an unreadable central layer fails the D4 gate CLOSED: login to a different server is rejected (LLP 0063 D4) + --- + duration_ms: 1.939648 + type: 'test' + ... +# Subtest: an unreadable central layer also refuses a same-origin re-login: the gate cannot tell it is the same (LLP 0063 D4) +ok 1771 - an unreadable central layer also refuses a same-origin re-login: the gate cannot tell it is the same (LLP 0063 D4) + --- + duration_ms: 1.675146 + type: 'test' + ... +# Subtest: an active-slot pointer naming a file that is gone is unreadable, not absent (LLP 0063 D4) +ok 1772 - an active-slot pointer naming a file that is gone is unreadable, not absent (LLP 0063 D4) + --- + duration_ms: 12.207545 + type: 'test' + ... +# Subtest: a central layer whose PATH cannot be resolved fails the D4 gate CLOSED too (LLP 0063 D4) +ok 1773 - a central layer whose PATH cannot be resolved fails the D4 gate CLOSED too (LLP 0063 D4) + --- + duration_ms: 6.590196 + type: 'test' + ... +# Subtest: a central layer directory that cannot be listed fails the D4 gate CLOSED (LLP 0063 D4) +ok 1774 - a central layer directory that cannot be listed fails the D4 gate CLOSED (LLP 0063 D4) + --- + duration_ms: 4.995803 + type: 'test' + ... +# Subtest: an unresolvable pointer does not overshoot: control-dir residue that is not a layer still permits login (LLP 0063 D4) +ok 1775 - an unresolvable pointer does not overshoot: control-dir residue that is not a layer still permits login (LLP 0063 D4) + --- + duration_ms: 6.438836 + type: 'test' + ... +# Subtest: an ABSENT central layer is not an enrollment and still permits login (LLP 0063 D4) +ok 1776 - an ABSENT central layer is not an enrollment and still permits login (LLP 0063 D4) + --- + duration_ms: 2.761708 + type: 'test' + ... +# Subtest: a PARSEABLE central layer keeps its D4 behavior: same origin re-logs in, different origin is rejected (LLP 0063 D4) +ok 1777 - a PARSEABLE central layer keeps its D4 behavior: same origin re-logs in, different origin is rejected (LLP 0063 D4) + --- + duration_ms: 7.070406 + type: 'test' + ... +# Subtest: a hand-authored LOCAL central sink is not an enrollment and does not block login to a different server (LLP 0063 D4) +ok 1778 - a hand-authored LOCAL central sink is not an enrollment and does not block login to a different server (LLP 0063 D4) + --- + duration_ms: 3.684541 + type: 'test' + ... +# Subtest: --no-forward on an already-enrolled machine reports the truth (stays enrolled), not "not enrolled" (LLP 0063 D3) +ok 1779 - --no-forward on an already-enrolled machine reports the truth (stays enrolled), not "not enrolled" (LLP 0063 D3) + --- + duration_ms: 3.507701 + type: 'test' + ... +# Subtest: a failure seeding the identity rolls the provisioned seed back so no credential-less sink lingers (LLP 0063) +ok 1780 - a failure seeding the identity rolls the provisioned seed back so no credential-less sink lingers (LLP 0063) + --- + duration_ms: 6.807276 + type: 'test' + ... +# Subtest: a session without a gateway credential seeds nothing and prints no forwarding output +ok 1781 - a session without a gateway credential seeds nothing and prints no forwarding output + --- + duration_ms: 3.668836 + type: 'test' + ... +# Subtest: replacing a bootstrap-minted identity is reported, never silent (LLP 0061 D4) +ok 1782 - replacing a bootstrap-minted identity is reported, never silent (LLP 0061 D4) + --- + duration_ms: 3.195697 + type: 'test' + ... +# Subtest: a seed write failure reports signed-in-but-not-seeded, not a login failure +ok 1783 - a seed write failure reports signed-in-but-not-seeded, not a login failure + --- + duration_ms: 6.028793 + type: 'test' + ... +# Subtest: the host label defaults to the machine hostname and --host overrides it (LLP 0061 D6) +ok 1784 - the host label defaults to the machine hostname and --host overrides it (LLP 0061 D6) + --- + duration_ms: 6.441992 + type: 'test' + ... +# Subtest: --host as the last arg with no value is a usage error +ok 1785 - --host as the last arg with no value is a usage error + --- + duration_ms: 0.563196 + type: 'test' + ... +# Subtest: a callback error maps to a clear org-selection message +ok 1786 - a callback error maps to a clear org-selection message + --- + duration_ms: 1.326556 + type: 'test' + ... +# Subtest: a browser login timeout points at the headless escape hatches +ok 1787 - a browser login timeout points at the headless escape hatches + --- + duration_ms: 1.268748 + type: 'test' + ... +# Subtest: a server callback error does not append the headless hint (it is already actionable) +ok 1788 - a server callback error does not append the headless hint (it is already actionable) + --- + duration_ms: 4.175647 + type: 'test' + ... +# Subtest: no_membership maps to its own message +ok 1789 - no_membership maps to its own message + --- + duration_ms: 1.68471 + type: 'test' + ... +# Subtest: browser mode on an unconfigured target refuses before any flow +ok 1790 - browser mode on an unconfigured target refuses before any flow + --- + duration_ms: 0.897704 + type: 'test' + ... +# Subtest: the static --token-file path is unchanged (stores kind: static) +ok 1791 - the static --token-file path is unchanged (stores kind: static) + --- + duration_ms: 2.924695 + type: 'test' + ... +# Subtest: a static login write failure keeps the friendly hyp remote login: message +ok 1792 - a static login write failure keeps the friendly hyp remote login: message + --- + duration_ms: 1.390364 + type: 'test' + ... +# Subtest: a remove whose token removal fails reports the partial state, not a raw throw +ok 1793 - a remove whose token removal fails reports the partial state, not a raw throw + --- + duration_ms: 1.887689 + type: 'test' + ... +# Subtest: a piped stdin token still takes the static path +ok 1794 - a piped stdin token still takes the static path + --- + duration_ms: 2.854299 + type: 'test' + ... +# Subtest: an empty piped stdin (no token) points at --browser instead of just "empty token" +ok 1795 - an empty piped stdin (no token) points at --browser instead of just "empty token" + --- + duration_ms: 0.815019 + type: 'test' + ... +# Subtest: --no-browser takes the browser flow even with a piped token (the flag wins) +ok 1796 - --no-browser takes the browser flow even with a piped token (the flag wins) + --- + duration_ms: 2.94711 + type: 'test' + ... +# Subtest: --browser overrides a piped stdin token and takes the browser flow +ok 1797 - --browser overrides a piped stdin token and takes the browser flow + --- + duration_ms: 2.411345 + type: 'test' + ... +# Subtest: a missing target name resolves the default (built-in) target; a value flag is not misread as the name +ok 1798 - a missing target name resolves the default (built-in) target; a value flag is not misread as the name + --- + duration_ms: 9.045568 + type: 'test' + ... +# Subtest: --org as the last arg with no value is a usage error +ok 1799 - --org as the last arg with no value is a usage error + --- + duration_ms: 0.842481 + type: 'test' + ... +# Subtest: --org is noted as ignored when a static token forces the static path +ok 1800 - --org is noted as ignored when a static token forces the static path + --- + duration_ms: 2.732383 + type: 'test' + ... +# Subtest: --org=acme (equals form) is honored, not silently dropped +ok 1801 - --org=acme (equals form) is honored, not silently dropped + --- + duration_ms: 2.527241 + type: 'test' + ... +# Subtest: --token-file=path (equals form) takes the static path, not the browser flow +ok 1802 - --token-file=path (equals form) takes the static path, not the browser flow + --- + duration_ms: 7.246754 + type: 'test' + ... +# Subtest: --org= (equals form, empty value) is a usage error +ok 1803 - --org= (equals form, empty value) is a usage error + --- + duration_ms: 0.890183 + type: 'test' + ... +# Subtest: a --no-daemon login prints the durable hint and provisions the sink (LLP 0102) +ok 1804 - a --no-daemon login prints the durable hint and provisions the sink (LLP 0102) + --- + duration_ms: 19.357862 + type: 'test' + ... +# Subtest: a fresh enroll prints the durable hint and never polls a capture wait (LLP 0102) +ok 1805 - a fresh enroll prints the durable hint and never polls a capture wait (LLP 0102) + --- + duration_ms: 3.607594 + type: 'test' + ... +# Subtest: a failed daemon install still prints the durable hint before returning (LLP 0102) +ok 1806 - a failed daemon install still prints the durable hint before returning (LLP 0102) + --- + duration_ms: 3.876582 + type: 'test' + ... +# Subtest: a re-login (already-enrolled, re-seed path) prints the durable hint (LLP 0102) +ok 1807 - a re-login (already-enrolled, re-seed path) prints the durable hint (LLP 0102) + --- + duration_ms: 8.089305 + type: 'test' + ... +# Subtest: a fresh enroll writes the first-sync hold BEFORE enrollCentralSink, with a future deadline (LLP 0101) +ok 1808 - a fresh enroll writes the first-sync hold BEFORE enrollCentralSink, with a future deadline (LLP 0101) + --- + duration_ms: 5.442813 + type: 'test' + ... +# Subtest: a fresh enroll on a TTY prints the deadline message on stderr (LLP 0100 R1) +ok 1809 - a fresh enroll on a TTY prints the deadline message on stderr (LLP 0100 R1) + --- + duration_ms: 4.362581 + type: 'test' + ... +# Subtest: a fresh enroll on non-TTY stdin prints the same deadline message on stderr (LLP 0100 R1) +ok 1810 - a fresh enroll on non-TTY stdin prints the same deadline message on stderr (LLP 0100 R1) + --- + duration_ms: 4.253825 + type: 'test' + ... +# Subtest: --no-daemon still prints the deadline message: the hold is already committed regardless of the daemon install +ok 1811 - --no-daemon still prints the deadline message: the hold is already committed regardless of the daemon install + --- + duration_ms: 9.765773 + type: 'test' + ... +# Subtest: an enrolling login names the server and prints no URL: terminals autolink one, and the server root is not browsable (\#391) +ok 1812 - an enrolling login names the server and prints no URL: terminals autolink one, and the server root is not browsable (\#391) + --- + duration_ms: 8.110447 + type: 'test' + ... +# Subtest: a bare 'hyp remote login' names the default target it resolved, and that name is still recoverable +ok 1813 - a bare 'hyp remote login' names the default target it resolved, and that name is still recoverable + --- + duration_ms: 5.04608 + type: 'test' + ... +# Subtest: a re-login (already-enrolled) prints no deadline message: there is no first sync to defer +ok 1814 - a re-login (already-enrolled) prints no deadline message: there is no first sync to defer + --- + duration_ms: 4.781588 + type: 'test' + ... +# Subtest: a fresh enroll whose enrollment throws still holds (the marker landed pre-enroll and is never cleared) (LLP 0101) +ok 1815 - a fresh enroll whose enrollment throws still holds (the marker landed pre-enroll and is never cleared) (LLP 0101) + --- + duration_ms: 2.594324 + type: 'test' + ... +# Subtest: a re-login (already-enrolled, re-seed path) writes no first-sync hold (LLP 0101 \#which) +ok 1816 - a re-login (already-enrolled, re-seed path) writes no first-sync hold (LLP 0101 \#which) + --- + duration_ms: 2.550056 + type: 'test' + ... +# Subtest: a query-only login (no gateway credential minted) writes no first-sync hold +ok 1817 - a query-only login (no gateway credential minted) writes no first-sync hold + --- + duration_ms: 4.985697 + type: 'test' + ... +# Subtest: --no-forward writes no first-sync hold (declines enrollment entirely) +ok 1818 - --no-forward writes no first-sync hold (declines enrollment entirely) + --- + duration_ms: 13.965096 + type: 'test' + ... +# Subtest: a matching state resolves { code } and closes the listener +ok 1819 - a matching state resolves { code } and closes the listener + --- + duration_ms: 29.009441 + type: 'test' + ... +# Subtest: a mismatched state is ignored, not consumed: the flow keeps waiting +ok 1820 - a mismatched state is ignored, not consumed: the flow keeps waiting + --- + duration_ms: 8.436052 + type: 'test' + ... +# Subtest: an error= callback rejects with the error code attached +ok 1821 - an error= callback rejects with the error code attached + --- + duration_ms: 4.364383 + type: 'test' + ... +# Subtest: an admission refusal names the reason and links contact on a managed target +ok 1822 - an admission refusal names the reason and links contact on a managed target + --- + duration_ms: 3.11845 + type: 'test' + ... +# Subtest: a self-hosted target sends the reader to their own admin, not to us +ok 1823 - a self-hosted target sends the reader to their own admin, not to us + --- + duration_ms: 6.078709 + type: 'test' + ... +# Subtest: a contact URL that is not a plain https link is dropped, not rendered +ok 1824 - a contact URL that is not a plain https link is dropped, not rendered + --- + duration_ms: 6.508472 + type: 'test' + ... +# Subtest: a provider denial says so instead of falling through to the generic page +ok 1825 - a provider denial says so instead of falling through to the generic page + --- + duration_ms: 5.032749 + type: 'test' + ... +# Subtest: an unknown error code still gets the generic page +ok 1826 - an unknown error code still gets the generic page + --- + duration_ms: 2.427199 + type: 'test' + ... +# Subtest: an org refusal points at --org rather than enumerating the account orgs +ok 1827 - an org refusal points at --org rather than enumerating the account orgs + --- + duration_ms: 8.39555 + type: 'test' + ... +# Subtest: an error code that names an Object.prototype key still gets the generic page +ok 1828 - an error code that names an Object.prototype key still gets the generic page + --- + duration_ms: 7.314638 + type: 'test' + ... +# Subtest: an error= callback with no state is ignored, not surfaced (anti-DoS) +ok 1829 - an error= callback with no state is ignored, not surfaced (anti-DoS) + --- + duration_ms: 3.236309 + type: 'test' + ... +# Subtest: a timeout rejects +ok 1830 - a timeout rejects + --- + duration_ms: 51.145316 + type: 'test' + ... +# Subtest: close() before a code arrives rejects a pending waitForCode (no hang) +ok 1831 - close() before a code arrives rejects a pending waitForCode (no hang) + --- + duration_ms: 0.537437 + type: 'test' + ... +# Subtest: a malformed request target returns 400 without crashing or settling the flow +ok 1832 - a malformed request target returns 400 without crashing or settling the flow + --- + duration_ms: 4.983424 + type: 'test' + ... +# Subtest: a request to a path other than /callback does not consume the single shot +ok 1833 - a request to a path other than /callback does not consume the single shot + --- + duration_ms: 3.126973 + type: 'test' + ... +# Subtest: deriveMcpEndpoint: derive-from-base and back-compat forms +ok 1834 - deriveMcpEndpoint: derive-from-base and back-compat forms + --- + duration_ms: 0.873056 + type: 'test' + ... +# Subtest: a base-URL target sends the verb MCP call to /v1/mcp +ok 1835 - a base-URL target sends the verb MCP call to /v1/mcp + --- + duration_ms: 11.681264 + type: 'test' + ... +# Subtest: a base-URL target with a trailing slash still lands on /v1/mcp +ok 1836 - a base-URL target with a trailing slash still lands on /v1/mcp + --- + duration_ms: 3.584098 + type: 'test' + ... +# Subtest: a URL that already ends in /v1/mcp is used verbatim (back-compat) +ok 1837 - a URL that already ends in /v1/mcp is used verbatim (back-compat) + --- + duration_ms: 7.628534 + type: 'test' + ... +# Subtest: the stdio proxy forwards a base-URL target to /v1/mcp +ok 1838 - the stdio proxy forwards a base-URL target to /v1/mcp + --- + duration_ms: 5.181325 + type: 'test' + ... +# Subtest: drives PKCE -> loopback -> exchange and returns the session +ok 1839 - drives PKCE -> loopback -> exchange and returns the session + --- + duration_ms: 3.707585 + type: 'test' + ... +# Subtest: the loopback receiver gets a contact URL only for a Hyperparam-run target +ok 1840 - the loopback receiver gets a contact URL only for a Hyperparam-run target + --- + duration_ms: 1.524037 + type: 'test' + ... +# Subtest: --no-browser prints the URL instead of opening it +ok 1841 - --no-browser prints the URL instead of opening it + --- + duration_ms: 0.417925 + type: 'test' + ... +# Subtest: closes the loopback even when the flow rejects +ok 1842 - closes the loopback even when the flow rejects + --- + duration_ms: 0.646443 + type: 'test' + ... +# Subtest: buildStartUrl omits org when not given +ok 1843 - buildStartUrl omits org when not given + --- + duration_ms: 0.201376 + type: 'test' + ... +# Subtest: darwin uses `open` +ok 1844 - darwin uses `open` + --- + duration_ms: 1.272084 + type: 'test' + ... +# Subtest: linux uses `xdg-open` +ok 1845 - linux uses `xdg-open` + --- + duration_ms: 0.141365 + type: 'test' + ... +# Subtest: win32 uses rundll32 so `&` in the URL is not a cmd separator +ok 1846 - win32 uses rundll32 so `&` in the URL is not a cmd separator + --- + duration_ms: 0.188456 + type: 'test' + ... +# Subtest: a synchronous spawn throw returns false (caller prints the URL) +ok 1847 - a synchronous spawn throw returns false (caller prints the URL) + --- + duration_ms: 0.153293 + type: 'test' + ... +# Subtest: an async spawn ENOENT (missing opener) does not crash the process +ok 1848 - an async spawn ENOENT (missing opener) does not crash the process + --- + duration_ms: 10.371554 + type: 'test' + ... +# Subtest: challenge is the base64url SHA-256 of the verifier +ok 1849 - challenge is the base64url SHA-256 of the verifier + --- + duration_ms: 1.363322 + type: 'test' + ... +# Subtest: verifier and challenge are base64url (no +/= padding chars) +ok 1850 - verifier and challenge are base64url (no +/= padding chars) + --- + duration_ms: 0.286746 + type: 'test' + ... +# Subtest: two pairs differ (fresh randomness per flow) +ok 1851 - two pairs differ (fresh randomness per flow) + --- + duration_ms: 0.273706 + type: 'test' + ... +# Subtest: proxy refreshes an oidc session on a live 401 and retries (no longer dies on a stale JWT) +ok 1852 - proxy refreshes an oidc session on a live 401 and retries (no longer dies on a stale JWT) + --- + duration_ms: 20.892575 + type: 'test' + ... +# Subtest: a stale JWT at startup refreshes once (lazily on the first message), not twice +ok 1853 - a stale JWT at startup refreshes once (lazily on the first message), not twice + --- + duration_ms: 5.630187 + type: 'test' + ... +# Subtest: proxy surfaces a failed forced refresh instead of a bare HTTP 401 +ok 1854 - proxy surfaces a failed forced refresh instead of a bare HTTP 401 + --- + duration_ms: 4.951065 + type: 'test' + ... +# Subtest: proxy gives re-login guidance, not a bare 401, when a clean refresh is still rejected +ok 1855 - proxy gives re-login guidance, not a bare 401, when a clean refresh is still rejected + --- + duration_ms: 6.712652 + type: 'test' + ... +# Subtest: proxy surfaces re-login guidance when the refresh is rejected (invalid_grant) +ok 1856 - proxy surfaces re-login guidance when the refresh is rejected (invalid_grant) + --- + duration_ms: 5.343973 + type: 'test' + ... +# Subtest: a stale stored JWT is refreshed and persisted before the call +ok 1857 - a stale stored JWT is refreshed and persisted before the call + --- + duration_ms: 15.458846 + type: 'test' + ... +# Subtest: a 401 mid-flight triggers exactly one refresh + retry +ok 1858 - a 401 mid-flight triggers exactly one refresh + retry + --- + duration_ms: 6.130758 + type: 'test' + ... +# Subtest: a notification 401 or 403 during handshake triggers exactly one refresh + retry + # Subtest: HTTP 401 + ok 1 - HTTP 401 + --- + duration_ms: 7.248907 + type: 'test' + ... + # Subtest: HTTP 403 + ok 2 - HTTP 403 + --- + duration_ms: 4.8559 + type: 'test' + ... + 1..2 +ok 1859 - a notification 401 or 403 during handshake triggers exactly one refresh + retry + --- + duration_ms: 12.729818 + type: 'test' + ... +# Subtest: a refresh that fails invalid_grant surfaces the re-login guidance +ok 1860 - a refresh that fails invalid_grant surfaces the re-login guidance + --- + duration_ms: 12.035753 + type: 'test' + ... +# Subtest: a stale JWT whose pre-call refresh fails maps to re-login (not an unhandled throw) +ok 1861 - a stale JWT whose pre-call refresh fails maps to re-login (not an unhandled throw) + --- + duration_ms: 4.372676 + type: 'test' + ... +# Subtest: a static token that 401s is not retried (cannot refresh) +ok 1862 - a static token that 401s is not retried (cannot refresh) + --- + duration_ms: 4.31622 + type: 'test' + ... +# Subtest: an env-override token that 401s does not advise a re-login it cannot fix +ok 1863 - an env-override token that 401s does not advise a re-login it cannot fix + --- + duration_ms: 1.068194 + type: 'test' + ... +# Subtest: an oidc session whose freshly-refreshed JWT is still rejected gets re-login guidance (exit 2) +ok 1864 - an oidc session whose freshly-refreshed JWT is still rejected gets re-login guidance (exit 2) + --- + duration_ms: 7.148254 + type: 'test' + ... +# Subtest: the canonical asset set is complete and lives with the renderer +ok 1865 - the canonical asset set is complete and lives with the renderer + --- + duration_ms: 1.193544 + type: 'test' + ... +# Subtest: the renderer is repo-owned code, not a script in a user working tree +ok 1866 - the renderer is repo-owned code, not a script in a user working tree + --- + duration_ms: 0.218182 + type: 'test' + ... +# Subtest: no bundled skill ships its own copy of the renderer assets +ok 1867 - no bundled skill ships its own copy of the renderer assets + --- + duration_ms: 2.698412 + type: 'test' + ... +# Subtest: deriveReportsEndpoint maps a base URL to /v1/reports +ok 1868 - deriveReportsEndpoint maps a base URL to /v1/reports + --- + duration_ms: 1.045339 + type: 'test' + ... +# Subtest: deriveReportsEndpoint strips a trailing /v1/mcp (the originally-documented form) +ok 1869 - deriveReportsEndpoint strips a trailing /v1/mcp (the originally-documented form) + --- + duration_ms: 0.137279 + type: 'test' + ... +# Subtest: deriveReportsEndpoint returns an unparseable URL unchanged +ok 1870 - deriveReportsEndpoint returns an unparseable URL unchanged + --- + duration_ms: 0.125591 + type: 'test' + ... +# Subtest: publish sends a single .md file with kind/period/title params and the content hash +ok 1871 - publish sends a single .md file with kind/period/title params and the content hash + --- + duration_ms: 32.868417 + type: 'test' + ... +# Subtest: publish reports a 200 dedup hit as already published +ok 1872 - publish reports a 200 dedup hit as already published + --- + duration_ms: 1.935232 + type: 'test' + ... +# Subtest: publish packs a folder as a gzipped ustar bundle +ok 1873 - publish packs a folder as a gzipped ustar bundle + --- + duration_ms: 9.5006 + type: 'test' + ... +# Subtest: publish rejects a folder without an entry document before any upload +ok 1874 - publish rejects a folder without an entry document before any upload + --- + duration_ms: 1.998518 + type: 'test' + ... +# Subtest: publish rejects an invalid kind before any network call +ok 1875 - publish rejects an invalid kind before any network call + --- + duration_ms: 1.196628 + type: 'test' + ... +# Subtest: publish rejects a single file that is neither .html nor .md +ok 1876 - publish rejects a single file that is neither .html nor .md + --- + duration_ms: 1.313497 + type: 'test' + ... +# Subtest: publish surfaces the quota error with its make-room guidance +ok 1877 - publish surfaces the quota error with its make-room guidance + --- + duration_ms: 1.754456 + type: 'test' + ... +# Subtest: publish forwards an explicit --org (the admin-token form) +ok 1878 - publish forwards an explicit --org (the admin-token form) + --- + duration_ms: 1.800557 + type: 'test' + ... +# Subtest: list renders the index newest first and passes filters through +ok 1879 - list renders the index newest first and passes filters through + --- + duration_ms: 0.754427 + type: 'test' + ... +# Subtest: list --json prints the raw records +ok 1880 - list --json prints the raw records + --- + duration_ms: 0.894259 + type: 'test' + ... +# Subtest: list with no reports points at publish +ok 1881 - list with no reports points at publish + --- + duration_ms: 0.340788 + type: 'test' + ... +# Subtest: an unknown remote target is rejected before any network call +ok 1882 - an unknown remote target is rejected before any network call + --- + duration_ms: 0.244081 + type: 'test' + ... +# Subtest: a 401 on an env-override token explains that re-login cannot fix it +ok 1883 - a 401 on an env-override token explains that re-login cannot fix it + --- + duration_ms: 0.430584 + type: 'test' + ... +# Subtest: get fetches the entry document to stdout +ok 1884 - get fetches the entry document to stdout + --- + duration_ms: 0.474311 + type: 'test' + ... +# Subtest: get fetches a named artifact and saves it with --output +ok 1885 - get fetches a named artifact and saves it with --output + --- + duration_ms: 2.626392 + type: 'test' + ... +# Subtest: get reports an unknown report from the server error body +ok 1886 - get reports an unknown report from the server error body + --- + duration_ms: 1.441241 + type: 'test' + ... +# Subtest: delete refuses without --yes when stdin is not a TTY +ok 1887 - delete refuses without --yes when stdin is not a TTY + --- + duration_ms: 0.28278 + type: 'test' + ... +# Subtest: delete with --yes issues the DELETE and confirms +ok 1888 - delete with --yes issues the DELETE and confirms + --- + duration_ms: 0.259815 + type: 'test' + ... +# Subtest: extractStats reads every metric, not just the first +ok 1889 - extractStats reads every metric, not just the first + --- + duration_ms: 1.73008 + type: 'test' + ... +# Subtest: extractStats keeps each judgment exactly +ok 1890 - extractStats keeps each judgment exactly + --- + duration_ms: 0.23703 + type: 'test' + ... +# Subtest: extractStats keeps labels verbatim rather than rewriting them +ok 1891 - extractStats keeps labels verbatim rather than rewriting them + --- + duration_ms: 0.146222 + type: 'test' + ... +# Subtest: extractStats keeps the value markup so a worded unit stays spaced +ok 1892 - extractStats keeps the value markup so a worded unit stays spaced + --- + duration_ms: 0.165071 + type: 'test' + ... +# Subtest: extractStats drops notes +ok 1893 - extractStats drops notes + --- + duration_ms: 0.202628 + type: 'test' + ... +# Subtest: extractStats returns nothing for a report with no metric grid +ok 1894 - extractStats returns nothing for a report with no metric grid + --- + duration_ms: 0.138751 + type: 'test' + ... +# Subtest: extractKicker prefers the eyebrow, then a Source line, then a subtitle +ok 1895 - extractKicker prefers the eyebrow, then a Source line, then a subtitle + --- + duration_ms: 0.369732 + type: 'test' + ... +# Subtest: extractTitle takes the first heading, else the fallback +ok 1896 - extractTitle takes the first heading, else the fallback + --- + duration_ms: 0.185712 + type: 'test' + ... +# Subtest: cards are newest first, with a companion card for ranked changes +ok 1897 - cards are newest first, with a companion card for ranked changes + --- + duration_ms: 3.365105 + type: 'test' + ... +# Subtest: the companion card falls back to the report scope when it has no eyebrow +ok 1898 - the companion card falls back to the report scope when it has no eyebrow + --- + duration_ms: 1.404705 + type: 'test' + ... +# Subtest: renderLandingPage links explicit index.html, never a bare directory +ok 1899 - renderLandingPage links explicit index.html, never a bare directory + --- + duration_ms: 1.463704 + type: 'test' + ... +# Subtest: renderLandingPage links theme.css only when the tree has one +ok 1900 - renderLandingPage links theme.css only when the tree has one + --- + duration_ms: 2.307868 + type: 'test' + ... +# Subtest: renderLandingPage is reproducible across runs +ok 1901 - renderLandingPage is reproducible across runs + --- + duration_ms: 1.083226 + type: 'test' + ... +# Subtest: report titles and labels are HTML-escaped +ok 1902 - report titles and labels are HTML-escaped + --- + duration_ms: 0.866947 + type: 'test' + ... +# Subtest: rewriteHrefs index: own section flattens, losing the slug directory +ok 1903 - rewriteHrefs index: own section flattens, losing the slug directory + --- + duration_ms: 0.747687 + type: 'test' + ... +# Subtest: rewriteHrefs index: own section keeps its fragment +ok 1904 - rewriteHrefs index: own section keeps its fragment + --- + duration_ms: 0.118791 + type: 'test' + ... +# Subtest: rewriteHrefs index: another report's section routes up and back down +ok 1905 - rewriteHrefs index: another report's section routes up and back down + --- + duration_ms: 0.07199 + type: 'test' + ... +# Subtest: rewriteHrefs index: another report's one-pager resolves to its index +ok 1906 - rewriteHrefs index: another report's one-pager resolves to its index + --- + duration_ms: 0.061764 + type: 'test' + ... +# Subtest: rewriteHrefs index: another report's one-pager keeps its fragment +ok 1907 - rewriteHrefs index: another report's one-pager keeps its fragment + --- + duration_ms: 0.055174 + type: 'test' + ... +# Subtest: rewriteHrefs section: back-reference to its own one-pager becomes index.html +ok 1908 - rewriteHrefs section: back-reference to its own one-pager becomes index.html + --- + duration_ms: 0.163618 + type: 'test' + ... +# Subtest: rewriteHrefs section: back-reference keeps its fragment +ok 1909 - rewriteHrefs section: back-reference keeps its fragment + --- + duration_ms: 0.088084 + type: 'test' + ... +# Subtest: rewriteHrefs section: another report's one-pager resolves to its index +ok 1910 - rewriteHrefs section: another report's one-pager resolves to its index + --- + duration_ms: 0.103777 + type: 'test' + ... +# Subtest: rewriteHrefs section: another report's section keeps its own path +ok 1911 - rewriteHrefs section: another report's section keeps its own path + --- + duration_ms: 0.33582 + type: 'test' + ... +# Subtest: rewriteHrefs section: a sibling section in the same report stays flat +ok 1912 - rewriteHrefs section: a sibling section in the same report stays flat + --- + duration_ms: 0.337763 + type: 'test' + ... +# Subtest: rewriteHrefs index: an absolute URL ending in .md is left alone +ok 1913 - rewriteHrefs index: an absolute URL ending in .md is left alone + --- + duration_ms: 0.138801 + type: 'test' + ... +# Subtest: rewriteHrefs section: an absolute URL ending in .md is left alone +ok 1914 - rewriteHrefs section: an absolute URL ending in .md is left alone + --- + duration_ms: 0.076206 + type: 'test' + ... +# Subtest: rewriteHrefs index: data-src is not href, so copy buttons keep their raw .md target +ok 1915 - rewriteHrefs index: data-src is not href, so copy buttons keep their raw .md target + --- + duration_ms: 0.066211 + type: 'test' + ... +# Subtest: rewriteHrefs section: data-src is not href, so copy buttons keep their raw .md target +ok 1916 - rewriteHrefs section: data-src is not href, so copy buttons keep their raw .md target + --- + duration_ms: 0.065099 + type: 'test' + ... +# Subtest: rewriteHrefs index: a non-markdown asset link is untouched +ok 1917 - rewriteHrefs index: a non-markdown asset link is untouched + --- + duration_ms: 0.058539 + type: 'test' + ... +# Subtest: rewriteHrefs rewrites links inside raw-HTML components, not just Markdown links +ok 1918 - rewriteHrefs rewrites links inside raw-HTML components, not just Markdown links + --- + duration_ms: 0.092861 + type: 'test' + ... +# Subtest: rewriteHrefs handles every occurrence on a line, not just the first +ok 1919 - rewriteHrefs handles every occurrence on a line, not just the first + --- + duration_ms: 0.0925 + type: 'test' + ... +# Subtest: rewriteHrefs treats a slug with regex metacharacters literally +ok 1920 - rewriteHrefs treats a slug with regex metacharacters literally + --- + duration_ms: 0.125651 + type: 'test' + ... +# Subtest: docLabel states the slug's date, else the generic wording +ok 1921 - docLabel states the slug's date, else the generic wording + --- + duration_ms: 0.904585 + type: 'test' + ... +# Subtest: pageTitle takes the first heading, trimmed, else the fallback +ok 1922 - pageTitle takes the first heading, trimmed, else the fallback + --- + duration_ms: 0.225543 + type: 'test' + ... +# Subtest: masthead carries the brand, the doc label, and the nav slot +ok 1923 - masthead carries the brand, the doc label, and the nav slot + --- + duration_ms: 0.187175 + type: 'test' + ... +# Subtest: discoverSections lists a report's section files sorted, and nothing without a section dir +ok 1924 - discoverSections lists a report's section files sorted, and nothing without a section dir + --- + duration_ms: 1.807257 + type: 'test' + ... +# Subtest: renderReports builds every report and no stale output +ok 1925 - renderReports builds every report and no stale output + --- + duration_ms: 31.843539 + type: 'test' + ... +# Subtest: no built page keeps a .md href +ok 1926 - no built page keeps a .md href + --- + duration_ms: 8.482883 + type: 'test' + ... +# Subtest: every page carries a copy action, and every report a full.md payload +ok 1927 - every page carries a copy action, and every report a full.md payload + --- + duration_ms: 11.473108 + type: 'test' + ... +# Subtest: one-pagers link back to the landing page, sections back to their report +ok 1928 - one-pagers link back to the landing page, sections back to their report + --- + duration_ms: 8.570937 + type: 'test' + ... +# Subtest: theme.css is created once, never overwritten, and reaches every page +ok 1929 - theme.css is created once, never overwritten, and reaches every page + --- + duration_ms: 20.704559 + type: 'test' + ... +# Subtest: the authoring vocabulary converts to the markup pandoc emitted +ok 1930 - the authoring vocabulary converts to the markup pandoc emitted + --- + duration_ms: 4.361028 + type: 'test' + ... +# Subtest: every in-page anchor resolves to an id on the page +ok 1931 - every in-page anchor resolves to an id on the page + --- + duration_ms: 4.456403 + type: 'test' + ... +# Subtest: heading ids NFC-normalize and keep combining marks, matching pandoc 3.1.11 +ok 1932 - heading ids NFC-normalize and keep combining marks, matching pandoc 3.1.11 + --- + duration_ms: 7.038488 + type: 'test' + ... +# Subtest: numeric and named HTML entities decode before the slug rule runs, matching pandoc 3.1.11 +ok 1933 - numeric and named HTML entities decode before the slug rule runs, matching pandoc 3.1.11 + --- + duration_ms: 3.971396 + type: 'test' + ... +# Subtest: an authored ’ heading resolves its own in-page anchor +ok 1934 - an authored ’ heading resolves its own in-page anchor + --- + duration_ms: 6.027361 + type: 'test' + ... +# Subtest: a heading that reduces to nothing emits no id attribute, matching pandoc 3.1.11 +ok 1935 - a heading that reduces to nothing emits no id attribute, matching pandoc 3.1.11 + --- + duration_ms: 4.773195 + type: 'test' + ... +# Subtest: an out-of-range numeric entity in a heading renders instead of crashing, matching pandoc 3.1.11 +ok 1936 - an out-of-range numeric entity in a heading renders instead of crashing, matching pandoc 3.1.11 + --- + duration_ms: 3.814207 + type: 'test' + ... +# Subtest: an uppercase hex numeric entity decodes like its lowercase spelling, matching pandoc 3.1.11 +ok 1937 - an uppercase hex numeric entity decodes like its lowercase spelling, matching pandoc 3.1.11 + --- + duration_ms: 3.969113 + type: 'test' + ... +# Subtest: the HTML5 ASCII-punctuation entity names decode, matching pandoc 3.1.11 +ok 1938 - the HTML5 ASCII-punctuation entity names decode, matching pandoc 3.1.11 + --- + duration_ms: 5.391675 + type: 'test' + ... +# Subtest: a
in a heading yields a space, matching pandoc 3.1.11 +ok 1939 - a
in a heading yields a space, matching pandoc 3.1.11 + --- + duration_ms: 9.756168 + type: 'test' + ... +# Subtest: every raw tag whose name starts with `br` yields a space, matching pandoc 3.1.11 +ok 1940 - every raw tag whose name starts with `br` yields a space, matching pandoc 3.1.11 + --- + duration_ms: 5.60598 + type: 'test' + ... +# Subtest: heading ids do not trim after the punctuation strip, matching pandoc 3.1.11 +ok 1941 - heading ids do not trim after the punctuation strip, matching pandoc 3.1.11 + --- + duration_ms: 6.987099 + type: 'test' + ... +# Subtest: a malformed entity in one report does not destroy the pages of the reports after it +ok 1942 - a malformed entity in one report does not destroy the pages of the reports after it + --- + duration_ms: 18.56748 + type: 'test' + ... +# Subtest: a loose task list still loses its bullet, matching pandoc's suppressed marker +ok 1943 - a loose task list still loses its bullet, matching pandoc's suppressed marker + --- + duration_ms: 4.155326 + type: 'test' + ... +# Subtest: resolveSingleSourceEnablement resolves the claude descriptor to ai-gateway + claude +ok 1944 - resolveSingleSourceEnablement resolves the claude descriptor to ai-gateway + claude + --- + duration_ms: 14.207734 + type: 'test' + ... +# Subtest: resolveSingleSourceEnablement resolves the openclaw descriptor to ai-gateway + openclaw +ok 1945 - resolveSingleSourceEnablement resolves the openclaw descriptor to ai-gateway + openclaw + --- + duration_ms: 23.46823 + type: 'test' + ... +# Subtest: resolveSingleSourceEnablement resolves a gateway-independent descriptor with just its own plugin +ok 1946 - resolveSingleSourceEnablement resolves a gateway-independent descriptor with just its own plugin + --- + duration_ms: 4.819095 + type: 'test' + ... +# Subtest: resolveSingleSourceEnablement returns empty entries for a descriptor with no compose block +ok 1947 - resolveSingleSourceEnablement returns empty entries for a descriptor with no compose block + --- + duration_ms: 0.18421 + type: 'test' + ... +# Subtest: run-tests forwards node --test flags before discovered test files +ok 1948 - run-tests forwards node --test flags before discovered test files + --- + duration_ms: 1.226545 + type: 'test' + ... +# Subtest: run-tests appends discovered test files when no flags are forwarded +ok 1949 - run-tests appends discovered test files when no flags are forwarded + --- + duration_ms: 0.14518 + type: 'test' + ... +# Subtest: isValidSemver accepts X.Y.Z and rejects junk +ok 1950 - isValidSemver accepts X.Y.Z and rejects junk + --- + duration_ms: 0.869932 + type: 'test' + ... +# Subtest: isValidRange accepts the operators the kernel matcher understands +ok 1951 - isValidRange accepts the operators the kernel matcher understands + --- + duration_ms: 0.297151 + type: 'test' + ... +# Subtest: isValidRange rejects empty and unparseable ranges +ok 1952 - isValidRange rejects empty and unparseable ranges + --- + duration_ms: 0.134604 + type: 'test' + ... +# Subtest: discovery finds the fixtures that install a real service label +ok 1953 - discovery finds the fixtures that install a real service label + --- + duration_ms: 0.597679 + type: 'test' + ... +# Subtest: the attach fixtures never reach a real service manager, even when one is on PATH +ok 1954 - the attach fixtures never reach a real service manager, even when one is on PATH + --- + duration_ms: 422.131158 + type: 'test' + ... +# Subtest: running a fixture directly, with no --test, still reaches no service manager +ok 1955 - running a fixture directly, with no --test, still reaches no service manager + --- + duration_ms: 315.466207 + type: 'test' + ... +# Subtest: running the fixtures without test isolation still reaches no service manager +ok 1956 - running the fixtures without test isolation still reaches no service manager + --- + duration_ms: 291.997448 + type: 'test' + ... +# Subtest: runServiceCommand refuses to spawn under the test runner +ok 1957 - runServiceCommand refuses to spawn under the test runner + --- + duration_ms: 0.594384 + type: 'test' + ... +# Subtest: the explicit opt-in still spawns +ok 1958 - the explicit opt-in still spawns + --- + duration_ms: 47.589422 + type: 'test' + ... +# Subtest: forward sink: ≈0 bytes on a no-new-rows tick, ≈N on an N-new tick +ok 1959 - forward sink: ≈0 bytes on a no-new-rows tick, ≈N on an N-new tick + --- + duration_ms: 112.31601 + type: 'test' + ... +# Subtest: blob sink: no blob on a no-new-rows tick, exactly N on an N-new tick +ok 1960 - blob sink: no blob on a no-new-rows tick, exactly N on an N-new tick + --- + duration_ms: 61.342314 + type: 'test' + ... +# Subtest: forward sink: exactly-once across a retention front-prune +ok 1961 - forward sink: exactly-once across a retention front-prune + --- + duration_ms: 62.006904 + type: 'test' + ... +# Subtest: blob sink: exactly-once across a retention front-prune +ok 1962 - blob sink: exactly-once across a retention front-prune + --- + duration_ms: 50.988427 + type: 'test' + ... +# Subtest: forward sink: exactly-once across a compaction generation swap +ok 1963 - forward sink: exactly-once across a compaction generation swap + --- + duration_ms: 40.316246 + type: 'test' + ... +# Subtest: blob sink: exactly-once across a compaction generation swap +ok 1964 - blob sink: exactly-once across a compaction generation swap + --- + duration_ms: 57.430068 + type: 'test' + ... +# Subtest: forward sink: watermark composes with the driver-outbox respool (suffix-only replay) +ok 1965 - forward sink: watermark composes with the driver-outbox respool (suffix-only replay) + --- + duration_ms: 49.47073 + type: 'test' + ... +# Subtest: blob sink: a lost watermark after a durable PUT re-PUTs the same object key (idempotent overwrite) +ok 1966 - blob sink: a lost watermark after a durable PUT re-PUTs the same object key (idempotent overwrite) + --- + duration_ms: 32.894897 + type: 'test' + ... +# Subtest: forward sink: a pure-legacy partition re-exports the null-seq backlog exactly once +ok 1967 - forward sink: a pure-legacy partition re-exports the null-seq backlog exactly once + --- + duration_ms: 17.937973 + type: 'test' + ... +# Subtest: forward sink: a mixed legacy+real partition ships everything once, then steady-state +ok 1968 - forward sink: a mixed legacy+real partition ships everything once, then steady-state + --- + duration_ms: 17.576775 + type: 'test' + ... +# Subtest: blob sink: a pure-legacy partition writes one blob, then no blob +ok 1969 - blob sink: a pure-legacy partition writes one blob, then no blob + --- + duration_ms: 15.379275 + type: 'test' + ... +# Subtest: blob sink: a mixed legacy+real partition writes one blob, then no blob +ok 1970 - blob sink: a mixed legacy+real partition writes one blob, then no blob + --- + duration_ms: 10.583856 + type: 'test' + ... +# Subtest: forward sink: two instances on one partition keep independent watermarks (no cross-instance skip) +ok 1971 - forward sink: two instances on one partition keep independent watermarks (no cross-instance skip) + --- + duration_ms: 30.467507 + type: 'test' + ... +# Subtest: withSeqRangeFilename inserts the range before the final extension +ok 1972 - withSeqRangeFilename inserts the range before the final extension + --- + duration_ms: 0.836281 + type: 'test' + ... +# Subtest: withSeqRangeFilename is deterministic and preserves dots in the base name +ok 1973 - withSeqRangeFilename is deterministic and preserves dots in the base name + --- + duration_ms: 0.134304 + type: 'test' + ... +# Subtest: openIncrementalRows reports empty for a missing table (no blob written) +ok 1974 - openIncrementalRows reports empty for a missing table (no blob written) + --- + duration_ms: 0.227475 + type: 'test' + ... +# Subtest: openIncrementalRows reports empty for a partition with no tablePath +ok 1975 - openIncrementalRows reports empty for a partition with no tablePath + --- + duration_ms: 0.939638 + type: 'test' + ... +# Subtest: openIncrementalRows tracks rowCount and the high-water lastAfter as the encoder drains +ok 1976 - openIncrementalRows tracks rowCount and the high-water lastAfter as the encoder drains + --- + duration_ms: 1.056886 + type: 'test' + ... +# Subtest: openIncrementalRows honours the since filter (only seq > since) +ok 1977 - openIncrementalRows honours the since filter (only seq > since) + --- + duration_ms: 0.2109 + type: 'test' + ... +# Subtest: openIncrementalRows reports empty when since already covers every row +ok 1978 - openIncrementalRows reports empty when since already covers every row + --- + duration_ms: 0.228197 + type: 'test' + ... +# Subtest: openIncrementalRows: null-seq legacy rows are emitted but never advance lastAfter +ok 1979 - openIncrementalRows: null-seq legacy rows are emitted but never advance lastAfter + --- + duration_ms: 0.198993 + type: 'test' + ... +# Subtest: watermarkKeyFor returns null without a tablePath and the logical key otherwise +ok 1980 - watermarkKeyFor returns null without a tablePath and the logical key otherwise + --- + duration_ms: 0.42093 + type: 'test' + ... +# Subtest: createInstanceWatermarkStore isolates instances under one plugin stateDir +ok 1981 - createInstanceWatermarkStore isolates instances under one plugin stateDir + --- + duration_ms: 29.498725 + type: 'test' + ... +# Subtest: createInstanceWatermarkStore requires stateDir and instanceName +ok 1982 - createInstanceWatermarkStore requires stateDir and instanceName + --- + duration_ms: 0.52006 + type: 'test' + ... +# Subtest: sink maintain without --compact never rewrites and exits 0 +ok 1983 - sink maintain without --compact never rewrites and exits 0 + --- + duration_ms: 32.052687 + type: 'test' + ... +# Subtest: sink maintain --compact rewrites past the configured threshold and exits 0 +ok 1984 - sink maintain --compact rewrites past the configured threshold and exits 0 + --- + duration_ms: 35.57455 + type: 'test' + ... +# Subtest: sink maintain --compact reports a commit conflict and exits 0 +ok 1985 - sink maintain --compact reports a commit conflict and exits 0 + --- + duration_ms: 12.92185 + type: 'test' + ... +# Subtest: sink maintain --compact exits 1 when the rewrite fails +ok 1986 - sink maintain --compact exits 1 when the rewrite fails + --- + duration_ms: 10.051998 + type: 'test' + ... +# Subtest: sink maintain rejects unknown flags with exit 2 +ok 1987 - sink maintain rejects unknown flags with exit 2 + --- + duration_ms: 0.906948 + type: 'test' + ... +# Subtest: fromProvider returns the capability value from the specified provider +ok 1988 - fromProvider returns the capability value from the specified provider + --- + duration_ms: 2.321949 + type: 'test' + ... +# Subtest: fromProvider returns undefined when the provider has not registered the capability +ok 1989 - fromProvider returns undefined when the provider has not registered the capability + --- + duration_ms: 0.291924 + type: 'test' + ... +# Subtest: fromProvider respects semver range +ok 1990 - fromProvider respects semver range + --- + duration_ms: 0.330402 + type: 'test' + ... +# Subtest: materializeSinks returns empty when config has no sinks +ok 1991 - materializeSinks returns empty when config has no sinks + --- + duration_ms: 0.891895 + type: 'test' + ... +# Subtest: materializeSinks returns empty when config is null +ok 1992 - materializeSinks returns empty when config is null + --- + duration_ms: 0.399898 + type: 'test' + ... +# Subtest: materializeSinks materializes a request sink from a plugin with one contribution +ok 1993 - materializeSinks materializes a request sink from a plugin with one contribution + --- + duration_ms: 2.043526 + type: 'test' + ... +# Subtest: materializeSinks errors when request sink plugin is not active +ok 1994 - materializeSinks errors when request sink plugin is not active + --- + duration_ms: 0.567903 + type: 'test' + ... +# Subtest: materializeSinks errors when request sink plugin has no contributions +ok 1995 - materializeSinks errors when request sink plugin has no contributions + --- + duration_ms: 0.409593 + type: 'test' + ... +# Subtest: materializeSinks errors when request sink plugin has multiple contributions +ok 1996 - materializeSinks errors when request sink plugin has multiple contributions + --- + duration_ms: 0.808028 + type: 'test' + ... +# Subtest: materializeSinks materializes a blob sink (encoder writer + destination) +ok 1997 - materializeSinks materializes a blob sink (encoder writer + destination) + --- + duration_ms: 1.080542 + type: 'test' + ... +# Subtest: materializeSinks materializes a table-format sink +ok 1998 - materializeSinks materializes a table-format sink + --- + duration_ms: 1.087423 + type: 'test' + ... +# Subtest: materializeSinks table-format sink uses config.encoder pin +ok 1999 - materializeSinks table-format sink uses config.encoder pin + --- + duration_ms: 0.753705 + type: 'test' + ... +# Subtest: materializeSinks errors when writer plugin is not active +ok 2000 - materializeSinks errors when writer plugin is not active + --- + duration_ms: 0.511998 + type: 'test' + ... +# Subtest: materializeSinks errors when destination plugin is not active +ok 2001 - materializeSinks errors when destination plugin is not active + --- + duration_ms: 0.408831 + type: 'test' + ... +# Subtest: materializeSinks errors when writer provides neither encoder nor table-format +ok 2002 - materializeSinks errors when writer provides neither encoder nor table-format + --- + duration_ms: 0.289139 + type: 'test' + ... +# Subtest: materializeSinks errors when table-format destination has no blob-store +ok 2003 - materializeSinks errors when table-format destination has no blob-store + --- + duration_ms: 0.360608 + type: 'test' + ... +# Subtest: materializeSinks errors when table-format encoder pin is not active +ok 2004 - materializeSinks errors when table-format encoder pin is not active + --- + duration_ms: 1.609085 + type: 'test' + ... +# Subtest: materializeSinks continues past failures and reports all errors +ok 2005 - materializeSinks continues past failures and reports all errors + --- + duration_ms: 0.314338 + type: 'test' + ... +# Subtest: materializeSinks destination contribution missing for blob sink +ok 2006 - materializeSinks destination contribution missing for blob sink + --- + duration_ms: 0.290521 + type: 'test' + ... +# Subtest: readRows back-compat: no opts is unchanged, internal fields never leak +ok 2007 - readRows back-compat: no opts is unchanged, internal fields never leak + --- + duration_ms: 49.591514 + type: 'test' + ... +# Subtest: readRowsSince pairs each row with a monotonic after token and strips the seq +ok 2008 - readRowsSince pairs each row with a monotonic after token and strips the seq + --- + duration_ms: 68.668901 + type: 'test' + ... +# Subtest: null-seq (legacy) rows are always treated as new and never skipped +ok 2009 - null-seq (legacy) rows are always treated as new and never skipped + --- + duration_ms: 16.319214 + type: 'test' + ... +# Subtest: a table with no seq column at all yields everything (pure legacy) +ok 2010 - a table with no seq column at all yields everything (pure legacy) + --- + duration_ms: 10.746614 + type: 'test' + ... +# Subtest: an invalid continuation token is rejected +ok 2011 - an invalid continuation token is rejected + --- + duration_ms: 21.328998 + type: 'test' + ... +# Subtest: deriveWatermarkKey splits dataset from the partition path +ok 2012 - deriveWatermarkKey splits dataset from the partition path + --- + duration_ms: 1.536766 + type: 'test' + ... +# Subtest: deriveWatermarkKey is keyed by the LOGICAL path, independent of tableDir +ok 2013 - deriveWatermarkKey is keyed by the LOGICAL path, independent of tableDir + --- + duration_ms: 0.42751 + type: 'test' + ... +# Subtest: deriveWatermarkKey preserves nested partition segments +ok 2014 - deriveWatermarkKey preserves nested partition segments + --- + duration_ms: 0.233115 + type: 'test' + ... +# Subtest: deriveWatermarkKey sanitizes unsafe segment characters +ok 2015 - deriveWatermarkKey sanitizes unsafe segment characters + --- + duration_ms: 0.145742 + type: 'test' + ... +# Subtest: deriveWatermarkKey falls back to a sentinel when no partition segment +ok 2016 - deriveWatermarkKey falls back to a sentinel when no partition segment + --- + duration_ms: 0.213094 + type: 'test' + ... +# Subtest: deriveWatermarkKey rejects paths outside the datasets root +ok 2017 - deriveWatermarkKey rejects paths outside the datasets root + --- + duration_ms: 2.650088 + type: 'test' + ... +# Subtest: read returns null when no watermark has been written +ok 2018 - read returns null when no watermark has been written + --- + duration_ms: 6.176918 + type: 'test' + ... +# Subtest: write then read round-trips the continuation and row count +ok 2019 - write then read round-trips the continuation and row count + --- + duration_ms: 4.726164 + type: 'test' + ... +# Subtest: write advances the watermark in place (latest wins) +ok 2020 - write advances the watermark in place (latest wins) + --- + duration_ms: 6.280406 + type: 'test' + ... +# Subtest: write is atomic write-rename and leaves no temp files +ok 2021 - write is atomic write-rename and leaves no temp files + --- + duration_ms: 1.450565 + type: 'test' + ... +# Subtest: write rejects a malformed continuation before touching disk +ok 2022 - write rejects a malformed continuation before touching disk + --- + duration_ms: 1.074974 + type: 'test' + ... +# Subtest: read returns null on a corrupt watermark file (safe re-export, never silent skip) +ok 2023 - read returns null on a corrupt watermark file (safe re-export, never silent skip) + --- + duration_ms: 4.623397 + type: 'test' + ... +# Subtest: keyFor matches deriveWatermarkKey +ok 2024 - keyFor matches deriveWatermarkKey + --- + duration_ms: 0.244251 + type: 'test' + ... +# Subtest: createSinkWatermarkStore requires a stateDir +ok 2025 - createSinkWatermarkStore requires a stateDir + --- + duration_ms: 0.113442 + type: 'test' + ... +# Subtest: instantiate table-format sink wires blobStore + encoder + config into createSink +ok 2026 - instantiate table-format sink wires blobStore + encoder + config into createSink + --- + duration_ms: 2.765564 + type: 'test' + ... +# Subtest: instantiate table-format sink intersects supports tags between provider and encoder +ok 2027 - instantiate table-format sink intersects supports tags between provider and encoder + --- + duration_ms: 0.443024 + type: 'test' + ... +# Subtest: instantiate table-format sink rejects missing blobStore / encoder / provider +ok 2028 - instantiate table-format sink rejects missing blobStore / encoder / provider + --- + duration_ms: 0.601755 + type: 'test' + ... +# Subtest: validateConfig accepts table-format writer + blob destination +ok 2029 - validateConfig accepts table-format writer + blob destination + --- + duration_ms: 0.93417 + type: 'test' + ... +# Subtest: validateConfig rejects table-format writer without blob-store destination +ok 2030 - validateConfig rejects table-format writer without blob-store destination + --- + duration_ms: 0.424566 + type: 'test' + ... +# Subtest: validateConfig rejects writer providing neither encoder nor table-format +ok 2031 - validateConfig rejects writer providing neither encoder nor table-format + --- + duration_ms: 0.520311 + type: 'test' + ... +# Subtest: validateConfig rejects table-format sink with unknown inner encoder pin +ok 2032 - validateConfig rejects table-format sink with unknown inner encoder pin + --- + duration_ms: 0.26291 + type: 'test' + ... +# Subtest: validateConfig rejects table-format sink whose encoder pin does not provide hypaware.encoder +ok 2033 - validateConfig rejects table-format sink whose encoder pin does not provide hypaware.encoder + --- + duration_ms: 0.248698 + type: 'test' + ... +# Subtest: first-party metadata still routes encoder writers through the legacy sink_pair_incompatible code +ok 2034 - first-party metadata still routes encoder writers through the legacy sink_pair_incompatible code + --- + duration_ms: 0.476073 + type: 'test' + ... +# Subtest: no central layer -> undefined (solo machines have nothing to withhold from) +ok 2035 - no central layer -> undefined (solo machines have nothing to withhold from) + --- + duration_ms: 8.368169 + type: 'test' + ... +# Subtest: enrolled machine with an empty (or absent) store withholds nothing: default-sync +ok 2036 - enrolled machine with an empty (or absent) store withholds nothing: default-sync + --- + duration_ms: 0.942573 + type: 'test' + ... +# Subtest: opt-out entries feed the withheld set +ok 2037 - opt-out entries feed the withheld set + --- + duration_ms: 4.441301 + type: 'test' + ... +# Subtest: a central-classified source cannot be withheld: a stale entry is inert (LLP 0188 \#locked) +ok 2038 - a central-classified source cannot be withheld: a stale entry is inert (LLP 0188 \#locked) + --- + duration_ms: 1.383403 + type: 'test' + ... +# Subtest: the store is re-read after the TTL, so an opt-out lands in a running daemon +ok 2039 - the store is re-read after the TTL, so an opt-out lands in a running daemon + --- + duration_ms: 1.960149 + type: 'test' + ... +# Subtest: a corrupt store throws ClientSyncListUnreadableError from shouldWithhold (fail closed) +ok 2040 - a corrupt store throws ClientSyncListUnreadableError from shouldWithhold (fail closed) + --- + duration_ms: 1.213033 + type: 'test' + ... +# Subtest: dataset-scoped withholding: an unattributed dataset drops wholesale only when every owning source is opted out +ok 2041 - dataset-scoped withholding: an unattributed dataset drops wholesale only when every owning source is opted out + --- + duration_ms: 1.233484 + type: 'test' + ... +# Subtest: fail-closed: any opted-out owner of an attributed dataset withholds its unattributed rows +ok 2042 - fail-closed: any opted-out owner of an attributed dataset withholds its unattributed rows + --- + duration_ms: 1.045729 + type: 'test' + ... +# Subtest: fail-closed arming set matches the real bundled manifests: the raw rows own ai_gateway_messages +ok 2043 - fail-closed arming set matches the real bundled manifests: the raw rows own ai_gateway_messages + --- + duration_ms: 16.363431 + type: 'test' + ... +# Subtest: fail-closed: inert with nothing opted out, and inert for an opt-out on a non-owner +ok 2044 - fail-closed: inert with nothing opted out, and inert for an opt-out on a non-owner + --- + duration_ms: 2.100011 + type: 'test' + ... +# Subtest: datasetAttributionColumnsFromCatalog folds declared attribution columns, first writer wins +ok 2045 - datasetAttributionColumnsFromCatalog folds declared attribution columns, first writer wins + --- + duration_ms: 0.306386 + type: 'test' + ... +# Subtest: datasetOwnedSourceIdsFromCatalog maps each dataset to its owning picker ids +ok 2046 - datasetOwnedSourceIdsFromCatalog maps each dataset to its owning picker ids + --- + duration_ms: 0.122847 + type: 'test' + ... +# Subtest: datasetOwnedSourceIdsFromCatalog unions owners across every plugin contributing a dataset +ok 2047 - datasetOwnedSourceIdsFromCatalog unions owners across every plugin contributing a dataset + --- + duration_ms: 0.156538 + type: 'test' + ... +# Subtest: a shared dataset is not withheld wholesale while a locked co-owner still syncs +ok 2048 - a shared dataset is not withheld wholesale while a locked co-owner still syncs + --- + duration_ms: 2.23048 + type: 'test' + ... +# Subtest: migration: central layer + absent store materializes the local-classified set as opt-outs +ok 2049 - migration: central layer + absent store materializes the local-classified set as opt-outs + --- + duration_ms: 1.915101 + type: 'test' + ... +# Subtest: migration: an existing store (even empty) is the new-era marker, no-op +ok 2050 - migration: an existing store (even empty) is the new-era marker, no-op + --- + duration_ms: 1.31612 + type: 'test' + ... +# Subtest: migration: no central layer, no-op (solo machine never migrates) +ok 2051 - migration: no central layer, no-op (solo machine never migrates) + --- + duration_ms: 5.113853 + type: 'test' + ... +# Subtest: migration: a corrupt store is left untouched (never overwrite a privacy signal) +ok 2052 - migration: a corrupt store is left untouched (never overwrite a privacy signal) + --- + duration_ms: 10.050135 + type: 'test' + ... +# Subtest: migration is idempotent: a second boot after materialization changes nothing +ok 2053 - migration is idempotent: a second boot after materialization changes nothing + --- + duration_ms: 5.188546 + type: 'test' + ... +# Subtest: readRowsSince: rows attributed to a withheld source are dropped from the payload but the cursor advances across them +ok 2054 - readRowsSince: rows attributed to a withheld source are dropped from the payload but the cursor advances across them + --- + duration_ms: 96.296481 + type: 'test' + ... +# Subtest: readRowsSince: a dataset with no declared attribution_column is never subject to source-scoped withholding +ok 2055 - readRowsSince: a dataset with no declared attribution_column is never subject to source-scoped withholding + --- + duration_ms: 42.838441 + type: 'test' + ... +# Subtest: readRowsSince: with no sourceWithholdResolver configured, nothing is ever withheld on attribution +ok 2056 - readRowsSince: with no sourceWithholdResolver configured, nothing is ever withheld on attribution + --- + duration_ms: 21.999136 + type: 'test' + ... +# Subtest: readRowsSince: a `columns` projection omitting the attribution column still withholds, and shipped rows come back without it +ok 2057 - readRowsSince: a `columns` projection omitting the attribution column still withholds, and shipped rows come back without it + --- + duration_ms: 22.060269 + type: 'test' + ... +# Subtest: readRowsSince: a dataset with no attribution column whose every owning source is withheld is dropped wholesale, cursor still advancing +ok 2058 - readRowsSince: a dataset with no attribution column whose every owning source is withheld is dropped wholesale, cursor still advancing + --- + duration_ms: 21.068131 + type: 'test' + ... +# Subtest: readRowsSince: unattributed rows in an attributed dataset are withheld once any owning source is opted out (fail closed) +ok 2059 - readRowsSince: unattributed rows in an attributed dataset are withheld once any owning source is opted out (fail closed) + --- + duration_ms: 39.325842 + type: 'test' + ... +# Subtest: readRowsSince: with no opt-out standing, unattributed rows still ship (the fail-closed rule is inert) +ok 2060 - readRowsSince: with no opt-out standing, unattributed rows still ship (the fail-closed rule is inert) + --- + duration_ms: 13.3824 + type: 'test' + ... +# Subtest: readRowsSince: the provider form of withheldSourceIds is consulted live, so an opt-out lands mid-run without a rebuild +ok 2061 - readRowsSince: the provider form of withheldSourceIds is consulted live, so an opt-out lands mid-run without a rebuild + --- + duration_ms: 20.656576 + type: 'test' + ... +# Subtest: readRowsSince: cwd-based and source-scoped withholding compose independently +ok 2062 - readRowsSince: cwd-based and source-scoped withholding compose independently + --- + duration_ms: 23.281396 + type: 'test' + ... +# Subtest: mixed done/failed/pending/n-a reads cleanly off the marker store + config +ok 2063 - mixed done/failed/pending/n-a reads cleanly off the marker store + config + --- + duration_ms: 39.37142 + type: 'test' + ... +# Subtest: a malformed on_join block renders n/a (not pending) on a joined host +ok 2064 - a malformed on_join block renders n/a (not pending) on a joined host + --- + duration_ms: 25.90316 + type: 'test' + ... +# Subtest: a default-on backfill target (enabled client, no explicit block) shows pending on a joined host +ok 2065 - a default-on backfill target (enabled client, no explicit block) shows pending on a joined host + --- + duration_ms: 13.218891 + type: 'test' + ... +# Subtest: a default-on client on a NON-joined host keeps the V1 surface (no spurious action) +ok 2066 - a default-on client on a NON-joined host keeps the V1 surface (no spurious action) + --- + duration_ms: 12.797872 + type: 'test' + ... +# Subtest: a failed backfill does not flip overall to degraded +ok 2067 - a failed backfill does not flip overall to degraded + --- + duration_ms: 9.215537 + type: 'test' + ... +# Subtest: a mixed done/failed/refused marker store renders all three; overall stays healthy (LLP 0186) +ok 2068 - a mixed done/failed/refused marker store renders all three; overall stays healthy (LLP 0186) + --- + duration_ms: 18.094712 + type: 'test' + ... +# Subtest: an ordinary host with no markers reports clientActions null (V1 surface unchanged) +ok 2069 - an ordinary host with no markers reports clientActions null (V1 surface unchanged) + --- + duration_ms: 7.919547 + type: 'test' + ... +# Subtest: JSON renderer emits a stable client_actions block +ok 2070 - JSON renderer emits a stable client_actions block + --- + duration_ms: 15.630197 + type: 'test' + ... +# Subtest: text renderer prints the client actions section with per-state detail +ok 2071 - text renderer prints the client actions section with per-state detail + --- + duration_ms: 5.884754 + type: 'test' + ... +# Subtest: a refused marker with no reason still renders the repair hint, never a bare [refused] +ok 2072 - a refused marker with no reason still renders the repair hint, never a bare [refused] + --- + duration_ms: 7.803891 + type: 'test' + ... +# Subtest: attach declared targets read mixed done/failed/pending/n-a cleanly (T9) +ok 2073 - attach declared targets read mixed done/failed/pending/n-a cleanly (T9) + --- + duration_ms: 6.839385 + type: 'test' + ... +# Subtest: attach renders n/a for on_join:false and for a non-joined explicit target; bare local stays V1 (T9) +ok 2074 - attach renders n/a for on_join:false and for a non-joined explicit target; bare local stays V1 (T9) + --- + duration_ms: 28.938403 + type: 'test' + ... +# Subtest: a failed attach does not flip overall to degraded (T9) +ok 2075 - a failed attach does not flip overall to degraded (T9) + --- + duration_ms: 14.606421 + type: 'test' + ... +# Subtest: a done attach marker renders attached and collapses with the declared target - no double row (T9) +ok 2076 - a done attach marker renders attached and collapses with the declared target - no double row (T9) + --- + duration_ms: 7.868209 + type: 'test' + ... +# Subtest: a client attached with no plugin enabling it is a warning naming the detach +ok 2077 - a client attached with no plugin enabling it is a warning naming the detach + --- + duration_ms: 20.463873 + type: 'test' + ... +# Subtest: a configured attached client draws no stranded diagnostic +ok 2078 - a configured attached client draws no stranded diagnostic + --- + duration_ms: 20.844061 + type: 'test' + ... +# Subtest: a managed host leaves the reverse lane to the reconciler and stays quiet +ok 2079 - a managed host leaves the reverse lane to the reconciler and stays quiet + --- + duration_ms: 21.432045 + type: 'test' + ... +# Subtest: an unreadable local config is not read as an instruction to detach +ok 2080 - an unreadable local config is not read as an instruction to detach + --- + duration_ms: 13.148334 + type: 'test' + ... +# Subtest: a missing config still names the client its marker strands +ok 2081 - a missing config still names the client its marker strands + --- + duration_ms: 10.550476 + type: 'test' + ... +# Subtest: the text renderer prints a client probe error instead of a bare not-attached +ok 2082 - the text renderer prints a client probe error instead of a bare not-attached + --- + duration_ms: 23.366185 + type: 'test' + ... +# Subtest: a client carrying an error is never collapsed into the clients "(none)" line +ok 2083 - a client carrying an error is never collapsed into the clients "(none)" line + --- + duration_ms: 26.779823 + type: 'test' + ... +# Subtest: an all-clean host keeps the "(none)" clients collapse (surface unchanged) +ok 2084 - an all-clean host keeps the "(none)" clients collapse (surface unchanged) + --- + duration_ms: 18.931254 + type: 'test' + ... +# Subtest: the JSON renderer carries the same client error +ok 2085 - the JSON renderer carries the same client error + --- + duration_ms: 11.383823 + type: 'test' + ... +# Subtest: an enrolled host with nothing opted out shows every configured source syncing (default-sync) +ok 2086 - an enrolled host with nothing opted out shows every configured source syncing (default-sync) + --- + duration_ms: 23.406315 + type: 'test' + ... +# Subtest: an opted-out local source shows local-only; a stale central opt-out entry is inert +ok 2087 - an opted-out local source shows local-only; a stale central opt-out entry is inert + --- + duration_ms: 30.167571 + type: 'test' + ... +# Subtest: a solo host leaves the split null and the V1 surface unchanged +ok 2088 - a solo host leaves the split null and the V1 surface unchanged + --- + duration_ms: 11.095313 + type: 'test' + ... +# Subtest: a corrupt opt-out store degrades to a null split plus a warning diagnostic +ok 2089 - a corrupt opt-out store degrades to a null split plus a warning diagnostic + --- + duration_ms: 21.999077 + type: 'test' + ... +# Subtest: no hold marker: the deadline is null and text/JSON stay quiet +ok 2090 - no hold marker: the deadline is null and text/JSON stay quiet + --- + duration_ms: 25.849659 + type: 'test' + ... +# Subtest: a live hold surfaces its deadline in text and JSON (LLP 0100 R9) +ok 2091 - a live hold surfaces its deadline in text and JSON (LLP 0100 R9) + --- + duration_ms: 41.695553 + type: 'test' + ... +# Subtest: an expired hold reads as absent, exactly as the sink driver sees it +ok 2092 - an expired hold reads as absent, exactly as the sink driver sees it + --- + duration_ms: 14.876101 + type: 'test' + ... +# Subtest: a corrupt marker fails open (absent, no diagnostic, no degrade) - LLP 0101 fail-open polarity +ok 2093 - a corrupt marker fails open (absent, no diagnostic, no degrade) - LLP 0101 fail-open polarity + --- + duration_ms: 9.426598 + type: 'test' + ... +# Subtest: gatewaySourceDetails surfaces the fallback marker from status.json +ok 2094 - gatewaySourceDetails surfaces the fallback marker from status.json + --- + duration_ms: 0.783431 + type: 'test' + ... +# Subtest: a fallback boot emits a non-degrading gateway_port_fallback warning +ok 2095 - a fallback boot emits a non-degrading gateway_port_fallback warning + --- + duration_ms: 30.662143 + type: 'test' + ... +# Subtest: a default-port boot emits no gateway_port_fallback diagnostic +ok 2096 - a default-port boot emits no gateway_port_fallback diagnostic + --- + duration_ms: 29.642985 + type: 'test' + ... +# Subtest: an idle gateway that was configured with upstreams warns +ok 2097 - an idle gateway that was configured with upstreams warns + --- + duration_ms: 45.513497 + type: 'test' + ... +# Subtest: an idle gateway whose configured upstream has no name warns +ok 2098 - an idle gateway whose configured upstream has no name warns + --- + duration_ms: 12.880187 + type: 'test' + ... +# Subtest: an idle gateway whose configured upstream has an empty name warns +ok 2099 - an idle gateway whose configured upstream has an empty name warns + --- + duration_ms: 12.944063 + type: 'test' + ... +# Subtest: an idle gateway with no configured upstreams stays quiet and healthy +ok 2100 - an idle gateway with no configured upstreams stays quiet and healthy + --- + duration_ms: 9.916742 + type: 'test' + ... +# Subtest: an idle gateway with no upstreams key at all stays quiet and healthy +ok 2101 - an idle gateway with no upstreams key at all stays quiet and healthy + --- + duration_ms: 8.538608 + type: 'test' + ... +# Subtest: a degenerate upstreams detail does not crash or warn +ok 2102 - a degenerate upstreams detail does not crash or warn + --- + duration_ms: 8.583727 + type: 'test' + ... +# Subtest: a status file with names but no count still warns +ok 2103 - a status file with names but no count still warns + --- + duration_ms: 13.611238 + type: 'test' + ... +# Subtest: a listening gateway never warns, however many upstreams it has +ok 2104 - a listening gateway never warns, however many upstreams it has + --- + duration_ms: 5.732853 + type: 'test' + ... +# Subtest: the idle warning does not degrade overall health +ok 2105 - the idle warning does not degrade overall health + --- + duration_ms: 6.49418 + type: 'test' + ... +# Subtest: a stopped daemon does not warn off a stale status snapshot +ok 2106 - a stopped daemon does not warn off a stale status snapshot + --- + duration_ms: 11.739914 + type: 'test' + ... +# Subtest: a gateway that lost one of two configured upstreams warns while listening +ok 2107 - a gateway that lost one of two configured upstreams warns while listening + --- + duration_ms: 20.8829 + type: 'test' + ... +# Subtest: a dropped upstream whose name a registered adapter preset backfills is not reported as silence +ok 2108 - a dropped upstream whose name a registered adapter preset backfills is not reported as silence + --- + duration_ms: 8.156317 + type: 'test' + ... +# Subtest: a partial loss with no usable name still warns off the count +ok 2109 - a partial loss with no usable name still warns off the count + --- + duration_ms: 7.564537 + type: 'test' + ... +# Subtest: a total loss still warns, through the same comparison +ok 2110 - a total loss still warns, through the same comparison + --- + duration_ms: 27.625618 + type: 'test' + ... +# Subtest: a fully valid gateway config stays quiet +ok 2111 - a fully valid gateway config stays quiet + --- + duration_ms: 6.575444 + type: 'test' + ... +# Subtest: a hermes-only gateway stays quiet through the same comparison +ok 2112 - a hermes-only gateway stays quiet through the same comparison + --- + duration_ms: 6.070547 + type: 'test' + ... +# Subtest: a status file without the dropped count does not guess at a partial loss +ok 2113 - a status file without the dropped count does not guess at a partial loss + --- + duration_ms: 6.623728 + type: 'test' + ... +# Subtest: a dropped upstream covered by no preset is reported as silent, definitively +ok 2114 - a dropped upstream covered by no preset is reported as silent, definitively + --- + duration_ms: 16.191879 + type: 'test' + ... +# Subtest: a dropped upstream a registered preset covers is reported as backfilled, definitively +ok 2115 - a dropped upstream a registered preset covers is reported as backfilled, definitively + --- + duration_ms: 11.923272 + type: 'test' + ... +# Subtest: a mixed drop separates the covered name from the silent one +ok 2116 - a mixed drop separates the covered name from the silent one + --- + duration_ms: 8.804732 + type: 'test' + ... +# Subtest: the dropped names do not read as the configured set +ok 2117 - the dropped names do not read as the configured set + --- + duration_ms: 5.662597 + type: 'test' + ... +# Subtest: a status file with no preset list keeps the hedge +ok 2118 - a status file with no preset list keeps the hedge + --- + duration_ms: 11.180333 + type: 'test' + ... +# Subtest: an unattributable drop keeps the hedge even with a preset list +ok 2119 - an unattributable drop keeps the hedge even with a preset list + --- + duration_ms: 7.947419 + type: 'test' + ... +# Subtest: a silent dropped name is not reported as a dead path, because a surviving catch-all still takes its traffic +ok 2120 - a silent dropped name is not reported as a dead path, because a surviving catch-all still takes its traffic + --- + duration_ms: 5.680734 + type: 'test' + ... +# Subtest: a covered dropped name is reported as losing its whole entry, not only its base_url +ok 2121 - a covered dropped name is reported as losing its whole entry, not only its base_url + --- + duration_ms: 6.517917 + type: 'test' + ... +# Subtest: two covered names read as two presets +ok 2122 - two covered names read as two presets + --- + duration_ms: 7.023765 + type: 'test' + ... +# Subtest: the hedged branch does not claim the traffic is dead either, because a catch-all still takes it +ok 2123 - the hedged branch does not claim the traffic is dead either, because a catch-all still takes it + --- + duration_ms: 8.34944 + type: 'test' + ... +# Subtest: the hedged branch pluralises for a multi-entry drop +ok 2124 - the hedged branch pluralises for a multi-entry drop + --- + duration_ms: 9.116295 + type: 'test' + ... +# Subtest: a covered name is not claimed to be proxied, because a surviving upstream can shadow the preset +ok 2125 - a covered name is not claimed to be proxied, because a surviving upstream can shadow the preset + --- + duration_ms: 14.401249 + type: 'test' + ... +# Subtest: a covered name does not claim path_prefix is in force, which a match()-carrying preset never routes on +ok 2126 - a covered name does not claim path_prefix is in force, which a match()-carrying preset never routes on + --- + duration_ms: 6.518487 + type: 'test' + ... +# Subtest: the hedged branch pluralises names off the names, not off the entry count +ok 2127 - the hedged branch pluralises names off the names, not off the entry count + --- + duration_ms: 12.997124 + type: 'test' + ... +# Subtest: a never-joined host reports no layering (V1 surface unchanged) +ok 2128 - a never-joined host reports no layering (V1 surface unchanged) + --- + duration_ms: 23.380215 + type: 'test' + ... +# Subtest: a joined host surfaces provenance and the dropped-local section +ok 2129 - a joined host surfaces provenance and the dropped-local section + --- + duration_ms: 19.646461 + type: 'test' + ... +# Subtest: status JSON renders per-row provenance and the config_layers block +ok 2130 - status JSON renders per-row provenance and the config_layers block + --- + duration_ms: 29.313994 + type: 'test' + ... +# Subtest: status text renders provenance tags and the dropped-local section +ok 2131 - status text renders provenance tags and the dropped-local section + --- + duration_ms: 14.175596 + type: 'test' + ... +# Subtest: a never-joined host renders no provenance tags or layers block +ok 2132 - a never-joined host renders no provenance tags or layers block + --- + duration_ms: 9.933558 + type: 'test' + ... +# Subtest: no local-only list yet: count is 0 and the text/JSON surfaces stay quiet +ok 2133 - no local-only list yet: count is 0 and the text/JSON surfaces stay quiet + --- + duration_ms: 24.792773 + type: 'test' + ... +# Subtest: an empty (but present) list also hides the line +ok 2134 - an empty (but present) list also hides the line + --- + duration_ms: 22.465395 + type: 'test' + ... +# Subtest: N > 0 renders the withholding line in text and the count in JSON +ok 2135 - N > 0 renders the withholding line in text and the count in JSON + --- + duration_ms: 12.348478 + type: 'test' + ... +# Subtest: a corrupt local-only list surfaces a diagnostic and a null usagePolicy, never a silent 0 +ok 2136 - a corrupt local-only list surfaces a diagnostic and a null usagePolicy, never a silent 0 + --- + duration_ms: 14.832844 + type: 'test' + ... +# Subtest: the new-folder line is enrolled-only, and states either mode +ok 2137 - the new-folder line is enrolled-only, and states either mode + --- + duration_ms: 14.405776 + type: 'test' + ... +# Subtest: an enabled claude-desktop with no attach marker warns, naming hyp claude-desktop install +ok 2138 - an enabled claude-desktop with no attach marker warns, naming hyp claude-desktop install + --- + duration_ms: 26.736647 + type: 'test' + ... +# Subtest: with claude-desktop not in the config, no such warning fires +ok 2139 - with claude-desktop not in the config, no such warning fires + --- + duration_ms: 20.291952 + type: 'test' + ... +# Subtest: recentEntrypointsFromSources lifts the gateway detail, newest first +ok 2140 - recentEntrypointsFromSources lifts the gateway detail, newest first + --- + duration_ms: 3.364614 + type: 'test' + ... +# Subtest: a malformed or partial entry is dropped, not repaired into a name no query can reproduce +ok 2141 - a malformed or partial entry is dropped, not repaired into a name no query can reproduce + --- + duration_ms: 0.2043 + type: 'test' + ... +# Subtest: a daemon with no gateway source, or an older daemon, yields an empty list +ok 2142 - a daemon with no gateway source, or an older daemon, yields an empty list + --- + duration_ms: 0.123809 + type: 'test' + ... +# Subtest: hyp status surfaces Codex Desktop traffic from status.json, with no cache read +ok 2143 - hyp status surfaces Codex Desktop traffic from status.json, with no cache read + --- + duration_ms: 36.174672 + type: 'test' + ... +# Subtest: an install that has never captured keeps the V1 text surface unchanged +ok 2144 - an install that has never captured keeps the V1 text surface unchanged + --- + duration_ms: 10.216919 + type: 'test' + ... +# Subtest: last-seen survives its daemon: a stopped daemon still reports what it saw +ok 2145 - last-seen survives its daemon: a stopped daemon still reports what it saw + --- + duration_ms: 14.211089 + type: 'test' + ... +# Subtest: formatEntrypointAge is coarse, and never renders a negative age +ok 2146 - formatEntrypointAge is coarse, and never renders a negative age + --- + duration_ms: 0.20444 + type: 'test' + ... +# Subtest: a client with no client_name renders without inventing one +ok 2147 - a client with no client_name renders without inventing one + --- + duration_ms: 9.792313 + type: 'test' + ... +# Subtest: recentEntrypointsFromSources cleans labels a foreign status file supplies +ok 2148 - recentEntrypointsFromSources cleans labels a foreign status file supplies + --- + duration_ms: 0.564989 + type: 'test' + ... +# Subtest: a rendered recent-clients block cannot be forged by a hostile entrypoint +ok 2149 - a rendered recent-clients block cannot be forged by a hostile entrypoint + --- + duration_ms: 8.340086 + type: 'test' + ... +# Subtest: a status file with an absurd number of entrypoints is capped on read +ok 2150 - a status file with an absurd number of entrypoints is capped on read + --- + duration_ms: 6.88229 + type: 'test' + ... +# Subtest: streaming reader handles a large file without loading it into memory +ok 2151 - streaming reader handles a large file without loading it into memory + --- + duration_ms: 733.986568 + type: 'test' + ... +# Subtest: partial trailing line is preserved and correctly handled +ok 2152 - partial trailing line is preserved and correctly handled + --- + duration_ms: 3.589486 + type: 'test' + ... +# Subtest: malformed JSON lines are counted, logged, and skipped without aborting +ok 2153 - malformed JSON lines are counted, logged, and skipped without aborting + --- + duration_ms: 6.911575 + type: 'test' + ... +# Subtest: resume cursor updates correctly and restart from cursor produces identical results +ok 2154 - resume cursor updates correctly and restart from cursor produces identical results + --- + duration_ms: 9.859385 + type: 'test' + ... +# Subtest: resume offset does not advance past a partially flushed line +ok 2155 - resume offset does not advance past a partially flushed line + --- + duration_ms: 2.983605 + type: 'test' + ... +# Subtest: batch boundaries respect row-count threshold +ok 2156 - batch boundaries respect row-count threshold + --- + duration_ms: 4.708527 + type: 'test' + ... +# Subtest: batch boundaries respect byte-size threshold +ok 2157 - batch boundaries respect byte-size threshold + --- + duration_ms: 5.383493 + type: 'test' + ... +# Subtest: rows are decorated with internal fields +ok 2158 - rows are decorated with internal fields + --- + duration_ms: 2.80995 + type: 'test' + ... +# Subtest: identical rows produce the same _hyp_cache_row_id +ok 2159 - identical rows produce the same _hyp_cache_row_id + --- + duration_ms: 1.90137 + type: 'test' + ... +# Subtest: progress file write and read roundtrip +ok 2160 - progress file write and read roundtrip + --- + duration_ms: 2.714776 + type: 'test' + ... +# Subtest: empty file yields no batches +ok 2161 - empty file yields no batches + --- + duration_ms: 1.265844 + type: 'test' + ... +# Subtest: no TTY and no --yes: refuses, exports nothing, and leaves the hold standing +ok 2162 - no TTY and no --yes: refuses, exports nothing, and leaves the hold standing + --- + duration_ms: 25.595763 + type: 'test' + ... +# Subtest: declining at the prompt cancels, exports nothing, and leaves the hold standing +ok 2163 - declining at the prompt cancels, exports nothing, and leaves the hold standing + --- + duration_ms: 7.782178 + type: 'test' + ... +# Subtest: confirming during the review window ends it and exports +ok 2164 - confirming during the review window ends it and exports + --- + duration_ms: 8.365014 + type: 'test' + ... +# Subtest: the held prompt states the window, the irreversibility, and the way out +ok 2165 - the held prompt states the window, the irreversibility, and the way out + --- + duration_ms: 2.898065 + type: 'test' + ... +# Subtest: --dry-run prints the plan, exports nothing, and keeps the window open +ok 2166 - --dry-run prints the plan, exports nothing, and keeps the window open + --- + duration_ms: 2.591278 + type: 'test' + ... +# Subtest: the plan names each destination and whether it leaves the machine +ok 2167 - the plan names each destination and whether it leaves the machine + --- + duration_ms: 2.743199 + type: 'test' + ... +# Subtest: an unnamed server falls back to its host, still not a linkifiable URL +ok 2168 - an unnamed server falls back to its host, still not a linkifiable URL + --- + duration_ms: 1.000941 + type: 'test' + ... +# Subtest: a named instance cannot release the hold: the plan it showed was not the hold's scope +ok 2169 - a named instance cannot release the hold: the plan it showed was not the hold's scope + --- + duration_ms: 1.908992 + type: 'test' + ... +# Subtest: --yes cannot release the hold: \#no-release licenses an attended confirmation only +ok 2170 - --yes cannot release the hold: \#no-release licenses an attended confirmation only + --- + duration_ms: 8.068363 + type: 'test' + ... +# Subtest: a hold that cannot be cleared fails loudly instead of exiting 0 with nothing sent +ok 2171 - a hold that cannot be cleared fails loudly instead of exiting 0 with nothing sent + --- + duration_ms: 4.101835 + type: 'test' + ... +# Subtest: the plan counts the directories being withheld +ok 2172 - the plan counts the directories being withheld + --- + duration_ms: 2.13161 + type: 'test' + ... +# Subtest: the plan names the clients kept local-only (LLP 0188 \#never-silent) +ok 2173 - the plan names the clients kept local-only (LLP 0188 \#never-silent) + --- + duration_ms: 3.154064 + type: 'test' + ... +# Subtest: with nothing marked, the plan says so in one line covering both stores +ok 2174 - with nothing marked, the plan says so in one line covering both stores + --- + duration_ms: 2.066091 + type: 'test' + ... +# Subtest: with no hold, --yes exports without inventing a review window +ok 2175 - with no hold, --yes exports without inventing a review window + --- + duration_ms: 2.312875 + type: 'test' + ... +# Subtest: an unknown instance names the ones that exist +ok 2176 - an unknown instance names the ones that exist + --- + duration_ms: 0.802309 + type: 'test' + ... +# Subtest: an instance argument ticks only that sink (no hold in play) +ok 2177 - an instance argument ticks only that sink (no hold in play) + --- + duration_ms: 4.278974 + type: 'test' + ... +# Subtest: no sinks at all is a no-op, not an error +ok 2178 - no sinks at all is a no-op, not an error + --- + duration_ms: 0.782058 + type: 'test' + ... +# Subtest: a failed export reports a nonzero exit +ok 2179 - a failed export reports a nonzero exit + --- + duration_ms: 1.65145 + type: 'test' + ... +# Subtest: unionSources unions columns and sums numRows +ok 2180 - unionSources unions columns and sums numRows + --- + duration_ms: 1.555294 + type: 'test' + ... +# Subtest: unionSources does not forward limit/offset to sub-sources +ok 2181 - unionSources does not forward limit/offset to sub-sources + --- + duration_ms: 0.54588 + type: 'test' + ... +# Subtest: unionSources forwards where/columns to sub-sources that have the predicate columns +ok 2182 - unionSources forwards where/columns to sub-sources that have the predicate columns + --- + duration_ms: 0.383453 + type: 'test' + ... +# Subtest: unionSources over two real parquet partitions filters correctly through executeSql (WHERE column folded into projection) +ok 2183 - unionSources over two real parquet partitions filters correctly through executeSql (WHERE column folded into projection) + --- + duration_ms: 16.000339 + type: 'test' + ... +# Subtest: unionSources drops where for a partition that lacks a predicate column but keeps it for one that has it +ok 2184 - unionSources drops where for a partition that lacks a predicate column but keeps it for one that has it + --- + duration_ms: 0.30981 + type: 'test' + ... +# Subtest: unionSources does not push qualified where predicates to sub-sources +ok 2185 - unionSources does not push qualified where predicates to sub-sources + --- + duration_ms: 0.211832 + type: 'test' + ... +# Subtest: unionSources does not push a non-enumerable where (subquery) to any sub-source +ok 2186 - unionSources does not push a non-enumerable where (subquery) to any sub-source + --- + duration_ms: 0.353697 + type: 'test' + ... +# Subtest: unionSources tolerates a scan with no options +ok 2187 - unionSources tolerates a scan with no options + --- + duration_ms: 0.199093 + type: 'test' + ... +# Subtest: unionSources omits scanColumn unless every partition can stream the column +ok 2188 - unionSources omits scanColumn unless every partition can stream the column + --- + duration_ms: 0.379216 + type: 'test' + ... +# Subtest: unionSources scanColumn concatenates partitions and owns limit/offset over the merged stream +ok 2189 - unionSources scanColumn concatenates partitions and owns limit/offset over the merged stream + --- + duration_ms: 0.780416 + type: 'test' + ... +# Subtest: unionSources scanColumn skips a whole partition its numRows proves is inside the offset +ok 2190 - unionSources scanColumn skips a whole partition its numRows proves is inside the offset + --- + duration_ms: 0.270521 + type: 'test' + ... +# Subtest: unionSources scanColumn forwards where per partition and reports the merged appliedWhere +ok 2191 - unionSources scanColumn forwards where per partition and reports the merged appliedWhere + --- + duration_ms: 0.482042 + type: 'test' + ... +# Subtest: unionSources scanColumn drops where for a partition lacking a predicate column and reports appliedWhere false +ok 2192 - unionSources scanColumn drops where for a partition lacking a predicate column and reports appliedWhere false + --- + duration_ms: 0.232093 + type: 'test' + ... +# Subtest: unionSources scanColumn reports appliedWhere false over a legacy bare-iterable partition +ok 2193 - unionSources scanColumn reports appliedWhere false over a legacy bare-iterable partition + --- + duration_ms: 0.28929 + type: 'test' + ... +# Subtest: normalizeScanColumn passes a flagged result through and shims a legacy iterable +ok 2194 - normalizeScanColumn passes a flagged result through and shims a legacy iterable + --- + duration_ms: 0.158441 + type: 'test' + ... +# Subtest: emptySource advertises the given columns and yields no rows +ok 2195 - emptySource advertises the given columns and yields no rows + --- + duration_ms: 0.183449 + type: 'test' + ... +# Subtest: verbArgvForClass maps each class to its hyp policy set token (LLP 0111 \#teaching) +ok 2196 - verbArgvForClass maps each class to its hyp policy set token (LLP 0111 \#teaching) + --- + duration_ms: 1.670088 + type: 'test' + ... +# Subtest: the three choices are presented least-to-most restrictive with their tokens +ok 2197 - the three choices are presented least-to-most restrictive with their tokens + --- + duration_ms: 0.184891 + type: 'test' + ... +# Subtest: buildClassificationPrompt names the folder, all three classes, and each policy set command +ok 2198 - buildClassificationPrompt names the folder, all three classes, and each policy set command + --- + duration_ms: 0.357914 + type: 'test' + ... +# Subtest: the prompt names its own off switch (LLP 0200 \#escape-hatch) +ok 2199 - the prompt names its own off switch (LLP 0200 \#escape-hatch) + --- + duration_ms: 0.131169 + type: 'test' + ... +# Subtest: decideClassification: with the ask on, prompt only when enrolled AND interactive AND unclassified +ok 2200 - decideClassification: with the ask on, prompt only when enrolled AND interactive AND unclassified + --- + duration_ms: 0.25656 + type: 'test' + ... +# Subtest: decideClassification: the default sync mode means no ask at all (LLP 0200 \#default) +ok 2201 - decideClassification: the default sync mode means no ask at all (LLP 0200 \#default) + --- + duration_ms: 0.127724 + type: 'test' + ... +# Subtest: evaluateCwdClassification honors a standing sync preference and reports it +ok 2202 - evaluateCwdClassification honors a standing sync preference and reports it + --- + duration_ms: 29.996651 + type: 'test' + ... +# Subtest: a machine that never answered is not asked: the default is sync (LLP 0200 \#default) +ok 2203 - a machine that never answered is not asked: the default is sync (LLP 0200 \#default) + --- + duration_ms: 1.358104 + type: 'test' + ... +# Subtest: turning the ask on restores the per-folder question +ok 2204 - turning the ask on restores the per-folder question + --- + duration_ms: 2.370603 + type: 'test' + ... +# Subtest: a corrupt preference costs a question, never a silent sync +ok 2205 - a corrupt preference costs a question, never a silent sync + --- + duration_ms: 3.545189 + type: 'test' + ... +# Subtest: evaluateCwdClassification prompts for an enrolled, interactive, unclassified cwd with the ask on +ok 2206 - evaluateCwdClassification prompts for an enrolled, interactive, unclassified cwd with the ask on + --- + duration_ms: 0.35535 + type: 'test' + ... +# Subtest: evaluateCwdClassification is inert on an unenrolled machine +ok 2207 - evaluateCwdClassification is inert on an unenrolled machine + --- + duration_ms: 0.941771 + type: 'test' + ... +# Subtest: evaluateCwdClassification does not prompt once the folder is classified +ok 2208 - evaluateCwdClassification does not prompt once the folder is classified + --- + duration_ms: 0.201536 + type: 'test' + ... +# Subtest: evaluateCwdClassification passes a non-interactive session through +ok 2209 - evaluateCwdClassification passes a non-interactive session through + --- + duration_ms: 0.218042 + type: 'test' + ... +# Subtest: evaluateCwdClassification never fails the session on a corrupt list or a broken enrollment read +ok 2210 - evaluateCwdClassification never fails the session on a corrupt list or a broken enrollment read + --- + duration_ms: 1.176328 + type: 'test' + ... +# Subtest: the classification answer lands via the real hyp policy set verb (LLP 0106 -> LLP 0111 -> LLP 0103) +ok 2211 - the classification answer lands via the real hyp policy set verb (LLP 0106 -> LLP 0111 -> LLP 0103) + --- + duration_ms: 6.99384 + type: 'test' + ... +# Subtest: readRowsSince: local-only cwd rows are dropped from the payload but the cursor advances across them; full and cwd-less rows pass +ok 2212 - readRowsSince: local-only cwd rows are dropped from the payload but the cursor advances across them; full and cwd-less rows pass + --- + duration_ms: 43.577464 + type: 'test' + ... +# Subtest: readRowsSince: a `columns` projection omitting cwd still withholds local-only rows, and full rows come back without a cwd field +ok 2213 - readRowsSince: a `columns` projection omitting cwd still withholds local-only rows, and full rows come back without a cwd field + --- + duration_ms: 17.29071 + type: 'test' + ... +# Subtest: readRowsSince: a corrupt list (resolver throws) fails the partition read rather than silently skipping +ok 2214 - readRowsSince: a corrupt list (resolver throws) fails the partition read rather than silently skipping + --- + duration_ms: 35.7853 + type: 'test' + ... +# Subtest: readRowsSince: with no resolver configured, nothing is ever dropped (cwd is ignored) +ok 2215 - readRowsSince: with no resolver configured, nothing is ever dropped (cwd is ignored) + --- + duration_ms: 22.839574 + type: 'test' + ... +# Subtest: openIncrementalRows: leading drops are skipped from the encoder stream but advance lastAfter +ok 2216 - openIncrementalRows: leading drops are skipped from the encoder stream but advance lastAfter + --- + duration_ms: 0.827538 + type: 'test' + ... +# Subtest: openIncrementalRows: trailing drops still advance lastAfter past the last real row +ok 2217 - openIncrementalRows: trailing drops still advance lastAfter past the last real row + --- + duration_ms: 0.334488 + type: 'test' + ... +# Subtest: openIncrementalRows: an all-drops partition is empty yet exposes droppedRowCount and an advanced lastAfter +ok 2218 - openIncrementalRows: an all-drops partition is empty yet exposes droppedRowCount and an advanced lastAfter + --- + duration_ms: 0.254367 + type: 'test' + ... +# Subtest: openIncrementalRows: interleaved drops are skipped, real rows kept in order +ok 2219 - openIncrementalRows: interleaved drops are skipped, real rows kept in order + --- + duration_ms: 0.27641 + type: 'test' + ... +# Subtest: the two fixture spellings are genuinely different strings that NFC folds together +ok 2220 - the two fixture spellings are genuinely different strings that NFC folds together + --- + duration_ms: 0.906368 + type: 'test' + ... +# Subtest: resolve: a local-only entry declared NFC still governs a cwd that arrives NFD +ok 2221 - resolve: a local-only entry declared NFC still governs a cwd that arrives NFD + --- + duration_ms: 2.067392 + type: 'test' + ... +# Subtest: resolve: an ignore entry declared NFD still governs a cwd that arrives NFC +ok 2222 - resolve: an ignore entry declared NFD still governs a cwd that arrives NFC + --- + duration_ms: 2.854478 + type: 'test' + ... +# Subtest: resolve: the fold is on the ancestor segment, not only the leaf +ok 2223 - resolve: the fold is on the ancestor segment, not only the leaf + --- + duration_ms: 0.589506 + type: 'test' + ... +# Subtest: foldPath distributes over the path separator, so a prefix test stays segment-aware +ok 2224 - foldPath distributes over the path separator, so a prefix test stays segment-aware + --- + duration_ms: 0.205082 + type: 'test' + ... +# Subtest: resolve: a sibling whose name merely shares a folded prefix is still NOT matched +ok 2225 - resolve: a sibling whose name merely shares a folded prefix is still NOT matched + --- + duration_ms: 0.474832 + type: 'test' + ... +# Subtest: resolve: a carve-out that gains reach only by folding does not punch a hole in a broader restrictive entry +ok 2226 - resolve: a carve-out that gains reach only by folding does not punch a hole in a broader restrictive entry + --- + duration_ms: 1.528543 + type: 'test' + ... +# Subtest: resolve: an entry that gains reach by folding overrides a shallower explicit full marker +ok 2227 - resolve: an entry that gains reach by folding overrides a shallower explicit full marker + --- + duration_ms: 0.410123 + type: 'test' + ... +# Subtest: resolve: a carve-out declared in the same spelling as the entry it carves out of is still honored +ok 2228 - resolve: a carve-out declared in the same spelling as the entry it carves out of is still honored + --- + duration_ms: 0.591469 + type: 'test' + ... +# Subtest: resolve: nearest-governs is measured on the folded spelling, not on the declared one +ok 2229 - resolve: nearest-governs is measured on the folded spelling, not on the declared one + --- + duration_ms: 0.761338 + type: 'test' + ... +# Subtest: resolve: folding never loosens, over every arrangement of a nested pair +ok 2230 - resolve: folding never loosens, over every arrangement of a nested pair + --- + duration_ms: 18.397282 + type: 'test' + ... +# Subtest: resolve: case is NOT folded by default, because this volume is case-sensitive +ok 2231 - resolve: case is NOT folded by default, because this volume is case-sensitive + --- + duration_ms: 0.272934 + type: 'test' + ... +# Subtest: resolve: case IS folded when the volume verdict says the volume is case-insensitive +ok 2232 - resolve: case IS folded when the volume verdict says the volume is case-insensitive + --- + duration_ms: 0.275128 + type: 'test' + ... +# Subtest: resolve: a case-insensitive volume verdict still does not let a carve-out loosen a broader entry +ok 2233 - resolve: a case-insensitive volume verdict still does not let a carve-out loosen a broader entry + --- + duration_ms: 0.332696 + type: 'test' + ... +# Subtest: resolve: the case verdict is asked per entry, so a per-volume answer applies per entry +ok 2234 - resolve: the case verdict is asked per entry, so a per-volume answer applies per entry + --- + duration_ms: 0.889312 + type: 'test' + ... +# Subtest: createVolumeCaseProbe is inert off darwin: constant false, and it issues no syscall +ok 2235 - createVolumeCaseProbe is inert off darwin: constant false, and it issues no syscall + --- + duration_ms: 0.104268 + type: 'test' + ... +# Subtest: createVolumeCaseProbe memoizes a definite verdict per volume, not per path +ok 2236 - createVolumeCaseProbe memoizes a definite verdict per volume, not per path + --- + duration_ms: 0.203359 + type: 'test' + ... +# Subtest: createVolumeCaseProbe does not memoize an undetermined answer as the volume verdict +ok 2237 - createVolumeCaseProbe does not memoize an undetermined answer as the volume verdict + --- + duration_ms: 0.163518 + type: 'test' + ... +# Subtest: createVolumeCaseProbe does not memoize a non-ENOENT failure of the flipped stat +ok 2238 - createVolumeCaseProbe does not memoize a non-ENOENT failure of the flipped stat + --- + duration_ms: 0.188687 + type: 'test' + ... +# Subtest: createVolumeCaseProbe reports case-sensitive when the flipped spelling does not exist +ok 2239 - createVolumeCaseProbe reports case-sensitive when the flipped spelling does not exist + --- + duration_ms: 0.12499 + type: 'test' + ... +# Subtest: createVolumeCaseProbe fails toward the pre-fold behaviour and logs a hashed skip +ok 2240 - createVolumeCaseProbe fails toward the pre-fold behaviour and logs a hashed skip + --- + duration_ms: 0.271002 + type: 'test' + ... +# Subtest: resolve emits a hashed usage_policy.fold_tightened only when folding changed the verdict +ok 2241 - resolve emits a hashed usage_policy.fold_tightened only when folding changed the verdict + --- + duration_ms: 0.543436 + type: 'test' + ... +# Subtest: resolve: folding happens on the cache miss only, so a repeated cwd re-reads nothing +ok 2242 - resolve: folding happens on the cache miss only, so a repeated cwd re-reads nothing + --- + duration_ms: 0.498528 + type: 'test' + ... +# Subtest: localOnlyListPath derives /usage-policy/local-only.json +ok 2243 - localOnlyListPath derives /usage-policy/local-only.json + --- + duration_ms: 0.643688 + type: 'test' + ... +# Subtest: localOnlyListPath requires a stateDir +ok 2244 - localOnlyListPath requires a stateDir + --- + duration_ms: 0.194115 + type: 'test' + ... +# Subtest: readLocalOnlyDirs returns [] when the list has never been written (the common case) +ok 2245 - readLocalOnlyDirs returns [] when the list has never been written (the common case) + --- + duration_ms: 5.077458 + type: 'test' + ... +# Subtest: writeLocalOnlyDirs then readLocalOnlyDirs round-trips the directory set +ok 2246 - writeLocalOnlyDirs then readLocalOnlyDirs round-trips the directory set + --- + duration_ms: 2.141135 + type: 'test' + ... +# Subtest: writeLocalOnlyDirs persists the LLP 0103 version-2 class-per-entry shape +ok 2247 - writeLocalOnlyDirs persists the LLP 0103 version-2 class-per-entry shape + --- + duration_ms: 1.005919 + type: 'test' + ... +# Subtest: writeLocalOnlyDirs normalizes to absolute paths, dedupes, and sorts +ok 2248 - writeLocalOnlyDirs normalizes to absolute paths, dedupes, and sorts + --- + duration_ms: 3.347398 + type: 'test' + ... +# Subtest: readLocalOnlyDirs re-normalizes a hand-edited file with duplicates/relative-looking entries +ok 2249 - readLocalOnlyDirs re-normalizes a hand-edited file with duplicates/relative-looking entries + --- + duration_ms: 1.260897 + type: 'test' + ... +# Subtest: readLocalOnlyDirs throws LocalOnlyListUnreadableError on unparseable JSON +ok 2250 - readLocalOnlyDirs throws LocalOnlyListUnreadableError on unparseable JSON + --- + duration_ms: 1.511257 + type: 'test' + ... +# Subtest: readLocalOnlyDirs throws LocalOnlyListUnreadableError on a wrong-shape file +ok 2251 - readLocalOnlyDirs throws LocalOnlyListUnreadableError on a wrong-shape file + --- + duration_ms: 1.549144 + type: 'test' + ... +# Subtest: readLocalOnlyDirs throws LocalOnlyListUnreadableError when dirs is not a string array +ok 2252 - readLocalOnlyDirs throws LocalOnlyListUnreadableError when dirs is not a string array + --- + duration_ms: 3.424495 + type: 'test' + ... +# Subtest: writeLocalOnlyDirs is atomic write-rename and leaves no temp files +ok 2253 - writeLocalOnlyDirs is atomic write-rename and leaves no temp files + --- + duration_ms: 1.169117 + type: 'test' + ... +# Subtest: writeLocalOnlyDirs replaces the file in place (latest wins, single file) +ok 2254 - writeLocalOnlyDirs replaces the file in place (latest wins, single file) + --- + duration_ms: 3.595795 + type: 'test' + ... +# Subtest: writeLocalOnlyDirs mkdir -p s the usage-policy parent directory on demand +ok 2255 - writeLocalOnlyDirs mkdir -p s the usage-policy parent directory on demand + --- + duration_ms: 1.462593 + type: 'test' + ... +# Subtest: resolve: an ignored directory reached by its symlink spelling is still ignored +ok 2256 - resolve: an ignored directory reached by its symlink spelling is still ignored + --- + duration_ms: 4.418305 + type: 'test' + ... +# Subtest: resolve: a descendant of a symlink that points into an ignored tree is ignored +ok 2257 - resolve: a descendant of a symlink that points into an ignored tree is ignored + --- + duration_ms: 1.792404 + type: 'test' + ... +# Subtest: resolve: a `.hypignore` governing the symlink's own ancestors still governs (the converse spelling is not lost) +ok 2258 - resolve: a `.hypignore` governing the symlink's own ancestors still governs (the converse spelling is not lost) + --- + duration_ms: 1.383943 + type: 'test' + ... +# Subtest: resolve: a local-only entry declared by its symlink spelling governs the real directory +ok 2259 - resolve: a local-only entry declared by its symlink spelling governs the real directory + --- + duration_ms: 3.673894 + type: 'test' + ... +# Subtest: resolve: a local-only entry declared canonically governs a cwd that arrives by symlink +ok 2260 - resolve: a local-only entry declared canonically governs a cwd that arrives by symlink + --- + duration_ms: 1.689688 + type: 'test' + ... +# Subtest: resolve: nested entries keep nearest-governs when the outer one matched by its canonical spelling +ok 2261 - resolve: nested entries keep nearest-governs when the outer one matched by its canonical spelling + --- + duration_ms: 1.502814 + type: 'test' + ... +# Subtest: resolve: a carve-out that gains reach by canonicalization does not punch a hole in a broader restrictive entry +ok 2262 - resolve: a carve-out that gains reach by canonicalization does not punch a hole in a broader restrictive entry + --- + duration_ms: 1.631319 + type: 'test' + ... +# Subtest: resolve: between two entries that both reach only by canonicalization, the deeper carve-out governs +ok 2263 - resolve: between two entries that both reach only by canonicalization, the deeper carve-out governs + --- + duration_ms: 2.035374 + type: 'test' + ... +# Subtest: governingListEntry names the entry whose verdict the gate used, not the longest declared string +ok 2264 - governingListEntry names the entry whose verdict the gate used, not the longest declared string + --- + duration_ms: 4.615706 + type: 'test' + ... +# Subtest: resolve: a cwd reached through a dangling symlink keeps the class its as-given spelling produces +ok 2265 - resolve: a cwd reached through a dangling symlink keeps the class its as-given spelling produces + --- + duration_ms: 3.266245 + type: 'test' + ... +# Subtest: resolve: a symlinked cwd is still not matched by a mere string-prefix sibling (segment-aware after canonicalization) +ok 2266 - resolve: a symlinked cwd is still not matched by a mere string-prefix sibling (segment-aware after canonicalization) + --- + duration_ms: 1.624208 + type: 'test' + ... +# Subtest: resolve: a list entry whose declared target has been deleted keeps governing its declared spelling +ok 2267 - resolve: a list entry whose declared target has been deleted keeps governing its declared spelling + --- + duration_ms: 1.166052 + type: 'test' + ... +# Subtest: resolve: an unresolvable cwd under a symlinked ancestor still meets the .hypignore governing its real tree +ok 2268 - resolve: an unresolvable cwd under a symlinked ancestor still meets the .hypignore governing its real tree + --- + duration_ms: 1.356452 + type: 'test' + ... +# Subtest: resolve: canonicalization costs one realpath per cwd per TTL window, not one per call +ok 2269 - resolve: canonicalization costs one realpath per cwd per TTL window, not one per call + --- + duration_ms: 0.836211 + type: 'test' + ... +# Subtest: canonicalizeDirSync: full, partial, and unresolvable outcomes +ok 2270 - canonicalizeDirSync: full, partial, and unresolvable outcomes + --- + duration_ms: 1.696218 + type: 'test' + ... +# Subtest: scopeGoverns / sameDirectory: spelling-agnostic, still segment-aware +ok 2271 - scopeGoverns / sameDirectory: spelling-agnostic, still segment-aware + --- + duration_ms: 2.56617 + type: 'test' + ... +# Subtest: parseHypignore: empty body => ignore (the empty-file opt-out) +ok 2272 - parseHypignore: empty body => ignore (the empty-file opt-out) + --- + duration_ms: 2.002623 + type: 'test' + ... +# Subtest: parseHypignore: comment-only/blank body => ignore +ok 2273 - parseHypignore: comment-only/blank body => ignore + --- + duration_ms: 0.212733 + type: 'test' + ... +# Subtest: parseHypignore: recognized `ignore` token => ignore, no warn +ok 2274 - parseHypignore: recognized `ignore` token => ignore, no warn + --- + duration_ms: 0.140663 + type: 'test' + ... +# Subtest: parseHypignore: unknown token => ignore + warn (fail-safe) +ok 2275 - parseHypignore: unknown token => ignore + warn (fail-safe) + --- + duration_ms: 0.212323 + type: 'test' + ... +# Subtest: parseHypignore: implemented `local-only` token resolves to local-only, no warn (LLP 0070) +ok 2276 - parseHypignore: implemented `local-only` token resolves to local-only, no warn (LLP 0070) + --- + duration_ms: 0.186143 + type: 'test' + ... +# Subtest: parseHypignore: a still-unimplemented token => ignore + warn (fail-safe) +ok 2277 - parseHypignore: a still-unimplemented token => ignore + warn (fail-safe) + --- + duration_ms: 0.13186 + type: 'test' + ... +# Subtest: parseHypignore: first token wins; trailing path patterns are parsed-but-ignored +ok 2278 - parseHypignore: first token wins; trailing path patterns are parsed-but-ignored + --- + duration_ms: 0.161836 + type: 'test' + ... +# Subtest: resolve: no .hypignore anywhere => full, governedBy null +ok 2279 - resolve: no .hypignore anywhere => full, governedBy null + --- + duration_ms: 1.2174 + type: 'test' + ... +# Subtest: resolve: nearest ancestor .hypignore wins +ok 2280 - resolve: nearest ancestor .hypignore wins + --- + duration_ms: 0.696198 + type: 'test' + ... +# Subtest: resolve: walks all the way to the filesystem root +ok 2281 - resolve: walks all the way to the filesystem root + --- + duration_ms: 0.564488 + type: 'test' + ... +# Subtest: resolve: dotfile `local-only` token is honored (implemented, LLP 0070), not clamped to ignore +ok 2282 - resolve: dotfile `local-only` token is honored (implemented, LLP 0070), not clamped to ignore + --- + duration_ms: 0.285113 + type: 'test' + ... +# Subtest: resolve: a still-unimplemented class in a governing file fails safe to ignore +ok 2283 - resolve: a still-unimplemented class in a governing file fails safe to ignore + --- + duration_ms: 0.224511 + type: 'test' + ... +# Subtest: resolve: a present-but-unreadable .hypignore fails closed to ignore (privacy-protecting) +ok 2284 - resolve: a present-but-unreadable .hypignore fails closed to ignore (privacy-protecting) + --- + duration_ms: 0.254317 + type: 'test' + ... +# Subtest: resolve: per-cwd cache is stable and reads the file once +ok 2285 - resolve: per-cwd cache is stable and reads the file once + --- + duration_ms: 0.566241 + type: 'test' + ... +# Subtest: resolve: relative cwd is normalized before caching +ok 2286 - resolve: relative cwd is normalized before caching + --- + duration_ms: 0.176599 + type: 'test' + ... +# Subtest: resolve: a .hypignore written after a cwd was cached `full` is honored once the TTL elapses +ok 2287 - resolve: a .hypignore written after a cwd was cached `full` is honored once the TTL elapses + --- + duration_ms: 0.48703 + type: 'test' + ... +# Subtest: resolve: removing a .hypignore is honored once the TTL elapses (unignore) +ok 2288 - resolve: removing a .hypignore is honored once the TTL elapses (unignore) + --- + duration_ms: 0.33609 + type: 'test' + ... +# Subtest: resolve: no localOnlyListPath configured => resolver behaves exactly as before +ok 2289 - resolve: no localOnlyListPath configured => resolver behaves exactly as before + --- + duration_ms: 0.279655 + type: 'test' + ... +# Subtest: resolve: cwd equal to a listed dir is local-only, governed by the list path +ok 2290 - resolve: cwd equal to a listed dir is local-only, governed by the list path + --- + duration_ms: 0.579841 + type: 'test' + ... +# Subtest: resolve: cwd descendant of a listed dir is local-only +ok 2291 - resolve: cwd descendant of a listed dir is local-only + --- + duration_ms: 0.344825 + type: 'test' + ... +# Subtest: resolve: sibling-prefix directory is NOT matched (segment-aware: /a/bc vs /a/b) +ok 2292 - resolve: sibling-prefix directory is NOT matched (segment-aware: /a/bc vs /a/b) + --- + duration_ms: 0.511097 + type: 'test' + ... +# Subtest: resolve: dotfile `ignore` beats a list `local-only` match (most-restrictive wins) +ok 2293 - resolve: dotfile `ignore` beats a list `local-only` match (most-restrictive wins) + --- + duration_ms: 0.318154 + type: 'test' + ... +# Subtest: resolve: list `local-only` beats an unlisted `full` dotfile default (most-restrictive wins) +ok 2294 - resolve: list `local-only` beats an unlisted `full` dotfile default (most-restrictive wins) + --- + duration_ms: 0.277842 + type: 'test' + ... +# Subtest: resolve: cwd not in the list and no governing dotfile => full +ok 2295 - resolve: cwd not in the list and no governing dotfile => full + --- + duration_ms: 0.268488 + type: 'test' + ... +# Subtest: resolve: a missing local-only list file is "no exclusions" ([]) +ok 2296 - resolve: a missing local-only list file is "no exclusions" ([]) + --- + duration_ms: 0.167515 + type: 'test' + ... +# Subtest: resolve: list parse is memoized (TTL) independent of per-cwd caching, and re-read picks up an edited list +ok 2297 - resolve: list parse is memoized (TTL) independent of per-cwd caching, and re-read picks up an edited list + --- + duration_ms: 0.662927 + type: 'test' + ... +# Subtest: resolve: a corrupt local-only list throws, not silently "no exclusions" (fail-safe) +ok 2298 - resolve: a corrupt local-only list throws, not silently "no exclusions" (fail-safe) + --- + duration_ms: 0.53285 + type: 'test' + ... +# Subtest: resolve: a v2 list entry resolves with its own recorded class, not a hardcoded local-only +ok 2299 - resolve: a v2 list entry resolves with its own recorded class, not a hardcoded local-only + --- + duration_ms: 0.3356 + type: 'test' + ... +# Subtest: resolve: an explicit v2 "full" entry resolves to full, governed by the list (the "asked; syncs" marker) +ok 2300 - resolve: an explicit v2 "full" entry resolves to full, governed by the list (the "asked; syncs" marker) + --- + duration_ms: 0.286766 + type: 'test' + ... +# Subtest: resolve: among nested v2 list entries the most specific (longest) dir wins +ok 2301 - resolve: among nested v2 list entries the most specific (longest) dir wins + --- + duration_ms: 0.610998 + type: 'test' + ... +# Subtest: resolve: a v1 list is migrated on read, and continues to resolve as local-only +ok 2302 - resolve: a v1 list is migrated on read, and continues to resolve as local-only + --- + duration_ms: 0.268367 + type: 'test' + ... +# Subtest: resolve: a v2 list entry with an unknown class throws, not silently "no exclusions" (fail-safe) +ok 2303 - resolve: a v2 list entry with an unknown class throws, not silently "no exclusions" (fail-safe) + --- + duration_ms: 0.201517 + type: 'test' + ... +# Subtest: createUsagePolicyResolver defaults fs to node:fs when none injected +ok 2304 - createUsagePolicyResolver defaults fs to node:fs when none injected + --- + duration_ms: 0.188797 + type: 'test' + ... +# Subtest: findRepoRoot: nearest ancestor with a .git entry is the repo root +ok 2305 - findRepoRoot: nearest ancestor with a .git entry is the repo root + --- + duration_ms: 0.161746 + type: 'test' + ... +# Subtest: findRepoRoot: the start dir itself can be the repo root +ok 2306 - findRepoRoot: the start dir itself can be the repo root + --- + duration_ms: 0.140574 + type: 'test' + ... +# Subtest: findRepoRoot: returns null when no ancestor has a .git +ok 2307 - findRepoRoot: returns null when no ancestor has a .git + --- + duration_ms: 0.113242 + type: 'test' + ... +# Subtest: findRepoRoot: defaults fs to node:fs without throwing +ok 2308 - findRepoRoot: defaults fs to node:fs without throwing + --- + duration_ms: 0.10519 + type: 'test' + ... +# Subtest: atomicWriteFile writes content and creates parent directories +ok 2309 - atomicWriteFile writes content and creates parent directories + --- + duration_ms: 9.053791 + type: 'test' + ... +# Subtest: atomicWriteFile leaves no temp file behind on success or failure +ok 2310 - atomicWriteFile leaves no temp file behind on success or failure + --- + duration_ms: 10.070907 + type: 'test' + ... +# Subtest: atomicWriteFile applies the requested file mode +ok 2311 - atomicWriteFile applies the requested file mode + --- + duration_ms: 6.34925 + type: 'test' + ... +# Subtest: atomicWriteFile enforces expectedMtimeMs (CONCURRENT_EDIT) +ok 2312 - atomicWriteFile enforces expectedMtimeMs (CONCURRENT_EDIT) + --- + duration_ms: 10.64555 + type: 'test' + ... +# Subtest: atomicWriteFileSync writes atomically and cleans up on failure +ok 2313 - atomicWriteFileSync writes atomically and cleans up on failure + --- + duration_ms: 2.084719 + type: 'test' + ... +# Subtest: atomicWriteJson round-trips through readJsonIfExists +ok 2314 - atomicWriteJson round-trips through readJsonIfExists + --- + duration_ms: 3.357743 + type: 'test' + ... +# Subtest: readFileIfExists/readJsonIfExists return null only for ENOENT +ok 2315 - readFileIfExists/readJsonIfExists return null only for ENOENT + --- + duration_ms: 2.328509 + type: 'test' + ... +# Subtest: isPlainObject accepts records, rejects null/arrays/primitives +ok 2316 - isPlainObject accepts records, rejects null/arrays/primitives + --- + duration_ms: 0.962212 + type: 'test' + ... +# Subtest: stringValue passes through non-empty strings only +ok 2317 - stringValue passes through non-empty strings only + --- + duration_ms: 0.188417 + type: 'test' + ... +# Subtest: parseMaybeJson parses strings, passes everything else through +ok 2318 - parseMaybeJson parses strings, passes everything else through + --- + duration_ms: 0.619121 + type: 'test' + ... +# Subtest: canonicalJson is key-order independent +ok 2319 - canonicalJson is key-order independent + --- + duration_ms: 0.26333 + type: 'test' + ... +# Subtest: sortKeys deep-copies without mutating the input +ok 2320 - sortKeys deep-copies without mutating the input + --- + duration_ms: 0.166173 + type: 'test' + ... +# Subtest: sha256Hex matches the known digest of an empty string +ok 2321 - sha256Hex matches the known digest of an empty string + --- + duration_ms: 0.484076 + type: 'test' + ... +# Subtest: errCode extracts string codes and nothing else +ok 2322 - errCode extracts string codes and nothing else + --- + duration_ms: 0.23025 + type: 'test' + ... +# Subtest: sanitizeLabel strips exactly the code points it stripped before LLP 0225 +ok 2323 - sanitizeLabel strips exactly the code points it stripped before LLP 0225 + --- + duration_ms: 23.176897 + type: 'test' + ... +# Subtest: escapeForDisplay replaces control characters with visible escapes +ok 2324 - escapeForDisplay replaces control characters with visible escapes + --- + duration_ms: 0.38189 + type: 'test' + ... +# Subtest: escapeForDisplay escapes bidi formatting and leaves zero-width formatting alone +ok 2325 - escapeForDisplay escapes bidi formatting and leaves zero-width formatting alone + --- + duration_ms: 0.320236 + type: 'test' + ... +# Subtest: escapeForDisplay never truncates, never drops, and passes clean text through +ok 2326 - escapeForDisplay never truncates, never drops, and passes clean text through + --- + duration_ms: 0.172743 + type: 'test' + ... +# Subtest: control flags: defaults when none given +ok 2327 - control flags: defaults when none given + --- + duration_ms: 2.478417 + type: 'test' + ... +# Subtest: control flags: strip render/transport flags, keep the verb tail in rest +ok 2328 - control flags: strip render/transport flags, keep the verb tail in rest + --- + duration_ms: 0.292965 + type: 'test' + ... +# Subtest: control flags: bare --remote selects the default target (empty sentinel) +ok 2329 - control flags: bare --remote selects the default target (empty sentinel) + --- + duration_ms: 0.125852 + type: 'test' + ... +# Subtest: control flags: --refresh sets refreshExplicit (for the --remote conflict check) +ok 2330 - control flags: --refresh sets refreshExplicit (for the --remote conflict check) + --- + duration_ms: 0.115245 + type: 'test' + ... +# Subtest: control flags: --format and --refresh validate their values +ok 2331 - control flags: --format and --refresh validate their values + --- + duration_ms: 0.199954 + type: 'test' + ... +# Subtest: codec: greedy positional joins all remaining tokens (SQL) +ok 2332 - codec: greedy positional joins all remaining tokens (SQL) + --- + duration_ms: 0.313016 + type: 'test' + ... +# Subtest: codec: positional + typed flags, kebab→snake, array split, defaults +ok 2333 - codec: positional + typed flags, kebab→snake, array split, defaults + --- + duration_ms: 0.351334 + type: 'test' + ... +# Subtest: codec: integer minimum, enum, unknown flag, extra positional +ok 2334 - codec: integer minimum, enum, unknown flag, extra positional + --- + duration_ms: 0.217861 + type: 'test' + ... +# Subtest: codec: required missing is reported +ok 2335 - codec: required missing is reported + --- + duration_ms: 0.315569 + type: 'test' + ... +# Subtest: codec: --flag=value inline form +ok 2336 - codec: --flag=value inline form + --- + duration_ms: 0.364294 + type: 'test' + ... +# Subtest: toJsonSchema strips CLI-only greedy/positional hints +ok 2337 - toJsonSchema strips CLI-only greedy/positional hints + --- + duration_ms: 0.219964 + type: 'test' + ... +# Subtest: usageForVerb renders positionals then flags +ok 2338 - usageForVerb renders positionals then flags + --- + duration_ms: 0.232473 + type: 'test' + ... +# Subtest: validateToolArguments coerces, applies defaults, enforces required + unknown +ok 2339 - validateToolArguments coerces, applies defaults, enforces required + unknown + --- + duration_ms: 0.249149 + type: 'test' + ... +# Subtest: register projects a CLI command into the command registry +ok 2340 - register projects a CLI command into the command registry + --- + duration_ms: 0.915801 + type: 'test' + ... +# Subtest: getByTool and get resolve the same verb; list is sorted +ok 2341 - getByTool and get resolve the same verb; list is sorted + --- + duration_ms: 7.363672 + type: 'test' + ... +# Subtest: duplicate verb name and duplicate tool name are both rejected +ok 2342 - duplicate verb name and duplicate tool name are both rejected + --- + duration_ms: 0.482313 + type: 'test' + ... +# Subtest: projection is idempotent when a command of that name already exists +ok 2343 - projection is idempotent when a command of that name already exists + --- + duration_ms: 0.21724 + type: 'test' + ... +# Subtest: exposure and auth-class default to cli+mcp / read +ok 2344 - exposure and auth-class default to cli+mcp / read + --- + duration_ms: 0.241597 + type: 'test' + ... +# Subtest: validation rejects malformed verbs +ok 2345 - validation rejects malformed verbs + --- + duration_ms: 0.17124 + type: 'test' + ... +# Subtest: --remote runs the remote tool and renders with the same render path +ok 2346 - --remote runs the remote tool and renders with the same render path + --- + duration_ms: 7.366857 + type: 'test' + ... +# Subtest: server-cap truncation is surfaced as its own stderr line +ok 2347 - server-cap truncation is surfaced as its own stderr line + --- + duration_ms: 0.844393 + type: 'test' + ... +# Subtest: bare --remote routes to the shipped default target (central server) +ok 2348 - bare --remote routes to the shipped default target (central server) + --- + duration_ms: 0.941782 + type: 'test' + ... +# Subtest: --remote with --refresh is a hard error (server owns its freshness) +ok 2349 - --remote with --refresh is a hard error (server owns its freshness) + --- + duration_ms: 0.331894 + type: 'test' + ... +# Subtest: an unknown remote target is rejected before any network call +ok 2350 - an unknown remote target is rejected before any network call + --- + duration_ms: 0.75699 + type: 'test' + ... +# Subtest: a missing token errors with login guidance +ok 2351 - a missing token errors with login guidance + --- + duration_ms: 5.235147 + type: 'test' + ... +# Subtest: a remote isError result maps to a nonzero exit with the message +ok 2352 - a remote isError result maps to a nonzero exit with the message + --- + duration_ms: 0.760366 + type: 'test' + ... +# Subtest: the finale reports asset counts per client, never a line per copy +ok 2353 - the finale reports asset counts per client, never a line per copy + --- + duration_ms: 81.514535 + type: 'test' + ... +# Subtest: picking claude-desktop records a not-applicable attach, not a failure +ok 2354 - picking claude-desktop records a not-applicable attach, not a failure + --- + duration_ms: 36.489921 + type: 'test' + ... +# Subtest: a registered adapter that throws still reports a real attach failure +ok 2355 - a registered adapter that throws still reports a real attach failure + --- + duration_ms: 26.13952 + type: 'test' + ... +# Subtest: defaultBackfillConsentPromptFactory, imported directly, asks the exact finale copy for a sample question +ok 2356 - defaultBackfillConsentPromptFactory, imported directly, asks the exact finale copy for a sample question + --- + duration_ms: 2.26347 + type: 'test' + ... +# Subtest: defaultBackfillConsentPromptFactory renders the multi-provider title identically and honors an explicit no +ok 2357 - defaultBackfillConsentPromptFactory renders the multi-provider title identically and honors an explicit no + --- + duration_ms: 0.409513 + type: 'test' + ... +# Subtest: onboarding with claude selected runs the backfill step and records stats +ok 2358 - onboarding with claude selected runs the backfill step and records stats + --- + duration_ms: 46.467706 + type: 'test' + ... +# Subtest: --dry-run onboarding includes the backfill plan but writes nothing +ok 2359 - --dry-run onboarding includes the backfill plan but writes nothing + --- + duration_ms: 20.558788 + type: 'test' + ... +# Subtest: --yes mode runs bounded backfill automatically without a consent prompt +ok 2360 - --yes mode runs bounded backfill automatically without a consent prompt + --- + duration_ms: 18.655295 + type: 'test' + ... +# Subtest: --no-daemon still backfills - it is a local file import +ok 2361 - --no-daemon still backfills - it is a local file import + --- + duration_ms: 19.23798 + type: 'test' + ... +# Subtest: interactive onboarding defaults backfill to enabled (consent yes runs it) +ok 2362 - interactive onboarding defaults backfill to enabled (consent yes runs it) + --- + duration_ms: 19.279352 + type: 'test' + ... +# Subtest: interactive onboarding lets the user decline backfill +ok 2363 - interactive onboarding lets the user decline backfill + --- + duration_ms: 26.138879 + type: 'test' + ... +# Subtest: interactive onboarding maps cancelled backfill consent to the cancel exit path +ok 2364 - interactive onboarding maps cancelled backfill consent to the cancel exit path + --- + duration_ms: 20.980849 + type: 'test' + ... +# Subtest: picked clients without a registered backfill provider are skipped +ok 2365 - picked clients without a registered backfill provider are skipped + --- + duration_ms: 12.372395 + type: 'test' + ... +# Subtest: a throwing backfill runner is caught and recorded as failed +ok 2366 - a throwing backfill runner is caught and recorded as failed + --- + duration_ms: 16.879806 + type: 'test' + ... +# Subtest: the finale runs no backfill when no backfill runner is injected +ok 2367 - the finale runs no backfill when no backfill runner is injected + --- + duration_ms: 16.456172 + type: 'test' + ... +# Subtest: onboarding with codex selected runs the backfill step and records stats +ok 2368 - onboarding with codex selected runs the backfill step and records stats + --- + duration_ms: 14.736438 + type: 'test' + ... +# Subtest: onboarding with both claude and codex selected runs both providers +ok 2369 - onboarding with both claude and codex selected runs both providers + --- + duration_ms: 18.348487 + type: 'test' + ... +# Subtest: interactive onboarding prompts codex backfill consent and runs it on yes +ok 2370 - interactive onboarding prompts codex backfill consent and runs it on yes + --- + duration_ms: 18.241035 + type: 'test' + ... +# Subtest: a failing provider does not abort the other selected providers +ok 2371 - a failing provider does not abort the other selected providers + --- + duration_ms: 14.998386 + type: 'test' + ... +# Subtest: a sweep-backed provider is disclosed and runs even when consent is declined +ok 2372 - a sweep-backed provider is disclosed and runs even when consent is declined + --- + duration_ms: 31.070174 + type: 'test' + ... +# Subtest: an openclaw-only pick asks no backfill question but still runs the first import +ok 2373 - an openclaw-only pick asks no backfill question but still runs the first import + --- + duration_ms: 17.868078 + type: 'test' + ... +# Subtest: cancelling consent skips sweep-backed providers too +ok 2374 - cancelling consent skips sweep-backed providers too + --- + duration_ms: 14.46121 + type: 'test' + ... +# Subtest: picker pre-checks detected sources, labels them, and defaults export to local-parquet +ok 2375 - picker pre-checks detected sources, labels them, and defaults export to local-parquet + --- + duration_ms: 47.446023 + type: 'test' + ... +# Subtest: nothing detected → no source pre-checked, export still defaults to local-parquet +ok 2376 - nothing detected → no source pre-checked, export still defaults to local-parquet + --- + duration_ms: 20.559589 + type: 'test' + ... +# Subtest: non-interactive picks skip detection entirely +ok 2377 - non-interactive picks skip detection entirely + --- + duration_ms: 13.414809 + type: 'test' + ... +# Subtest: picker prompt prints context under source options and defaults export to local-parquet +ok 2378 - picker prompt prints context under source options and defaults export to local-parquet + --- + duration_ms: 48.14135 + type: 'test' + ... +# Subtest: enterKeepsChecked: the fallback renders the checked state and a bare enter keeps it +ok 2379 - enterKeepsChecked: the fallback renders the checked state and a bare enter keeps it + --- + duration_ms: 0.81592 + type: 'test' + ... +# Subtest: enterKeepsChecked: typed indices still replace the checked set +ok 2380 - enterKeepsChecked: typed indices still replace the checked set + --- + duration_ms: 0.317753 + type: 'test' + ... +# Subtest: enterKeepsChecked: an answer naming no row re-asks once, and a correction in the same chunk still wins +ok 2381 - enterKeepsChecked: an answer naming no row re-asks once, and a correction in the same chunk still wins + --- + duration_ms: 1.419347 + type: 'test' + ... +# Subtest: enterKeepsChecked: endless invalid input stops after one re-ask instead of looping +ok 2382 - enterKeepsChecked: endless invalid input stops after one re-ask instead of looping + --- + duration_ms: 0.511868 + type: 'test' + ... +# Subtest: enterKeepsChecked: an invalid answer then EOF resolves with the checked defaults instead of hanging +ok 2383 - enterKeepsChecked: an invalid answer then EOF resolves with the checked defaults instead of hanging + --- + duration_ms: 0.520832 + type: 'test' + ... +# Subtest: EOF with no answer at all settles rather than hanging, on both paths +ok 2384 - EOF with no answer at all settles rather than hanging, on both paths + --- + duration_ms: 0.94752 + type: 'test' + ... +# Subtest: "none" is the explicit empty selection on both paths +ok 2385 - "none" is the explicit empty selection on both paths + --- + duration_ms: 0.916573 + type: 'test' + ... +# Subtest: enterKeepsChecked: a partially valid answer wins without a re-ask +ok 2386 - enterKeepsChecked: a partially valid answer wins without a re-ask + --- + duration_ms: 0.517437 + type: 'test' + ... +# Subtest: without enterKeepsChecked an answer naming no row still selects nothing, asked exactly once +ok 2387 - without enterKeepsChecked an answer naming no row still selects nothing, asked exactly once + --- + duration_ms: 0.504467 + type: 'test' + ... +# Subtest: back and all survive on both paths +ok 2388 - back and all survive on both paths + --- + duration_ms: 1.048353 + type: 'test' + ... +# Subtest: enterKeepsChecked: a spent re-ask budget keeps the checked rows and says which +ok 2389 - enterKeepsChecked: a spent re-ask budget keeps the checked rows and says which + --- + duration_ms: 0.354288 + type: 'test' + ... +# Subtest: enterKeepsChecked: a spent budget with nothing checked says the selection is empty +ok 2390 - enterKeepsChecked: a spent budget with nothing checked says the selection is empty + --- + duration_ms: 0.350843 + type: 'test' + ... +# Subtest: without enterKeepsChecked a closed stdin cancels instead of selecting nothing +ok 2391 - without enterKeepsChecked a closed stdin cancels instead of selecting nothing + --- + duration_ms: 0.242389 + type: 'test' + ... +# Subtest: a dropped terminal at the source picker cancels the run instead of installing with no sources +ok 2392 - a dropped terminal at the source picker cancels the run instead of installing with no sources + --- + duration_ms: 12.100482 + type: 'test' + ... +# Subtest: every later ask on a spent stdin settles instead of hanging +ok 2393 - every later ask on a spent stdin settles instead of hanging + --- + duration_ms: 0.570557 + type: 'test' + ... +# Subtest: without enterKeepsChecked a bare enter still selects nothing and no state is rendered +ok 2394 - without enterKeepsChecked a bare enter still selects nothing and no state is rendered + --- + duration_ms: 0.238392 + type: 'test' + ... +# Subtest: runPickerWalkthrough drives the TUI multiselect end-to-end when stdin+stdout are TTYs +ok 2395 - runPickerWalkthrough drives the TUI multiselect end-to-end when stdin+stdout are TTYs + --- + duration_ms: 43.914236 + type: 'test' + ... +# Subtest: runPickerWalkthrough returns a deterministic cancel exit code when the user cancels at the source prompt +ok 2396 - runPickerWalkthrough returns a deterministic cancel exit code when the user cancels at the source prompt + --- + duration_ms: 26.532577 + type: 'test' + ... +# Subtest: runPickerWalkthrough falls back to the legacy numbered prompt under HYP_NO_TUI=1 +ok 2397 - runPickerWalkthrough falls back to the legacy numbered prompt under HYP_NO_TUI=1 + --- + duration_ms: 21.996172 + type: 'test' + ... +# Subtest: shouldUseTui returns true when stdin and stdout are TTYs and HYP_NO_TUI unset +ok 2398 - shouldUseTui returns true when stdin and stdout are TTYs and HYP_NO_TUI unset + --- + duration_ms: 1.12506 + type: 'test' + ... +# Subtest: shouldUseTui returns false when HYP_NO_TUI=1 even with both TTYs +ok 2399 - shouldUseTui returns false when HYP_NO_TUI=1 even with both TTYs + --- + duration_ms: 0.261708 + type: 'test' + ... +# Subtest: shouldUseTui returns false when stdin is non-TTY +ok 2400 - shouldUseTui returns false when stdin is non-TTY + --- + duration_ms: 0.196198 + type: 'test' + ... +# Subtest: shouldUseTui returns false when stdout is non-TTY +ok 2401 - shouldUseTui returns false when stdout is non-TTY + --- + duration_ms: 0.141735 + type: 'test' + ... +# Subtest: shouldUseTui returns false when stdout is a duck-typed write sink (no isTTY) +ok 2402 - shouldUseTui returns false when stdout is a duck-typed write sink (no isTTY) + --- + duration_ms: 0.191501 + type: 'test' + ... +# Subtest: shouldUseTui falls back to process.stdin when opts.stdin is omitted +ok 2403 - shouldUseTui falls back to process.stdin when opts.stdin is omitted + --- + duration_ms: 0.200364 + type: 'test' + ... +# Subtest: shouldUseTui treats HYP_NO_TUI=0 as not-set (only the literal "1" disables) +ok 2404 - shouldUseTui treats HYP_NO_TUI=0 as not-set (only the literal "1" disables) + --- + duration_ms: 0.201766 + type: 'test' + ... +# Subtest: shouldUseTui honors opts.env over process.env when both are set +ok 2405 - shouldUseTui honors opts.env over process.env when both are set + --- + duration_ms: 0.148846 + type: 'test' + ... +# Subtest: shouldUseTui ignores process.env.HYP_NO_TUI when opts.env is supplied without it +ok 2406 - shouldUseTui ignores process.env.HYP_NO_TUI when opts.env is supplied without it + --- + duration_ms: 0.356773 + type: 'test' + ... +# Subtest: isTty rejects undefined, null, and primitives +ok 2407 - isTty rejects undefined, null, and primitives + --- + duration_ms: 0.346887 + type: 'test' + ... +# Subtest: isTty accepts only objects with isTTY === true (not truthy) +ok 2408 - isTty accepts only objects with isTTY === true (not truthy) + --- + duration_ms: 0.157149 + type: 'test' + ... +# Subtest: the finale names a still-attached client the new config no longer collects +ok 2409 - the finale names a still-attached client the new config no longer collects + --- + duration_ms: 63.258226 + type: 'test' + ... +# Subtest: a picked client that stays configured draws no stranded-attach warning +ok 2410 - a picked client that stays configured draws no stranded-attach warning + --- + duration_ms: 21.878934 + type: 'test' + ... +# Subtest: an unattached client the picker skipped is not warned about +ok 2411 - an unattached client the picker skipped is not warned about + --- + duration_ms: 20.523544 + type: 'test' + ... +# Subtest: a client the central layer names is not stranded by an unpicking run +ok 2412 - a client the central layer names is not stranded by an unpicking run + --- + duration_ms: 19.528211 + type: 'test' + ... +# Subtest: a plugin entry left in the config with enabled false does not count as configured +ok 2413 - a plugin entry left in the config with enabled false does not count as configured + --- + duration_ms: 4.788208 + type: 'test' + ... +# Subtest: registerExchangeProjector accepts a complete projector record +ok 2414 - registerExchangeProjector accepts a complete projector record + --- + duration_ms: 1.727916 + type: 'test' + ... +# Subtest: registerExchangeProjector assigns _seq in registration order +ok 2415 - registerExchangeProjector assigns _seq in registration order + --- + duration_ms: 0.793607 + type: 'test' + ... +# Subtest: registerExchangeProjector rejects missing or non-string name +ok 2416 - registerExchangeProjector rejects missing or non-string name + --- + duration_ms: 0.40158 + type: 'test' + ... +# Subtest: registerExchangeProjector rejects missing match() +ok 2417 - registerExchangeProjector rejects missing match() + --- + duration_ms: 0.183299 + type: 'test' + ... +# Subtest: registerExchangeProjector rejects missing project() +ok 2418 - registerExchangeProjector rejects missing project() + --- + duration_ms: 0.497947 + type: 'test' + ... +# Subtest: registerExchangeProjector preserves a missing priority as undefined +ok 2419 - registerExchangeProjector preserves a missing priority as undefined + --- + duration_ms: 0.135165 + type: 'test' + ... +# Subtest: registerUpstreamPreset stores presets by name and rejects invalid records +ok 2420 - registerUpstreamPreset stores presets by name and rejects invalid records + --- + duration_ms: 0.27668 + type: 'test' + ... +# Subtest: registerUpstreamPreset replaces an existing entry with the same name +ok 2421 - registerUpstreamPreset replaces an existing entry with the same name + --- + duration_ms: 0.285514 + type: 'test' + ... +# Subtest: registerClient validates name, defaultUpstream, and attach +ok 2422 - registerClient validates name, defaultUpstream, and attach + --- + duration_ms: 0.438597 + type: 'test' + ... +# Subtest: getClient and listClients expose the registered clients +ok 2423 - getClient and listClients expose the registered clients + --- + duration_ms: 0.405877 + type: 'test' + ... +# Subtest: localEndpoint throws before the source starts +ok 2424 - localEndpoint throws before the source starts + --- + duration_ms: 0.202668 + type: 'test' + ... +# Subtest: localEndpoint returns the bound host:port once the source has started +ok 2425 - localEndpoint returns the bound host:port once the source has started + --- + duration_ms: 0.121524 + type: 'test' + ... +# Subtest: localEndpoint appends pathPrefix, normalizing a missing leading slash +ok 2426 - localEndpoint appends pathPrefix, normalizing a missing leading slash + --- + duration_ms: 0.092951 + type: 'test' + ... +# Subtest: localEndpoint brackets IPv6 hosts so URL parsers do not choke +ok 2427 - localEndpoint brackets IPv6 hosts so URL parsers do not choke + --- + duration_ms: 0.082806 + type: 'test' + ... +# Subtest: materializer parity with live projector: native message ids +ok 2428 - materializer parity with live projector: native message ids + --- + duration_ms: 7.18476 + type: 'test' + ... +# Subtest: a per-message provider overrides the exchange provider row by row +ok 2429 - a per-message provider overrides the exchange provider row by row + --- + duration_ms: 0.62516 + type: 'test' + ... +# Subtest: materializer parity with live projector: fallback identity and chain +ok 2430 - materializer parity with live projector: fallback identity and chain + --- + duration_ms: 1.273556 + type: 'test' + ... +# Subtest: materializer ignores malformed payloads and yields no rows +ok 2431 - materializer ignores malformed payloads and yields no rows + --- + duration_ms: 0.16462 + type: 'test' + ... +# Subtest: materializer stamps hashed source-path provenance (raw path not stored) +ok 2432 - materializer stamps hashed source-path provenance (raw path not stored) + --- + duration_ms: 0.722979 + type: 'test' + ... +# Subtest: materialized rows are stripped to the gateway schema columns +ok 2433 - materialized rows are stripped to the gateway schema columns + --- + duration_ms: 0.5002 + type: 'test' + ... +# Subtest: backfill dedupe: a clean rerun writes zero new rows +ok 2434 - backfill dedupe: a clean rerun writes zero new rows + --- + duration_ms: 0.775709 + type: 'test' + ... +# Subtest: backfill dedupe: partial prior write only backfills the missing parts +ok 2435 - backfill dedupe: partial prior write only backfills the missing parts + --- + duration_ms: 0.35511 + type: 'test' + ... +# Subtest: backfill dedupe: matches legacy committed rows that predate part_id via message_id + part_index +ok 2436 - backfill dedupe: matches legacy committed rows that predate part_id via message_id + part_index + --- + duration_ms: 0.378365 + type: 'test' + ... +# Subtest: backfill dedupe: a re-yielded item within the same run is skipped without re-committing +ok 2437 - backfill dedupe: a re-yielded item within the same run is skipped without re-committing + --- + duration_ms: 0.558479 + type: 'test' + ... +# Subtest: backfill dedupe: a storage stub without the read surface skips dedupe entirely +ok 2438 - backfill dedupe: a storage stub without the read surface skips dedupe entirely + --- + duration_ms: 0.295309 + type: 'test' + ... +# Subtest: backfill dedupe: an unreadable partition degrades to no dedupe rather than dropping rows +ok 2439 - backfill dedupe: an unreadable partition degrades to no dedupe rather than dropping rows + --- + duration_ms: 0.288468 + type: 'test' + ... +# Subtest: backfill dedupe: rows pending in the spool are not re-materialized +ok 2440 - backfill dedupe: rows pending in the spool are not re-materialized + --- + duration_ms: 0.301008 + type: 'test' + ... +# Subtest: backfill dedupe: only the spool-overlapping parts are skipped +ok 2441 - backfill dedupe: only the spool-overlapping parts are skipped + --- + duration_ms: 0.202538 + type: 'test' + ... +# Subtest: backfill dedupe: spool dedupe also matches legacy rows via message_id + part_index +ok 2442 - backfill dedupe: spool dedupe also matches legacy rows via message_id + part_index + --- + duration_ms: 0.20384 + type: 'test' + ... +# Subtest: backfill dedupe: committed and spooled part_ids are unioned into one seen-set +ok 2443 - backfill dedupe: committed and spooled part_ids are unioned into one seen-set + --- + duration_ms: 0.203359 + type: 'test' + ... +# Subtest: backfill dedupe: an unreadable spool degrades to committed-only dedupe +ok 2444 - backfill dedupe: an unreadable spool degrades to committed-only dedupe + --- + duration_ms: 0.243871 + type: 'test' + ... +# Subtest: backfill dedupe: a storage stub without readSpooledRows still dedupes against committed +ok 2445 - backfill dedupe: a storage stub without readSpooledRows still dedupes against committed + --- + duration_ms: 0.892998 + type: 'test' + ... +# Subtest: string path: a stringified JSON array of input_image keeps the marker and drops the pixels +ok 2446 - string path: a stringified JSON array of input_image keeps the marker and drops the pixels + --- + duration_ms: 2.143758 + type: 'test' + ... +# Subtest: tool_result string branch: the payload is stripped there too +ok 2447 - tool_result string branch: the payload is stripped there too + --- + duration_ms: 0.413388 + type: 'test' + ... +# Subtest: a data URI embedded mid-prose loses only the payload, not the prose +ok 2448 - a data URI embedded mid-prose loses only the payload, not the prose + --- + duration_ms: 0.244251 + type: 'test' + ... +# Subtest: multiple data URIs in one string are all stripped +ok 2449 - multiple data URIs in one string are all stripped + --- + duration_ms: 0.23639 + type: 'test' + ... +# Subtest: a non-image mime type is stripped as well +ok 2450 - a non-image mime type is stripped as well + --- + duration_ms: 0.326746 + type: 'test' + ... +# Subtest: mime case and mime parameters do not defeat the strip +ok 2451 - mime case and mime parameters do not defeat the strip + --- + duration_ms: 0.384595 + type: 'test' + ... +# Subtest: a base64url payload (containing - and _) is fully stripped, no bare tail survives +ok 2452 - a base64url payload (containing - and _) is fully stripped, no bare tail survives + --- + duration_ms: 0.533932 + type: 'test' + ... +# Subtest: stripping an already-stripped value is a no-op +ok 2453 - stripping an already-stripped value is a no-op + --- + duration_ms: 0.266686 + type: 'test' + ... +# Subtest: content with no data URI passes through byte-identical +ok 2454 - content with no data URI passes through byte-identical + --- + duration_ms: 0.571749 + type: 'test' + ... +# Subtest: the prefix class cannot splice a `data:` to an unrelated `;base64,` +ok 2455 - the prefix class cannot splice a `data:` to an unrelated `;base64,` + --- + duration_ms: 0.563917 + type: 'test' + ... +# Subtest: the array path still drops image blocks entirely and keeps sibling text +ok 2456 - the array path still drops image blocks entirely and keeps sibling text + --- + duration_ms: 0.243661 + type: 'test' + ... +# Subtest: an image-only array block still contributes no content_text +ok 2457 - an image-only array block still contributes no content_text + --- + duration_ms: 0.208977 + type: 'test' + ... +# Subtest: thinking and error blocks are covered by the same pass +ok 2458 - thinking and error blocks are covered by the same pass + --- + duration_ms: 0.277732 + type: 'test' + ... +# Subtest: the marker names the stripped mediatype, not a fixed `image` +ok 2459 - the marker names the stripped mediatype, not a fixed `image` + --- + duration_ms: 0.196208 + type: 'test' + ... +# Subtest: a `+`-bearing mediatype reaches the marker intact +ok 2460 - a `+`-bearing mediatype reaches the marker intact + --- + duration_ms: 0.182487 + type: 'test' + ... +# Subtest: an empty mediatype falls back to application/octet-stream +ok 2461 - an empty mediatype falls back to application/octet-stream + --- + duration_ms: 0.186894 + type: 'test' + ... +# Subtest: mediatype case is recorded as it arrived on the wire +ok 2462 - mediatype case is recorded as it arrived on the wire + --- + duration_ms: 0.178311 + type: 'test' + ... +# Subtest: mediatype parameters are carried into the marker +ok 2463 - mediatype parameters are carried into the marker + --- + duration_ms: 0.176117 + type: 'test' + ... +# Subtest: several mediatypes in one string each keep their own +ok 2464 - several mediatypes in one string each keep their own + --- + duration_ms: 0.246094 + type: 'test' + ... +# Subtest: the marker stays idempotent for every mediatype it can now emit +ok 2465 - the marker stays idempotent for every mediatype it can now emit + --- + duration_ms: 1.883593 + type: 'test' + ... +# Subtest: stripping bounds the row: a multi-megabyte payload lands as a short value +ok 2466 - stripping bounds the row: a multi-megabyte payload lands as a short value + --- + duration_ms: 99.81561 + type: 'test' + ... +# Subtest: the echoed mediatype cannot grow the marker past the regex cap +ok 2467 - the echoed mediatype cannot grow the marker past the regex cap + --- + duration_ms: 0.245153 + type: 'test' + ... +# Subtest: POST adds a session id and reports ignored:true with the running total +ok 2468 - POST adds a session id and reports ignored:true with the running total + --- + duration_ms: 26.52795 + type: 'test' + ... +# Subtest: DELETE removes a session id and reports ignored:false +ok 2469 - DELETE removes a session id and reports ignored:false + --- + duration_ms: 7.870352 + type: 'test' + ... +# Subtest: both verbs are idempotent and .total tracks the set across a sequence +ok 2470 - both verbs are idempotent and .total tracks the set across a sequence + --- + duration_ms: 24.924262 + type: 'test' + ... +# Subtest: session_id whitespace only gates non-emptiness; the STORED token is the raw value verbatim (R5) +ok 2471 - session_id whitespace only gates non-emptiness; the STORED token is the raw value verbatim (R5) + --- + duration_ms: 4.483564 + type: 'test' + ... +# Subtest: a whitespace-padded session_id round-trips byte-identical to what an adapter would resolve and look up (R5) +ok 2472 - a whitespace-padded session_id round-trips byte-identical to what an adapter would resolve and look up (R5) + --- + duration_ms: 6.807487 + type: 'test' + ... +# Subtest: 400 when session_id is missing, empty, or not a string +ok 2473 - 400 when session_id is missing, empty, or not a string + --- + duration_ms: 7.598359 + type: 'test' + ... +# Subtest: 400 on malformed JSON +ok 2474 - 400 on malformed JSON + --- + duration_ms: 2.878425 + type: 'test' + ... +# Subtest: 405 on an unsupported method for the ignore route +ok 2475 - 405 on an unsupported method for the ignore route + --- + duration_ms: 6.209548 + type: 'test' + ... +# Subtest: 404 on an unknown /_hypaware/* control path +ok 2476 - 404 on an unknown /_hypaware/* control path + --- + duration_ms: 2.854909 + type: 'test' + ... +# Subtest: 413 when the request body exceeds the size bound +ok 2477 - 413 when the request body exceeds the size bound + --- + duration_ms: 2.860257 + type: 'test' + ... +# Subtest: a control request emits a structured ignore log carrying the running total +ok 2478 - a control request emits a structured ignore log carrying the running total + --- + duration_ms: 2.048363 + type: 'test' + ... +# Subtest: isControlPath recognizes the reserved prefix at segment boundaries only +ok 2479 - isControlPath recognizes the reserved prefix at segment boundaries only + --- + duration_ms: 0.109015 + type: 'test' + ... +# Subtest: records one entry per entrypoint, counting rows and stamping last-seen +ok 2480 - records one entry per entrypoint, counting rows and stamping last-seen + --- + duration_ms: 1.656027 + type: 'test' + ... +# Subtest: the snapshot is most-recently-seen first, so a stale surface sinks +ok 2481 - the snapshot is most-recently-seen first, so a stale surface sinks + --- + duration_ms: 0.257502 + type: 'test' + ... +# Subtest: rows with no entrypoint are ignored rather than bucketed under a placeholder +ok 2482 - rows with no entrypoint are ignored rather than bucketed under a placeholder + --- + duration_ms: 0.878545 + type: 'test' + ... +# Subtest: a client-supplied entrypoint cannot grow the map without bound +ok 2483 - a client-supplied entrypoint cannot grow the map without bound + --- + duration_ms: 0.310271 + type: 'test' + ... +# Subtest: seeing an old entrypoint again rescues it from eviction +ok 2484 - seeing an old entrypoint again rescues it from eviction + --- + duration_ms: 0.181205 + type: 'test' + ... +# Subtest: an empty or malformed batch is a no-op, not a throw +ok 2485 - an empty or malformed batch is a no-op, not a throw + --- + duration_ms: 0.091859 + type: 'test' + ... +# Subtest: a control-character entrypoint cannot forge lines or move the cursor +ok 2486 - a control-character entrypoint cannot forge lines or move the cursor + --- + duration_ms: 0.234897 + type: 'test' + ... +# Subtest: an over-long entrypoint is clamped, so status.json stays small +ok 2487 - an over-long entrypoint is clamped, so status.json stays small + --- + duration_ms: 0.352186 + type: 'test' + ... +# Subtest: an entrypoint of nothing but control bytes is skipped, not stored blank +ok 2488 - an entrypoint of nothing but control bytes is skipped, not stored blank + --- + duration_ms: 0.318324 + type: 'test' + ... +# Subtest: the default cap is 32 distinct entrypoints +ok 2489 - the default cap is 32 distinct entrypoints + --- + duration_ms: 0.541042 + type: 'test' + ... +# Subtest: a bidi override cannot reorder the rendered status line +ok 2490 - a bidi override cannot reorder the rendered status line + --- + duration_ms: 0.23652 + type: 'test' + ... +# Subtest: invisible characters cannot dilute the eviction cap +ok 2491 - invisible characters cannot dilute the eviction cap + --- + duration_ms: 6.27691 + type: 'test' + ... +# Subtest: clamping an astral entrypoint leaves a well-formed string +ok 2492 - clamping an astral entrypoint leaves a well-formed string + --- + duration_ms: 0.171861 + type: 'test' + ... +# Subtest: the gateway+graph+connector chain activates in order and the connector registers its contract +ok 2493 - the gateway+graph+connector chain activates in order and the connector registers its contract + --- + duration_ms: 78.726777 + type: 'test' + ... +# Subtest: host Repo node converges with the GitHub Repo node +ok 2494 - host Repo node converges with the GitHub Repo node + --- + duration_ms: 1.77007 + type: 'test' + ... +# Subtest: host Commit node converges with the GitHub Commit node (full sha, any case) +ok 2495 - host Commit node converges with the GitHub Commit node (full sha, any case) + --- + duration_ms: 0.235728 + type: 'test' + ... +# Subtest: host File node converges with the GitHub File node via owner/repo:relpath +ok 2496 - host File node converges with the GitHub File node via owner/repo:relpath + --- + duration_ms: 0.464666 + type: 'test' + ... +# Subtest: host Commit -in-> Repo edge converges with the GitHub edge +ok 2497 - host Commit -in-> Repo edge converges with the GitHub edge + --- + duration_ms: 1.551088 + type: 'test' + ... +# Subtest: worktrees of one repo converge on a single File node +ok 2498 - worktrees of one repo converge on a single File node + --- + duration_ms: 0.366157 + type: 'test' + ... +# Subtest: contract carries its source/projector metadata +ok 2499 - contract carries its source/projector metadata + --- + duration_ms: 0.966139 + type: 'test' + ... +# Subtest: PROJECTOR_VERSION is 2 after the additive Program/invoked rules +ok 2500 - PROJECTOR_VERSION is 2 after the additive Program/invoked rules + --- + duration_ms: 0.13801 + type: 'test' + ... +# Subtest: Session rule builds a node keyed on session_id with pruned props +ok 2501 - Session rule builds a node keyed on session_id with pruned props + --- + duration_ms: 1.19726 + type: 'test' + ... +# Subtest: node rules skip rows missing their natural key +ok 2502 - node rules skip rows missing their natural key + --- + duration_ms: 0.202999 + type: 'test' + ... +# Subtest: Session rule with no optional fields builds null props +ok 2503 - Session rule with no optional fields builds null props + --- + duration_ms: 0.222488 + type: 'test' + ... +# Subtest: File rule resolves file_path from file-touching tools only +ok 2504 - File rule resolves file_path from file-touching tools only + --- + duration_ms: 0.293956 + type: 'test' + ... +# Subtest: File rule parses tool_args arriving as a JSON string, skipping malformed JSON +ok 2505 - File rule parses tool_args arriving as a JSON string, skipping malformed JSON + --- + duration_ms: 0.228928 + type: 'test' + ... +# Subtest: File rule falls back to notebook_path +ok 2506 - File rule falls back to notebook_path + --- + duration_ms: 0.139832 + type: 'test' + ... +# Subtest: touched edge wires Session and File node ids and skips partial rows +ok 2507 - touched edge wires Session and File node ids and skips partial rows + --- + duration_ms: 0.578749 + type: 'test' + ... +# Subtest: via and used_model edges skip rows missing either endpoint +ok 2508 - via and used_model edges skip rows missing either endpoint + --- + duration_ms: 0.455272 + type: 'test' + ... +# Subtest: toRow normalizes first_seen from Date and epoch-number timestamps +ok 2509 - toRow normalizes first_seen from Date and epoch-number timestamps + --- + duration_ms: 0.455092 + type: 'test' + ... +# Subtest: numeric natural keys are stringified +ok 2510 - numeric natural keys are stringified + --- + duration_ms: 0.136307 + type: 'test' + ... +# Subtest: the contract declares the aux rowFilter on attributes, and raw rules select it +ok 2511 - the contract declares the aux rowFilter on attributes, and raw rules select it + --- + duration_ms: 0.312495 + type: 'test' + ... +# Subtest: the aux rowFilter drops aux-tagged rows and keeps real ones +ok 2512 - the aux rowFilter drops aux-tagged rows and keeps real ones + --- + duration_ms: 0.145061 + type: 'test' + ... +# Subtest: non-aux rows pass through unchanged (attributes present but no aux_kind) +ok 2513 - non-aux rows pass through unchanged (attributes present but no aux_kind) + --- + duration_ms: 0.128535 + type: 'test' + ... +# Subtest: Repo node keys on owner/repo derived from the git remote +ok 2514 - Repo node keys on owner/repo derived from the git remote + --- + duration_ms: 0.301048 + type: 'test' + ... +# Subtest: Commit node keys on the full HEAD sha and rejects an abbreviated one +ok 2515 - Commit node keys on the full HEAD sha and rejects an abbreviated one + --- + duration_ms: 0.200034 + type: 'test' + ... +# Subtest: File node re-keys an in-repo path to owner/repo:relpath (relpath case preserved) +ok 2516 - File node re-keys an in-repo path to owner/repo:relpath (relpath case preserved) + --- + duration_ms: 0.236339 + type: 'test' + ... +# Subtest: File node falls back to the absolute path when it cannot be relativized +ok 2517 - File node falls back to the absolute path when it cannot be relativized + --- + duration_ms: 0.209479 + type: 'test' + ... +# Subtest: File node falls back (does not mint a bogus bridge key) for a path that escapes the root via `..` +ok 2518 - File node falls back (does not mint a bogus bridge key) for a path that escapes the root via `..` + --- + duration_ms: 0.176969 + type: 'test' + ... +# Subtest: File node still relativizes an in-repo `..` that stays inside the root +ok 2519 - File node still relativizes an in-repo `..` that stays inside the root + --- + duration_ms: 0.198452 + type: 'test' + ... +# Subtest: touched edge re-keys its File endpoint identically to the File node +ok 2520 - touched edge re-keys its File endpoint identically to the File node + --- + duration_ms: 0.173914 + type: 'test' + ... +# Subtest: Session -in-> Repo and Session -at-> Commit wire the bridge nodes +ok 2521 - Session -in-> Repo and Session -at-> Commit wire the bridge nodes + --- + duration_ms: 0.251031 + type: 'test' + ... +# Subtest: Commit -in-> Repo is the second `in` edge and converges with the GitHub edge +ok 2522 - Commit -in-> Repo is the second `in` edge and converges with the GitHub edge + --- + duration_ms: 0.169137 + type: 'test' + ... +# Subtest: Program/invoked rules select only tool_call rows from the two shell tools +ok 2523 - Program/invoked rules select only tool_call rows from the two shell tools + --- + duration_ms: 0.187766 + type: 'test' + ... +# Subtest: Program node keys on the validity-gated basename(argv[0]) of the first command +ok 2524 - Program node keys on the validity-gated basename(argv[0]) of the first command + --- + duration_ms: 0.567002 + type: 'test' + ... +# Subtest: Program node mints nothing when the facet cannot be cleanly bounded (fail-closed) +ok 2525 - Program node mints nothing when the facet cannot be cleanly bounded (fail-closed) + --- + duration_ms: 0.155537 + type: 'test' + ... +# Subtest: Program node parses tool_args arriving as a JSON string +ok 2526 - Program node parses tool_args arriving as a JSON string + --- + duration_ms: 0.198622 + type: 'test' + ... +# Subtest: invoked edge wires Session -> Program ids and carries no props +ok 2527 - invoked edge wires Session -> Program ids and carries no props + --- + duration_ms: 0.183689 + type: 'test' + ... +# Subtest: invoked edge skips rows missing the session or an un-gateable program +ok 2528 - invoked edge skips rows missing the session or an un-gateable program + --- + duration_ms: 0.123097 + type: 'test' + ... +# Subtest: Skill/ran rules declare the strict per-surface filters +ok 2529 - Skill/ran rules declare the strict per-surface filters + --- + duration_ms: 0.259665 + type: 'test' + ... +# Subtest: Skill node from the Skill tool call keys on tool_args.skill +ok 2530 - Skill node from the Skill tool call keys on tool_args.skill + --- + duration_ms: 0.198782 + type: 'test' + ... +# Subtest: Skill node from the marker keys on the base-directory basename +ok 2531 - Skill node from the marker keys on the base-directory basename + --- + duration_ms: 0.209248 + type: 'test' + ... +# Subtest: Skill node from a slash command keys on the de-slashed name +ok 2532 - Skill node from a slash command keys on the de-slashed name + --- + duration_ms: 0.178221 + type: 'test' + ... +# Subtest: Skill node from a Codex exec_command read keys on the .codex/skills//SKILL.md path +ok 2533 - Skill node from a Codex exec_command read keys on the .codex/skills//SKILL.md path + --- + duration_ms: 0.310031 + type: 'test' + ... +# Subtest: all four surfaces converge on one Skill node id (cross-surface identity) +ok 2534 - all four surfaces converge on one Skill node id (cross-surface identity) + --- + duration_ms: 0.22339 + type: 'test' + ... +# Subtest: ran edges wire Session -> Skill with exactly their own dispatch flag +ok 2535 - ran edges wire Session -> Skill with exactly their own dispatch flag + --- + duration_ms: 0.407849 + type: 'test' + ... +# Subtest: ran edges skip rows missing the session or an un-gateable skill +ok 2536 - ran edges skip rows missing the session or an un-gateable skill + --- + duration_ms: 0.136307 + type: 'test' + ... +# Subtest: false-positive matrix: near-miss signals mint nothing +ok 2537 - false-positive matrix: near-miss signals mint nothing + --- + duration_ms: 0.158601 + type: 'test' + ... +# Subtest: aux-tagged skill rows are excluded by the contract rowFilter +ok 2538 - aux-tagged skill rows are excluded by the contract rowFilter + --- + duration_ms: 0.106412 + type: 'test' + ... +# Subtest: commandStringFrom reads Bash.command and exec_command.cmd (fallback command) +ok 2539 - commandStringFrom reads Bash.command and exec_command.cmd (fallback command) + --- + duration_ms: 1.384093 + type: 'test' + ... +# Subtest: commandStringFrom parses tool_args arriving as a JSON string +ok 2540 - commandStringFrom parses tool_args arriving as a JSON string + --- + duration_ms: 0.198291 + type: 'test' + ... +# Subtest: commandStringFrom is null for non-shell tools, bad args, or a missing/non-string command +ok 2541 - commandStringFrom is null for non-shell tools, bad args, or a missing/non-string command + --- + duration_ms: 0.146814 + type: 'test' + ... +# Subtest: programFrom: bare command +ok 2542 - programFrom: bare command + --- + duration_ms: 0.414801 + type: 'test' + ... +# Subtest: programFrom: single token +ok 2543 - programFrom: single token + --- + duration_ms: 0.165401 + type: 'test' + ... +# Subtest: programFrom: absolute path is basenamed +ok 2544 - programFrom: absolute path is basenamed + --- + duration_ms: 0.09893 + type: 'test' + ... +# Subtest: programFrom: relative path is basenamed +ok 2545 - programFrom: relative path is basenamed + --- + duration_ms: 0.229209 + type: 'test' + ... +# Subtest: programFrom: uppercase is lowercased +ok 2546 - programFrom: uppercase is lowercased + --- + duration_ms: 0.085279 + type: 'test' + ... +# Subtest: programFrom: pathed + cased converge +ok 2547 - programFrom: pathed + cased converge + --- + duration_ms: 0.274537 + type: 'test' + ... +# Subtest: programFrom: pipe takes the head +ok 2548 - programFrom: pipe takes the head + --- + duration_ms: 0.317753 + type: 'test' + ... +# Subtest: programFrom: && takes the head +ok 2549 - programFrom: && takes the head + --- + duration_ms: 0.141194 + type: 'test' + ... +# Subtest: programFrom: || takes the head +ok 2550 - programFrom: || takes the head + --- + duration_ms: 0.091339 + type: 'test' + ... +# Subtest: programFrom: ; takes the head +ok 2551 - programFrom: ; takes the head + --- + duration_ms: 0.075955 + type: 'test' + ... +# Subtest: programFrom: newline takes the head +ok 2552 - programFrom: newline takes the head + --- + duration_ms: 0.064758 + type: 'test' + ... +# Subtest: programFrom: single & (background) does not cut +ok 2553 - programFrom: single & (background) does not cut + --- + duration_ms: 0.064007 + type: 'test' + ... +# Subtest: programFrom: quoted connector after argv[0] is head-safe +ok 2554 - programFrom: quoted connector after argv[0] is head-safe + --- + duration_ms: 0.059049 + type: 'test' + ... +# Subtest: programFrom: leading subshell paren stripped +ok 2555 - programFrom: leading subshell paren stripped + --- + duration_ms: 0.060201 + type: 'test' + ... +# Subtest: programFrom: paren with no space stripped +ok 2556 - programFrom: paren with no space stripped + --- + duration_ms: 0.059451 + type: 'test' + ... +# Subtest: programFrom: single env assignment skipped +ok 2557 - programFrom: single env assignment skipped + --- + duration_ms: 0.057687 + type: 'test' + ... +# Subtest: programFrom: multiple env assignments skipped +ok 2558 - programFrom: multiple env assignments skipped + --- + duration_ms: 1.055885 + type: 'test' + ... +# Subtest: programFrom: sudo unwrapped +ok 2559 - programFrom: sudo unwrapped + --- + duration_ms: 0.130869 + type: 'test' + ... +# Subtest: programFrom: sudo no-arg flag skipped +ok 2560 - programFrom: sudo no-arg flag skipped + --- + duration_ms: 0.238923 + type: 'test' + ... +# Subtest: programFrom: env wrapper + assignment skipped +ok 2561 - programFrom: env wrapper + assignment skipped + --- + duration_ms: 0.077368 + type: 'test' + ... +# Subtest: programFrom: nohup unwrapped +ok 2562 - programFrom: nohup unwrapped + --- + duration_ms: 0.063016 + type: 'test' + ... +# Subtest: programFrom: nice + numeric flag arg skipped +ok 2563 - programFrom: nice + numeric flag arg skipped + --- + duration_ms: 0.067152 + type: 'test' + ... +# Subtest: programFrom: time keyword unwrapped +ok 2564 - programFrom: time keyword unwrapped + --- + duration_ms: 0.051248 + type: 'test' + ... +# Subtest: programFrom: command builtin unwrapped +ok 2565 - programFrom: command builtin unwrapped + --- + duration_ms: 0.044598 + type: 'test' + ... +# Subtest: programFrom: stdbuf flags skipped +ok 2566 - programFrom: stdbuf flags skipped + --- + duration_ms: 0.049415 + type: 'test' + ... +# Subtest: programFrom: timeout numeric duration skipped +ok 2567 - programFrom: timeout numeric duration skipped + --- + duration_ms: 0.044998 + type: 'test' + ... +# Subtest: programFrom: timeout suffix duration skipped +ok 2568 - programFrom: timeout suffix duration skipped + --- + duration_ms: 0.044768 + type: 'test' + ... +# Subtest: programFrom: stacked wrappers unwrapped +ok 2569 - programFrom: stacked wrappers unwrapped + --- + duration_ms: 0.041904 + type: 'test' + ... +# Subtest: programFrom: wrapper of pathed program +ok 2570 - programFrom: wrapper of pathed program + --- + duration_ms: 0.03912 + type: 'test' + ... +# Subtest: programFrom: sudo -u with a separate-token value consumes the pair +ok 2571 - programFrom: sudo -u with a separate-token value consumes the pair + --- + duration_ms: 0.043045 + type: 'test' + ... +# Subtest: programFrom: env -C with a separate-token value consumes the pair +ok 2572 - programFrom: env -C with a separate-token value consumes the pair + --- + duration_ms: 0.06577 + type: 'test' + ... +# Subtest: programFrom: timeout --signal with a separate-token value consumes the pair, then the numeric duration +ok 2573 - programFrom: timeout --signal with a separate-token value consumes the pair, then the numeric duration + --- + duration_ms: 0.048153 + type: 'test' + ... +# Subtest: programFrom: stdbuf -o with a separate-token value consumes the pair +ok 2574 - programFrom: stdbuf -o with a separate-token value consumes the pair + --- + duration_ms: 0.05961 + type: 'test' + ... +# Subtest: programFrom: nice -n with a separate-token value consumes the pair +ok 2575 - programFrom: nice -n with a separate-token value consumes the pair + --- + duration_ms: 0.045479 + type: 'test' + ... +# Subtest: programFrom: sudo -u attached value +ok 2576 - programFrom: sudo -u attached value + --- + duration_ms: 0.040562 + type: 'test' + ... +# Subtest: programFrom: env --chdir= attached value +ok 2577 - programFrom: env --chdir= attached value + --- + duration_ms: 0.040822 + type: 'test' + ... +# Subtest: programFrom: sudo with an unrecognized flag+value shape mints nothing +ok 2578 - programFrom: sudo with an unrecognized flag+value shape mints nothing + --- + duration_ms: 0.048854 + type: 'test' + ... +# Subtest: programFrom: env with an unrecognized flag+value shape mints nothing +ok 2579 - programFrom: env with an unrecognized flag+value shape mints nothing + --- + duration_ms: 0.057878 + type: 'test' + ... +# Subtest: programFrom: sudo -- ends options, next token is argv[0] +ok 2580 - programFrom: sudo -- ends options, next token is argv[0] + --- + duration_ms: 0.055825 + type: 'test' + ... +# Subtest: programFrom: env -- ends options +ok 2581 - programFrom: env -- ends options + --- + duration_ms: 0.055334 + type: 'test' + ... +# Subtest: programFrom: bash -lc unwraps the inner command +ok 2582 - programFrom: bash -lc unwraps the inner command + --- + duration_ms: 0.103988 + type: 'test' + ... +# Subtest: programFrom: sh -c unwraps +ok 2583 - programFrom: sh -c unwraps + --- + duration_ms: 0.083957 + type: 'test' + ... +# Subtest: programFrom: zsh -c unwraps +ok 2584 - programFrom: zsh -c unwraps + --- + duration_ms: 0.061413 + type: 'test' + ... +# Subtest: programFrom: bash -c single-quoted inner +ok 2585 - programFrom: bash -c single-quoted inner + --- + duration_ms: 0.063056 + type: 'test' + ... +# Subtest: programFrom: inner command with its own connector takes head +ok 2586 - programFrom: inner command with its own connector takes head + --- + duration_ms: 0.066281 + type: 'test' + ... +# Subtest: programFrom: inner command with a pipe takes head +ok 2587 - programFrom: inner command with a pipe takes head + --- + duration_ms: 0.062955 + type: 'test' + ... +# Subtest: programFrom: bash script.sh (no -c) keeps bash +ok 2588 - programFrom: bash script.sh (no -c) keeps bash + --- + duration_ms: 0.055344 + type: 'test' + ... +# Subtest: programFrom: bash with no -c keeps bash +ok 2589 - programFrom: bash with no -c keeps bash + --- + duration_ms: 0.054983 + type: 'test' + ... +# Subtest: programFrom: nested bash -lc unwraps to depth 2 +ok 2590 - programFrom: nested bash -lc unwraps to depth 2 + --- + duration_ms: 0.062615 + type: 'test' + ... +# Subtest: programFrom: empty string mints nothing +ok 2591 - programFrom: empty string mints nothing + --- + duration_ms: 0.049304 + type: 'test' + ... +# Subtest: programFrom: whitespace mints nothing +ok 2592 - programFrom: whitespace mints nothing + --- + duration_ms: 0.050988 + type: 'test' + ... +# Subtest: programFrom: all-numeric token mints nothing +ok 2593 - programFrom: all-numeric token mints nothing + --- + duration_ms: 0.055003 + type: 'test' + ... +# Subtest: programFrom: token with a space char fails the gate +ok 2594 - programFrom: token with a space char fails the gate + --- + duration_ms: 0.054432 + type: 'test' + ... +# Subtest: programFrom: token with illegal char fails the gate +ok 2595 - programFrom: token with illegal char fails the gate + --- + duration_ms: 0.055384 + type: 'test' + ... +# Subtest: programFrom: leading connector mints nothing +ok 2596 - programFrom: leading connector mints nothing + --- + duration_ms: 0.058969 + type: 'test' + ... +# Subtest: programFrom rejects an over-long token (PROGRAM_RE cap) +ok 2597 - programFrom rejects an over-long token (PROGRAM_RE cap) + --- + duration_ms: 0.137649 + type: 'test' + ... +# Subtest: programFrom returns null for non-string / null input (from commandStringFrom) +ok 2598 - programFrom returns null for non-string / null input (from commandStringFrom) + --- + duration_ms: 0.107284 + type: 'test' + ... +# Subtest: the depth cap stops shell -c recursion at 2 levels (keeps the innermost shell) +ok 2599 - the depth cap stops shell -c recursion at 2 levels (keeps the innermost shell) + --- + duration_ms: 0.538378 + type: 'test' + ... +# Subtest: skillFromToolArgs reads tool_args.skill, parsed or as a JSON string +ok 2600 - skillFromToolArgs reads tool_args.skill, parsed or as a JSON string + --- + duration_ms: 0.173414 + type: 'test' + ... +# Subtest: skillFromToolArgs is null for missing/bad args or an un-gateable name +ok 2601 - skillFromToolArgs is null for missing/bad args or an un-gateable name + --- + duration_ms: 0.178651 + type: 'test' + ... +# Subtest: skillFromMarker takes the basename of the offset-0 base directory +ok 2602 - skillFromMarker takes the basename of the offset-0 base directory + --- + duration_ms: 0.199534 + type: 'test' + ... +# Subtest: skillFromMarker trims a trailing slash and unwraps a SKILL.md file path +ok 2603 - skillFromMarker trims a trailing slash and unwraps a SKILL.md file path + --- + duration_ms: 0.103357 + type: 'test' + ... +# Subtest: skillFromMarker rejects anything not anchored at offset 0 +ok 2604 - skillFromMarker rejects anything not anchored at offset 0 + --- + duration_ms: 0.093181 + type: 'test' + ... +# Subtest: skillFromMarker fails closed on an un-gateable basename +ok 2605 - skillFromMarker fails closed on an un-gateable basename + --- + duration_ms: 0.103086 + type: 'test' + ... +# Subtest: skillFromMarker resolves a base directory containing a space to the correct trailing basename +ok 2606 - skillFromMarker resolves a base directory containing a space to the correct trailing basename + --- + duration_ms: 0.091129 + type: 'test' + ... +# Subtest: skillFromMarker still mints nothing for a marker not on the first line (anchor not loosened) +ok 2607 - skillFromMarker still mints nothing for a marker not on the first line (anchor not loosened) + --- + duration_ms: 0.084198 + type: 'test' + ... +# Subtest: skillFromSlash reads an offset-0 command tag, stripping a leading / +ok 2608 - skillFromSlash reads an offset-0 command tag, stripping a leading / + --- + duration_ms: 0.175917 + type: 'test' + ... +# Subtest: skillFromSlash drops Claude Code built-in commands +ok 2609 - skillFromSlash drops Claude Code built-in commands + --- + duration_ms: 0.158882 + type: 'test' + ... +# Subtest: skillFromSlash rejects tags not anchored at offset 0 and malformed tags +ok 2610 - skillFromSlash rejects tags not anchored at offset 0 and malformed tags + --- + duration_ms: 0.094674 + type: 'test' + ... +# Subtest: skillFromCodexRead: sed of a user's home path (LLP 0075 example) +ok 2611 - skillFromCodexRead: sed of a user's home path (LLP 0075 example) + --- + duration_ms: 0.156247 + type: 'test' + ... +# Subtest: skillFromCodexRead: bare read +ok 2612 - skillFromCodexRead: bare read + --- + duration_ms: 0.102266 + type: 'test' + ... +# Subtest: skillFromCodexRead: ~-prefixed path +ok 2613 - skillFromCodexRead: ~-prefixed path + --- + duration_ms: 0.077047 + type: 'test' + ... +# Subtest: skillFromCodexRead: double-quoted path +ok 2614 - skillFromCodexRead: double-quoted path + --- + duration_ms: 0.068594 + type: 'test' + ... +# Subtest: skillFromCodexRead: single-quoted path +ok 2615 - skillFromCodexRead: single-quoted path + --- + duration_ms: 0.067732 + type: 'test' + ... +# Subtest: skillFromCodexRead: namespaced skill name kept +ok 2616 - skillFromCodexRead: namespaced skill name kept + --- + duration_ms: 0.080542 + type: 'test' + ... +# Subtest: skillFromCodexRead: head is a read tool +ok 2617 - skillFromCodexRead: head is a read tool + --- + duration_ms: 0.083386 + type: 'test' + ... +# Subtest: skillFromCodexRead: grep is a read tool +ok 2618 - skillFromCodexRead: grep is a read tool + --- + duration_ms: 0.077878 + type: 'test' + ... +# Subtest: skillFromCodexRead: Claude path (.claude, not .codex) mints nothing +ok 2619 - skillFromCodexRead: Claude path (.claude, not .codex) mints nothing + --- + duration_ms: 0.119501 + type: 'test' + ... +# Subtest: skillFromCodexRead: not reading SKILL.md itself mints nothing +ok 2620 - skillFromCodexRead: not reading SKILL.md itself mints nothing + --- + duration_ms: 0.077668 + type: 'test' + ... +# Subtest: skillFromCodexRead: a bare directory listing (no SKILL.md) mints nothing +ok 2621 - skillFromCodexRead: a bare directory listing (no SKILL.md) mints nothing + --- + duration_ms: 0.054933 + type: 'test' + ... +# Subtest: skillFromCodexRead: no .codex/skills path at all mints nothing +ok 2622 - skillFromCodexRead: no .codex/skills path at all mints nothing + --- + duration_ms: 0.050257 + type: 'test' + ... +# Subtest: skillFromCodexRead: name containing a space fails to close the match +ok 2623 - skillFromCodexRead: name containing a space fails to close the match + --- + duration_ms: 0.048323 + type: 'test' + ... +# Subtest: skillFromCodexRead: empty string mints nothing +ok 2624 - skillFromCodexRead: empty string mints nothing + --- + duration_ms: 0.076105 + type: 'test' + ... +# Subtest: skillFromCodexRead: echo (non-read command) naming the path mints nothing +ok 2625 - skillFromCodexRead: echo (non-read command) naming the path mints nothing + --- + duration_ms: 0.064808 + type: 'test' + ... +# Subtest: skillFromCodexRead: rm (non-read command) naming the path mints nothing +ok 2626 - skillFromCodexRead: rm (non-read command) naming the path mints nothing + --- + duration_ms: 0.054723 + type: 'test' + ... +# Subtest: skillFromCodexRead composes with commandStringFrom (the exec_command wire shape) +ok 2627 - skillFromCodexRead composes with commandStringFrom (the exec_command wire shape) + --- + duration_ms: 0.110928 + type: 'test' + ... +# Subtest: skillFromCodexRead fails closed on an un-gateable captured name and non-string input +ok 2628 - skillFromCodexRead fails closed on an un-gateable captured name and non-string input + --- + duration_ms: 0.088535 + type: 'test' + ... +# Subtest: skillFromCodexRead fails closed on nested/system skill directory layouts (accepted exclusion, not widened) +ok 2629 - skillFromCodexRead fails closed on nested/system skill directory layouts (accepted exclusion, not widened) + --- + duration_ms: 0.068725 + type: 'test' + ... +# Subtest: SKILL_NAME_RE gates to a bounded verbatim-name domain +ok 2630 - SKILL_NAME_RE gates to a bounded verbatim-name domain + --- + duration_ms: 0.123818 + type: 'test' + ... +# Subtest: PROGRAM_RE gates to a bounded, lowercased basename domain +ok 2631 - PROGRAM_RE gates to a bounded, lowercased basename domain + --- + duration_ms: 0.106471 + type: 'test' + ... +# Subtest: compileConfig defaults listen to the fixed well-known port +ok 2632 - compileConfig defaults listen to the fixed well-known port + --- + duration_ms: 0.677019 + type: 'test' + ... +# Subtest: compileConfig marks an explicit listen as configured +ok 2633 - compileConfig marks an explicit listen as configured + --- + duration_ms: 0.101955 + type: 'test' + ... +# Subtest: a defaulted listen falls back to an ephemeral bind on EADDRINUSE +ok 2634 - a defaulted listen falls back to an ephemeral bind on EADDRINUSE + --- + duration_ms: 0.755167 + type: 'test' + ... +# Subtest: a clean default bind never signals fallback +ok 2635 - a clean default bind never signals fallback + --- + duration_ms: 0.16459 + type: 'test' + ... +# Subtest: a configured listen propagates EADDRINUSE instead of falling back +ok 2636 - a configured listen propagates EADDRINUSE instead of falling back + --- + duration_ms: 0.495904 + type: 'test' + ... +# Subtest: a defaulted listen propagates non-EADDRINUSE bind errors +ok 2637 - a defaulted listen propagates non-EADDRINUSE bind errors + --- + duration_ms: 0.167665 + type: 'test' + ... +# Subtest: ai_gateway_messages schema exposes the gateway message columns +ok 2638 - ai_gateway_messages schema exposes the gateway message columns + --- + duration_ms: 2.526991 + type: 'test' + ... +# Subtest: projectExchange returns zero rows when no projector is registered +ok 2639 - projectExchange returns zero rows when no projector is registered + --- + duration_ms: 0.478127 + type: 'test' + ... +# Subtest: projectExchange returns zero rows when no projector matches +ok 2640 - projectExchange returns zero rows when no projector matches + --- + duration_ms: 0.230651 + type: 'test' + ... +# Subtest: first successful projector wins, sorted by descending priority then registration order +ok 2641 - first successful projector wins, sorted by descending priority then registration order + --- + duration_ms: 4.709308 + type: 'test' + ... +# Subtest: throwing projectors are skipped and the next matching projector wins +ok 2642 - throwing projectors are skipped and the next matching projector wins + --- + duration_ms: 0.433148 + type: 'test' + ... +# Subtest: projector returning undefined or an empty messages array is skipped +ok 2643 - projector returning undefined or an empty messages array is skipped + --- + duration_ms: 0.402933 + type: 'test' + ... +# Subtest: a usage-policy drop is terminal: dispatch stops, writes no row, and is logged as a drop (not no_projector_match) +ok 2644 - a usage-policy drop is terminal: dispatch stops, writes no row, and is logged as a drop (not no_projector_match) + --- + duration_ms: 0.250451 + type: 'test' + ... +# Subtest: a bare undefined decline still falls through to the next matching projector (only the drop sentinel is terminal) +ok 2645 - a bare undefined decline still falls through to the next matching projector (only the drop sentinel is terminal) + --- + duration_ms: 0.403633 + type: 'test' + ... +# Subtest: projector returning an invalid shape is skipped and the next one is tried +ok 2646 - projector returning an invalid shape is skipped and the next one is tried + --- + duration_ms: 0.703479 + type: 'test' + ... +# Subtest: all projectors failing returns zero rows and warns once per failure +ok 2647 - all projectors failing returns zero rows and warns once per failure + --- + duration_ms: 0.501523 + type: 'test' + ... +# Subtest: skipping a non-matching projector does not call its project() +ok 2648 - skipping a non-matching projector does not call its project() + --- + duration_ms: 0.419338 + type: 'test' + ... +# Subtest: projector-supplied message_id and previous_message_id are preserved +ok 2649 - projector-supplied message_id and previous_message_id are preserved + --- + duration_ms: 0.323942 + type: 'test' + ... +# Subtest: supplied message_id without history gets the immediate predecessor as previous_message_id +ok 2650 - supplied message_id without history gets the immediate predecessor as previous_message_id + --- + duration_ms: 0.271493 + type: 'test' + ... +# Subtest: fallback identity stamps gateway.identity_source and a linear previous_message_id chain +ok 2651 - fallback identity stamps gateway.identity_source and a linear previous_message_id chain + --- + duration_ms: 0.326045 + type: 'test' + ... +# Subtest: fallback message_id ignores cache_control so identity is stable across replays +ok 2652 - fallback message_id ignores cache_control so identity is stable across replays + --- + duration_ms: 0.166983 + type: 'test' + ... +# Subtest: fallback message_id is scoped by agent_id so subagents do not collide on shared content +ok 2653 - fallback message_id is scoped by agent_id so subagents do not collide on shared content + --- + duration_ms: 0.110198 + type: 'test' + ... +# Subtest: previous_message_id chains are scoped per (conversation_id ?? session_id, agent_id) +ok 2654 - previous_message_id chains are scoped per (conversation_id ?? session_id, agent_id) + --- + duration_ms: 0.405356 + type: 'test' + ... +# Subtest: session_id is the partition key; conversation_id is null for Claude, the thread for Codex +ok 2655 - session_id is the partition key; conversation_id is null for Claude, the thread for Codex + --- + duration_ms: 0.28948 + type: 'test' + ... +# Subtest: dispatcher threads a working isSessionIgnored predicate into the projector ctx +ok 2656 - dispatcher threads a working isSessionIgnored predicate into the projector ctx + --- + duration_ms: 0.298193 + type: 'test' + ... +# Subtest: projector ctx defaults isSessionIgnored to a false predicate when none is supplied +ok 2657 - projector ctx defaults isSessionIgnored to a false predicate when none is supplied + --- + duration_ms: 0.221236 + type: 'test' + ... +# Subtest: a projection without session_id is rejected as an invalid shape +ok 2658 - a projection without session_id is rejected as an invalid shape + --- + duration_ms: 0.268058 + type: 'test' + ... +# Subtest: attributes.gateway carries exchange provenance and dev_run_id +ok 2659 - attributes.gateway carries exchange provenance and dev_run_id + --- + duration_ms: 0.231372 + type: 'test' + ... +# Subtest: row output is stripped to the schema (no extra fields leak) +ok 2660 - row output is stripped to the schema (no extra fields leak) + --- + duration_ms: 0.908531 + type: 'test' + ... +# Subtest: a multi-block usage-bearing message stamps usage on only the last part +ok 2661 - a multi-block usage-bearing message stamps usage on only the last part + --- + duration_ms: 0.318154 + type: 'test' + ... +# Subtest: two Codex threads sharing a session_id keep separate start time and tool lookup +ok 2662 - two Codex threads sharing a session_id keep separate start time and tool lookup + --- + duration_ms: 0.492228 + type: 'test' + ... +# Subtest: per-message model wins over the exchange model; absent it falls back to the exchange model +ok 2663 - per-message model wins over the exchange model; absent it falls back to the exchange model + --- + duration_ms: 0.240485 + type: 'test' + ... +# Subtest: restart replay: seeds seen-set from committed part_ids so prior history re-emits zero rows +ok 2664 - restart replay: seeds seen-set from committed part_ids so prior history re-emits zero rows + --- + duration_ms: 0.667545 + type: 'test' + ... +# Subtest: restart replay: seeding scans each session lazily and at most once per listener +ok 2665 - restart replay: seeding scans each session lazily and at most once per listener + --- + duration_ms: 0.286105 + type: 'test' + ... +# Subtest: seeding: sessions with no committed rows share one index build and skip the per-session scan +ok 2666 - seeding: sessions with no committed rows share one index build and skip the per-session scan + --- + duration_ms: 0.392266 + type: 'test' + ... +# Subtest: committed-session index: a miss past the rebuild window triggers exactly one rebuild +ok 2667 - committed-session index: a miss past the rebuild window triggers exactly one rebuild + --- + duration_ms: 0.989584 + type: 'test' + ... +# Subtest: committed-session index: N concurrent fresh-session misses past the rebuild window share one rebuild +ok 2668 - committed-session index: N concurrent fresh-session misses past the rebuild window share one rebuild + --- + duration_ms: 0.550257 + type: 'test' + ... +# Subtest: committed-session index: a scan that throws degrades the index instead of wedging it +ok 2669 - committed-session index: a scan that throws degrades the index instead of wedging it + --- + duration_ms: 15.318362 + type: 'test' + ... +# Subtest: committed-session index: a scan slower than the rebuild window still serves cache hits +ok 2670 - committed-session index: a scan slower than the rebuild window still serves cache hits + --- + duration_ms: 0.876172 + type: 'test' + ... +# Subtest: restart replay: concurrent first exchanges for one session seed once and emit no duplicates +ok 2671 - restart replay: concurrent first exchanges for one session seed once and emit no duplicates + --- + duration_ms: 0.326136 + type: 'test' + ... +# Subtest: restart replay: a different session is not deduped against another session rows +ok 2672 - restart replay: a different session is not deduped against another session rows + --- + duration_ms: 0.26341 + type: 'test' + ... +# Subtest: restart replay: with no storage, behavior is unchanged (committed history is not seeded) +ok 2673 - restart replay: with no storage, behavior is unchanged (committed history is not seeded) + --- + duration_ms: 0.207385 + type: 'test' + ... +# Subtest: restart replay: a throwing storage degrades to not-seeded and never drops rows +ok 2674 - restart replay: a throwing storage degrades to not-seeded and never drops rows + --- + duration_ms: 0.195367 + type: 'test' + ... +# Subtest: committed-session index: a build that could not scan is not cached as "no committed rows" +ok 2675 - committed-session index: a build that could not scan is not cached as "no committed rows" + --- + duration_ms: 0.444606 + type: 'test' + ... +# Subtest: seed failure: a storage that breaks its discover contract loses no rows and does not poison the session memo +ok 2676 - seed failure: a storage that breaks its discover contract loses no rows and does not poison the session memo + --- + duration_ms: 0.598419 + type: 'test' + ... +# Subtest: forwardHeaders strips x-hypaware-* request headers before proxying upstream +ok 2677 - forwardHeaders strips x-hypaware-* request headers before proxying upstream + --- + duration_ms: 0.879777 + type: 'test' + ... +# Subtest: compileUpstreams sorts by descending priority, then longer prefix, then registration order +ok 2678 - compileUpstreams sorts by descending priority, then longer prefix, then registration order + --- + duration_ms: 0.782208 + type: 'test' + ... +# Subtest: compileUpstreams rejects non-http(s) base_url +ok 2679 - compileUpstreams rejects non-http(s) base_url + --- + duration_ms: 0.363402 + type: 'test' + ... +# Subtest: compileUpstreams rejects unparseable base_url +ok 2680 - compileUpstreams rejects unparseable base_url + --- + duration_ms: 0.143127 + type: 'test' + ... +# Subtest: matchUpstream invokes match() and returns the first upstream whose match() is true +ok 2681 - matchUpstream invokes match() and returns the first upstream whose match() is true + --- + duration_ms: 0.28951 + type: 'test' + ... +# Subtest: matchUpstream short-circuits on the first match - lower-priority match() is not called +ok 2682 - matchUpstream short-circuits on the first match - lower-priority match() is not called + --- + duration_ms: 0.167915 + type: 'test' + ... +# Subtest: matchUpstream ties on priority are broken by registration order +ok 2683 - matchUpstream ties on priority are broken by registration order + --- + duration_ms: 0.204681 + type: 'test' + ... +# Subtest: matchUpstream falls back to path-prefix when no match() is supplied +ok 2684 - matchUpstream falls back to path-prefix when no match() is supplied + --- + duration_ms: 0.133222 + type: 'test' + ... +# Subtest: matchUpstream treats a throwing match() as a non-match and continues to the next upstream +ok 2685 - matchUpstream treats a throwing match() as a non-match and continues to the next upstream + --- + duration_ms: 0.358916 + type: 'test' + ... +# Subtest: matchUpstream returns undefined when nothing matches +ok 2686 - matchUpstream returns undefined when nothing matches + --- + duration_ms: 0.391995 + type: 'test' + ... +# Subtest: matchUpstream hands match() a lowercased, array-valued header view +ok 2687 - matchUpstream hands match() a lowercased, array-valued header view + --- + duration_ms: 0.333397 + type: 'test' + ... +# Subtest: pathMatchesPrefix: catch-all root, exact, segment, and non-match +ok 2688 - pathMatchesPrefix: catch-all root, exact, segment, and non-match + --- + duration_ms: 0.101615 + type: 'test' + ... +# Subtest: a /_hypaware/* control request is handled locally: not forwarded to a catch-all upstream and starts no exchange (R2) +ok 2689 - a /_hypaware/* control request is handled locally: not forwarded to a catch-all upstream and starts no exchange (R2) + --- + duration_ms: 41.113879 + type: 'test' + ... +# Subtest: R2 proxy edge cases: query string and trailing slash stay local; a look-alike path is proxied +ok 2690 - R2 proxy edge cases: query string and trailing slash stay local; a look-alike path is proxied + --- + duration_ms: 12.766644 + type: 'test' + ... +# Subtest: an unknown /_hypaware/* path with no control handler is 404ed locally, not proxied +ok 2691 - an unknown /_hypaware/* path with no control handler is 404ed locally, not proxied + --- + duration_ms: 6.72424 + type: 'test' + ... +# Subtest: decodes a gzip-encoded response body +ok 2692 - decodes a gzip-encoded response body + --- + duration_ms: 2.847227 + type: 'test' + ... +# Subtest: decodes a brotli-encoded response body +ok 2693 - decodes a brotli-encoded response body + --- + duration_ms: 1.458237 + type: 'test' + ... +# Subtest: decodes a deflate-encoded response body (zlib and raw) +ok 2694 - decodes a deflate-encoded response body (zlib and raw) + --- + duration_ms: 1.39503 + type: 'test' + ... +# Subtest: passes through an identity / unencoded response body unchanged +ok 2695 - passes through an identity / unencoded response body unchanged + --- + duration_ms: 0.362311 + type: 'test' + ... +# Subtest: decodes a gzip-encoded request body +ok 2696 - decodes a gzip-encoded request body + --- + duration_ms: 0.523105 + type: 'test' + ... +# Subtest: handles content-encoding header case-insensitively +ok 2697 - handles content-encoding header case-insensitively + --- + duration_ms: 0.320227 + type: 'test' + ... +# Subtest: decodes the body even when content-encoding is a redacted header +ok 2698 - decodes the body even when content-encoding is a redacted header + --- + duration_ms: 0.681726 + type: 'test' + ... +# Subtest: falls back to raw bytes when the encoding is unknown +ok 2699 - falls back to raw bytes when the encoding is unknown + --- + duration_ms: 0.222849 + type: 'test' + ... +# Subtest: falls back to raw bytes when a gzip body is corrupt +ok 2700 - falls back to raw bytes when a gzip body is corrupt + --- + duration_ms: 0.481982 + type: 'test' + ... +# Subtest: parses SSE events from a gzip-encoded stream at finalize +ok 2701 - parses SSE events from a gzip-encoded stream at finalize + --- + duration_ms: 0.866578 + type: 'test' + ... +# Subtest: uncompressed SSE streams still parse incrementally per chunk +ok 2702 - uncompressed SSE streams still parse incrementally per chunk + --- + duration_ms: 0.331354 + type: 'test' + ... +# Subtest: header-blind SSE: a stream with no content-type is sniffed and parsed +ok 2703 - header-blind SSE: a stream with no content-type is sniffed and parsed + --- + duration_ms: 0.211932 + type: 'test' + ... +# Subtest: a JSON response with no content-type stays non-SSE (no false sniff) +ok 2704 - a JSON response with no content-type stays non-SSE (no false sniff) + --- + duration_ms: 0.163949 + type: 'test' + ... +# Subtest: a finalized exchange leaves the recorder active set +ok 2705 - a finalized exchange leaves the recorder active set + --- + duration_ms: 1.691411 + type: 'test' + ... +# Subtest: drain force-finish also empties the active set +ok 2706 - drain force-finish also empties the active set + --- + duration_ms: 10.082695 + type: 'test' + ... +# Subtest: finalize releases the raw chunk buffers while the row keeps the decoded bodies +ok 2707 - finalize releases the raw chunk buffers while the row keeps the decoded bodies + --- + duration_ms: 0.377223 + type: 'test' + ... +# Subtest: a successful ignore receipt does not claim a drop, for an id live traffic never carries +ok 2708 - a successful ignore receipt does not claim a drop, for an id live traffic never carries + --- + duration_ms: 22.045086 + type: 'test' + ... +# Subtest: the --json receipt states its guarantee, so `status: ok` cannot read as "dropped" +ok 2709 - the --json receipt states its guarantee, so `status: ok` cannot read as "dropped" + --- + duration_ms: 8.798913 + type: 'test' + ... +# Subtest: the unignore receipt reports the removal, not a resumption it cannot verify +ok 2710 - the unignore receipt reports the removal, not a resumption it cannot verify + --- + duration_ms: 3.193443 + type: 'test' + ... +# Subtest: the reader carries the same qualifier, so writer and reader cannot drift +ok 2711 - the reader carries the same qualifier, so writer and reader cannot drift + --- + duration_ms: 2.462853 + type: 'test' + ... +# Subtest: claude/skills/hypaware-privacy/SKILL.md checks the control reply is about the session it posted +ok 2712 - claude/skills/hypaware-privacy/SKILL.md checks the control reply is about the session it posted + --- + duration_ms: 0.455412 + type: 'test' + ... +# Subtest: codex/skills/hypaware-privacy/SKILL.md checks the control reply is about the session it posted +ok 2713 - codex/skills/hypaware-privacy/SKILL.md checks the control reply is about the session it posted + --- + duration_ms: 0.163769 + type: 'test' + ... +# Subtest: an impostor that echoes the token is believed - and every answer discloses that it was never authenticated +ok 2714 - an impostor that echoes the token is believed - and every answer discloses that it was never authenticated + --- + duration_ms: 22.904722 + type: 'test' + ... +# Subtest: `hyp session ignore` carries the disclosure too, where a spoofed success reads as done +ok 2715 - `hyp session ignore` carries the disclosure too, where a spoofed success reads as done + --- + duration_ms: 6.59955 + type: 'test' + ... +# Subtest: the disclosure is unconditional: a real gateway answer carries it too +ok 2716 - the disclosure is unconditional: a real gateway answer carries it too + --- + duration_ms: 3.205683 + type: 'test' + ... +# Subtest: an UNKNOWN answer reports the field too, so a reader never has to infer it from absence +ok 2717 - an UNKNOWN answer reports the field too, so a reader never has to infer it from absence + --- + duration_ms: 1.36186 + type: 'test' + ... +# Subtest: the ignored-session set is readable: GET reports current membership +ok 2718 - the ignored-session set is readable: GET reports current membership + --- + duration_ms: 28.369569 + type: 'test' + ... +# Subtest: a gateway restart no longer fails open SILENTLY: the reader reports the resumed recording +ok 2719 - a gateway restart no longer fails open SILENTLY: the reader reports the resumed recording + --- + duration_ms: 5.383373 + type: 'test' + ... +# Subtest: GET without a session_id is a 400, and an unrelated /_hypaware path is still a 404 +ok 2720 - GET without a session_id is a 400, and an unrelated /_hypaware path is still a 404 + --- + duration_ms: 2.54657 + type: 'test' + ... +# Subtest: GET round-trips a session id verbatim (R5): no trimming, no normalization +ok 2721 - GET round-trips a session id verbatim (R5): no trimming, no normalization + --- + duration_ms: 1.620223 + type: 'test' + ... +# Subtest: hyp session status reports `ignored` against a live gateway +ok 2722 - hyp session status reports `ignored` against a live gateway + --- + duration_ms: 3.413899 + type: 'test' + ... +# Subtest: hyp session status reports `not_ignored` distinctly, with a nonzero exit +ok 2723 - hyp session status reports `not_ignored` distinctly, with a nonzero exit + --- + duration_ms: 1.709969 + type: 'test' + ... +# Subtest: hyp session status FAILS CLOSED when the gateway is unreachable: unknown, never ignored:false +ok 2724 - hyp session status FAILS CLOSED when the gateway is unreachable: unknown, never ignored:false + --- + duration_ms: 1.518688 + type: 'test' + ... +# Subtest: hyp session status fails closed when no gateway endpoint can be resolved at all +ok 2725 - hyp session status fails closed when no gateway endpoint can be resolved at all + --- + duration_ms: 0.312305 + type: 'test' + ... +# Subtest: hyp session status names the folder governor rather than omitting it (R7) +ok 2726 - hyp session status names the folder governor rather than omitting it (R7) + --- + duration_ms: 2.449813 + type: 'test' + ... +# Subtest: hyp session status fails closed on a 200 with no `ignored` field +ok 2727 - hyp session status fails closed on a 200 with no `ignored` field + --- + duration_ms: 1.849511 + type: 'test' + ... +# Subtest: hyp session status fails closed on a 200 whose `ignored` is a string, not a boolean +ok 2728 - hyp session status fails closed on a 200 whose `ignored` is a string, not a boolean + --- + duration_ms: 1.517086 + type: 'test' + ... +# Subtest: hyp session status fails closed on a 200 with no numeric `total` +ok 2729 - hyp session status fails closed on a 200 with no numeric `total` + --- + duration_ms: 1.423113 + type: 'test' + ... +# Subtest: hyp session status fails closed on a 200 JSON array +ok 2730 - hyp session status fails closed on a 200 JSON array + --- + duration_ms: 1.252834 + type: 'test' + ... +# Subtest: hyp session status fails closed on a 200 that is not JSON at all +ok 2731 - hyp session status fails closed on a 200 that is not JSON at all + --- + duration_ms: 1.448842 + type: 'test' + ... +# Subtest: hyp session status fails closed when the answer is about a DIFFERENT session +ok 2732 - hyp session status fails closed when the answer is about a DIFFERENT session + --- + duration_ms: 1.442583 + type: 'test' + ... +# Subtest: hyp session status fails closed on a non-200 from the endpoint +ok 2733 - hyp session status fails closed on a non-200 from the endpoint + --- + duration_ms: 1.263831 + type: 'test' + ... +# Subtest: hyp session ignore does not report a quiet success against a non-gateway responder +ok 2734 - hyp session ignore does not report a quiet success against a non-gateway responder + --- + duration_ms: 1.722658 + type: 'test' + ... +# Subtest: hyp session ignore / unignore round-trip through the control route +ok 2735 - hyp session ignore / unignore round-trip through the control route + --- + duration_ms: 2.585059 + type: 'test' + ... +# Subtest: the ephemerality caveat names the fork that mints a new session id, not only a restart +ok 2736 - the ephemerality caveat names the fork that mints a new session id, not only a restart + --- + duration_ms: 2.920959 + type: 'test' + ... +# Subtest: an explicit session id argument beats the environment +ok 2737 - an explicit session id argument beats the environment + --- + duration_ms: 1.482874 + type: 'test' + ... +# Subtest: resolves a Codex session id from the rollout whose payload.cwd matches the invocation cwd +ok 2738 - resolves a Codex session id from the rollout whose payload.cwd matches the invocation cwd + --- + duration_ms: 0.96118 + type: 'test' + ... +# Subtest: a first line that is not a session_meta record resolves nothing, however much it looks like one +ok 2739 - a first line that is not a session_meta record resolves nothing, however much it looks like one + --- + duration_ms: 0.482734 + type: 'test' + ... +# Subtest: refuses (never guesses newest) when several Codex rollouts match the cwd +ok 2740 - refuses (never guesses newest) when several Codex rollouts match the cwd + --- + duration_ms: 0.885626 + type: 'test' + ... +# Subtest: a cwd match with NO thread id still makes the answer ambiguous: it is not discarded before the count +ok 2741 - a cwd match with NO thread id still makes the answer ambiguous: it is not discarded before the count + --- + duration_ms: 0.656698 + type: 'test' + ... +# Subtest: a LONE cwd match with no thread id refuses rather than resolving the container it states +ok 2742 - a LONE cwd match with no thread id refuses rather than resolving the container it states + --- + duration_ms: 0.566471 + type: 'test' + ... +# Subtest: a header stating NEITHER field is not diagnosed as an old Codex +ok 2743 - a header stating NEITHER field is not diagnosed as an old Codex + --- + duration_ms: 0.675456 + type: 'test' + ... +# Subtest: a rollout that DOES state a thread id but no session_id is still the legacy diagnosis +ok 2744 - a rollout that DOES state a thread id but no session_id is still the legacy diagnosis + --- + duration_ms: 1.383072 + type: 'test' + ... +# Subtest: a stated thread ignores an id-less rollout entirely: that path is identity, not counting +ok 2745 - a stated thread ignores an id-less rollout entirely: that path is identity, not counting + --- + duration_ms: 0.771793 + type: 'test' + ... +# Subtest: refuses when no Codex rollout matches the cwd +ok 2746 - refuses when no Codex rollout matches the cwd + --- + duration_ms: 0.543946 + type: 'test' + ... +# Subtest: CLAUDE_CODE_SESSION_ID wins over any Codex rollout scan +ok 2747 - CLAUDE_CODE_SESSION_ID wins over any Codex rollout scan + --- + duration_ms: 0.399347 + type: 'test' + ... +# Subtest: a truncated rollout scan refuses rather than claiming a unique cwd match +ok 2748 - a truncated rollout scan refuses rather than claiming a unique cwd match + --- + duration_ms: 0.748578 + type: 'test' + ... +# Subtest: a SINGLE STALE rollout refuses: one cwd match is not evidence the session is live +ok 2749 - a SINGLE STALE rollout refuses: one cwd match is not evidence the session is live + --- + duration_ms: 0.624099 + type: 'test' + ... +# Subtest: a fresh rollout still resolves, and reports that the id was inferred from disk +ok 2750 - a fresh rollout still resolves, and reports that the id was inferred from disk + --- + duration_ms: 0.596346 + type: 'test' + ... +# Subtest: the staleness bound is what refuses, not the cwd match: the same rollout resolves under a wider bound +ok 2751 - the staleness bound is what refuses, not the cwd match: the same rollout resolves under a wider bound + --- + duration_ms: 0.799876 + type: 'test' + ... +# Subtest: a disk-inferred id is never presented as if the client had stated it +ok 2752 - a disk-inferred id is never presented as if the client had stated it + --- + duration_ms: 9.506859 + type: 'test' + ... +# Subtest: a Codex SUBAGENT thread resolves the session container, and that is the id the gateway actually drops +ok 2753 - a Codex SUBAGENT thread resolves the session container, and that is the id the gateway actually drops + --- + duration_ms: 2.507061 + type: 'test' + ... +# Subtest: a legacy rollout with no session_id field REFUSES: the back-filled thread id is not the key +ok 2754 - a legacy rollout with no session_id field REFUSES: the back-filled thread id is not the key + --- + duration_ms: 1.214916 + type: 'test' + ... +# Subtest: `hyp session ignore` on a legacy rollout reports no success and exits unknown +ok 2755 - `hyp session ignore` on a legacy rollout reports no success and exits unknown + --- + duration_ms: 1.444105 + type: 'test' + ... +# Subtest: CODEX_THREAD_ID selects the live rollout without the mtime proxy, then the container is read from it +ok 2756 - CODEX_THREAD_ID selects the live rollout without the mtime proxy, then the container is read from it + --- + duration_ms: 1.376091 + type: 'test' + ... +# Subtest: a BLANK session_id is as unusable as an absent one, and refuses the same way +ok 2757 - a BLANK session_id is as unusable as an absent one, and refuses the same way + --- + duration_ms: 2.230721 + type: 'test' + ... +# Subtest: the container is read from the session_meta header, not from any first line that carries the fields +ok 2758 - the container is read from the session_meta header, not from any first line that carries the fields + --- + duration_ms: 1.226193 + type: 'test' + ... +# Subtest: rollouts that disagree about which session contains a thread refuse, naming both +ok 2759 - rollouts that disagree about which session contains a thread refuse, naming both + --- + duration_ms: 1.259825 + type: 'test' + ... +# Subtest: a stated thread still resolves from a rollout whose cwd is unusable: that path never reads cwd +ok 2760 - a stated thread still resolves from a rollout whose cwd is unusable: that path never reads cwd + --- + duration_ms: 1.940459 + type: 'test' + ... +# Subtest: two clients each stating a session refuse rather than picking one +ok 2761 - two clients each stating a session refuse rather than picking one + --- + duration_ms: 0.410394 + type: 'test' + ... +# Subtest: a Codex answer discloses the grain it acts at, and names the thread beside the container +ok 2762 - a Codex answer discloses the grain it acts at, and names the thread beside the container + --- + duration_ms: 5.577147 + type: 'test' + ... +# Subtest: an endpoint nothing proved is the gateway is reported as such +ok 2763 - an endpoint nothing proved is the gateway is reported as such + --- + duration_ms: 3.736058 + type: 'test' + ... +# Subtest: `--` ends flag parsing so a session id beginning with `-` is reachable +ok 2764 - `--` ends flag parsing so a session id beginning with `-` is reachable + --- + duration_ms: 1.629747 + type: 'test' + ... +# Subtest: an oversized control response is refused rather than buffered +ok 2765 - an oversized control response is refused rather than buffered + --- + duration_ms: 3.965778 + type: 'test' + ... +# Subtest: an unresolvable session id fails closed, it does not report not-ignored +ok 2766 - an unresolvable session id fails closed, it does not report not-ignored + --- + duration_ms: 0.938877 + type: 'test' + ... +# Subtest: settle dedupe stops reading once every batch part_id is resolved +ok 2767 - settle dedupe stops reading once every batch part_id is resolved + --- + duration_ms: 3.805004 + type: 'test' + ... +# Subtest: settle dedupe still keeps a row whose part_id is nowhere committed +ok 2768 - settle dedupe still keeps a row whose part_id is nowhere committed + --- + duration_ms: 0.967961 + type: 'test' + ... +# Subtest: source starts with only adapter-registered upstream presets +ok 2769 - source starts with only adapter-registered upstream presets + --- + duration_ms: 38.324359 + type: 'test' + ... +# Subtest: operator configured upstream wins over same-name adapter preset +ok 2770 - operator configured upstream wins over same-name adapter preset + --- + duration_ms: 7.848539 + type: 'test' + ... +# Subtest: the ignored-session set survives a reload() of the same GatewayState +ok 2771 - the ignored-session set survives a reload() of the same GatewayState + --- + duration_ms: 9.706393 + type: 'test' + ... +# Subtest: restart-drops-state: a fresh GatewayState never carries a previous run's opt-outs +ok 2772 - restart-drops-state: a fresh GatewayState never carries a previous run's opt-outs + --- + duration_ms: 0.700915 + type: 'test' + ... +# Subtest: the gateway source a hermes-only picker run composes starts, idle +ok 2773 - the gateway source a hermes-only picker run composes starts, idle + --- + duration_ms: 11.393488 + type: 'test' + ... +# Subtest: an idle gateway binds once a reload brings an upstream +ok 2774 - an idle gateway binds once a reload brings an upstream + --- + duration_ms: 4.643708 + type: 'test' + ... +# Subtest: a reload that removes every upstream tears the listener down and idles +ok 2775 - a reload that removes every upstream tears the listener down and idles + --- + duration_ms: 2.680985 + type: 'test' + ... +# Subtest: status() reports the reloaded config upstreams, not the boot-time ones +ok 2776 - status() reports the reloaded config upstreams, not the boot-time ones + --- + duration_ms: 0.2364 + type: 'test' + ... +# Subtest: status() counts a configured upstream it cannot name +ok 2777 - status() counts a configured upstream it cannot name + --- + duration_ms: 0.299645 + type: 'test' + ... +# Subtest: status() counts zero upstreams for a config that named none +ok 2778 - status() counts zero upstreams for a config that named none + --- + duration_ms: 0.287527 + type: 'test' + ... +# Subtest: the idle log is a warning only when configured upstreams were dropped +ok 2779 - the idle log is a warning only when configured upstreams were dropped + --- + duration_ms: 0.215989 + type: 'test' + ... +# Subtest: a gateway that lost one of two upstreams warns at boot and reports the drop +ok 2780 - a gateway that lost one of two upstreams warns at boot and reports the drop + --- + duration_ms: 0.637108 + type: 'test' + ... +# Subtest: a gateway whose upstreams all compile reports no drop and logs nothing +ok 2781 - a gateway whose upstreams all compile reports no drop and logs nothing + --- + duration_ms: 1.781017 + type: 'test' + ... +# Subtest: claude projector drops a session whose cwd is marked `ignore` in the machine-local list +ok 2782 - claude projector drops a session whose cwd is marked `ignore` in the machine-local list + --- + duration_ms: 18.218811 + type: 'test' + ... +# Subtest: claude projector still records a session whose cwd is marked `local-only` in the machine-local list +ok 2783 - claude projector still records a session whose cwd is marked `local-only` in the machine-local list + --- + duration_ms: 15.657969 + type: 'test' + ... +# Subtest: codex projector drops an exchange whose cwd is marked `ignore` in the machine-local list +ok 2784 - codex projector drops an exchange whose cwd is marked `ignore` in the machine-local list + --- + duration_ms: 4.136998 + type: 'test' + ... +# Subtest: codex projector still records an exchange whose cwd is marked `local-only` in the machine-local list +ok 2785 - codex projector still records an exchange whose cwd is marked `local-only` in the machine-local list + --- + duration_ms: 4.049705 + type: 'test' + ... +# Subtest: claude activate() derives the machine-local list from the shared state root, so an `ignore` cwd drops at capture +ok 2786 - claude activate() derives the machine-local list from the shared state root, so an `ignore` cwd drops at capture + --- + duration_ms: 7.063286 + type: 'test' + ... +# Subtest: claude activate() still records a `local-only` cwd (recorded at capture, withheld only at export) +ok 2787 - claude activate() still records a `local-only` cwd (recorded at capture, withheld only at export) + --- + duration_ms: 12.018877 + type: 'test' + ... +# Subtest: codex activate() derives the machine-local list from the shared state root, so an `ignore` cwd drops at capture +ok 2788 - codex activate() derives the machine-local list from the shared state root, so an `ignore` cwd drops at capture + --- + duration_ms: 5.742608 + type: 'test' + ... +# Subtest: codex activate() still records a `local-only` cwd (recorded at capture, withheld only at export) +ok 2789 - codex activate() still records a `local-only` cwd (recorded at capture, withheld only at export) + --- + duration_ms: 5.154515 + type: 'test' + ... +# Subtest: abortableSleep resolves after the delay when not aborted +ok 2790 - abortableSleep resolves after the delay when not aborted + --- + duration_ms: 21.031385 + type: 'test' + ... +# Subtest: abortableSleep rejects immediately when the signal is already aborted +ok 2791 - abortableSleep rejects immediately when the signal is already aborted + --- + duration_ms: 0.666914 + type: 'test' + ... +# Subtest: abortableSleep rejects promptly when aborted mid-sleep +ok 2792 - abortableSleep rejects promptly when aborted mid-sleep + --- + duration_ms: 10.825754 + type: 'test' + ... +# Subtest: parseRetryAfter and the ladder are the shared canonical source +ok 2793 - parseRetryAfter and the ladder are the shared canonical source + --- + duration_ms: 0.732333 + type: 'test' + ... +# Subtest: start pulls immediately; a 200 confirms the poll and stages the document with its etag +ok 2794 - start pulls immediately; a 200 confirms the poll and stages the document with its etag + --- + duration_ms: 21.095053 + type: 'test' + ... +# Subtest: If-None-Match always presents the running config etag +ok 2795 - If-None-Match always presents the running config etag + --- + duration_ms: 0.32262 + type: 'test' + ... +# Subtest: 401 refreshes the JWT and retries once; a second 401 escalates without staging +ok 2796 - 401 refreshes the JWT and retries once; a second 401 escalates without staging + --- + duration_ms: 0.790121 + type: 'test' + ... +# Subtest: a 200 without an etag header is dropped, not staged +ok 2797 - a 200 without an etag header is dropped, not staged + --- + duration_ms: 0.591229 + type: 'test' + ... +# Subtest: an oversized 200 body is dropped before parsing +ok 2798 - an oversized 200 body is dropped before parsing + --- + duration_ms: 2.069546 + type: 'test' + ... +# Subtest: invalid JSON in a 200 body is dropped +ok 2799 - invalid JSON in a 200 body is dropped + --- + duration_ms: 0.587833 + type: 'test' + ... +# Subtest: 404 takes the legacy backoff branch without confirming probation +ok 2800 - 404 takes the legacy backoff branch without confirming probation + --- + duration_ms: 0.301549 + type: 'test' + ... +# Subtest: the steady timer keeps polling on the configured cadence +ok 2801 - the steady timer keeps polling on the configured cadence + --- + duration_ms: 121.167473 + type: 'test' + ... +# Subtest: stop prevents any further polls +ok 2802 - stop prevents any further polls + --- + duration_ms: 64.448465 + type: 'test' + ... +# Subtest: transport errors back off and keep the loop alive +ok 2803 - transport errors back off and keep the loop alive + --- + duration_ms: 0.682908 + type: 'test' + ... +# Subtest: an oversized Content-Length is rejected without reading the body +ok 2804 - an oversized Content-Length is rejected without reading the body + --- + duration_ms: 0.971938 + type: 'test' + ... +# Subtest: a chunked oversized body is cancelled at the cap, not buffered whole +ok 2805 - a chunked oversized body is cancelled at the cap, not buffered whole + --- + duration_ms: 1.197741 + type: 'test' + ... +# Subtest: stop() aborts a poll stuck on a never-resolving fetch after the drain grace +ok 2806 - stop() aborts a poll stuck on a never-resolving fetch after the drain grace + --- + duration_ms: 20.543684 + type: 'test' + ... +# Subtest: the request deadline aborts a stalled poll and the loop stays alive +ok 2807 - the request deadline aborts a stalled poll and the loop stays alive + --- + duration_ms: 100.739574 + type: 'test' + ... +# Subtest: 429 with Retry-After schedules from the header without confirming the poll +ok 2808 - 429 with Retry-After schedules from the header without confirming the poll + --- + duration_ms: 0.480801 + type: 'test' + ... +# Subtest: 503 with a garbage Retry-After falls back to the backoff ladder +ok 2809 - 503 with a garbage Retry-After falls back to the backoff ladder + --- + duration_ms: 0.260196 + type: 'test' + ... +# Subtest: 429 with Retry-After: 0 reschedules via the ladder, not an immediate re-poll +ok 2810 - 429 with Retry-After: 0 reschedules via the ladder, not an immediate re-poll + --- + duration_ms: 50.427294 + type: 'test' + ... +# Subtest: parseRetryAfter: delta-seconds, HTTP-date, and garbage +ok 2811 - parseRetryAfter: delta-seconds, HTTP-date, and garbage + --- + duration_ms: 0.435772 + type: 'test' + ... +# Subtest: forward sink chunks a large partition into bounded POSTs +ok 2812 - forward sink chunks a large partition into bounded POSTs + --- + duration_ms: 92.654256 + type: 'test' + ... +# Subtest: a partition that fits in one chunk makes exactly one POST +ok 2813 - a partition that fits in one chunk makes exactly one POST + --- + duration_ms: 0.373447 + type: 'test' + ... +# Subtest: chunk batch-ids are deterministic across re-exports (idempotent retry) +ok 2814 - chunk batch-ids are deterministic across re-exports (idempotent retry) + --- + duration_ms: 112.389431 + type: 'test' + ... +# Subtest: a transport failure marks the partition for retry, not the whole batch +ok 2815 - a transport failure marks the partition for retry, not the whole batch + --- + duration_ms: 45.014026 + type: 'test' + ... +# Subtest: empty batch is a no-op success +ok 2816 - empty batch is a no-op success + --- + duration_ms: 0.164069 + type: 'test' + ... +# Subtest: byte-identical chunks get distinct batch-ids (no ledger collision) +ok 2817 - byte-identical chunks get distinct batch-ids (no ledger collision) + --- + duration_ms: 43.157034 + type: 'test' + ... +# Subtest: a dataset with no sourceSignal fails the partition for retry (unknown signal) +ok 2818 - a dataset with no sourceSignal fails the partition for retry (unknown signal) + --- + duration_ms: 0.310391 + type: 'test' + ... +# Subtest: the byte budget splits wide rows even when the row count is tiny +ok 2819 - the byte budget splits wide rows even when the row count is tiny + --- + duration_ms: 93.777673 + type: 'test' + ... +# Subtest: a 401 re-sends the same body + batch-id after one refresh +ok 2820 - a 401 re-sends the same body + batch-id after one refresh + --- + duration_ms: 0.614394 + type: 'test' + ... +# Subtest: each chunk emits central.forward.chunk telemetry +ok 2821 - each chunk emits central.forward.chunk telemetry + --- + duration_ms: 52.882486 + type: 'test' + ... +# Subtest: central.forward.failed names the failing chunk and how many landed +ok 2822 - central.forward.failed names the failing chunk and how many landed + --- + duration_ms: 50.21376 + type: 'test' + ... +# Subtest: 429 honors Retry-After and retries the same chunk to success +ok 2823 - 429 honors Retry-After and retries the same chunk to success + --- + duration_ms: 0.420369 + type: 'test' + ... +# Subtest: 429 without Retry-After falls back to the backoff ladder +ok 2824 - 429 without Retry-After falls back to the backoff ladder + --- + duration_ms: 0.206694 + type: 'test' + ... +# Subtest: 503 is backpressure (retried), not a hard failure +ok 2825 - 503 is backpressure (retried), not a hard failure + --- + duration_ms: 0.205893 + type: 'test' + ... +# Subtest: repeated 429s walk the ladder before succeeding +ok 2826 - repeated 429s walk the ladder before succeeding + --- + duration_ms: 0.281658 + type: 'test' + ... +# Subtest: backpressure beyond the inline budget fails the partition for retry +ok 2827 - backpressure beyond the inline budget fails the partition for retry + --- + duration_ms: 0.257662 + type: 'test' + ... +# Subtest: a non-positive Retry-After (0 / past date) uses the ladder, never a zero-delay spin +ok 2828 - a non-positive Retry-After (0 / past date) uses the ladder, never a zero-delay spin + --- + duration_ms: 0.243781 + type: 'test' + ... +# Subtest: backpressure drains the throttle response body before parking +ok 2829 - backpressure drains the throttle response body before parking + --- + duration_ms: 0.287827 + type: 'test' + ... +# Subtest: each backpressure wait emits central.forward.backpressure telemetry +ok 2830 - each backpressure wait emits central.forward.backpressure telemetry + --- + duration_ms: 0.19679 + type: 'test' + ... +# Subtest: close() aborts a chunk paused on backpressure (no shutdown wedge) +ok 2831 - close() aborts a chunk paused on backpressure (no shutdown wedge) + --- + duration_ms: 15.783149 + type: 'test' + ... +# Subtest: a tick with no new rows transmits zero bytes and zero chunks +ok 2832 - a tick with no new rows transmits zero bytes and zero chunks + --- + duration_ms: 0.278292 + type: 'test' + ... +# Subtest: a tick after N new rows reads/sends only the new suffix and advances the watermark +ok 2833 - a tick after N new rows reads/sends only the new suffix and advances the watermark + --- + duration_ms: 0.21065 + type: 'test' + ... +# Subtest: the watermark advances once, at end-of-partition, to the high-water after +ok 2834 - the watermark advances once, at end-of-partition, to the high-water after + --- + duration_ms: 43.263847 + type: 'test' + ... +# Subtest: a mid-partition failure leaves the watermark unadvanced (no partial checkpoint) +ok 2835 - a mid-partition failure leaves the watermark unadvanced (no partial checkpoint) + --- + duration_ms: 29.2257 + type: 'test' + ... +# Subtest: a respool re-reads the whole partition with STABLE batch-ids (ledger-dedups the acked prefix) +ok 2836 - a respool re-reads the whole partition with STABLE batch-ids (ledger-dedups the acked prefix) + --- + duration_ms: 60.344167 + type: 'test' + ... +# Subtest: a fresh partition (no watermark) reads from the start and advances +ok 2837 - a fresh partition (no watermark) reads from the start and advances + --- + duration_ms: 0.199653 + type: 'test' + ... +# Subtest: an unordered scan never skips a lower-seq row when a later chunk fails (BLOCKER, LLP 0040 §4 risk \#3) +ok 2838 - an unordered scan never skips a lower-seq row when a later chunk fails (BLOCKER, LLP 0040 §4 risk \#3) + --- + duration_ms: 34.246822 + type: 'test' + ... +# Subtest: a drop-only tick POSTs nothing yet checkpoints past the withheld rows +ok 2839 - a drop-only tick POSTs nothing yet checkpoints past the withheld rows + --- + duration_ms: 1.582034 + type: 'test' + ... +# Subtest: a mixed tick ships only the full rows and advances to the partition high-water +ok 2840 - a mixed tick ships only the full rows and advances to the partition high-water + --- + duration_ms: 1.517647 + type: 'test' + ... +# Subtest: a directory un-excluded AFTER a drop-only checkpoint is not re-sent (durably passed) +ok 2841 - a directory un-excluded AFTER a drop-only checkpoint is not re-sent (durably passed) + --- + duration_ms: 0.306486 + type: 'test' + ... +# Subtest: a failed chunk never checkpoints, even when the partition also dropped rows +ok 2842 - a failed chunk never checkpoints, even when the partition also dropped rows + --- + duration_ms: 0.537227 + type: 'test' + ... +# Subtest: a corrupt local-only list fails the tick with the watermark untouched +ok 2843 - a corrupt local-only list fails the tick with the watermark untouched + --- + duration_ms: 0.322961 + type: 'test' + ... +# Subtest: cwd-less datasets are unaffected: a partition with no drops ships and advances exactly as before +ok 2844 - cwd-less datasets are unaffected: a partition with no drops ships and advances exactly as before + --- + duration_ms: 1.539389 + type: 'test' + ... +# Subtest: writeLoginSeed writes a 0600 login-origin identity stamped with the central URL +ok 2845 - writeLoginSeed writes a 0600 login-origin identity stamped with the central URL + --- + duration_ms: 1.418386 + type: 'test' + ... +# Subtest: writeLoginSeed returns the identity it displaced +ok 2846 - writeLoginSeed returns the identity it displaced + --- + duration_ms: 0.761567 + type: 'test' + ... +# Subtest: acquire() loads a login seed with no bootstrap token configured (LLP 0061 D2/D3) +ok 2847 - acquire() loads a login seed with no bootstrap token configured (LLP 0061 D2/D3) + --- + duration_ms: 0.728337 + type: 'test' + ... +# Subtest: a configured bootstrap token does not re-bootstrap over a same-URL login seed (LLP 0061 D3) +ok 2848 - a configured bootstrap token does not re-bootstrap over a same-URL login seed (LLP 0061 D3) + --- + duration_ms: 0.432838 + type: 'test' + ... +# Subtest: a login seed for a different URL still re-bootstraps when a token is configured (LLP 0061 D4) +ok 2849 - a login seed for a different URL still re-bootstraps when a token is configured (LLP 0061 D4) + --- + duration_ms: 14.257199 + type: 'test' + ... +# Subtest: a re-point with no token refuses a login seed and points at re-login, not hyp join +ok 2850 - a re-point with no token refuses a login seed and points at re-login, not hyp join + --- + duration_ms: 0.851504 + type: 'test' + ... +# Subtest: refresh preserves the login origin and central_url on the persisted identity +ok 2851 - refresh preserves the login origin and central_url on the persisted identity + --- + duration_ms: 1.341439 + type: 'test' + ... +# Subtest: first join bootstraps and stamps the minting url + token fingerprint +ok 2852 - first join bootstraps and stamps the minting url + token fingerprint + --- + duration_ms: 20.341227 + type: 'test' + ... +# Subtest: reboot with the same mint reuses the persisted identity (no re-bootstrap) +ok 2853 - reboot with the same mint reuses the persisted identity (no re-bootstrap) + --- + duration_ms: 0.933168 + type: 'test' + ... +# Subtest: re-join with a different token re-bootstraps a fresh gateway identity +ok 2854 - re-join with a different token re-bootstraps a fresh gateway identity + --- + duration_ms: 1.231602 + type: 'test' + ... +# Subtest: re-join pointed at a different central URL re-bootstraps +ok 2855 - re-join pointed at a different central URL re-bootstraps + --- + duration_ms: 1.106602 + type: 'test' + ... +# Subtest: re-pointed at a different central URL with no token refuses to load the old identity +ok 2856 - re-pointed at a different central URL with no token refuses to load the old identity + --- + duration_ms: 1.034973 + type: 'test' + ... +# Subtest: reboot at the same URL with no token still loads (no false mismatch) +ok 2857 - reboot at the same URL with no token still loads (no false mismatch) + --- + duration_ms: 0.7035 + type: 'test' + ... +# Subtest: an identity from an older build (no mint stamp) re-bootstraps when a token is set +ok 2858 - an identity from an older build (no mint stamp) re-bootstraps when a token is set + --- + duration_ms: 2.339436 + type: 'test' + ... +# Subtest: validateClaudeAccountConfig accepts an empty / absent config +ok 2859 - validateClaudeAccountConfig accepts an empty / absent config + --- + duration_ms: 1.522423 + type: 'test' + ... +# Subtest: validateClaudeAccountConfig accepts both modes +ok 2860 - validateClaudeAccountConfig accepts both modes + --- + duration_ms: 0.173795 + type: 'test' + ... +# Subtest: validateClaudeAccountConfig rejects a non-object config +ok 2861 - validateClaudeAccountConfig rejects a non-object config + --- + duration_ms: 0.863322 + type: 'test' + ... +# Subtest: validateClaudeAccountConfig rejects an unknown mode +ok 2862 - validateClaudeAccountConfig rejects an unknown mode + --- + duration_ms: 0.128535 + type: 'test' + ... +# Subtest: validateClaudeAccountConfig requires a key source in org_key mode +ok 2863 - validateClaudeAccountConfig requires a key source in org_key mode + --- + duration_ms: 0.301929 + type: 'test' + ... +# Subtest: validateClaudeAccountConfig rejects api_key together with api_key_env +ok 2864 - validateClaudeAccountConfig rejects api_key together with api_key_env + --- + duration_ms: 0.168897 + type: 'test' + ... +# Subtest: validateClaudeAccountConfig rejects empty strings and unknown keys +ok 2865 - validateClaudeAccountConfig rejects empty strings and unknown keys + --- + duration_ms: 0.208667 + type: 'test' + ... +# Subtest: resolveMode defaults to subscription and honors the config +ok 2866 - resolveMode defaults to subscription and honors the config + --- + duration_ms: 0.168256 + type: 'test' + ... +# Subtest: org_key mode uses the configured key with empty headers +ok 2867 - org_key mode uses the configured key with empty headers + --- + duration_ms: 1.102857 + type: 'test' + ... +# Subtest: org_key mode resolves api_key_env and fails loudly when unset +ok 2868 - org_key mode resolves api_key_env and fails loudly when unset + --- + duration_ms: 0.303722 + type: 'test' + ... +# Subtest: subscription mode errors with login guidance when not signed in +ok 2869 - subscription mode errors with login guidance when not signed in + --- + duration_ms: 0.609757 + type: 'test' + ... +# Subtest: subscription mode returns the stored token with the oauth beta header +ok 2870 - subscription mode returns the stored token with the oauth beta header + --- + duration_ms: 0.780867 + type: 'test' + ... +# Subtest: subscription mode refreshes inside the expiry window and persists the rotated pair +ok 2871 - subscription mode refreshes inside the expiry window and persists the rotated pair + --- + duration_ms: 2.652031 + type: 'test' + ... +# Subtest: subscription refresh failure surfaces as an error, not a stale token +ok 2872 - subscription refresh failure surfaces as an error, not a stale token + --- + duration_ms: 0.615676 + type: 'test' + ... +# Subtest: createAuthorizationAttempt derives the S256 challenge from the verifier +ok 2873 - createAuthorizationAttempt derives the S256 challenge from the verifier + --- + duration_ms: 2.119762 + type: 'test' + ... +# Subtest: buildAuthorizeUrl carries the PKCE and state parameters +ok 2874 - buildAuthorizeUrl carries the PKCE and state parameters + --- + duration_ms: 0.348389 + type: 'test' + ... +# Subtest: buildAuthorizeUrl honors a loopback redirect override +ok 2875 - buildAuthorizeUrl honors a loopback redirect override + --- + duration_ms: 1.068604 + type: 'test' + ... +# Subtest: parsePastedAuthorization handles code\#state and full URLs +ok 2876 - parsePastedAuthorization handles code\#state and full URLs + --- + duration_ms: 0.94739 + type: 'test' + ... +# Subtest: exchangeAuthorizationCode verifies state before spending the code +ok 2877 - exchangeAuthorizationCode verifies state before spending the code + --- + duration_ms: 0.285034 + type: 'test' + ... +# Subtest: exchangeAuthorizationCode maps the grant into a stored record +ok 2878 - exchangeAuthorizationCode maps the grant into a stored record + --- + duration_ms: 0.367329 + type: 'test' + ... +# Subtest: exchangeAuthorizationCode echoes the loopback redirect it authorized with +ok 2879 - exchangeAuthorizationCode echoes the loopback redirect it authorized with + --- + duration_ms: 0.20346 + type: 'test' + ... +# Subtest: exchangeAuthorizationCode rejects an unrecognized token response +ok 2880 - exchangeAuthorizationCode rejects an unrecognized token response + --- + duration_ms: 0.13177 + type: 'test' + ... +# Subtest: paste lane fails, rather than hanging, when stdin ends with no listener to wait on +ok 2881 - paste lane fails, rather than hanging, when stdin ends with no listener to wait on + --- + duration_ms: 4.481832 + type: 'test' + ... +# Subtest: paste lane fails, rather than hanging, on a stdin that was already spent +ok 2882 - paste lane fails, rather than hanging, on a stdin that was already spent + --- + duration_ms: 1.476925 + type: 'test' + ... +# Subtest: paste lane leaves the loopback listener to finish when stdin ends under it +ok 2883 - paste lane leaves the loopback listener to finish when stdin ends under it + --- + duration_ms: 23.436732 + type: 'test' + ... +# Subtest: paste lane parses a code delivered in the same burst as the EOF +ok 2884 - paste lane parses a code delivered in the same burst as the EOF + --- + duration_ms: 1.13194 + type: 'test' + ... +# Subtest: paste lane parses the pasted code, not a line that followed it +ok 2885 - paste lane parses the pasted code, not a line that followed it + --- + duration_ms: 0.501993 + type: 'test' + ... +# Subtest: paste lane still parses a pasted code +ok 2886 - paste lane still parses a pasted code + --- + duration_ms: 0.377493 + type: 'test' + ... +# Subtest: read returns undefined when no credential is stored +ok 2887 - read returns undefined when no credential is stored + --- + duration_ms: 0.996044 + type: 'test' + ... +# Subtest: write / read round-trips and sets 0600 +ok 2888 - write / read round-trips and sets 0600 + --- + duration_ms: 1.362621 + type: 'test' + ... +# Subtest: read throws on corrupt JSON and on an unrecognized shape +ok 2889 - read throws on corrupt JSON and on an unrecognized shape + --- + duration_ms: 0.813687 + type: 'test' + ... +# Subtest: clear removes the file and tolerates absence +ok 2890 - clear removes the file and tolerates absence + --- + duration_ms: 1.379386 + type: 'test' + ... +# Subtest: tokenFingerprint never contains the token +ok 2891 - tokenFingerprint never contains the token + --- + duration_ms: 0.549465 + type: 'test' + ... +# Subtest: withCredentialLock serializes concurrent critical sections +ok 2892 - withCredentialLock serializes concurrent critical sections + --- + duration_ms: 113.613611 + type: 'test' + ... +# Subtest: withCredentialLock breaks a stale lock +ok 2893 - withCredentialLock breaks a stale lock + --- + duration_ms: 0.743319 + type: 'test' + ... +# Subtest: provider advertises a stable contribution shape +ok 2894 - provider advertises a stable contribution shape + --- + duration_ms: 1.662837 + type: 'test' + ... +# Subtest: fixture transcript projects into canonical ai_gateway_messages rows +ok 2895 - fixture transcript projects into canonical ai_gateway_messages rows + --- + duration_ms: 15.883201 + type: 'test' + ... +# Subtest: assistant token usage is folded into attributes.usage like live capture +ok 2896 - assistant token usage is folded into attributes.usage like live capture + --- + duration_ms: 4.15836 + type: 'test' + ... +# Subtest: usage lands once - on the last block of a split assistant API message +ok 2897 - usage lands once - on the last block of a split assistant API message + --- + duration_ms: 4.766123 + type: 'test' + ... +# Subtest: assistant model is surfaced per message, switches mid-session, and drops +ok 2898 - assistant model is surfaced per message, switches mid-session, and drops + --- + duration_ms: 8.912447 + type: 'test' + ... +# Subtest: token usage merges with subagent spawn provenance +ok 2899 - token usage merges with subagent spawn provenance + --- + duration_ms: 26.514079 + type: 'test' + ... +# Subtest: native DAG identity is preserved verbatim +ok 2900 - native DAG identity is preserved verbatim + --- + duration_ms: 13.324061 + type: 'test' + ... +# Subtest: raw_frame is minimized - never a full transcript copy +ok 2901 - raw_frame is minimized - never a full transcript copy + --- + duration_ms: 3.332415 + type: 'test' + ... +# Subtest: reruns are deterministic (idempotent items and rows) +ok 2902 - reruns are deterministic (idempotent items and rows) + --- + duration_ms: 18.604878 + type: 'test' + ... +# Subtest: sessions are grouped into one item each, across multiple files +ok 2903 - sessions are grouped into one item each, across multiple files + --- + duration_ms: 10.104758 + type: 'test' + ... +# Subtest: grouping keys on the entry session id, not the file name +ok 2904 - grouping keys on the entry session id, not the file name + --- + duration_ms: 3.433428 + type: 'test' + ... +# Subtest: since bound filters out messages older than the window +ok 2905 - since bound filters out messages older than the window + --- + duration_ms: 6.299385 + type: 'test' + ... +# Subtest: subagent rows carry the spawning tool call from the meta sidecar +ok 2906 - subagent rows carry the spawning tool call from the meta sidecar + --- + duration_ms: 4.385275 + type: 'test' + ... +# Subtest: missing transcript root yields nothing without throwing +ok 2907 - missing transcript root yields nothing without throwing + --- + duration_ms: 1.110468 + type: 'test' + ... +# Subtest: recovers git_remote/repo_root from the transcript cwd when the record predates git capture +ok 2908 - recovers git_remote/repo_root from the transcript cwd when the record predates git capture + --- + duration_ms: 6.095985 + type: 'test' + ... +# Subtest: record-provided remote wins; no derivation is attempted +ok 2909 - record-provided remote wins; no derivation is attempted + --- + duration_ms: 7.091779 + type: 'test' + ... +# Subtest: derivation is memoized per cwd across sessions +ok 2910 - derivation is memoized per cwd across sessions + --- + duration_ms: 8.708366 + type: 'test' + ... +# Subtest: recovers git_remote from the record cwd when the record predates git capture +ok 2911 - recovers git_remote from the record cwd when the record predates git capture + --- + duration_ms: 4.060562 + type: 'test' + ... +# Subtest: record repo_root is preserved when only the remote is derived +ok 2912 - record repo_root is preserved when only the remote is derived + --- + duration_ms: 6.651981 + type: 'test' + ... +# Subtest: an already-aborted signal stops the scan before any session is projected +ok 2913 - an already-aborted signal stops the scan before any session is projected + --- + duration_ms: 13.552288 + type: 'test' + ... +# Subtest: transcript toolUseResult is promoted onto the backfilled row +ok 2914 - transcript toolUseResult is promoted onto the backfilled row + --- + duration_ms: 3.567903 + type: 'test' + ... +# Subtest: an unclassified interactive session gets a SessionStart additionalContext prompt +ok 2915 - an unclassified interactive session gets a SessionStart additionalContext prompt + --- + duration_ms: 2.612741 + type: 'test' + ... +# Subtest: a classified / unenrolled / non-interactive evaluation emits nothing +ok 2916 - a classified / unenrolled / non-interactive evaluation emits nothing + --- + duration_ms: 1.603176 + type: 'test' + ... +# Subtest: a session-start event with no cwd is a passthrough (no evaluation, no output) +ok 2917 - a session-start event with no cwd is a passthrough (no evaluation, no output) + --- + duration_ms: 0.405547 + type: 'test' + ... +# Subtest: malformed stdin never throws back into Claude +ok 2918 - malformed stdin never throws back into Claude + --- + duration_ms: 0.396493 + type: 'test' + ... +# Subtest: an evaluation that throws is swallowed with exit 0 and no output +ok 2919 - an evaluation that throws is swallowed with exit 0 and no output + --- + duration_ms: 0.432427 + type: 'test' + ... +# Subtest: --help prints usage and does not read stdin +ok 2920 - --help prints usage and does not read stdin + --- + duration_ms: 0.235748 + type: 'test' + ... +# Subtest: isInteractiveClaudeSession: startup is interactive; compact / CI / escape-hatch are not +ok 2921 - isInteractiveClaudeSession: startup is interactive; compact / CI / escape-hatch are not + --- + duration_ms: 0.11202 + type: 'test' + ... +# Subtest: claudeClientName stamps claude-desktop off the Desktop User-Agent +ok 2922 - claudeClientName stamps claude-desktop off the Desktop User-Agent + --- + duration_ms: 1.452228 + type: 'test' + ... +# Subtest: claudeClientName falls back for CLI and generic SDK traffic +ok 2923 - claudeClientName falls back for CLI and generic SDK traffic + --- + duration_ms: 0.09266 + type: 'test' + ... +# Subtest: claudeClientName honors a non-default fallback +ok 2924 - claudeClientName honors a non-default fallback + --- + duration_ms: 0.069155 + type: 'test' + ... +# Subtest: claudeClientVersion extracts both CLI and Desktop versions +ok 2925 - claudeClientVersion extracts both CLI and Desktop versions + --- + duration_ms: 0.161626 + type: 'test' + ... +# Subtest: validateClaudeConfig accepts an empty / absent config +ok 2926 - validateClaudeConfig accepts an empty / absent config + --- + duration_ms: 1.365676 + type: 'test' + ... +# Subtest: validateClaudeConfig leaves non-backfill keys (e.g. proxy) untouched +ok 2927 - validateClaudeConfig leaves non-backfill keys (e.g. proxy) untouched + --- + duration_ms: 0.175376 + type: 'test' + ... +# Subtest: validateClaudeConfig accepts a full backfill block +ok 2928 - validateClaudeConfig accepts a full backfill block + --- + duration_ms: 0.194496 + type: 'test' + ... +# Subtest: validateClaudeConfig rejects a non-object config +ok 2929 - validateClaudeConfig rejects a non-object config + --- + duration_ms: 0.143909 + type: 'test' + ... +# Subtest: validateClaudeConfig rejects a malformed backfill block +ok 2930 - validateClaudeConfig rejects a malformed backfill block + --- + duration_ms: 0.298333 + type: 'test' + ... +# Subtest: validateBackfillSection mounts pointers under the supplied prefix +ok 2931 - validateBackfillSection mounts pointers under the supplied prefix + --- + duration_ms: 0.128295 + type: 'test' + ... +# Subtest: validateClaudeConfig accepts an attach block +ok 2932 - validateClaudeConfig accepts an attach block + --- + duration_ms: 0.194847 + type: 'test' + ... +# Subtest: validateClaudeConfig rejects a malformed attach block +ok 2933 - validateClaudeConfig rejects a malformed attach block + --- + duration_ms: 0.235458 + type: 'test' + ... +# Subtest: validateAttachSection mounts pointers under the supplied prefix +ok 2934 - validateAttachSection mounts pointers under the supplied prefix + --- + duration_ms: 0.298704 + type: 'test' + ... +# Subtest: the registered claude section drives validatePluginConfig +ok 2935 - the registered claude section drives validatePluginConfig + --- + duration_ms: 0.790081 + type: 'test' + ... +# Subtest: a central-locked backfill.on_join cannot be flipped by a colliding local entry +ok 2936 - a central-locked backfill.on_join cannot be flipped by a colliding local entry + --- + duration_ms: 0.303582 + type: 'test' + ... +# Subtest: a central-locked attach.on_join cannot be flipped by a colliding local entry +ok 2937 - a central-locked attach.on_join cannot be flipped by a colliding local entry + --- + duration_ms: 0.12468 + type: 'test' + ... +# Subtest: findDesktop3pProjectsDirs discovers nested .claude/projects under both container layouts +ok 2938 - findDesktop3pProjectsDirs discovers nested .claude/projects under both container layouts + --- + duration_ms: 26.234104 + type: 'test' + ... +# Subtest: findDesktop3pProjectsDirs is empty when no 3p container exists +ok 2939 - findDesktop3pProjectsDirs is empty when no 3p container exists + --- + duration_ms: 1.054342 + type: 'test' + ... +# Subtest: loadTranscript falls back to the 3p sandbox tree when the shared tree misses +ok 2940 - loadTranscript falls back to the 3p sandbox tree when the shared tree misses + --- + duration_ms: 9.190489 + type: 'test' + ... +# Subtest: loadTranscript does not scan the 3p tree when the shared tree matches +ok 2941 - loadTranscript does not scan the 3p tree when the shared tree matches + --- + duration_ms: 8.701786 + type: 'test' + ... +# Subtest: backfill imports a 3p sandbox session and attributes it to the configured owner +ok 2942 - backfill imports a 3p sandbox session and attributes it to the configured owner + --- + duration_ms: 10.156026 + type: 'test' + ... +# Subtest: backfill gates 3p sessions with absent or unclaimed entrypoints when Desktop is unconfigured +ok 2943 - backfill gates 3p sessions with absent or unclaimed entrypoints when Desktop is unconfigured + --- + duration_ms: 14.831041 + type: 'test' + ... +# Subtest: backfill gates 3p sessions when no owners map or predicate is supplied at all +ok 2944 - backfill gates 3p sessions when no owners map or predicate is supplied at all + --- + duration_ms: 5.172622 + type: 'test' + ... +# Subtest: backfill imports the container for a configured Desktop that declares no entrypoint values +ok 2945 - backfill imports the container for a configured Desktop that declares no entrypoint values + --- + duration_ms: 6.188566 + type: 'test' + ... +# Subtest: backfill stamps subagent provenance from sidecars inside the 3p container +ok 2946 - backfill stamps subagent provenance from sidecars inside the 3p container + --- + duration_ms: 10.721515 + type: 'test' + ... +# Subtest: backfill gates a 3p sandbox session when its owner is not configured +ok 2947 - backfill gates a 3p sandbox session when its owner is not configured + --- + duration_ms: 13.495482 + type: 'test' + ... +# Subtest: createDesktop3pDirsCache serves cached roots within the TTL and re-sweeps after it +ok 2948 - createDesktop3pDirsCache serves cached roots within the TTL and re-sweeps after it + --- + duration_ms: 12.466888 + type: 'test' + ... +# Subtest: loadTranscript finds a sandbox home created after the root cache was primed +ok 2949 - loadTranscript finds a sandbox home created after the root cache was primed + --- + duration_ms: 11.681104 + type: 'test' + ... +# Subtest: the claude-desktop manifest declares no attach_probe +ok 2950 - the claude-desktop manifest declares no attach_probe + --- + duration_ms: 2.759224 + type: 'test' + ... +# Subtest: hyp detach --client claude-desktop is a no-op over the managed plist +ok 2951 - hyp detach --client claude-desktop is a no-op over the managed plist + --- + duration_ms: 1.875781 + type: 'test' + ... +# Subtest: the managed plist never surfaces as a client attach-probe error +ok 2952 - the managed plist never surfaces as a client attach-probe error + --- + duration_ms: 1.766795 + type: 'test' + ... +# Subtest: install-helper writes an executable no-arg wrapper under the state dir +ok 2953 - install-helper writes an executable no-arg wrapper under the state dir + --- + duration_ms: 1.83571 + type: 'test' + ... +# Subtest: the generated wrapper runs its target with no arguments +ok 2954 - the generated wrapper runs its target with no arguments + --- + duration_ms: 5.361409 + type: 'test' + ... +# Subtest: status reports the helper as not installed until install-helper runs +ok 2955 - status reports the helper as not installed until install-helper runs + --- + duration_ms: 1.050036 + type: 'test' + ... +# Subtest: install: org_key mode skips the login step and never calls claude-account login +ok 2956 - install: org_key mode skips the login step and never calls claude-account login + --- + duration_ms: 2.425156 + type: 'test' + ... +# Subtest: install: subscription mode already signed in skips login without calling it +ok 2957 - install: subscription mode already signed in skips login without calling it + --- + duration_ms: 0.709428 + type: 'test' + ... +# Subtest: install: subscription mode not signed in runs login, and a failed login drops the run +ok 2958 - install: subscription mode not signed in runs login, and a failed login drops the run + --- + duration_ms: 0.679573 + type: 'test' + ... +# Subtest: install: no stdin in subscription mode fails the login step without attempting login +ok 2959 - install: no stdin in subscription mode fails the login step without attempting login + --- + duration_ms: 0.551759 + type: 'test' + ... +# Subtest: install: refuses up front on an ephemeral gateway listen, with no side effects +ok 2960 - install: refuses up front on an ephemeral gateway listen, with no side effects + --- + duration_ms: 0.520891 + type: 'test' + ... +# Subtest: install: refuses up front on a non-macOS platform, with no side effects +ok 2961 - install: refuses up front on a non-macOS platform, with no side effects + --- + duration_ms: 0.29632 + type: 'test' + ... +# Subtest: install: --print-commands passes the platform gate, because it applies nothing +ok 2962 - install: --print-commands passes the platform gate, because it applies nothing + --- + duration_ms: 0.741437 + type: 'test' + ... +# Subtest: install: residue directory is backed up and cleared when present +ok 2963 - install: residue directory is backed up and cleared when present + --- + duration_ms: 2.071659 + type: 'test' + ... +# Subtest: install: a re-run with no residue present is a plain skip, not a failure +ok 2964 - install: a re-run with no residue present is a plain skip, not a failure + --- + duration_ms: 0.791813 + type: 'test' + ... +# Subtest: install: an already up-to-date managed plist is skipped, no sudo invoked for it +ok 2965 - install: an already up-to-date managed plist is skipped, no sudo invoked for it + --- + duration_ms: 0.812344 + type: 'test' + ... +# Subtest: install: a stale managed plist is rewritten via sudo cp with the freshly rendered content +ok 2966 - install: a stale managed plist is rewritten via sudo cp with the freshly rendered content + --- + duration_ms: 0.669127 + type: 'test' + ... +# Subtest: install: a failed privileged write drops with a re-run hint, not a thrown error +ok 2967 - install: a failed privileged write drops with a re-run hint, not a thrown error + --- + duration_ms: 0.574152 + type: 'test' + ... +# Subtest: install: --print-commands prints the sudo and killall commands without invoking spawn +ok 2968 - install: --print-commands prints the sudo and killall commands without invoking spawn + --- + duration_ms: 0.438536 + type: 'test' + ... +# Subtest: install: --print-commands applies nothing at all, including the non-privileged steps +ok 2969 - install: --print-commands applies nothing at all, including the non-privileged steps + --- + duration_ms: 0.911976 + type: 'test' + ... +# Subtest: install: --print-commands never asks for consent, because it changes nothing +ok 2970 - install: --print-commands never asks for consent, because it changes nothing + --- + duration_ms: 0.416974 + type: 'test' + ... +# Subtest: install: a bare enter proceeds - disclosure, question, then the steps +ok 2971 - install: a bare enter proceeds - disclosure, question, then the steps + --- + duration_ms: 2.532108 + type: 'test' + ... +# Subtest: install: an explicit no declines - nothing changed, nonzero exit +ok 2972 - install: an explicit no declines - nothing changed, nonzero exit + --- + duration_ms: 1.136507 + type: 'test' + ... +# Subtest: install: org_key mode asks without claiming a browser sign-in +ok 2973 - install: org_key mode asks without claiming a browser sign-in + --- + duration_ms: 0.731301 + type: 'test' + ... +# Subtest: install: --yes skips the question and runs the steps +ok 2974 - install: --yes skips the question and runs the steps + --- + duration_ms: 0.592961 + type: 'test' + ... +# Subtest: install: a stdin that ends without an answer declines instead of hanging +ok 2975 - install: a stdin that ends without an answer declines instead of hanging + --- + duration_ms: 0.764933 + type: 'test' + ... +# Subtest: install: the disclosure names the credential posture and every file it will touch +ok 2976 - install: the disclosure names the credential posture and every file it will touch + --- + duration_ms: 0.674155 + type: 'test' + ... +# Subtest: install: org_key mode disclosure promises no sign-in +ok 2977 - install: org_key mode disclosure promises no sign-in + --- + duration_ms: 0.615806 + type: 'test' + ... +# Subtest: install: consent names the residue clear only when residue is actually present +ok 2978 - install: consent names the residue clear only when residue is actually present + --- + duration_ms: 1.201887 + type: 'test' + ... +# Subtest: install: an already-configured machine is not re-prompted +ok 2979 - install: an already-configured machine is not re-prompted + --- + duration_ms: 0.528873 + type: 'test' + ... +# Subtest: buildPlistWriteCommands renders mkdir, cp, and chmod against the target path +ok 2980 - buildPlistWriteCommands renders mkdir, cp, and chmod against the target path + --- + duration_ms: 0.182608 + type: 'test' + ... +# Subtest: computeDesiredPlistContent matches renderManagedPreferencesPlist(buildManagedProfile(...)) +ok 2981 - computeDesiredPlistContent matches renderManagedPreferencesPlist(buildManagedProfile(...)) + --- + duration_ms: 0.325174 + type: 'test' + ... +# Subtest: every registered claude-desktop command summary matches its manifest entry +ok 2982 - every registered claude-desktop command summary matches its manifest entry + --- + duration_ms: 2.782739 + type: 'test' + ... +# Subtest: the duplicated stable default listen matches the ai-gateway default (parity) +ok 2983 - the duplicated stable default listen matches the ai-gateway default (parity) + --- + duration_ms: 0.909022 + type: 'test' + ... +# Subtest: resolveGatewayBaseUrl uses the fixed default when the fleet sets no listen +ok 2984 - resolveGatewayBaseUrl uses the fixed default when the fleet sets no listen + --- + duration_ms: 0.221787 + type: 'test' + ... +# Subtest: resolveGatewayBaseUrl honors an explicit fleet listen +ok 2985 - resolveGatewayBaseUrl honors an explicit fleet listen + --- + duration_ms: 0.208927 + type: 'test' + ... +# Subtest: resolveGatewayBaseUrl refuses an ephemeral listen +ok 2986 - resolveGatewayBaseUrl refuses an ephemeral listen + --- + duration_ms: 0.383583 + type: 'test' + ... +# Subtest: resolveGatewayBaseUrl honors and normalizes the endpoint override +ok 2987 - resolveGatewayBaseUrl honors and normalizes the endpoint override + --- + duration_ms: 0.21092 + type: 'test' + ... +# Subtest: buildManagedProfile renders the app schema and carries no secret material +ok 2988 - buildManagedProfile renders the app schema and carries no secret material + --- + duration_ms: 0.648395 + type: 'test' + ... +# Subtest: org_key mode renders under the x-api-key scheme +ok 2989 - org_key mode renders under the x-api-key scheme + --- + duration_ms: 0.086511 + type: 'test' + ... +# Subtest: renderCredentialHelperScript wraps the credential command via the absolute interpreter, no args +ok 2990 - renderCredentialHelperScript wraps the credential command via the absolute interpreter, no args + --- + duration_ms: 0.169728 + type: 'test' + ... +# Subtest: renderCredentialHelperScript shell-quotes a path with spaces +ok 2991 - renderCredentialHelperScript shell-quotes a path with spaces + --- + duration_ms: 0.227626 + type: 'test' + ... +# Subtest: renderCredentialHelperScript embeds a non-default HYP env, omits an empty one +ok 2992 - renderCredentialHelperScript embeds a non-default HYP env, omits an empty one + --- + duration_ms: 0.323151 + type: 'test' + ... +# Subtest: renderManagedPreferencesPlist emits a well-formed dict +ok 2993 - renderManagedPreferencesPlist emits a well-formed dict + --- + duration_ms: 0.245003 + type: 'test' + ... +# Subtest: validateClaudeDesktopConfig accepts valid shapes and rejects typos +ok 2994 - validateClaudeDesktopConfig accepts valid shapes and rejects typos + --- + duration_ms: 0.313236 + type: 'test' + ... +# Subtest: verify: missing plist and clean residue is incomplete but not thrown +ok 2995 - verify: missing plist and clean residue is incomplete but not thrown + --- + duration_ms: 1.830883 + type: 'test' + ... +# Subtest: verify: up-to-date plist and clean residue is a green exit code +ok 2996 - verify: up-to-date plist and clean residue is a green exit code + --- + duration_ms: 0.602466 + type: 'test' + ... +# Subtest: verify: a present but stale plist is reported STALE and fails +ok 2997 - verify: a present but stale plist is reported STALE and fails + --- + duration_ms: 0.430565 + type: 'test' + ... +# Subtest: verify: leftover dialog residue fails even with a correct plist +ok 2998 - verify: leftover dialog residue fails even with a correct plist + --- + duration_ms: 0.606542 + type: 'test' + ... +# Subtest: verify: refuses cleanly (no throw) on an ephemeral gateway listen +ok 2999 - verify: refuses cleanly (no throw) on an ephemeral gateway listen + --- + duration_ms: 0.493009 + type: 'test' + ... +# Subtest: verify: refuses on a non-macOS platform instead of reporting MISSING +ok 3000 - verify: refuses on a non-macOS platform instead of reporting MISSING + --- + duration_ms: 0.275479 + type: 'test' + ... +# Subtest: checkInstallState is a pure read: never mutates the residue directory or plist +ok 3001 - checkInstallState is a pure read: never mutates the residue directory or plist + --- + duration_ms: 0.63807 + type: 'test' + ... +# Subtest: derives redacted remote and repo_root, and never asks for HEAD +ok 3002 - derives redacted remote and repo_root, and never asks for HEAD + --- + duration_ms: 2.946338 + type: 'test' + ... +# Subtest: an SSH remote is left intact (no userinfo to strip) +ok 3003 - an SSH remote is left intact (no userinfo to strip) + --- + duration_ms: 0.198692 + type: 'test' + ... +# Subtest: degrades to empty when the cwd is not a git repo +ok 3004 - degrades to empty when the cwd is not a git repo + --- + duration_ms: 0.196128 + type: 'test' + ... +# Subtest: a repo with no origin still yields repo_root +ok 3005 - a repo with no origin still yields repo_root + --- + duration_ms: 0.176829 + type: 'test' + ... +# Subtest: returns empty for an absent cwd without invoking git +ok 3006 - returns empty for an absent cwd without invoking git + --- + duration_ms: 0.130739 + type: 'test' + ... +# Subtest: native DAG identity: uuid from JSONL transcript becomes message_id and provider_uuid +ok 3007 - native DAG identity: uuid from JSONL transcript becomes message_id and provider_uuid + --- + duration_ms: 15.885815 + type: 'test' + ... +# Subtest: transcript-matched live rows carry the minimized raw_frame, never the full line +ok 3008 - transcript-matched live rows carry the minimized raw_frame, never the full line + --- + duration_ms: 7.227665 + type: 'test' + ... +# Subtest: live: response-level usage lands once, on the last block of a split turn +ok 3009 - live: response-level usage lands once, on the last block of a split turn + --- + duration_ms: 3.616166 + type: 'test' + ... +# Subtest: root message gets previous_message_id = [] when parentUuid is null +ok 3010 - root message gets previous_message_id = [] when parentUuid is null + --- + duration_ms: 3.943153 + type: 'test' + ... +# Subtest: transcript-enriched previous_message_id carries the immediate predecessor, scoped per thread +ok 3011 - transcript-enriched previous_message_id carries the immediate predecessor, scoped per thread + --- + duration_ms: 7.222237 + type: 'test' + ... +# Subtest: subagent transcript under /subagents supplies sidechain identity +ok 3012 - subagent transcript under /subagents supplies sidechain identity + --- + duration_ms: 5.397814 + type: 'test' + ... +# Subtest: transcript_path from session context also loads sibling subagent files +ok 3013 - transcript_path from session context also loads sibling subagent files + --- + duration_ms: 6.13884 + type: 'test' + ... +# Subtest: subagent exchange stamps spawned_by_tool_use_id from the meta sidecar +ok 3014 - subagent exchange stamps spawned_by_tool_use_id from the meta sidecar + --- + duration_ms: 6.153873 + type: 'test' + ... +# Subtest: cache_control on wire blocks and caller on transcript blocks do not break matching +ok 3015 - cache_control on wire blocks and caller on transcript blocks do not break matching + --- + duration_ms: 6.13235 + type: 'test' + ... +# Subtest: multi-block assistant turn splits into per-line uuid messages (LLP 0023) +ok 3016 - multi-block assistant turn splits into per-line uuid messages (LLP 0023) + --- + duration_ms: 5.428711 + type: 'test' + ... +# Subtest: parallel tool_results split one message per result, joined by tool_use_id +ok 3017 - parallel tool_results split one message per result, joined by tool_use_id + --- + duration_ms: 3.077697 + type: 'test' + ... +# Subtest: transcript toolUseResult is promoted onto the matched live row +ok 3018 - transcript toolUseResult is promoted onto the matched live row + --- + duration_ms: 2.873017 + type: 'test' + ... +# Subtest: reminder-wrapped prompt canonicalizes to transcript content + wire_only extra +ok 3019 - reminder-wrapped prompt canonicalizes to transcript content + wire_only extra + --- + duration_ms: 5.022033 + type: 'test' + ... +# Subtest: real text riding with a tool_result is matched, not dumped as wire_only +ok 3020 - real text riding with a tool_result is matched, not dumped as wire_only + --- + duration_ms: 4.702237 + type: 'test' + ... +# Subtest: x-claude-code-agent-id header stamps is_sidechain even without a transcript +ok 3021 - x-claude-code-agent-id header stamps is_sidechain even without a transcript + --- + duration_ms: 1.616127 + type: 'test' + ... +# Subtest: unmatched multi-block assistant turn still splits, with stable per-block fallback ids +ok 3022 - unmatched multi-block assistant turn still splits, with stable per-block fallback ids + --- + duration_ms: 7.496284 + type: 'test' + ... +# Subtest: missing transcript → gateway fallback identity + claude.identity_source marker +ok 3023 - missing transcript → gateway fallback identity + claude.identity_source marker + --- + duration_ms: 4.219833 + type: 'test' + ... +# Subtest: transcript-matched rows do NOT carry a settlement match_key +ok 3024 - transcript-matched rows do NOT carry a settlement match_key + --- + duration_ms: 2.974361 + type: 'test' + ... +# Subtest: harness aux traffic (security monitor) is tagged with aux_kind, not dropped +ok 3025 - harness aux traffic (security monitor) is tagged with aux_kind, not dropped + --- + duration_ms: 1.554262 + type: 'test' + ... +# Subtest: ordinary conversation traffic carries no aux_kind +ok 3026 - ordinary conversation traffic carries no aux_kind + --- + duration_ms: 1.427379 + type: 'test' + ... +# Subtest: session-context state file supplies cwd and git_branch on the row +ok 3027 - session-context state file supplies cwd and git_branch on the row + --- + duration_ms: 2.790402 + type: 'test' + ... +# Subtest: exchange without anthropic signature is skipped by match() +ok 3028 - exchange without anthropic signature is skipped by match() + --- + duration_ms: 1.189628 + type: 'test' + ... +# Subtest: match() accepts /v1/messages path even without anthropic headers +ok 3029 - match() accepts /v1/messages path even without anthropic headers + --- + duration_ms: 0.985187 + type: 'test' + ... +# Subtest: match() accepts requests with anthropic-version header on non-canonical paths +ok 3030 - match() accepts requests with anthropic-version header on non-canonical paths + --- + duration_ms: 1.366077 + type: 'test' + ... +# Subtest: hook → state file → projector roundtrip writes cwd onto the row +ok 3031 - hook → state file → projector roundtrip writes cwd onto the row + --- + duration_ms: 48.29877 + type: 'test' + ... +# Subtest: hook appends the minimal cwd record before the git subprocesses run +ok 3032 - hook appends the minimal cwd record before the git subprocesses run + --- + duration_ms: 4.761948 + type: 'test' + ... +# Subtest: hook still lands the minimal cwd record when git throws +ok 3033 - hook still lands the minimal cwd record when git throws + --- + duration_ms: 3.312264 + type: 'test' + ... +# Subtest: hook captures repo identity for a real git repo and the projector stamps it +ok 3034 - hook captures repo identity for a real git repo and the projector stamps it + --- + duration_ms: 55.548048 + type: 'test' + ... +# Subtest: hook redacts credential userinfo from an https remote before it is recorded (LLP 0032) +ok 3035 - hook redacts credential userinfo from an https remote before it is recorded (LLP 0032) + --- + duration_ms: 41.602251 + type: 'test' + ... +# Subtest: redactRemoteUserinfo strips only the credential-bearing URL form +ok 3036 - redactRemoteUserinfo strips only the credential-bearing URL form + --- + duration_ms: 0.266625 + type: 'test' + ... +# Subtest: pickLatestMatching prefers newer records when multiple share a session_id +ok 3037 - pickLatestMatching prefers newer records when multiple share a session_id + --- + duration_ms: 10.178871 + type: 'test' + ... +# Subtest: pickLatestMatching prefers transcript_path over session_id when both keys hit +ok 3038 - pickLatestMatching prefers transcript_path over session_id when both keys hit + --- + duration_ms: 3.191921 + type: 'test' + ... +# Subtest: defaultSessionContextFile resolves under the plugin state dir +ok 3039 - defaultSessionContextFile resolves under the plugin state dir + --- + duration_ms: 0.427359 + type: 'test' + ... +# Subtest: appendSessionContext compacts large state files to recent records +ok 3040 - appendSessionContext compacts large state files to recent records + --- + duration_ms: 11.313676 + type: 'test' + ... +# Subtest: attach records the managed env + hook entries into the marker undo record +ok 3041 - attach records the managed env + hook entries into the marker undo record + --- + duration_ms: 18.354958 + type: 'test' + ... +# Subtest: attach backs up a pre-existing foreign ANTHROPIC_BASE_URL as prev_base_url +ok 3042 - attach backs up a pre-existing foreign ANTHROPIC_BASE_URL as prev_base_url + --- + duration_ms: 12.560481 + type: 'test' + ... +# Subtest: attach omits prev_base_url when there was no pre-existing base URL +ok 3043 - attach omits prev_base_url when there was no pre-existing base URL + --- + duration_ms: 7.170559 + type: 'test' + ... +# Subtest: attach leaves a user-owned ENABLE_TOOL_SEARCH untouched and unmanaged +ok 3044 - attach leaves a user-owned ENABLE_TOOL_SEARCH untouched and unmanaged + --- + duration_ms: 4.681145 + type: 'test' + ... +# Subtest: re-attach keeps managing an ENABLE_TOOL_SEARCH it owns +ok 3045 - re-attach keeps managing an ENABLE_TOOL_SEARCH it owns + --- + duration_ms: 9.267005 + type: 'test' + ... +# Subtest: attach declares the gateway base URL first-party so the assumed context window is not cut +ok 3046 - attach declares the gateway base URL first-party so the assumed context window is not cut + --- + duration_ms: 6.069505 + type: 'test' + ... +# Subtest: attach leaves a user-owned _CLAUDE_CODE_ASSUME_FIRST_PARTY_BASE_URL untouched and unmanaged +ok 3047 - attach leaves a user-owned _CLAUDE_CODE_ASSUME_FIRST_PARTY_BASE_URL untouched and unmanaged + --- + duration_ms: 6.384003 + type: 'test' + ... +# Subtest: attach leaves a user-owned boolean env value untouched and unmanaged +ok 3048 - attach leaves a user-owned boolean env value untouched and unmanaged + --- + duration_ms: 6.998417 + type: 'test' + ... +# Subtest: attach leaves a user-owned number env value untouched and unmanaged +ok 3049 - attach leaves a user-owned number env value untouched and unmanaged + --- + duration_ms: 5.351384 + type: 'test' + ... +# Subtest: attach leaves a user-owned null env value untouched and unmanaged +ok 3050 - attach leaves a user-owned null env value untouched and unmanaged + --- + duration_ms: 6.377053 + type: 'test' + ... +# Subtest: re-attach keeps managing a _CLAUDE_CODE_ASSUME_FIRST_PARTY_BASE_URL it owns +ok 3051 - re-attach keeps managing a _CLAUDE_CODE_ASSUME_FIRST_PARTY_BASE_URL it owns + --- + duration_ms: 8.210789 + type: 'test' + ... +# Subtest: idempotent re-attach keeps the original prev_base_url, not the gateway URL +ok 3052 - idempotent re-attach keeps the original prev_base_url, not the gateway URL + --- + duration_ms: 11.397463 + type: 'test' + ... +# Subtest: idempotent re-attach does not invent a prev_base_url when none existed +ok 3053 - idempotent re-attach does not invent a prev_base_url when none existed + --- + duration_ms: 16.807485 + type: 'test' + ... +# Subtest: the marker undo record is stable across re-attach (modulo attached_at) +ok 3054 - the marker undo record is stable across re-attach (modulo attached_at) + --- + duration_ms: 12.523124 + type: 'test' + ... +# Subtest: attach backs up a 8080 base URL into prev_base_url +ok 3055 - attach backs up a 8080 base URL into prev_base_url + --- + duration_ms: 9.998937 + type: 'test' + ... +# Subtest: attach backs up a false base URL into prev_base_url +ok 3056 - attach backs up a false base URL into prev_base_url + --- + duration_ms: 12.041512 + type: 'test' + ... +# Subtest: attach backs up a null base URL into prev_base_url +ok 3057 - attach backs up a null base URL into prev_base_url + --- + duration_ms: 3.991216 + type: 'test' + ... +# Subtest: attach backs up a {"url":"x"} base URL into prev_base_url +ok 3058 - attach backs up a {"url":"x"} base URL into prev_base_url + --- + duration_ms: 8.724971 + type: 'test' + ... +# Subtest: attach records no prev_base_url when the base URL is absent, whatever else env holds +ok 3059 - attach records no prev_base_url when the base URL is absent, whatever else env holds + --- + duration_ms: 3.558548 + type: 'test' + ... +# Subtest: attach refuses a JSONC settings.json and marks the error as refused +ok 3060 - attach refuses a JSONC settings.json and marks the error as refused + --- + duration_ms: 1.957485 + type: 'test' + ... +# Subtest: attach on malformed non-JSONC JSON is not marked as refused +ok 3061 - attach on malformed non-JSONC JSON is not marked as refused + --- + duration_ms: 1.461381 + type: 'test' + ... +# Subtest: activate() attach() rethrows the JSONC refusal with the refusal mark intact +ok 3062 - activate() attach() rethrows the JSONC refusal with the refusal mark intact + --- + duration_ms: 4.443303 + type: 'test' + ... +# Subtest: attach backs a non-object env up into the marker instead of discarding it +ok 3063 - attach backs a non-object env up into the marker instead of discarding it + --- + duration_ms: 12.48738 + type: 'test' + ... +# Subtest: attach backs a non-array hooks. up into the marker instead of discarding it +ok 3064 - attach backs a non-array hooks. up into the marker instead of discarding it + --- + duration_ms: 8.244922 + type: 'test' + ... +# Subtest: attach backs up a non-object hooks root, and a null block counts as present +ok 3065 - attach backs up a non-object hooks root, and a null block counts as present + --- + duration_ms: 9.471896 + type: 'test' + ... +# Subtest: a re-attach keeps the first attach backup and stops warning about it +ok 3066 - a re-attach keeps the first attach backup and stops warning about it + --- + duration_ms: 9.118399 + type: 'test' + ... +# Subtest: a second displacement at an already-backed-up path is reported as discarded, not as backed up +ok 3067 - a second displacement at an already-backed-up path is reported as discarded, not as backed up + --- + duration_ms: 23.005997 + type: 'test' + ... +# Subtest: a backed-up null still outranks a later displacement: the collision test is presence, not truthiness +ok 3068 - a backed-up null still outranks a later displacement: the collision test is presence, not truthiness + --- + duration_ms: 17.206212 + type: 'test' + ... +# Subtest: a hooks root and a hooks. backup cannot both go back; the shallower one wins and the other is reported +ok 3069 - a hooks root and a hooks. backup cannot both go back; the shallower one wins and the other is reported + --- + duration_ms: 13.557345 + type: 'test' + ... +# Subtest: the same shallowest-first order keeps the older value when the older break was the shallow one +ok 3070 - the same shallowest-first order keeps the older value when the older break was the shallow one + --- + duration_ms: 25.045246 + type: 'test' + ... +# Subtest: a hand-edited prev_malformed path cannot escape the settings object +ok 3071 - a hand-edited prev_malformed path cannot escape the settings object + --- + duration_ms: 12.304602 + type: 'test' + ... +# Subtest: an absent env/hooks block attaches normally, with no backup and no warning +ok 3072 - an absent env/hooks block attaches normally, with no backup and no warning + --- + duration_ms: 8.638049 + type: 'test' + ... +# Subtest: a present, well-formed but unusual env/hooks block is updated, not backed up +ok 3073 - a present, well-formed but unusual env/hooks block is updated, not backed up + --- + duration_ms: 4.659632 + type: 'test' + ... +# Subtest: detach restores the backed-up env block from the marker +ok 3074 - detach restores the backed-up env block from the marker + --- + duration_ms: 14.408931 + type: 'test' + ... +# Subtest: detach restores a backed-up hooks., recreating the emptied hooks root +ok 3075 - detach restores a backed-up hooks., recreating the emptied hooks root + --- + duration_ms: 12.831813 + type: 'test' + ... +# Subtest: detach leaves a backed-up path alone, and reports it, when something else is using it now +ok 3076 - detach leaves a backed-up path alone, and reports it, when something else is using it now + --- + duration_ms: 11.518808 + type: 'test' + ... +# Subtest: detach of an attach that displaced nothing is unchanged by the restore step +ok 3077 - detach of an attach that displaced nothing is unchanged by the restore step + --- + duration_ms: 12.79749 + type: 'test' + ... +# Subtest: enricher drops a null-cwd row whose late-resolved cwd is .hypignore-governed +ok 3078 - enricher drops a null-cwd row whose late-resolved cwd is .hypignore-governed + --- + duration_ms: 11.958716 + type: 'test' + ... +# Subtest: enricher enriches a null-cwd row whose late-resolved cwd is not ignored +ok 3079 - enricher enriches a null-cwd row whose late-resolved cwd is not ignored + --- + duration_ms: 3.480981 + type: 'test' + ... +# Subtest: enricher leaves a null-cwd row unchanged when its session context never arrives (SDK/headless) +ok 3080 - enricher leaves a null-cwd row unchanged when its session context never arrives (SDK/headless) + --- + duration_ms: 2.786665 + type: 'test' + ... +# Subtest: ignore-then-clean: the opening row resolves against the EARLIER ignored record and is DROPPED +ok 3081 - ignore-then-clean: the opening row resolves against the EARLIER ignored record and is DROPPED + --- + duration_ms: 4.59273 + type: 'test' + ... +# Subtest: clean-then-ignore: the opening row resolves against the EARLIER clean record and is KEPT (not dropped) +ok 3082 - clean-then-ignore: the opening row resolves against the EARLIER clean record and is KEPT (not dropped) + --- + duration_ms: 3.38823 + type: 'test' + ... +# Subtest: per-row selection within one batch: a later row takes the later record, an opening row the earlier one +ok 3083 - per-row selection within one batch: a later row takes the later record, an opening row the earlier one + --- + duration_ms: 2.738703 + type: 'test' + ... +# Subtest: ts tie-break: the enriched record wins over the minimal one from the same fire +ok 3084 - ts tie-break: the enriched record wins over the minimal one from the same fire + --- + duration_ms: 2.441881 + type: 'test' + ... +# Subtest: settleBatch REMOVES a null-cwd row whose late-resolved cwd is ignored +ok 3085 - settleBatch REMOVES a null-cwd row whose late-resolved cwd is ignored + --- + duration_ms: 2.169618 + type: 'test' + ... +# Subtest: settleBatch keeps and enriches a null-cwd row whose late-resolved cwd is clean +ok 3086 - settleBatch keeps and enriches a null-cwd row whose late-resolved cwd is clean + --- + duration_ms: 1.976304 + type: 'test' + ... +# Subtest: settleBatch dispatches null-cwd rows even when no row is a gateway fallback +ok 3087 - settleBatch dispatches null-cwd rows even when no row is a gateway fallback + --- + duration_ms: 1.998208 + type: 'test' + ... +# Subtest: resettleBatch never drops a late-resolved ignore row (compaction is not a purge) +ok 3088 - resettleBatch never drops a late-resolved ignore row (compaction is not a purge) + --- + duration_ms: 12.071617 + type: 'test' + ... +# Subtest: enricher upgrades a fallback row to native transcript identity +ok 3089 - enricher upgrades a fallback row to native transcript identity + --- + duration_ms: 15.27715 + type: 'test' + ... +# Subtest: enricher leaves a row unchanged when no transcript line matches +ok 3090 - enricher leaves a row unchanged when no transcript line matches + --- + duration_ms: 3.782809 + type: 'test' + ... +# Subtest: dataset settleBatch is a pure no-op when the batch has no fallback rows +ok 3091 - dataset settleBatch is a pure no-op when the batch has no fallback rows + --- + duration_ms: 0.561353 + type: 'test' + ... +# Subtest: settleBatch dispatches to the enricher and dedupes the upgraded row against committed part_ids +ok 3092 - settleBatch dispatches to the enricher and dedupes the upgraded row against committed part_ids + --- + duration_ms: 4.620012 + type: 'test' + ... +# Subtest: live projector returns no rows when the exchange cwd is governed by .hypignore +ok 3093 - live projector returns no rows when the exchange cwd is governed by .hypignore + --- + duration_ms: 13.522543 + type: 'test' + ... +# Subtest: live projector records normally when the exchange cwd is not ignored +ok 3094 - live projector records normally when the exchange cwd is not ignored + --- + duration_ms: 8.590116 + type: 'test' + ... +# Subtest: live projector with no resolved cwd records normally (no folder to match) +ok 3095 - live projector with no resolved cwd records normally (no folder to match) + --- + duration_ms: 3.646302 + type: 'test' + ... +# Subtest: live projector returns no rows when the resolved session_id is in the gateway ignored-session set +ok 3096 - live projector returns no rows when the resolved session_id is in the gateway ignored-session set + --- + duration_ms: 4.395651 + type: 'test' + ... +# Subtest: live projector records normally when the resolved session_id is not in the ignored set +ok 3097 - live projector records normally when the resolved session_id is not in the ignored set + --- + duration_ms: 11.104828 + type: 'test' + ... +# Subtest: the session opt-out drop logs policy_source: session_opt_out with the matched session_id +ok 3098 - the session opt-out drop logs policy_source: session_opt_out with the matched session_id + --- + duration_ms: 3.297412 + type: 'test' + ... +# Subtest: independence matrix: .hypignore-governed cwd + session NOT in the ignored set still drops +ok 3099 - independence matrix: .hypignore-governed cwd + session NOT in the ignored set still drops + --- + duration_ms: 3.547431 + type: 'test' + ... +# Subtest: independence matrix: clean cwd + session IN the ignored set still drops +ok 3100 - independence matrix: clean cwd + session IN the ignored set still drops + --- + duration_ms: 3.505688 + type: 'test' + ... +# Subtest: independence matrix: .hypignore-governed cwd AND session in the ignored set both drop (no double-count, no interaction) +ok 3101 - independence matrix: .hypignore-governed cwd AND session in the ignored set both drop (no double-count, no interaction) + --- + duration_ms: 3.918265 + type: 'test' + ... +# Subtest: independence matrix: clean cwd + session NOT in the ignored set records normally +ok 3102 - independence matrix: clean cwd + session NOT in the ignored set records normally + --- + duration_ms: 8.068874 + type: 'test' + ... +# Subtest: backfill skips an ignored session and yields only the clean one +ok 3103 - backfill skips an ignored session and yields only the clean one + --- + duration_ms: 6.497966 + type: 'test' + ... +# Subtest: backfill imports every session when none are ignored +ok 3104 - backfill imports every session when none are ignored + --- + duration_ms: 7.546781 + type: 'test' + ... +# Subtest: claude: the manifest and the registration code name the same skills +ok 3105 - claude: the manifest and the registration code name the same skills + --- + duration_ms: 152.464781 + type: 'test' + ... +# Subtest: claude: every registered skill exists on disk +ok 3106 - claude: every registered skill exists on disk + --- + duration_ms: 0.525679 + type: 'test' + ... +# Subtest: claude: every skill on disk is registered +ok 3107 - claude: every skill on disk is registered + --- + duration_ms: 0.399136 + type: 'test' + ... +# Subtest: claude: the manifest description does not name a skill it no longer ships +ok 3108 - claude: the manifest description does not name a skill it no longer ships + --- + duration_ms: 0.260746 + type: 'test' + ... +# Subtest: codex: the manifest and the registration code name the same skills +ok 3109 - codex: the manifest and the registration code name the same skills + --- + duration_ms: 12.1912 + type: 'test' + ... +# Subtest: codex: every registered skill exists on disk +ok 3110 - codex: every registered skill exists on disk + --- + duration_ms: 0.44056 + type: 'test' + ... +# Subtest: codex: every skill on disk is registered +ok 3111 - codex: every skill on disk is registered + --- + duration_ms: 0.349141 + type: 'test' + ... +# Subtest: codex: the manifest description does not name a skill it no longer ships +ok 3112 - codex: the manifest description does not name a skill it no longer ships + --- + duration_ms: 0.17808 + type: 'test' + ... +# Subtest: readCodexAuthMode returns an explicit auth_mode verbatim +ok 3113 - readCodexAuthMode returns an explicit auth_mode verbatim + --- + duration_ms: 8.851543 + type: 'test' + ... +# Subtest: readCodexAuthMode infers chatgpt from tokens without an API key +ok 3114 - readCodexAuthMode infers chatgpt from tokens without an API key + --- + duration_ms: 6.470605 + type: 'test' + ... +# Subtest: readCodexAuthMode does not infer chatgpt when an API key is present +ok 3115 - readCodexAuthMode does not infer chatgpt when an API key is present + --- + duration_ms: 1.181286 + type: 'test' + ... +# Subtest: readCodexAuthMode returns undefined for missing or malformed files +ok 3116 - readCodexAuthMode returns undefined for missing or malformed files + --- + duration_ms: 2.772163 + type: 'test' + ... +# Subtest: providerRouteForAuthMode maps chatgpt to the backend-api route +ok 3117 - providerRouteForAuthMode maps chatgpt to the backend-api route + --- + duration_ms: 0.738573 + type: 'test' + ... +# Subtest: provider advertises a stable contribution shape +ok 3118 - provider advertises a stable contribution shape + --- + duration_ms: 1.28986 + type: 'test' + ... +# Subtest: modern rollout projects into canonical ai_gateway_messages rows +ok 3119 - modern rollout projects into canonical ai_gateway_messages rows + --- + duration_ms: 16.135384 + type: 'test' + ... +# Subtest: backfill skips a session whose cwd is .hypignore-ignored +ok 3120 - backfill skips a session whose cwd is .hypignore-ignored + --- + duration_ms: 9.456733 + type: 'test' + ... +# Subtest: backfill is unaffected when a different cwd is ignored +ok 3121 - backfill is unaffected when a different cwd is ignored + --- + duration_ms: 9.212973 + type: 'test' + ... +# Subtest: a blank or relative rollout cwd never reaches the usage-policy gate +ok 3122 - a blank or relative rollout cwd never reaches the usage-policy gate + --- + duration_ms: 16.103646 + type: 'test' + ... +# Subtest: token_count event folds per-turn usage (net of cache) onto the turn assistant message +ok 3123 - token_count event folds per-turn usage (net of cache) onto the turn assistant message + --- + duration_ms: 15.991045 + type: 'test' + ... +# Subtest: multi-turn token_count: each turn stamps its own per-turn delta on its own last assistant row +ok 3124 - multi-turn token_count: each turn stamps its own per-turn delta on its own last assistant row + --- + duration_ms: 6.943294 + type: 'test' + ... +# Subtest: backfill redacts credential userinfo from the git remote (LLP 0032) +ok 3125 - backfill redacts credential userinfo from the git remote (LLP 0032) + --- + duration_ms: 6.191631 + type: 'test' + ... +# Subtest: a subagent rollout partitions on session_meta.session_id, with the thread in conversation_id +ok 3126 - a subagent rollout partitions on session_meta.session_id, with the thread in conversation_id + --- + duration_ms: 13.690558 + type: 'test' + ... +# Subtest: a rollout with no session_meta.session_id keeps the thread as its partition key +ok 3127 - a rollout with no session_meta.session_id keeps the thread as its partition key + --- + duration_ms: 4.04013 + type: 'test' + ... +# Subtest: native ids are preserved verbatim; sidechain inferred from thread_source +ok 3128 - native ids are preserved verbatim; sidechain inferred from thread_source + --- + duration_ms: 8.381458 + type: 'test' + ... +# Subtest: sessions are grouped one-per-file across nested date partitions +ok 3129 - sessions are grouped one-per-file across nested date partitions + --- + duration_ms: 5.249199 + type: 'test' + ... +# Subtest: legacy single-document rollouts parse version-defensively +ok 3130 - legacy single-document rollouts parse version-defensively + --- + duration_ms: 2.728136 + type: 'test' + ... +# Subtest: app/browser storage is flagged via unsupported_location, never parsed +ok 3131 - app/browser storage is flagged via unsupported_location, never parsed + --- + duration_ms: 6.903573 + type: 'test' + ... +# Subtest: encrypted reasoning is never projected; only plaintext summary is kept +ok 3132 - encrypted reasoning is never projected; only plaintext summary is kept + --- + duration_ms: 11.256138 + type: 'test' + ... +# Subtest: since bound filters out items older than the window +ok 3133 - since bound filters out items older than the window + --- + duration_ms: 5.479268 + type: 'test' + ... +# Subtest: reruns are deterministic (idempotent items and rows) +ok 3134 - reruns are deterministic (idempotent items and rows) + --- + duration_ms: 7.420599 + type: 'test' + ... +# Subtest: missing sessions root yields nothing without throwing +ok 3135 - missing sessions root yields nothing without throwing + --- + duration_ms: 3.510917 + type: 'test' + ... +# Subtest: diagnostic-only history source is detected but not used as canonical +ok 3136 - diagnostic-only history source is detected but not used as canonical + --- + duration_ms: 6.64526 + type: 'test' + ... +# Subtest: an unclassified interactive session prints a plain-text classification nag +ok 3137 - an unclassified interactive session prints a plain-text classification nag + --- + duration_ms: 2.659693 + type: 'test' + ... +# Subtest: no prompt -> no output +ok 3138 - no prompt -> no output + --- + duration_ms: 0.96758 + type: 'test' + ... +# Subtest: falls back to ctx.cwd when no event cwd is piped +ok 3139 - falls back to ctx.cwd when no event cwd is piped + --- + duration_ms: 0.528944 + type: 'test' + ... +# Subtest: an evaluation that throws is swallowed with exit 0 +ok 3140 - an evaluation that throws is swallowed with exit 0 + --- + duration_ms: 0.51316 + type: 'test' + ... +# Subtest: isInteractiveCodexSession honors CI and the escape hatch +ok 3141 - isInteractiveCodexSession honors CI and the escape hatch + --- + duration_ms: 0.128656 + type: 'test' + ... +# Subtest: validateCodexConfig accepts an empty / absent config +ok 3142 - validateCodexConfig accepts an empty / absent config + --- + duration_ms: 1.307977 + type: 'test' + ... +# Subtest: validateCodexConfig accepts a full backfill block +ok 3143 - validateCodexConfig accepts a full backfill block + --- + duration_ms: 0.182748 + type: 'test' + ... +# Subtest: validateCodexConfig rejects a non-object config +ok 3144 - validateCodexConfig rejects a non-object config + --- + duration_ms: 0.124459 + type: 'test' + ... +# Subtest: validateCodexConfig rejects a malformed backfill block +ok 3145 - validateCodexConfig rejects a malformed backfill block + --- + duration_ms: 0.228437 + type: 'test' + ... +# Subtest: validateBackfillSection mounts pointers under the supplied prefix +ok 3146 - validateBackfillSection mounts pointers under the supplied prefix + --- + duration_ms: 0.092761 + type: 'test' + ... +# Subtest: validateCodexConfig accepts an attach block +ok 3147 - validateCodexConfig accepts an attach block + --- + duration_ms: 0.168666 + type: 'test' + ... +# Subtest: validateCodexConfig rejects a malformed attach block +ok 3148 - validateCodexConfig rejects a malformed attach block + --- + duration_ms: 0.172392 + type: 'test' + ... +# Subtest: validateAttachSection mounts pointers under the supplied prefix +ok 3149 - validateAttachSection mounts pointers under the supplied prefix + --- + duration_ms: 0.101054 + type: 'test' + ... +# Subtest: the registered codex section drives validatePluginConfig +ok 3150 - the registered codex section drives validatePluginConfig + --- + duration_ms: 0.736139 + type: 'test' + ... +# Subtest: the codex picker names Codex Desktop, not just "Codex conversations" +ok 3151 - the codex picker names Codex Desktop, not just "Codex conversations" + --- + duration_ms: 3.942472 + type: 'test' + ... +# Subtest: the codex plugin description names both Codex surfaces +ok 3152 - the codex plugin description names both Codex surfaces + --- + duration_ms: 0.922552 + type: 'test' + ... +# Subtest: the Codex app-container unsupported_location says what IS still captured +ok 3153 - the Codex app-container unsupported_location says what IS still captured + --- + duration_ms: 5.751422 + type: 'test' + ... +# Subtest: project() returns no projection when the exchange cwd is .hypignore-ignored +ok 3154 - project() returns no projection when the exchange cwd is .hypignore-ignored + --- + duration_ms: 2.554493 + type: 'test' + ... +# Subtest: project() is unaffected when the exchange cwd is not ignored +ok 3155 - project() is unaffected when the exchange cwd is not ignored + --- + duration_ms: 1.017116 + type: 'test' + ... +# Subtest: project() emits a usage_policy_drop log on an ignored cwd +ok 3156 - project() emits a usage_policy_drop log on an ignored cwd + --- + duration_ms: 0.359416 + type: 'test' + ... +# Subtest: project() escalates a fail-safe clamp to a warn-level drop with the declared token (R3) +ok 3157 - project() escalates a fail-safe clamp to a warn-level drop with the declared token (R3) + --- + duration_ms: 0.435582 + type: 'test' + ... +# Subtest: project() computes no .hypignore verdict from a RELATIVE in-band cwd +ok 3158 - project() computes no .hypignore verdict from a RELATIVE in-band cwd + --- + duration_ms: 0.290001 + type: 'test' + ... +# Subtest: project() computes no .hypignore verdict from a BLANK in-band cwd +ok 3159 - project() computes no .hypignore verdict from a BLANK in-band cwd + --- + duration_ms: 0.212413 + type: 'test' + ... +# Subtest: project() logs an unusable in-band cwd rather than skipping the gate silently +ok 3160 - project() logs an unusable in-band cwd rather than skipping the gate silently + --- + duration_ms: 0.235077 + type: 'test' + ... +# Subtest: the in-band cwd seam answers exactly as the shared sessionMetaCwd predicate does +ok 3161 - the in-band cwd seam answers exactly as the shared sessionMetaCwd predicate does + --- + duration_ms: 0.851294 + type: 'test' + ... +# Subtest: project() drops every conversation_id thread under one ignored session_id (documents the over-drop, R8) +ok 3162 - project() drops every conversation_id thread under one ignored session_id (documents the over-drop, R8) + --- + duration_ms: 0.619191 + type: 'test' + ... +# Subtest: project() leaves a different session in the same run unaffected +ok 3163 - project() leaves a different session in the same run unaffected + --- + duration_ms: 0.752734 + type: 'test' + ... +# Subtest: project() emits a usage_policy_drop log with policy_source: session_opt_out and the matched session_id +ok 3164 - project() emits a usage_policy_drop log with policy_source: session_opt_out and the matched session_id + --- + duration_ms: 0.298454 + type: 'test' + ... +# Subtest: the session opt-out drop is also visible through the gateway message-projector dispatcher (parity with Claude) +ok 3165 - the session opt-out drop is also visible through the gateway message-projector dispatcher (parity with Claude) + --- + duration_ms: 0.54553 + type: 'test' + ... +# Subtest: match() accepts the three transports it owns and rejects others +ok 3166 - match() accepts the three transports it owns and rejects others + --- + duration_ms: 0.136327 + type: 'test' + ... +# Subtest: match() also accepts non-codex paths tagged with x-codex-turn-metadata +ok 3167 - match() also accepts non-codex paths tagged with x-codex-turn-metadata + --- + duration_ms: 0.115776 + type: 'test' + ... +# Subtest: OpenAI Chat projection: request+response messages roll up into user+assistant +ok 3168 - OpenAI Chat projection: request+response messages roll up into user+assistant + --- + duration_ms: 0.244411 + type: 'test' + ... +# Subtest: OpenAI Chat projection normalizes usage onto the assistant response +ok 3169 - OpenAI Chat projection normalizes usage onto the assistant response + --- + duration_ms: 0.360959 + type: 'test' + ... +# Subtest: OpenAI Chat tool messages map to tool_result blocks +ok 3170 - OpenAI Chat tool messages map to tool_result blocks + --- + duration_ms: 0.217791 + type: 'test' + ... +# Subtest: OpenAI Responses with output_text in the body produces an assistant message +ok 3171 - OpenAI Responses with output_text in the body produces an assistant message + --- + duration_ms: 1.335219 + type: 'test' + ... +# Subtest: OpenAI Responses body usage is normalized onto one assistant response item +ok 3172 - OpenAI Responses body usage is normalized onto one assistant response item + --- + duration_ms: 0.300176 + type: 'test' + ... +# Subtest: OpenAI Responses captures top-level instructions into system_text +ok 3173 - OpenAI Responses captures top-level instructions into system_text + --- + duration_ms: 0.224942 + type: 'test' + ... +# Subtest: OpenAI Chat system field still wins over instructions +ok 3174 - OpenAI Chat system field still wins over instructions + --- + duration_ms: 0.133243 + type: 'test' + ... +# Subtest: OpenAI Responses SSE deltas reconstruct the assistant body +ok 3175 - OpenAI Responses SSE deltas reconstruct the assistant body + --- + duration_ms: 0.359467 + type: 'test' + ... +# Subtest: OpenAI Responses SSE completed usage is normalized onto the assistant response +ok 3176 - OpenAI Responses SSE completed usage is normalized onto the assistant response + --- + duration_ms: 0.202297 + type: 'test' + ... +# Subtest: OpenAI Responses function_call in input becomes an assistant tool_use message +ok 3177 - OpenAI Responses function_call in input becomes an assistant tool_use message + --- + duration_ms: 0.23683 + type: 'test' + ... +# Subtest: OpenAI Responses bare-string content array entries project as text blocks +ok 3178 - OpenAI Responses bare-string content array entries project as text blocks + --- + duration_ms: 0.122025 + type: 'test' + ... +# Subtest: OpenAI Responses reasoning items in input replay project as assistant thinking +ok 3179 - OpenAI Responses reasoning items in input replay project as assistant thinking + --- + duration_ms: 0.246785 + type: 'test' + ... +# Subtest: OpenAI Responses custom_tool_call uses payload.input when arguments is missing +ok 3180 - OpenAI Responses custom_tool_call uses payload.input when arguments is missing + --- + duration_ms: 0.137929 + type: 'test' + ... +# Subtest: OpenAI Responses fans out response.output items into per-item assistant messages +ok 3181 - OpenAI Responses fans out response.output items into per-item assistant messages + --- + duration_ms: 0.138751 + type: 'test' + ... +# Subtest: OpenAI Responses turn-1 response shape matches turn-2 input replay shape (dedupe) +ok 3182 - OpenAI Responses turn-1 response shape matches turn-2 input replay shape (dedupe) + --- + duration_ms: 0.155006 + type: 'test' + ... +# Subtest: OpenAI Responses SSE captures tool_use from response.output_item.done +ok 3183 - OpenAI Responses SSE captures tool_use from response.output_item.done + --- + duration_ms: 0.260837 + type: 'test' + ... +# Subtest: OpenAI Responses SSE prefers full response.completed body when present +ok 3184 - OpenAI Responses SSE prefers full response.completed body when present + --- + duration_ms: 0.273616 + type: 'test' + ... +# Subtest: OpenAI Responses SSE merges streamed text into a tool-only completed body +ok 3185 - OpenAI Responses SSE merges streamed text into a tool-only completed body + --- + duration_ms: 0.194355 + type: 'test' + ... +# Subtest: Codex turn metadata + headers project into first-class columns and codex.* attributes +ok 3186 - Codex turn metadata + headers project into first-class columns and codex.* attributes + --- + duration_ms: 0.6249 + type: 'test' + ... +# Subtest: live projector redacts credential userinfo from the turn-metadata remote (LLP 0032) +ok 3187 - live projector redacts credential userinfo from the turn-metadata remote (LLP 0032) + --- + duration_ms: 0.347648 + type: 'test' + ... +# Subtest: thread_source=subagent flips is_sidechain to true +ok 3188 - thread_source=subagent flips is_sidechain to true + --- + duration_ms: 0.307007 + type: 'test' + ... +# Subtest: subagent turn metadata captures parent_thread_id (lineage) +ok 3189 - subagent turn metadata captures parent_thread_id (lineage) + --- + duration_ms: 0.219213 + type: 'test' + ... +# Subtest: Codex workspace selection prefers recorded cwd over first metadata key +ok 3190 - Codex workspace selection prefers recorded cwd over first metadata key + --- + duration_ms: 0.355831 + type: 'test' + ... +# Subtest: the .hypignore gate uses the request cwd, not a substituted workspace key (\#476 case a) +ok 3191 - the .hypignore gate uses the request cwd, not a substituted workspace key (\#476 case a) + --- + duration_ms: 0.288819 + type: 'test' + ... +# Subtest: an unrelated ignored workspace key does not drop a session it never covered (\#476 case b) +ok 3192 - an unrelated ignored workspace key does not drop a session it never covered (\#476 case b) + --- + duration_ms: 0.287928 + type: 'test' + ... +# Subtest: a refused workspace substitution is logged with hashed paths, not silently applied (\#476 case c) +ok 3193 - a refused workspace substitution is logged with hashed paths, not silently applied (\#476 case c) + --- + duration_ms: 0.28292 + type: 'test' + ... +# Subtest: a refused workspace substitution still enriches the row from the workspace key (\#476) +ok 3194 - a refused workspace substitution still enriches the row from the workspace key (\#476) + --- + duration_ms: 0.25006 + type: 'test' + ... +# Subtest: the workspace key still supplies the gate cwd when the request states none (\#476) +ok 3195 - the workspace key still supplies the gate cwd when the request states none (\#476) + --- + duration_ms: 0.200976 + type: 'test' + ... +# Subtest: no workspace-cwd refusal is logged when the key matches or the request states no cwd (\#476) +ok 3196 - no workspace-cwd refusal is logged when the key matches or the request states no cwd (\#476) + --- + duration_ms: 0.303341 + type: 'test' + ... +# Subtest: an ordinary subdirectory-of-workspace session logs no workspace-cwd refusal (\#481) +ok 3197 - an ordinary subdirectory-of-workspace session logs no workspace-cwd refusal (\#481) + --- + duration_ms: 1.24349 + type: 'test' + ... +# Subtest: a workspace key off the session cwd ancestry is still a refusal (\#481) +ok 3198 - a workspace key off the session cwd ancestry is still a refusal (\#481) + --- + duration_ms: 1.222238 + type: 'test' + ... +# Subtest: an ancestor key that resolves MORE restrictively than the cwd is silent (documents the residue, \#481) +ok 3199 - an ancestor key that resolves MORE restrictively than the cwd is silent (documents the residue, \#481) + --- + duration_ms: 1.544397 + type: 'test' + ... +# Subtest: non-codex provider has no codex turn metadata but still stamps identity_source for symmetry +ok 3200 - non-codex provider has no codex turn metadata but still stamps identity_source for symmetry + --- + duration_ms: 0.192663 + type: 'test' + ... +# Subtest: project() returns undefined when the request body is missing or malformed +ok 3201 - project() returns undefined when the request body is missing or malformed + --- + duration_ms: 0.136889 + type: 'test' + ... +# Subtest: project() returns undefined when no messages can be extracted +ok 3202 - project() returns undefined when no messages can be extracted + --- + duration_ms: 1.110127 + type: 'test' + ... +# Subtest: conversation_id falls back to a stable hash when no codex metadata or session id is present +ok 3203 - conversation_id falls back to a stable hash when no codex metadata or session id is present + --- + duration_ms: 0.133232 + type: 'test' + ... +# Subtest: Codex lineage resolves from the durable body client_metadata when no lineage header is sent +ok 3204 - Codex lineage resolves from the durable body client_metadata when no lineage header is sent + --- + duration_ms: 0.145661 + type: 'test' + ... +# Subtest: body client_metadata alone identifies a Codex exchange on a generic responses path +ok 3205 - body client_metadata alone identifies a Codex exchange on a generic responses path + --- + duration_ms: 0.240005 + type: 'test' + ... +# Subtest: Codex lineage resolves from the compatibility headers Codex actually sends +ok 3206 - Codex lineage resolves from the compatibility headers Codex actually sends + --- + duration_ms: 0.205833 + type: 'test' + ... +# Subtest: a bare lineage header name Codex never sends resolves to nothing, not a wrong value +ok 3207 - a bare lineage header name Codex never sends resolves to nothing, not a wrong value + --- + duration_ms: 0.421791 + type: 'test' + ... +# Subtest: body client_metadata wins over the turn-metadata blob when the two disagree +ok 3208 - body client_metadata wins over the turn-metadata blob when the two disagree + --- + duration_ms: 0.322901 + type: 'test' + ... +# Subtest: agreeing lineage surfaces record no lineage_conflict +ok 3209 - agreeing lineage surfaces record no lineage_conflict + --- + duration_ms: 0.184981 + type: 'test' + ... +# Subtest: a lineage field only one surface states is not a conflict +ok 3210 - a lineage field only one surface states is not a conflict + --- + duration_ms: 0.181746 + type: 'test' + ... +# Subtest: lineage_source names the surface the thread actually came from +ok 3211 - lineage_source names the surface the thread actually came from + --- + duration_ms: 0.183499 + type: 'test' + ... +# Subtest: a non-Codex client sending only a flat client_metadata identity pair is not treated as Codex +ok 3212 - a non-Codex client sending only a flat client_metadata identity pair is not treated as Codex + --- + duration_ms: 0.489754 + type: 'test' + ... +# Subtest: a transport-corroborated Codex request still resolves lineage from a flat-only client_metadata +ok 3213 - a transport-corroborated Codex request still resolves lineage from a flat-only client_metadata + --- + duration_ms: 0.154985 + type: 'test' + ... +# Subtest: every route Codex posts to is matched, so a body-only Codex request is never dropped at the gate +ok 3214 - every route Codex posts to is matched, so a body-only Codex request is never dropped at the gate + --- + duration_ms: 0.170419 + type: 'test' + ... +# Subtest: part_id and message_id stay byte-identical for the turn-metadata shape already recorded +ok 3215 - part_id and message_id stay byte-identical for the turn-metadata shape already recorded + --- + duration_ms: 1.851464 + type: 'test' + ... +# Subtest: strips user:token userinfo from an https remote +ok 3216 - strips user:token userinfo from an https remote + --- + duration_ms: 0.88822 + type: 'test' + ... +# Subtest: strips a token-only userinfo from an https remote +ok 3217 - strips a token-only userinfo from an https remote + --- + duration_ms: 0.18436 + type: 'test' + ... +# Subtest: strips userinfo from an ssh:// URL (key-auth, but harmless to drop) +ok 3218 - strips userinfo from an ssh:// URL (key-auth, but harmless to drop) + --- + duration_ms: 0.112661 + type: 'test' + ... +# Subtest: leaves the scp-like SSH form intact (git@ is the conventional user, no secret) +ok 3219 - leaves the scp-like SSH form intact (git@ is the conventional user, no secret) + --- + duration_ms: 1.25023 + type: 'test' + ... +# Subtest: leaves a credential-free https remote unchanged +ok 3220 - leaves a credential-free https remote unchanged + --- + duration_ms: 0.139412 + type: 'test' + ... +# Subtest: passes undefined / empty through unchanged +ok 3221 - passes undefined / empty through unchanged + --- + duration_ms: 0.110618 + type: 'test' + ... +# Subtest: does not mistake an @ in the path for userinfo +ok 3222 - does not mistake an @ in the path for userinfo + --- + duration_ms: 0.096617 + type: 'test' + ... +# Subtest: Step 1 sends the session container, never a thread id +ok 3223 - Step 1 sends the session container, never a thread id + --- + duration_ms: 0.852015 + type: 'test' + ... +# Subtest: Step 1 resolves the rollout by cwd and refuses rather than guessing +ok 3224 - Step 1 resolves the rollout by cwd and refuses rather than guessing + --- + duration_ms: 0.206574 + type: 'test' + ... +# Subtest: Step 1 reports the id as inferred and names both ways the opt-out lapses +ok 3225 - Step 1 reports the id as inferred and names both ways the opt-out lapses + --- + duration_ms: 1.040902 + type: 'test' + ... +# Subtest: subscription-route Codex with no in-band cwd is .hypignore-dropped via the rollout cwd +ok 3226 - subscription-route Codex with no in-band cwd is .hypignore-dropped via the rollout cwd + --- + duration_ms: 2.923714 + type: 'test' + ... +# Subtest: subscription-route Codex records the rollout cwd on the row (live/backfill parity) +ok 3227 - subscription-route Codex records the rollout cwd on the row (live/backfill parity) + --- + duration_ms: 0.994271 + type: 'test' + ... +# Subtest: an in-band cwd stays the fast path and short-circuits the rollout lookup +ok 3228 - an in-band cwd stays the fast path and short-circuits the rollout lookup + --- + duration_ms: 0.647124 + type: 'test' + ... +# Subtest: a workspaces key does not preempt the rollout session_meta.cwd (\#480) +ok 3229 - a workspaces key does not preempt the rollout session_meta.cwd (\#480) + --- + duration_ms: 0.425957 + type: 'test' + ... +# Subtest: the rollout cwd is stamped on the row while the workspace key still enriches it (\#480) +ok 3230 - the rollout cwd is stamped on the row while the workspace key still enriches it (\#480) + --- + duration_ms: 0.581123 + type: 'test' + ... +# Subtest: the workspace key still gates when there is no rollout to outrank it (\#480) +ok 3231 - the workspace key still gates when there is no rollout to outrank it (\#480) + --- + duration_ms: 0.383463 + type: 'test' + ... +# Subtest: a relative workspace key is refused rather than resolved against the daemon (\#480, \#471) +ok 3232 - a relative workspace key is refused rather than resolved against the daemon (\#480, \#471) + --- + duration_ms: 0.377394 + type: 'test' + ... +# Subtest: createRolloutCwdResolver reads session_meta.cwd from the session rollout +ok 3233 - createRolloutCwdResolver reads session_meta.cwd from the session rollout + --- + duration_ms: 10.904804 + type: 'test' + ... +# Subtest: createRolloutCwdResolver caches per session id (bounded fs on the hot path) +ok 3234 - createRolloutCwdResolver caches per session id (bounded fs on the hot path) + --- + duration_ms: 2.344674 + type: 'test' + ... +# Subtest: createRolloutCwdResolver returns undefined when the sessions root is missing +ok 3235 - createRolloutCwdResolver returns undefined when the sessions root is missing + --- + duration_ms: 0.413399 + type: 'test' + ... +# Subtest: a rollout whose first line is a different envelope type yields no cwd, even carrying one +ok 3236 - a rollout whose first line is a different envelope type yields no cwd, even carrying one + --- + duration_ms: 0.461111 + type: 'test' + ... +# Subtest: a blank session_meta.cwd is no cwd, not a blank path handed to the policy matcher +ok 3237 - a blank session_meta.cwd is no cwd, not a blank path handed to the policy matcher + --- + duration_ms: 0.397404 + type: 'test' + ... +# Subtest: a relative session_meta.cwd is no cwd: the matcher would resolve it against the daemon +ok 3238 - a relative session_meta.cwd is no cwd: the matcher would resolve it against the daemon + --- + duration_ms: 0.929763 + type: 'test' + ... +# Subtest: a missing-then-present rollout is re-resolved after the negative TTL, but a miss is cached within it +ok 3239 - a missing-then-present rollout is re-resolved after the negative TTL, but a miss is cached within it + --- + duration_ms: 1.94715 + type: 'test' + ... +# Subtest: a transient read error is retried rather than cached as a permanent miss +ok 3240 - a transient read error is retried rather than cached as a permanent miss + --- + duration_ms: 1.306466 + type: 'test' + ... +# Subtest: a newest-dir rollout is found without descending the older-date branch; an older-dir rollout still resolves +ok 3241 - a newest-dir rollout is found without descending the older-date branch; an older-dir rollout still resolves + --- + duration_ms: 3.780436 + type: 'test' + ... +# Subtest: a subagent turn is .hypignore-dropped by ITS OWN rollout cwd, not the root thread's +ok 3242 - a subagent turn is .hypignore-dropped by ITS OWN rollout cwd, not the root thread's + --- + duration_ms: 2.160944 + type: 'test' + ... +# Subtest: a subagent turn outside an ignored root is recorded, with its own cwd on the row +ok 3243 - a subagent turn outside an ignored root is recorded, with its own cwd on the row + --- + duration_ms: 4.11238 + type: 'test' + ... +# Subtest: the root thread of the same session still resolves its own cwd +ok 3244 - the root thread of the same session still resolves its own cwd + --- + duration_ms: 3.387588 + type: 'test' + ... +# Subtest: a legacy rollout pair with no session_id still resolves each thread's own cwd +ok 3245 - a legacy rollout pair with no session_id still resolves each thread's own cwd + --- + duration_ms: 3.731372 + type: 'test' + ... +# Subtest: a rollout whose body disagrees with its filename is refused, not silently used +ok 3246 - a rollout whose body disagrees with its filename is refused, not silently used + --- + duration_ms: 1.05951 + type: 'test' + ... +# Subtest: a subagent turn that states lineage but not its own thread id resolves no cwd +ok 3247 - a subagent turn that states lineage but not its own thread id resolves no cwd + --- + duration_ms: 1.449843 + type: 'test' + ... +# Subtest: a turn whose metadata states thread_source=subagent but no thread id resolves no cwd +ok 3248 - a turn whose metadata states thread_source=subagent but no thread id resolves no cwd + --- + duration_ms: 2.695286 + type: 'test' + ... +# Subtest: a turn stating lineage only via x-codex-parent-thread-id resolves no cwd +ok 3249 - a turn stating lineage only via x-codex-parent-thread-id resolves no cwd + --- + duration_ms: 1.720616 + type: 'test' + ... +# Subtest: a turn stating lineage only via x-openai-subagent resolves no cwd +ok 3250 - a turn stating lineage only via x-openai-subagent resolves no cwd + --- + duration_ms: 2.073652 + type: 'test' + ... +# Subtest: DOCUMENTED MIRROR: x-openai-subagent=review with no thread id refuses a container the root would have resolved +ok 3251 - DOCUMENTED MIRROR: x-openai-subagent=review with no thread id refuses a container the root would have resolved + --- + duration_ms: 3.472649 + type: 'test' + ... +# Subtest: x-openai-subagent=review does NOT cost anything once the turn states its thread +ok 3252 - x-openai-subagent=review does NOT cost anything once the turn states its thread + --- + duration_ms: 2.286295 + type: 'test' + ... +# Subtest: DOCUMENTED MIRROR: x-openai-subagent=compact with no thread id refuses a container the root would have resolved +ok 3253 - DOCUMENTED MIRROR: x-openai-subagent=compact with no thread id refuses a container the root would have resolved + --- + duration_ms: 1.566501 + type: 'test' + ... +# Subtest: x-openai-subagent=compact does NOT cost anything once the turn states its thread +ok 3254 - x-openai-subagent=compact does NOT cost anything once the turn states its thread + --- + duration_ms: 3.12516 + type: 'test' + ... +# Subtest: DOCUMENTED MIRROR: x-openai-subagent=memory_consolidation with no thread id refuses a container the root would have resolved +ok 3255 - DOCUMENTED MIRROR: x-openai-subagent=memory_consolidation with no thread id refuses a container the root would have resolved + --- + duration_ms: 2.01978 + type: 'test' + ... +# Subtest: x-openai-subagent=memory_consolidation does NOT cost anything once the turn states its thread +ok 3256 - x-openai-subagent=memory_consolidation does NOT cost anything once the turn states its thread + --- + duration_ms: 1.817102 + type: 'test' + ... +# Subtest: a memory-consolidation turn is answered by the body map, whose id pair the blob withholds +ok 3257 - a memory-consolidation turn is answered by the body map, whose id pair the blob withholds + --- + duration_ms: 1.653413 + type: 'test' + ... +# Subtest: a session_meta line with a cwd but no payload.id is refused, not matched by its filename +ok 3258 - a session_meta line with a cwd but no payload.id is refused, not matched by its filename + --- + duration_ms: 1.198201 + type: 'test' + ... +# Subtest: DOCUMENTED GAP: a turn stating a container and NO lineage at all is taken as its root thread +ok 3259 - DOCUMENTED GAP: a turn stating a container and NO lineage at all is taken as its root thread + --- + duration_ms: 2.333867 + type: 'test' + ... +# Subtest: prepareAttach inserts managed Codex provider blocks and preserves previous provider +ok 3260 - prepareAttach inserts managed Codex provider blocks and preserves previous provider + --- + duration_ms: 3.224491 + type: 'test' + ... +# Subtest: prepareAttach is idempotent for an already managed config +ok 3261 - prepareAttach is idempotent for an already managed config + --- + duration_ms: 0.404455 + type: 'test' + ... +# Subtest: prepareAttach records the prior model_provider in the marked block undo record +ok 3262 - prepareAttach records the prior model_provider in the marked block undo record + --- + duration_ms: 0.323762 + type: 'test' + ... +# Subtest: re-attach keeps the original previous_model_provider, not the managed one +ok 3263 - re-attach keeps the original previous_model_provider, not the managed one + --- + duration_ms: 0.277451 + type: 'test' + ... +# Subtest: prepareDetach removes managed Codex blocks and restores previous provider +ok 3264 - prepareDetach removes managed Codex blocks and restores previous provider + --- + duration_ms: 0.451026 + type: 'test' + ... +# Subtest: prepareDetach is a no-op when HypAware did not manage the config +ok 3265 - prepareDetach is a no-op when HypAware did not manage the config + --- + duration_ms: 0.623558 + type: 'test' + ... +# Subtest: managed marker parsing rejects unterminated blocks +ok 3266 - managed marker parsing rejects unterminated blocks + --- + duration_ms: 0.429373 + type: 'test' + ... +# Subtest: complete sends x-api-key + anthropic-version, lifts system, maps tools, parses blocks +ok 3267 - complete sends x-api-key + anthropic-version, lifts system, maps tools, parses blocks + --- + duration_ms: 2.576707 + type: 'test' + ... +# Subtest: complete picks per-request model over the provider default (tiering) +ok 3268 - complete picks per-request model over the provider default (tiering) + --- + duration_ms: 0.404245 + type: 'test' + ... +# Subtest: complete merges params (thinking/output_config) and lifts betas to the anthropic-beta header +ok 3269 - complete merges params (thinking/output_config) and lifts betas to the anthropic-beta header + --- + duration_ms: 0.32865 + type: 'test' + ... +# Subtest: complete returns a refusal as stopReason without throwing (HTTP 200) +ok 3270 - complete returns a refusal as stopReason without throwing (HTTP 200) + --- + duration_ms: 0.261798 + type: 'test' + ... +# Subtest: complete translates the neutral toolChoice to the Anthropic shape +ok 3271 - complete translates the neutral toolChoice to the Anthropic shape + --- + duration_ms: 0.714226 + type: 'test' + ... +# Subtest: complete without the env var sends no x-api-key (localhost proxies) +ok 3272 - complete without the env var sends no x-api-key (localhost proxies) + --- + duration_ms: 0.248858 + type: 'test' + ... +# Subtest: complete maps a 401 without a key to a hint and never leaks the key or provider body +ok 3273 - complete maps a 401 without a key to a hint and never leaks the key or provider body + --- + duration_ms: 4.853347 + type: 'test' + ... +# Subtest: complete rejects an empty messages array +ok 3274 - complete rejects an empty messages array + --- + duration_ms: 0.272394 + type: 'test' + ... +# Subtest: stream yields text deltas then a terminal stopReason + usage +ok 3275 - stream yields text deltas then a terminal stopReason + usage + --- + duration_ms: 1.261507 + type: 'test' + ... +# Subtest: parseAnthropicMessageResponse rejects a payload with no content array +ok 3276 - parseAnthropicMessageResponse rejects a payload with no content array + --- + duration_ms: 0.413969 + type: 'test' + ... +# Subtest: batch.submit posts {requests:[{custom_id,params}]} to /v1/messages/batches with auth +ok 3277 - batch.submit posts {requests:[{custom_id,params}]} to /v1/messages/batches with auth + --- + duration_ms: 1.099521 + type: 'test' + ... +# Subtest: batch.poll returns the normalized status with counts +ok 3278 - batch.poll returns the normalized status with counts + --- + duration_ms: 0.315851 + type: 'test' + ... +# Subtest: batch.results normalizes succeeded (incl. refusal) and surfaces only the error category +ok 3279 - batch.results normalizes succeeded (incl. refusal) and surfaces only the error category + --- + duration_ms: 0.666583 + type: 'test' + ... +# Subtest: batch.results returns [] while the job is still in progress (caller polls again) +ok 3280 - batch.results returns [] while the job is still in progress (caller polls again) + --- + duration_ms: 0.220886 + type: 'test' + ... +# Subtest: batch.submit rejects an empty request list +ok 3281 - batch.submit rejects an empty request list + --- + duration_ms: 0.176899 + type: 'test' + ... +# Subtest: validateAnthropicCompletionConfig defaults to Anthropic with ANTHROPIC_API_KEY and Opus +ok 3282 - validateAnthropicCompletionConfig defaults to Anthropic with ANTHROPIC_API_KEY and Opus + --- + duration_ms: 0.57868 + type: 'test' + ... +# Subtest: validateAnthropicCompletionConfig accepts a proxy/localhost override +ok 3283 - validateAnthropicCompletionConfig accepts a proxy/localhost override + --- + duration_ms: 0.161465 + type: 'test' + ... +# Subtest: validateAnthropicCompletionConfig rejects a non-object config +ok 3284 - validateAnthropicCompletionConfig rejects a non-object config + --- + duration_ms: 0.103638 + type: 'test' + ... +# Subtest: validateAnthropicCompletionConfig rejects a non-http base_url +ok 3285 - validateAnthropicCompletionConfig rejects a non-http base_url + --- + duration_ms: 0.791473 + type: 'test' + ... +# Subtest: validateAnthropicCompletionConfig rejects non-positive numeric fields +ok 3286 - validateAnthropicCompletionConfig rejects non-positive numeric fields + --- + duration_ms: 0.208927 + type: 'test' + ... +# Subtest: messagesEndpoint appends /v1/messages to a bare origin and does not double /v1 +ok 3287 - messagesEndpoint appends /v1/messages to a bare origin and does not double /v1 + --- + duration_ms: 0.124089 + type: 'test' + ... +# Subtest: complete sends Bearer key, leading system message, function tools; parses content + tool_calls +ok 3288 - complete sends Bearer key, leading system message, function tools; parses content + tool_calls + --- + duration_ms: 2.986609 + type: 'test' + ... +# Subtest: complete uses the per-request model over the default (tiering) +ok 3289 - complete uses the per-request model over the default (tiering) + --- + duration_ms: 0.363783 + type: 'test' + ... +# Subtest: complete passes responseFormat and params (tool_choice) through +ok 3290 - complete passes responseFormat and params (tool_choice) through + --- + duration_ms: 0.304763 + type: 'test' + ... +# Subtest: complete translates the neutral toolChoice to OpenAI shape (wins over params) +ok 3291 - complete translates the neutral toolChoice to OpenAI shape (wins over params) + --- + duration_ms: 0.634825 + type: 'test' + ... +# Subtest: complete without the env var sends no Authorization (localhost servers) +ok 3292 - complete without the env var sends no Authorization (localhost servers) + --- + duration_ms: 0.32278 + type: 'test' + ... +# Subtest: complete maps a 401 without a key to a hint and never leaks key or provider body +ok 3293 - complete maps a 401 without a key to a hint and never leaks key or provider body + --- + duration_ms: 0.73699 + type: 'test' + ... +# Subtest: complete rejects an empty messages array +ok 3294 - complete rejects an empty messages array + --- + duration_ms: 0.261928 + type: 'test' + ... +# Subtest: stream yields text deltas then a terminal stopReason + usage, stopping at [DONE] +ok 3295 - stream yields text deltas then a terminal stopReason + usage, stopping at [DONE] + --- + duration_ms: 0.66442 + type: 'test' + ... +# Subtest: parseOpenAiChatResponse rejects a payload with no choices +ok 3296 - parseOpenAiChatResponse rejects a payload with no choices + --- + duration_ms: 0.319235 + type: 'test' + ... +# Subtest: validateOpenAiCompletionConfig defaults to OpenAI with OPENAI_API_KEY +ok 3297 - validateOpenAiCompletionConfig defaults to OpenAI with OPENAI_API_KEY + --- + duration_ms: 0.604098 + type: 'test' + ... +# Subtest: validateOpenAiCompletionConfig accepts an Ollama-shaped localhost override +ok 3298 - validateOpenAiCompletionConfig accepts an Ollama-shaped localhost override + --- + duration_ms: 0.132871 + type: 'test' + ... +# Subtest: validateOpenAiCompletionConfig rejects a non-object config +ok 3299 - validateOpenAiCompletionConfig rejects a non-object config + --- + duration_ms: 0.129336 + type: 'test' + ... +# Subtest: validateOpenAiCompletionConfig rejects a non-http base_url +ok 3300 - validateOpenAiCompletionConfig rejects a non-http base_url + --- + duration_ms: 0.095536 + type: 'test' + ... +# Subtest: chatCompletionsEndpoint appends /v1/chat/completions and does not double /v1 +ok 3301 - chatCompletionsEndpoint appends /v1/chat/completions and does not double /v1 + --- + duration_ms: 0.154625 + type: 'test' + ... +# Subtest: activate provides the context-graph capability and registers node/edge datasets, the graph commands + graph_neighbors verb, its group help, and no skill +ok 3302 - activate provides the context-graph capability and registers node/edge datasets, the graph commands + graph_neighbors verb, its group help, and no skill + --- + duration_ms: 1.420038 + type: 'test' + ... +# Subtest: graph project with no contracts registered reports cleanly and exits 0 +ok 3303 - graph project with no contracts registered reports cleanly and exits 0 + --- + duration_ms: 2.250621 + type: 'test' + ... +# Subtest: graph project --source with no matching contract reports cleanly and exits 0 +ok 3304 - graph project --source with no matching contract reports cleanly and exits 0 + --- + duration_ms: 0.203499 + type: 'test' + ... +# Subtest: graph project --source= (equals form) parses and filters +ok 3305 - graph project --source= (equals form) parses and filters + --- + duration_ms: 0.154144 + type: 'test' + ... +# Subtest: graph project usage errors exit 2 and report the offending argv on stderr +ok 3306 - graph project usage errors exit 2 and report the offending argv on stderr + --- + duration_ms: 0.39456 + type: 'test' + ... +# Subtest: usage errors exit 2 and report the offending argv on stderr +ok 3307 - usage errors exit 2 and report the offending argv on stderr + --- + duration_ms: 1.364734 + type: 'test' + ... +# Subtest: an unresolved seed exits 1 with a not-found note on stderr +ok 3308 - an unresolved seed exits 1 with a not-found note on stderr + --- + duration_ms: 63.854412 + type: 'test' + ... +# Subtest: an ambiguous seed exits 1 and lists the candidates on stderr +ok 3309 - an ambiguous seed exits 1 and lists the candidates on stderr + --- + duration_ms: 28.40991 + type: 'test' + ... +# Subtest: a resolved seed exits 0 and renders neighbors on stdout +ok 3310 - a resolved seed exits 0 and renders neighbors on stdout + --- + duration_ms: 34.640861 + type: 'test' + ... +# Subtest: --limit truncates and reports the true total on stdout (not stderr) +ok 3311 - --limit truncates and reports the true total on stdout (not stderr) + --- + duration_ms: 23.92223 + type: 'test' + ... +# Subtest: --json emits the structured result on stdout +ok 3312 - --json emits the structured result on stdout + --- + duration_ms: 26.412374 + type: 'test' + ... +# Subtest: a synced caller context suppresses graph content, says so on stderr, and --include-local-only restores it +ok 3313 - a synced caller context suppresses graph content, says so on stderr, and --include-local-only restores it + --- + duration_ms: 21.438484 + type: 'test' + ... +# Subtest: TEXT renderer disambiguates two Files sharing a basename into distinct rows +ok 3314 - TEXT renderer disambiguates two Files sharing a basename into distinct rows + --- + duration_ms: 0.403243 + type: 'test' + ... +# Subtest: TEXT renderer keeps deep same-suffix Files distinct when the path tail is truncated +ok 3315 - TEXT renderer keeps deep same-suffix Files distinct when the path tail is truncated + --- + duration_ms: 0.172472 + type: 'test' + ... +# Subtest: TEXT renderer breaks the tie with the full node_id when shortId prefixes also collide +ok 3316 - TEXT renderer breaks the tie with the full node_id when shortId prefixes also collide + --- + duration_ms: 0.169007 + type: 'test' + ... +# Subtest: TEXT renderer leaves a non-colliding label readable (no disambiguator) +ok 3317 - TEXT renderer leaves a non-colliding label readable (no disambiguator) + --- + duration_ms: 0.200595 + type: 'test' + ... +# Subtest: --json output is unchanged by the collision: node.natural_key is the path, labels untouched +ok 3318 - --json output is unchanged by the collision: node.natural_key is the path, labels untouched + --- + duration_ms: 0.930424 + type: 'test' + ... +# Subtest: node: every declared content column is a real schema column +ok 3319 - node: every declared content column is a real schema column + --- + duration_ms: 0.778113 + type: 'test' + ... +# Subtest: node: content and structural allowlists are disjoint +ok 3320 - node: content and structural allowlists are disjoint + --- + duration_ms: 0.570787 + type: 'test' + ... +# Subtest: node: every schema column is classified as content or structural +ok 3321 - node: every schema column is classified as content or structural + --- + duration_ms: 0.189619 + type: 'test' + ... +# Subtest: edge: every declared content column is a real schema column +ok 3322 - edge: every declared content column is a real schema column + --- + duration_ms: 0.088574 + type: 'test' + ... +# Subtest: edge: content and structural allowlists are disjoint +ok 3323 - edge: content and structural allowlists are disjoint + --- + duration_ms: 0.086752 + type: 'test' + ... +# Subtest: edge: every schema column is classified as content or structural +ok 3324 - edge: every schema column is classified as content or structural + --- + duration_ms: 0.141014 + type: 'test' + ... +# Subtest: makeRowBuilders stamps provenance from the passed metadata +ok 3325 - makeRowBuilders stamps provenance from the passed metadata + --- + duration_ms: 1.350283 + type: 'test' + ... +# Subtest: makeRowBuilders normalizes first_seen and prunes empty props to null +ok 3326 - makeRowBuilders normalizes first_seen and prunes empty props to null + --- + duration_ms: 0.420388 + type: 'test' + ... +# Subtest: buildEdge passes props through, prunes empty to null, and leaves absent props null +ok 3327 - buildEdge passes props through, prunes empty to null, and leaves absent props null + --- + duration_ms: 0.153623 + type: 'test' + ... +# Subtest: edge ids are stable across presence/absence of props (ids hash src/type/dst only) +ok 3328 - edge ids are stable across presence/absence of props (ids hash src/type/dst only) + --- + duration_ms: 0.151289 + type: 'test' + ... +# Subtest: the id recipe is source-agnostic - same (type, key) converges across sources +ok 3329 - the id recipe is source-agnostic - same (type, key) converges across sources + --- + duration_ms: 0.14455 + type: 'test' + ... +# Subtest: contract registry registers contracts and lists them name-sorted +ok 3330 - contract registry registers contracts and lists them name-sorted + --- + duration_ms: 4.870212 + type: 'test' + ... +# Subtest: contract registry rejects malformed contracts +ok 3331 - contract registry rejects malformed contracts + --- + duration_ms: 0.479089 + type: 'test' + ... +# Subtest: contract registry rejects malformed rules, naming the offending rule index +ok 3332 - contract registry rejects malformed rules, naming the offending rule index + --- + duration_ms: 0.273877 + type: 'test' + ... +# Subtest: contract registry validates the declarative rule form +ok 3333 - contract registry validates the declarative rule form + --- + duration_ms: 1.516385 + type: 'test' + ... +# Subtest: contract registry validates rowFilter, and raw sql must select its columns +ok 3334 - contract registry validates rowFilter, and raw sql must select its columns + --- + duration_ms: 0.785344 + type: 'test' + ... +# Subtest: contract registry rejects a duplicate (plugin, name) +ok 3335 - contract registry rejects a duplicate (plugin, name) + --- + duration_ms: 0.165792 + type: 'test' + ... +# Subtest: pollUntilEnded polls until the job ends, reporting progress +ok 3336 - pollUntilEnded polls until the job ends, reporting progress + --- + duration_ms: 4.359976 + type: 'test' + ... +# Subtest: runCurateBatch submits the whole pool as a batch, collects, and routes the results +ok 3337 - runCurateBatch submits the whole pool as a batch, collects, and routes the results + --- + duration_ms: 4.698872 + type: 'test' + ... +# Subtest: runCurateBatch falls back to a synchronous tick when the provider has no batch API +ok 3338 - runCurateBatch falls back to a synchronous tick when the provider has no batch API + --- + duration_ms: 0.71734 + type: 'test' + ... +# Subtest: runCurateBatch --dry-run reports the scoped pool + clusters and writes nothing +ok 3339 - runCurateBatch --dry-run reports the scoped pool + clusters and writes nothing + --- + duration_ms: 0.462192 + type: 'test' + ... +# Subtest: runCurateBatch --dry-run scopes the pool to --since anchorKeys +ok 3340 - runCurateBatch --dry-run scopes the pool to --since anchorKeys + --- + duration_ms: 1.793075 + type: 'test' + ... +# Subtest: runCurateBatch resumes a pre-persisted backfill job (collects, does not re-submit) +ok 3341 - runCurateBatch resumes a pre-persisted backfill job (collects, does not re-submit) + --- + duration_ms: 4.312625 + type: 'test' + ... +# Subtest: runCurateBatch refuses to run while a daemon curate job is in flight (no clobber) +ok 3342 - runCurateBatch refuses to run while a daemon curate job is in flight (no clobber) + --- + duration_ms: 1.412367 + type: 'test' + ... +# Subtest: a daemon tick (collectCurateJob) leaves a backfill job untouched +ok 3343 - a daemon tick (collectCurateJob) leaves a backfill job untouched + --- + duration_ms: 0.846486 + type: 'test' + ... +# Subtest: submitCurateJob records the job in the sidecar without collecting; collectCurateJob routes it once ended +ok 3344 - submitCurateJob records the job in the sidecar without collecting; collectCurateJob routes it once ended + --- + duration_ms: 3.080291 + type: 'test' + ... +# Subtest: submitCurateJob is a no-op while a job is already in flight +ok 3345 - submitCurateJob is a no-op while a job is already in flight + --- + duration_ms: 3.767556 + type: 'test' + ... +# Subtest: collectCurateJob waits (no append, job kept) while the batch is still running +ok 3346 - collectCurateJob waits (no append, job kept) while the batch is still running + --- + duration_ms: 1.19746 + type: 'test' + ... +# Subtest: submitCurateJob writes skip resolutions but submits no batch when everything is below salience +ok 3347 - submitCurateJob writes skip resolutions but submits no batch when everything is below salience + --- + duration_ms: 0.582485 + type: 'test' + ... +# Subtest: parseBackfillArgv: bare argv runs both phases +ok 3348 - parseBackfillArgv: bare argv runs both phases + --- + duration_ms: 1.464937 + type: 'test' + ... +# Subtest: parseBackfillArgv: --propose-only selects only propose +ok 3349 - parseBackfillArgv: --propose-only selects only propose + --- + duration_ms: 0.143498 + type: 'test' + ... +# Subtest: parseBackfillArgv: --curate-only selects only curate +ok 3350 - parseBackfillArgv: --curate-only selects only curate + --- + duration_ms: 0.124369 + type: 'test' + ... +# Subtest: parseBackfillArgv: --since scopes the curate pool +ok 3351 - parseBackfillArgv: --since scopes the curate pool + --- + duration_ms: 0.169307 + type: 'test' + ... +# Subtest: parseBackfillArgv: --since= (equals form) is accepted +ok 3352 - parseBackfillArgv: --since= (equals form) is accepted + --- + duration_ms: 0.271773 + type: 'test' + ... +# Subtest: parseBackfillArgv: --dry-run is parsed +ok 3353 - parseBackfillArgv: --dry-run is parsed + --- + duration_ms: 0.127865 + type: 'test' + ... +# Subtest: parseBackfillArgv: a malformed --since date is rejected +ok 3354 - parseBackfillArgv: a malformed --since date is rejected + --- + duration_ms: 0.265995 + type: 'test' + ... +# Subtest: parseBackfillArgv: --since requires a value +ok 3355 - parseBackfillArgv: --since requires a value + --- + duration_ms: 1.384123 + type: 'test' + ... +# Subtest: parseBackfillArgv: --since does not apply to --propose-only +ok 3356 - parseBackfillArgv: --since does not apply to --propose-only + --- + duration_ms: 0.314688 + type: 'test' + ... +# Subtest: parseBackfillArgv: the two phase flags are mutually exclusive +ok 3357 - parseBackfillArgv: the two phase flags are mutually exclusive + --- + duration_ms: 0.349972 + type: 'test' + ... +# Subtest: parseBackfillArgv: an unknown flag is rejected +ok 3358 - parseBackfillArgv: an unknown flag is rejected + --- + duration_ms: 0.163959 + type: 'test' + ... +# Subtest: parseBackfillArgv: a stray positional is rejected (backfill takes no argument) +ok 3359 - parseBackfillArgv: a stray positional is rejected (backfill takes no argument) + --- + duration_ms: 0.103377 + type: 'test' + ... +# Subtest: parseBackfillArgv: an unknown flag is reported even alongside a valid flag +ok 3360 - parseBackfillArgv: an unknown flag is reported even alongside a valid flag + --- + duration_ms: 0.091429 + type: 'test' + ... +# Subtest: inWindowSessions includes a session at exactly the --since UTC midnight and excludes one a second before +ok 3361 - inWindowSessions includes a session at exactly the --since UTC midnight and excludes one a second before + --- + duration_ms: 0.626412 + type: 'test' + ... +# Subtest: inWindowSessions accepts string and epoch-millis timestamps and drops unparseable ones +ok 3362 - inWindowSessions accepts string and epoch-millis timestamps and drops unparseable ones + --- + duration_ms: 0.157289 + type: 'test' + ... +# Subtest: validateEnrichConfig fills source + tier defaults +ok 3363 - validateEnrichConfig fills source + tier defaults + --- + duration_ms: 2.843252 + type: 'test' + ... +# Subtest: validateEnrichConfig accepts the clustering overrides +ok 3364 - validateEnrichConfig accepts the clustering overrides + --- + duration_ms: 0.217881 + type: 'test' + ... +# Subtest: validateEnrichConfig accepts the regime selector overrides +ok 3365 - validateEnrichConfig accepts the regime selector overrides + --- + duration_ms: 0.143608 + type: 'test' + ... +# Subtest: validateEnrichConfig rejects a non-positive settle_cutoff_minutes +ok 3366 - validateEnrichConfig rejects a non-positive settle_cutoff_minutes + --- + duration_ms: 0.153453 + type: 'test' + ... +# Subtest: validateEnrichConfig accepts overrides incl. recall_index +ok 3367 - validateEnrichConfig accepts overrides incl. recall_index + --- + duration_ms: 0.257652 + type: 'test' + ... +# Subtest: validateEnrichConfig rejects a non-object config +ok 3368 - validateEnrichConfig rejects a non-object config + --- + duration_ms: 0.123888 + type: 'test' + ... +# Subtest: validateEnrichConfig rejects an out-of-range confidence_floor +ok 3369 - validateEnrichConfig rejects an out-of-range confidence_floor + --- + duration_ms: 0.205362 + type: 'test' + ... +# Subtest: validateEnrichConfig rejects a non-positive interval +ok 3370 - validateEnrichConfig rejects a non-positive interval + --- + duration_ms: 0.137399 + type: 'test' + ... +# Subtest: validateEnrichConfig rejects a column name that is not a SQL identifier +ok 3371 - validateEnrichConfig rejects a column name that is not a SQL identifier + --- + duration_ms: 0.433339 + type: 'test' + ... +# Subtest: validateEnrichConfig accepts a custom tiebreak_column +ok 3372 - validateEnrichConfig accepts a custom tiebreak_column + --- + duration_ms: 0.428531 + type: 'test' + ... +# Subtest: validateEnrichConfig accepts row-selection overrides incl. an empty exclude list +ok 3373 - validateEnrichConfig accepts row-selection overrides incl. an empty exclude list + --- + duration_ms: 0.259855 + type: 'test' + ... +# Subtest: validateEnrichConfig gates the default tool_result filter to the default source schema +ok 3374 - validateEnrichConfig gates the default tool_result filter to the default source schema + --- + duration_ms: 0.186684 + type: 'test' + ... +# Subtest: validateEnrichConfig rejects a non-string-array exclude_part_types +ok 3375 - validateEnrichConfig rejects a non-string-array exclude_part_types + --- + duration_ms: 0.120232 + type: 'test' + ... +# Subtest: validateEnrichConfig rejects a part_type_column that is not a SQL identifier +ok 3376 - validateEnrichConfig rejects a part_type_column that is not a SQL identifier + --- + duration_ms: 0.123428 + type: 'test' + ... +# Subtest: validateEnrichConfig rejects a non-boolean require_text +ok 3377 - validateEnrichConfig rejects a non-boolean require_text + --- + duration_ms: 0.112371 + type: 'test' + ... +# Subtest: contract reads only the committed dataset +ok 3378 - contract reads only the committed dataset + --- + duration_ms: 0.817593 + type: 'test' + ... +# Subtest: node rule maps item_type→type, item_id→key, folds confidence into props +ok 3379 - node rule maps item_type→type, item_id→key, folds confidence into props + --- + duration_ms: 0.720004 + type: 'test' + ... +# Subtest: edge rule links the anchor (Session) to the enrichment node via produced +ok 3380 - edge rule links the anchor (Session) to the enrichment node via produced + --- + duration_ms: 0.19763 + type: 'test' + ... +# Subtest: toRow parses a JSON-string props column (engine may return JSON as text) +ok 3381 - toRow parses a JSON-string props column (engine may return JSON as text) + --- + duration_ms: 0.166433 + type: 'test' + ... +# Subtest: toRow returns null when required fields are missing +ok 3382 - toRow returns null when required fields are missing + --- + duration_ms: 0.143568 + type: 'test' + ... +# Subtest: routeDecision commit writes a committed row + a resolution, never rejected/merged +ok 3383 - routeDecision commit writes a committed row + a resolution, never rejected/merged + --- + duration_ms: 1.584298 + type: 'test' + ... +# Subtest: routeDecision commit reuses an explicit item_key (convergence) over the label +ok 3384 - routeDecision commit reuses an explicit item_key (convergence) over the label + --- + duration_ms: 0.154635 + type: 'test' + ... +# Subtest: routeDecision deepen also commits an item +ok 3385 - routeDecision deepen also commits an item + --- + duration_ms: 0.143729 + type: 'test' + ... +# Subtest: routeDecision reject commits nothing - a rejected prospect never reaches the graph +ok 3386 - routeDecision reject commits nothing - a rejected prospect never reaches the graph + --- + duration_ms: 0.142897 + type: 'test' + ... +# Subtest: routeDecision treats an omitted decision as an implicit reject +ok 3387 - routeDecision treats an omitted decision as an implicit reject + --- + duration_ms: 0.131199 + type: 'test' + ... +# Subtest: routeDecision merge writes a committed row under the canonical key with the merging session anchor +ok 3388 - routeDecision merge writes a committed row under the canonical key with the merging session anchor + --- + duration_ms: 0.229148 + type: 'test' + ... +# Subtest: routeDecision leaves an under-specified merge pending (no commit, no resolution) - avoids mis-routing the produced edge +ok 3389 - routeDecision leaves an under-specified merge pending (no commit, no resolution) - avoids mis-routing the produced edge + --- + duration_ms: 0.120924 + type: 'test' + ... +# Subtest: routeDecision leaves a merge missing merge_into pending too +ok 3390 - routeDecision leaves a merge missing merge_into pending too + --- + duration_ms: 0.109096 + type: 'test' + ... +# Subtest: cosine: identical → 1, orthogonal → 0, zero vector → 0 +ok 3391 - cosine: identical → 1, orthogonal → 0, zero vector → 0 + --- + duration_ms: 0.282229 + type: 'test' + ... +# Subtest: greedyCosineClusters groups near-duplicates and separates distinct ones, deterministically +ok 3392 - greedyCosineClusters groups near-duplicates and separates distinct ones, deterministically + --- + duration_ms: 0.399186 + type: 'test' + ... +# Subtest: clusterByRecallRegion buckets warm prospects by their top recalled node id +ok 3393 - clusterByRecallRegion buckets warm prospects by their top recalled node id + --- + duration_ms: 0.198171 + type: 'test' + ... +# Subtest: chunkBySize splits an oversized cluster and leaves a small one whole +ok 3394 - chunkBySize splits an oversized cluster and leaves a small one whole + --- + duration_ms: 0.133453 + type: 'test' + ... +# Subtest: selectPending returns every unresolved prospect when no anchorKeys are given +ok 3395 - selectPending returns every unresolved prospect when no anchorKeys are given + --- + duration_ms: 0.71737 + type: 'test' + ... +# Subtest: selectPending with anchorKeys scopes the queue to in-window sessions, leaving the rest pending +ok 3396 - selectPending with anchorKeys scopes the queue to in-window sessions, leaving the rest pending + --- + duration_ms: 0.154044 + type: 'test' + ... +# Subtest: selectPending excludes already-resolved prospects +ok 3397 - selectPending excludes already-resolved prospects + --- + duration_ms: 0.184701 + type: 'test' + ... +# Subtest: runCurateTick curates pending prospects, writes committed + resolution rows, and skips already-resolved +ok 3398 - runCurateTick curates pending prospects, writes committed + resolution rows, and skips already-resolved + --- + duration_ms: 2.845645 + type: 'test' + ... +# Subtest: runCurateTick merges cross-session duplicates - each contributing session gets a committed row (produced edge) +ok 3399 - runCurateTick merges cross-session duplicates - each contributing session gets a committed row (produced edge) + --- + duration_ms: 0.647824 + type: 'test' + ... +# Subtest: runCurateTick leaves an under-specified merge prospect pending while committing its clustermate +ok 3400 - runCurateTick leaves an under-specified merge prospect pending while committing its clustermate + --- + duration_ms: 0.643669 + type: 'test' + ... +# Subtest: runCurateTick processes a duplicated prospect_id only once (idempotency defense-in-depth) +ok 3401 - runCurateTick processes a duplicated prospect_id only once (idempotency defense-in-depth) + --- + duration_ms: 0.489053 + type: 'test' + ... +# Subtest: runCurateTick auto-skips below-salience prospects with a terminal resolution and no curator call +ok 3402 - runCurateTick auto-skips below-salience prospects with a terminal resolution and no curator call + --- + duration_ms: 1.948531 + type: 'test' + ... +# Subtest: runCurateTick leaves a cluster pending (no resolution) when the curator returns no decisions +ok 3403 - runCurateTick leaves a cluster pending (no resolution) when the curator returns no decisions + --- + duration_ms: 0.544438 + type: 'test' + ... +# Subtest: runCurateTick derefs the source with the shared content filter (T1/T2 parity) +ok 3404 - runCurateTick derefs the source with the shared content filter (T1/T2 parity) + --- + duration_ms: 0.724281 + type: 'test' + ... +# Subtest: prospectId is deterministic for the same inputs +ok 3405 - prospectId is deterministic for the same inputs + --- + duration_ms: 1.002564 + type: 'test' + ... +# Subtest: prospectId changes with any input (anchor, candidate, version) +ok 3406 - prospectId changes with any input (anchor, candidate, version) + --- + duration_ms: 0.167304 + type: 'test' + ... +# Subtest: columnsFor returns the schema for known datasets and throws otherwise +ok 3407 - columnsFor returns the schema for known datasets and throws otherwise + --- + duration_ms: 0.251732 + type: 'test' + ... +# Subtest: buildProposeRequest forces the emit_prospects tool via the neutral toolChoice +ok 3408 - buildProposeRequest forces the emit_prospects tool via the neutral toolChoice + --- + duration_ms: 1.158441 + type: 'test' + ... +# Subtest: buildCurateBatchRequest batches prospects and shares source/neighborhood once +ok 3409 - buildCurateBatchRequest batches prospects and shares source/neighborhood once + --- + duration_ms: 1.955222 + type: 'test' + ... +# Subtest: buildCurateBatchRequest on Anthropic uses thinking + high effort, no forced tool +ok 3410 - buildCurateBatchRequest on Anthropic uses thinking + high effort, no forced tool + --- + duration_ms: 0.142727 + type: 'test' + ... +# Subtest: buildCurateBatchRequest on a non-Anthropic provider forces the tool, no thinking params +ok 3411 - buildCurateBatchRequest on a non-Anthropic provider forces the tool, no thinking params + --- + duration_ms: 0.122015 + type: 'test' + ... +# Subtest: parseProspects keeps valid candidates and drops unknown types / missing labels +ok 3412 - parseProspects keeps valid candidates and drops unknown types / missing labels + --- + duration_ms: 0.214075 + type: 'test' + ... +# Subtest: parseProspects returns [] on a refusal +ok 3413 - parseProspects returns [] on a refusal + --- + duration_ms: 0.101244 + type: 'test' + ... +# Subtest: parseDecisions keeps valid indexed decisions and drops invalid ones +ok 3414 - parseDecisions keeps valid indexed decisions and drops invalid ones + --- + duration_ms: 0.187024 + type: 'test' + ... +# Subtest: parseDecisions returns [] when there is no tool call +ok 3415 - parseDecisions returns [] when there is no tool call + --- + duration_ms: 0.099121 + type: 'test' + ... +# Subtest: buildSessionAggregateQuery ranks the precise latest (ts, tiebreak) per session, applying the content filter +ok 3416 - buildSessionAggregateQuery ranks the precise latest (ts, tiebreak) per session, applying the content filter + --- + duration_ms: 1.364043 + type: 'test' + ... +# Subtest: buildSessionAggregateQuery omits the inner content filter when both filters are off +ok 3417 - buildSessionAggregateQuery omits the inner content filter when both filters are off + --- + duration_ms: 0.194185 + type: 'test' + ... +# Subtest: buildSessionPartsQuery selects all transcript columns for one session, with no LIMIT +ok 3418 - buildSessionPartsQuery selects all transcript columns for one session, with no LIMIT + --- + duration_ms: 0.23045 + type: 'test' + ... +# Subtest: buildSessionPartsQuery escapes a single quote in the session id (no injection surface) +ok 3419 - buildSessionPartsQuery escapes a single quote in the session id (no injection surface) + --- + duration_ms: 0.143067 + type: 'test' + ... +# Subtest: buildSessionPartsQuery for a custom source is the bare anchor predicate (no part_type column) +ok 3420 - buildSessionPartsQuery for a custom source is the bare anchor predicate (no part_type column) + --- + duration_ms: 0.287848 + type: 'test' + ... +# Subtest: orderSessionParts sorts by (timestamp, tiebreak) and coerces Date/ISO timestamps +ok 3421 - orderSessionParts sorts by (timestamp, tiebreak) and coerces Date/ISO timestamps + --- + duration_ms: 0.716039 + type: 'test' + ... +# Subtest: buildTranscript stitches ordered text and dedups provenance ids, skipping empties +ok 3422 - buildTranscript stitches ordered text and dedups provenance ids, skipping empties + --- + duration_ms: 0.286966 + type: 'test' + ... +# Subtest: sessionMark returns the latest ordered part tuple +ok 3423 - sessionMark returns the latest ordered part tuple + --- + duration_ms: 0.272084 + type: 'test' + ... +# Subtest: selectSessions ongoing keeps only settled, past-watermark sessions, oldest first, capped +ok 3424 - selectSessions ongoing keeps only settled, past-watermark sessions, oldest first, capped + --- + duration_ms: 0.622456 + type: 'test' + ... +# Subtest: selectSessions ongoing reselects a same-timestamp session whose latest part advanced past the mark (tiebreak) +ok 3425 - selectSessions ongoing reselects a same-timestamp session whose latest part advanced past the mark (tiebreak) + --- + duration_ms: 0.437536 + type: 'test' + ... +# Subtest: selectSessions ongoing drops a session already enriched through its exact latest (ts, tiebreak) +ok 3426 - selectSessions ongoing drops a session already enriched through its exact latest (ts, tiebreak) + --- + duration_ms: 0.218482 + type: 'test' + ... +# Subtest: selectSessions backfill returns every session, ignoring settle + watermark + cap +ok 3427 - selectSessions backfill returns every session, ignoring settle + watermark + cap + --- + duration_ms: 0.242579 + type: 'test' + ... +# Subtest: collectProspectRows dedups identical (type,label) within a session and shapes the row +ok 3428 - collectProspectRows dedups identical (type,label) within a session and shapes the row + --- + duration_ms: 0.623297 + type: 'test' + ... +# Subtest: collectProspectRows keeps the same label under different sessions as distinct prospects +ok 3429 - collectProspectRows keeps the same label under different sessions as distinct prospects + --- + duration_ms: 0.159383 + type: 'test' + ... +# Subtest: runProposeTick extracts a whole session in one call, appends prospects, and advances its mark +ok 3430 - runProposeTick extracts a whole session in one call, appends prospects, and advances its mark + --- + duration_ms: 4.196419 + type: 'test' + ... +# Subtest: runProposeTick is idempotent across ticks: re-extracting the same session appends no duplicate +ok 3431 - runProposeTick is idempotent across ticks: re-extracting the same session appends no duplicate + --- + duration_ms: 1.57242 + type: 'test' + ... +# Subtest: runProposeTick preserves a curate_job submitted concurrently during its await window (no lost update) +ok 3432 - runProposeTick preserves a curate_job submitted concurrently during its await window (no lost update) + --- + duration_ms: 3.171009 + type: 'test' + ... +# Subtest: runProposeTick ongoing skips a session already enriched through its latest part +ok 3433 - runProposeTick ongoing skips a session already enriched through its latest part + --- + duration_ms: 0.905596 + type: 'test' + ... +# Subtest: runProposeTick drops candidates below the confidence floor before appending +ok 3434 - runProposeTick drops candidates below the confidence floor before appending + --- + duration_ms: 1.924786 + type: 'test' + ... +# Subtest: sqlQuote doubles single quotes and is idempotent under the doubling +ok 3435 - sqlQuote doubles single quotes and is idempotent under the doubling + --- + duration_ms: 0.756429 + type: 'test' + ... +# Subtest: contentFilterClauses emits the require_text + exclude_part_types predicates +ok 3436 - contentFilterClauses emits the require_text + exclude_part_types predicates + --- + duration_ms: 0.728337 + type: 'test' + ... +# Subtest: contentFilterClauses honors each knob independently and sqlQuotes the values +ok 3437 - contentFilterClauses honors each knob independently and sqlQuotes the values + --- + duration_ms: 0.153153 + type: 'test' + ... +# Subtest: isMissingDatasetError matches ENOENT and "unknown dataset", not arbitrary errors +ok 3438 - isMissingDatasetError matches ENOENT and "unknown dataset", not arbitrary errors + --- + duration_ms: 0.161695 + type: 'test' + ... +# Subtest: runSql tolerates a missing dataset only when allowMissing is set (else fail-fast) +ok 3439 - runSql tolerates a missing dataset only when allowMissing is set (else fail-fast) + --- + duration_ms: 0.505148 + type: 'test' + ... +# Subtest: runSql rethrows non-missing errors even with allowMissing +ok 3440 - runSql rethrows non-missing errors even with allowMissing + --- + duration_ms: 0.137439 + type: 'test' + ... +# Subtest: runSql returns the executor rows on success +ok 3441 - runSql returns the executor rows on success + --- + duration_ms: 0.20427 + type: 'test' + ... +# Subtest: readState returns an empty mark map + no job when the sidecar is missing +ok 3442 - readState returns an empty mark map + no job when the sidecar is missing + --- + duration_ms: 1.573142 + type: 'test' + ... +# Subtest: writeState then readState round-trips the per-session marks +ok 3443 - writeState then readState round-trips the per-session marks + --- + duration_ms: 0.832826 + type: 'test' + ... +# Subtest: writeState then readState round-trips the in-flight curate job +ok 3444 - writeState then readState round-trips the in-flight curate job + --- + duration_ms: 1.442091 + type: 'test' + ... +# Subtest: readState reads a legacy curate job with no source as daemon (the original owner) +ok 3445 - readState reads a legacy curate job with no source as daemon (the original owner) + --- + duration_ms: 0.632191 + type: 'test' + ... +# Subtest: readState coerces an unknown curate job source to daemon +ok 3446 - readState coerces an unknown curate job source to daemon + --- + duration_ms: 0.649848 + type: 'test' + ... +# Subtest: writeState creates the state dir and persists atomically (no leftover temp files) +ok 3447 - writeState creates the state dir and persists atomically (no leftover temp files) + --- + duration_ms: 0.856511 + type: 'test' + ... +# Subtest: readState falls back to an empty state on malformed JSON +ok 3448 - readState falls back to an empty state on malformed JSON + --- + duration_ms: 0.469073 + type: 'test' + ... +# Subtest: readState ignores an older schema_version (the global-cursor v2 sidecar is discarded) +ok 3449 - readState ignores an older schema_version (the global-cursor v2 sidecar is discarded) + --- + duration_ms: 0.347398 + type: 'test' + ... +# Subtest: readState drops only the malformed marks, keeping the well-formed ones +ok 3450 - readState drops only the malformed marks, keeping the well-formed ones + --- + duration_ms: 0.622416 + type: 'test' + ... +# Subtest: readState drops a malformed curate job (no clusters array) +ok 3451 - readState drops a malformed curate job (no clusters array) + --- + duration_ms: 0.530006 + type: 'test' + ... +# Subtest: nodeId pins known digests +ok 3452 - nodeId pins known digests + --- + duration_ms: 0.987681 + type: 'test' + ... +# Subtest: nodeId pins the GitHub↔LLM bridge keys +ok 3453 - nodeId pins the GitHub↔LLM bridge keys + --- + duration_ms: 0.155927 + type: 'test' + ... +# Subtest: edgeId pins known digests +ok 3454 - edgeId pins known digests + --- + duration_ms: 0.211562 + type: 'test' + ... +# Subtest: ids are 24 lowercase hex chars and delimiter-collision-free +ok 3455 - ids are 24 lowercase hex chars and delimiter-collision-free + --- + duration_ms: 0.272364 + type: 'test' + ... +# Subtest: compactGraphTables merges cross-partition duplicates into the earliest partition, sorted +ok 3456 - compactGraphTables merges cross-partition duplicates into the earliest partition, sorted + --- + duration_ms: 100.270922 + type: 'test' + ... +# Subtest: compactGraphTables is a no-op on an empty cache +ok 3457 - compactGraphTables is a no-op on an empty cache + --- + duration_ms: 2.251652 + type: 'test' + ... +# Subtest: compactGraphTables refuses to touch a partition whose cursor is corrupt +ok 3458 - compactGraphTables refuses to touch a partition whose cursor is corrupt + --- + duration_ms: 21.957253 + type: 'test' + ... +# Subtest: rewritePartition aborts the swap when the cursor changed during the rewrite window +ok 3459 - rewritePartition aborts the swap when the cursor changed during the rewrite window + --- + duration_ms: 23.826254 + type: 'test' + ... +# Subtest: graph compact CLI surfaces skipped partitions on stderr and exits nonzero for unreadable cursors +ok 3460 - graph compact CLI surfaces skipped partitions on stderr and exits nonzero for unreadable cursors + --- + duration_ms: 7.570826 + type: 'test' + ... +# Subtest: projectGraph runs a contributed contract end to end and is idempotent +ok 3461 - projectGraph runs a contributed contract end to end and is idempotent + --- + duration_ms: 142.066217 + type: 'test' + ... +# Subtest: projectGraph excludes retained Claude aux rows from the graph +ok 3462 - projectGraph excludes retained Claude aux rows from the graph + --- + duration_ms: 36.25219 + type: 'test' + ... +# Subtest: projectGraph with no contracts writes nothing +ok 3463 - projectGraph with no contracts writes nothing + --- + duration_ms: 8.547441 + type: 'test' + ... +# Subtest: bumping projectorVersion does not re-project: committed rows keep their original version +ok 3464 - bumping projectorVersion does not re-project: committed rows keep their original version + --- + duration_ms: 23.025106 + type: 'test' + ... +# Subtest: projectGraph mints Skill/Program nodes and ran/invoked edges from all activation surfaces (\#229/\#230), and is idempotent +ok 3465 - projectGraph mints Skill/Program nodes and ran/invoked edges from all activation surfaces (\#229/\#230), and is idempotent + --- + duration_ms: 45.147329 + type: 'test' + ... +# Subtest: a session sighted via both the marker and slash surfaces merges onto one ran edge with both dispatch flags +ok 3466 - a session sighted via both the marker and slash surfaces merges onto one ran edge with both dispatch flags + --- + duration_ms: 28.049141 + type: 'test' + ... +# Subtest: issue \#229 headline SQL: sessions-per-skill ranking +ok 3467 - issue \#229 headline SQL: sessions-per-skill ranking + --- + duration_ms: 37.794684 + type: 'test' + ... +# Subtest: issue \#230 headline SQL: which sessions ran the `git` program +ok 3468 - issue \#230 headline SQL: which sessions ran the `git` program + --- + duration_ms: 34.272561 + type: 'test' + ... +# Subtest: graph neighbors traversal: --edge-type ran reaches Skill, --edge-type invoked reaches Program +ok 3469 - graph neighbors traversal: --edge-type ran reaches Skill, --edge-type invoked reaches Program + --- + duration_ms: 40.051985 + type: 'test' + ... +# Subtest: shared-scan projection is row-identical to per-rule SQL execution +ok 3470 - shared-scan projection is row-identical to per-rule SQL execution + --- + duration_ms: 82.332908 + type: 'test' + ... +# Subtest: mergeRow keeps the earliest first_seen and unions disjoint props +ok 3471 - mergeRow keeps the earliest first_seen and unions disjoint props + --- + duration_ms: 0.964236 + type: 'test' + ... +# Subtest: mergeRow resolves props conflicts in favor of the earliest row, in any merge order +ok 3472 - mergeRow resolves props conflicts in favor of the earliest row, in any merge order + --- + duration_ms: 0.133102 + type: 'test' + ... +# Subtest: mergeRow is order-independent when a key is absent from the earliest row +ok 3473 - mergeRow is order-independent when a key is absent from the earliest row + --- + duration_ms: 0.289731 + type: 'test' + ... +# Subtest: mergeRow breaks equal-timestamp conflicts by value, order-independently +ok 3474 - mergeRow breaks equal-timestamp conflicts by value, order-independently + --- + duration_ms: 0.123027 + type: 'test' + ... +# Subtest: mergeRow tolerates rows with unparseable or missing first_seen +ok 3475 - mergeRow tolerates rows with unparseable or missing first_seen + --- + duration_ms: 0.191831 + type: 'test' + ... +# Subtest: matchesPredicate likePrefix keeps only matching-prefix string rows +ok 3476 - matchesPredicate likePrefix keeps only matching-prefix string rows + --- + duration_ms: 0.196668 + type: 'test' + ... +# Subtest: firstSeenTime normalizes strings, Dates, and epoch numbers +ok 3477 - firstSeenTime normalizes strings, Dates, and epoch numbers + --- + duration_ms: 0.13869 + type: 'test' + ... +# Subtest: projectGraph passes the default 3 GiB projection budget at all three scan sites when the knob is unset +ok 3478 - projectGraph passes the default 3 GiB projection budget at all three scan sites when the knob is unset + --- + duration_ms: 2.980029 + type: 'test' + ... +# Subtest: HYP_GRAPH_PROJECTION_MAX_HEAP_MB overrides the projection budget at every scan site +ok 3479 - HYP_GRAPH_PROJECTION_MAX_HEAP_MB overrides the projection budget at every scan site + --- + duration_ms: 0.445146 + type: 'test' + ... +# Subtest: resolveProjectionMaxHeapBytes defaults to 3 GiB and never returns 0 +ok 3480 - resolveProjectionMaxHeapBytes defaults to 3 GiB and never returns 0 + --- + duration_ms: 0.22384 + type: 'test' + ... +# Subtest: depth-1 out from a Session reaches its app/model/tool/file +ok 3481 - depth-1 out from a Session reaches its app/model/tool/file + --- + duration_ms: 1.324073 + type: 'test' + ... +# Subtest: depth-1 in from a File reaches the Sessions that touched it +ok 3482 - depth-1 in from a File reaches the Sessions that touched it + --- + duration_ms: 0.284071 + type: 'test' + ... +# Subtest: depth-2 both from a File yields co-occurrence (file → sessions → resources) +ok 3483 - depth-2 both from a File yields co-occurrence (file → sessions → resources) + --- + duration_ms: 0.184861 + type: 'test' + ... +# Subtest: --edge-type restricts which relations are walked +ok 3484 - --edge-type restricts which relations are walked + --- + duration_ms: 0.209418 + type: 'test' + ... +# Subtest: direction out from a leaf File yields no neighbors (but succeeds) +ok 3485 - direction out from a leaf File yields no neighbors (but succeeds) + --- + duration_ms: 0.134755 + type: 'test' + ... +# Subtest: --limit truncates in BFS order and reports the true reachable total +ok 3486 - --limit truncates in BFS order and reports the true reachable total + --- + duration_ms: 0.120323 + type: 'test' + ... +# Subtest: a node with no visited dedup is never revisited across hops +ok 3487 - a node with no visited dedup is never revisited across hops + --- + duration_ms: 0.171311 + type: 'test' + ... +# Subtest: resolveSeed matches node_id, then natural_key, then label +ok 3488 - resolveSeed matches node_id, then natural_key, then label + --- + duration_ms: 0.134003 + type: 'test' + ... +# Subtest: resolveSeed reports ambiguity with candidates rather than silently picking +ok 3489 - resolveSeed reports ambiguity with candidates rather than silently picking + --- + duration_ms: 0.284262 + type: 'test' + ... +# Subtest: resolveSeed --type narrows the match +ok 3490 - resolveSeed --type narrows the match + --- + duration_ms: 0.298564 + type: 'test' + ... +# Subtest: traverse returns an error shape for an unresolved seed +ok 3491 - traverse returns an error shape for an unresolved seed + --- + duration_ms: 0.158832 + type: 'test' + ... +# Subtest: queryNeighbors reads node/edge through the query surface and walks (integration) +ok 3492 - queryNeighbors reads node/edge through the query surface and walks (integration) + --- + duration_ms: 85.879208 + type: 'test' + ... +# Subtest: queryNeighbors suppresses graph content for an unknown caller and reports it +ok 3493 - queryNeighbors suppresses graph content for an unknown caller and reports it + --- + duration_ms: 49.359341 + type: 'test' + ... +# Subtest: queryNeighbors folds pre-compaction duplicate rows so a natural-key seed still resolves +ok 3494 - queryNeighbors folds pre-compaction duplicate rows so a natural-key seed still resolves + --- + duration_ms: 24.675685 + type: 'test' + ... +# Subtest: queryNeighbors reports an unprojected graph as empty, not as a missing node +ok 3495 - queryNeighbors reports an unprojected graph as empty, not as a missing node + --- + duration_ms: 2.095995 + type: 'test' + ... +# Subtest: a populated graph with an unknown seed is not reported as empty +ok 3496 - a populated graph with an unknown seed is not reported as empty + --- + duration_ms: 13.02724 + type: 'test' + ... +# Subtest: the renderer names `hyp graph project` when the graph is empty +ok 3497 - the renderer names `hyp graph project` when the graph is empty + --- + duration_ms: 0.343883 + type: 'test' + ... +# Subtest: an ordinary not-found still renders its own error and candidates +ok 3498 - an ordinary not-found still renders its own error and candidates + --- + duration_ms: 0.124779 + type: 'test' + ... +# Subtest: embed sends Bearer key from the configured env var and returns aligned vectors +ok 3499 - embed sends Bearer key from the configured env var and returns aligned vectors + --- + duration_ms: 3.049916 + type: 'test' + ... +# Subtest: embed chunks batches larger than max_batch and preserves order +ok 3500 - embed chunks batches larger than max_batch and preserves order + --- + duration_ms: 0.603748 + type: 'test' + ... +# Subtest: embed without the env var sends no Authorization header (localhost servers) +ok 3501 - embed without the env var sends no Authorization header (localhost servers) + --- + duration_ms: 0.471276 + type: 'test' + ... +# Subtest: embed maps a 401 without a key to a hint naming the env var, never the value +ok 3502 - embed maps a 401 without a key to a hint naming the env var, never the value + --- + duration_ms: 0.858955 + type: 'test' + ... +# Subtest: embed error messages never contain the API key +ok 3503 - embed error messages never contain the API key + --- + duration_ms: 0.557197 + type: 'test' + ... +# Subtest: embed errors and logs never contain the provider error body +ok 3504 - embed errors and logs never contain the provider error body + --- + duration_ms: 0.418176 + type: 'test' + ... +# Subtest: embed rejects an empty input batch +ok 3505 - embed rejects an empty input batch + --- + duration_ms: 0.261718 + type: 'test' + ... +# Subtest: embed surfaces a count mismatch as embedder_bad_response +ok 3506 - embed surfaces a count mismatch as embedder_bad_response + --- + duration_ms: 0.44707 + type: 'test' + ... +# Subtest: parseEmbeddingsPayload rejects a malformed entry and a missing index +ok 3507 - parseEmbeddingsPayload rejects a malformed entry and a missing index + --- + duration_ms: 0.259524 + type: 'test' + ... +# Subtest: validateEmbedderConfig defaults to OpenAI with OPENAI_API_KEY +ok 3508 - validateEmbedderConfig defaults to OpenAI with OPENAI_API_KEY + --- + duration_ms: 0.950405 + type: 'test' + ... +# Subtest: validateEmbedderConfig accepts a localhost override (Ollama shape) +ok 3509 - validateEmbedderConfig accepts a localhost override (Ollama shape) + --- + duration_ms: 0.225472 + type: 'test' + ... +# Subtest: validateEmbedderConfig accepts dimensions for v3 shortening +ok 3510 - validateEmbedderConfig accepts dimensions for v3 shortening + --- + duration_ms: 1.267877 + type: 'test' + ... +# Subtest: validateEmbedderConfig rejects a non-object config +ok 3511 - validateEmbedderConfig rejects a non-object config + --- + duration_ms: 0.14422 + type: 'test' + ... +# Subtest: validateEmbedderConfig rejects a non-http base_url +ok 3512 - validateEmbedderConfig rejects a non-http base_url + --- + duration_ms: 0.154895 + type: 'test' + ... +# Subtest: validateEmbedderConfig rejects a malformed base_url +ok 3513 - validateEmbedderConfig rejects a malformed base_url + --- + duration_ms: 0.138531 + type: 'test' + ... +# Subtest: validateEmbedderConfig rejects non-positive numeric fields +ok 3514 - validateEmbedderConfig rejects non-positive numeric fields + --- + duration_ms: 0.179552 + type: 'test' + ... +# Subtest: embeddingsEndpoint appends /v1/embeddings to a bare origin +ok 3515 - embeddingsEndpoint appends /v1/embeddings to a bare origin + --- + duration_ms: 0.122837 + type: 'test' + ... +# Subtest: embeddingsEndpoint does not double /v1 on a /v1-suffixed base +ok 3516 - embeddingsEndpoint does not double /v1 on a /v1-suffixed base + --- + duration_ms: 0.321719 + type: 'test' + ... +# Subtest: without clustering, a wide per-conversation column stays dictionary-encoded (hyparquet-writer\#35) +ok 3517 - without clustering, a wide per-conversation column stays dictionary-encoded (hyparquet-writer\#35) + --- + duration_ms: 60.477089 + type: 'test' + ... +# Subtest: with clustering, the wide column stays dictionary-encoded at no size cost +ok 3518 - with clustering, the wide column stays dictionary-encoded at no size cost + --- + duration_ms: 44.46368 + type: 'test' + ... +# Subtest: the per-group byte cap splits a single high-volume conversation into multiple row groups +ok 3519 - the per-group byte cap splits a single high-volume conversation into multiple row groups + --- + duration_ms: 7.91513 + type: 'test' + ... +# Subtest: a fat row flushes the group before it is added, so no group overshoots the byte cap +ok 3520 - a fat row flushes the group before it is added, so no group overshoots the byte cap + --- + duration_ms: 225.217569 + type: 'test' + ... +# Subtest: JSON object columns are interned so they dictionary-encode AND round-trip as objects +ok 3521 - JSON object columns are interned so they dictionary-encode AND round-trip as objects + --- + duration_ms: 8.442662 + type: 'test' + ... +# Subtest: interning never merges distinct values: BigInt vs same-text string vs sentinel-shaped object +ok 3522 - interning never merges distinct values: BigInt vs same-text string vs sentinel-shaped object + --- + duration_ms: 0.594013 + type: 'test' + ... +# Subtest: clusterColumns leaves row counts and schema unchanged +ok 3523 - clusterColumns leaves row counts and schema unchanged + --- + duration_ms: 0.812324 + type: 'test' + ... +# Subtest: gascity discoverParts surfaces committed source= partitions alongside the spool +ok 3524 - gascity discoverParts surfaces committed source= partitions alongside the spool + --- + duration_ms: 30.170746 + type: 'test' + ... +# Subtest: gascity createDataSource reads rows committed under source= partitions +ok 3525 - gascity createDataSource reads rows committed under source= partitions + --- + duration_ms: 19.879375 + type: 'test' + ... +# Subtest: gascity createDataSource returns an empty source on a cold cache +ok 3526 - gascity createDataSource returns an empty source on a cold cache + --- + duration_ms: 1.456443 + type: 'test' + ... +# Subtest: codex activates before openclaw in the real bundled boot order +ok 3527 - codex activates before openclaw in the real bundled boot order + --- + duration_ms: 26.949841 + type: 'test' + ... +# Subtest: the codex-before-openclaw order does not depend on manifest input order +ok 3528 - the codex-before-openclaw order does not depend on manifest input order + --- + duration_ms: 6.623327 + type: 'test' + ... +# Subtest: the route table still carries the coverage the no-change tests walk +ok 3529 - the route table still carries the coverage the no-change tests walk + --- + duration_ms: 0.238672 + type: 'test' + ... +# Subtest: existing routes are unchanged when the openai slot is won by activation order @hypaware/codex then @hypaware/openclaw +ok 3530 - existing routes are unchanged when the openai slot is won by activation order @hypaware/codex then @hypaware/openclaw + --- + duration_ms: 1.872456 + type: 'test' + ... +# Subtest: a steered openai turn is routable when the openai slot is won by activation order @hypaware/codex then @hypaware/openclaw +ok 3531 - a steered openai turn is routable when the openai slot is won by activation order @hypaware/codex then @hypaware/openclaw + --- + duration_ms: 0.363503 + type: 'test' + ... +# Subtest: existing routes are unchanged when the openai slot is won by activation order @hypaware/openclaw then @hypaware/codex +ok 3532 - existing routes are unchanged when the openai slot is won by activation order @hypaware/openclaw then @hypaware/codex + --- + duration_ms: 0.540872 + type: 'test' + ... +# Subtest: a steered openai turn is routable when the openai slot is won by activation order @hypaware/openclaw then @hypaware/codex +ok 3533 - a steered openai turn is routable when the openai slot is won by activation order @hypaware/openclaw then @hypaware/codex + --- + duration_ms: 0.63766 + type: 'test' + ... +# Subtest: the header rung does not divert anthropic traffic that never sends it +ok 3534 - the header rung does not divert anthropic traffic that never sends it + --- + duration_ms: 0.390163 + type: 'test' + ... +# Subtest: the surviving openai preset does not outrank a config-declared anthropic upstream (@hypaware/codex) +ok 3535 - the surviving openai preset does not outrank a config-declared anthropic upstream (@hypaware/codex) + --- + duration_ms: 0.491987 + type: 'test' + ... +# Subtest: the surviving openai preset does not outrank a config-declared anthropic upstream (@hypaware/codex then @hypaware/openclaw) +ok 3536 - the surviving openai preset does not outrank a config-declared anthropic upstream (@hypaware/codex then @hypaware/openclaw) + --- + duration_ms: 0.503705 + type: 'test' + ... +# Subtest: the surviving openai preset does not outrank a config-declared anthropic upstream (@hypaware/openclaw then @hypaware/codex) +ok 3537 - the surviving openai preset does not outrank a config-declared anthropic upstream (@hypaware/openclaw then @hypaware/codex) + --- + duration_ms: 0.26302 + type: 'test' + ... +# (node:741851) ExperimentalWarning: SQLite is an experimental feature and might change at any time +# (Use `node --trace-warnings ...` to show where the warning was created) +# Subtest: provider advertises a stable contribution shape +ok 3538 - provider advertises a stable contribution shape + --- + duration_ms: 1.393739 + type: 'test' + ... +# Subtest: defaultHermesStateDbPath resolves under /.hermes/state.db +ok 3539 - defaultHermesStateDbPath resolves under /.hermes/state.db + --- + duration_ms: 0.175948 + type: 'test' + ... +# Subtest: every session projects into one item, addressed to the projected-exchange materializer +ok 3540 - every session projects into one item, addressed to the projected-exchange materializer + --- + duration_ms: 89.634916 + type: 'test' + ... +# Subtest: provenance carries the state.db path, client name, and native session id +ok 3541 - provenance carries the state.db path, client name, and native session id + --- + duration_ms: 67.83999 + type: 'test' + ... +# Subtest: since bound keeps only sessions started on or after the window, by session (not by message) +ok 3542 - since bound keeps only sessions started on or after the window, by session (not by message) + --- + duration_ms: 61.534676 + type: 'test' + ... +# Subtest: until bound excludes sessions started after the window +ok 3543 - until bound excludes sessions started after the window + --- + duration_ms: 48.226329 + type: 'test' + ... +# Subtest: no window (no since/until/retentionDays) imports every session +ok 3544 - no window (no since/until/retentionDays) imports every session + --- + duration_ms: 52.673679 + type: 'test' + ... +# Subtest: a state.db with zero sessions yields nothing, without throwing +ok 3545 - a state.db with zero sessions yields nothing, without throwing + --- + duration_ms: 22.260995 + type: 'test' + ... +# Subtest: a missing state.db (no hermes installation) yields nothing, without throwing +ok 3546 - a missing state.db (no hermes installation) yields nothing, without throwing + --- + duration_ms: 1.060722 + type: 'test' + ... +# Subtest: a session whose cwd is .hypignore-ignored is skipped, others still import +ok 3547 - a session whose cwd is .hypignore-ignored is skipped, others still import + --- + duration_ms: 35.011484 + type: 'test' + ... +# Subtest: reruns are deterministic: identical items across runs +ok 3548 - reruns are deterministic: identical items across runs + --- + duration_ms: 57.002548 + type: 'test' + ... +# Subtest: validateHermesConfig accepts an empty / absent config +ok 3549 - validateHermesConfig accepts an empty / absent config + --- + duration_ms: 1.363803 + type: 'test' + ... +# Subtest: validateHermesConfig accepts a full valid config +ok 3550 - validateHermesConfig accepts a full valid config + --- + duration_ms: 0.238132 + type: 'test' + ... +# Subtest: validateHermesConfig accepts every documented poll_interval duration suffix +ok 3551 - validateHermesConfig accepts every documented poll_interval duration suffix + --- + duration_ms: 0.275639 + type: 'test' + ... +# Subtest: validateHermesConfig rejects a non-object config +ok 3552 - validateHermesConfig rejects a non-object config + --- + duration_ms: 0.15134 + type: 'test' + ... +# Subtest: validateHermesConfig rejects malformed keys +ok 3553 - validateHermesConfig rejects malformed keys + --- + duration_ms: 0.294377 + type: 'test' + ... +# Subtest: resolveHermesEnabled defaults to true (missing/absent/non-object config) +ok 3554 - resolveHermesEnabled defaults to true (missing/absent/non-object config) + --- + duration_ms: 0.138411 + type: 'test' + ... +# Subtest: resolveHermesEnabled honors an explicit override +ok 3555 - resolveHermesEnabled honors an explicit override + --- + duration_ms: 0.168967 + type: 'test' + ... +# Subtest: the registered hermes section drives validatePluginConfig +ok 3556 - the registered hermes section drives validatePluginConfig + --- + duration_ms: 0.536145 + type: 'test' + ... +# (node:741870) ExperimentalWarning: SQLite is an experimental feature and might change at any time +# (Use `node --trace-warnings ...` to show where the warning was created) +# Subtest: activate() registers the hermes source but does not start it (matches claude/codex) +ok 3557 - activate() registers the hermes source but does not start it (matches claude/codex) + --- + duration_ms: 4.773335 + type: 'test' + ... +# Subtest: a re-projection after another writer committed the tail appends zero duplicate rows (spec R2) +ok 3558 - a re-projection after another writer committed the tail appends zero duplicate rows (spec R2) + --- + duration_ms: 88.053002 + type: 'test' + ... +# Subtest: derives redacted remote and repo_root, never asks for HEAD +ok 3559 - derives redacted remote and repo_root, never asks for HEAD + --- + duration_ms: 1.398074 + type: 'test' + ... +# Subtest: an SSH remote is left intact (no userinfo to strip) +ok 3560 - an SSH remote is left intact (no userinfo to strip) + --- + duration_ms: 2.812245 + type: 'test' + ... +# Subtest: degrades to empty when the cwd is not a git repo (deleted worktree, channel scope path) +ok 3561 - degrades to empty when the cwd is not a git repo (deleted worktree, channel scope path) + --- + duration_ms: 0.265514 + type: 'test' + ... +# Subtest: returns empty for an absent cwd without invoking git +ok 3562 - returns empty for an absent cwd without invoking git + --- + duration_ms: 0.16462 + type: 'test' + ... +# Subtest: redactRemoteUserinfo strips credential userinfo from an https remote only +ok 3563 - redactRemoteUserinfo strips credential userinfo from an https remote only + --- + duration_ms: 0.15149 + type: 'test' + ... +# Subtest: hermes manifest loads and validates +ok 3564 - hermes manifest loads and validates + --- + duration_ms: 4.682777 + type: 'test' + ... +# Subtest: hermes requires @hypaware/ai-gateway as a hard plugin dependency +ok 3565 - hermes requires @hypaware/ai-gateway as a hard plugin dependency + --- + duration_ms: 0.863041 + type: 'test' + ... +# Subtest: hermes contributes a config section and a source, no dataset of its own +ok 3566 - hermes contributes a config section and a source, no dataset of its own + --- + duration_ms: 1.92797 + type: 'test' + ... +# Subtest: hermes node_engine requires the node:sqlite floor (LLP 0125) +ok 3567 - hermes node_engine requires the node:sqlite floor (LLP 0125) + --- + duration_ms: 0.827668 + type: 'test' + ... +# Subtest: hermes is bundled and default-activated beside claude and codex +ok 3568 - hermes is bundled and default-activated beside claude and codex + --- + duration_ms: 0.183059 + type: 'test' + ... +# Subtest: golden projection: open interactive session with tool call and reasoning +ok 3569 - golden projection: open interactive session with tool call and reasoning + --- + duration_ms: 3.227295 + type: 'test' + ... +# Subtest: an open session never carries a session-end part +ok 3570 - an open session never carries a session-end part + --- + duration_ms: 0.445247 + type: 'test' + ... +# Subtest: an ended session carries exactly one session-end part with final totals and costs +ok 3571 - an ended session carries exactly one session-end part with final totals and costs + --- + duration_ms: 0.485248 + type: 'test' + ... +# Subtest: projecting the same session twice yields byte-identical identity +ok 3572 - projecting the same session twice yields byte-identical identity + --- + duration_ms: 0.768939 + type: 'test' + ... +# Subtest: a session under an ignored cwd is skipped before any row is built +ok 3573 - a session under an ignored cwd is skipped before any row is built + --- + duration_ms: 0.437926 + type: 'test' + ... +# Subtest: a channel session is stamped with the canonical channel scope path, real cwd preserved +ok 3574 - a channel session is stamped with the canonical channel scope path, real cwd preserved + --- + duration_ms: 0.415952 + type: 'test' + ... +# Subtest: a channel session is governed by a marked channel scope, same as any other cwd +ok 3575 - a channel session is governed by a marked channel scope, same as any other cwd + --- + duration_ms: 0.341649 + type: 'test' + ... +# Subtest: an interactive session with NULL cwd records unconditionally (no scope to match) +ok 3576 - an interactive session with NULL cwd records unconditionally (no scope to match) + --- + duration_ms: 0.285994 + type: 'test' + ... +# Subtest: a session with no messages and no end yields undefined +ok 3577 - a session with no messages and no end yields undefined + --- + duration_ms: 0.581484 + type: 'test' + ... +# Subtest: hermesScopeId and mintHermesMessageId namespace hermes store-scoped integer ids +ok 3578 - hermesScopeId and mintHermesMessageId namespace hermes store-scoped integer ids + --- + duration_ms: 0.36162 + type: 'test' + ... +# Subtest: normalizeHermesProvider prefers a known base_url host, falls back to billing_provider, then unknown +ok 3579 - normalizeHermesProvider prefers a known base_url host, falls back to billing_provider, then unknown + --- + duration_ms: 0.191911 + type: 'test' + ... +# (node:741908) ExperimentalWarning: SQLite is an experimental feature and might change at any time +# (Use `node --trace-warnings ...` to show where the warning was created) +# Subtest: a missing state.db idles cleanly: no db opened, no error noise, idle logged once +ok 3580 - a missing state.db idles cleanly: no db opened, no error noise, idle logged once + --- + duration_ms: 6.32917 + type: 'test' + ... +# Subtest: startHermesSource reports ready/idle status when hermes is not installed +ok 3581 - startHermesSource reports ready/idle status when hermes is not installed + --- + duration_ms: 1.627154 + type: 'test' + ... +# Subtest: a poll tick advances the per-session watermark to the store's current state and persists it +ok 3582 - a poll tick advances the per-session watermark to the store's current state and persists it + --- + duration_ms: 77.633114 + type: 'test' + ... +# Subtest: re-projecting a whole session on each tick appends only the new tail (materializer part_id dedupe) +ok 3583 - re-projecting a whole session on each tick appends only the new tail (materializer part_id dedupe) + --- + duration_ms: 76.279537 + type: 'test' + ... +# Subtest: a session ending with no new messages still triggers re-projection and lands the session-end part +ok 3584 - a session ending with no new messages still triggers re-projection and lands the session-end part + --- + duration_ms: 63.909927 + type: 'test' + ... +# Subtest: stop() clears the timer, closes the db, and is safe to call twice +ok 3585 - stop() clears the timer, closes the db, and is safe to call twice + --- + duration_ms: 39.565365 + type: 'test' + ... +# (node:741922) ExperimentalWarning: SQLite is an experimental feature and might change at any time +# (Use `node --trace-warnings ...` to show where the warning was created) +# Subtest: openHermesStateDb opens read-only and lists sessions in id order +ok 3586 - openHermesStateDb opens read-only and lists sessions in id order + --- + duration_ms: 71.259798 + type: 'test' + ... +# Subtest: openHermesStateDb.listMessagesForSession returns tool-call and reasoning fields +ok 3587 - openHermesStateDb.listMessagesForSession returns tool-call and reasoning fields + --- + duration_ms: 59.862525 + type: 'test' + ... +# Subtest: the reader connection is genuinely read-only +ok 3588 - the reader connection is genuinely read-only + --- + duration_ms: 58.518012 + type: 'test' + ... +# Subtest: openHermesStateDb refuses cleanly when state.db does not exist +ok 3589 - openHermesStateDb refuses cleanly when state.db does not exist + --- + duration_ms: 0.612441 + type: 'test' + ... +# Subtest: loadSqliteModule turns a missing node:sqlite builtin into a HermesStateDbError +ok 3590 - loadSqliteModule turns a missing node:sqlite builtin into a HermesStateDbError + --- + duration_ms: 0.148305 + type: 'test' + ... +# Subtest: openHermesStateDb propagates the activation refusal via requireFn injection +ok 3591 - openHermesStateDb propagates the activation refusal via requireFn injection + --- + duration_ms: 52.237506 + type: 'test' + ... +# Subtest: isRetryableBusyError classifies real node:sqlite busy/locked errors and nothing else +ok 3592 - isRetryableBusyError classifies real node:sqlite busy/locked errors and nothing else + --- + duration_ms: 0.437665 + type: 'test' + ... +# Subtest: withBusyRetry retries busy errors within the bound then succeeds +ok 3593 - withBusyRetry retries busy errors within the bound then succeeds + --- + duration_ms: 0.198071 + type: 'test' + ... +# Subtest: withBusyRetry gives up after the bound and throws a typed sqlite_busy error +ok 3594 - withBusyRetry gives up after the bound and throws a typed sqlite_busy error + --- + duration_ms: 0.352716 + type: 'test' + ... +# Subtest: withBusyRetry lets a non-busy error propagate immediately, no retry +ok 3595 - withBusyRetry lets a non-busy error propagate immediately, no retry + --- + duration_ms: 0.516806 + type: 'test' + ... +# Subtest: withBusyRetry defaults to DEFAULT_BUSY_RETRY_ATTEMPTS +ok 3596 - withBusyRetry defaults to DEFAULT_BUSY_RETRY_ATTEMPTS + --- + duration_ms: 0.293687 + type: 'test' + ... +# Subtest: HermesStateDb read methods retry through a transiently busy connection +ok 3597 - HermesStateDb read methods retry through a transiently busy connection + --- + duration_ms: 0.221086 + type: 'test' + ... +# Subtest: listChangedSessions flags every session on an empty watermark +ok 3598 - listChangedSessions flags every session on an empty watermark + --- + duration_ms: 42.453084 + type: 'test' + ... +# Subtest: listChangedSessions is empty once the watermark matches current state +ok 3599 - listChangedSessions is empty once the watermark matches current state + --- + duration_ms: 43.949919 + type: 'test' + ... +# Subtest: listChangedSessions catches a new message appended to an open session +ok 3600 - listChangedSessions catches a new message appended to an open session + --- + duration_ms: 34.702144 + type: 'test' + ... +# Subtest: listChangedSessions catches the ended_at NULL -> set transition with no new messages +ok 3601 - listChangedSessions catches the ended_at NULL -> set transition with no new messages + --- + duration_ms: 38.440396 + type: 'test' + ... +# Subtest: cached hermes rows carry stored message times, not the projection wall clock +ok 3602 - cached hermes rows carry stored message times, not the projection wall clock + --- + duration_ms: 2.699593 + type: 'test' + ... +# Subtest: re-projecting the same session yields byte-identical timestamps (dedupe-safe) +ok 3603 - re-projecting the same session yields byte-identical timestamps (dedupe-safe) + --- + duration_ms: 1.453529 + type: 'test' + ... +# Subtest: tableUrlForBlobPrefix normalises slashes and emits blob:// scheme +ok 3604 - tableUrlForBlobPrefix normalises slashes and emits blob:// scheme + --- + duration_ms: 1.979869 + type: 'test' + ... +# Subtest: pathToKey reverses the table URL and accepts subpaths +ok 3605 - pathToKey reverses the table URL and accepts subpaths + --- + duration_ms: 0.145761 + type: 'test' + ... +# Subtest: pathToKey rejects empty input +ok 3606 - pathToKey rejects empty input + --- + duration_ms: 0.273086 + type: 'test' + ... +# Subtest: createBlobStoreIO writer flushes via putObject and applies ifNoneMatch +ok 3607 - createBlobStoreIO writer flushes via putObject and applies ifNoneMatch + --- + duration_ms: 29.756737 + type: 'test' + ... +# Subtest: createBlobStoreIO reader returns AsyncBuffer with byte-faithful slice +ok 3608 - createBlobStoreIO reader returns AsyncBuffer with byte-faithful slice + --- + duration_ms: 2.492328 + type: 'test' + ... +# Subtest: createBlobStoreIO reader surfaces ENOENT for missing objects +ok 3609 - createBlobStoreIO reader surfaces ENOENT for missing objects + --- + duration_ms: 0.270251 + type: 'test' + ... +# Subtest: createBlobStoreIO lister returns immediate basenames sorted +ok 3610 - createBlobStoreIO lister returns immediate basenames sorted + --- + duration_ms: 0.405407 + type: 'test' + ... +# Subtest: collectStream concatenates Node-stream chunks deterministically +ok 3611 - collectStream concatenates Node-stream chunks deterministically + --- + duration_ms: 0.66457 + type: 'test' + ... +# Subtest: createBlobStoreIO refuses BlobStores without putObject +ok 3612 - createBlobStoreIO refuses BlobStores without putObject + --- + duration_ms: 0.34232 + type: 'test' + ... +# Subtest: probeTable reports exists=false when no metadata has been written +ok 3613 - probeTable reports exists=false when no metadata has been written + --- + duration_ms: 5.668285 + type: 'test' + ... +# Subtest: commitBatch creates an Iceberg table on first append and produces a snapshot +ok 3614 - commitBatch creates an Iceberg table on first append and produces a snapshot + --- + duration_ms: 28.602493 + type: 'test' + ... +# Subtest: commitBatch appends keep schema ids stable across batches +ok 3615 - commitBatch appends keep schema ids stable across batches + --- + duration_ms: 28.364792 + type: 'test' + ... +# Subtest: commitBatch retries past a transient metadata precondition collision +ok 3616 - commitBatch retries past a transient metadata precondition collision + --- + duration_ms: 59.348183 + type: 'test' + ... +# Subtest: commitBatch surfaces iceberg_commit_conflict when initial create races +ok 3617 - commitBatch surfaces iceberg_commit_conflict when initial create races + --- + duration_ms: 8.291612 + type: 'test' + ... +# Subtest: probeTable discovers latest snapshot when version-hint.text is stale +ok 3618 - probeTable discovers latest snapshot when version-hint.text is stale + --- + duration_ms: 8.850472 + type: 'test' + ... +# Subtest: probeTable discovers latest snapshot when version-hint.text is missing +ok 3619 - probeTable discovers latest snapshot when version-hint.text is missing + --- + duration_ms: 6.581774 + type: 'test' + ... +# Subtest: markerSubsumedBySnapshot recognises a marker whose snapshot is an ancestor of the current snapshot (no duplicate rows on retry) +ok 3620 - markerSubsumedBySnapshot recognises a marker whose snapshot is an ancestor of the current snapshot (no duplicate rows on retry) + --- + duration_ms: 7.800376 + type: 'test' + ... +# Subtest: probeTable propagates transient metadata read failures instead of masking them as miss +ok 3621 - probeTable propagates transient metadata read failures instead of masking them as miss + --- + duration_ms: 0.518989 + type: 'test' + ... +# Subtest: commitBatch normalises blob_precondition_failed into iceberg_commit_conflict +ok 3622 - commitBatch normalises blob_precondition_failed into iceberg_commit_conflict + --- + duration_ms: 0.523766 + type: 'test' + ... +# Subtest: the committed Iceberg snapshot excludes rows from a local-only directory +ok 3623 - the committed Iceberg snapshot excludes rows from a local-only directory + --- + duration_ms: 74.094457 + type: 'test' + ... +# Subtest: unaffected when no local-only list is configured: every row commits, as before +ok 3624 - unaffected when no local-only list is configured: every row commits, as before + --- + duration_ms: 27.273263 + type: 'test' + ... +# Subtest: normalizeExportRetentionConfig fills defaults +ok 3625 - normalizeExportRetentionConfig fills defaults + --- + duration_ms: 0.851524 + type: 'test' + ... +# Subtest: normalizeExportRetentionConfig honors overrides +ok 3626 - normalizeExportRetentionConfig honors overrides + --- + duration_ms: 0.121104 + type: 'test' + ... +# Subtest: compactExportTable reports no compaction for a missing table +ok 3627 - compactExportTable reports no compaction for a missing table + --- + duration_ms: 8.366076 + type: 'test' + ... +# Subtest: compactExportTable reports a metadata load failure as reason=error, not no-table +ok 3628 - compactExportTable reports a metadata load failure as reason=error, not no-table + --- + duration_ms: 3.042675 + type: 'test' + ... +# Subtest: compactExportTable rewrites a v3 table once the data-file threshold is reached +ok 3629 - compactExportTable rewrites a v3 table once the data-file threshold is reached + --- + duration_ms: 24.104376 + type: 'test' + ... +# Subtest: compactExportTable skips a table whose total-files-size exceeds compact_max_bytes +ok 3630 - compactExportTable skips a table whose total-files-size exceeds compact_max_bytes + --- + duration_ms: 10.687744 + type: 'test' + ... +# Subtest: compactExportTable reports a commit conflict and cleans up the staged files +ok 3631 - compactExportTable reports a commit conflict and cleans up the staged files + --- + duration_ms: 16.782508 + type: 'test' + ... +# Subtest: compactExportTable reports a non-conflict commit failure as reason=error and leaves staged files +ok 3632 - compactExportTable reports a non-conflict commit failure as reason=error and leaves staged files + --- + duration_ms: 9.52747 + type: 'test' + ... +# Subtest: compactExportTable cleans up partial output when staging fails mid-flight +ok 3633 - compactExportTable cleans up partial output when staging fails mid-flight + --- + duration_ms: 8.789759 + type: 'test' + ... +# Subtest: compactExportTable reports success when the commit landed despite a thrown 412 +ok 3634 - compactExportTable reports success when the commit landed despite a thrown 412 + --- + duration_ms: 9.050546 + type: 'test' + ... +# Subtest: derivePartitioning returns null without a registration +ok 3635 - derivePartitioning returns null without a registration + --- + duration_ms: 0.830172 + type: 'test' + ... +# Subtest: derivePartitioning returns null when no primaryTimestampColumn +ok 3636 - derivePartitioning returns null when no primaryTimestampColumn + --- + duration_ms: 0.099361 + type: 'test' + ... +# Subtest: derivePartitioning returns null when the timestamp column is absent from the schema +ok 3637 - derivePartitioning returns null when the timestamp column is absent from the schema + --- + duration_ms: 0.114123 + type: 'test' + ... +# Subtest: derivePartitioning builds day(primaryTimestampColumn) + conversation sort for ai-gateway +ok 3638 - derivePartitioning builds day(primaryTimestampColumn) + conversation sort for ai-gateway + --- + duration_ms: 0.885415 + type: 'test' + ... +# Subtest: derivePartitioning day grain is independent of cachePartitioning (does not inherit conversation_id partitioning) +ok 3639 - derivePartitioning day grain is independent of cachePartitioning (does not inherit conversation_id partitioning) + --- + duration_ms: 0.185732 + type: 'test' + ... +# Subtest: derivePartitioning yields an empty (unsorted) order when no cachePartitioning is declared +ok 3640 - derivePartitioning yields an empty (unsorted) order when no cachePartitioning is declared + --- + duration_ms: 1.057297 + type: 'test' + ... +# Subtest: derivePartitioning excludes non-identity cachePartitioning fields from the sort +ok 3641 - derivePartitioning excludes non-identity cachePartitioning fields from the sort + --- + duration_ms: 0.232203 + type: 'test' + ... +# Subtest: commitBatch creates a day-partitioned, conversation-sorted table and buckets rows by day +ok 3642 - commitBatch creates a day-partitioned, conversation-sorted table and buckets rows by day + --- + duration_ms: 46.711507 + type: 'test' + ... +# Subtest: commitBatch rejects partition-spec drift on a previously-unpartitioned table +ok 3643 - commitBatch rejects partition-spec drift on a previously-unpartitioned table + --- + duration_ms: 10.042884 + type: 'test' + ... +# Subtest: commitBatch rejects reverse drift: null partitioning onto a partitioned table +ok 3644 - commitBatch rejects reverse drift: null partitioning onto a partitioned table + --- + duration_ms: 10.75689 + type: 'test' + ... +# Subtest: commitBatch accepts a re-commit with the same derived partitioning (no false drift) +ok 3645 - commitBatch accepts a re-commit with the same derived partitioning (no false drift) + --- + duration_ms: 20.251861 + type: 'test' + ... +# Subtest: format-iceberg manifest is valid +ok 3646 - format-iceberg manifest is valid + --- + duration_ms: 8.113381 + type: 'test' + ... +# Subtest: format-iceberg manifest passes the pure validator +ok 3647 - format-iceberg manifest passes the pure validator + --- + duration_ms: 1.829831 + type: 'test' + ... +# Subtest: format-iceberg is on the V1 bundled allowlist +ok 3648 - format-iceberg is on the V1 bundled allowlist + --- + duration_ms: 0.258322 + type: 'test' + ... +# Subtest: discoverBundledPlugins surfaces format-iceberg from the workspace +ok 3649 - discoverBundledPlugins surfaces format-iceberg from the workspace + --- + duration_ms: 10.367427 + type: 'test' + ... +# Subtest: firstPartyPluginMetadata records the iceberg requires/provides matrix +ok 3650 - firstPartyPluginMetadata records the iceberg requires/provides matrix + --- + duration_ms: 0.27598 + type: 'test' + ... +# Subtest: createTableFormatProvider returns the expected capability shape +ok 3651 - createTableFormatProvider returns the expected capability shape + --- + duration_ms: 0.139442 + type: 'test' + ... +# Subtest: TableFormatProvider.createSink rejects missing BlobStore / encoder / non-parquet encoder +ok 3652 - TableFormatProvider.createSink rejects missing BlobStore / encoder / non-parquet encoder + --- + duration_ms: 2.024447 + type: 'test' + ... +# Subtest: writer maps s3 errorKind=blob_precondition_failed -> iceberg iceberg_commit_conflict +ok 3653 - writer maps s3 errorKind=blob_precondition_failed -> iceberg iceberg_commit_conflict + --- + duration_ms: 27.997433 + type: 'test' + ... +# Subtest: writer maps s3 errorKind=s3_access_denied -> iceberg iceberg_blob_store_missing +ok 3654 - writer maps s3 errorKind=s3_access_denied -> iceberg iceberg_blob_store_missing + --- + duration_ms: 0.237962 + type: 'test' + ... +# Subtest: writer maps s3 errorKind=s3_bucket_missing -> iceberg iceberg_blob_store_missing +ok 3655 - writer maps s3 errorKind=s3_bucket_missing -> iceberg iceberg_blob_store_missing + --- + duration_ms: 0.198922 + type: 'test' + ... +# Subtest: writer maps s3 errorKind=s3_credentials_missing -> iceberg iceberg_blob_store_missing +ok 3656 - writer maps s3 errorKind=s3_credentials_missing -> iceberg iceberg_blob_store_missing + --- + duration_ms: 0.154876 + type: 'test' + ... +# Subtest: writer maps s3 errorKind=s3_region_mismatch -> iceberg iceberg_blob_store_missing +ok 3657 - writer maps s3 errorKind=s3_region_mismatch -> iceberg iceberg_blob_store_missing + --- + duration_ms: 0.15159 + type: 'test' + ... +# Subtest: writer maps s3 errorKind=s3_config_invalid -> iceberg iceberg_blob_store_missing +ok 3658 - writer maps s3 errorKind=s3_config_invalid -> iceberg iceberg_blob_store_missing + --- + duration_ms: 0.151971 + type: 'test' + ... +# Subtest: writer maps s3 errorKind=s3_blob_store_unconfigured -> iceberg iceberg_blob_store_missing +ok 3659 - writer maps s3 errorKind=s3_blob_store_unconfigured -> iceberg iceberg_blob_store_missing + --- + duration_ms: 0.142166 + type: 'test' + ... +# Subtest: writer maps s3 errorKind=s3_put_failed -> iceberg iceberg_data_write_failed +ok 3660 - writer maps s3 errorKind=s3_put_failed -> iceberg iceberg_data_write_failed + --- + duration_ms: 0.168115 + type: 'test' + ... +# Subtest: writer maps s3 errorKind=s3_throttled -> iceberg iceberg_data_write_failed +ok 3661 - writer maps s3 errorKind=s3_throttled -> iceberg iceberg_data_write_failed + --- + duration_ms: 0.563316 + type: 'test' + ... +# Subtest: reader maps s3 errorKind=s3_access_denied -> iceberg iceberg_blob_store_missing +ok 3662 - reader maps s3 errorKind=s3_access_denied -> iceberg iceberg_blob_store_missing + --- + duration_ms: 0.57264 + type: 'test' + ... +# Subtest: reader maps s3 errorKind=s3_bucket_missing -> iceberg iceberg_blob_store_missing +ok 3663 - reader maps s3 errorKind=s3_bucket_missing -> iceberg iceberg_blob_store_missing + --- + duration_ms: 0.248558 + type: 'test' + ... +# Subtest: reader maps s3 errorKind=s3_credentials_missing -> iceberg iceberg_blob_store_missing +ok 3664 - reader maps s3 errorKind=s3_credentials_missing -> iceberg iceberg_blob_store_missing + --- + duration_ms: 0.216429 + type: 'test' + ... +# Subtest: reader maps s3 errorKind=s3_put_failed -> iceberg iceberg_metadata_read_failed +ok 3665 - reader maps s3 errorKind=s3_put_failed -> iceberg iceberg_metadata_read_failed + --- + duration_ms: 0.198392 + type: 'test' + ... +# Subtest: reader maps s3 errorKind=s3_throttled -> iceberg iceberg_metadata_read_failed +ok 3666 - reader maps s3 errorKind=s3_throttled -> iceberg iceberg_metadata_read_failed + --- + duration_ms: 0.168947 + type: 'test' + ... +# Subtest: lister maps s3 errorKind=s3_access_denied -> iceberg iceberg_blob_store_missing +ok 3667 - lister maps s3 errorKind=s3_access_denied -> iceberg iceberg_blob_store_missing + --- + duration_ms: 0.325124 + type: 'test' + ... +# Subtest: lister maps s3 errorKind=s3_bucket_missing -> iceberg iceberg_blob_store_missing +ok 3668 - lister maps s3 errorKind=s3_bucket_missing -> iceberg iceberg_blob_store_missing + --- + duration_ms: 0.179693 + type: 'test' + ... +# Subtest: lister maps s3 errorKind=s3_put_failed -> iceberg iceberg_blob_io_list_failed +ok 3669 - lister maps s3 errorKind=s3_put_failed -> iceberg iceberg_blob_io_list_failed + --- + duration_ms: 0.177289 + type: 'test' + ... +# Subtest: writer onWrite observer fires with key + etag for successful puts +ok 3670 - writer onWrite observer fires with key + etag for successful puts + --- + duration_ms: 0.291132 + type: 'test' + ... +# Subtest: writer onWrite observer that throws does not break the commit +ok 3671 - writer onWrite observer that throws does not break the commit + --- + duration_ms: 0.239113 + type: 'test' + ... +# Subtest: icebergSchemaForColumns maps every kernel basic type +ok 3672 - icebergSchemaForColumns maps every kernel basic type + --- + duration_ms: 1.279976 + type: 'test' + ... +# Subtest: mergeFieldIdsFromTable preserves existing field ids and appends nullable additions +ok 3673 - mergeFieldIdsFromTable preserves existing field ids and appends nullable additions + --- + duration_ms: 0.371294 + type: 'test' + ... +# Subtest: mergeFieldIdsFromTable rejects incompatible type changes with iceberg_schema_incompatible +ok 3674 - mergeFieldIdsFromTable rejects incompatible type changes with iceberg_schema_incompatible + --- + duration_ms: 0.339977 + type: 'test' + ... +# Subtest: mergeFieldIdsFromTable rejects new required columns +ok 3675 - mergeFieldIdsFromTable rejects new required columns + --- + duration_ms: 0.137779 + type: 'test' + ... +# Subtest: mergeFieldIdsFromTable rejects column removals +ok 3676 - mergeFieldIdsFromTable rejects column removals + --- + duration_ms: 0.420139 + type: 'test' + ... +# Subtest: mergeFieldIdsFromTable rejects nullable → required tightening +ok 3677 - mergeFieldIdsFromTable rejects nullable → required tightening + --- + duration_ms: 0.141846 + type: 'test' + ... +# Subtest: rowsToIcebergRecords coerces numeric strings and BigInt for INT64 +ok 3678 - rowsToIcebergRecords coerces numeric strings and BigInt for INT64 + --- + duration_ms: 0.257832 + type: 'test' + ... +# Subtest: rowsToIcebergRecords throws iceberg_data_write_failed on required nulls +ok 3679 - rowsToIcebergRecords throws iceberg_data_write_failed on required nulls + --- + duration_ms: 0.146443 + type: 'test' + ... +# Subtest: rowsToIcebergRecords canonicalizes JSON objects to strings +ok 3680 - rowsToIcebergRecords canonicalizes JSON objects to strings + --- + duration_ms: 0.28925 + type: 'test' + ... +# Subtest: markerKey renders /state/exported-batches///.json +ok 3681 - markerKey renders /state/exported-batches///.json + --- + duration_ms: 0.96029 + type: 'test' + ... +# Subtest: markerKey rejects empty segments with iceberg_state_invalid +ok 3682 - markerKey rejects empty segments with iceberg_state_invalid + --- + duration_ms: 1.334879 + type: 'test' + ... +# Subtest: markerKey sanitizes path-separator characters out of dataset/batch ids +ok 3683 - markerKey sanitizes path-separator characters out of dataset/batch ids + --- + duration_ms: 0.117338 + type: 'test' + ... +# Subtest: writeMarker / loadMarker roundtrips a record verbatim +ok 3684 - writeMarker / loadMarker roundtrips a record verbatim + --- + duration_ms: 2.780506 + type: 'test' + ... +# Subtest: loadMarker returns null for missing markers +ok 3685 - loadMarker returns null for missing markers + --- + duration_ms: 0.203659 + type: 'test' + ... +# Subtest: loadMarker surfaces malformed JSON as iceberg_metadata_read_failed +ok 3686 - loadMarker surfaces malformed JSON as iceberg_metadata_read_failed + --- + duration_ms: 0.760986 + type: 'test' + ... +# Subtest: markerSubsumedBySnapshot returns true when current snapshot matches the marker exactly +ok 3687 - markerSubsumedBySnapshot returns true when current snapshot matches the marker exactly + --- + duration_ms: 0.228637 + type: 'test' + ... +# Subtest: markerSubsumedBySnapshot accepts a probe-state object and resolves equality without metadata +ok 3688 - markerSubsumedBySnapshot accepts a probe-state object and resolves equality without metadata + --- + duration_ms: 0.120072 + type: 'test' + ... +# Subtest: markerSubsumedBySnapshot walks parent-snapshot-id to recognise a superseded ancestor +ok 3689 - markerSubsumedBySnapshot walks parent-snapshot-id to recognise a superseded ancestor + --- + duration_ms: 0.305465 + type: 'test' + ... +# Subtest: markerSubsumedBySnapshot returns false when the marker snapshot is no longer in the snapshot graph (expired) +ok 3690 - markerSubsumedBySnapshot returns false when the marker snapshot is no longer in the snapshot graph (expired) + --- + duration_ms: 0.340408 + type: 'test' + ... +# Subtest: markerSubsumedBySnapshot tolerates a malformed cyclic parent chain without spinning +ok 3691 - markerSubsumedBySnapshot tolerates a malformed cyclic parent chain without spinning + --- + duration_ms: 0.174806 + type: 'test' + ... +# Subtest: local-fs incremental export: ranged filename, watermark advance, skip-empty, then a new range +ok 3692 - local-fs incremental export: ranged filename, watermark advance, skip-empty, then a new range + --- + duration_ms: 23.229227 + type: 'test' + ... +# Subtest: local-fs drop-only tick: no blob is written, but the watermark advances past the withheld rows (LLP 0070) +ok 3693 - local-fs drop-only tick: no blob is written, but the watermark advances past the withheld rows (LLP 0070) + --- + duration_ms: 10.604858 + type: 'test' + ... +# Subtest: attach writes exactly the two provider entries, bare origin vs +/v1 +ok 3694 - attach writes exactly the two provider entries, bare origin vs +/v1 + --- + duration_ms: 11.431785 + type: 'test' + ... +# Subtest: attach preserves every other key in openclaw.json (R1) +ok 3695 - attach preserves every other key in openclaw.json (R1) + --- + duration_ms: 3.627833 + type: 'test' + ... +# Subtest: attach refuses without writing when a provider key already exists (R2) +ok 3696 - attach refuses without writing when a provider key already exists (R2) + --- + duration_ms: 10.436933 + type: 'test' + ... +# Subtest: a second attach at a moved endpoint rewrites both baseUrls (re-attach on drift) +ok 3697 - a second attach at a moved endpoint rewrites both baseUrls (re-attach on drift) + --- + duration_ms: 4.395601 + type: 'test' + ... +# Subtest: re-attach over a pre-client-header entry succeeds and adds the header +ok 3698 - re-attach over a pre-client-header entry succeeds and adds the header + --- + duration_ms: 6.18473 + type: 'test' + ... +# Subtest: an entry that is not ours still refuses, however close it looks (R2) +ok 3699 - an entry that is not ours still refuses, however close it looks (R2) + --- + duration_ms: 11.732372 + type: 'test' + ... +# Subtest: attach never throws on refusal, so attach-on-join warns instead of failing +ok 3700 - attach never throws on refusal, so attach-on-join warns instead of failing + --- + duration_ms: 2.304322 + type: 'test' + ... +# Subtest: attach prints the openclaw gateway restart instruction on the human path (R4) +ok 3701 - attach prints the openclaw gateway restart instruction on the human path (R4) + --- + duration_ms: 2.418867 + type: 'test' + ... +# Subtest: attach prints the restart instruction on the --json path too (R4) +ok 3702 - attach prints the restart instruction on the --json path too (R4) + --- + duration_ms: 2.562474 + type: 'test' + ... +# Subtest: attach --dry-run reports the write without touching the file +ok 3703 - attach --dry-run reports the write without touching the file + --- + duration_ms: 2.524196 + type: 'test' + ... +# Subtest: attach --dry-run reports the refusal it would hit, not a write it would not do +ok 3704 - attach --dry-run reports the refusal it would hit, not a write it would not do + --- + duration_ms: 2.187115 + type: 'test' + ... +# Subtest: attach resolves openclaw.json through $OPENCLAW_HOME when set +ok 3705 - attach resolves openclaw.json through $OPENCLAW_HOME when set + --- + duration_ms: 6.649637 + type: 'test' + ... +# Subtest: a missing openclaw.json is a hard failure, not a config attach invents +ok 3706 - a missing openclaw.json is a hard failure, not a config attach invents + --- + duration_ms: 4.58565 + type: 'test' + ... +# Subtest: a malformed openclaw.json is a hard failure, and is left alone +ok 3707 - a malformed openclaw.json is a hard failure, and is left alone + --- + duration_ms: 4.711051 + type: 'test' + ... +# Subtest: a trailing slash on the endpoint does not double the openai /v1 separator +ok 3708 - a trailing slash on the endpoint does not double the openai /v1 separator + --- + duration_ms: 2.544318 + type: 'test' + ... +# Subtest: the fixture writes the record shape OpenClaw actually appends +ok 3709 - the fixture writes the record shape OpenClaw actually appends + --- + duration_ms: 10.525939 + type: 'test' + ... +# Subtest: projects one item per session file, with the header cwd and native session id +ok 3710 - projects one item per session file, with the header cwd and native session id + --- + duration_ms: 5.805724 + type: 'test' + ... +# Subtest: backfilled rows carry the session file's own message ids, never a fallback hash +ok 3711 - backfilled rows carry the session file's own message ids, never a fallback hash + --- + duration_ms: 4.456333 + type: 'test' + ... +# Subtest: the gateway chains previous_message_id across the session, rooting the first message +ok 3712 - the gateway chains previous_message_id across the session, rooting the first message + --- + duration_ms: 4.404234 + type: 'test' + ... +# Subtest: assistant usage lands under the gateway-wide token names, whatever spelling the file used +ok 3713 - assistant usage lands under the gateway-wide token names, whatever spelling the file used + --- + duration_ms: 4.477015 + type: 'test' + ... +# Subtest: a toolResult record lands under the role the sibling adapters already write +ok 3714 - a toolResult record lands under the role the sibling adapters already write + --- + duration_ms: 8.566751 + type: 'test' + ... +# Subtest: a backfilled row and a settled live row for the same turn carry identical message_id/part_id +ok 3715 - a backfilled row and a settled live row for the same turn carry identical message_id/part_id + --- + duration_ms: 5.75671 + type: 'test' + ... +# Subtest: a session whose header cwd is policy-ignored projects nothing at all +ok 3716 - a session whose header cwd is policy-ignored projects nothing at all + --- + duration_ms: 2.952778 + type: 'test' + ... +# Subtest: a session with no usable cwd is not gated, matching the existing convention +ok 3717 - a session with no usable cwd is not gated, matching the existing convention + --- + duration_ms: 3.028744 + type: 'test' + ... +# Subtest: an unrelated ignored directory leaves the session projecting +ok 3718 - an unrelated ignored directory leaves the session projecting + --- + duration_ms: 2.937905 + type: 'test' + ... +# Subtest: a claude-cli turn is excluded whole, prompt included, and reported as covered elsewhere +ok 3719 - a claude-cli turn is excluded whole, prompt included, and reported as covered elsewhere + --- + duration_ms: 4.213734 + type: 'test' + ... +# Subtest: a mixed real-shape session partially projects: anthropic turns land, claude-cli turns stay excluded +ok 3720 - a mixed real-shape session partially projects: anthropic turns land, claude-cli turns stay excluded + --- + duration_ms: 3.672832 + type: 'test' + ... +# Subtest: an unrecognized direct-API provider projects, no release needed +ok 3721 - an unrecognized direct-API provider projects, no release needed + --- + duration_ms: 1.916743 + type: 'test' + ... +# Subtest: an ollama turn projects at transcript fidelity, each row under its own provider +ok 3722 - an ollama turn projects at transcript fidelity, each row under its own provider + --- + duration_ms: 1.794487 + type: 'test' + ... +# Subtest: an unrecognized provider stamped api "cli" is excluded, and not covered_by anything +ok 3723 - an unrecognized provider stamped api "cli" is excluded, and not covered_by anything + --- + duration_ms: 1.5203 + type: 'test' + ... +# Subtest: a claude-cli turn stamped api "cli" is excluded and covered by the Claude transcript +ok 3724 - a claude-cli turn stamped api "cli" is excluded and covered by the Claude transcript + --- + duration_ms: 1.916093 + type: 'test' + ... +# Subtest: a prompt whose turn has no anchor is excluded as unknown, not projected under the next turn +ok 3725 - a prompt whose turn has no anchor is excluded as unknown, not projected under the next turn + --- + duration_ms: 2.030817 + type: 'test' + ... +# Subtest: a tool result after a mid-loop abort resolves to its own turn, not the next one +ok 3726 - a tool result after a mid-loop abort resolves to its own turn, not the next one + --- + duration_ms: 2.607453 + type: 'test' + ... +# Subtest: a trailing unanswered prompt is unknown, not the previous turn's backend +ok 3727 - a trailing unanswered prompt is unknown, not the previous turn's backend + --- + duration_ms: 2.541694 + type: 'test' + ... +# Subtest: a window cut between prompt and reply does not change the prompt's attribution +ok 3728 - a window cut between prompt and reply does not change the prompt's attribution + --- + duration_ms: 1.999198 + type: 'test' + ... +# Subtest: a record stating only provider does not inherit a neighbor's api +ok 3729 - a record stating only provider does not inherit a neighbor's api + --- + duration_ms: 2.014813 + type: 'test' + ... +# Subtest: codex and codex-mini are denied by prefix with their covering route; codexcloud is neither denied nor mislabeled +ok 3730 - codex and codex-mini are denied by prefix with their covering route; codexcloud is neither denied nor mislabeled + --- + duration_ms: 2.313386 + type: 'test' + ... +# Subtest: whitespace and case on api or provider do not slip the denylist +ok 3731 - whitespace and case on api or provider do not slip the denylist + --- + duration_ms: 2.090147 + type: 'test' + ... +# Subtest: an api-only record does not block the borrow to the turn's real anchor +ok 3732 - an api-only record does not block the borrow to the turn's real anchor + --- + duration_ms: 2.139382 + type: 'test' + ... +# Subtest: a session that never states a provider projects nothing +ok 3733 - a session that never states a provider projects nothing + --- + duration_ms: 2.186374 + type: 'test' + ... +# Subtest: records outside the resolved window are not projected +ok 3734 - records outside the resolved window are not projected + --- + duration_ms: 2.620383 + type: 'test' + ... +# Subtest: every agent under the agents root is scanned, in a deterministic order +ok 3735 - every agent under the agents root is scanned, in a deterministic order + --- + duration_ms: 2.422952 + type: 'test' + ... +# Subtest: a relocated install is found through OPENCLAW_HOME, the same way settlement finds it +ok 3736 - a relocated install is found through OPENCLAW_HOME, the same way settlement finds it + --- + duration_ms: 1.896152 + type: 'test' + ... +# Subtest: a missing OpenClaw install scans to zero sessions rather than failing the run +ok 3737 - a missing OpenClaw install scans to zero sessions rather than failing the run + --- + duration_ms: 0.792664 + type: 'test' + ... +# Subtest: a session file with no readable header still projects, ungated +ok 3738 - a session file with no readable header still projects, ungated + --- + duration_ms: 3.007541 + type: 'test' + ... +# Subtest: plan() reports the session files a run would scan without projecting them +ok 3739 - plan() reports the session files a run would scan without projecting them + --- + duration_ms: 1.678161 + type: 'test' + ... +# Subtest: reruns are deterministic: the same session yields byte-identical row identity +ok 3740 - reruns are deterministic: the same session yields byte-identical row identity + --- + duration_ms: 3.69148 + type: 'test' + ... +# Subtest: a session file rotated by a reset is scanned, and projects what it projected before the rotation +ok 3741 - a session file rotated by a reset is scanned, and projects what it projected before the rotation + --- + duration_ms: 3.821668 + type: 'test' + ... +# Subtest: a session file rotated by a delete is scanned the same way +ok 3742 - a session file rotated by a delete is scanned the same way + --- + duration_ms: 1.770511 + type: 'test' + ... +# Subtest: a headerless rotated file takes its session id with the .jsonl extension and rotation marker removed +ok 3743 - a headerless rotated file takes its session id with the .jsonl extension and rotation marker removed + --- + duration_ms: 1.77622 + type: 'test' + ... +# Subtest: a trajectory sibling stays distinguishable, and a non-rotation suffix is still skipped +ok 3744 - a trajectory sibling stays distinguishable, and a non-rotation suffix is still skipped + --- + duration_ms: 1.972688 + type: 'test' + ... +# Subtest: a headerless trajectory file resolves its own id, not the session it sits beside +ok 3745 - a headerless trajectory file resolves its own id, not the session it sits beside + --- + duration_ms: 1.936313 + type: 'test' + ... +# Subtest: sweep.cron reads the configured backfill.sweep_cron value +ok 3746 - sweep.cron reads the configured backfill.sweep_cron value + --- + duration_ms: 0.599762 + type: 'test' + ... +# Subtest: sweep.cron falls back to the every-5-minutes default when config is absent +ok 3747 - sweep.cron falls back to the every-5-minutes default when config is absent + --- + duration_ms: 0.473119 + type: 'test' + ... +# Subtest: a session file with mtime inside the quiesce window is excluded from the run +ok 3748 - a session file with mtime inside the quiesce window is excluded from the run + --- + duration_ms: 2.689157 + type: 'test' + ... +# Subtest: a session file with mtime outside the quiesce window is included +ok 3749 - a session file with mtime outside the quiesce window is included + --- + duration_ms: 2.088654 + type: 'test' + ... +# Subtest: the quiesce window defaults to exactly 180000ms when config.backfill.quiesce_ms is absent +ok 3750 - the quiesce window defaults to exactly 180000ms when config.backfill.quiesce_ms is absent + --- + duration_ms: 2.048394 + type: 'test' + ... +# Subtest: the default quiesce window excludes a fresh file and includes one older than three minutes +ok 3751 - the default quiesce window excludes a fresh file and includes one older than three minutes + --- + duration_ms: 2.510936 + type: 'test' + ... +# Subtest: a file outside the quiesce window still goes through the CLI-backend exclusion unchanged +ok 3752 - a file outside the quiesce window still goes through the CLI-backend exclusion unchanged + --- + duration_ms: 7.024787 + type: 'test' + ... +# Subtest: activate() registers the openclaw client and wires attach() to the real write +ok 3753 - activate() registers the openclaw client and wires attach() to the real write + --- + duration_ms: 9.335258 + type: 'test' + ... +# Subtest: activate() threads ctx.config into the backfill provider (sweep_cron and quiesce_ms) +ok 3754 - activate() threads ctx.config into the backfill provider (sweep_cron and quiesce_ms) + --- + duration_ms: 1.963405 + type: 'test' + ... +# Subtest: activate() attach() reports the same write under --json +ok 3755 - activate() attach() reports the same write under --json + --- + duration_ms: 4.698391 + type: 'test' + ... +# Subtest: activate() attach() rethrows a refusal, marked so it is not retried forever +ok 3756 - activate() attach() rethrows a refusal, marked so it is not retried forever + --- + duration_ms: 1.778223 + type: 'test' + ... +# Subtest: activate() attach() rethrows a hard failure unmarked, so it is still retried +ok 3757 - activate() attach() rethrows a hard failure unmarked, so it is still retried + --- + duration_ms: 1.52632 + type: 'test' + ... +# Subtest: a refused openclaw attach is a refused reconciler outcome, not done, and the join continues +ok 3758 - a refused openclaw attach is a refused reconciler outcome, not done, and the join continues + --- + duration_ms: 1.816521 + type: 'test' + ... +# Subtest: activate() registers the settlement enricher right after the exchange projector +ok 3759 - activate() registers the settlement enricher right after the exchange projector + --- + duration_ms: 0.27017 + type: 'test' + ... +# Subtest: hyp attach openclaw resolves the client and does not error unknown client +ok 3760 - hyp attach openclaw resolves the client and does not error unknown client + --- + duration_ms: 7.089756 + type: 'test' + ... +# Subtest: hyp attach openclaw exits nonzero when the attach refuses +ok 3761 - hyp attach openclaw exits nonzero when the attach refuses + --- + duration_ms: 1.534011 + type: 'test' + ... +# Subtest: hyp detach openclaw resolves the client from the real manifest as an honest no-op +ok 3762 - hyp detach openclaw resolves the client from the real manifest as an honest no-op + --- + duration_ms: 5.101504 + type: 'test' + ... +# Subtest: hyp detach openclaw reverses a real attach: the ownership-based json_path undo fires end to end +ok 3763 - hyp detach openclaw reverses a real attach: the ownership-based json_path undo fires end to end + --- + duration_ms: 8.652781 + type: 'test' + ... +# Subtest: hyp clients: the descriptor map behind client listing/status resolves openclaw +ok 3764 - hyp clients: the descriptor map behind client listing/status resolves openclaw + --- + duration_ms: 3.097167 + type: 'test' + ... +# Subtest: buildAttachPluginCatalog returns the full catalog, not just clientDescriptors +ok 3765 - buildAttachPluginCatalog returns the full catalog, not just clientDescriptors + --- + duration_ms: 5.054703 + type: 'test' + ... +# Subtest: validateOpenclawConfig accepts an empty / absent config +ok 3766 - validateOpenclawConfig accepts an empty / absent config + --- + duration_ms: 1.409332 + type: 'test' + ... +# Subtest: validateOpenclawConfig leaves non-attach keys (e.g. proxy) untouched +ok 3767 - validateOpenclawConfig leaves non-attach keys (e.g. proxy) untouched + --- + duration_ms: 0.183178 + type: 'test' + ... +# Subtest: validateOpenclawConfig accepts the attach policy block +ok 3768 - validateOpenclawConfig accepts the attach policy block + --- + duration_ms: 0.176608 + type: 'test' + ... +# Subtest: validateOpenclawConfig rejects a non-object config +ok 3769 - validateOpenclawConfig rejects a non-object config + --- + duration_ms: 0.121164 + type: 'test' + ... +# Subtest: validateOpenclawConfig rejects a malformed attach block +ok 3770 - validateOpenclawConfig rejects a malformed attach block + --- + duration_ms: 0.173343 + type: 'test' + ... +# Subtest: validateAttachSection mounts errors at the caller-supplied pointer +ok 3771 - validateAttachSection mounts errors at the caller-supplied pointer + --- + duration_ms: 0.071248 + type: 'test' + ... +# Subtest: the section name matches the manifest config_sections entry +ok 3772 - the section name matches the manifest config_sections entry + --- + duration_ms: 0.094754 + type: 'test' + ... +# Subtest: validateOpenclawConfig accepts a full backfill block +ok 3773 - validateOpenclawConfig accepts a full backfill block + --- + duration_ms: 0.143989 + type: 'test' + ... +# Subtest: validateOpenclawConfig rejects a malformed backfill block +ok 3774 - validateOpenclawConfig rejects a malformed backfill block + --- + duration_ms: 0.383533 + type: 'test' + ... +# Subtest: validateBackfillSection mounts errors at the caller-supplied pointer +ok 3775 - validateBackfillSection mounts errors at the caller-supplied pointer + --- + duration_ms: 0.357213 + type: 'test' + ... +# Subtest: validateOpenclawConfig accepts sweep_cron and quiesce_ms +ok 3776 - validateOpenclawConfig accepts sweep_cron and quiesce_ms + --- + duration_ms: 0.455302 + type: 'test' + ... +# Subtest: validateOpenclawConfig rejects an invalid sweep_cron +ok 3777 - validateOpenclawConfig rejects an invalid sweep_cron + --- + duration_ms: 0.143979 + type: 'test' + ... +# Subtest: validateOpenclawConfig rejects a negative quiesce_ms +ok 3778 - validateOpenclawConfig rejects a negative quiesce_ms + --- + duration_ms: 0.093883 + type: 'test' + ... +# Subtest: validateOpenclawConfig rejects a non-integer quiesce_ms +ok 3779 - validateOpenclawConfig rejects a non-integer quiesce_ms + --- + duration_ms: 0.119611 + type: 'test' + ... +# Subtest: validateOpenclawConfig still rejects a genuinely unknown backfill key +ok 3780 - validateOpenclawConfig still rejects a genuinely unknown backfill key + --- + duration_ms: 0.107724 + type: 'test' + ... +# Subtest: openclaw manifest loads and validates +ok 3781 - openclaw manifest loads and validates + --- + duration_ms: 5.066861 + type: 'test' + ... +# Subtest: openclaw contributes.client.attach_probe parses to the exact json_path shape +ok 3782 - openclaw contributes.client.attach_probe parses to the exact json_path shape + --- + duration_ms: 1.414921 + type: 'test' + ... +# Subtest: openclaw description and picker summary no longer reference the steering plugin +ok 3783 - openclaw description and picker summary no longer reference the steering plugin + --- + duration_ms: 1.742588 + type: 'test' + ... +# Subtest: openclaw description and picker summary state the two capture tiers directly +ok 3784 - openclaw description and picker summary state the two capture tiers directly + --- + duration_ms: 1.50021 + type: 'test' + ... +# Subtest: claude manifest onboarding copy names the claude-cli OpenClaw case +ok 3785 - claude manifest onboarding copy names the claude-cli OpenClaw case + --- + duration_ms: 0.979879 + type: 'test' + ... +# Subtest: canonicalMatchKey is a pure function of role and tuples +ok 3786 - canonicalMatchKey is a pure function of role and tuples + --- + duration_ms: 1.559671 + type: 'test' + ... +# Subtest: wireMatchKey: same text content on the same role matches regardless of key order +ok 3787 - wireMatchKey: same text content on the same role matches regardless of key order + --- + duration_ms: 0.54616 + type: 'test' + ... +# Subtest: wireMatchKey: a bare string content and its one-block-array equivalent match +ok 3788 - wireMatchKey: a bare string content and its one-block-array equivalent match + --- + duration_ms: 0.149497 + type: 'test' + ... +# Subtest: wireMatchKey: empty string content and empty array content both yield the empty-block key +ok 3789 - wireMatchKey: empty string content and empty array content both yield the empty-block key + --- + duration_ms: 0.121635 + type: 'test' + ... +# Subtest: wireMatchKey: an OpenClaw wire timestamp prefix matches the bare session text +ok 3790 - wireMatchKey: an OpenClaw wire timestamp prefix matches the bare session text + --- + duration_ms: 0.256549 + type: 'test' + ... +# Subtest: wireMatchKey: the timestamp strip tolerates seconds and short zone names +ok 3791 - wireMatchKey: the timestamp strip tolerates seconds and short zone names + --- + duration_ms: 0.141656 + type: 'test' + ... +# Subtest: wireMatchKey: the timestamp strip accepts GMT-offset short zone names +ok 3792 - wireMatchKey: the timestamp strip accepts GMT-offset short zone names + --- + duration_ms: 0.292595 + type: 'test' + ... +# Subtest: wireMatchKey: a stamp on both sides still matches (strip is symmetric) +ok 3793 - wireMatchKey: a stamp on both sides still matches (strip is symmetric) + --- + duration_ms: 0.120153 + type: 'test' + ... +# Subtest: wireMatchKey: near-miss brackets are not stripped +ok 3794 - wireMatchKey: near-miss brackets are not stripped + --- + duration_ms: 0.36846 + type: 'test' + ... +# Subtest: wireMatchKey: only the leading stamp strips, not one mid-text +ok 3795 - wireMatchKey: only the leading stamp strips, not one mid-text + --- + duration_ms: 0.292805 + type: 'test' + ... +# Subtest: wireMatchKey strips volatile block fields before hashing +ok 3796 - wireMatchKey strips volatile block fields before hashing + --- + duration_ms: 0.15839 + type: 'test' + ... +# Subtest: wireMatchKey: tool_use identity is content-based, not id-based +ok 3797 - wireMatchKey: tool_use identity is content-based, not id-based + --- + duration_ms: 0.107093 + type: 'test' + ... +# Subtest: wireMatchKey: tool_result identity depends only on content +ok 3798 - wireMatchKey: tool_result identity depends only on content + --- + duration_ms: 0.120023 + type: 'test' + ... +# Subtest: sessionMatchKey: toolCall session block matches an equivalent wire tool_use block +ok 3799 - sessionMatchKey: toolCall session block matches an equivalent wire tool_use block + --- + duration_ms: 0.113782 + type: 'test' + ... +# Subtest: sessionMatchKey: toolUse and function_call synonyms fold onto the same tool_use identity +ok 3800 - sessionMatchKey: toolUse and function_call synonyms fold onto the same tool_use identity + --- + duration_ms: 0.130007 + type: 'test' + ... +# Subtest: sessionMatchKey: redacted_thinking folds onto the thinking kind +ok 3801 - sessionMatchKey: redacted_thinking folds onto the thinking kind + --- + duration_ms: 0.110598 + type: 'test' + ... +# Subtest: sessionMatchKey: a standalone toolResult record matches the wire tool_result-in-user shape +ok 3802 - sessionMatchKey: a standalone toolResult record matches the wire tool_result-in-user shape + --- + duration_ms: 0.115947 + type: 'test' + ... +# Subtest: sessionMatchKey: toolResult content array and equivalent string content agree with the wire side +ok 3803 - sessionMatchKey: toolResult content array and equivalent string content agree with the wire side + --- + duration_ms: 0.882131 + type: 'test' + ... +# Subtest: sessionMatchKey: toolResult identity ignores toolCallId, mirroring wireMatchKey ignoring tool_use_id +ok 3804 - sessionMatchKey: toolResult identity ignores toolCallId, mirroring wireMatchKey ignoring tool_use_id + --- + duration_ms: 0.102376 + type: 'test' + ... +# Subtest: sessionMatchKey: an OpenAI-shaped role: "tool" record is an accepted residue, not normalized +ok 3805 - sessionMatchKey: an OpenAI-shaped role: "tool" record is an accepted residue, not normalized + --- + duration_ms: 0.120443 + type: 'test' + ... +# Subtest: ordinalFallbackKey combines role and ordinal into one readable string +ok 3806 - ordinalFallbackKey combines role and ordinal into one readable string + --- + duration_ms: 0.077979 + type: 'test' + ... +# Subtest: withRoleOrdinals assigns 1-based ordinals per role in given order +ok 3807 - withRoleOrdinals assigns 1-based ordinals per role in given order + --- + duration_ms: 1.470104 + type: 'test' + ... +# Subtest: matchOrdinalFallback finds the closest-in-time candidate within the window +ok 3808 - matchOrdinalFallback finds the closest-in-time candidate within the window + --- + duration_ms: 0.201296 + type: 'test' + ... +# Subtest: matchOrdinalFallback returns undefined outside the window bound +ok 3809 - matchOrdinalFallback returns undefined outside the window bound + --- + duration_ms: 0.143107 + type: 'test' + ... +# Subtest: matchOrdinalFallback returns undefined for an unknown role/ordinal position +ok 3810 - matchOrdinalFallback returns undefined for an unknown role/ordinal position + --- + duration_ms: 0.091499 + type: 'test' + ... +# Subtest: matchOrdinalFallback picks the nearest candidate when several share a position across replays +ok 3811 - matchOrdinalFallback picks the nearest candidate when several share a position across replays + --- + duration_ms: 0.095294 + type: 'test' + ... +# Subtest: shape detection: path, request body, and response body each suffice +ok 3812 - shape detection: path, request body, and response body each suffice + --- + duration_ms: 1.029164 + type: 'test' + ... +# Subtest: a full Responses exchange projects request turns plus the assistant +ok 3813 - a full Responses exchange projects request turns plus the assistant + --- + duration_ms: 3.396092 + type: 'test' + ... +# Subtest: a bare-string input is one user turn (the shorthand form) +ok 3814 - a bare-string input is one user turn (the shorthand form) + --- + duration_ms: 0.330762 + type: 'test' + ... +# Subtest: function_call and function_call_output items map to tool_use / tool_result turns +ok 3815 - function_call and function_call_output items map to tool_use / tool_result turns + --- + duration_ms: 0.24366 + type: 'test' + ... +# Subtest: request-side reasoning replay items project nothing +ok 3816 - request-side reasoning replay items project nothing + --- + duration_ms: 0.145351 + type: 'test' + ... +# Subtest: instructions and the leading system items fold into system_text; mid-run ones stay turns +ok 3817 - instructions and the leading system items fold into system_text; mid-run ones stay turns + --- + duration_ms: 0.266936 + type: 'test' + ... +# Subtest: a streamed exchange reconstructs from the terminal response.completed event +ok 3818 - a streamed exchange reconstructs from the terminal response.completed event + --- + duration_ms: 0.444215 + type: 'test' + ... +# Subtest: a stream cut before its terminal event degrades to finished items, marked error +ok 3819 - a stream cut before its terminal event degrades to finished items, marked error + --- + duration_ms: 0.340227 + type: 'test' + ... +# Subtest: a stream cut with zero finished items emits no assistant row +ok 3820 - a stream cut with zero finished items emits no assistant row + --- + duration_ms: 1.609116 + type: 'test' + ... +# Subtest: an incomplete response records its incomplete reason as stop_reason +ok 3821 - an incomplete response records its incomplete reason as stop_reason + --- + duration_ms: 0.431926 + type: 'test' + ... +# Subtest: Responses tool-call turns match the session file toolCall shape +ok 3822 - Responses tool-call turns match the session file toolCall shape + --- + duration_ms: 0.332466 + type: 'test' + ... +# Subtest: a projected Responses text turn matches its bare session-file text +ok 3823 - a projected Responses text turn matches its bare session-file text + --- + duration_ms: 0.141345 + type: 'test' + ... +# Subtest: project() falls back to the anthropic shape and provider when the upstream header is absent +ok 3824 - project() falls back to the anthropic shape and provider when the upstream header is absent + --- + duration_ms: 2.521873 + type: 'test' + ... +# Subtest: project() reads the anthropic shape when the upstream header names anthropic +ok 3825 - project() reads the anthropic shape when the upstream header names anthropic + --- + duration_ms: 0.429833 + type: 'test' + ... +# Subtest: project() records an unrecognized upstream verbatim and keeps the anthropic parse +ok 3826 - project() records an unrecognized upstream verbatim and keeps the anthropic parse + --- + duration_ms: 0.325475 + type: 'test' + ... +# Subtest: project() maps a non-streamed OpenAI Chat Completions exchange +ok 3827 - project() maps a non-streamed OpenAI Chat Completions exchange + --- + duration_ms: 0.747726 + type: 'test' + ... +# Subtest: project() keys the OpenAI session on the system-prompt head, like the anthropic shape +ok 3828 - project() keys the OpenAI session on the system-prompt head, like the anthropic shape + --- + duration_ms: 0.794178 + type: 'test' + ... +# Subtest: project() normalizes OpenAI tool calls and tool results into shared blocks +ok 3829 - project() normalizes OpenAI tool calls and tool results into shared blocks + --- + duration_ms: 0.389552 + type: 'test' + ... +# Subtest: project() assembles a streamed OpenAI assistant message from SSE chunks +ok 3830 - project() assembles a streamed OpenAI assistant message from SSE chunks + --- + duration_ms: 0.76358 + type: 'test' + ... +# Subtest: project() accumulates a streamed OpenAI tool call across chunks +ok 3831 - project() accumulates a streamed OpenAI tool call across chunks + --- + duration_ms: 0.327538 + type: 'test' + ... +# Subtest: project() preserves an argument-less streamed OpenAI tool call as empty input +ok 3832 - project() preserves an argument-less streamed OpenAI tool call as empty input + --- + duration_ms: 0.710419 + type: 'test' + ... +# Subtest: project() marks a truncated OpenAI stream stop_reason=error +ok 3833 - project() marks a truncated OpenAI stream stop_reason=error + --- + duration_ms: 0.474561 + type: 'test' + ... +# Subtest: project() emits no assistant row for a cut OpenAI stream that carried no content +ok 3834 - project() emits no assistant row for a cut OpenAI stream that carried no content + --- + duration_ms: 0.316541 + type: 'test' + ... +# Subtest: project() emits no assistant row for a cut Anthropic stream that carried no content +ok 3835 - project() emits no assistant row for a cut Anthropic stream that carried no content + --- + duration_ms: 0.399186 + type: 'test' + ... +# Subtest: project() still records a terminal Anthropic stream whose content is genuinely empty +ok 3836 - project() still records a terminal Anthropic stream whose content is genuinely empty + --- + duration_ms: 0.277592 + type: 'test' + ... +# Subtest: project() still records a terminal OpenAI stream whose content is genuinely empty +ok 3837 - project() still records a terminal OpenAI stream whose content is genuinely empty + --- + duration_ms: 0.201005 + type: 'test' + ... +# Subtest: project() still records a non-streamed OpenAI response whose content is empty +ok 3838 - project() still records a non-streamed OpenAI response whose content is empty + --- + duration_ms: 1.196288 + type: 'test' + ... +# Subtest: project() still records a terminal response whose wire stop reason is literally error +ok 3839 - project() still records a terminal response whose wire stop reason is literally error + --- + duration_ms: 0.48052 + type: 'test' + ... +# Subtest: project() never drops a request-history assistant turn that replays stop_reason error +ok 3840 - project() never drops a request-history assistant turn that replays stop_reason error + --- + duration_ms: 0.222488 + type: 'test' + ... +# Subtest: the cut-stream marker is only ever stamped through markCutStream +ok 3841 - the cut-stream marker is only ever stamped through markCutStream + --- + duration_ms: 0.379988 + type: 'test' + ... +# Subtest: project() stamps openclaw.match_key on every row of both shapes +ok 3842 - project() stamps openclaw.match_key on every row of both shapes + --- + duration_ms: 0.421521 + type: 'test' + ... +# Subtest: an OpenAI-captured tool call carries the same match key as its session-file record +ok 3843 - an OpenAI-captured tool call carries the same match key as its session-file record + --- + duration_ms: 0.301278 + type: 'test' + ... +# Subtest: match() is true iff the x-hypaware-client header says openclaw +ok 3844 - match() is true iff the x-hypaware-client header says openclaw + --- + duration_ms: 0.909783 + type: 'test' + ... +# Subtest: openclaw projector priority is above the claude projector +ok 3845 - openclaw projector priority is above the claude projector + --- + duration_ms: 0.273906 + type: 'test' + ... +# Subtest: project() maps a JSON exchange: model, usage, stop_reason, client identity +ok 3846 - project() maps a JSON exchange: model, usage, stop_reason, client identity + --- + duration_ms: 1.279875 + type: 'test' + ... +# Subtest: project() derives a stable session_id from the system-prompt head +ok 3847 - project() derives a stable session_id from the system-prompt head + --- + duration_ms: 0.627053 + type: 'test' + ... +# Subtest: project() assembles a streamed assistant message from SSE events +ok 3848 - project() assembles a streamed assistant message from SSE events + --- + duration_ms: 0.552059 + type: 'test' + ... +# Subtest: project() preserves an empty-input streamed tool_use call +ok 3849 - project() preserves an empty-input streamed tool_use call + --- + duration_ms: 0.3022 + type: 'test' + ... +# Subtest: project() parses a non-empty-input streamed tool_use call +ok 3850 - project() parses a non-empty-input streamed tool_use call + --- + duration_ms: 0.277662 + type: 'test' + ... +# Subtest: openclawSessionId falls back to the exchange id for a content-less first message +ok 3851 - openclawSessionId falls back to the exchange id for a content-less first message + --- + duration_ms: 0.091359 + type: 'test' + ... +# Subtest: openclawSessionId keys on the first input item for an instruction-less Responses request +ok 3852 - openclawSessionId keys on the first input item for an instruction-less Responses request + --- + duration_ms: 0.236329 + type: 'test' + ... +# Subtest: project() does not drop an exchange whose first message has no content +ok 3853 - project() does not drop an exchange whose first message has no content + --- + duration_ms: 0.324223 + type: 'test' + ... +# Subtest: project() declines an unparseable request body +ok 3854 - project() declines an unparseable request body + --- + duration_ms: 0.149227 + type: 'test' + ... +# Subtest: the anthropic upstream preset is equivalent to the claude plugin preset +ok 3855 - the anthropic upstream preset is equivalent to the claude plugin preset + --- + duration_ms: 0.319325 + type: 'test' + ... +# Subtest: a configured anthropic keeps /v1/messages even with the openclaw openai preset registered +ok 3856 - a configured anthropic keeps /v1/messages even with the openclaw openai preset registered + --- + duration_ms: 0.395932 + type: 'test' + ... +# Subtest: a steered /chat/completions reaches the openai upstream in every config shape +ok 3857 - a steered /chat/completions reaches the openai upstream in every config shape + --- + duration_ms: 0.426548 + type: 'test' + ... +# Subtest: a configured openai upstream replaces the preset, steering rung included +ok 3858 - a configured openai upstream replaces the preset, steering rung included + --- + duration_ms: 0.14497 + type: 'test' + ... +# Subtest: unsteered Claude and Codex traffic routes unchanged through the compiled table +ok 3859 - unsteered Claude and Codex traffic routes unchanged through the compiled table + --- + duration_ms: 0.169918 + type: 'test' + ... +# Subtest: x-hypaware-upstream names a preset provider: match() is unconditional +ok 3860 - x-hypaware-upstream names a preset provider: match() is unconditional + --- + duration_ms: 0.126883 + type: 'test' + ... +# Subtest: regression: unsteered Claude/Codex traffic routes unchanged with both openclaw presets registered +ok 3861 - regression: unsteered Claude/Codex traffic routes unchanged with both openclaw presets registered + --- + duration_ms: 0.140434 + type: 'test' + ... +# Subtest: the manifest declares both required upstreams +ok 3862 - the manifest declares both required upstreams + --- + duration_ms: 0.318814 + type: 'test' + ... +# Subtest: a non-session record type is not read as the header, even carrying id/cwd +ok 3863 - a non-session record type is not read as the header, even carrying id/cwd + --- + duration_ms: 1.061103 + type: 'test' + ... +# Subtest: a missing, blank, or non-string type is not "session" either +ok 3864 - a missing, blank, or non-string type is not "session" either + --- + duration_ms: 0.215497 + type: 'test' + ... +# Subtest: a line that is not JSON, or JSON that is not an object, resolves nothing +ok 3865 - a line that is not JSON, or JSON that is not an object, resolves nothing + --- + duration_ms: 0.142637 + type: 'test' + ... +# Subtest: a blank or non-string field is absent, never a substitute value +ok 3866 - a blank or non-string field is absent, never a substitute value + --- + duration_ms: 0.334508 + type: 'test' + ... +# Subtest: a session header with no other fields resolves no field +ok 3867 - a session header with no other fields resolves no field + --- + duration_ms: 0.613643 + type: 'test' + ... +# Subtest: a field that survives the blank test is returned byte-identical +ok 3868 - a field that survives the blank test is returned byte-identical + --- + duration_ms: 0.1313 + type: 'test' + ... +# Subtest: a relative cwd is no cwd, not a path resolved against the daemon +ok 3869 - a relative cwd is no cwd, not a path resolved against the daemon + --- + duration_ms: 0.218271 + type: 'test' + ... +# Subtest: an absolute cwd resolves +ok 3870 - an absolute cwd resolves + --- + duration_ms: 0.106652 + type: 'test' + ... +# Subtest: openclawSessionCwd is the one cwd predicate, usable by a caller that reads its own record +ok 3871 - openclawSessionCwd is the one cwd predicate, usable by a caller that reads its own record + --- + duration_ms: 0.283862 + type: 'test' + ... +# Subtest: readOpenclawSessionHeader reads the first line and ignores the rest of the file +ok 3872 - readOpenclawSessionHeader reads the first line and ignores the rest of the file + --- + duration_ms: 0.76322 + type: 'test' + ... +# Subtest: a first line longer than the read bound resolves nothing rather than half a line +ok 3873 - a first line longer than the read bound resolves nothing rather than half a line + --- + duration_ms: 0.728337 + type: 'test' + ... +# Subtest: an unreadable, empty, or absent session file resolves nothing +ok 3874 - an unreadable, empty, or absent session file resolves nothing + --- + duration_ms: 0.479879 + type: 'test' + ... +# Subtest: a session file with no trailing newline is still one whole first line +ok 3875 - a session file with no trailing newline is still one whole first line + --- + duration_ms: 0.291784 + type: 'test' + ... +# Subtest: readOpenclawSessionMessages returns only type:"message" records, in file order +ok 3876 - readOpenclawSessionMessages returns only type:"message" records, in file order + --- + duration_ms: 9.911985 + type: 'test' + ... +# Subtest: a message field the nested envelope states is never read off the record line +ok 3877 - a message field the nested envelope states is never read off the record line + --- + duration_ms: 1.148465 + type: 'test' + ... +# Subtest: a record with no nested envelope reads its fields off the record line +ok 3878 - a record with no nested envelope reads its fields off the record line + --- + duration_ms: 1.795609 + type: 'test' + ... +# Subtest: the record line supplies the timestamp when the nested envelope states none +ok 3879 - the record line supplies the timestamp when the nested envelope states none + --- + duration_ms: 3.761386 + type: 'test' + ... +# Subtest: the envelope wins for timestamp, the record line wins for id +ok 3880 - the envelope wins for timestamp, the record line wins for id + --- + duration_ms: 1.027331 + type: 'test' + ... +# Subtest: a record that states its id only in the envelope still resolves an identity +ok 3881 - a record that states its id only in the envelope still resolves an identity + --- + duration_ms: 3.531427 + type: 'test' + ... +# Subtest: a blank or wrong-typed nested field reads as absent, so the record line still supplies it +ok 3882 - a blank or wrong-typed nested field reads as absent, so the record line still supplies it + --- + duration_ms: 1.000481 + type: 'test' + ... +# Subtest: a blank nested field with nothing on the record line is absent, not a substitute +ok 3883 - a blank nested field with nothing on the record line is absent, not a substitute + --- + duration_ms: 0.734517 + type: 'test' + ... +# Subtest: a nulled-out nested content is unstated, so the record line still supplies it +ok 3884 - a nulled-out nested content is unstated, so the record line still supplies it + --- + duration_ms: 0.655887 + type: 'test' + ... +# Subtest: a `message` key that is not an object leaves the record line as the only address +ok 3885 - a `message` key that is not an object leaves the record line as the only address + --- + duration_ms: 0.638431 + type: 'test' + ... +# Subtest: readOpenclawSessionMessages skips blank and unparseable lines without aborting the rest +ok 3886 - readOpenclawSessionMessages skips blank and unparseable lines without aborting the rest + --- + duration_ms: 1.448251 + type: 'test' + ... +# Subtest: readOpenclawSessionMessages treats a blank id/timestamp/model as absent, not a substitute +ok 3887 - readOpenclawSessionMessages treats a blank id/timestamp/model as absent, not a substitute + --- + duration_ms: 1.971066 + type: 'test' + ... +# Subtest: readOpenclawSessionMessages resolves to an empty list for a missing or empty file +ok 3888 - readOpenclawSessionMessages resolves to an empty list for a missing or empty file + --- + duration_ms: 1.216308 + type: 'test' + ... +# Subtest: listOpenclawSessionFiles scans a rotated session file the same way it scans a live one +ok 3889 - listOpenclawSessionFiles scans a rotated session file the same way it scans a live one + --- + duration_ms: 2.474582 + type: 'test' + ... +# Subtest: the enricher registers under the LLP 0161 name and client +ok 3890 - the enricher registers under the LLP 0161 name and client + --- + duration_ms: 1.732152 + type: 'test' + ... +# Subtest: every row of a realistic, fully-written session settles by content match (rate 1.00) +ok 3891 - every row of a realistic, fully-written session settles by content match (rate 1.00) + --- + duration_ms: 10.903944 + type: 'test' + ... +# Subtest: a match_key stored as a JSON attributes string settles the same way +ok 3892 - a match_key stored as a JSON attributes string settles the same way + --- + duration_ms: 1.647003 + type: 'test' + ... +# Subtest: a timestamp-prefixed wire user turn settles onto its bare session record +ok 3893 - a timestamp-prefixed wire user turn settles onto its bare session record + --- + duration_ms: 2.293466 + type: 'test' + ... +# Subtest: an ignore-classed session cwd drops every row of that session +ok 3894 - an ignore-classed session cwd drops every row of that session + --- + duration_ms: 2.879426 + type: 'test' + ... +# Subtest: a row that matches nothing still drops when its session cwd is ignored +ok 3895 - a row that matches nothing still drops when its session cwd is ignored + --- + duration_ms: 2.139342 + type: 'test' + ... +# Subtest: a content miss settles through the ordinal/time fallback when position, role and time align +ok 3896 - a content miss settles through the ordinal/time fallback when position, role and time align + --- + duration_ms: 1.990967 + type: 'test' + ... +# Subtest: the ordinal fallback declines outside its five-minute window +ok 3897 - the ordinal fallback declines outside its five-minute window + --- + duration_ms: 1.631961 + type: 'test' + ... +# Subtest: the ordinal fallback declines when the file records a different role at that position +ok 3898 - the ordinal fallback declines when the file records a different role at that position + --- + duration_ms: 1.909241 + type: 'test' + ... +# Subtest: an ambiguous content key declines to upgrade rather than duplicating a native id +ok 3899 - an ambiguous content key declines to upgrade rather than duplicating a native id + --- + duration_ms: 2.244501 + type: 'test' + ... +# Subtest: rows no session file claims are neither upgraded nor dropped +ok 3900 - rows no session file claims are neither upgraded nor dropped + --- + duration_ms: 1.824383 + type: 'test' + ... +# Subtest: two concurrent sessions each bind to their own file +ok 3901 - two concurrent sessions each bind to their own file + --- + duration_ms: 5.302299 + type: 'test' + ... +# Subtest: a missing agents root settles nothing and throws nothing +ok 3902 - a missing agents root settles nothing and throws nothing + --- + duration_ms: 0.651981 + type: 'test' + ... +# Subtest: an unparseable session file settles nothing and throws nothing +ok 3903 - an unparseable session file settles nothing and throws nothing + --- + duration_ms: 1.208126 + type: 'test' + ... +# Subtest: a session header with no usable cwd upgrades identity without gating +ok 3904 - a session header with no usable cwd upgrades identity without gating + --- + duration_ms: 1.930294 + type: 'test' + ... +# Subtest: an already-populated row cwd is never overwritten by the header +ok 3905 - an already-populated row cwd is never overwritten by the header + --- + duration_ms: 1.384605 + type: 'test' + ... +# Subtest: an empty batch is returned untouched +ok 3906 - an empty batch is returned untouched + --- + duration_ms: 0.38833 + type: 'test' + ... +# Subtest: claude picker summary discloses the attach and the skill install +ok 3907 - claude picker summary discloses the attach and the skill install + --- + duration_ms: 6.866336 + type: 'test' + ... +# Subtest: claude picker summary discloses that a local gateway listener is started +ok 3908 - claude picker summary discloses that a local gateway listener is started + --- + duration_ms: 1.073612 + type: 'test' + ... +# Subtest: codex picker summary discloses the gateway config write and the skill install +ok 3909 - codex picker summary discloses the gateway config write and the skill install + --- + duration_ms: 0.887189 + type: 'test' + ... +# Subtest: otel picker summary discloses that a local receiver is started +ok 3910 - otel picker summary discloses that a local receiver is started + --- + duration_ms: 0.63168 + type: 'test' + ... +# Subtest: openclaw picker summary discloses the gateway-config rewrite +ok 3911 - openclaw picker summary discloses the gateway-config rewrite + --- + duration_ms: 1.286395 + type: 'test' + ... +# Subtest: openclaw picker summary names the manual gateway restart attach requires +ok 3912 - openclaw picker summary names the manual gateway restart attach requires + --- + duration_ms: 0.889752 + type: 'test' + ... +# Subtest: raw-anthropic picker summary discloses that a local gateway listener is started +ok 3913 - raw-anthropic picker summary discloses that a local gateway listener is started + --- + duration_ms: 0.820017 + type: 'test' + ... +# Subtest: raw-openai picker summary discloses that a local gateway listener is started +ok 3914 - raw-openai picker summary discloses that a local gateway listener is started + --- + duration_ms: 1.478808 + type: 'test' + ... +# Subtest: claude-desktop picker summary keeps the asks-before-changing reassurance +ok 3915 - claude-desktop picker summary keeps the asks-before-changing reassurance + --- + duration_ms: 1.352746 + type: 'test' + ... +# Subtest: claude-desktop picker summary discloses that a local gateway listener is started +ok 3916 - claude-desktop picker summary discloses that a local gateway listener is started + --- + duration_ms: 1.09252 + type: 'test' + ... +# Subtest: clean source plugin reports no issues +ok 3917 - clean source plugin reports no issues + --- + duration_ms: 12.184028 + type: 'test' + ... +# Subtest: missing activate export is flagged +ok 3918 - missing activate export is flagged + --- + duration_ms: 3.136498 + type: 'test' + ... +# Subtest: declared-but-unregistered contribution is the headline error +ok 3919 - declared-but-unregistered contribution is the headline error + --- + duration_ms: 3.056776 + type: 'test' + ... +# Subtest: activate that throws is reported as activate_threw +ok 3920 - activate that throws is reported as activate_threw + --- + duration_ms: 2.915671 + type: 'test' + ... +# Subtest: unresolved required capability is an error +ok 3921 - unresolved required capability is an error + --- + duration_ms: 3.228467 + type: 'test' + ... +# Subtest: required capability resolves when a provider is known +ok 3922 - required capability resolves when a provider is known + --- + duration_ms: 3.142586 + type: 'test' + ... +# Subtest: required capability with a known name but unsatisfied range is unresolved +ok 3923 - required capability with a known name but unsatisfied range is unresolved + --- + duration_ms: 4.134625 + type: 'test' + ... +# Subtest: requireCapability and using its handle during activate does not false-fail +ok 3924 - requireCapability and using its handle during activate does not false-fail + --- + duration_ms: 9.994652 + type: 'test' + ... +# Subtest: malformed contributes entry is flagged as contributes_malformed +ok 3925 - malformed contributes entry is flagged as contributes_malformed + --- + duration_ms: 8.286715 + type: 'test' + ... +# Subtest: malformed config_sections entry is flagged as contributes_malformed +ok 3926 - malformed config_sections entry is flagged as contributes_malformed + --- + duration_ms: 3.814318 + type: 'test' + ... +# Subtest: declared-but-never-provided capability is a warning +ok 3927 - declared-but-never-provided capability is a warning + --- + duration_ms: 3.153663 + type: 'test' + ... +# Subtest: a syntax error in the entrypoint surfaces as entrypoint_import_failed +ok 3928 - a syntax error in the entrypoint surfaces as entrypoint_import_failed + --- + duration_ms: 3.014201 + type: 'test' + ... +# Subtest: invalid semver and missing entrypoint are caught statically +ok 3929 - invalid semver and missing entrypoint are caught statically + --- + duration_ms: 3.784101 + type: 'test' + ... +# Subtest: an invalid manifest short-circuits with manifest_invalid +ok 3930 - an invalid manifest short-circuits with manifest_invalid + --- + duration_ms: 1.149086 + type: 'test' + ... +# Subtest: registered-but-undeclared contribution is a warning, not an error +ok 3931 - registered-but-undeclared contribution is a warning, not an error + --- + duration_ms: 3.043817 + type: 'test' + ... +# Subtest: slugFromName strips scope and sanitizes +ok 3932 - slugFromName strips scope and sanitizes + --- + duration_ms: 0.961201 + type: 'test' + ... +# Subtest: scaffold (source) writes files and passes doctor with zero errors +ok 3933 - scaffold (source) writes files and passes doctor with zero errors + --- + duration_ms: 12.666261 + type: 'test' + ... +# Subtest: scaffold (sink) writes files and passes doctor with zero errors +ok 3934 - scaffold (sink) writes files and passes doctor with zero errors + --- + duration_ms: 5.279314 + type: 'test' + ... +# Subtest: scaffold (dataset) writes files and passes doctor with zero errors +ok 3935 - scaffold (dataset) writes files and passes doctor with zero errors + --- + duration_ms: 5.521172 + type: 'test' + ... +# Subtest: scaffold refuses to clobber an existing directory +ok 3936 - scaffold refuses to clobber an existing directory + --- + duration_ms: 2.071659 + type: 'test' + ... +# Subtest: scaffold rejects an unknown kind +ok 3937 - scaffold rejects an unknown kind + --- + duration_ms: 0.607764 + type: 'test' + ... +# Subtest: every client copy of a content-reading skill states that recorded content is data, not instructions +ok 3938 - every client copy of a content-reading skill states that recorded content is data, not instructions + --- + duration_ms: 5.894889 + type: 'test' + ... +# Subtest: a boundary section separates captured content from the changes its skill may propose +ok 3939 - a boundary section separates captured content from the changes its skill may propose + --- + duration_ms: 3.53915 + type: 'test' + ... +# Subtest: hypaware-query restates the boundary in its Guardrails list +ok 3940 - hypaware-query restates the boundary in its Guardrails list + --- + duration_ms: 0.883343 + type: 'test' + ... +# Subtest: a content boundary does not drift between the Claude and Codex copies +ok 3941 - a content boundary does not drift between the Claude and Codex copies + --- + duration_ms: 0.970645 + type: 'test' + ... +# Subtest: hypaware-query frontmatter is identical across both client copies +ok 3942 - hypaware-query frontmatter is identical across both client copies + --- + duration_ms: 2.365255 + type: 'test' + ... +# Subtest: hypaware-query description names the natural session-search intents +ok 3943 - hypaware-query description names the natural session-search intents + --- + duration_ms: 0.53303 + type: 'test' + ... +# Subtest: hypaware-query description keeps its original product vocabulary +ok 3944 - hypaware-query description keeps its original product vocabulary + --- + duration_ms: 0.372456 + type: 'test' + ... +# Subtest: hypaware-query description stays a single-line scalar within budget +ok 3945 - hypaware-query description stays a single-line scalar within budget + --- + duration_ms: 0.32254 + type: 'test' + ... +# Subtest: s3 BlobStore puts and gets a round-trip object honouring prefix +ok 3946 - s3 BlobStore puts and gets a round-trip object honouring prefix + --- + duration_ms: 3.366967 + type: 'test' + ... +# Subtest: s3 BlobStore getObject returns null when AWS reports NotFound +ok 3947 - s3 BlobStore getObject returns null when AWS reports NotFound + --- + duration_ms: 0.19722 + type: 'test' + ... +# Subtest: s3 BlobStore listObjects strips the prefix from emitted keys +ok 3948 - s3 BlobStore listObjects strips the prefix from emitted keys + --- + duration_ms: 6.865425 + type: 'test' + ... +# Subtest: s3 BlobStore listObjects does not leak into sibling-namespace keys (hyp/exports vs hyp/exports2) +ok 3949 - s3 BlobStore listObjects does not leak into sibling-namespace keys (hyp/exports vs hyp/exports2) + --- + duration_ms: 0.41413 + type: 'test' + ... +# Subtest: s3 BlobStore listObjects empty-prefix without a configured prefix lists the whole bucket +ok 3950 - s3 BlobStore listObjects empty-prefix without a configured prefix lists the whole bucket + --- + duration_ms: 0.338745 + type: 'test' + ... +# Subtest: s3 BlobStore putObject ifNoneMatch="*" surfaces blob_precondition_failed on conflict +ok 3951 - s3 BlobStore putObject ifNoneMatch="*" surfaces blob_precondition_failed on conflict + --- + duration_ms: 0.448842 + type: 'test' + ... +# Subtest: s3 BlobStore rejects keys that try to escape the configured prefix +ok 3952 - s3 BlobStore rejects keys that try to escape the configured prefix + --- + duration_ms: 0.244802 + type: 'test' + ... +# Subtest: s3 BlobStore exposes bucket and prefix for downstream telemetry +ok 3953 - s3 BlobStore exposes bucket and prefix for downstream telemetry + --- + duration_ms: 0.090337 + type: 'test' + ... +# Subtest: s3 BlobStore tags AccessDenied with errorKind=s3_access_denied +ok 3954 - s3 BlobStore tags AccessDenied with errorKind=s3_access_denied + --- + duration_ms: 0.443144 + type: 'test' + ... +# Subtest: s3 BlobStore tags NoSuchBucket on listObjects with errorKind=s3_bucket_missing +ok 3955 - s3 BlobStore tags NoSuchBucket on listObjects with errorKind=s3_bucket_missing + --- + duration_ms: 0.392656 + type: 'test' + ... +# Subtest: s3 BlobStore tags getObject errors that are not NotFound +ok 3956 - s3 BlobStore tags getObject errors that are not NotFound + --- + duration_ms: 0.227065 + type: 'test' + ... +# Subtest: s3 BlobStore deleteObject treats NotFound as benign and returns +ok 3957 - s3 BlobStore deleteObject treats NotFound as benign and returns + --- + duration_ms: 0.185392 + type: 'test' + ... +# Subtest: createUnconfiguredS3BlobStore throws actionable s3_blob_store_unconfigured on use +ok 3958 - createUnconfiguredS3BlobStore throws actionable s3_blob_store_unconfigured on use + --- + duration_ms: 0.314138 + type: 'test' + ... +# Subtest: detectCredentialSourceKind prefers explicit profile +ok 3959 - detectCredentialSourceKind prefers explicit profile + --- + duration_ms: 0.862681 + type: 'test' + ... +# Subtest: detectCredentialSourceKind falls through to env vars when no profile is set +ok 3960 - detectCredentialSourceKind falls through to env vars when no profile is set + --- + duration_ms: 0.113773 + type: 'test' + ... +# Subtest: detectCredentialSourceKind detects web identity (EKS IRSA) +ok 3961 - detectCredentialSourceKind detects web identity (EKS IRSA) + --- + duration_ms: 0.104209 + type: 'test' + ... +# Subtest: detectCredentialSourceKind detects SSO +ok 3962 - detectCredentialSourceKind detects SSO + --- + duration_ms: 0.129397 + type: 'test' + ... +# Subtest: detectCredentialSourceKind detects credential_process +ok 3963 - detectCredentialSourceKind detects credential_process + --- + duration_ms: 0.116457 + type: 'test' + ... +# Subtest: detectCredentialSourceKind falls back to metadata when nothing else matches +ok 3964 - detectCredentialSourceKind falls back to metadata when nothing else matches + --- + duration_ms: 0.107904 + type: 'test' + ... +# Subtest: detectCredentialSourceKind never returns the credential material itself +ok 3965 - detectCredentialSourceKind never returns the credential material itself + --- + duration_ms: 0.213645 + type: 'test' + ... +# Subtest: detectCredentialSourceKind ignores empty-string AWS_ACCESS_KEY_ID +ok 3966 - detectCredentialSourceKind ignores empty-string AWS_ACCESS_KEY_ID + --- + duration_ms: 0.119762 + type: 'test' + ... +# Subtest: validateS3SinkConfig accepts a minimum bucket-only config +ok 3967 - validateS3SinkConfig accepts a minimum bucket-only config + --- + duration_ms: 1.086672 + type: 'test' + ... +# Subtest: validateS3SinkConfig accepts a full real-AWS config shape +ok 3968 - validateS3SinkConfig accepts a full real-AWS config shape + --- + duration_ms: 0.980941 + type: 'test' + ... +# Subtest: validateS3SinkConfig accepts an S3-compatible config (MinIO) +ok 3969 - validateS3SinkConfig accepts an S3-compatible config (MinIO) + --- + duration_ms: 0.154825 + type: 'test' + ... +# Subtest: validateS3SinkConfig rejects missing bucket with s3_config_invalid +ok 3970 - validateS3SinkConfig rejects missing bucket with s3_config_invalid + --- + duration_ms: 0.141655 + type: 'test' + ... +# Subtest: validateS3SinkConfig rejects non-string bucket +ok 3971 - validateS3SinkConfig rejects non-string bucket + --- + duration_ms: 0.196398 + type: 'test' + ... +# Subtest: validateS3SinkConfig rejects non-object input +ok 3972 - validateS3SinkConfig rejects non-object input + --- + duration_ms: 0.115586 + type: 'test' + ... +# Subtest: validateS3SinkConfig rejects unknown storage class +ok 3973 - validateS3SinkConfig rejects unknown storage class + --- + duration_ms: 0.274237 + type: 'test' + ... +# Subtest: validateS3SinkConfig rejects malformed endpoint_url +ok 3974 - validateS3SinkConfig rejects malformed endpoint_url + --- + duration_ms: 0.163479 + type: 'test' + ... +# Subtest: validateS3SinkConfig rejects non-http(s) endpoint_url +ok 3975 - validateS3SinkConfig rejects non-http(s) endpoint_url + --- + duration_ms: 0.443924 + type: 'test' + ... +# Subtest: validateS3SinkConfig rejects non-boolean force_path_style +ok 3976 - validateS3SinkConfig rejects non-boolean force_path_style + --- + duration_ms: 0.379146 + type: 'test' + ... +# Subtest: normalizePrefix strips leading and trailing slashes +ok 3977 - normalizePrefix strips leading and trailing slashes + --- + duration_ms: 0.165221 + type: 'test' + ... +# Subtest: classifyAwsError maps CredentialsProviderError to s3_credentials_missing +ok 3978 - classifyAwsError maps CredentialsProviderError to s3_credentials_missing + --- + duration_ms: 1.988782 + type: 'test' + ... +# Subtest: classifyAwsError maps AccessDenied to s3_access_denied +ok 3979 - classifyAwsError maps AccessDenied to s3_access_denied + --- + duration_ms: 0.168185 + type: 'test' + ... +# Subtest: classifyAwsError maps HTTP 403 even when name is generic +ok 3980 - classifyAwsError maps HTTP 403 even when name is generic + --- + duration_ms: 0.095284 + type: 'test' + ... +# Subtest: classifyAwsError maps NoSuchBucket to s3_bucket_missing +ok 3981 - classifyAwsError maps NoSuchBucket to s3_bucket_missing + --- + duration_ms: 0.072309 + type: 'test' + ... +# Subtest: classifyAwsError maps PermanentRedirect to s3_region_mismatch +ok 3982 - classifyAwsError maps PermanentRedirect to s3_region_mismatch + --- + duration_ms: 0.076887 + type: 'test' + ... +# Subtest: classifyAwsError maps AuthorizationHeaderMalformed to s3_region_mismatch +ok 3983 - classifyAwsError maps AuthorizationHeaderMalformed to s3_region_mismatch + --- + duration_ms: 0.073902 + type: 'test' + ... +# Subtest: classifyAwsError maps SlowDown to s3_throttled +ok 3984 - classifyAwsError maps SlowDown to s3_throttled + --- + duration_ms: 0.092491 + type: 'test' + ... +# Subtest: classifyAwsError maps HTTP 503 to s3_throttled +ok 3985 - classifyAwsError maps HTTP 503 to s3_throttled + --- + duration_ms: 0.120352 + type: 'test' + ... +# Subtest: classifyAwsError maps HTTP 429 to s3_throttled +ok 3986 - classifyAwsError maps HTTP 429 to s3_throttled + --- + duration_ms: 0.280416 + type: 'test' + ... +# Subtest: classifyAwsError uses .Code when .name is missing +ok 3987 - classifyAwsError uses .Code when .name is missing + --- + duration_ms: 0.350743 + type: 'test' + ... +# Subtest: classifyAwsError falls back to s3_put_failed for unknown errors +ok 3988 - classifyAwsError falls back to s3_put_failed for unknown errors + --- + duration_ms: 0.177049 + type: 'test' + ... +# Subtest: classifyAwsError survives non-object inputs +ok 3989 - classifyAwsError survives non-object inputs + --- + duration_ms: 0.097769 + type: 'test' + ... +# Subtest: describeS3ErrorKind returns a non-empty diagnostic for every error kind +ok 3990 - describeS3ErrorKind returns a non-empty diagnostic for every error kind + --- + duration_ms: 0.242318 + type: 'test' + ... +# Subtest: exportBatch terminal failure: retryPartitions excludes already-uploaded partitions +ok 3991 - exportBatch terminal failure: retryPartitions excludes already-uploaded partitions + --- + duration_ms: 10.914269 + type: 'test' + ... +# Subtest: exportBatch partial failure: retryPartitions has only the failed partition +ok 3992 - exportBatch partial failure: retryPartitions has only the failed partition + --- + duration_ms: 4.40863 + type: 'test' + ... +# Subtest: exportBatch forwards dataset cluster columns to the encoder +ok 3993 - exportBatch forwards dataset cluster columns to the encoder + --- + duration_ms: 2.992528 + type: 'test' + ... +# Subtest: exportBatch all-success: no retryPartitions field +ok 3994 - exportBatch all-success: no retryPartitions field + --- + duration_ms: 2.637328 + type: 'test' + ... +# Subtest: exportBatch skips a partition with no new rows: no PUT, no blob +ok 3995 - exportBatch skips a partition with no new rows: no PUT, no blob + --- + duration_ms: 1.505648 + type: 'test' + ... +# Subtest: exportBatch embeds the [sinceSeq,lastSeq] range in the object key and advances the watermark +ok 3996 - exportBatch embeds the [sinceSeq,lastSeq] range in the object key and advances the watermark + --- + duration_ms: 4.91456 + type: 'test' + ... +# Subtest: exportBatch re-PUTs the same object key when the watermark is lost (idempotent crash retry) +ok 3997 - exportBatch re-PUTs the same object key when the watermark is lost (idempotent crash retry) + --- + duration_ms: 3.955983 + type: 'test' + ... +# Subtest: drop-only tick: no object is PUT, but the watermark advances past the withheld rows (LLP 0070) +ok 3998 - drop-only tick: no object is PUT, but the watermark advances past the withheld rows (LLP 0070) + --- + duration_ms: 3.40781 + type: 'test' + ... +# Subtest: partitionSegment renders empty partition as "all" +ok 3999 - partitionSegment renders empty partition as "all" + --- + duration_ms: 1.899487 + type: 'test' + ... +# Subtest: partitionSegment joins ordered key=value pairs +ok 4000 - partitionSegment joins ordered key=value pairs + --- + duration_ms: 0.217019 + type: 'test' + ... +# Subtest: partitionSegment strips path-separator characters so the segment cannot escape its dataset directory +ok 4001 - partitionSegment strips path-separator characters so the segment cannot escape its dataset directory + --- + duration_ms: 0.21021 + type: 'test' + ... +# Subtest: renderObjectKey composes prefix/dataset/segment/filename +ok 4002 - renderObjectKey composes prefix/dataset/segment/filename + --- + duration_ms: 0.182678 + type: 'test' + ... +# Subtest: renderObjectKey omits the prefix segment when prefix is empty +ok 4003 - renderObjectKey omits the prefix segment when prefix is empty + --- + duration_ms: 0.130218 + type: 'test' + ... +# Subtest: renderObjectKey normalizes leading and trailing slashes in prefix +ok 4004 - renderObjectKey normalizes leading and trailing slashes in prefix + --- + duration_ms: 0.124619 + type: 'test' + ... +# Subtest: renderObjectKey strips path separators from dataset and filename so the key stays inside the configured prefix +ok 4005 - renderObjectKey strips path separators from dataset and filename so the key stays inside the configured prefix + --- + duration_ms: 0.175016 + type: 'test' + ... +# Subtest: renderObjectKey requires a non-empty dataset +ok 4006 - renderObjectKey requires a non-empty dataset + --- + duration_ms: 0.332816 + type: 'test' + ... +# Subtest: renderObjectKey requires a non-empty filename +ok 4007 - renderObjectKey requires a non-empty filename + --- + duration_ms: 0.30904 + type: 'test' + ... +# Subtest: keyIsWithinPrefix accepts keys under the prefix+dataset namespace +ok 4008 - keyIsWithinPrefix accepts keys under the prefix+dataset namespace + --- + duration_ms: 0.353307 + type: 'test' + ... +# Subtest: keyIsWithinPrefix rejects keys outside the prefix+dataset namespace +ok 4009 - keyIsWithinPrefix rejects keys outside the prefix+dataset namespace + --- + duration_ms: 0.165942 + type: 'test' + ... +# Subtest: keyIsWithinPrefix tolerates an empty prefix +ok 4010 - keyIsWithinPrefix tolerates an empty prefix + --- + duration_ms: 0.101224 + type: 'test' + ... +# Subtest: activate fails at boot when query_sources is malformed +ok 4011 - activate fails at boot when query_sources is malformed + --- + duration_ms: 1.292354 + type: 'test' + ... +# Subtest: activate fails at boot when a query source has no resolvable bucket +ok 4012 - activate fails at boot when a query source has no resolvable bucket + --- + duration_ms: 0.415822 + type: 'test' + ... +# Subtest: activate registers nothing when query_sources is absent +ok 4013 - activate registers nothing when query_sources is absent + --- + duration_ms: 0.169228 + type: 'test' + ... +# Subtest: same-bucket query source inherits the plugin prefix as its root +ok 4014 - same-bucket query source inherits the plugin prefix as its root + --- + duration_ms: 0.478468 + type: 'test' + ... +# Subtest: bucket-override query source drops the plugin prefix and roots at the source prefix +ok 4015 - bucket-override query source drops the plugin prefix and roots at the source prefix + --- + duration_ms: 0.20404 + type: 'test' + ... +# Subtest: activate registers one dataset per valid query source +ok 4016 - activate registers one dataset per valid query source + --- + duration_ms: 0.485919 + type: 'test' + ... +# Subtest: validateS3QuerySources accepts parquet and iceberg sources +ok 4017 - validateS3QuerySources accepts parquet and iceberg sources + --- + duration_ms: 2.38845 + type: 'test' + ... +# Subtest: validateS3QuerySources rejects a non-array +ok 4018 - validateS3QuerySources rejects a non-array + --- + duration_ms: 0.211352 + type: 'test' + ... +# Subtest: validateS3QuerySources reports stable pointers for malformed entries +ok 4019 - validateS3QuerySources reports stable pointers for malformed entries + --- + duration_ms: 0.754647 + type: 'test' + ... +# Subtest: validateS3QuerySources rejects duplicate names +ok 4020 - validateS3QuerySources rejects duplicate names + --- + duration_ms: 0.191651 + type: 'test' + ... +# Subtest: validateS3QuerySources validates declared column types +ok 4021 - validateS3QuerySources validates declared column types + --- + duration_ms: 0.234396 + type: 'test' + ... +# Subtest: validateS3QuerySources treats absent query_sources as caller concern (empty array ok) +ok 4022 - validateS3QuerySources treats absent query_sources as caller concern (empty array ok) + --- + duration_ms: 0.132071 + type: 'test' + ... +# Subtest: validateS3QuerySources rejects a prefix that normalizes to empty +ok 4023 - validateS3QuerySources rejects a prefix that normalizes to empty + --- + duration_ms: 0.209919 + type: 'test' + ... +# Subtest: validateS3QuerySources rejects an invalid endpoint_url at boot +ok 4024 - validateS3QuerySources rejects an invalid endpoint_url at boot + --- + duration_ms: 0.207415 + type: 'test' + ... +# Subtest: validateS3QuerySources accepts a valid endpoint_url +ok 4025 - validateS3QuerySources accepts a valid endpoint_url + --- + duration_ms: 1.621765 + type: 'test' + ... +# Subtest: parquet query source reads a single object back through SQL +ok 4026 - parquet query source reads a single object back through SQL + --- + duration_ms: 11.214685 + type: 'test' + ... +# Subtest: parquet query source unions multiple objects and ignores non-parquet keys +ok 4027 - parquet query source unions multiple objects and ignores non-parquet keys + --- + duration_ms: 12.827447 + type: 'test' + ... +# Subtest: parquet query source with no objects yields an empty result +ok 4028 - parquet query source with no objects yields an empty result + --- + duration_ms: 0.272414 + type: 'test' + ... +# Subtest: iceberg query source with no metadata reads as empty (no throw) +ok 4029 - iceberg query source with no metadata reads as empty (no throw) + --- + duration_ms: 0.286906 + type: 'test' + ... +# Subtest: iceberg query source is NULL-correct through a real BlobStore round trip +ok 4030 - iceberg query source is NULL-correct through a real BlobStore round trip + --- + duration_ms: 46.818069 + type: 'test' + ... +# Subtest: parquet discovery bounds the prefix to a directory, excluding sibling namespaces +ok 4031 - parquet discovery bounds the prefix to a directory, excluding sibling namespaces + --- + duration_ms: 1.873928 + type: 'test' + ... +# Subtest: parquet read rejects when a listed object disappears before read (list→read race) +ok 4032 - parquet read rejects when a listed object disappears before read (list→read race) + --- + duration_ms: 0.416112 + type: 'test' + ... +# Subtest: the constraint inventory is non-empty and has no duplicate ids +ok 4033 - the constraint inventory is non-empty and has no duplicate ids + --- + duration_ms: 1.518988 + type: 'test' + ... +# Subtest: every constraint states the harm of dropping it +ok 4034 - every constraint states the harm of dropping it + --- + duration_ms: 0.254307 + type: 'test' + ... +# Subtest: claude: constraint "one-carrier-rule" survives somewhere in the skill corpus +ok 4035 - claude: constraint "one-carrier-rule" survives somewhere in the skill corpus + --- + duration_ms: 0.467581 + type: 'test' + ... +# Subtest: claude: constraint "usage-not-raw-frame" survives somewhere in the skill corpus +ok 4036 - claude: constraint "usage-not-raw-frame" survives somewhere in the skill corpus + --- + duration_ms: 0.207846 + type: 'test' + ... +# Subtest: claude: constraint "captured-content-is-data" survives somewhere in the skill corpus +ok 4037 - claude: constraint "captured-content-is-data" survives somewhere in the skill corpus + --- + duration_ms: 0.283291 + type: 'test' + ... +# Subtest: claude: constraint "changes-from-behaviour-not-payloads" survives somewhere in the skill corpus +ok 4038 - claude: constraint "changes-from-behaviour-not-payloads" survives somewhere in the skill corpus + --- + duration_ms: 0.191551 + type: 'test' + ... +# Subtest: claude: constraint "privacy-per-item-confirmation" survives somewhere in the skill corpus +ok 4039 - claude: constraint "privacy-per-item-confirmation" survives somewhere in the skill corpus + --- + duration_ms: 0.154565 + type: 'test' + ... +# Subtest: claude: constraint "privacy-session-optout-first" survives somewhere in the skill corpus +ok 4040 - claude: constraint "privacy-session-optout-first" survives somewhere in the skill corpus + --- + duration_ms: 0.13156 + type: 'test' + ... +# Subtest: claude: constraint "graph-derived-facets" survives somewhere in the skill corpus +ok 4041 - claude: constraint "graph-derived-facets" survives somewhere in the skill corpus + --- + duration_ms: 0.3224 + type: 'test' + ... +# Subtest: claude: constraint "graph-project-first" survives somewhere in the skill corpus +ok 4042 - claude: constraint "graph-project-first" survives somewhere in the skill corpus + --- + duration_ms: 0.274157 + type: 'test' + ... +# Subtest: claude: constraint "graph-keys-converge" survives somewhere in the skill corpus +ok 4043 - claude: constraint "graph-keys-converge" survives somewhere in the skill corpus + --- + duration_ms: 0.141825 + type: 'test' + ... +# Subtest: claude: constraint "graph-is-derived-not-truth" survives somewhere in the skill corpus +ok 4044 - claude: constraint "graph-is-derived-not-truth" survives somewhere in the skill corpus + --- + duration_ms: 0.100183 + type: 'test' + ... +# Subtest: codex: constraint "one-carrier-rule" survives somewhere in the skill corpus +ok 4045 - codex: constraint "one-carrier-rule" survives somewhere in the skill corpus + --- + duration_ms: 0.286024 + type: 'test' + ... +# Subtest: codex: constraint "usage-not-raw-frame" survives somewhere in the skill corpus +ok 4046 - codex: constraint "usage-not-raw-frame" survives somewhere in the skill corpus + --- + duration_ms: 0.120614 + type: 'test' + ... +# Subtest: codex: constraint "captured-content-is-data" survives somewhere in the skill corpus +ok 4047 - codex: constraint "captured-content-is-data" survives somewhere in the skill corpus + --- + duration_ms: 0.075344 + type: 'test' + ... +# Subtest: codex: constraint "changes-from-behaviour-not-payloads" survives somewhere in the skill corpus +ok 4048 - codex: constraint "changes-from-behaviour-not-payloads" survives somewhere in the skill corpus + --- + duration_ms: 0.061243 + type: 'test' + ... +# Subtest: codex: constraint "privacy-per-item-confirmation" survives somewhere in the skill corpus +ok 4049 - codex: constraint "privacy-per-item-confirmation" survives somewhere in the skill corpus + --- + duration_ms: 0.035294 + type: 'test' + ... +# Subtest: codex: constraint "privacy-session-optout-first" survives somewhere in the skill corpus +ok 4050 - codex: constraint "privacy-session-optout-first" survives somewhere in the skill corpus + --- + duration_ms: 0.035124 + type: 'test' + ... +# Subtest: codex: constraint "graph-derived-facets" survives somewhere in the skill corpus +ok 4051 - codex: constraint "graph-derived-facets" survives somewhere in the skill corpus + --- + duration_ms: 0.052029 + type: 'test' + ... +# Subtest: codex: constraint "graph-project-first" survives somewhere in the skill corpus +ok 4052 - codex: constraint "graph-project-first" survives somewhere in the skill corpus + --- + duration_ms: 0.051178 + type: 'test' + ... +# Subtest: codex: constraint "graph-keys-converge" survives somewhere in the skill corpus +ok 4053 - codex: constraint "graph-keys-converge" survives somewhere in the skill corpus + --- + duration_ms: 0.08537 + type: 'test' + ... +# Subtest: codex: constraint "graph-is-derived-not-truth" survives somewhere in the skill corpus +ok 4054 - codex: constraint "graph-is-derived-not-truth" survives somewhere in the skill corpus + --- + duration_ms: 0.430624 + type: 'test' + ... +# Subtest: every skill shipped to both hosts is covered by the divergence record +ok 4055 - every skill shipped to both hosts is covered by the divergence record + --- + duration_ms: 0.984116 + type: 'test' + ... +# Subtest: hypaware-privacy: host-specific content matches the recorded surface +ok 4056 - hypaware-privacy: host-specific content matches the recorded surface + --- + duration_ms: 0.185712 + type: 'test' + ... +# Subtest: hypaware-query: host-specific content matches the recorded surface +ok 4057 - hypaware-query: host-specific content matches the recorded surface + --- + duration_ms: 0.075815 + type: 'test' + ... +# Subtest: hypaware-reference: host-specific content matches the recorded surface +ok 4058 - hypaware-reference: host-specific content matches the recorded surface + --- + duration_ms: 0.080292 + type: 'test' + ... +# Subtest: skills recorded as fully shared stay identical across hosts +ok 4059 - skills recorded as fully shared stay identical across hosts + --- + duration_ms: 0.168406 + type: 'test' + ... +# Subtest: validateVectorSearchConfig defaults to no indexes and enabled refresh +ok 4060 - validateVectorSearchConfig defaults to no indexes and enabled refresh + --- + duration_ms: 1.203029 + type: 'test' + ... +# Subtest: refresh interval default is longer than cache maintenance (60m) +ok 4061 - refresh interval default is longer than cache maintenance (60m) + --- + duration_ms: 0.931536 + type: 'test' + ... +# Subtest: index name defaults to dataset.column +ok 4062 - index name defaults to dataset.column + --- + duration_ms: 0.280517 + type: 'test' + ... +# Subtest: explicit index name and id_column are honored +ok 4063 - explicit index name and id_column are honored + --- + duration_ms: 0.143779 + type: 'test' + ... +# Subtest: index names that would escape the state dir are rejected +ok 4064 - index names that would escape the state dir are rejected + --- + duration_ms: 0.226945 + type: 'test' + ... +# Subtest: duplicate index names are rejected +ok 4065 - duplicate index names are rejected + --- + duration_ms: 0.234937 + type: 'test' + ... +# Subtest: index declarations missing dataset or column are rejected with pointers +ok 4066 - index declarations missing dataset or column are rejected with pointers + --- + duration_ms: 0.132181 + type: 'test' + ... +# Subtest: refresh budgets reject non-positive values +ok 4067 - refresh budgets reject non-positive values + --- + duration_ms: 0.127995 + type: 'test' + ... +# Subtest: refresh interval accepts fractional minutes (sub-minute smoke ticks) +ok 4068 - refresh interval accepts fractional minutes (sub-minute smoke ticks) + --- + duration_ms: 0.256189 + type: 'test' + ... +# Subtest: refresh can be disabled +ok 4069 - refresh can be disabled + --- + duration_ms: 0.324824 + type: 'test' + ... +# Subtest: embedder-openai and vector-search manifests load and validate +ok 4070 - embedder-openai and vector-search manifests load and validate + --- + duration_ms: 4.193374 + type: 'test' + ... +# Subtest: both plugins are bundled but excluded from default activation +ok 4071 - both plugins are bundled but excluded from default activation + --- + duration_ms: 0.178451 + type: 'test' + ... +# Subtest: parseVectorSearchArgv parses flags in any order and joins the query +ok 4072 - parseVectorSearchArgv parses flags in any order and joins the query + --- + duration_ms: 0.172061 + type: 'test' + ... +# Subtest: parseVectorSearchArgv rejects a missing query and bad flags +ok 4073 - parseVectorSearchArgv rejects a missing query and bad flags + --- + duration_ms: 0.1642 + type: 'test' + ... +# Subtest: refreshIndexes: an already-expired deadline skips every pending shard and reports exhaustion +ok 4074 - refreshIndexes: an already-expired deadline skips every pending shard and reports exhaustion + --- + duration_ms: 1.42732 + type: 'test' + ... +# Subtest: refreshIndexes: the row budget stops further builds after the shard that crosses it +ok 4075 - refreshIndexes: the row budget stops further builds after the shard that crosses it + --- + duration_ms: 66.817065 + type: 'test' + ... +# Subtest: refreshIndexes: orphans sweep even when the budget is already spent +ok 4076 - refreshIndexes: orphans sweep even when the budget is already spent + --- + duration_ms: 0.930334 + type: 'test' + ... +# Subtest: refreshIndexes: a fresh shard is not rebuilt, and the report says so +ok 4077 - refreshIndexes: a fresh shard is not rebuilt, and the report says so + --- + duration_ms: 0.448782 + type: 'test' + ... +# Subtest: refreshIndexes: a configured embedder dimension that differs from the sidecar forces a rebuild +ok 4078 - refreshIndexes: a configured embedder dimension that differs from the sidecar forces a rebuild + --- + duration_ms: 2.430595 + type: 'test' + ... +# Subtest: collectShardTexts: identical texts collapse to one embedding by content hash +ok 4079 - collectShardTexts: identical texts collapse to one embedding by content hash + --- + duration_ms: 0.207736 + type: 'test' + ... +# Subtest: collectShardTexts: id_column keys rows by id and skips rows without one +ok 4080 - collectShardTexts: id_column keys rows by id and skips rows without one + --- + duration_ms: 0.192443 + type: 'test' + ... +# Subtest: searchVectorIndexes: --no-refresh with a shard built by another model is vector_model_mismatch +ok 4081 - searchVectorIndexes: --no-refresh with a shard built by another model is vector_model_mismatch + --- + duration_ms: 58.53609 + type: 'test' + ... +# Subtest: searchVectorIndexes: --no-refresh with a shard at another dimension is vector_dimension_mismatch +ok 4082 - searchVectorIndexes: --no-refresh with a shard at another dimension is vector_dimension_mismatch + --- + duration_ms: 0.82245 + type: 'test' + ... +# Subtest: searchVectorIndexes: no configured index matching the filter is vector_no_indexes +ok 4083 - searchVectorIndexes: no configured index matching the filter is vector_no_indexes + --- + duration_ms: 0.38768 + type: 'test' + ... +# Subtest: shardFileBase renders a sorted human label plus a partition hash +ok 4084 - shardFileBase renders a sorted human label plus a partition hash + --- + duration_ms: 6.978036 + type: 'test' + ... +# Subtest: shardFileBase: partitions whose labels collide get distinct names +ok 4085 - shardFileBase: partitions whose labels collide get distinct names + --- + duration_ms: 0.454941 + type: 'test' + ... +# Subtest: partitionLabel renders unhashed display labels +ok 4086 - partitionLabel renders unhashed display labels + --- + duration_ms: 0.203349 + type: 'test' + ... +# Subtest: computeShardStates: live partition without a shard is missing +ok 4087 - computeShardStates: live partition without a shard is missing + --- + duration_ms: 0.202197 + type: 'test' + ... +# Subtest: computeShardStates: matching declaration, model, and row count is fresh +ok 4088 - computeShardStates: matching declaration, model, and row count is fresh + --- + duration_ms: 0.176508 + type: 'test' + ... +# Subtest: computeShardStates: model mismatch is stale_model, not an error +ok 4089 - computeShardStates: model mismatch is stale_model, not an error + --- + duration_ms: 0.122997 + type: 'test' + ... +# Subtest: computeShardStates: row count drift is stale_rows +ok 4090 - computeShardStates: row count drift is stale_rows + --- + duration_ms: 0.102837 + type: 'test' + ... +# Subtest: computeShardStates: model mismatch wins over row drift (one rebuild fixes both) +ok 4091 - computeShardStates: model mismatch wins over row drift (one rebuild fixes both) + --- + duration_ms: 0.968933 + type: 'test' + ... +# Subtest: computeShardStates: dataset/column/id_column drift is stale_config even when model and rows match +ok 4092 - computeShardStates: dataset/column/id_column drift is stale_config even when model and rows match + --- + duration_ms: 0.408531 + type: 'test' + ... +# Subtest: computeShardStates: sidecar whose recorded partition disagrees is stale_config +ok 4093 - computeShardStates: sidecar whose recorded partition disagrees is stale_config + --- + duration_ms: 0.322029 + type: 'test' + ... +# Subtest: computeShardStates: dimension drift is stale_dimension when an expected dimension is known +ok 4094 - computeShardStates: dimension drift is stale_dimension when an expected dimension is known + --- + duration_ms: 0.159713 + type: 'test' + ... +# Subtest: computeShardStates: dimension is ignored when unknown, matching, or the shard is empty +ok 4095 - computeShardStates: dimension is ignored when unknown, matching, or the shard is empty + --- + duration_ms: 0.142617 + type: 'test' + ... +# Subtest: computeShardStates: shard for an evicted partition is orphan +ok 4096 - computeShardStates: shard for an evicted partition is orphan + --- + duration_ms: 0.193053 + type: 'test' + ... +# Subtest: mergeTopK merges descending by score across shards +ok 4097 - mergeTopK merges descending by score across shards + --- + duration_ms: 0.481312 + type: 'test' + ... +# Subtest: mergeTopK with fewer hits than topK returns everything +ok 4098 - mergeTopK with fewer hits than topK returns everything + --- + duration_ms: 0.057197 + type: 'test' + ... +# Subtest: contentId is stable and collision-distinct for different texts +ok 4099 - contentId is stable and collision-distinct for different texts + --- + duration_ms: 0.089025 + type: 'test' + ... +# Subtest: gateway fallback id is invariant under every volatile block field +ok 4100 - gateway fallback id is invariant under every volatile block field + --- + duration_ms: 1.024036 + type: 'test' + ... +# Subtest: claude transcript match key is invariant under every volatile block field +ok 4101 - claude transcript match key is invariant under every volatile block field + --- + duration_ms: 0.170579 + type: 'test' + ... +# Subtest: stripVolatileBlockFields removes exactly the canonical list +ok 4102 - stripVolatileBlockFields removes exactly the canonical list + --- + duration_ms: 0.418355 + type: 'test' + ... +1..4102 +# tests 4104 +# suites 0 +# pass 4103 +# fail 0 +# cancelled 0 +# skipped 1 +# todo 0 +# duration_ms 15911.037278 +TEST EXIT=0 diff --git a/x/order.mjs b/x/order.mjs new file mode 100644 index 00000000..4238d51e --- /dev/null +++ b/x/order.mjs @@ -0,0 +1,10 @@ +import readline from 'node:readline/promises' +import { Readable } from 'node:stream' + +const input = Readable.from(['y\n', '3\n']) +const out = { write: () => true } +const rl = readline.createInterface({ input, output: out }) +rl.on('line', (l) => console.log('EVENT line:', JSON.stringify(l))) +rl.once('close', () => console.log('EVENT close')) +rl.question('Q? ').then((a) => console.log('EVENT question resolved:', JSON.stringify(a))) +setTimeout(() => { rl.close(); }, 200) diff --git a/x/pty_prog.mjs b/x/pty_prog.mjs new file mode 100644 index 00000000..3a02007f --- /dev/null +++ b/x/pty_prog.mjs @@ -0,0 +1,5 @@ +import process from 'node:process' +import { askYesNo } from '../src/core/cli/confirm.js' +const res = await askYesNo({ stdin: process.stdin, stderr: process.stderr }, 'Delete everything? [y/N] ') +process.stderr.write(`\nRESULT=${res}\n`) +process.exit(0) diff --git a/x/pty_variants.mjs b/x/pty_variants.mjs new file mode 100644 index 00000000..a094624f --- /dev/null +++ b/x/pty_variants.mjs @@ -0,0 +1,20 @@ +import process from 'node:process' +import readline from 'node:readline/promises' + +const variant = process.argv[2] +const rl = readline.createInterface({ input: process.stdin, output: process.stderr }) +function oldAskLineOnce(rl, input, prompt) { + return new Promise((resolve) => { + let settled = false + const done = (line) => { if (settled) return; settled = true; resolve(line) } + rl.once('close', () => done(null)) + rl.question(prompt).then(done, () => done(null)) + if (input.readableEnded === true) done(null) + }) +} +let answer +if (variant === 'old') answer = await oldAskLineOnce(rl, process.stdin, 'Delete everything? [y/N] ') +else answer = await Promise.race([rl.question('Delete everything? [y/N] '), new Promise(r => setTimeout(() => r(''), 400))]) +rl.close() +process.stderr.write(`\nANSWER=${JSON.stringify(answer)}\n`) +process.exit(0) diff --git a/x/typecheck.log b/x/typecheck.log new file mode 100644 index 00000000..57685aae --- /dev/null +++ b/x/typecheck.log @@ -0,0 +1,5 @@ + +> hypaware@1.22.0 typecheck +> tsc -p tsconfig.json --noEmit + +TC EXIT=0