From 171e0e9a778ab28a4bbc3c5a6fe25719c387a65a Mon Sep 17 00:00:00 2001 From: test Date: Sat, 15 Aug 2026 02:33:33 +0000 Subject: [PATCH 1/4] Close the rl.question EOF-hang class: fork menu, both y/N confirms, the OAuth paste `node:readline`'s `rl.question()` leaves its promise permanently unsettled when the input stream ends without a line. PR #773 fixed the three prompts in `walkthrough.js`; the four sites outside it had the same defect. The worst is the wizard's fork menu, which is the first screen `hyp init` shows, so a terminal whose stdin dried up never got past it: exit 13 on an unsettled top-level await when nothing else held the event loop, an indefinite hang when something did. Two decisions this makes, both stated in LLP 0190 #eof-everywhere: - The asker lives in `src/core/cli/line_asker.js`, beside `stdio.js` and `flush-streams.js` in the tree's existing home for small shared CLI helpers, not in `src/core/util/` (fs and JSON) and not in a new top-level location. Plugin workspaces already import `src/core/...` by relative path, so `claude-account` reaches it the way it reaches the observability and usage-policy modules. `queuedLineAsker` moves there unchanged; `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 cursor bookkeeping) and replacing only its promise. - EOF at the fork exits 0, not 130. The prompt prints `default 3` and LLP 0129 #fork settled that the default is Quit, so a spent stdin takes the answer the screen advertised; the fork's TUI path already returns `quit` for a real ctrl+c, so 130 in the readline fallback would judge a dropped terminal more harshly than the TUI judges a deliberate cancel. 130 stays where LLP 0135's cancel put it: prompts whose enter answers nothing. The `Code: ` paste in `claude-account login` is the one prompt with no default, so it does not invent one. With no loopback listener left to finish the sign-in, EOF is a failure that says so; with a listener up the paste lane stays pending rather than losing a race the browser may still win. Regression tests race every case against a 500ms timer, because the pre-fix failure mode is a hang and an unraced assertion never runs. Nine of the fourteen fail on master's behaviour and all fourteen pass here. --- .../claude-account/src/index.js | 45 +++- llp/0190-wizard-defaults-gate.decision.md | 32 +++ src/core/cli/confirm.js | 17 +- src/core/cli/line_asker.js | 127 ++++++++++++ src/core/cli/walkthrough.js | 75 +------ src/core/cli/wizard/fork.js | 22 +- src/core/plugin_install/confirm.js | 17 +- test/core/readline-prompt-eof.test.js | 193 ++++++++++++++++++ test/plugins/claude-account-paste-eof.test.js | 128 ++++++++++++ 9 files changed, 579 insertions(+), 77 deletions(-) create mode 100644 src/core/cli/line_asker.js create mode 100644 test/core/readline-prompt-eof.test.js create mode 100644 test/plugins/claude-account-paste-eof.test.js diff --git a/hypaware-core/plugins-workspace/claude-account/src/index.js b/hypaware-core/plugins-workspace/claude-account/src/index.js index 121713e8..f90a2f85 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,7 +203,11 @@ async function runLogin(cmdCtx, mode, stateDir) { output: /** @type {NodeJS.WritableStream} */ (/** @type {unknown} */ (cmdCtx.stdout)), }) try { - const pastePromise = rl.question('Code: ').then((pasted) => parsePastedAuthorization(pasted)) + const pastePromise = pasteAuthorizationLane({ + rl, + stdin: /** @type {NodeJS.ReadableStream} */ (cmdCtx.stdin), + hasCallback: callback !== null && callback !== undefined, + }) // A settled race leaves the loser pending; readline close (finally) // rejects a pending question, so keep that rejection handled. pastePromise.catch(() => {}) 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..b8cfb495 --- /dev/null +++ b/src/core/cli/line_asker.js @@ -0,0 +1,127 @@ +// @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. + * + * `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) + } + rl.once('close', () => done(null)) + // Writes the query synchronously, on a spent stream too. + rl.question(prompt).then(done, () => done(null)) + if (/** @type {{ readableEnded?: boolean }} */ (input).readableEnded === true) done(null) + }) +} + +/** + * 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..52226fa4 --- /dev/null +++ b/test/core/readline-prompt-eof.test.js @@ -0,0 +1,193 @@ +// @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 } 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('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..4545cacf --- /dev/null +++ b/test/plugins/claude-account-paste-eof.test.js @@ -0,0 +1,128 @@ +// @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 } 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 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() +}) From 2843b78616a5b72f06186387070caaea8f49c10c Mon Sep 17 00:00:00 2001 From: test Date: Sat, 15 Aug 2026 03:33:13 +0000 Subject: [PATCH 2/4] askLineOnce must not let EOF outrun an answer that arrived (review of #785) `rl.question` hands its answer back through a promise, a microtask late, while `close` fires synchronously. A stdin that delivers the line and the EOF in one burst - `Readable.from(['y\n'])`, the idiom this repo's own stdin fixtures use, or any readable that pushes data and `null` together - had `close` win that race, so a typed `y` at an irreversible `[y/N]` was silently read as the printed no, and a pasted OAuth code was silently discarded. `rl.question` alone got these right, so the new asker was strictly worse than what it replaced. The EOF settlements now run a turn behind the answer, and the last line of a stream that ends without a trailing newline (readline hands that one to `line`, never to the pending question) is taken too. Genuine EOF, spent streams, and real-terminal redraw are unchanged: verified on a pty for both the paced-keystroke redraw and ctrl+D, and for `hyp init < /dev/null` still exiting 0. Also corrects the stale claim beside the OAuth race that closing the interface rejects a pending question. It does not; that is the defect being worked around. Co-Authored-By: Claude --- .../claude-account/src/index.js | 6 +++-- src/core/cli/line_asker.js | 21 +++++++++++++-- test/core/readline-prompt-eof.test.js | 27 ++++++++++++++++++- test/plugins/claude-account-paste-eof.test.js | 16 ++++++++++- 4 files changed, 64 insertions(+), 6 deletions(-) diff --git a/hypaware-core/plugins-workspace/claude-account/src/index.js b/hypaware-core/plugins-workspace/claude-account/src/index.js index f90a2f85..23a2c4b0 100644 --- a/hypaware-core/plugins-workspace/claude-account/src/index.js +++ b/hypaware-core/plugins-workspace/claude-account/src/index.js @@ -208,8 +208,10 @@ async function runLogin(cmdCtx, mode, stateDir) { stdin: /** @type {NodeJS.ReadableStream} */ (cmdCtx.stdin), hasCallback: callback !== null && callback !== undefined, }) - // A settled race leaves the loser pending; readline close (finally) - // rejects a pending question, so keep that rejection handled. + // 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/src/core/cli/line_asker.js b/src/core/cli/line_asker.js index b8cfb495..04df8425 100644 --- a/src/core/cli/line_asker.js +++ b/src/core/cli/line_asker.js @@ -23,6 +23,17 @@ * `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 @@ -44,10 +55,16 @@ export function askLineOnce(rl, input, prompt) { settled = true resolve(line) } - rl.once('close', () => done(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(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) done(null) + if (/** @type {{ readableEnded?: boolean }} */ (input).readableEnded === true) endOfInput() }) } diff --git a/test/core/readline-prompt-eof.test.js b/test/core/readline-prompt-eof.test.js index 52226fa4..e1d5bbdd 100644 --- a/test/core/readline-prompt-eof.test.js +++ b/test/core/readline-prompt-eof.test.js @@ -20,7 +20,7 @@ import test from 'node:test' import assert from 'node:assert/strict' -import { PassThrough } from 'node:stream' +import { PassThrough, Readable } from 'node:stream' import { askYesNo } from '../../src/core/cli/confirm.js' import { @@ -165,6 +165,31 @@ test('askYesNo still honours an explicit yes', async () => { 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('plugin install confirm declines on a stdin that ends without a line', async () => { const stdin = new PassThrough() const stdout = makeBuf() diff --git a/test/plugins/claude-account-paste-eof.test.js b/test/plugins/claude-account-paste-eof.test.js index 4545cacf..ea8b0f05 100644 --- a/test/plugins/claude-account-paste-eof.test.js +++ b/test/plugins/claude-account-paste-eof.test.js @@ -18,7 +18,7 @@ import test from 'node:test' import assert from 'node:assert/strict' import readline from 'node:readline/promises' -import { PassThrough } from 'node:stream' +import { PassThrough, Readable } from 'node:stream' import { pasteAuthorizationLane } from '../../hypaware-core/plugins-workspace/claude-account/src/index.js' @@ -116,6 +116,20 @@ test('paste lane leaves the loopback listener to finish when stdin ends under it 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 still parses a pasted code', async () => { const stdin = new PassThrough() const rl = makeRl(stdin, makeBuf()) From 75a3effec2dfaed76487c1495d7cfe292468519f Mon Sep 17 00:00:00 2001 From: neutral Date: Sat, 15 Aug 2026 04:22:32 +0000 Subject: [PATCH 3/4] 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 --- npm-install.log | 11 + src/core/cli/line_asker.js | 26 +- test/core/readline-prompt-eof.test.js | 25 + test/plugins/claude-account-paste-eof.test.js | 14 + x/burst.mjs | 39 + x/invert.mjs | 20 + x/line_asker.fixed.js | 160 + x/line_asker.orig.js | 144 + x/msg.txt | 27 + x/npm-test.log | 24648 ++++++++++++++++ x/order.mjs | 10 + x/pty_prog.mjs | 5 + x/pty_variants.mjs | 20 + x/typecheck.log | 5 + 14 files changed, 25149 insertions(+), 5 deletions(-) create mode 100644 npm-install.log create mode 100644 x/burst.mjs create mode 100644 x/invert.mjs create mode 100644 x/line_asker.fixed.js create mode 100644 x/line_asker.orig.js create mode 100644 x/msg.txt create mode 100644 x/npm-test.log create mode 100644 x/order.mjs create mode 100644 x/pty_prog.mjs create mode 100644 x/pty_variants.mjs create mode 100644 x/typecheck.log diff --git a/npm-install.log b/npm-install.log new file mode 100644 index 00000000..f571f73b --- /dev/null +++ b/npm-install.log @@ -0,0 +1,11 @@ + +> hypaware@1.22.0 prepare +> npm run build:types + + +> hypaware@1.22.0 build:types +> tsc -p tsconfig.build.json + + +added 44 packages in 2s +INSTALL EXIT=0 diff --git a/src/core/cli/line_asker.js b/src/core/cli/line_asker.js index 04df8425..1627dafb 100644 --- a/src/core/cli/line_asker.js +++ b/src/core/cli/line_asker.js @@ -32,7 +32,12 @@ * 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. + * 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 @@ -55,13 +60,24 @@ export function askLineOnce(rl, input, prompt) { 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(null)) + const endOfInput = () => setImmediate(() => done(unaskedLine)) 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) + // 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() diff --git a/test/core/readline-prompt-eof.test.js b/test/core/readline-prompt-eof.test.js index e1d5bbdd..b6a2dc89 100644 --- a/test/core/readline-prompt-eof.test.js +++ b/test/core/readline-prompt-eof.test.js @@ -190,6 +190,31 @@ test('askYesNo takes a final answer with no trailing newline', async () => { 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() diff --git a/test/plugins/claude-account-paste-eof.test.js b/test/plugins/claude-account-paste-eof.test.js index ea8b0f05..a294fca5 100644 --- a/test/plugins/claude-account-paste-eof.test.js +++ b/test/plugins/claude-account-paste-eof.test.js @@ -130,6 +130,20 @@ test('paste lane parses a code delivered in the same burst as the EOF', async () 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()) 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 From a1787e0ef09c84277d23a14530fbc112a4a3aba4 Mon Sep 17 00:00:00 2001 From: test Date: Sat, 15 Aug 2026 05:14:30 +0000 Subject: [PATCH 4/4] Drop npm-install.log, committed by accident It was picked up by the round 2 fix commit. It is not gitignored and is not in the published files set, so it has no runtime effect, but it would otherwise land on master as build noise. --- npm-install.log | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 npm-install.log diff --git a/npm-install.log b/npm-install.log deleted file mode 100644 index f571f73b..00000000 --- a/npm-install.log +++ /dev/null @@ -1,11 +0,0 @@ - -> hypaware@1.22.0 prepare -> npm run build:types - - -> hypaware@1.22.0 build:types -> tsc -p tsconfig.build.json - - -added 44 packages in 2s -INSTALL EXIT=0