diff --git a/hypaware-core/plugins-workspace/claude-account/src/index.js b/hypaware-core/plugins-workspace/claude-account/src/index.js
index 121713e8..23a2c4b0 100644
--- a/hypaware-core/plugins-workspace/claude-account/src/index.js
+++ b/hypaware-core/plugins-workspace/claude-account/src/index.js
@@ -4,6 +4,7 @@ import { spawn } from 'node:child_process'
import http from 'node:http'
import readline from 'node:readline/promises'
+import { askLineOnce } from '../../../../src/core/cli/line_asker.js'
import { CLAUDE_ACCOUNT_CONFIG_SECTION, resolveMode, validateClaudeAccountConfig } from './config.js'
import { resolveCredential } from './credential.js'
import {
@@ -22,6 +23,7 @@ import {
} from './store.js'
/**
+ * @import { Interface } from 'node:readline/promises'
* @import { PluginActivationContext, CommandRunContext } from '../../../../hypaware-plugin-kernel-types.js'
* @import { AnthropicCredentialCapability } from './types.js'
*/
@@ -131,6 +133,43 @@ async function runCredential(cmdCtx, config, stateDir) {
}
}
+/**
+ * The `Code: ` paste fallback as its own lane, so the rule it encodes for
+ * a stdin that can no longer answer is readable and testable without a
+ * browser.
+ *
+ * `rl.question` leaves its promise permanently unsettled at EOF, so with
+ * no loopback listener to race - the port could not be bound, or the
+ * consumer flow fell back to the hosted callback - a login on a stdin
+ * that dried up waited forever on a paste that could never arrive.
+ * `askLineOnce` settles that as `null`, and the two cases part here:
+ *
+ * - No listener: nothing else can finish the login, so EOF is a real
+ * failure and says so, and `runLogin` prints it and exits 1.
+ * - A listener is up: the browser can still land on it, so the paste lane
+ * deliberately stays pending rather than losing the race for it. An EOF
+ * on the fallback input is not evidence about the primary flow, and a
+ * rejection here would settle `Promise.race` and abort a sign-in that
+ * was still on its way.
+ *
+ * `null` is not folded into the empty line the way the wizard's prompts
+ * fold it: `parsePastedAuthorization('')` throws `empty authorization
+ * code`, so an unanswerable prompt would report itself as a malformed
+ * paste the user never made.
+ *
+ * @ref LLP 0190#eof-everywhere [implements]: a spent stdin settles the prompt instead of waiting on an answer that can never come; this prompt has no default, so it settles as a failure rather than an answer
+ *
+ * @param {{ rl: Interface, stdin: NodeJS.ReadableStream, hasCallback: boolean }} args
+ * @returns {Promise<{ code: string, state: string }>}
+ */
+export function pasteAuthorizationLane({ rl, stdin, hasCallback }) {
+ return askLineOnce(rl, stdin, 'Code: ').then((pasted) => {
+ if (pasted !== null) return parsePastedAuthorization(pasted)
+ if (hasCallback) return new Promise(() => {})
+ throw new Error('stdin ended before an authorization code was pasted')
+ })
+}
+
/**
* @param {CommandRunContext} cmdCtx
* @param {'org_key' | 'subscription'} mode
@@ -164,9 +203,15 @@ async function runLogin(cmdCtx, mode, stateDir) {
output: /** @type {NodeJS.WritableStream} */ (/** @type {unknown} */ (cmdCtx.stdout)),
})
try {
- const pastePromise = rl.question('Code: ').then((pasted) => parsePastedAuthorization(pasted))
- // A settled race leaves the loser pending; readline close (finally)
- // rejects a pending question, so keep that rejection handled.
+ const pastePromise = pasteAuthorizationLane({
+ rl,
+ stdin: /** @type {NodeJS.ReadableStream} */ (cmdCtx.stdin),
+ hasCallback: callback !== null && callback !== undefined,
+ })
+ // A settled race leaves the loser pending. Closing the interface does
+ // not settle it either way (that is the defect `askLineOnce` works
+ // around), so what is guarded here is a malformed paste landing after
+ // the callback already won: keep that rejection handled.
pastePromise.catch(() => {})
const { code, state } = await (callback
? Promise.race([callback.result, pastePromise])
diff --git a/llp/0190-wizard-defaults-gate.decision.md b/llp/0190-wizard-defaults-gate.decision.md
index 3613e4df..5149bb3b 100644
--- a/llp/0190-wizard-defaults-gate.decision.md
+++ b/llp/0190-wizard-defaults-gate.decision.md
@@ -125,6 +125,38 @@ everything behind the prompt - the store schema, editor semantics over
shown candidates, seam enforcement, corrupt-store fail-closed - is
LLP 0188's and is unchanged.
+**The EOF rule is the tree's, not this
+lane's, and the asker that implements it is shared.** The defect is
+`node:readline`'s, so it is at every readline prompt HypAware has, and
+the rule above answers all of them: **a stdin that can no longer answer
+takes the default the prompt printed; a prompt whose enter has no
+default settles as a cancel or a failure instead, never as an invented
+answer.** `queuedLineAsker` moves out of `walkthrough.js` into
+`src/core/cli/line_asker.js`, beside `stdio.js` and `flush-streams.js`,
+where a plugin workspace can import it too; `askLineOnce` joins it for
+the prompts that ask once on an interface that may be a real terminal,
+keeping `rl.question` as the thing that writes the query (readline
+redraws a terminal line from its own bookkeeping, so a query it never
+saw is a query it cannot redraw) and replacing only its promise.
+
+Applied outward, the rule settles the exit code the class kept raising.
+The wizard's fork menu prints `default 3`, and LLP 0129 #fork already
+settled that the default is Quit, so EOF there is Quit and the wizard
+exits 0 having written nothing: the same result the fork's TUI path
+already returns for a real ctrl+c at that screen. 130 is not the answer
+for a dropped terminal at a prompt that advertised a default, because a
+prompt with a default cannot tell a dropped terminal from a bare enter
+and should not pretend to; 130 stays where LLP 0135's cancel put it, at
+the prompts whose enter answers nothing. The two `[y/N]` confirms
+(`src/core/cli/confirm.js`, `src/core/plugin_install/confirm.js`) take
+their printed no, which is the safe direction for the irreversible verbs
+behind them, and their callers' exit codes are unchanged: an EOF decline
+is reported exactly as a typed `n` is. The one prompt with no default is
+`claude-account login`'s `Code: ` paste, and it does not invent one: with
+no loopback listener left to finish the sign-in, EOF is a failure that
+says so, and with a listener up the paste lane stays pending rather than
+losing a race the browser may still win.
+
**One gate prompt shape for both lanes.** The
gate is a `ConfirmSelectQuestion` asked through
`defaultConfirmSelectPromptFactory`: a TUI select on a real TTY, a
diff --git a/src/core/cli/confirm.js b/src/core/cli/confirm.js
index 47b50866..b1785b17 100644
--- a/src/core/cli/confirm.js
+++ b/src/core/cli/confirm.js
@@ -3,6 +3,7 @@
import process from 'node:process'
import readline from 'node:readline/promises'
+import { askLineOnce } from './line_asker.js'
import { isTty } from './stdio.js'
/**
@@ -17,18 +18,28 @@ import { isTty } from './stdio.js'
* Anything other than `y`/`yes` is a no: the default has to be the safe
* one for a verb nobody can undo.
*
+ * That includes a terminal that stops being able to answer. `rl.question`
+ * leaves its promise permanently unsettled at EOF, so a ctrl+D or a
+ * dropped session hung the irreversible verb on its own confirmation
+ * instead of declining it. `askLineOnce` settles that case as `null`,
+ * read here as the empty line the `[y/N]` already treats as a no - so
+ * the EOF answer is the printed default, and cannot drift from it.
+ *
+ * @ref LLP 0190#eof-everywhere [implements]: a spent stdin lands on the prompt's stated default rather than waiting on an answer that can never come
+ *
* @param {CommandRunContext} ctx
* @param {string} question rendered verbatim, including its `[y/N]` suffix
* @returns {Promise}
*/
export async function askYesNo(ctx, question) {
+ const input = /** @type {NodeJS.ReadableStream} */ (ctx.stdin ?? process.stdin)
const rl = readline.createInterface({
- input: /** @type {NodeJS.ReadableStream} */ (ctx.stdin ?? process.stdin),
+ input,
output: /** @type {NodeJS.WritableStream} */ (/** @type {unknown} */ (ctx.stderr)),
})
try {
- const answer = await rl.question(question)
- return /^y(es)?$/i.test(answer.trim())
+ const answer = await askLineOnce(rl, input, question)
+ return /^y(es)?$/i.test((answer ?? '').trim())
} finally {
rl.close()
}
diff --git a/src/core/cli/line_asker.js b/src/core/cli/line_asker.js
new file mode 100644
index 00000000..1627dafb
--- /dev/null
+++ b/src/core/cli/line_asker.js
@@ -0,0 +1,160 @@
+// @ts-check
+
+/**
+ * @import { Interface } from 'node:readline/promises'
+ */
+
+/**
+ * Ask one question and settle even when the stream can no longer answer.
+ *
+ * The same EOF defect `queuedLineAsker` documents below, for the prompts
+ * that ask exactly once and are not built with `terminal: false`. There,
+ * writing the query directly to the output stream is not equivalent to
+ * asking: on a real terminal readline redraws the line it is editing from
+ * its own cursor bookkeeping, and a query it never saw is a query it
+ * cannot redraw. So this keeps `rl.question` as the thing that writes and
+ * reads, and only replaces the promise, which is the part that is broken.
+ *
+ * Two settlements are added to it: `close`, for a stream that ends while
+ * the question is on screen (a terminal that dropped, a ctrl+D), and the
+ * stream's own `readableEnded`, for a stream that was already spent when
+ * the interface was built. Readline registers its `end` listener at
+ * construction, so the second case never emits `close` at all and a
+ * `close`-only guard still hangs on it. Both resolve `null`, which callers
+ * read as the empty line - the answer a bare enter would have given.
+ *
+ * Neither settlement may outrun an answer that did arrive, and both can:
+ * `close` fires synchronously, while `rl.question` hands its answer back
+ * through a promise, a microtask later. A stream that delivers the line
+ * and EOF in one burst (`Readable.from(['y\n'])`, any readable that
+ * pushes data and `null` together) would have `close` win that race and
+ * discard a real `y`, so the EOF settlements are deferred a turn and the
+ * delivered line settles first. Readline also routes the last line of a
+ * stream that ends without a trailing newline to `line` rather than to
+ * the pending question, which is a second answer `question` alone never
+ * sees, so that one is taken too - but only at EOF, and only if the
+ * question never got a line of its own. Readline emits `line` while no
+ * question is pending, so the same event also carries anything typed or
+ * pasted PAST the answer ("n\ny\n" in one burst), and settling on that
+ * directly would answer with the last line of the burst rather than the
+ * first: a typed `n` at an irreversible confirm would read as `y`.
+ *
+ * `rl.question`'s own promise is left permanently unsettled at EOF rather
+ * than rejected, so nothing here can leak an unhandled rejection; the
+ * handler attached to it is only there for the abort/close rejections
+ * newer readline versions may raise.
+ *
+ * @ref LLP 0190#eof-everywhere [implements]: a spent stdin settles on the prompt's stated default instead of waiting on an answer that can never come
+ *
+ * @param {Interface} rl
+ * @param {NodeJS.ReadableStream} input
+ * @param {string} prompt
+ * @returns {Promise} the answer line, `null` once the stream is spent
+ */
+export function askLineOnce(rl, input, prompt) {
+ return new Promise((resolve) => {
+ let settled = false
+ /** @param {string | null} line */
+ const done = (line) => {
+ if (settled) return
+ settled = true
+ resolve(line)
+ }
+ /**
+ * The last line readline emitted outside the question, held rather
+ * than settled on. Only read if the question itself never answered.
+ * @type {string | null}
+ */
+ let unaskedLine = null
+ // A turn behind the answer, never ahead of it: an answer delivered in
+ // the same burst as the EOF has to settle first.
+ const endOfInput = () => setImmediate(() => done(unaskedLine))
+ rl.once('close', endOfInput)
+ // Readline emits `line` only while no question is pending, so this is
+ // either the unterminated last line of the stream (an answer the
+ // question never sees) or a line that arrived past the answer. Holding
+ // it for EOF takes the first without letting the second overwrite an
+ // answer the question already has.
+ rl.on('line', (line) => {
+ unaskedLine = line
+ })
+ // Writes the query synchronously, on a spent stream too.
+ rl.question(prompt).then(done, () => done(null))
+ if (/** @type {{ readableEnded?: boolean }} */ (input).readableEnded === true) endOfInput()
+ })
+}
+
+/**
+ * Ceiling on the answer lines held for a still-unasked prompt. A prompt
+ * interface asks a small fixed number of questions and is closed straight
+ * after, so anything past a handful is unreadable backlog: a pipe that
+ * floods stdin (`yes |`) must not grow an array for as long as the prompt
+ * is on screen.
+ */
+const MAX_QUEUED_LINES = 4
+
+/**
+ * Read answer lines off a readline interface without losing one and
+ * without ever waiting on a stream that can no longer answer.
+ *
+ * `rl.question()` cannot do either job here. It registers its `line`
+ * listener only when it is called, so a second answer line arriving in
+ * the same chunk as the first ("y\n3\n" from a pipe) is emitted and
+ * dropped before a re-ask can ask: readline emits both synchronously
+ * and the next `question()` is a microtask away. And at EOF its promise
+ * is left permanently unsettled - the interface closes, `question`
+ * neither resolves nor rejects - which is a hang, or a silent
+ * "unsettled top-level await" exit. Queueing every line from
+ * construction fixes the first; resolving the pending ask as `null` on
+ * `close` fixes the second.
+ *
+ * `close` alone is not enough for the second job. Readline registers its
+ * `end` listener when the interface is built, so an interface built over
+ * a stream that has ALREADY ended never sees an `end` and never emits
+ * `close` - the second prompt asked on a spent stdin would wait forever
+ * even though the first resolved. The stream's own `readableEnded` is
+ * the answer readline can no longer give, so it seeds `closed` here.
+ *
+ * This lives beside `stdio.js` and `flush-streams.js` rather than inside
+ * any one prompt: every readline prompt in the tree has the same EOF
+ * defect, and a shared asker is what keeps the answer to it in one place.
+ *
+ * @ref LLP 0190#eof-everywhere [implements]: a spent stdin settles on the prompt's stated default instead of waiting on an answer that can never come
+ *
+ * @param {Interface} rl
+ * @param {NodeJS.ReadableStream} input
+ * @param {NodeJS.WritableStream} output
+ * @returns {(prompt: string) => Promise} writes one prompt and takes the next line, `null` once the stream is spent
+ */
+export function queuedLineAsker(rl, input, output) {
+ /** @type {string[]} */
+ const queued = []
+ /** @type {((line: string | null) => void) | null} */
+ let waiting = null
+ let closed = /** @type {{ readableEnded?: boolean }} */ (input).readableEnded === true
+ const take = () => {
+ const resolve = waiting
+ waiting = null
+ return resolve
+ }
+ rl.on('line', (line) => {
+ const resolve = take()
+ if (resolve) resolve(line)
+ else if (queued.length < MAX_QUEUED_LINES) queued.push(line)
+ })
+ rl.on('close', () => {
+ closed = true
+ const resolve = take()
+ if (resolve) resolve(null)
+ })
+ return function askLine(prompt) {
+ // Byte-identical to what `rl.question` writes: with `terminal: false`
+ // readline puts the query straight on the output stream.
+ output.write(prompt)
+ if (queued.length > 0) return Promise.resolve(/** @type {string} */ (queued.shift()))
+ if (closed) return Promise.resolve(null)
+ return new Promise((resolve) => {
+ waiting = resolve
+ })
+ }
+}
diff --git a/src/core/cli/walkthrough.js b/src/core/cli/walkthrough.js
index 5ced86ff..920d9e1b 100644
--- a/src/core/cli/walkthrough.js
+++ b/src/core/cli/walkthrough.js
@@ -15,6 +15,7 @@ import { materializeClientAssets } from '../runtime/client_assets.js'
import { clientAssetStateRoot } from '../runtime/client_asset_ledger.js'
import { buildPluginCatalog } from '../plugin_catalog.js'
import { detectPickerSources } from './detect.js'
+import { queuedLineAsker } from './line_asker.js'
import { withSpinner } from './spinner.js'
import { multiselect, select } from './tui/index.js'
import { PromptBackRequestedError, PromptCancelledError, isPromptCancelledError } from './tui/runtime.js'
@@ -29,7 +30,6 @@ import { shouldUseTui } from './tui-router.js'
export const WALKTHROUGH_CANCEL_EXIT_CODE = 130
/**
- * @import { Interface } from 'node:readline/promises'
* @import { AiGatewayCapability, CapabilityRegistry, HypAwareV2Config, PluginConfigInstance, PluginName, SinkConfigInstance } from '../../../hypaware-plugin-kernel-types.js'
* @import { ClientDescriptor, PickerDescriptor } from '../../../src/core/types.js'
* @import { DaemonInstallOptions } from '../../../src/core/daemon/types.js'
@@ -84,74 +84,11 @@ export function resolveHypHome(env) {
*/
const MAX_MALFORMED_REASKS = 1
-/**
- * Ceiling on the answer lines held for a still-unasked prompt. An
- * interface asks at most `1 + MAX_MALFORMED_REASKS` questions and is
- * closed straight after, so anything past a handful is unreadable
- * backlog: a pipe that floods stdin (`yes |`) must not grow an array
- * for as long as the prompt is on screen.
- */
-const MAX_QUEUED_LINES = 4
-
-/**
- * Read answer lines off a readline interface without losing one and
- * without ever waiting on a stream that can no longer answer.
- *
- * `rl.question()` cannot do either job here. It registers its `line`
- * listener only when it is called, so a second answer line arriving in
- * the same chunk as the first ("y\n3\n" from a pipe) is emitted and
- * dropped before a re-ask can ask: readline emits both synchronously
- * and the next `question()` is a microtask away. And at EOF its promise
- * is left permanently unsettled - the interface closes, `question`
- * neither resolves nor rejects - which is a hang, or a silent
- * "unsettled top-level await" exit. Queueing every line from
- * construction fixes the first; resolving the pending ask as `null` on
- * `close` fixes the second.
- *
- * `close` alone is not enough for the second job. Readline registers its
- * `end` listener when the interface is built, so an interface built over
- * a stream that has ALREADY ended never sees an `end` and never emits
- * `close` - the second prompt asked on a spent stdin would wait forever
- * even though the first resolved. The stream's own `readableEnded` is
- * the answer readline can no longer give, so it seeds `closed` here.
- *
- * @param {Interface} rl
- * @param {NodeJS.ReadableStream} input
- * @param {NodeJS.WritableStream} output
- * @returns {(prompt: string) => Promise} writes one prompt and takes the next line, `null` once the stream is spent
- */
-function queuedLineAsker(rl, input, output) {
- /** @type {string[]} */
- const queued = []
- /** @type {((line: string | null) => void) | null} */
- let waiting = null
- let closed = /** @type {{ readableEnded?: boolean }} */ (input).readableEnded === true
- const take = () => {
- const resolve = waiting
- waiting = null
- return resolve
- }
- rl.on('line', (line) => {
- const resolve = take()
- if (resolve) resolve(line)
- else if (queued.length < MAX_QUEUED_LINES) queued.push(line)
- })
- rl.on('close', () => {
- closed = true
- const resolve = take()
- if (resolve) resolve(null)
- })
- return function askLine(prompt) {
- // Byte-identical to what `rl.question` writes: with `terminal: false`
- // readline puts the query straight on the output stream.
- output.write(prompt)
- if (queued.length > 0) return Promise.resolve(/** @type {string} */ (queued.shift()))
- if (closed) return Promise.resolve(null)
- return new Promise((resolve) => {
- waiting = resolve
- })
- }
-}
+// `queuedLineAsker` used to live here, module-private. It is now shared
+// from `./line_asker.js`: the same EOF defect it was written for exists at
+// every readline prompt in the tree (the wizard's fork menu, the y/N
+// confirms, a plugin's OAuth paste fallback), and one asker they all read
+// through is what keeps the answer in one place.
/**
* Build the default interactive prompt. Uses Node's `readline` against
diff --git a/src/core/cli/wizard/fork.js b/src/core/cli/wizard/fork.js
index 45c4373d..d067d03c 100644
--- a/src/core/cli/wizard/fork.js
+++ b/src/core/cli/wizard/fork.js
@@ -11,6 +11,7 @@ import readline from 'node:readline/promises'
import { Attr, getLogger, withSpan } from '../../observability/index.js'
import { collectHypAwareStatus } from '../../daemon/status.js'
+import { queuedLineAsker } from '../line_asker.js'
import { select } from '../tui/index.js'
import { isPromptBackError, isPromptCancelledError } from '../tui/runtime.js'
import { shouldUseTui } from '../tui-router.js'
@@ -366,6 +367,22 @@ const FRIENDLY_CLIENT_LABELS = /** @type {Record} */ ({
* `allowBack`, a `b` answer resolves to `back` (the readline form of the
* TUI's escape, LLP 0191); any other stray answer still quits.
*
+ * A stdin that ends without a line is read through `queuedLineAsker`
+ * rather than `rl.question`, whose promise is left permanently unsettled
+ * at EOF - which on the wizard's first screen is the whole wizard
+ * hanging, or dying on an unsettled top-level await, before it has asked
+ * anything else. The EOF `null` is coalesced into the empty line rather
+ * than branched on, so a spent stdin takes exactly the default the
+ * prompt just printed (`default 3`, Quit) and the EOF answer cannot
+ * drift from the advertised one. Quit here means exit 0 with nothing
+ * written, which is also what the TUI path returns for a real ctrl+c at
+ * this screen (`isPromptCancelledError` -> `quit` above), so the
+ * fallback does not judge a dropped terminal more harshly than the TUI
+ * judges a deliberate cancel.
+ *
+ * @ref LLP 0190#eof-everywhere [implements]: a spent stdin lands on the prompt's stated default; 130 is for prompts whose enter has no default, and this one prints its own
+ * @ref LLP 0129#fork [constrained-by]: quit is the safe default at the fork, so the EOF answer is quit and the wizard reconfigures nothing by accident
+ *
* @param {{ stdin?: NodeJS.ReadableStream, stdout: RunWizardForkOptions['stdout'] }} opts
* @param {ConfiguredMenuOption[]} options
* @param {string} title
@@ -377,16 +394,17 @@ async function legacyMenuPrompt(opts, options, title, allowBack = false) {
const output = /** @type {NodeJS.WritableStream} */ (/** @type {any} */ (opts.stdout))
const defaultIdx = Math.max(0, options.findIndex((o) => o.value === 'quit'))
const rl = readline.createInterface({ input, output, terminal: false })
+ const askLine = queuedLineAsker(rl, input, output)
try {
output.write(`${title}\n`)
options.forEach((opt, i) => {
output.write(` ${i + 1}) ${opt.label}\n`)
if (opt.summary) output.write(` ${opt.summary}\n`)
})
- const answer = await rl.question(
+ const answer = await askLine(
`Choose [1-${options.length}, default ${defaultIdx + 1}${allowBack ? ', b back' : ''}]: `
)
- const trimmed = answer.trim()
+ const trimmed = (answer ?? '').trim()
if (allowBack && trimmed.toLowerCase() === 'b') return 'back'
if (trimmed === '') return options[defaultIdx]?.value ?? 'quit'
const n = Number.parseInt(trimmed, 10)
diff --git a/src/core/plugin_install/confirm.js b/src/core/plugin_install/confirm.js
index dc413a6b..d3ba32ef 100644
--- a/src/core/plugin_install/confirm.js
+++ b/src/core/plugin_install/confirm.js
@@ -2,6 +2,8 @@
import readline from 'node:readline/promises'
+import { queuedLineAsker } from '../cli/line_asker.js'
+
/**
* @import { PluginLockEntry, PluginSourceSpec } from '../../../hypaware-plugin-kernel-types.js'
* @import { ConfirmDecision, StagedArtifact } from '../../../src/core/plugin_install/types.js'
@@ -136,6 +138,16 @@ export async function decideConfirmation({ yes, tty, ask }) {
* counts as rejection. Closes the readline interface once an answer
* comes back so the dispatcher doesn't leak file descriptors.
*
+ * "Anything else" includes a stdin that ends without a line.
+ * `rl.question` leaves its promise permanently unsettled at EOF, so a
+ * terminal that dropped mid-install hung on the confirm rather than
+ * declining it. The asker's EOF `null` is coalesced into the empty line,
+ * which the printed `[y/N]` already reads as a no: the unanswerable
+ * question takes the default it advertised, and `decideConfirmation`
+ * turns that into the same `rejected` a typed `n` gives.
+ *
+ * @ref LLP 0190#eof-everywhere [implements]: a spent stdin lands on the prompt's stated default rather than waiting on an answer that can never come
+ *
* @param {{
* stdin: NodeJS.ReadableStream,
* stdout: NodeJS.WritableStream,
@@ -146,9 +158,10 @@ export async function decideConfirmation({ yes, tty, ask }) {
export function buildTtyPrompt({ stdin, stdout, promptText }) {
return async function ask() {
const rl = readline.createInterface({ input: stdin, output: stdout, terminal: false })
+ const askLine = queuedLineAsker(rl, stdin, stdout)
try {
- const answer = await rl.question(promptText ?? 'Proceed? [y/N] ')
- const trimmed = answer.trim().toLowerCase()
+ const answer = await askLine(promptText ?? 'Proceed? [y/N] ')
+ const trimmed = (answer ?? '').trim().toLowerCase()
return trimmed === 'y' || trimmed === 'yes'
} finally {
rl.close()
diff --git a/test/core/readline-prompt-eof.test.js b/test/core/readline-prompt-eof.test.js
new file mode 100644
index 00000000..b6a2dc89
--- /dev/null
+++ b/test/core/readline-prompt-eof.test.js
@@ -0,0 +1,243 @@
+// @ts-check
+
+// The readline prompts outside `walkthrough.js` on a stdin that can no
+// longer answer. `rl.question()` leaves its promise permanently unsettled
+// at EOF, so each of these waited forever on an answer that could never
+// arrive instead of taking the default it had just printed.
+//
+// The wizard's fork menu is the exposed one: it is the first screen `hyp
+// init` shows, so a terminal whose stdin dries up or drops never got past
+// it. The two `[y/N]` confirms are behind a TTY gate (LLP 0104,
+// LLP 0155#delete-confirm), so what lands on them is a ctrl+D or a dropped
+// session rather than a pipe.
+//
+// Every case is raced against a timer, because the pre-fix failure mode is
+// a hang rather than a wrong value and an unraced assertion would never
+// run at all.
+//
+// @ref LLP 0190#eof-everywhere [tests]: a spent stdin lands on the prompt's stated default rather than waiting on an answer that can never come
+// @ref LLP 0129#fork [tests]: quit stays the fork's answer when the terminal stops answering, so nothing is reconfigured by accident
+
+import test from 'node:test'
+import assert from 'node:assert/strict'
+import { PassThrough, Readable } from 'node:stream'
+
+import { askYesNo } from '../../src/core/cli/confirm.js'
+import {
+ buildForkOptions,
+ buildReturningGateOptions,
+ legacyForkPrompt,
+ legacyReturningGatePrompt,
+} from '../../src/core/cli/wizard/fork.js'
+import { buildTtyPrompt } from '../../src/core/plugin_install/confirm.js'
+
+/** Long enough to be unambiguous, short enough that a hang fails fast. */
+const SETTLE_MS = 500
+
+/** Sentinel the race resolves to when the prompt never answers. */
+const HUNG = Symbol('hung')
+
+/**
+ * Resolve `promise` or fail the test with the hang it is guarding against.
+ *
+ * @template T
+ * @param {Promise} promise
+ * @param {string} what
+ * @returns {Promise}
+ */
+async function settles(promise, what) {
+ /** @type {NodeJS.Timeout | undefined} */
+ let timer
+ const timeout = new Promise((resolve) => {
+ timer = setTimeout(() => resolve(HUNG), SETTLE_MS)
+ })
+ try {
+ const result = await Promise.race([promise, timeout])
+ assert.notEqual(result, HUNG, `${what} never settled within ${SETTLE_MS}ms - the prompt hung on EOF`)
+ return /** @type {T} */ (result)
+ } finally {
+ if (timer) clearTimeout(timer)
+ }
+}
+
+function makeBuf() {
+ /** @type {string[]} */
+ const chunks = []
+ return {
+ write(/** @type {string} */ chunk) {
+ chunks.push(chunk)
+ return true
+ },
+ text() {
+ return chunks.join('')
+ },
+ }
+}
+
+/**
+ * A stream that has already ended before the readline interface is built.
+ * Readline registers its `end` listener at construction, so this one never
+ * emits `close` - the case a `close`-only guard still hangs on.
+ *
+ * @returns {Promise}
+ */
+async function spentStdin() {
+ const stdin = new PassThrough()
+ stdin.resume()
+ stdin.end()
+ await new Promise((resolve) => setImmediate(resolve))
+ return stdin
+}
+
+test('fork menu takes its printed default on a stdin that ends without a line', async () => {
+ const stdin = new PassThrough()
+ const stdout = makeBuf()
+ const choice = legacyForkPrompt({ stdout: /** @type {any} */ (stdout), stderr: /** @type {any} */ (makeBuf()), stdin, env: {} }, buildForkOptions())
+ stdin.end()
+
+ // Quit, the default the menu printed, and the answer a bare enter gives:
+ // `runInitWizard` turns it into exit 0 with nothing written, which is
+ // also what the TUI path returns for a real ctrl+c at this screen.
+ assert.equal(await settles(choice, 'fork menu at EOF'), 'quit')
+ // The question is still asked, byte for byte: the default is taken
+ // because it was advertised, not instead of advertising it.
+ assert.match(stdout.text(), /Choose \[1-3, default 3\]: $/)
+})
+
+test('fork menu takes its printed default on a stdin that was already spent', async () => {
+ const stdin = await spentStdin()
+ const choice = legacyForkPrompt({ stdout: /** @type {any} */ (makeBuf()), stderr: /** @type {any} */ (makeBuf()), stdin, env: {} }, buildForkOptions())
+
+ assert.equal(await settles(choice, 'fork menu on a spent stdin'), 'quit')
+})
+
+test('fork menu still honours an explicit pick', async () => {
+ const stdin = new PassThrough()
+ const choice = legacyForkPrompt({ stdout: /** @type {any} */ (makeBuf()), stderr: /** @type {any} */ (makeBuf()), stdin, env: {} }, buildForkOptions())
+ stdin.write('1\n')
+
+ assert.equal(await settles(choice, 'fork menu with an answer'), 'team')
+})
+
+test('returning gate menu takes its printed default on a stdin that ends without a line', async () => {
+ const stdin = new PassThrough()
+ const stdout = makeBuf()
+ const choice = legacyReturningGatePrompt(
+ { stdout: /** @type {any} */ (stdout), stderr: /** @type {any} */ (makeBuf()), stdin, env: {} },
+ buildReturningGateOptions()
+ )
+ stdin.end()
+
+ assert.equal(await settles(choice, 'returning gate at EOF'), 'quit')
+ assert.match(stdout.text(), /Choose \[1-3, default 3\]: $/)
+})
+
+test('askYesNo declines on a stdin that ends without a line', async () => {
+ const stdin = new PassThrough()
+ const stderr = makeBuf()
+ const answered = askYesNo(/** @type {any} */ ({ stdin, stderr }), 'Delete everything? [y/N] ')
+ stdin.end()
+
+ // The `[y/N]` says no is the default, and an irreversible verb has to
+ // land there when the terminal stops being able to say otherwise.
+ assert.equal(await settles(answered, 'askYesNo at EOF'), false)
+ assert.match(stderr.text(), /\[y\/N\] $/)
+})
+
+test('askYesNo declines on a stdin that was already spent', async () => {
+ const stdin = await spentStdin()
+ const answered = askYesNo(
+ /** @type {any} */ ({ stdin, stderr: makeBuf() }),
+ 'Delete everything? [y/N] '
+ )
+
+ assert.equal(await settles(answered, 'askYesNo on a spent stdin'), false)
+})
+
+test('askYesNo still honours an explicit yes', async () => {
+ const stdin = new PassThrough()
+ const answered = askYesNo(
+ /** @type {any} */ ({ stdin, stderr: makeBuf() }),
+ 'Delete everything? [y/N] '
+ )
+ stdin.write('yes\n')
+
+ assert.equal(await settles(answered, 'askYesNo with an answer'), true)
+})
+
+test('askYesNo takes an answer delivered in the same burst as the EOF', async () => {
+ // `Readable.from` pushes the line and the EOF together, which is how a
+ // caller that injects a stdin usually builds one. The answer is there,
+ // so only a settlement that outran it could turn this `y` into a no.
+ const answered = askYesNo(
+ /** @type {any} */ ({ stdin: Readable.from(['y\n']), stderr: makeBuf() }),
+ 'Delete everything? [y/N] '
+ )
+
+ assert.equal(await settles(answered, 'askYesNo on a one-burst stdin'), true)
+})
+
+test('askYesNo takes a final answer with no trailing newline', async () => {
+ const stdin = new PassThrough()
+ const answered = askYesNo(
+ /** @type {any} */ ({ stdin, stderr: makeBuf() }),
+ 'Delete everything? [y/N] '
+ )
+ // Readline hands this one to `line` rather than to the pending
+ // question, so it is an answer `rl.question` alone never sees.
+ stdin.end('y')
+
+ assert.equal(await settles(answered, 'askYesNo on an unterminated answer'), true)
+})
+
+test('askYesNo answers with the line that answered it, not one that arrived after', async () => {
+ // Readline gives the first line to the pending question and emits every
+ // later one as `line`, so a burst carrying more than one line (a paste,
+ // or type-ahead for the next prompt) offers a second candidate answer.
+ // Taking it would read a typed `n` at an irreversible confirm as `y`,
+ // which is the one direction a `[y/N]` must never drift in.
+ const answered = askYesNo(
+ /** @type {any} */ ({ stdin: Readable.from(['n\ny\n']), stderr: makeBuf() }),
+ 'Delete everything? [y/N] '
+ )
+
+ assert.equal(await settles(answered, 'askYesNo on a two-line burst'), false)
+})
+
+test('askYesNo keeps its answer when an unterminated line follows it', async () => {
+ // Same shape, with the trailing line arriving through the end-of-stream
+ // flush rather than as a line of its own.
+ const answered = askYesNo(
+ /** @type {any} */ ({ stdin: Readable.from(['n\ny']), stderr: makeBuf() }),
+ 'Delete everything? [y/N] '
+ )
+
+ assert.equal(await settles(answered, 'askYesNo on a burst with an unterminated tail'), false)
+})
+
+test('plugin install confirm declines on a stdin that ends without a line', async () => {
+ const stdin = new PassThrough()
+ const stdout = makeBuf()
+ const ask = buildTtyPrompt({ stdin, stdout: /** @type {any} */ (stdout) })
+ const answered = ask()
+ stdin.end()
+
+ assert.equal(await settles(answered, 'plugin install confirm at EOF'), false)
+ assert.match(stdout.text(), /Proceed\? \[y\/N\] $/)
+})
+
+test('plugin install confirm declines on a stdin that was already spent', async () => {
+ const stdin = await spentStdin()
+ const ask = buildTtyPrompt({ stdin, stdout: /** @type {any} */ (makeBuf()) })
+
+ assert.equal(await settles(ask(), 'plugin install confirm on a spent stdin'), false)
+})
+
+test('plugin install confirm still honours an explicit yes', async () => {
+ const stdin = new PassThrough()
+ const ask = buildTtyPrompt({ stdin, stdout: /** @type {any} */ (makeBuf()) })
+ const answered = ask()
+ stdin.write('y\n')
+
+ assert.equal(await settles(answered, 'plugin install confirm with an answer'), true)
+})
diff --git a/test/plugins/claude-account-paste-eof.test.js b/test/plugins/claude-account-paste-eof.test.js
new file mode 100644
index 00000000..a294fca5
--- /dev/null
+++ b/test/plugins/claude-account-paste-eof.test.js
@@ -0,0 +1,156 @@
+// @ts-check
+
+// The `Code: ` paste fallback in `claude-account login` on a stdin that can
+// no longer answer. `rl.question()` leaves its promise permanently unsettled
+// at EOF, so a login with no loopback listener to race waited forever on a
+// paste that could never arrive.
+//
+// The two halves are asymmetric on purpose, and that is what these cases
+// pin: with no listener there is nothing else that can finish the login, so
+// EOF is a failure and says so; with a listener up the browser can still
+// land on it, so the paste lane must not settle and take the race away from
+// a sign-in that was still on its way.
+//
+// Raced against a timer, because the pre-fix failure mode is a hang.
+//
+// @ref LLP 0190#eof-everywhere [tests]: a spent stdin settles the prompt instead of waiting on an answer that can never come
+
+import test from 'node:test'
+import assert from 'node:assert/strict'
+import readline from 'node:readline/promises'
+import { PassThrough, Readable } from 'node:stream'
+
+import { pasteAuthorizationLane } from '../../hypaware-core/plugins-workspace/claude-account/src/index.js'
+
+const SETTLE_MS = 500
+const HUNG = Symbol('hung')
+
+/**
+ * @template T
+ * @param {Promise} promise
+ * @returns {Promise}
+ */
+async function raceSettle(promise) {
+ /** @type {NodeJS.Timeout | undefined} */
+ let timer
+ const timeout = new Promise((resolve) => {
+ timer = setTimeout(() => resolve(HUNG), SETTLE_MS)
+ })
+ try {
+ return await Promise.race([promise.then((v) => /** @type {any} */ ({ ok: v })), timeout])
+ } catch (err) {
+ return /** @type {any} */ ({ err })
+ } finally {
+ if (timer) clearTimeout(timer)
+ }
+}
+
+function makeBuf() {
+ /** @type {string[]} */
+ const chunks = []
+ return {
+ write(/** @type {string} */ chunk) {
+ chunks.push(chunk)
+ return true
+ },
+ text() {
+ return chunks.join('')
+ },
+ }
+}
+
+/**
+ * @param {PassThrough} stdin
+ * @param {{ write(chunk: string): unknown }} stdout
+ */
+function makeRl(stdin, stdout) {
+ return readline.createInterface({
+ input: stdin,
+ output: /** @type {any} */ (stdout),
+ terminal: false,
+ })
+}
+
+test('paste lane fails, rather than hanging, when stdin ends with no listener to wait on', async () => {
+ const stdin = new PassThrough()
+ const stdout = makeBuf()
+ const rl = makeRl(stdin, stdout)
+ const lane = pasteAuthorizationLane({ rl, stdin, hasCallback: false })
+ stdin.end()
+
+ const settled = /** @type {any} */ (await raceSettle(lane))
+ assert.notEqual(settled, HUNG, 'the paste lane never settled - it hung on EOF')
+ assert.ok(settled.err instanceof Error, 'EOF with no listener is a login failure, not a silent wait')
+ assert.match(settled.err.message, /stdin ended/)
+ assert.equal(stdout.text(), 'Code: ')
+ rl.close()
+})
+
+test('paste lane fails, rather than hanging, on a stdin that was already spent', async () => {
+ const stdin = new PassThrough()
+ stdin.resume()
+ stdin.end()
+ await new Promise((resolve) => setImmediate(resolve))
+
+ const rl = makeRl(stdin, makeBuf())
+ const settled = /** @type {any} */ (await raceSettle(pasteAuthorizationLane({ rl, stdin, hasCallback: false })))
+ assert.notEqual(settled, HUNG, 'the paste lane never settled on an already-spent stdin')
+ assert.ok(settled.err instanceof Error)
+ rl.close()
+})
+
+test('paste lane leaves the loopback listener to finish when stdin ends under it', async () => {
+ const stdin = new PassThrough()
+ const rl = makeRl(stdin, makeBuf())
+ const lane = pasteAuthorizationLane({ rl, stdin, hasCallback: true })
+ lane.catch(() => {})
+ stdin.end()
+
+ // The race a real login runs. An EOF on the fallback input is not
+ // evidence about the browser flow, so the listener still wins it.
+ const callbackResult = new Promise((resolve) => {
+ setTimeout(() => resolve({ code: 'from-browser', state: 'st' }), 20)
+ })
+ const settled = /** @type {any} */ (await raceSettle(Promise.race([callbackResult, lane])))
+ assert.deepEqual(settled.ok, { code: 'from-browser', state: 'st' })
+ rl.close()
+})
+
+test('paste lane parses a code delivered in the same burst as the EOF', async () => {
+ // The paste and the EOF arrive together, which is what a stdin built
+ // from a string does. The EOF must not take the race off a paste that
+ // landed.
+ const stdin = Readable.from(['the-code#the-state\n'])
+ const rl = readline.createInterface({ input: stdin, output: /** @type {any} */ (makeBuf()), terminal: false })
+
+ const settled = /** @type {any} */ (await raceSettle(
+ pasteAuthorizationLane({ rl, stdin: /** @type {any} */ (stdin), hasCallback: false })
+ ))
+ assert.deepEqual(settled.ok, { code: 'the-code', state: 'the-state' })
+ rl.close()
+})
+
+test('paste lane parses the pasted code, not a line that followed it', async () => {
+ // Readline hands the first line to the pending question and emits every
+ // later one as `line`. Settling on `line` directly would exchange the
+ // last line of the burst for the code that actually answered the prompt.
+ const stdin = Readable.from(['the-code#the-state\nnoise#noise\n'])
+ const rl = readline.createInterface({ input: stdin, output: /** @type {any} */ (makeBuf()), terminal: false })
+
+ const settled = /** @type {any} */ (await raceSettle(
+ pasteAuthorizationLane({ rl, stdin: /** @type {any} */ (stdin), hasCallback: false })
+ ))
+ assert.deepEqual(settled.ok, { code: 'the-code', state: 'the-state' })
+ rl.close()
+})
+
+test('paste lane still parses a pasted code', async () => {
+ const stdin = new PassThrough()
+ const rl = makeRl(stdin, makeBuf())
+ const lane = pasteAuthorizationLane({ rl, stdin, hasCallback: true })
+ stdin.write('the-code#the-state\n')
+
+ const settled = /** @type {any} */ (await raceSettle(lane))
+ assert.deepEqual(settled.ok, { code: 'the-code', state: 'the-state' })
+ rl.close()
+})
diff --git a/x/burst.mjs b/x/burst.mjs
new file mode 100644
index 00000000..9f69931e
--- /dev/null
+++ b/x/burst.mjs
@@ -0,0 +1,39 @@
+import readline from 'node:readline/promises'
+import { Readable } from 'node:stream'
+import { askLineOnce, queuedLineAsker } from '../src/core/cli/line_asker.js'
+
+async function viaAskLineOnce(chunks, terminal) {
+ const input = Readable.from(chunks)
+ const out = { write: () => true }
+ const rl = readline.createInterface({ input, output: out, ...(terminal === undefined ? {} : { terminal }) })
+ try { return await askLineOnce(rl, input, 'Proceed? [y/N] ') } finally { rl.close() }
+}
+async function viaPlainQuestion(chunks, terminal) {
+ const input = Readable.from(chunks)
+ const out = { write: () => true }
+ const rl = readline.createInterface({ input, output: out, ...(terminal === undefined ? {} : { terminal }) })
+ try {
+ return await Promise.race([rl.question('Proceed? [y/N] '), new Promise(r => setTimeout(() => r(''), 300))])
+ } finally { rl.close() }
+}
+async function viaQueued(chunks, terminal) {
+ const input = Readable.from(chunks)
+ const out = { write: () => true }
+ const rl = readline.createInterface({ input, output: out, ...(terminal === undefined ? {} : { terminal }) })
+ try { return await queuedLineAsker(rl, input, out)('Proceed? [y/N] ') } finally { rl.close() }
+}
+
+const cases = [
+ ['single line', ['y\n']],
+ ['two lines one burst', ['y\n3\n']],
+ ['two lines separate chunks', ['y\n', '3\n']],
+ ['line + trailing junk no newline', ['y\nzzz']],
+ ['no newline only', ['y']],
+ ['empty EOF', []],
+]
+for (const [name, chunks] of cases) {
+ const a = await viaAskLineOnce(chunks)
+ const p = await viaPlainQuestion(chunks)
+ const q = await viaQueued(chunks)
+ console.log(`${name.padEnd(32)} askLineOnce=${JSON.stringify(a)} rl.question=${JSON.stringify(p)} queued=${JSON.stringify(q)}`)
+}
diff --git a/x/invert.mjs b/x/invert.mjs
new file mode 100644
index 00000000..7a04ef89
--- /dev/null
+++ b/x/invert.mjs
@@ -0,0 +1,20 @@
+import { Readable, PassThrough } from 'node:stream'
+import { askYesNo } from '../src/core/cli/confirm.js'
+
+const mk = (chunks) => {
+ const s = Readable.from(chunks)
+ Object.defineProperty(s, 'isTTY', { value: true })
+ return s
+}
+const buf = () => { let v=''; return { write(c){ v+=String(c); return true }, text(){return v} } }
+
+for (const [name, chunks] of [
+ ['typed n then y', ['n\ny\n']],
+ ['typed n then y, 2 chunks', ['n\n','y\n']],
+ ['typed y then n', ['y\nn\n']],
+ ['typed n, junk no newline', ['n\ny']],
+]) {
+ const stderr = buf()
+ const res = await askYesNo({ stdin: mk(chunks), stderr }, 'Delete everything? [y/N] ')
+ console.log(`${name.padEnd(28)} -> askYesNo=${res}`)
+}
diff --git a/x/line_asker.fixed.js b/x/line_asker.fixed.js
new file mode 100644
index 00000000..1627dafb
--- /dev/null
+++ b/x/line_asker.fixed.js
@@ -0,0 +1,160 @@
+// @ts-check
+
+/**
+ * @import { Interface } from 'node:readline/promises'
+ */
+
+/**
+ * Ask one question and settle even when the stream can no longer answer.
+ *
+ * The same EOF defect `queuedLineAsker` documents below, for the prompts
+ * that ask exactly once and are not built with `terminal: false`. There,
+ * writing the query directly to the output stream is not equivalent to
+ * asking: on a real terminal readline redraws the line it is editing from
+ * its own cursor bookkeeping, and a query it never saw is a query it
+ * cannot redraw. So this keeps `rl.question` as the thing that writes and
+ * reads, and only replaces the promise, which is the part that is broken.
+ *
+ * Two settlements are added to it: `close`, for a stream that ends while
+ * the question is on screen (a terminal that dropped, a ctrl+D), and the
+ * stream's own `readableEnded`, for a stream that was already spent when
+ * the interface was built. Readline registers its `end` listener at
+ * construction, so the second case never emits `close` at all and a
+ * `close`-only guard still hangs on it. Both resolve `null`, which callers
+ * read as the empty line - the answer a bare enter would have given.
+ *
+ * Neither settlement may outrun an answer that did arrive, and both can:
+ * `close` fires synchronously, while `rl.question` hands its answer back
+ * through a promise, a microtask later. A stream that delivers the line
+ * and EOF in one burst (`Readable.from(['y\n'])`, any readable that
+ * pushes data and `null` together) would have `close` win that race and
+ * discard a real `y`, so the EOF settlements are deferred a turn and the
+ * delivered line settles first. Readline also routes the last line of a
+ * stream that ends without a trailing newline to `line` rather than to
+ * the pending question, which is a second answer `question` alone never
+ * sees, so that one is taken too - but only at EOF, and only if the
+ * question never got a line of its own. Readline emits `line` while no
+ * question is pending, so the same event also carries anything typed or
+ * pasted PAST the answer ("n\ny\n" in one burst), and settling on that
+ * directly would answer with the last line of the burst rather than the
+ * first: a typed `n` at an irreversible confirm would read as `y`.
+ *
+ * `rl.question`'s own promise is left permanently unsettled at EOF rather
+ * than rejected, so nothing here can leak an unhandled rejection; the
+ * handler attached to it is only there for the abort/close rejections
+ * newer readline versions may raise.
+ *
+ * @ref LLP 0190#eof-everywhere [implements]: a spent stdin settles on the prompt's stated default instead of waiting on an answer that can never come
+ *
+ * @param {Interface} rl
+ * @param {NodeJS.ReadableStream} input
+ * @param {string} prompt
+ * @returns {Promise} the answer line, `null` once the stream is spent
+ */
+export function askLineOnce(rl, input, prompt) {
+ return new Promise((resolve) => {
+ let settled = false
+ /** @param {string | null} line */
+ const done = (line) => {
+ if (settled) return
+ settled = true
+ resolve(line)
+ }
+ /**
+ * The last line readline emitted outside the question, held rather
+ * than settled on. Only read if the question itself never answered.
+ * @type {string | null}
+ */
+ let unaskedLine = null
+ // A turn behind the answer, never ahead of it: an answer delivered in
+ // the same burst as the EOF has to settle first.
+ const endOfInput = () => setImmediate(() => done(unaskedLine))
+ rl.once('close', endOfInput)
+ // Readline emits `line` only while no question is pending, so this is
+ // either the unterminated last line of the stream (an answer the
+ // question never sees) or a line that arrived past the answer. Holding
+ // it for EOF takes the first without letting the second overwrite an
+ // answer the question already has.
+ rl.on('line', (line) => {
+ unaskedLine = line
+ })
+ // Writes the query synchronously, on a spent stream too.
+ rl.question(prompt).then(done, () => done(null))
+ if (/** @type {{ readableEnded?: boolean }} */ (input).readableEnded === true) endOfInput()
+ })
+}
+
+/**
+ * Ceiling on the answer lines held for a still-unasked prompt. A prompt
+ * interface asks a small fixed number of questions and is closed straight
+ * after, so anything past a handful is unreadable backlog: a pipe that
+ * floods stdin (`yes |`) must not grow an array for as long as the prompt
+ * is on screen.
+ */
+const MAX_QUEUED_LINES = 4
+
+/**
+ * Read answer lines off a readline interface without losing one and
+ * without ever waiting on a stream that can no longer answer.
+ *
+ * `rl.question()` cannot do either job here. It registers its `line`
+ * listener only when it is called, so a second answer line arriving in
+ * the same chunk as the first ("y\n3\n" from a pipe) is emitted and
+ * dropped before a re-ask can ask: readline emits both synchronously
+ * and the next `question()` is a microtask away. And at EOF its promise
+ * is left permanently unsettled - the interface closes, `question`
+ * neither resolves nor rejects - which is a hang, or a silent
+ * "unsettled top-level await" exit. Queueing every line from
+ * construction fixes the first; resolving the pending ask as `null` on
+ * `close` fixes the second.
+ *
+ * `close` alone is not enough for the second job. Readline registers its
+ * `end` listener when the interface is built, so an interface built over
+ * a stream that has ALREADY ended never sees an `end` and never emits
+ * `close` - the second prompt asked on a spent stdin would wait forever
+ * even though the first resolved. The stream's own `readableEnded` is
+ * the answer readline can no longer give, so it seeds `closed` here.
+ *
+ * This lives beside `stdio.js` and `flush-streams.js` rather than inside
+ * any one prompt: every readline prompt in the tree has the same EOF
+ * defect, and a shared asker is what keeps the answer to it in one place.
+ *
+ * @ref LLP 0190#eof-everywhere [implements]: a spent stdin settles on the prompt's stated default instead of waiting on an answer that can never come
+ *
+ * @param {Interface} rl
+ * @param {NodeJS.ReadableStream} input
+ * @param {NodeJS.WritableStream} output
+ * @returns {(prompt: string) => Promise} writes one prompt and takes the next line, `null` once the stream is spent
+ */
+export function queuedLineAsker(rl, input, output) {
+ /** @type {string[]} */
+ const queued = []
+ /** @type {((line: string | null) => void) | null} */
+ let waiting = null
+ let closed = /** @type {{ readableEnded?: boolean }} */ (input).readableEnded === true
+ const take = () => {
+ const resolve = waiting
+ waiting = null
+ return resolve
+ }
+ rl.on('line', (line) => {
+ const resolve = take()
+ if (resolve) resolve(line)
+ else if (queued.length < MAX_QUEUED_LINES) queued.push(line)
+ })
+ rl.on('close', () => {
+ closed = true
+ const resolve = take()
+ if (resolve) resolve(null)
+ })
+ return function askLine(prompt) {
+ // Byte-identical to what `rl.question` writes: with `terminal: false`
+ // readline puts the query straight on the output stream.
+ output.write(prompt)
+ if (queued.length > 0) return Promise.resolve(/** @type {string} */ (queued.shift()))
+ if (closed) return Promise.resolve(null)
+ return new Promise((resolve) => {
+ waiting = resolve
+ })
+ }
+}
diff --git a/x/line_asker.orig.js b/x/line_asker.orig.js
new file mode 100644
index 00000000..04df8425
--- /dev/null
+++ b/x/line_asker.orig.js
@@ -0,0 +1,144 @@
+// @ts-check
+
+/**
+ * @import { Interface } from 'node:readline/promises'
+ */
+
+/**
+ * Ask one question and settle even when the stream can no longer answer.
+ *
+ * The same EOF defect `queuedLineAsker` documents below, for the prompts
+ * that ask exactly once and are not built with `terminal: false`. There,
+ * writing the query directly to the output stream is not equivalent to
+ * asking: on a real terminal readline redraws the line it is editing from
+ * its own cursor bookkeeping, and a query it never saw is a query it
+ * cannot redraw. So this keeps `rl.question` as the thing that writes and
+ * reads, and only replaces the promise, which is the part that is broken.
+ *
+ * Two settlements are added to it: `close`, for a stream that ends while
+ * the question is on screen (a terminal that dropped, a ctrl+D), and the
+ * stream's own `readableEnded`, for a stream that was already spent when
+ * the interface was built. Readline registers its `end` listener at
+ * construction, so the second case never emits `close` at all and a
+ * `close`-only guard still hangs on it. Both resolve `null`, which callers
+ * read as the empty line - the answer a bare enter would have given.
+ *
+ * Neither settlement may outrun an answer that did arrive, and both can:
+ * `close` fires synchronously, while `rl.question` hands its answer back
+ * through a promise, a microtask later. A stream that delivers the line
+ * and EOF in one burst (`Readable.from(['y\n'])`, any readable that
+ * pushes data and `null` together) would have `close` win that race and
+ * discard a real `y`, so the EOF settlements are deferred a turn and the
+ * delivered line settles first. Readline also routes the last line of a
+ * stream that ends without a trailing newline to `line` rather than to
+ * the pending question, which is a second answer `question` alone never
+ * sees, so that one is taken too.
+ *
+ * `rl.question`'s own promise is left permanently unsettled at EOF rather
+ * than rejected, so nothing here can leak an unhandled rejection; the
+ * handler attached to it is only there for the abort/close rejections
+ * newer readline versions may raise.
+ *
+ * @ref LLP 0190#eof-everywhere [implements]: a spent stdin settles on the prompt's stated default instead of waiting on an answer that can never come
+ *
+ * @param {Interface} rl
+ * @param {NodeJS.ReadableStream} input
+ * @param {string} prompt
+ * @returns {Promise} the answer line, `null` once the stream is spent
+ */
+export function askLineOnce(rl, input, prompt) {
+ return new Promise((resolve) => {
+ let settled = false
+ /** @param {string | null} line */
+ const done = (line) => {
+ if (settled) return
+ settled = true
+ resolve(line)
+ }
+ // A turn behind the answer, never ahead of it: an answer delivered in
+ // the same burst as the EOF has to settle first.
+ const endOfInput = () => setImmediate(() => done(null))
+ rl.once('close', endOfInput)
+ // The last line of a stream that ends without a trailing newline goes
+ // here rather than to the pending question.
+ rl.on('line', done)
+ // Writes the query synchronously, on a spent stream too.
+ rl.question(prompt).then(done, () => done(null))
+ if (/** @type {{ readableEnded?: boolean }} */ (input).readableEnded === true) endOfInput()
+ })
+}
+
+/**
+ * Ceiling on the answer lines held for a still-unasked prompt. A prompt
+ * interface asks a small fixed number of questions and is closed straight
+ * after, so anything past a handful is unreadable backlog: a pipe that
+ * floods stdin (`yes |`) must not grow an array for as long as the prompt
+ * is on screen.
+ */
+const MAX_QUEUED_LINES = 4
+
+/**
+ * Read answer lines off a readline interface without losing one and
+ * without ever waiting on a stream that can no longer answer.
+ *
+ * `rl.question()` cannot do either job here. It registers its `line`
+ * listener only when it is called, so a second answer line arriving in
+ * the same chunk as the first ("y\n3\n" from a pipe) is emitted and
+ * dropped before a re-ask can ask: readline emits both synchronously
+ * and the next `question()` is a microtask away. And at EOF its promise
+ * is left permanently unsettled - the interface closes, `question`
+ * neither resolves nor rejects - which is a hang, or a silent
+ * "unsettled top-level await" exit. Queueing every line from
+ * construction fixes the first; resolving the pending ask as `null` on
+ * `close` fixes the second.
+ *
+ * `close` alone is not enough for the second job. Readline registers its
+ * `end` listener when the interface is built, so an interface built over
+ * a stream that has ALREADY ended never sees an `end` and never emits
+ * `close` - the second prompt asked on a spent stdin would wait forever
+ * even though the first resolved. The stream's own `readableEnded` is
+ * the answer readline can no longer give, so it seeds `closed` here.
+ *
+ * This lives beside `stdio.js` and `flush-streams.js` rather than inside
+ * any one prompt: every readline prompt in the tree has the same EOF
+ * defect, and a shared asker is what keeps the answer to it in one place.
+ *
+ * @ref LLP 0190#eof-everywhere [implements]: a spent stdin settles on the prompt's stated default instead of waiting on an answer that can never come
+ *
+ * @param {Interface} rl
+ * @param {NodeJS.ReadableStream} input
+ * @param {NodeJS.WritableStream} output
+ * @returns {(prompt: string) => Promise} writes one prompt and takes the next line, `null` once the stream is spent
+ */
+export function queuedLineAsker(rl, input, output) {
+ /** @type {string[]} */
+ const queued = []
+ /** @type {((line: string | null) => void) | null} */
+ let waiting = null
+ let closed = /** @type {{ readableEnded?: boolean }} */ (input).readableEnded === true
+ const take = () => {
+ const resolve = waiting
+ waiting = null
+ return resolve
+ }
+ rl.on('line', (line) => {
+ const resolve = take()
+ if (resolve) resolve(line)
+ else if (queued.length < MAX_QUEUED_LINES) queued.push(line)
+ })
+ rl.on('close', () => {
+ closed = true
+ const resolve = take()
+ if (resolve) resolve(null)
+ })
+ return function askLine(prompt) {
+ // Byte-identical to what `rl.question` writes: with `terminal: false`
+ // readline puts the query straight on the output stream.
+ output.write(prompt)
+ if (queued.length > 0) return Promise.resolve(/** @type {string} */ (queued.shift()))
+ if (closed) return Promise.resolve(null)
+ return new Promise((resolve) => {
+ waiting = resolve
+ })
+ }
+}
diff --git a/x/msg.txt b/x/msg.txt
new file mode 100644
index 00000000..42092e7d
--- /dev/null
+++ b/x/msg.txt
@@ -0,0 +1,27 @@
+askLineOnce must answer with the line that answered it (review 2 of #785)
+
+The `rl.on('line', done)` added in 2843b78 takes the LAST line of a
+burst rather than the first. Readline emits `line` only while no
+question is pending, so the handler fires for everything typed or
+pasted past the answer, and it fires synchronously while the question
+hands its own answer back a microtask later. A stdin delivering
+"n\ny\n" in one chunk therefore answered `y`.
+
+That is the one direction an irreversible `[y/N]` must never drift in:
+`hyp purge`, `hyp report delete` and `hyp attach`'s enable prompt read
+a typed `n` as a confirmation. Reproduced on a real pty, where a paced
+`n` is still correct but a pasted "n\ny\n" proceeded with the delete;
+both `rl.question` and the pre-2843b78 asker returned "n" there, so it
+was a regression introduced by the fix rather than a pre-existing gap.
+
+The line is now held rather than settled on, and read only at EOF and
+only if the question never answered. That keeps 2843b78's two cases
+(an answer delivered in the same burst as the EOF, and the
+unterminated last line readline routes past the question) and drops
+the overwrite. On every input where `rl.question` answers at all,
+`askLineOnce` now returns exactly what it returns.
+
+Three regression tests, each verified to fail against 2843b78's body
+and pass against this one.
+
+Co-Authored-By: Claude
diff --git a/x/npm-test.log b/x/npm-test.log
new file mode 100644
index 00000000..c84e550d
--- /dev/null
+++ b/x/npm-test.log
@@ -0,0 +1,24648 @@
+
+> hypaware@1.22.0 test
+> node scripts/run-tests.js
+
+TAP version 13
+# Subtest: the default attachHandler is an attach-kind, reversible ActionHandler
+ok 1 - the default attachHandler is an attach-kind, reversible ActionHandler
+ ---
+ duration_ms: 0.706344
+ type: 'test'
+ ...
+# Subtest: desired() emits one action per enabled client descriptor with a registered client
+ok 2 - desired() emits one action per enabled client descriptor with a registered client
+ ---
+ duration_ms: 0.757632
+ type: 'test'
+ ...
+# Subtest: desired() emits an action per enabled descriptor across two client plugins
+ok 3 - desired() emits an action per enabled descriptor across two client plugins
+ ---
+ duration_ms: 0.22394
+ type: 'test'
+ ...
+# Subtest: desired() excludes a descriptor whose owning plugin is disabled or absent
+ok 4 - desired() excludes a descriptor whose owning plugin is disabled or absent
+ ---
+ duration_ms: 0.274437
+ type: 'test'
+ ...
+# Subtest: desired() honors an explicit attach.on_join:false opt-out (no action)
+ok 5 - desired() honors an explicit attach.on_join:false opt-out (no action)
+ ---
+ duration_ms: 0.237191
+ type: 'test'
+ ...
+# Subtest: desired() does not fail open on a non-boolean on_join (treats it as opt-out)
+ok 6 - desired() does not fail open on a non-boolean on_join (treats it as opt-out)
+ ---
+ duration_ms: 0.228517
+ type: 'test'
+ ...
+# Subtest: desired() excludes a probe-less descriptor - attach-eligibility requires reverse-capability (\#212)
+ok 7 - desired() excludes a probe-less descriptor - attach-eligibility requires reverse-capability (\#212)
+ ---
+ duration_ms: 0.231262
+ type: 'test'
+ ...
+# Subtest: desired() guards on the runtime registry actually having the client
+ok 8 - desired() guards on the runtime registry actually having the client
+ ---
+ duration_ms: 0.178061
+ type: 'test'
+ ...
+# Subtest: desired() is daemon-only: inert with no clientDescriptors and with no clients (a plain CLI boot)
+ok 9 - desired() is daemon-only: inert with no clientDescriptors and with no clients (a plain CLI boot)
+ ---
+ duration_ms: 0.450044
+ type: 'test'
+ ...
+# Subtest: perform() attaches via the registry (endpoint + json mode) and records settings_path + prev_value
+ok 10 - perform() attaches via the registry (endpoint + json mode) and records settings_path + prev_value
+ ---
+ duration_ms: 0.720715
+ type: 'test'
+ ...
+# Subtest: perform() records done with only settings_path when the attach had no prior value to back up
+ok 11 - perform() records done with only settings_path when the attach had no prior value to back up
+ ---
+ duration_ms: 0.290642
+ type: 'test'
+ ...
+# Subtest: perform() records done (endpoint only) on an idempotent re-attach (changed:false)
+ok 12 - perform() records done (endpoint only) on an idempotent re-attach (changed:false)
+ ---
+ duration_ms: 0.205262
+ type: 'test'
+ ...
+# Subtest: perform() records done (endpoint only) when the adapter emits an unparseable payload
+ok 13 - perform() records done (endpoint only) when the adapter emits an unparseable payload
+ ---
+ duration_ms: 0.217711
+ type: 'test'
+ ...
+# Subtest: perform() parses the last non-empty line when prose precedes the JSON
+ok 14 - perform() parses the last non-empty line when prose precedes the JSON
+ ---
+ duration_ms: 0.2307
+ type: 'test'
+ ...
+# Subtest: perform() returns failed when the adapter throws (file not writable)
+ok 15 - perform() returns failed when the adapter throws (file not writable)
+ ---
+ duration_ms: 0.680734
+ type: 'test'
+ ...
+# Subtest: perform() returns refused when the adapter throws a marked refusal
+ok 16 - perform() returns refused when the adapter throws a marked refusal
+ ---
+ duration_ms: 0.188567
+ type: 'test'
+ ...
+# Subtest: perform() returns failed when the registry has no such client
+ok 17 - perform() returns failed when the registry has no such client
+ ---
+ duration_ms: 0.154755
+ type: 'test'
+ ...
+# Subtest: perform() returns failed when no gateway endpoint is set
+ok 18 - perform() returns failed when no gateway endpoint is set
+ ---
+ duration_ms: 0.152412
+ type: 'test'
+ ...
+# Subtest: perform() guards against a missing client name
+ok 19 - perform() guards against a missing client name
+ ---
+ duration_ms: 0.142026
+ type: 'test'
+ ...
+# Subtest: perform() materializes the client assets and records them as the undo record
+ok 20 - perform() materializes the client assets and records them as the undo record
+ ---
+ duration_ms: 11.296129
+ type: 'test'
+ ...
+# Subtest: perform() stays done when an asset copy fails - the attach itself applied
+ok 21 - perform() stays done when an asset copy fails - the attach itself applied
+ ---
+ duration_ms: 2.077297
+ type: 'test'
+ ...
+# Subtest: perform() installs no assets when the daemon threaded no registries
+ok 22 - perform() installs no assets when the daemon threaded no registries
+ ---
+ duration_ms: 1.242819
+ type: 'test'
+ ...
+# Subtest: reverse() removes exactly the assets its own marker recorded
+ok 23 - reverse() removes exactly the assets its own marker recorded
+ ---
+ duration_ms: 4.023305
+ type: 'test'
+ ...
+# Subtest: reverse() refuses to remove a marker path outside the client asset dirs
+ok 24 - reverse() refuses to remove a marker path outside the client asset dirs
+ ---
+ duration_ms: 1.841188
+ type: 'test'
+ ...
+# Subtest: reverse() of a marker with no installed_assets touches no files
+ok 25 - reverse() of a marker with no installed_assets touches no files
+ ---
+ duration_ms: 1.209208
+ type: 'test'
+ ...
+# Subtest: reverse() invokes the disk-driven undo once and never consults ctx.clients
+ok 26 - reverse() invokes the disk-driven undo once and never consults ctx.clients
+ ---
+ duration_ms: 0.338324
+ type: 'test'
+ ...
+# Subtest: reverse() reports a replayed malformed-block backup by path, and never its contents
+ok 27 - reverse() reports a replayed malformed-block backup by path, and never its contents
+ ---
+ duration_ms: 0.291664
+ type: 'test'
+ ...
+# Subtest: reverse() says nothing about a restore that did not happen
+ok 28 - reverse() says nothing about a restore that did not happen
+ ---
+ duration_ms: 0.22997
+ type: 'test'
+ ...
+# Subtest: reverse() replays the real core undo from disk with no adapter loaded (fs round-trip)
+ok 29 - reverse() replays the real core undo from disk with no adapter loaded (fs round-trip)
+ ---
+ duration_ms: 7.245873
+ type: 'test'
+ ...
+# Subtest: reverse() of a probe-less descriptor fails - never silently drops the marker, orphaning settings (\#212)
+ok 30 - reverse() of a probe-less descriptor fails - never silently drops the marker, orphaning settings (\#212)
+ ---
+ duration_ms: 0.340878
+ type: 'test'
+ ...
+# Subtest: reverse() returns failed (retry next pass) when the descriptor is gone from the catalog
+ok 31 - reverse() returns failed (retry next pass) when the descriptor is gone from the catalog
+ ---
+ duration_ms: 0.231011
+ type: 'test'
+ ...
+# Subtest: reverse() returns failed when the disk undo throws (concurrent edit)
+ok 32 - reverse() returns failed when the disk undo throws (concurrent edit)
+ ---
+ duration_ms: 0.255799
+ type: 'test'
+ ...
+# Subtest: the reverse-gap contract: a dropped client falls out of desired() and reverse() then undoes it
+ok 33 - the reverse-gap contract: a dropped client falls out of desired() and reverse() then undoes it
+ ---
+ duration_ms: 0.576717
+ type: 'test'
+ ...
+# Subtest: the default backfillHandler is a backfill-kind ActionHandler
+ok 34 - the default backfillHandler is a backfill-kind ActionHandler
+ ---
+ duration_ms: 0.890914
+ type: 'test'
+ ...
+# Subtest: desired() emits one action per enabled provider (default on_join, plugin->provider mapping)
+ok 35 - desired() emits one action per enabled provider (default on_join, plugin->provider mapping)
+ ---
+ duration_ms: 3.106291
+ type: 'test'
+ ...
+# Subtest: desired() honors an explicit on_join:false opt-out (no action)
+ok 36 - desired() honors an explicit on_join:false opt-out (no action)
+ ---
+ duration_ms: 0.320017
+ type: 'test'
+ ...
+# Subtest: desired() does not fail open on a non-boolean on_join (treats it as opt-out)
+ok 37 - desired() does not fail open on a non-boolean on_join (treats it as opt-out)
+ ---
+ duration_ms: 0.426699
+ type: 'test'
+ ...
+# Subtest: desired() carries window_days through to params when present
+ok 38 - desired() carries window_days through to params when present
+ ---
+ duration_ms: 0.40754
+ type: 'test'
+ ...
+# Subtest: desired() excludes a provider whose owning plugin is not enabled
+ok 39 - desired() excludes a provider whose owning plugin is not enabled
+ ---
+ duration_ms: 0.457666
+ type: 'test'
+ ...
+# Subtest: perform() resolves window_days to a --since flag (assert spawned argv)
+ok 40 - perform() resolves window_days to a --since flag (assert spawned argv)
+ ---
+ duration_ms: 0.760937
+ type: 'test'
+ ...
+# Subtest: perform() spawns with the daemon-resolved env (HYP_HOME), not process.env
+ok 41 - perform() spawns with the daemon-resolved env (HYP_HOME), not process.env
+ ---
+ duration_ms: 0.351575
+ type: 'test'
+ ...
+# Subtest: perform() omits --since when window_days is absent (retention fallback)
+ok 42 - perform() omits --since when window_days is absent (retention fallback)
+ ---
+ duration_ms: 0.510246
+ type: 'test'
+ ...
+# Subtest: perform() sums rows_written across providers in the --json payload
+ok 43 - perform() sums rows_written across providers in the --json payload
+ ---
+ duration_ms: 0.57912
+ type: 'test'
+ ...
+# Subtest: perform() records done (without rows) on exit 0 with an unparseable payload
+ok 44 - perform() records done (without rows) on exit 0 with an unparseable payload
+ ---
+ duration_ms: 0.337874
+ type: 'test'
+ ...
+# Subtest: perform() returns failed on a non-zero exit
+ok 45 - perform() returns failed on a non-zero exit
+ ---
+ duration_ms: 0.35558
+ type: 'test'
+ ...
+# Subtest: perform() returns failed on a spawn error
+ok 46 - perform() returns failed on a spawn error
+ ---
+ duration_ms: 0.29578
+ type: 'test'
+ ...
+# Subtest: perform() guards against a missing provider name
+ok 47 - perform() guards against a missing provider name
+ ---
+ duration_ms: 0.247406
+ type: 'test'
+ ...
+# Subtest: driven through the reconciler: a failed perform writes a failed marker, then a retry flips to done
+ok 48 - driven through the reconciler: a failed perform writes a failed marker, then a retry flips to done
+ ---
+ duration_ms: 22.257589
+ type: 'test'
+ ...
+# Subtest: reconcile runs a desired action once and short-circuits on the done marker
+ok 49 - reconcile runs a desired action once and short-circuits on the done marker
+ ---
+ duration_ms: 5.773525
+ type: 'test'
+ ...
+# Subtest: a missed pass (no marker yet) runs on the next reconcile call
+ok 50 - a missed pass (no marker yet) runs on the next reconcile call
+ ---
+ duration_ms: 1.421812
+ type: 'test'
+ ...
+# Subtest: atomic marker read/write round-trips through readClientActionStatus and readStatus
+ok 51 - atomic marker read/write round-trips through readClientActionStatus and readStatus
+ ---
+ duration_ms: 1.467049
+ type: 'test'
+ ...
+# Subtest: a failed perform writes a failed marker (not done) and retries with bumped attempts
+ok 52 - a failed perform writes a failed marker (not done) and retries with bumped attempts
+ ---
+ duration_ms: 3.221456
+ type: 'test'
+ ...
+# Subtest: a thrown perform is normalized to a failed marker
+ok 53 - a thrown perform is normalized to a failed marker
+ ---
+ duration_ms: 1.629777
+ type: 'test'
+ ...
+# Subtest: a corrupt marker file does not wedge reconcile (treated as empty, pass still runs)
+ok 54 - a corrupt marker file does not wedge reconcile (treated as empty, pass still runs)
+ ---
+ duration_ms: 1.463794
+ type: 'test'
+ ...
+# Subtest: a handler whose desired() throws does not wedge other handlers
+ok 55 - a handler whose desired() throws does not wedge other handlers
+ ---
+ duration_ms: 1.364413
+ type: 'test'
+ ...
+# Subtest: a reversible handler undoes a previously-applied key the config no longer names
+ok 56 - a reversible handler undoes a previously-applied key the config no longer names
+ ---
+ duration_ms: 1.437605
+ type: 'test'
+ ...
+# Subtest: a failed reverse keeps the marker in the store (probe-less attach is never orphaned) (\#212)
+ok 57 - a failed reverse keeps the marker in the store (probe-less attach is never orphaned) (\#212)
+ ---
+ duration_ms: 1.646182
+ type: 'test'
+ ...
+# Subtest: a failed marker that recorded an effect is reversed, not dropped (LLP 0138 marker-undo)
+ok 58 - a failed marker that recorded an effect is reversed, not dropped (LLP 0138 marker-undo)
+ ---
+ duration_ms: 1.912637
+ type: 'test'
+ ...
+# Subtest: a run-once handler never reverses a no-longer-desired done marker
+ok 59 - a run-once handler never reverses a no-longer-desired done marker
+ ---
+ duration_ms: 1.593642
+ type: 'test'
+ ...
+# Subtest: clearClientActionMarker is a no-op (returns false, writes nothing) when the file, bucket, or key is missing
+ok 60 - clearClientActionMarker is a no-op (returns false, writes nothing) when the file, bucket, or key is missing
+ ---
+ duration_ms: 1.517347
+ type: 'test'
+ ...
+# Subtest: clearClientActionMarker drops an emptied bucket but preserves sibling buckets and keys
+ok 61 - clearClientActionMarker drops an emptied bucket but preserves sibling buckets and keys
+ ---
+ duration_ms: 1.468863
+ type: 'test'
+ ...
+# Subtest: rearmRefusedActionMarker only touches a refused marker, and drops it only when it records no assets
+ok 62 - rearmRefusedActionMarker only touches a refused marker, and drops it only when it records no assets
+ ---
+ duration_ms: 2.60526
+ type: 'test'
+ ...
+# Subtest: a refused marker short-circuits unconditionally, unlike a done marker the handler reports stale (LLP 0186)
+ok 63 - a refused marker short-circuits unconditionally, unlike a done marker the handler reports stale (LLP 0186)
+ ---
+ duration_ms: 3.80299
+ type: 'test'
+ ...
+# Subtest: a refused outcome writes a terminal marker (reason + at, no attempts) that carries installed_assets forward (LLP 0186)
+ok 64 - a refused outcome writes a terminal marker (reason + at, no attempts) that carries installed_assets forward (LLP 0186)
+ ---
+ duration_ms: 1.640924
+ type: 'test'
+ ...
+# Subtest: the reverse gap drops an assetless refused marker and reverses one that recorded an effect (LLP 0186)
+ok 65 - the reverse gap drops an assetless refused marker and reverses one that recorded an effect (LLP 0186)
+ ---
+ duration_ms: 1.75002
+ type: 'test'
+ ...
+# Subtest: a thrown, marked Error round-trips through isActionRefused as true
+ok 66 - a thrown, marked Error round-trips through isActionRefused as true
+ ---
+ duration_ms: 0.527401
+ type: 'test'
+ ...
+# Subtest: a plain Error reads as not refused
+ok 67 - a plain Error reads as not refused
+ ---
+ duration_ms: 0.090307
+ type: 'test'
+ ...
+# Subtest: a non-Error throw reads as not refused
+ok 68 - a non-Error throw reads as not refused
+ ---
+ duration_ms: 0.082515
+ type: 'test'
+ ...
+# Subtest: ai-gateway registers cache partitioning for source columns and iceberg fields
+ok 69 - ai-gateway registers cache partitioning for source columns and iceberg fields
+ ---
+ duration_ms: 1.30236
+ type: 'test'
+ ...
+# Subtest: ai-gateway registers sourceSignal proxy so rows forward under a known ingest signal
+ok 70 - ai-gateway registers sourceSignal proxy so rows forward under a known ingest signal
+ ---
+ duration_ms: 0.10569
+ type: 'test'
+ ...
+# Subtest: registry rejects cachePartitioning with source column absent from schema
+ok 71 - registry rejects cachePartitioning with source column absent from schema
+ ---
+ duration_ms: 0.321869
+ type: 'test'
+ ...
+# Subtest: registry rejects cachePartitioning with required Iceberg field absent from schema
+ok 72 - registry rejects cachePartitioning with required Iceberg field absent from schema
+ ---
+ duration_ms: 0.120172
+ type: 'test'
+ ...
+# Subtest: registry accepts cachePartitioning with optional Iceberg field absent from schema
+ok 73 - registry accepts cachePartitioning with optional Iceberg field absent from schema
+ ---
+ duration_ms: 0.095224
+ type: 'test'
+ ...
+# Subtest: ai-gateway createDataSource honors scope when re-discovering fresh partitions
+ok 74 - ai-gateway createDataSource honors scope when re-discovering fresh partitions
+ ---
+ duration_ms: 33.144436
+ type: 'test'
+ ...
+# Subtest: ai-gateway discoverParts unions legacy and source-table partitions without duplicates
+ok 75 - ai-gateway discoverParts unions legacy and source-table partitions without duplicates
+ ---
+ duration_ms: 18.02771
+ type: 'test'
+ ...
+# Subtest: ai-gateway createDataSource unions legacy and source-table data
+ok 76 - ai-gateway createDataSource unions legacy and source-table data
+ ---
+ duration_ms: 16.202336
+ type: 'test'
+ ...
+# Subtest: ai-gateway createDataSource pads declared schema columns absent from an old partition
+ok 77 - ai-gateway createDataSource pads declared schema columns absent from an old partition
+ ---
+ duration_ms: 20.535032
+ type: 'test'
+ ...
+# Subtest: ai-gateway createDataSource streams scanColumn with nulls for a physically absent column
+ok 78 - ai-gateway createDataSource streams scanColumn with nulls for a physically absent column
+ ---
+ duration_ms: 9.835749
+ type: 'test'
+ ...
+# Subtest: ai-gateway scanColumn pushes a convertible WHERE down to the parquet read and reports it applied
+ok 79 - ai-gateway scanColumn pushes a convertible WHERE down to the parquet read and reports it applied
+ ---
+ duration_ms: 22.659661
+ type: 'test'
+ ...
+# Subtest: hyp attach all: a catalog-known client missing from the live registry gets a note, the live ones attach
+ok 80 - hyp attach all: a catalog-known client missing from the live registry gets a note, the live ones attach
+ ---
+ duration_ms: 38.193761
+ type: 'test'
+ ...
+# Subtest: hyp attach all: the note does not change the exit code, only real attach failures among the live set do
+ok 81 - hyp attach all: the note does not change the exit code, only real attach failures among the live set do
+ ---
+ duration_ms: 23.818962
+ type: 'test'
+ ...
+# Subtest: accept-then-yes: the provider runs with the expected shape and its result is reported
+ok 82 - accept-then-yes: the provider runs with the expected shape and its result is reported
+ ---
+ duration_ms: 134.170586
+ type: 'test'
+ ...
+# Subtest: accept-then-no: declining the backfill question runs no import
+ok 83 - accept-then-no: declining the backfill question runs no import
+ ---
+ duration_ms: 24.148142
+ type: 'test'
+ ...
+# Subtest: client with no registered backfill provider: the question is not asked at all
+ok 84 - client with no registered backfill provider: the question is not asked at all
+ ---
+ duration_ms: 29.355687
+ type: 'test'
+ ...
+# Subtest: OpenClaw never reaches the backfill question, even with a registered provider
+ok 85 - OpenClaw never reaches the backfill question, even with a registered provider
+ ---
+ duration_ms: 19.229156
+ type: 'test'
+ ...
+# Subtest: decline: exits 1 with zero side effects, no config or backup file written
+ok 86 - decline: exits 1 with zero side effects, no config or backup file written
+ ---
+ duration_ms: 95.43364
+ type: 'test'
+ ...
+# Subtest: decline via a bare Enter (empty line) is treated as no
+ok 87 - decline via a bare Enter (empty line) is treated as no
+ ---
+ duration_ms: 10.443593
+ type: 'test'
+ ...
+# Subtest: accept: enables the adapter and dispatches attach() in the same invocation
+ok 88 - accept: enables the adapter and dispatches attach() in the same invocation
+ ---
+ duration_ms: 34.4827
+ type: 'test'
+ ...
+# Subtest: OpenClaw prompt names the periodic sweep import; Claude/Codex does not
+ok 89 - OpenClaw prompt names the periodic sweep import; Claude/Codex does not
+ ---
+ duration_ms: 23.517985
+ type: 'test'
+ ...
+# Subtest: bootstrap floor: no local config file at all skips the prompt entirely
+ok 90 - bootstrap floor: no local config file at all skips the prompt entirely
+ ---
+ duration_ms: 4.455232
+ type: 'test'
+ ...
+# Subtest: an entry already present but disabled skips the prompt: the additive write cannot enable it
+ok 91 - an entry already present but disabled skips the prompt: the additive write cannot enable it
+ ---
+ duration_ms: 9.644369
+ type: 'test'
+ ...
+# Subtest: a disabled @hypaware/ai-gateway entry also skips the prompt, not just the adapter entry
+ok 92 - a disabled @hypaware/ai-gateway entry also skips the prompt, not just the adapter entry
+ ---
+ duration_ms: 16.689927
+ type: 'test'
+ ...
+# Subtest: every requested plugin already present and enabled skips the prompt: the write would append nothing
+ok 93 - every requested plugin already present and enabled skips the prompt: the write would append nothing
+ ---
+ duration_ms: 20.275507
+ type: 'test'
+ ...
+# Subtest: an adapter absent but its gateway present-and-enabled still prompts: the write has something to append
+ok 94 - an adapter absent but its gateway present-and-enabled still prompts: the write has something to append
+ ---
+ duration_ms: 23.589674
+ type: 'test'
+ ...
+# Subtest: non-interactive (no stdin/TTY) keeps the unchanged not_enabled refusal
+ok 95 - non-interactive (no stdin/TTY) keeps the unchanged not_enabled refusal
+ ---
+ duration_ms: 9.40858
+ type: 'test'
+ ...
+# Subtest: --dry-run never prompts: no config write, no backup, no attach
+ok 96 - --dry-run never prompts: no config write, no backup, no attach
+ ---
+ duration_ms: 8.502062
+ type: 'test'
+ ...
+# Subtest: --json never prompts even with a TTY
+ok 97 - --json never prompts even with a TTY
+ ---
+ duration_ms: 5.307697
+ type: 'test'
+ ...
+# Subtest: accept, write succeeds, restart fails: names the restart step, the backup path, and the resume instruction; does not retry the enable
+ok 98 - accept, write succeeds, restart fails: names the restart step, the backup path, and the resume instruction; does not retry the enable
+ ---
+ duration_ms: 119.412696
+ type: 'test'
+ ...
+# Subtest: second invocation after a partial failure: config already carries the entry, so attach never re-prompts
+ok 99 - second invocation after a partial failure: config already carries the entry, so attach never re-prompts
+ ---
+ duration_ms: 5.951456
+ type: 'test'
+ ...
+# Subtest: capability gate: a catalog-known client reports not_enabled, not cap_missing
+ok 100 - capability gate: a catalog-known client reports not_enabled, not cap_missing
+ ---
+ duration_ms: 98.006711
+ type: 'test'
+ ...
+# Subtest: capability gate: bare `hyp attach` defaults to claude and takes the same path
+ok 101 - capability gate: bare `hyp attach` defaults to claude and takes the same path
+ ---
+ duration_ms: 5.858174
+ type: 'test'
+ ...
+# Subtest: capability gate: --json carries error_kind adapter_not_enabled in the same payload shape
+ok 102 - capability gate: --json carries error_kind adapter_not_enabled in the same payload shape
+ ---
+ duration_ms: 6.03343
+ type: 'test'
+ ...
+# Subtest: capability gate: a name no plugin contributes keeps the cap_missing wording
+ok 103 - capability gate: a name no plugin contributes keeps the cap_missing wording
+ ---
+ duration_ms: 7.34293
+ type: 'test'
+ ...
+# Subtest: registry miss: another gateway plugin is live but the requested adapter is not enabled
+ok 104 - registry miss: another gateway plugin is live but the requested adapter is not enabled
+ ---
+ duration_ms: 10.387118
+ type: 'test'
+ ...
+# Subtest: registry miss: --json reports adapter_not_enabled
+ok 105 - registry miss: --json reports adapter_not_enabled
+ ---
+ duration_ms: 6.130428
+ type: 'test'
+ ...
+# Subtest: registry miss: a genuinely unrecognized name still gets the plain unknown client text
+ok 106 - registry miss: a genuinely unrecognized name still gets the plain unknown client text
+ ---
+ duration_ms: 12.281066
+ type: 'test'
+ ...
+# Subtest: fleet-disabled adapter refuses with the central-managed explanation, not the local remedy
+ok 107 - fleet-disabled adapter refuses with the central-managed explanation, not the local remedy
+ ---
+ duration_ms: 9.712221
+ type: 'test'
+ ...
+# Subtest: fleet-disabled adapter reports adapter_disabled_central through the capability gate too
+ok 108 - fleet-disabled adapter reports adapter_disabled_central through the capability gate too
+ ---
+ duration_ms: 6.0064
+ type: 'test'
+ ...
+# Subtest: a locally disabled adapter is fixable, so it renders the not_enabled remedy
+ok 109 - a locally disabled adapter is fixable, so it renders the not_enabled remedy
+ ---
+ duration_ms: 4.242208
+ type: 'test'
+ ...
+# Subtest: a registered client still attaches unchanged
+ok 110 - a registered client still attaches unchanged
+ ---
+ duration_ms: 6.373166
+ type: 'test'
+ ...
+# Subtest: a rebind (new endpoint) re-attaches instead of short-circuiting on the done marker (\#277 Gap 2)
+ok 111 - a rebind (new endpoint) re-attaches instead of short-circuiting on the done marker (\#277 Gap 2)
+ ---
+ duration_ms: 5.876181
+ type: 'test'
+ ...
+# Subtest: a legacy done attach marker with no recorded endpoint re-attaches once (backward compatible) (\#277 Gap 2)
+ok 112 - a legacy done attach marker with no recorded endpoint re-attaches once (backward compatible) (\#277 Gap 2)
+ ---
+ duration_ms: 2.346967
+ type: 'test'
+ ...
+# Subtest: a changed asset set re-attaches at an unchanged endpoint (LLP 0107 currency)
+ok 113 - a changed asset set re-attaches at an unchanged endpoint (LLP 0107 currency)
+ ---
+ duration_ms: 17.475721
+ type: 'test'
+ ...
+# Subtest: an unresolvable endpoint this pass leaves the existing done attach untouched (\#277 Gap 2)
+ok 114 - an unresolvable endpoint this pass leaves the existing done attach untouched (\#277 Gap 2)
+ ---
+ duration_ms: 2.024397
+ type: 'test'
+ ...
+# Subtest: attach without endpoint: already-attached client is a no-op success
+ok 115 - attach without endpoint: already-attached client is a no-op success
+ ---
+ duration_ms: 27.316288
+ type: 'test'
+ ...
+# Subtest: attach without endpoint: already-attached --json reports ok/unchanged
+ok 116 - attach without endpoint: already-attached --json reports ok/unchanged
+ ---
+ duration_ms: 7.795719
+ type: 'test'
+ ...
+# Subtest: attach without endpoint: no daemon installed names hyp daemon install/start
+ok 117 - attach without endpoint: no daemon installed names hyp daemon install/start
+ ---
+ duration_ms: 9.990875
+ type: 'test'
+ ...
+# Subtest: attach without endpoint: installed-but-unreachable daemon keeps the existing hyp start message
+ok 118 - attach without endpoint: installed-but-unreachable daemon keeps the existing hyp start message
+ ---
+ duration_ms: 15.271151
+ type: 'test'
+ ...
+# Subtest: attach without endpoint: not-attached --json reports failed/no_endpoint
+ok 119 - attach without endpoint: not-attached --json reports failed/no_endpoint
+ ---
+ duration_ms: 8.700414
+ type: 'test'
+ ...
+# Subtest: attach with configured listen still uses the config fallback
+ok 120 - attach with configured listen still uses the config fallback
+ ---
+ duration_ms: 9.569785
+ type: 'test'
+ ...
+# Subtest: attach discovers the daemon live port from status.json and attaches there (\#277 Gap 1)
+ok 121 - attach discovers the daemon live port from status.json and attaches there (\#277 Gap 1)
+ ---
+ duration_ms: 25.175435
+ type: 'test'
+ ...
+# Subtest: attach re-attaches when the recorded marker port is stale vs the live port (\#277 Gap 1/2)
+ok 122 - attach re-attaches when the recorded marker port is stale vs the live port (\#277 Gap 1/2)
+ ---
+ duration_ms: 7.231601
+ type: 'test'
+ ...
+# Subtest: attach reports already-attached (no-op) when the recorded port matches the live port (\#277 Gap 2)
+ok 123 - attach reports already-attached (no-op) when the recorded port matches the live port (\#277 Gap 2)
+ ---
+ duration_ms: 11.00049
+ type: 'test'
+ ...
+# Subtest: attach installs client assets even when the settings are already attached (LLP 0107 every-attach)
+ok 124 - attach installs client assets even when the settings are already attached (LLP 0107 every-attach)
+ ---
+ duration_ms: 12.354677
+ type: 'test'
+ ...
+# Subtest: attach does NOT trust a dead daemon status.json (liveness gate) (\#277 Gap 1)
+ok 125 - attach does NOT trust a dead daemon status.json (liveness gate) (\#277 Gap 1)
+ ---
+ duration_ms: 12.761566
+ type: 'test'
+ ...
+# Subtest: readAttachPolicy: no entry → default-on (onJoin undefined)
+ok 126 - readAttachPolicy: no entry → default-on (onJoin undefined)
+ ---
+ duration_ms: 1.034022
+ type: 'test'
+ ...
+# Subtest: readAttachPolicy: entry with no config → default-on
+ok 127 - readAttachPolicy: entry with no config → default-on
+ ---
+ duration_ms: 0.804183
+ type: 'test'
+ ...
+# Subtest: readAttachPolicy: absent attach block → default-on (onJoin undefined)
+ok 128 - readAttachPolicy: absent attach block → default-on (onJoin undefined)
+ ---
+ duration_ms: 0.106322
+ type: 'test'
+ ...
+# Subtest: readAttachPolicy: present block with no on_join → default-on
+ok 129 - readAttachPolicy: present block with no on_join → default-on
+ ---
+ duration_ms: 0.089717
+ type: 'test'
+ ...
+# Subtest: readAttachPolicy: on_join: true → opt-in
+ok 130 - readAttachPolicy: on_join: true → opt-in
+ ---
+ duration_ms: 0.080533
+ type: 'test'
+ ...
+# Subtest: readAttachPolicy: on_join: false → opt-out
+ok 131 - readAttachPolicy: on_join: false → opt-out
+ ---
+ duration_ms: 0.110828
+ type: 'test'
+ ...
+# Subtest: readAttachPolicy: present-but-malformed on_join → fail-safe opt-out
+ok 132 - readAttachPolicy: present-but-malformed on_join → fail-safe opt-out
+ ---
+ duration_ms: 0.168246
+ type: 'test'
+ ...
+# Subtest: readAttachPolicy: attach block that is an array → default-on (ignored)
+ok 133 - readAttachPolicy: attach block that is an array → default-on (ignored)
+ ---
+ duration_ms: 0.081765
+ type: 'test'
+ ...
+# Subtest: readAttachPolicy: attach block that is a scalar → default-on (ignored)
+ok 134 - readAttachPolicy: attach block that is a scalar → default-on (ignored)
+ ---
+ duration_ms: 0.201356
+ type: 'test'
+ ...
+# Subtest: readAttachPolicy: off switch reads as onJoin !== false
+ok 135 - readAttachPolicy: off switch reads as onJoin !== false
+ ---
+ duration_ms: 0.280556
+ type: 'test'
+ ...
+# Subtest: a successful manual hyp attach clears a refused marker, and the next reconcile re-performs (LLP 0186 re-arm)
+ok 136 - a successful manual hyp attach clears a refused marker, and the next reconcile re-performs (LLP 0186 re-arm)
+ ---
+ duration_ms: 46.826852
+ type: 'test'
+ ...
+# Subtest: a failed manual hyp attach leaves the refused marker in place (never cleared on failure)
+ok 137 - a failed manual hyp attach leaves the refused marker in place (never cleared on failure)
+ ---
+ duration_ms: 3.528723
+ type: 'test'
+ ...
+# Subtest: a successful manual hyp attach leaves a done marker (and its installed_assets) alone
+ok 138 - a successful manual hyp attach leaves a done marker (and its installed_assets) alone
+ ---
+ duration_ms: 9.483765
+ type: 'test'
+ ...
+# Subtest: a successful manual hyp attach re-arms an asset-bearing refused marker without dropping its undo record
+ok 139 - a successful manual hyp attach re-arms an asset-bearing refused marker without dropping its undo record
+ ---
+ duration_ms: 13.197318
+ type: 'test'
+ ...
+# Subtest: hyp attach --dry-run never clears a refused marker
+ok 140 - hyp attach --dry-run never clears a refused marker
+ ---
+ duration_ms: 9.575124
+ type: 'test'
+ ...
+# Subtest: parseRunArgv collects providers and flags in space and = forms
+ok 141 - parseRunArgv collects providers and flags in space and = forms
+ ---
+ duration_ms: 2.542184
+ type: 'test'
+ ...
+# Subtest: parseRunArgv defaults flags off and keeps date window unset
+ok 142 - parseRunArgv defaults flags off and keeps date window unset
+ ---
+ duration_ms: 0.165872
+ type: 'test'
+ ...
+# Subtest: parseRunArgv reports usage errors for bad input
+ok 143 - parseRunArgv reports usage errors for bad input
+ ---
+ duration_ms: 0.309841
+ type: 'test'
+ ...
+# Subtest: parseRunArgv accepts an equal since/until boundary
+ok 144 - parseRunArgv accepts an equal since/until boundary
+ ---
+ duration_ms: 0.101184
+ type: 'test'
+ ...
+# Subtest: parsePlanArgv accepts retention-days but rejects since/until
+ok 145 - parsePlanArgv accepts retention-days but rejects since/until
+ ---
+ duration_ms: 0.244201
+ type: 'test'
+ ...
+# Subtest: selectProviders intersects explicit names and reports unknowns
+ok 146 - selectProviders intersects explicit names and reports unknowns
+ ---
+ duration_ms: 0.235217
+ type: 'test'
+ ...
+# Subtest: selectProviders defaults to providers whose plugin is enabled in config
+ok 147 - selectProviders defaults to providers whose plugin is enabled in config
+ ---
+ duration_ms: 0.161956
+ type: 'test'
+ ...
+# Subtest: resolveRetentionDays prefers the flag, then config, then the default
+ok 148 - resolveRetentionDays prefers the flag, then config, then the default
+ ---
+ duration_ms: 0.169067
+ type: 'test'
+ ...
+# Subtest: runBackfill materializes rows, appends to the dataset path, and flushes
+ok 149 - runBackfill materializes rows, appends to the dataset path, and flushes
+ ---
+ duration_ms: 24.187422
+ type: 'test'
+ ...
+# Subtest: runBackfill --dry-run scans without appending or flushing any rows
+ok 150 - runBackfill --dry-run scans without appending or flushing any rows
+ ---
+ duration_ms: 8.04632
+ type: 'test'
+ ...
+# Subtest: runBackfill --json reports per-provider counts
+ok 151 - runBackfill --json reports per-provider counts
+ ---
+ duration_ms: 10.912025
+ type: 'test'
+ ...
+# Subtest: runBackfill --json marks provider failed for materializer_missing
+ok 152 - runBackfill --json marks provider failed for materializer_missing
+ ---
+ duration_ms: 10.450093
+ type: 'test'
+ ...
+# Subtest: runBackfill --json marks provider failed for dataset_mismatch
+ok 153 - runBackfill --json marks provider failed for dataset_mismatch
+ ---
+ duration_ms: 5.292485
+ type: 'test'
+ ...
+# Subtest: runBackfill --json marks provider failed for dataset_not_registered
+ok 154 - runBackfill --json marks provider failed for dataset_not_registered
+ ---
+ duration_ms: 5.840137
+ type: 'test'
+ ...
+# Subtest: runBackfill fails with exit 1 for an unknown explicit provider
+ok 155 - runBackfill fails with exit 1 for an unknown explicit provider
+ ---
+ duration_ms: 0.312375
+ type: 'test'
+ ...
+# Subtest: runBackfill fails with exit 2 for invalid --since
+ok 156 - runBackfill fails with exit 2 for invalid --since
+ ---
+ duration_ms: 0.176929
+ type: 'test'
+ ...
+# Subtest: runBackfill fails with exit 2 for invalid --until
+ok 157 - runBackfill fails with exit 2 for invalid --until
+ ---
+ duration_ms: 0.223349
+ type: 'test'
+ ...
+# Subtest: runBackfill fails with exit 2 for --since after --until
+ok 158 - runBackfill fails with exit 2 for --since after --until
+ ---
+ duration_ms: 0.153934
+ type: 'test'
+ ...
+# Subtest: runBackfillProvider runs one provider and returns compact counts + appends
+ok 159 - runBackfillProvider runs one provider and returns compact counts + appends
+ ---
+ duration_ms: 7.163308
+ type: 'test'
+ ...
+# Subtest: runBackfillProvider dry-run scans without appending or flushing
+ok 160 - runBackfillProvider dry-run scans without appending or flushing
+ ---
+ duration_ms: 8.981481
+ type: 'test'
+ ...
+# Subtest: runBackfillProvider reports a failed result for an unknown provider
+ok 161 - runBackfillProvider reports a failed result for an unknown provider
+ ---
+ duration_ms: 0.22976
+ type: 'test'
+ ...
+# Subtest: the entrypoint gate counts a config-listed plugin as configured even when this process never activated it
+ok 162 - the entrypoint gate counts a config-listed plugin as configured even when this process never activated it
+ ---
+ duration_ms: 11.357232
+ type: 'test'
+ ...
+# Subtest: the entrypoint gate stays closed for a plugin in neither the config nor the activation set
+ok 163 - the entrypoint gate stays closed for a plugin in neither the config nor the activation set
+ ---
+ duration_ms: 4.655986
+ type: 'test'
+ ...
+# Subtest: a plugin listed with enabled:false is not configured
+ok 164 - a plugin listed with enabled:false is not configured
+ ---
+ duration_ms: 4.402311
+ type: 'test'
+ ...
+# Subtest: hyp backfill plan hands providers the same entrypoint owner map the run gets
+ok 165 - hyp backfill plan hands providers the same entrypoint owner map the run gets
+ ---
+ duration_ms: 4.390553
+ type: 'test'
+ ...
+# Subtest: resolveEntrypointOwners maps each declared entrypoint to its owning client
+ok 166 - resolveEntrypointOwners maps each declared entrypoint to its owning client
+ ---
+ duration_ms: 1.001653
+ type: 'test'
+ ...
+# Subtest: resolveEntrypointOwners records whether each owning plugin is configured
+ok 167 - resolveEntrypointOwners records whether each owning plugin is configured
+ ---
+ duration_ms: 0.150158
+ type: 'test'
+ ...
+# Subtest: resolveEntrypointOwners ignores clients declaring no entrypoints
+ok 168 - resolveEntrypointOwners ignores clients declaring no entrypoints
+ ---
+ duration_ms: 0.074052
+ type: 'test'
+ ...
+# Subtest: resolveEntrypointOwners is first-declaration-wins on a contested entrypoint
+ok 169 - resolveEntrypointOwners is first-declaration-wins on a contested entrypoint
+ ---
+ duration_ms: 0.127905
+ type: 'test'
+ ...
+# Subtest: classifyTranscriptEntrypoint skips an entrypoint owned by an unconfigured client
+ok 170 - classifyTranscriptEntrypoint skips an entrypoint owned by an unconfigured client
+ ---
+ duration_ms: 0.107133
+ type: 'test'
+ ...
+# Subtest: classifyTranscriptEntrypoint attributes an owned+configured entrypoint to its owner, not the scanner
+ok 171 - classifyTranscriptEntrypoint attributes an owned+configured entrypoint to its owner, not the scanner
+ ---
+ duration_ms: 0.120133
+ type: 'test'
+ ...
+# Subtest: classifyTranscriptEntrypoint fails open on an unclaimed entrypoint
+ok 172 - classifyTranscriptEntrypoint fails open on an unclaimed entrypoint
+ ---
+ duration_ms: 0.081955
+ type: 'test'
+ ...
+# Subtest: classifyTranscriptEntrypoint fails open on an absent entrypoint
+ok 173 - classifyTranscriptEntrypoint fails open on an absent entrypoint
+ ---
+ duration_ms: 0.090307
+ type: 'test'
+ ...
+# Subtest: classifyTranscriptEntrypoint with an empty owner map imports everything (pre-gate behavior)
+ok 174 - classifyTranscriptEntrypoint with an empty owner map imports everything (pre-gate behavior)
+ ---
+ duration_ms: 0.186453
+ type: 'test'
+ ...
+# Subtest: classifyContainerSession imports as the owner when its plugin is configured
+ok 175 - classifyContainerSession imports as the owner when its plugin is configured
+ ---
+ duration_ms: 0.260295
+ type: 'test'
+ ...
+# Subtest: classifyContainerSession gates when the owning plugin is unconfigured
+ok 176 - classifyContainerSession gates when the owning plugin is unconfigured
+ ---
+ duration_ms: 0.150799
+ type: 'test'
+ ...
+# Subtest: classifyContainerSession gates without a predicate, unlike the value classifier
+ok 177 - classifyContainerSession gates without a predicate, unlike the value classifier
+ ---
+ duration_ms: 0.062134
+ type: 'test'
+ ...
+# Subtest: classifyContainerSession is independent of entrypoint value declarations
+ok 178 - classifyContainerSession is independent of entrypoint value declarations
+ ---
+ duration_ms: 0.584819
+ type: 'test'
+ ...
+# Subtest: sessionEntrypoint takes the first non-empty value across a session
+ok 179 - sessionEntrypoint takes the first non-empty value across a session
+ ---
+ duration_ms: 0.077197
+ type: 'test'
+ ...
+# Subtest: the real claude-desktop manifest claims every observed entrypoint value
+ok 180 - the real claude-desktop manifest claims every observed entrypoint value
+ ---
+ duration_ms: 17.798192
+ type: 'test'
+ ...
+# Subtest: the real claude manifest claims cli and sdk-cli
+ok 181 - the real claude manifest claims cli and sdk-cli
+ ---
+ duration_ms: 8.365615
+ type: 'test'
+ ...
+# Subtest: real manifests: Desktop entrypoints gate off when only @hypaware/claude is configured
+ok 182 - real manifests: Desktop entrypoints gate off when only @hypaware/claude is configured
+ ---
+ duration_ms: 9.491747
+ type: 'test'
+ ...
+# Subtest: real manifests: Desktop entrypoints import as claude-desktop once it is configured
+ok 183 - real manifests: Desktop entrypoints import as claude-desktop once it is configured
+ ---
+ duration_ms: 5.108264
+ type: 'test'
+ ...
+# Subtest: the 3p container gates off until claude-desktop is configured, whatever the tag
+ok 184 - the 3p container gates off until claude-desktop is configured, whatever the tag
+ ---
+ duration_ms: 0.176889
+ type: 'test'
+ ...
+# Subtest: every known real-world shared-tree entrypoint value is claimed by a bundled client
+ok 185 - every known real-world shared-tree entrypoint value is claimed by a bundled client
+ ---
+ duration_ms: 5.840097
+ type: 'test'
+ ...
+# Subtest: BackfillRegistry registers, gets, and lists providers sorted by name
+ok 186 - BackfillRegistry registers, gets, and lists providers sorted by name
+ ---
+ duration_ms: 5.94084
+ type: 'test'
+ ...
+# Subtest: BackfillRegistry rejects a duplicate provider name
+ok 187 - BackfillRegistry rejects a duplicate provider name
+ ---
+ duration_ms: 0.327287
+ type: 'test'
+ ...
+# Subtest: BackfillRegistry validates the contribution shape
+ok 188 - BackfillRegistry validates the contribution shape
+ ---
+ duration_ms: 0.163509
+ type: 'test'
+ ...
+# Subtest: BackfillRegistry accepts a provider with an optional plan() hook
+ok 189 - BackfillRegistry accepts a provider with an optional plan() hook
+ ---
+ duration_ms: 0.192883
+ type: 'test'
+ ...
+# Subtest: BackfillMaterializerRegistry registers, gets, lists, and rejects duplicate kinds
+ok 190 - BackfillMaterializerRegistry registers, gets, lists, and rejects duplicate kinds
+ ---
+ duration_ms: 0.253285
+ type: 'test'
+ ...
+# Subtest: BackfillMaterializerRegistry validates the contribution shape
+ok 191 - BackfillMaterializerRegistry validates the contribution shape
+ ---
+ duration_ms: 0.198442
+ type: 'test'
+ ...
+# Subtest: resolveExportsBaseDir prefers explicit pluginConfig.exports_dir
+ok 192 - resolveExportsBaseDir prefers explicit pluginConfig.exports_dir
+ ---
+ duration_ms: 0.84211
+ type: 'test'
+ ...
+# Subtest: resolveExportsBaseDir falls back to HYP_HOME
+ok 193 - resolveExportsBaseDir falls back to HYP_HOME
+ ---
+ duration_ms: 0.187364
+ type: 'test'
+ ...
+# Subtest: resolveExportsBaseDir falls back to homedir/.hyp when HYP_HOME unset
+ok 194 - resolveExportsBaseDir falls back to homedir/.hyp when HYP_HOME unset
+ ---
+ duration_ms: 0.153944
+ type: 'test'
+ ...
+# Subtest: local-fs BlobStore puts and gets bytes by key
+ok 195 - local-fs BlobStore puts and gets bytes by key
+ ---
+ duration_ms: 9.468441
+ type: 'test'
+ ...
+# Subtest: local-fs BlobStore getObject returns null for missing keys
+ok 196 - local-fs BlobStore getObject returns null for missing keys
+ ---
+ duration_ms: 0.823551
+ type: 'test'
+ ...
+# Subtest: local-fs BlobStore lists objects under a prefix in deterministic order
+ok 197 - local-fs BlobStore lists objects under a prefix in deterministic order
+ ---
+ duration_ms: 12.334157
+ type: 'test'
+ ...
+# Subtest: local-fs BlobStore listObjects with empty prefix returns all entries
+ok 198 - local-fs BlobStore listObjects with empty prefix returns all entries
+ ---
+ duration_ms: 6.041262
+ type: 'test'
+ ...
+# Subtest: local-fs BlobStore deleteObject removes the key and is idempotent
+ok 199 - local-fs BlobStore deleteObject removes the key and is idempotent
+ ---
+ duration_ms: 6.194876
+ type: 'test'
+ ...
+# Subtest: local-fs BlobStore getObject body survives a concurrent unlink
+ok 200 - local-fs BlobStore getObject body survives a concurrent unlink
+ ---
+ duration_ms: 5.369151
+ type: 'test'
+ ...
+# Subtest: local-fs BlobStore putObject honours ifNoneMatch="*" by failing on existing keys
+ok 201 - local-fs BlobStore putObject honours ifNoneMatch="*" by failing on existing keys
+ ---
+ duration_ms: 4.682738
+ type: 'test'
+ ...
+# Subtest: local-fs BlobStore ifNoneMatch="*" succeeds when the key is new
+ok 202 - local-fs BlobStore ifNoneMatch="*" succeeds when the key is new
+ ---
+ duration_ms: 3.119801
+ type: 'test'
+ ...
+# Subtest: local-fs BlobStore rejects keys that escape the configured root
+ok 203 - local-fs BlobStore rejects keys that escape the configured root
+ ---
+ duration_ms: 2.742809
+ type: 'test'
+ ...
+# Subtest: local-fs BlobStore accepts a Readable stream as body
+ok 204 - local-fs BlobStore accepts a Readable stream as body
+ ---
+ duration_ms: 8.208866
+ type: 'test'
+ ...
+# Subtest: in-memory BlobStore satisfies the BlobStore contract end-to-end
+ok 205 - in-memory BlobStore satisfies the BlobStore contract end-to-end
+ ---
+ duration_ms: 1.215197
+ type: 'test'
+ ...
+# Subtest: discoverInstalledPlugins returns loaded manifests from the lock
+ok 206 - discoverInstalledPlugins returns loaded manifests from the lock
+ ---
+ duration_ms: 15.791862
+ type: 'test'
+ ...
+# Subtest: discoverInstalledPlugins flags manifest/lock name mismatch as failed
+ok 207 - discoverInstalledPlugins flags manifest/lock name mismatch as failed
+ ---
+ duration_ms: 3.958357
+ type: 'test'
+ ...
+# Subtest: bootKernel merges bundled and installed manifest pools
+ok 208 - bootKernel merges bundled and installed manifest pools
+ ---
+ duration_ms: 12.662356
+ type: 'test'
+ ...
+# Subtest: bootKernel does not activate installed plugins under all-bundled
+ok 209 - bootKernel does not activate installed plugins under all-bundled
+ ---
+ duration_ms: 8.161985
+ type: 'test'
+ ...
+# Subtest: bootKernel rejects installed plugins that shadow bundled first-party names
+ok 210 - bootKernel rejects installed plugins that shadow bundled first-party names
+ ---
+ duration_ms: 6.821058
+ type: 'test'
+ ...
+# Subtest: bootKernel lets installed plugins replace excluded bundled skeletons and exposes their init presets
+ok 211 - bootKernel lets installed plugins replace excluded bundled skeletons and exposes their init presets
+ ---
+ duration_ms: 8.2769
+ type: 'test'
+ ...
+# Subtest: dispatch routes hyp init even when preset args include --yes
+ok 212 - dispatch routes hyp init even when preset args include --yes
+ ---
+ duration_ms: 6.725572
+ type: 'test'
+ ...
+# Subtest: resolveDependencies works over a merged bundled+installed manifest pool
+ok 213 - resolveDependencies works over a merged bundled+installed manifest pool
+ ---
+ duration_ms: 1.092531
+ type: 'test'
+ ...
+# Subtest: mergeInstalledManifestsIntoKnown propagates capability provides/requires
+ok 214 - mergeInstalledManifestsIntoKnown propagates capability provides/requires
+ ---
+ duration_ms: 0.425296
+ type: 'test'
+ ...
+# Subtest: mergeInstalledManifestsIntoKnown does not override first-party metadata
+ok 215 - mergeInstalledManifestsIntoKnown does not override first-party metadata
+ ---
+ duration_ms: 0.394409
+ type: 'test'
+ ...
+# Subtest: the all-available boot (hyp init profile) registers bundled backfill providers including codex
+ok 216 - the all-available boot (hyp init profile) registers bundled backfill providers including codex
+ ---
+ duration_ms: 130.213582
+ type: 'test'
+ ...
+# Subtest: validateConfig does not flag installed plugin names as plugin_unknown
+ok 217 - validateConfig does not flag installed plugin names as plugin_unknown
+ ---
+ duration_ms: 0.731582
+ type: 'test'
+ ...
+# Subtest: a host with only a local layer boots it verbatim (effective = local)
+ok 218 - a host with only a local layer boots it verbatim (effective = local)
+ ---
+ duration_ms: 21.342919
+ type: 'test'
+ ...
+# Subtest: central seed + local layer: effective is the merge, collisions drop, central query ignored
+ok 219 - central seed + local layer: effective is the merge, collisions drop, central query ignored
+ ---
+ duration_ms: 25.081862
+ type: 'test'
+ ...
+# Subtest: a local plugin that invalidates the merge (capability tie) is dropped; central boots
+ok 220 - a local plugin that invalidates the merge (capability tie) is dropped; central boots
+ ---
+ duration_ms: 26.812192
+ type: 'test'
+ ...
+# Subtest: a config-enabled plugin the boot profile drops is reported unavailable
+ok 221 - a config-enabled plugin the boot profile drops is reported unavailable
+ ---
+ duration_ms: 21.221494
+ type: 'test'
+ ...
+# Subtest: an ordinary config-profile boot reports nothing unavailable
+ok 222 - an ordinary config-profile boot reports nothing unavailable
+ ---
+ duration_ms: 23.385154
+ type: 'test'
+ ...
+# Subtest: a plugin the dep graph eliminated is reported unavailable
+ok 223 - a plugin the dep graph eliminated is reported unavailable
+ ---
+ duration_ms: 5.421951
+ type: 'test'
+ ...
+# Subtest: a manifest that would not load is reported unavailable
+ok 224 - a manifest that would not load is reported unavailable
+ ---
+ duration_ms: 15.890912
+ type: 'test'
+ ...
+# Subtest: a boot that selected nothing still reports what it could not load
+ok 225 - a boot that selected nothing still reports what it could not load
+ ---
+ duration_ms: 4.406627
+ type: 'test'
+ ...
+# Subtest: the default-activation allowlist and the excluded set are disjoint
+ok 226 - the default-activation allowlist and the excluded set are disjoint
+ ---
+ duration_ms: 1.042254
+ type: 'test'
+ ...
+# Subtest: a compaction that cannot reduce the file count records that it did not
+ok 227 - a compaction that cannot reduce the file count records that it did not
+ ---
+ duration_ms: 74.512071
+ type: 'test'
+ ...
+# Subtest: a partition frozen by an ineffective compaction is retried once under a new compaction writer
+ok 228 - a partition frozen by an ineffective compaction is retried once under a new compaction writer
+ ---
+ duration_ms: 78.434833
+ type: 'test'
+ ...
+# Subtest: a compaction that did reduce the file count is never retried
+ok 229 - a compaction that did reduce the file count is never retried
+ ---
+ duration_ms: 68.323796
+ type: 'test'
+ ...
+# Subtest: a retry whose rewrite throws still spends its writer generation
+ok 230 - a retry whose rewrite throws still spends its writer generation
+ ---
+ duration_ms: 23.208575
+ type: 'test'
+ ...
+# Subtest: a partition frozen by a failed retry is reported as skipped on every later tick
+ok 231 - a partition frozen by a failed retry is reported as skipped on every later tick
+ ---
+ duration_ms: 39.649584
+ type: 'test'
+ ...
+# Subtest: a committed ineffective verdict outranks the failed attempt that followed it
+ok 232 - a committed ineffective verdict outranks the failed attempt that followed it
+ ---
+ duration_ms: 41.316447
+ type: 'test'
+ ...
+# Subtest: a rewrite that throws after committing its cursor keeps the generation it committed
+ok 233 - a rewrite that throws after committing its cursor keeps the generation it committed
+ ---
+ duration_ms: 34.616394
+ type: 'test'
+ ...
+# Subtest: a partition already at one data file is not reported as an ineffective compaction
+ok 234 - a partition already at one data file is not reported as an ineffective compaction
+ ---
+ duration_ms: 14.989192
+ type: 'test'
+ ...
+# Subtest: a spent-attempt stamp with no usable timestamp is not reported
+ok 235 - a spent-attempt stamp with no usable timestamp is not reported
+ ---
+ duration_ms: 32.807133
+ type: 'test'
+ ...
+# Subtest: compaction sizes output files by bytes written, not the in-memory batch estimate
+ok 236 - compaction sizes output files by bytes written, not the in-memory batch estimate
+ ---
+ duration_ms: 308.810241
+ type: 'test'
+ ...
+# Subtest: compaction rolls to a new data file once target_file_bytes is written
+ok 237 - compaction rolls to a new data file once target_file_bytes is written
+ ---
+ duration_ms: 243.45645
+ type: 'test'
+ ...
+# Subtest: compaction converges past the descriptor cap when a partition holds more tuples than it
+ok 238 - compaction converges past the descriptor cap when a partition holds more tuples than it
+ ---
+ duration_ms: 1263.803397
+ type: 'test'
+ ...
+# Subtest: a streaming append holds one file per tuple open while capping descriptors
+ok 239 - a streaming append holds one file per tuple open while capping descriptors
+ ---
+ duration_ms: 290.844064
+ type: 'test'
+ ...
+# Subtest: a streaming append rolls on retained row-group metadata, not only on target_file_bytes
+ok 240 - a streaming append rolls on retained row-group metadata, not only on target_file_bytes
+ ---
+ duration_ms: 296.925698
+ type: 'test'
+ ...
+# Subtest: aborting a streaming append releases every open descriptor and temp file
+ok 241 - aborting a streaming append releases every open descriptor and temp file
+ ---
+ duration_ms: 2.266605
+ type: 'test'
+ ...
+# Subtest: additive nullable column evolves the cache schema in place - new column queryable, no recreate
+ok 242 - additive nullable column evolves the cache schema in place - new column queryable, no recreate
+ ---
+ duration_ms: 36.582351
+ type: 'test'
+ ...
+# Subtest: backfill works against the evolved schema - repeated V2 appends keep the new column populated
+ok 243 - backfill works against the evolved schema - repeated V2 appends keep the new column populated
+ ---
+ duration_ms: 16.233704
+ type: 'test'
+ ...
+# Subtest: schema evolution is a no-op when columns are unchanged - only one schema persists
+ok 244 - schema evolution is a no-op when columns are unchanged - only one schema persists
+ ---
+ duration_ms: 8.120913
+ type: 'test'
+ ...
+# Subtest: breaking changes still reject after the table exists (no in-place evolution for them)
+ok 245 - breaking changes still reject after the table exists (no in-place evolution for them)
+ ---
+ duration_ms: 8.425756
+ type: 'test'
+ ...
+# Subtest: required→nullable widening evolves the table in place - a later null append lands
+ok 246 - required→nullable widening evolves the table in place - a later null append lands
+ ---
+ duration_ms: 12.890773
+ type: 'test'
+ ...
+# Subtest: a rejected append does not advance the table schema (coerce before any commit)
+ok 247 - a rejected append does not advance the table schema (coerce before any commit)
+ ---
+ duration_ms: 10.828288
+ type: 'test'
+ ...
+# Subtest: partitionSpecForDeclaration builds spec for ai_gateway_messages
+ok 248 - partitionSpecForDeclaration builds spec for ai_gateway_messages
+ ---
+ duration_ms: 0.785794
+ type: 'test'
+ ...
+# Subtest: partitionSpecForDeclaration skips optional column not in schema
+ok 249 - partitionSpecForDeclaration skips optional column not in schema
+ ---
+ duration_ms: 0.093552
+ type: 'test'
+ ...
+# Subtest: partitionSpecForDeclaration throws when required column not in schema
+ok 250 - partitionSpecForDeclaration throws when required column not in schema
+ ---
+ duration_ms: 0.308339
+ type: 'test'
+ ...
+# Subtest: partitionSpecForDeclaration supports non-identity transforms
+ok 251 - partitionSpecForDeclaration supports non-identity transforms
+ ---
+ duration_ms: 0.093673
+ type: 'test'
+ ...
+# Subtest: mergeFieldIdsFromTable preserves existing field IDs
+ok 252 - mergeFieldIdsFromTable preserves existing field IDs
+ ---
+ duration_ms: 0.546841
+ type: 'test'
+ ...
+# Subtest: mergeFieldIdsFromTable appends nullable additions with fresh IDs
+ok 253 - mergeFieldIdsFromTable appends nullable additions with fresh IDs
+ ---
+ duration_ms: 0.209779
+ type: 'test'
+ ...
+# Subtest: mergeFieldIdsFromTable rejects type changes
+ok 254 - mergeFieldIdsFromTable rejects type changes
+ ---
+ duration_ms: 0.160584
+ type: 'test'
+ ...
+# Subtest: mergeFieldIdsFromTable rejects new required columns
+ok 255 - mergeFieldIdsFromTable rejects new required columns
+ ---
+ duration_ms: 0.162016
+ type: 'test'
+ ...
+# Subtest: mergeFieldIdsFromTable rejects column removal
+ok 256 - mergeFieldIdsFromTable rejects column removal
+ ---
+ duration_ms: 0.352917
+ type: 'test'
+ ...
+# Subtest: mergeFieldIdsFromTable rejects nullable → required tightening
+ok 257 - mergeFieldIdsFromTable rejects nullable → required tightening
+ ---
+ duration_ms: 0.389412
+ type: 'test'
+ ...
+# Subtest: mergeFieldIdsFromTable uses specific error for partition column type change
+ok 258 - mergeFieldIdsFromTable uses specific error for partition column type change
+ ---
+ duration_ms: 0.215919
+ type: 'test'
+ ...
+# Subtest: mergeFieldIdsFromTable uses specific error for partition column removal
+ok 259 - mergeFieldIdsFromTable uses specific error for partition column removal
+ ---
+ duration_ms: 0.15123
+ type: 'test'
+ ...
+# Subtest: validatePartitionSpecStability passes when spec matches declaration
+ok 260 - validatePartitionSpecStability passes when spec matches declaration
+ ---
+ duration_ms: 0.277833
+ type: 'test'
+ ...
+# Subtest: validatePartitionSpecStability rejects new partition field
+ok 261 - validatePartitionSpecStability rejects new partition field
+ ---
+ duration_ms: 0.180554
+ type: 'test'
+ ...
+# Subtest: validatePartitionSpecStability rejects removed partition field
+ok 262 - validatePartitionSpecStability rejects removed partition field
+ ---
+ duration_ms: 0.179022
+ type: 'test'
+ ...
+# Subtest: validatePartitionSpecStability compares effective optional fields against schema
+ok 263 - validatePartitionSpecStability compares effective optional fields against schema
+ ---
+ duration_ms: 0.129196
+ type: 'test'
+ ...
+# Subtest: validatePartitionSpecStability rejects transform changes
+ok 264 - validatePartitionSpecStability rejects transform changes
+ ---
+ duration_ms: 0.130989
+ type: 'test'
+ ...
+# Subtest: appendRowsToTable creates table with partition spec from declaration
+ok 265 - appendRowsToTable creates table with partition spec from declaration
+ ---
+ duration_ms: 28.217728
+ type: 'test'
+ ...
+# Subtest: appendRowsToTable without declaration creates unpartitioned table
+ok 266 - appendRowsToTable without declaration creates unpartitioned table
+ ---
+ duration_ms: 4.615505
+ type: 'test'
+ ...
+# Subtest: appendRowsToTable validates schema on existing table with declaration
+ok 267 - appendRowsToTable validates schema on existing table with declaration
+ ---
+ duration_ms: 29.351161
+ type: 'test'
+ ...
+# Subtest: basicTypeForIcebergType round-trips known types
+ok 268 - basicTypeForIcebergType round-trips known types
+ ---
+ duration_ms: 0.199122
+ type: 'test'
+ ...
+# Subtest: basicTypeForIcebergType defaults unknown types to STRING
+ok 269 - basicTypeForIcebergType defaults unknown types to STRING
+ ---
+ duration_ms: 0.11226
+ type: 'test'
+ ...
+# Subtest: columnsFromIcebergSchema preserves types and nullability
+ok 270 - columnsFromIcebergSchema preserves types and nullability
+ ---
+ duration_ms: 0.137128
+ type: 'test'
+ ...
+# Subtest: a partition whose compaction throws does not abort the rest of the walk
+ok 271 - a partition whose compaction throws does not abort the rest of the walk
+ ---
+ duration_ms: 118.096675
+ type: 'test'
+ ...
+# Subtest: the partition that threw still spends its writer generation
+ok 272 - the partition that threw still spends its writer generation
+ ---
+ duration_ms: 55.246529
+ type: 'test'
+ ...
+# Subtest: snapshot expiry still runs for the partitions behind the one that threw
+ok 273 - snapshot expiry still runs for the partitions behind the one that threw
+ ---
+ duration_ms: 34.707853
+ type: 'test'
+ ...
+# Subtest: a tick that lost a partition reports the loss rather than swallowing it
+ok 274 - a tick that lost a partition reports the loss rather than swallowing it
+ ---
+ duration_ms: 39.770257
+ type: 'test'
+ ...
+# Subtest: a zero budget does not let a failed first partition starve the healthy partition behind it
+ok 275 - a zero budget does not let a failed first partition starve the healthy partition behind it
+ ---
+ duration_ms: 38.721854
+ type: 'test'
+ ...
+# Subtest: an all-failing cache does not walk past its budget unbounded: it stops at the failure cap
+ok 276 - an all-failing cache does not walk past its budget unbounded: it stops at the failure cap
+ ---
+ duration_ms: 52.131274
+ type: 'test'
+ ...
+# Subtest: migrateLegacyPartitions dry-run reports legacy partitions without modifying
+ok 277 - migrateLegacyPartitions dry-run reports legacy partitions without modifying
+ ---
+ duration_ms: 28.766112
+ type: 'test'
+ ...
+# Subtest: migrateLegacyPartitions --force moves rows to source-table layout and retires legacy dir
+ok 278 - migrateLegacyPartitions --force moves rows to source-table layout and retires legacy dir
+ ---
+ duration_ms: 23.96834
+ type: 'test'
+ ...
+# Subtest: migrateLegacyPartitions is idempotent
+ok 279 - migrateLegacyPartitions is idempotent
+ ---
+ duration_ms: 25.974289
+ type: 'test'
+ ...
+# Subtest: migrateLegacyPartitions skips source-table partitions
+ok 280 - migrateLegacyPartitions skips source-table partitions
+ ---
+ duration_ms: 6.667954
+ type: 'test'
+ ...
+# Subtest: migrateLegacyPartitions migrates client=/date= epoch partitions
+ok 281 - migrateLegacyPartitions migrates client=/date= epoch partitions
+ ---
+ duration_ms: 39.347895
+ type: 'test'
+ ...
+# Subtest: migrateLegacyPartitions dry-run on epoch partitions reports without modifying
+ok 282 - migrateLegacyPartitions dry-run on epoch partitions reports without modifying
+ ---
+ duration_ms: 8.352054
+ type: 'test'
+ ...
+# Subtest: migrateLegacyPartitions retires only after successful append
+ok 283 - migrateLegacyPartitions retires only after successful append
+ ---
+ duration_ms: 25.870671
+ type: 'test'
+ ...
+# Subtest: appendRowsToPartition creates epoch directory and cursor on first write
+ok 284 - appendRowsToPartition creates epoch directory and cursor on first write
+ ---
+ duration_ms: 32.910801
+ type: 'test'
+ ...
+# Subtest: appendRowsToPartition with single partition segment
+ok 285 - appendRowsToPartition with single partition segment
+ ---
+ duration_ms: 9.117698
+ type: 'test'
+ ...
+# Subtest: appendRowsToPartition with multi-segment path
+ok 286 - appendRowsToPartition with multi-segment path
+ ---
+ duration_ms: 15.178881
+ type: 'test'
+ ...
+# Subtest: appendRowsToPartition updates cursor rowCount on subsequent writes
+ok 287 - appendRowsToPartition updates cursor rowCount on subsequent writes
+ ---
+ duration_ms: 8.728957
+ type: 'test'
+ ...
+# Subtest: appendRowsToPartition returns early for empty rows
+ok 288 - appendRowsToPartition returns early for empty rows
+ ---
+ duration_ms: 0.797712
+ type: 'test'
+ ...
+# Subtest: readCursorSync returns default cursor for missing file
+ok 289 - readCursorSync returns default cursor for missing file
+ ---
+ duration_ms: 0.602746
+ type: 'test'
+ ...
+# Subtest: writeCursor creates the file and readCursorSync reads it back
+ok 290 - writeCursor creates the file and readCursorSync reads it back
+ ---
+ duration_ms: 1.620413
+ type: 'test'
+ ...
+# Subtest: discoverCachePartitions returns empty for empty cache
+ok 291 - discoverCachePartitions returns empty for empty cache
+ ---
+ duration_ms: 0.807898
+ type: 'test'
+ ...
+# Subtest: discoverCachePartitions finds a single dataset partition
+ok 292 - discoverCachePartitions finds a single dataset partition
+ ---
+ duration_ms: 18.471705
+ type: 'test'
+ ...
+# Subtest: discoverCachePartitions discovers multiple dates
+ok 293 - discoverCachePartitions discovers multiple dates
+ ---
+ duration_ms: 23.280895
+ type: 'test'
+ ...
+# Subtest: discoverCachePartitions filters by scope.datasets
+ok 294 - discoverCachePartitions filters by scope.datasets
+ ---
+ duration_ms: 18.642134
+ type: 'test'
+ ...
+# Subtest: discoverCachePartitions filters by scope.date
+ok 295 - discoverCachePartitions filters by scope.date
+ ---
+ duration_ms: 14.52686
+ type: 'test'
+ ...
+# Subtest: discoverCachePartitions filters by scope.from and scope.to
+ok 296 - discoverCachePartitions filters by scope.from and scope.to
+ ---
+ duration_ms: 23.059618
+ type: 'test'
+ ...
+# Subtest: resolveClientName returns client_name when present
+ok 297 - resolveClientName returns client_name when present
+ ---
+ duration_ms: 0.17739
+ type: 'test'
+ ...
+# Subtest: resolveClientName falls back to conversation_source
+ok 298 - resolveClientName falls back to conversation_source
+ ---
+ duration_ms: 0.065379
+ type: 'test'
+ ...
+# Subtest: resolveClientName falls back to provider
+ok 299 - resolveClientName falls back to provider
+ ---
+ duration_ms: 0.065459
+ type: 'test'
+ ...
+# Subtest: resolveClientName falls back to "unknown"
+ok 300 - resolveClientName falls back to "unknown"
+ ---
+ duration_ms: 0.04607
+ type: 'test'
+ ...
+# Subtest: resolveClientName skips empty strings in the fallback chain
+ok 301 - resolveClientName skips empty strings in the fallback chain
+ ---
+ duration_ms: 0.043887
+ type: 'test'
+ ...
+# Subtest: resolvePartitionDate extracts date from ISO timestamp string
+ok 302 - resolvePartitionDate extracts date from ISO timestamp string
+ ---
+ duration_ms: 0.104829
+ type: 'test'
+ ...
+# Subtest: resolvePartitionDate extracts date from Date object
+ok 303 - resolvePartitionDate extracts date from Date object
+ ---
+ duration_ms: 0.247075
+ type: 'test'
+ ...
+# Subtest: resolvePartitionDate extracts date from epoch ms number
+ok 304 - resolvePartitionDate extracts date from epoch ms number
+ ---
+ duration_ms: 0.082084
+ type: 'test'
+ ...
+# Subtest: resolvePartitionDate extracts date from created_at field
+ok 305 - resolvePartitionDate extracts date from created_at field
+ ---
+ duration_ms: 0.089667
+ type: 'test'
+ ...
+# Subtest: resolvePartitionDate extracts date from date field
+ok 306 - resolvePartitionDate extracts date from date field
+ ---
+ duration_ms: 0.061042
+ type: 'test'
+ ...
+# Subtest: resolvePartitionDate returns undefined when no timestamp field present
+ok 307 - resolvePartitionDate returns undefined when no timestamp field present
+ ---
+ duration_ms: 0.05248
+ type: 'test'
+ ...
+# Subtest: resolvePartitionSegments returns client+date for rows with both
+ok 308 - resolvePartitionSegments returns client+date for rows with both
+ ---
+ duration_ms: 0.108996
+ type: 'test'
+ ...
+# Subtest: resolvePartitionSegments returns client+date using fallback chain
+ok 309 - resolvePartitionSegments returns client+date using fallback chain
+ ---
+ duration_ms: 0.085019
+ type: 'test'
+ ...
+# Subtest: resolvePartitionSegments falls back to ["all"] when no partition keys
+ok 310 - resolvePartitionSegments falls back to ["all"] when no partition keys
+ ---
+ duration_ms: 0.104518
+ type: 'test'
+ ...
+# Subtest: resolvePartitionSegments returns client=unknown+date when only date present
+ok 311 - resolvePartitionSegments returns client=unknown+date when only date present
+ ---
+ duration_ms: 0.07851
+ type: 'test'
+ ...
+# Subtest: discoverCachePartitions detects a legacy Iceberg table without cursor.json
+ok 312 - discoverCachePartitions detects a legacy Iceberg table without cursor.json
+ ---
+ duration_ms: 9.259754
+ type: 'test'
+ ...
+# Subtest: discoverCachePartitions finds both legacy and new-style partitions
+ok 313 - discoverCachePartitions finds both legacy and new-style partitions
+ ---
+ duration_ms: 14.90255
+ type: 'test'
+ ...
+# Subtest: discoverCachePartitions skips .retired directories
+ok 314 - discoverCachePartitions skips .retired directories
+ ---
+ duration_ms: 6.380658
+ type: 'test'
+ ...
+# Subtest: sanitizePathSegment passes through normal values
+ok 315 - sanitizePathSegment passes through normal values
+ ---
+ duration_ms: 0.235718
+ type: 'test'
+ ...
+# Subtest: sanitizePathSegment replaces path separators and special chars
+ok 316 - sanitizePathSegment replaces path separators and special chars
+ ---
+ duration_ms: 0.081794
+ type: 'test'
+ ...
+# Subtest: sanitizePathSegment escapes dot and dotdot
+ok 317 - sanitizePathSegment escapes dot and dotdot
+ ---
+ duration_ms: 0.069436
+ type: 'test'
+ ...
+# Subtest: sanitizePathSegment handles empty string
+ok 318 - sanitizePathSegment handles empty string
+ ---
+ duration_ms: 0.051438
+ type: 'test'
+ ...
+# Subtest: sanitizePathSegment replaces control characters
+ok 319 - sanitizePathSegment replaces control characters
+ ---
+ duration_ms: 0.11819
+ type: 'test'
+ ...
+# Subtest: resolveSourceSegments uses first non-empty source column
+ok 320 - resolveSourceSegments uses first non-empty source column
+ ---
+ duration_ms: 0.175366
+ type: 'test'
+ ...
+# Subtest: resolveSourceSegments falls through source columns
+ok 321 - resolveSourceSegments falls through source columns
+ ---
+ duration_ms: 0.112711
+ type: 'test'
+ ...
+# Subtest: resolveSourceSegments uses fallback when no columns match
+ok 322 - resolveSourceSegments uses fallback when no columns match
+ ---
+ duration_ms: 0.071378
+ type: 'test'
+ ...
+# Subtest: resolveSourceSegments skips empty string values
+ok 323 - resolveSourceSegments skips empty string values
+ ---
+ duration_ms: 0.061443
+ type: 'test'
+ ...
+# Subtest: resolveSourceSegments sanitizes path-unsafe source values
+ok 324 - resolveSourceSegments sanitizes path-unsafe source values
+ ---
+ duration_ms: 0.07251
+ type: 'test'
+ ...
+# Subtest: validateIcebergPartitionFields passes when required fields present
+ok 325 - validateIcebergPartitionFields passes when required fields present
+ ---
+ duration_ms: 0.10535
+ type: 'test'
+ ...
+# Subtest: validateIcebergPartitionFields fails when required fields missing
+ok 326 - validateIcebergPartitionFields fails when required fields missing
+ ---
+ duration_ms: 0.080743
+ type: 'test'
+ ...
+# Subtest: validateIcebergPartitionFields ignores optional fields
+ok 327 - validateIcebergPartitionFields ignores optional fields
+ ---
+ duration_ms: 0.069656
+ type: 'test'
+ ...
+# Subtest: validateIcebergPartitionFields treats empty strings as missing
+ok 328 - validateIcebergPartitionFields treats empty strings as missing
+ ---
+ duration_ms: 0.068214
+ type: 'test'
+ ...
+# Subtest: validateIcebergPartitionFields accepts non-string required fields
+ok 329 - validateIcebergPartitionFields accepts non-string required fields
+ ---
+ duration_ms: 0.083106
+ type: 'test'
+ ...
+# Subtest: validateIcebergPartitionFields rejects null and undefined required fields
+ok 330 - validateIcebergPartitionFields rejects null and undefined required fields
+ ---
+ duration_ms: 0.109807
+ type: 'test'
+ ...
+# Subtest: readCursorSync preserves layout and retention fields
+ok 331 - readCursorSync preserves layout and retention fields
+ ---
+ duration_ms: 11.739062
+ type: 'test'
+ ...
+# Subtest: readCursorSync returns default cursor without new fields when missing
+ok 332 - readCursorSync returns default cursor without new fields when missing
+ ---
+ duration_ms: 0.195818
+ type: 'test'
+ ...
+# Subtest: resolveIcebergDir returns tablePath/table for source-table layout
+ok 333 - resolveIcebergDir returns tablePath/table for source-table layout
+ ---
+ duration_ms: 1.424846
+ type: 'test'
+ ...
+# Subtest: resolveIcebergDir uses custom tableDir for source-table layout
+ok 334 - resolveIcebergDir uses custom tableDir for source-table layout
+ ---
+ duration_ms: 0.98694
+ type: 'test'
+ ...
+# Subtest: resolveIcebergDir returns epoch path for legacy layout
+ok 335 - resolveIcebergDir returns epoch path for legacy layout
+ ---
+ duration_ms: 1.775849
+ type: 'test'
+ ...
+# Subtest: resolveIcebergDir returns tablePath unchanged when no cursor
+ok 336 - resolveIcebergDir returns tablePath unchanged when no cursor
+ ---
+ duration_ms: 0.110498
+ type: 'test'
+ ...
+# Subtest: appendRowsToSourceTable creates source-table layout with table subdirectory
+ok 337 - appendRowsToSourceTable creates source-table layout with table subdirectory
+ ---
+ duration_ms: 6.556626
+ type: 'test'
+ ...
+# Subtest: appendRowsToSourceTable accumulates rowCount across multiple writes
+ok 338 - appendRowsToSourceTable accumulates rowCount across multiple writes
+ ---
+ duration_ms: 6.874238
+ type: 'test'
+ ...
+# Subtest: appendRowsToSourceTable with multiple dates creates one source table
+ok 339 - appendRowsToSourceTable with multiple dates creates one source table
+ ---
+ duration_ms: 4.833666
+ type: 'test'
+ ...
+# Subtest: appendRowsToSourceTable with two sources creates two source tables
+ok 340 - appendRowsToSourceTable with two sources creates two source tables
+ ---
+ duration_ms: 15.386656
+ type: 'test'
+ ...
+# Subtest: appendRowsToSourceTable returns early for empty rows
+ok 341 - appendRowsToSourceTable returns early for empty rows
+ ---
+ duration_ms: 1.310422
+ type: 'test'
+ ...
+# Subtest: discoverCachePartitions finds source-table partitions
+ok 342 - discoverCachePartitions finds source-table partitions
+ ---
+ duration_ms: 5.242749
+ type: 'test'
+ ...
+# Subtest: discoverCachePartitions does not filter source-table partitions by date scope
+ok 343 - discoverCachePartitions does not filter source-table partitions by date scope
+ ---
+ duration_ms: 11.567392
+ type: 'test'
+ ...
+# Subtest: discoverCachePartitions finds both source-table and legacy partitions
+ok 344 - discoverCachePartitions finds both source-table and legacy partitions
+ ---
+ duration_ms: 5.713023
+ type: 'test'
+ ...
+# Subtest: re-settle sweep collapses a split fallback/uuid twin pair after the fact
+ok 345 - re-settle sweep collapses a split fallback/uuid twin pair after the fact
+ ---
+ duration_ms: 160.169842
+ type: 'test'
+ ...
+# Subtest: re-settle sweep upgrades a lone fallback row even when its twin never arrived
+ok 346 - re-settle sweep upgrades a lone fallback row even when its twin never arrived
+ ---
+ duration_ms: 47.368736
+ type: 'test'
+ ...
+# Subtest: re-settle sweep leaves the fallback row untouched when the transcript is unavailable
+ok 347 - re-settle sweep leaves the fallback row untouched when the transcript is unavailable
+ ---
+ duration_ms: 47.951912
+ type: 'test'
+ ...
+# Subtest: a fallback marker auto-triggers compaction without force; a no-marker partition does not
+ok 348 - a fallback marker auto-triggers compaction without force; a no-marker partition does not
+ ---
+ duration_ms: 55.890699
+ type: 'test'
+ ...
+# Subtest: an unmatchable fallback does not force a rewrite every tick - only on new data
+ok 349 - an unmatchable fallback does not force a rewrite every tick - only on new data
+ ---
+ duration_ms: 85.727727
+ type: 'test'
+ ...
+# Subtest: default retention days is 90
+ok 350 - default retention days is 90
+ ---
+ duration_ms: 1.162087
+ type: 'test'
+ ...
+# Subtest: retention normalizeConfig applies defaults
+ok 351 - retention normalizeConfig applies defaults
+ ---
+ duration_ms: 0.694756
+ type: 'test'
+ ...
+# Subtest: retention tick on empty cache returns empty results
+ok 352 - retention tick on empty cache returns empty results
+ ---
+ duration_ms: 12.662736
+ type: 'test'
+ ...
+# Subtest: retention commits Iceberg deletes on source-table rows older than cutoff
+ok 353 - retention commits Iceberg deletes on source-table rows older than cutoff
+ ---
+ duration_ms: 38.35126
+ type: 'test'
+ ...
+# Subtest: retention skips source tables when all rows are within retention
+ok 354 - retention skips source tables when all rows are within retention
+ ---
+ duration_ms: 11.681535
+ type: 'test'
+ ...
+# Subtest: retention respects per-dataset override
+ok 355 - retention respects per-dataset override
+ ---
+ duration_ms: 40.112627
+ type: 'test'
+ ...
+# Subtest: cacheStatus reports source-table layout with source field
+ok 356 - cacheStatus reports source-table layout with source field
+ ---
+ duration_ms: 16.671389
+ type: 'test'
+ ...
+# Subtest: cacheStatus reports lastRetentionCutoffDate after retention runs
+ok 357 - cacheStatus reports lastRetentionCutoffDate after retention runs
+ ---
+ duration_ms: 15.537946
+ type: 'test'
+ ...
+# Subtest: normalizeMaintenanceConfig fills defaults
+ok 358 - normalizeMaintenanceConfig fills defaults
+ ---
+ duration_ms: 0.403994
+ type: 'test'
+ ...
+# Subtest: maintenance counts data files and snapshots for source tables
+ok 359 - maintenance counts data files and snapshots for source tables
+ ---
+ duration_ms: 35.533007
+ type: 'test'
+ ...
+# Subtest: maintenance expires snapshots on source tables
+ok 360 - maintenance expires snapshots on source tables
+ ---
+ duration_ms: 56.885891
+ type: 'test'
+ ...
+# Subtest: compaction preserves source-table layout
+ok 361 - compaction preserves source-table layout
+ ---
+ duration_ms: 197.712203
+ type: 'test'
+ ...
+# Subtest: compaction retires empty source table and advances cursor
+ok 362 - compaction retires empty source table and advances cursor
+ ---
+ duration_ms: 9.64543
+ type: 'test'
+ ...
+# Subtest: compaction preserves partition spec and column types from declaration
+ok 363 - compaction preserves partition spec and column types from declaration
+ ---
+ duration_ms: 282.70272
+ type: 'test'
+ ...
+# Subtest: retention second tick reports zero newly deleted rows (no duplicate deletes)
+ok 364 - retention second tick reports zero newly deleted rows (no duplicate deletes)
+ ---
+ duration_ms: 19.052237
+ type: 'test'
+ ...
+# Subtest: retention re-scans unchanged source table when cutoff advances
+ok 365 - retention re-scans unchanged source table when cutoff advances
+ ---
+ duration_ms: 24.075673
+ type: 'test'
+ ...
+# Subtest: retention uses dataset primaryTimestampColumn for source tables
+ok 366 - retention uses dataset primaryTimestampColumn for source tables
+ ---
+ duration_ms: 14.202086
+ type: 'test'
+ ...
+# Subtest: retention evicts source table by mtime when no timestamp column is resolvable
+ok 367 - retention evicts source table by mtime when no timestamp column is resolvable
+ ---
+ duration_ms: 6.794116
+ type: 'test'
+ ...
+# Subtest: retention cursor stays accurate after new data arrives between ticks
+ok 368 - retention cursor stays accurate after new data arrives between ticks
+ ---
+ duration_ms: 20.17886
+ type: 'test'
+ ...
+# Subtest: source-table directory remains intact after retention
+ok 369 - source-table directory remains intact after retention
+ ---
+ duration_ms: 12.176257
+ type: 'test'
+ ...
+# Subtest: normalizeMaintenanceConfig fills compact_batch_bytes default
+ok 370 - normalizeMaintenanceConfig fills compact_batch_bytes default
+ ---
+ duration_ms: 0.147745
+ type: 'test'
+ ...
+# Subtest: normalizeMaintenanceConfig honours an explicit compact_batch_bytes
+ok 371 - normalizeMaintenanceConfig honours an explicit compact_batch_bytes
+ ---
+ duration_ms: 0.076967
+ type: 'test'
+ ...
+# Subtest: compaction flushes by byte budget so a fat column cannot blow up one batch
+ok 372 - compaction flushes by byte budget so a fat column cannot blow up one batch
+ ---
+ duration_ms: 144.444582
+ type: 'test'
+ ...
+# Subtest: a generous byte budget compacts the same input into a single row group
+ok 373 - a generous byte budget compacts the same input into a single row group
+ ---
+ duration_ms: 114.955471
+ type: 'test'
+ ...
+# Subtest: maintenance reclaims a stale cursor-orphaned table dir with no .retired marker
+ok 374 - maintenance reclaims a stale cursor-orphaned table dir with no .retired marker
+ ---
+ duration_ms: 8.238342
+ type: 'test'
+ ...
+# Subtest: orphan sweep never deletes the live table when cursor.json is unreadable
+ok 375 - orphan sweep never deletes the live table when cursor.json is unreadable
+ ---
+ duration_ms: 10.850662
+ type: 'test'
+ ...
+# Subtest: orphan sweep reclaims a stale epoch generation and keeps the live one
+ok 376 - orphan sweep reclaims a stale epoch generation and keeps the live one
+ ---
+ duration_ms: 9.954971
+ type: 'test'
+ ...
+# Subtest: legacy epoch compaction preserves the table sort order
+ok 377 - legacy epoch compaction preserves the table sort order
+ ---
+ duration_ms: 17.058027
+ type: 'test'
+ ...
+# Subtest: an already-compacted partition is not recompacted until new data flushes
+ok 378 - an already-compacted partition is not recompacted until new data flushes
+ ---
+ duration_ms: 29.371472
+ type: 'test'
+ ...
+# Subtest: a foreign sorted replace re-baselines the cursor instead of being rewritten
+ok 379 - a foreign sorted replace re-baselines the cursor instead of being rewritten
+ ---
+ duration_ms: 29.34419
+ type: 'test'
+ ...
+# Subtest: a rebaseline that fails to persist is not reported as rebaselined, and is not counted
+ok 380 - a rebaseline that fails to persist is not reported as rebaselined, and is not counted
+ ---
+ duration_ms: 24.378012
+ type: 'test'
+ ...
+# Subtest: foreign sorted replace recognition works on the source-table layout
+ok 381 - foreign sorted replace recognition works on the source-table layout
+ ---
+ duration_ms: 14.806014
+ type: 'test'
+ ...
+# Subtest: recognition outranks the re-settle force: a fallback row does not undo the sorted layout
+ok 382 - recognition outranks the re-settle force: a fallback row does not undo the sorted layout
+ ---
+ duration_ms: 10.764861
+ type: 'test'
+ ...
+# Subtest: a foreign replace without a declared sort order is not blessed
+ok 383 - a foreign replace without a declared sort order is not blessed
+ ---
+ duration_ms: 28.891653
+ type: 'test'
+ ...
+# Subtest: force still rewrites a foreign sorted replace
+ok 384 - force still rewrites a foreign sorted replace
+ ---
+ duration_ms: 24.31029
+ type: 'test'
+ ...
+# Subtest: a foreign sorted replace tags the maintenance.partition span with rebaselined
+ok 385 - a foreign sorted replace tags the maintenance.partition span with rebaselined
+ ---
+ duration_ms: 17.208075
+ type: 'test'
+ ...
+# Subtest: a partition already due for compaction skips the resettle-candidate row scan
+ok 386 - a partition already due for compaction skips the resettle-candidate row scan
+ ---
+ duration_ms: 11.154644
+ type: 'test'
+ ...
+# Subtest: maintenance walks partitions neediest-first, not directory order
+ok 387 - maintenance walks partitions neediest-first, not directory order
+ ---
+ duration_ms: 12.986648
+ type: 'test'
+ ...
+# Subtest: neediest-first order reads the live table dir for source-table partitions
+ok 388 - neediest-first order reads the live table dir for source-table partitions
+ ---
+ duration_ms: 14.322378
+ type: 'test'
+ ...
+# Subtest: default spool threshold is Iceberg-sized to avoid frequent small commits
+ok 389 - default spool threshold is Iceberg-sized to avoid frequent small commits
+ ---
+ duration_ms: 0.965087
+ type: 'test'
+ ...
+# Subtest: storage.appendRowsToPartition writes data without error
+ok 390 - storage.appendRowsToPartition writes data without error
+ ---
+ duration_ms: 22.89633
+ type: 'test'
+ ...
+# Subtest: spool flush groups rows by source and creates source-table layout
+ok 391 - spool flush groups rows by source and creates source-table layout
+ ---
+ duration_ms: 38.186129
+ type: 'test'
+ ...
+# Subtest: spool flush falls back to source=unknown when no client columns present
+ok 392 - spool flush falls back to source=unknown when no client columns present
+ ---
+ duration_ms: 17.248405
+ type: 'test'
+ ...
+# Subtest: storage.dataSourceForTable keeps columns and cells aligned after internal-field filtering
+ok 393 - storage.dataSourceForTable keeps columns and cells aligned after internal-field filtering
+ ---
+ duration_ms: 14.241937
+ type: 'test'
+ ...
+# Subtest: spool flush creates Iceberg table with partition spec when declaration is provided
+ok 394 - spool flush creates Iceberg table with partition spec when declaration is provided
+ ---
+ duration_ms: 31.980948
+ type: 'test'
+ ...
+# Subtest: spool flush reports rows dropped by required partition validation
+ok 395 - spool flush reports rows dropped by required partition validation
+ ---
+ duration_ms: 19.165119
+ type: 'test'
+ ...
+# Subtest: spool flush uses resolveSourceSegments when declaration is provided
+ok 396 - spool flush uses resolveSourceSegments when declaration is provided
+ ---
+ duration_ms: 35.032476
+ type: 'test'
+ ...
+# Subtest: readSpooledRows yields unflushed rows and goes empty after flush
+ok 397 - readSpooledRows yields unflushed rows and goes empty after flush
+ ---
+ duration_ms: 14.333776
+ type: 'test'
+ ...
+# Subtest: readSpooledRows projects to requested columns and filters by dataset
+ok 398 - readSpooledRows projects to requested columns and filters by dataset
+ ---
+ duration_ms: 23.197348
+ type: 'test'
+ ...
+# Subtest: readSpooledRows skips a parseable envelope missing columns, matching what flush drops
+ok 399 - readSpooledRows skips a parseable envelope missing columns, matching what flush drops
+ ---
+ duration_ms: 6.935992
+ type: 'test'
+ ...
+# Subtest: readSpooledRows on an unknown dataset is an empty stream
+ok 400 - readSpooledRows on an unknown dataset is an empty stream
+ ---
+ duration_ms: 2.28335
+ type: 'test'
+ ...
+# Subtest: readSpooledRows streams a large spool file rather than reading it whole (bounded memory, issue \#280)
+ok 401 - readSpooledRows streams a large spool file rather than reading it whole (bounded memory, issue \#280)
+ ---
+ duration_ms: 448.008209
+ type: 'test'
+ ...
+# Subtest: classifyInactiveState returns absent when the entry is missing from the effective config
+ok 402 - classifyInactiveState returns absent when the entry is missing from the effective config
+ ---
+ duration_ms: 0.804422
+ type: 'test'
+ ...
+# Subtest: classifyInactiveState returns disabled-local when the disabled entry exists only in the local layer
+ok 403 - classifyInactiveState returns disabled-local when the disabled entry exists only in the local layer
+ ---
+ duration_ms: 0.15153
+ type: 'test'
+ ...
+# Subtest: classifyInactiveState returns disabled-central when the disabled entry also names the plugin centrally
+ok 404 - classifyInactiveState returns disabled-central when the disabled entry also names the plugin centrally
+ ---
+ duration_ms: 0.129286
+ type: 'test'
+ ...
+# Subtest: hyp --version prints version and exits 0
+ok 405 - hyp --version prints version and exits 0
+ ---
+ duration_ms: 2.745163
+ type: 'test'
+ ...
+# Subtest: hyp -V prints version and exits 0
+ok 406 - hyp -V prints version and exits 0
+ ---
+ duration_ms: 0.602405
+ type: 'test'
+ ...
+# Subtest: hyp version prints version info and exits 0
+ok 407 - hyp version prints version info and exits 0
+ ---
+ duration_ms: 1.675877
+ type: 'test'
+ ...
+# Subtest: version string matches package.json
+ok 408 - version string matches package.json
+ ---
+ duration_ms: 0.565028
+ type: 'test'
+ ...
+# Subtest: init help documents every flag that selects the non-interactive path
+ok 409 - init help documents every flag that selects the non-interactive path
+ ---
+ duration_ms: 1.833616
+ type: 'test'
+ ...
+# Subtest: init usage does not imply --yes gates the other flags
+ok 410 - init usage does not imply --yes gates the other flags
+ ---
+ duration_ms: 0.288909
+ type: 'test'
+ ...
+# Subtest: renderSelect: an option summary lands on its own indented line under its row
+ok 411 - renderSelect: an option summary lands on its own indented line under its row
+ ---
+ duration_ms: 0.821258
+ type: 'test'
+ ...
+# Subtest: renderSelect: the summary line is dim, not the row colour
+ok 412 - renderSelect: the summary line is dim, not the row colour
+ ---
+ duration_ms: 0.233174
+ type: 'test'
+ ...
+# Subtest: TTY gate: the accept disclosure reaches the screen through the real select prompt
+ok 413 - TTY gate: the accept disclosure reaches the screen through the real select prompt
+ ---
+ duration_ms: 35.229235
+ type: 'test'
+ ...
+# Subtest: non-TTY gate: the numbered fallback prints the accept disclosure under its row
+ok 414 - non-TTY gate: the numbered fallback prints the accept disclosure under its row
+ ---
+ duration_ms: 1.36203
+ type: 'test'
+ ...
+# Subtest: withSpinner off a TTY prints the label once and nothing else
+ok 415 - withSpinner off a TTY prints the label once and nothing else
+ ---
+ duration_ms: 0.950815
+ type: 'test'
+ ...
+# Subtest: withSpinner under HYP_NO_TUI=1 stays on the plain path even on a TTY
+ok 416 - withSpinner under HYP_NO_TUI=1 stays on the plain path even on a TTY
+ ---
+ duration_ms: 0.188096
+ type: 'test'
+ ...
+# Subtest: withSpinner on a TTY animates in place and clears the line when done
+ok 417 - withSpinner on a TTY animates in place and clears the line when done
+ ---
+ duration_ms: 32.164547
+ type: 'test'
+ ...
+# Subtest: withSpinner clears the line and rethrows when the work fails
+ok 418 - withSpinner clears the line and rethrows when the work fails
+ ---
+ duration_ms: 0.590047
+ type: 'test'
+ ...
+# Subtest: installStreamErrorHandlers: EPIPE is swallowed without a word
+ok 419 - installStreamErrorHandlers: EPIPE is swallowed without a word
+ ---
+ duration_ms: 1.090177
+ type: 'test'
+ ...
+# Subtest: installStreamErrorHandlers: any other failure is reported once
+ok 420 - installStreamErrorHandlers: any other failure is reported once
+ ---
+ duration_ms: 0.993199
+ type: 'test'
+ ...
+# Subtest: installStreamErrorHandlers: the returned detach removes the listeners
+ok 421 - installStreamErrorHandlers: the returned detach removes the listeners
+ ---
+ duration_ms: 0.236059
+ type: 'test'
+ ...
+# Subtest: a write past the pipe buffer survives a reader that walked away
+ok 422 - a write past the pipe buffer survives a reader that walked away
+ ---
+ duration_ms: 236.786262
+ type: 'test'
+ ...
+# Subtest: the same write without the handler is what we are protecting against
+ok 423 - the same write without the handler is what we are protecting against
+ ---
+ duration_ms: 36.039917
+ type: 'test'
+ ...
+# Subtest: the palette maps severities to the standard SGR codes, pinned as bytes
+ok 424 - the palette maps severities to the standard SGR codes, pinned as bytes
+ ---
+ duration_ms: 1.19076
+ type: 'test'
+ ...
+# Subtest: rule order is observable: the continuation rule outranks failed: on an indented line
+ok 425 - rule order is observable: the continuation rule outranks failed: on an indented line
+ ---
+ duration_ms: 0.169417
+ type: 'test'
+ ...
+# Subtest: a severity word mid-line is not a prefix, so the line stays plain
+ok 426 - a severity word mid-line is not a prefix, so the line stays plain
+ ---
+ duration_ms: 0.237881
+ type: 'test'
+ ...
+# Subtest: errors are red and warnings are yellow
+ok 427 - errors are red and warnings are yellow
+ ---
+ duration_ms: 0.169368
+ type: 'test'
+ ...
+# Subtest: the shouted and thrown spellings classify the same as the lowercase ones
+ok 428 - the shouted and thrown spellings classify the same as the lowercase ones
+ ---
+ duration_ms: 0.11168
+ type: 'test'
+ ...
+# Subtest: note, tip and usage are dim, not coloured
+ok 429 - note, tip and usage are dim, not coloured
+ ---
+ duration_ms: 0.104088
+ type: 'test'
+ ...
+# Subtest: a `hyp :` diagnostic is red, at every depth of subcommand
+ok 430 - a `hyp :` diagnostic is red, at every depth of subcommand
+ ---
+ duration_ms: 0.20448
+ type: 'test'
+ ...
+# Subtest: a cancellation is not an error
+ok 431 - a cancellation is not an error
+ ---
+ duration_ms: 0.114333
+ type: 'test'
+ ...
+# Subtest: `... failed:` diagnostics without a hyp prefix are red too
+ok 432 - `... failed:` diagnostics without a hyp prefix are red too
+ ---
+ duration_ms: 0.379667
+ type: 'test'
+ ...
+# Subtest: only the prefix is painted, never the message body
+ok 433 - only the prefix is painted, never the message body
+ ---
+ duration_ms: 0.38814
+ type: 'test'
+ ...
+# Subtest: continuation lines keep the severity of the line above by staying plain
+ok 434 - continuation lines keep the severity of the line above by staying plain
+ ---
+ duration_ms: 0.183038
+ type: 'test'
+ ...
+# Subtest: unclassified output is left alone rather than guessed at
+ok 435 - unclassified output is left alone rather than guessed at
+ ---
+ duration_ms: 0.103197
+ type: 'test'
+ ...
+# Subtest: every line of a multi-line chunk is classified
+ok 436 - every line of a multi-line chunk is classified
+ ---
+ duration_ms: 0.147253
+ type: 'test'
+ ...
+# Subtest: a chunk that resumes mid-line is not re-classified as a new diagnostic
+ok 437 - a chunk that resumes mid-line is not re-classified as a new diagnostic
+ ---
+ duration_ms: 0.086481
+ type: 'test'
+ ...
+# Subtest: a non-TTY stream is returned untouched, byte for byte
+ok 438 - a non-TTY stream is returned untouched, byte for byte
+ ---
+ duration_ms: 0.184581
+ type: 'test'
+ ...
+# Subtest: NO_COLOR wins over a TTY
+ok 439 - NO_COLOR wins over a TTY
+ ---
+ duration_ms: 0.111069
+ type: 'test'
+ ...
+# Subtest: a TTY stream is wrapped, and paints across separate writes
+ok 440 - a TTY stream is wrapped, and paints across separate writes
+ ---
+ duration_ms: 0.167425
+ type: 'test'
+ ...
+# Subtest: a write that does not end in a newline leaves the next write mid-line
+ok 441 - a write that does not end in a newline leaves the next write mid-line
+ ---
+ duration_ms: 0.130228
+ type: 'test'
+ ...
+# Subtest: the wrap preserves the rest of the stream surface
+ok 442 - the wrap preserves the rest of the stream surface
+ ---
+ duration_ms: 0.0921
+ type: 'test'
+ ...
+# Subtest: non-string chunks pass through unexamined
+ok 443 - non-string chunks pass through unexamined
+ ---
+ duration_ms: 0.109356
+ type: 'test'
+ ...
+# Subtest: reduce: ctrl+c cancels any active state
+ok 444 - reduce: ctrl+c cancels any active state
+ ---
+ duration_ms: 1.277201
+ type: 'test'
+ ...
+# Subtest: reduce: escape cancels any active state
+ok 445 - reduce: escape cancels any active state
+ ---
+ duration_ms: 0.214816
+ type: 'test'
+ ...
+# Subtest: reduce: terminal states ignore further input
+ok 446 - reduce: terminal states ignore further input
+ ---
+ duration_ms: 0.140864
+ type: 'test'
+ ...
+# Subtest: multiselect: arrow down moves cursor through the submit row and wraps
+ok 447 - multiselect: arrow down moves cursor through the submit row and wraps
+ ---
+ duration_ms: 0.240996
+ type: 'test'
+ ...
+# Subtest: multiselect: arrow up wraps to the submit row
+ok 448 - multiselect: arrow up wraps to the submit row
+ ---
+ duration_ms: 0.204601
+ type: 'test'
+ ...
+# Subtest: multiselect: j and k are aliases for down and up
+ok 449 - multiselect: j and k are aliases for down and up
+ ---
+ duration_ms: 0.142737
+ type: 'test'
+ ...
+# Subtest: multiselect: space toggles current option
+ok 450 - multiselect: space toggles current option
+ ---
+ duration_ms: 0.207005
+ type: 'test'
+ ...
+# Subtest: multiselect: a toggles all on then all off
+ok 451 - multiselect: a toggles all on then all off
+ ---
+ duration_ms: 0.766745
+ type: 'test'
+ ...
+# Subtest: multiselect: a with mixed selection checks all
+ok 452 - multiselect: a with mixed selection checks all
+ ---
+ duration_ms: 0.356993
+ type: 'test'
+ ...
+# Subtest: multiselect: space on a disabled row is a no-op
+ok 453 - multiselect: space on a disabled row is a no-op
+ ---
+ duration_ms: 0.372687
+ type: 'test'
+ ...
+# Subtest: multiselect: a leaves disabled rows fixed and toggles the rest
+ok 454 - multiselect: a leaves disabled rows fixed and toggles the rest
+ ---
+ duration_ms: 0.235188
+ type: 'test'
+ ...
+# Subtest: multiselect: digit keys 1-3 jump to in-range index
+ok 455 - multiselect: digit keys 1-3 jump to in-range index
+ ---
+ duration_ms: 0.227847
+ type: 'test'
+ ...
+# Subtest: multiselect: digit out of range is a no-op
+ok 456 - multiselect: digit out of range is a no-op
+ ---
+ duration_ms: 0.118961
+ type: 'test'
+ ...
+# Subtest: multiselect: space on the submit row resolves
+ok 457 - multiselect: space on the submit row resolves
+ ---
+ duration_ms: 0.174535
+ type: 'test'
+ ...
+# Subtest: multiselect: space on the submit row below bounds.min sets error and stays active
+ok 458 - multiselect: space on the submit row below bounds.min sets error and stays active
+ ---
+ duration_ms: 1.366648
+ type: 'test'
+ ...
+# Subtest: multiselect: digit keys never jump to the submit row
+ok 459 - multiselect: digit keys never jump to the submit row
+ ---
+ duration_ms: 0.094143
+ type: 'test'
+ ...
+# Subtest: multiselect: enter without bounds resolves
+ok 460 - multiselect: enter without bounds resolves
+ ---
+ duration_ms: 0.083346
+ type: 'test'
+ ...
+# Subtest: multiselect: enter below bounds.min sets error and stays active
+ok 461 - multiselect: enter below bounds.min sets error and stays active
+ ---
+ duration_ms: 0.088265
+ type: 'test'
+ ...
+# Subtest: multiselect: enter above bounds.max sets error and stays active
+ok 462 - multiselect: enter above bounds.max sets error and stays active
+ ---
+ duration_ms: 0.119972
+ type: 'test'
+ ...
+# Subtest: multiselect: bounds error clears on next cursor move
+ok 463 - multiselect: bounds error clears on next cursor move
+ ---
+ duration_ms: 0.141485
+ type: 'test'
+ ...
+# Subtest: multiselect: empty options + enter with bounds.min still rejects
+ok 464 - multiselect: empty options + enter with bounds.min still rejects
+ ---
+ duration_ms: 0.09839
+ type: 'test'
+ ...
+# Subtest: select: cursor moves and wraps; enter resolves
+ok 465 - select: cursor moves and wraps; enter resolves
+ ---
+ duration_ms: 0.117289
+ type: 'test'
+ ...
+# Subtest: select: space does not toggle (single-select has no toggle)
+ok 466 - select: space does not toggle (single-select has no toggle)
+ ---
+ duration_ms: 0.074473
+ type: 'test'
+ ...
+# Subtest: text: printable characters append to value
+ok 467 - text: printable characters append to value
+ ---
+ duration_ms: 0.139352
+ type: 'test'
+ ...
+# Subtest: text: backspace removes the last char and stops at empty
+ok 468 - text: backspace removes the last char and stops at empty
+ ---
+ duration_ms: 0.093642
+ type: 'test'
+ ...
+# Subtest: text: control characters are ignored as input
+ok 469 - text: control characters are ignored as input
+ ---
+ duration_ms: 0.083597
+ type: 'test'
+ ...
+# Subtest: text: enter without validate resolves with current value
+ok 470 - text: enter without validate resolves with current value
+ ---
+ duration_ms: 0.086932
+ type: 'test'
+ ...
+# Subtest: text: enter on empty value applies default
+ok 471 - text: enter on empty value applies default
+ ---
+ duration_ms: 0.085129
+ type: 'test'
+ ...
+# Subtest: text: enter when validate rejects sets error and stays active
+ok 472 - text: enter when validate rejects sets error and stays active
+ ---
+ duration_ms: 0.098229
+ type: 'test'
+ ...
+# Subtest: text: mask flag does not change the value field; only render relies on it
+ok 473 - text: mask flag does not change the value field; only render relies on it
+ ---
+ duration_ms: 0.083076
+ type: 'test'
+ ...
+# Subtest: non-TTY stdin rejects multiselect with the documented error
+ok 474 - non-TTY stdin rejects multiselect with the documented error
+ ---
+ duration_ms: 1.338915
+ type: 'test'
+ ...
+# Subtest: non-TTY stdin rejects select with the documented error
+ok 475 - non-TTY stdin rejects select with the documented error
+ ---
+ duration_ms: 0.459639
+ type: 'test'
+ ...
+# Subtest: non-TTY stdin rejects text with the documented error
+ok 476 - non-TTY stdin rejects text with the documented error
+ ---
+ duration_ms: 0.327998
+ type: 'test'
+ ...
+# Subtest: HYP_NO_TUI=1 forces the same TTY error even for fake-TTY streams
+ok 477 - HYP_NO_TUI=1 forces the same TTY error even for fake-TTY streams
+ ---
+ duration_ms: 1.545309
+ type: 'test'
+ ...
+# Subtest: injected env.HYP_NO_TUI=1 forces the TTY error even when process.env is clean
+ok 478 - injected env.HYP_NO_TUI=1 forces the TTY error even when process.env is clean
+ ---
+ duration_ms: 0.325925
+ type: 'test'
+ ...
+# Subtest: package export "hypaware/tui" resolves to the same module
+ok 479 - package export "hypaware/tui" resolves to the same module
+ ---
+ duration_ms: 0.678942
+ type: 'test'
+ ...
+# Subtest: multiselect: NO_COLOR frame contains no SGR escapes
+ok 480 - multiselect: NO_COLOR frame contains no SGR escapes
+ ---
+ duration_ms: 1.012529
+ type: 'test'
+ ...
+# Subtest: multiselect: colored frame contains at least one SGR escape
+ok 481 - multiselect: colored frame contains at least one SGR escape
+ ---
+ duration_ms: 0.181115
+ type: 'test'
+ ...
+# Subtest: multiselect: cursor row uses pointer ">", others use space
+ok 482 - multiselect: cursor row uses pointer ">", others use space
+ ---
+ duration_ms: 0.147685
+ type: 'test'
+ ...
+# Subtest: multiselect: a disabled row renders its label and checkbox
+ok 483 - multiselect: a disabled row renders its label and checkbox
+ ---
+ duration_ms: 0.115396
+ type: 'test'
+ ...
+# Subtest: multiselect: a disabled row under the cursor renders dim, not the cyan cursor color
+ok 484 - multiselect: a disabled row under the cursor renders dim, not the cyan cursor color
+ ---
+ duration_ms: 0.143839
+ type: 'test'
+ ...
+# Subtest: multiselect: summary lines appear under labels when set
+ok 485 - multiselect: summary lines appear under labels when set
+ ---
+ duration_ms: 0.096446
+ type: 'test'
+ ...
+# Subtest: multiselect: empty options renders title + hint + submit row without option rows
+ok 486 - multiselect: empty options renders title + hint + submit row without option rows
+ ---
+ duration_ms: 0.185452
+ type: 'test'
+ ...
+# Subtest: multiselect: submit row renders below the options, pointer-free when not focused
+ok 487 - multiselect: submit row renders below the options, pointer-free when not focused
+ ---
+ duration_ms: 0.133123
+ type: 'test'
+ ...
+# Subtest: multiselect: submit row carries the pointer when the cursor is on it
+ok 488 - multiselect: submit row carries the pointer when the cursor is on it
+ ---
+ duration_ms: 0.271172
+ type: 'test'
+ ...
+# Subtest: multiselect: error line is included when set
+ok 489 - multiselect: error line is included when set
+ ---
+ duration_ms: 0.280536
+ type: 'test'
+ ...
+# Subtest: multiselect: frame ends with a single trailing newline
+ok 490 - multiselect: frame ends with a single trailing newline
+ ---
+ duration_ms: 0.131109
+ type: 'test'
+ ...
+# Subtest: select: renders pointer-and-label rows
+ok 491 - select: renders pointer-and-label rows
+ ---
+ duration_ms: 0.152562
+ type: 'test'
+ ...
+# Subtest: text: render shows "> " followed by the value
+ok 492 - text: render shows "> " followed by the value
+ ---
+ duration_ms: 0.113792
+ type: 'test'
+ ...
+# Subtest: text: render masks value when mask is true
+ok 493 - text: render masks value when mask is true
+ ---
+ duration_ms: 0.075484
+ type: 'test'
+ ...
+# Subtest: text: default hint shown when value is empty
+ok 494 - text: default hint shown when value is empty
+ ---
+ duration_ms: 0.937896
+ type: 'test'
+ ...
+# Subtest: text: default hint disappears once value is typed
+ok 495 - text: default hint disappears once value is typed
+ ---
+ duration_ms: 0.07206
+ type: 'test'
+ ...
+# Subtest: render: hint override replaces default hint line
+ok 496 - render: hint override replaces default hint line
+ ---
+ duration_ms: 0.086241
+ type: 'test'
+ ...
+# Subtest: render: items render verbatim between the title and the hint
+ok 497 - render: items render verbatim between the title and the hint
+ ---
+ duration_ms: 0.076006
+ type: 'test'
+ ...
+# Subtest: box: frame is wrapped in a border, every row padded to one width
+ok 498 - box: frame is wrapped in a border, every row padded to one width
+ ---
+ duration_ms: 0.245233
+ type: 'test'
+ ...
+# Subtest: box: a frame wider than the terminal is dropped, not soft-wrapped
+ok 499 - box: a frame wider than the terminal is dropped, not soft-wrapped
+ ---
+ duration_ms: 0.102746
+ type: 'test'
+ ...
+# Subtest: box: border width measures visible columns, not style escapes
+ok 500 - box: border width measures visible columns, not style escapes
+ ---
+ duration_ms: 0.117188
+ type: 'test'
+ ...
+# Subtest: box: an unboxed state renders exactly as it did before the field existed
+ok 501 - box: an unboxed state renders exactly as it did before the field existed
+ ---
+ duration_ms: 0.116948
+ type: 'test'
+ ...
+# Subtest: render: a state without items renders exactly as it does today
+ok 502 - render: a state without items renders exactly as it does today
+ ---
+ duration_ms: 0.06595
+ type: 'test'
+ ...
+# Subtest: runtime: multiselect happy path returns selected values in order
+ok 503 - runtime: multiselect happy path returns selected values in order
+ ---
+ duration_ms: 8.348108
+ type: 'test'
+ ...
+# Subtest: runtime: multiselect cancel via ctrl+c throws PromptCancelledError
+ok 504 - runtime: multiselect cancel via ctrl+c throws PromptCancelledError
+ ---
+ duration_ms: 2.355019
+ type: 'test'
+ ...
+# Subtest: runtime: escape on an allowBack multiselect throws PromptBackRequestedError
+ok 505 - runtime: escape on an allowBack multiselect throws PromptBackRequestedError
+ ---
+ duration_ms: 501.343883
+ type: 'test'
+ ...
+# Subtest: runtime: escape on an allowBack select throws PromptBackRequestedError
+ok 506 - runtime: escape on an allowBack select throws PromptBackRequestedError
+ ---
+ duration_ms: 503.920369
+ type: 'test'
+ ...
+# Subtest: runtime: ctrl+c on an allowBack prompt still cancels
+ok 507 - runtime: ctrl+c on an allowBack prompt still cancels
+ ---
+ duration_ms: 0.939348
+ type: 'test'
+ ...
+# Subtest: runtime: escape on a prompt without allowBack still cancels
+ok 508 - runtime: escape on a prompt without allowBack still cancels
+ ---
+ duration_ms: 500.925848
+ type: 'test'
+ ...
+# Subtest: runtime: prompt cancellation predicate recognizes PromptCancelledError
+ok 509 - runtime: prompt cancellation predicate recognizes PromptCancelledError
+ ---
+ duration_ms: 0.242939
+ type: 'test'
+ ...
+# Subtest: runtime: multiselect bounds rejection retains active state until satisfied
+ok 510 - runtime: multiselect bounds rejection retains active state until satisfied
+ ---
+ duration_ms: 1.074653
+ type: 'test'
+ ...
+# Subtest: runtime: select returns the value at the cursor on enter
+ok 511 - runtime: select returns the value at the cursor on enter
+ ---
+ duration_ms: 0.821218
+ type: 'test'
+ ...
+# Subtest: runtime: select cancel throws PromptCancelledError
+ok 512 - runtime: select cancel throws PromptCancelledError
+ ---
+ duration_ms: 501.843423
+ type: 'test'
+ ...
+# Subtest: runtime: text returns the typed buffer on enter
+ok 513 - runtime: text returns the typed buffer on enter
+ ---
+ duration_ms: 0.920309
+ type: 'test'
+ ...
+# Subtest: runtime: text empty + default returns default on enter
+ok 514 - runtime: text empty + default returns default on enter
+ ---
+ duration_ms: 0.390654
+ type: 'test'
+ ...
+# Subtest: runtime: text validate-rejected enter stays active until valid
+ok 515 - runtime: text validate-rejected enter stays active until valid
+ ---
+ duration_ms: 0.592761
+ type: 'test'
+ ...
+# Subtest: runtime: cursor-hide is written on entry and cursor-show on resolve
+ok 516 - runtime: cursor-hide is written on entry and cursor-show on resolve
+ ---
+ duration_ms: 0.46748
+ type: 'test'
+ ...
+# Subtest: runtime: cursor-show is written even on cancel
+ok 517 - runtime: cursor-show is written even on cancel
+ ---
+ duration_ms: 0.463354
+ type: 'test'
+ ...
+# Subtest: runtime: cleanup restores paused stdin after prompt completion
+ok 518 - runtime: cleanup restores paused stdin after prompt completion
+ ---
+ duration_ms: 0.36203
+ type: 'test'
+ ...
+# Subtest: runtime: cleanup preserves previously flowing stdin after prompt completion
+ok 519 - runtime: cleanup preserves previously flowing stdin after prompt completion
+ ---
+ duration_ms: 1.046841
+ type: 'test'
+ ...
+# Subtest: runtime: render failures during keypress reject and clean up
+ok 520 - runtime: render failures during keypress reject and clean up
+ ---
+ duration_ms: 0.431335
+ type: 'test'
+ ...
+# Subtest: countPhysicalRows: counts each logical line once when nothing wraps
+ok 521 - countPhysicalRows: counts each logical line once when nothing wraps
+ ---
+ duration_ms: 0.073662
+ type: 'test'
+ ...
+# Subtest: countPhysicalRows: empty lines still occupy one row
+ok 522 - countPhysicalRows: empty lines still occupy one row
+ ---
+ duration_ms: 0.042224
+ type: 'test'
+ ...
+# Subtest: countPhysicalRows: a line wider than the terminal counts its wrapped rows
+ok 523 - countPhysicalRows: a line wider than the terminal counts its wrapped rows
+ ---
+ duration_ms: 0.0528
+ type: 'test'
+ ...
+# Subtest: countPhysicalRows: a line exactly the terminal width stays one row
+ok 524 - countPhysicalRows: a line exactly the terminal width stays one row
+ ---
+ duration_ms: 0.043436
+ type: 'test'
+ ...
+# Subtest: countPhysicalRows: ANSI style codes do not inflate the width
+ok 525 - countPhysicalRows: ANSI style codes do not inflate the width
+ ---
+ duration_ms: 0.040972
+ type: 'test'
+ ...
+# Subtest: countPhysicalRows: defaults to 80 columns for non-TTY widths
+ok 526 - countPhysicalRows: defaults to 80 columns for non-TTY widths
+ ---
+ duration_ms: 0.035424
+ type: 'test'
+ ...
+# Subtest: runtime: redraw moves up by physical (wrapped) rows on a narrow terminal
+ok 527 - runtime: redraw moves up by physical (wrapped) rows on a narrow terminal
+ ---
+ duration_ms: 1.578629
+ type: 'test'
+ ...
+# Subtest: runtime: clearOnResolve erases the settled frame on resolve
+ok 528 - runtime: clearOnResolve erases the settled frame on resolve
+ ---
+ duration_ms: 0.691481
+ type: 'test'
+ ...
+# Subtest: runtime: clearOnResolve erases the settled frame on cancel
+ok 529 - runtime: clearOnResolve erases the settled frame on cancel
+ ---
+ duration_ms: 0.605661
+ type: 'test'
+ ...
+# Subtest: runtime: without clearOnResolve the settled frame is left in place
+ok 530 - runtime: without clearOnResolve the settled frame is left in place
+ ---
+ duration_ms: 0.335811
+ type: 'test'
+ ...
+# Subtest: runtime: overlapping prompts are rejected
+ok 531 - runtime: overlapping prompts are rejected
+ ---
+ duration_ms: 0.403994
+ type: 'test'
+ ...
+# Subtest: reduce: escape settles an allowBack prompt as backed, not cancelled
+ok 532 - reduce: escape settles an allowBack prompt as backed, not cancelled
+ ---
+ duration_ms: 1.156979
+ type: 'test'
+ ...
+# Subtest: reduce: without allowBack escape keeps meaning cancel
+ok 533 - reduce: without allowBack escape keeps meaning cancel
+ ---
+ duration_ms: 0.158681
+ type: 'test'
+ ...
+# Subtest: reduce: ctrl+c cancels even on an allowBack prompt
+ok 534 - reduce: ctrl+c cancels even on an allowBack prompt
+ ---
+ duration_ms: 0.153162
+ type: 'test'
+ ...
+# Subtest: render: the default hint tells the truth about escape
+ok 535 - render: the default hint tells the truth about escape
+ ---
+ duration_ms: 0.474922
+ type: 'test'
+ ...
+# Subtest: isPromptBackError recognises the error and name-preserving copies
+ok 536 - isPromptBackError recognises the error and name-preserving copies
+ ---
+ duration_ms: 0.200225
+ type: 'test'
+ ...
+# Subtest: runWizardFork: with allowBack the legacy prompt accepts b and resolves back
+ok 537 - runWizardFork: with allowBack the legacy prompt accepts b and resolves back
+ ---
+ duration_ms: 3.564889
+ type: 'test'
+ ...
+# Subtest: runWizardFork: without allowBack a stray b quits, and the prompt never mentions back
+ok 538 - runWizardFork: without allowBack a stray b quits, and the prompt never mentions back
+ ---
+ duration_ms: 1.298193
+ type: 'test'
+ ...
+# Subtest: runWizardPick: back at the gate propagates only when the orchestrator allowed it
+ok 539 - runWizardPick: back at the gate propagates only when the orchestrator allowed it
+ ---
+ duration_ms: 38.595751
+ type: 'test'
+ ...
+# Subtest: runWizardPick: back at the menu returns to the gate, not out of the lane
+ok 540 - runWizardPick: back at the menu returns to the gate, not out of the lane
+ ---
+ duration_ms: 10.321247
+ type: 'test'
+ ...
+# Subtest: runWizardPick: without a gate and without allowBack the menu offers no back
+ok 541 - runWizardPick: without a gate and without allowBack the menu offers no back
+ ---
+ duration_ms: 13.271481
+ type: 'test'
+ ...
+# Subtest: runWizardPick: initialSelection seeds the boxes and skips detection
+ok 542 - runWizardPick: initialSelection seeds the boxes and skips detection
+ ---
+ duration_ms: 9.624699
+ type: 'test'
+ ...
+# Subtest: runWizardSyncScope: back at the gate propagates and leaves the store unwritten
+ok 543 - runWizardSyncScope: back at the gate propagates and leaves the store unwritten
+ ---
+ duration_ms: 1.136347
+ type: 'test'
+ ...
+# Subtest: runWizardSyncScope: back at the menu returns to the gate
+ok 544 - runWizardSyncScope: back at the menu returns to the gate
+ ---
+ duration_ms: 1.143448
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a back from pick re-presents the fork
+ok 545 - runInitWizard: a back from pick re-presents the fork
+ ---
+ duration_ms: 1.8491
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a back from pick re-presents the express gate, not the fork
+ok 546 - runInitWizard: a back from pick re-presents the express gate, not the fork
+ ---
+ duration_ms: 10.174655
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a back from sync re-presents pick without re-asking the express gate
+ok 547 - runInitWizard: a back from sync re-presents pick without re-asking the express gate
+ ---
+ duration_ms: 12.708356
+ type: 'test'
+ ...
+# Subtest: runInitWizard: with nothing to accept there is no gate, and pick backs straight to the fork
+ok 548 - runInitWizard: with nothing to accept there is no gate, and pick backs straight to the fork
+ ---
+ duration_ms: 15.436972
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a back from folders skips a sync lane that asked nothing and reaches pick
+ok 549 - runInitWizard: a back from folders skips a sync lane that asked nothing and reaches pick
+ ---
+ duration_ms: 4.520901
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a back from folders re-presents a sync lane that did ask
+ok 550 - runInitWizard: a back from folders re-presents a sync lane that did ask
+ ---
+ duration_ms: 5.361098
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a back from pick reaches the fork when the confirmed picks leave the gate empty
+ok 551 - runInitWizard: a back from pick reaches the fork when the confirmed picks leave the gate empty
+ ---
+ duration_ms: 9.976173
+ type: 'test'
+ ...
+# Subtest: runInitWizard: back past a completed join reuses it instead of re-running the login
+ok 552 - runInitWizard: back past a completed join reuses it instead of re-running the login
+ ---
+ duration_ms: 16.374067
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a back from sync re-runs pick seeded with the confirmed selection
+ok 553 - runInitWizard: a back from sync re-runs pick seeded with the confirmed selection
+ ---
+ duration_ms: 15.591969
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a back from the fork re-presents the returning gate
+ok 554 - runInitWizard: a back from the fork re-presents the returning gate
+ ---
+ duration_ms: 4.687084
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a first-run fork has no gate screen and offers no back
+ok 555 - runInitWizard: a first-run fork has no gate screen and offers no back
+ ---
+ duration_ms: 4.385375
+ type: 'test'
+ ...
+# Subtest: runInitWizard: interactive pick lanes are offered back; non-interactive are not
+ok 556 - runInitWizard: interactive pick lanes are offered back; non-interactive are not
+ ---
+ duration_ms: 6.372536
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a join that locked nothing still counts as enrolled when backing to local
+ok 557 - runInitWizard: a join that locked nothing still counts as enrolled when backing to local
+ ---
+ duration_ms: 10.174114
+ type: 'test'
+ ...
+# Subtest: runInitWizard: disconnecting after a join that locked nothing drops the sync lane
+ok 558 - runInitWizard: disconnecting after a join that locked nothing drops the sync lane
+ ---
+ duration_ms: 7.025418
+ type: 'test'
+ ...
+# Subtest: runInitWizard: ctrl+c at the disconnect question cancels the run instead of re-presenting the fork
+ok 559 - runInitWizard: ctrl+c at the disconnect question cancels the run instead of re-presenting the fork
+ ---
+ duration_ms: 4.343051
+ type: 'test'
+ ...
+# Subtest: runInitWizard end-to-end: join, back to the fork, local, and the enrolled machine is still asked what syncs
+ok 560 - runInitWizard end-to-end: join, back to the fork, local, and the enrolled machine is still asked what syncs
+ ---
+ duration_ms: 13.29678
+ type: 'test'
+ ...
+# Subtest: runConfigurePhase: a zero exit keeps the source and records ok
+ok 561 - runConfigurePhase: a zero exit keeps the source and records ok
+ ---
+ duration_ms: 2.547523
+ type: 'test'
+ ...
+# Subtest: runConfigurePhase: only needs_setup descriptors with a configure_command run
+ok 562 - runConfigurePhase: only needs_setup descriptors with a configure_command run
+ ---
+ duration_ms: 0.4934
+ type: 'test'
+ ...
+# Subtest: runConfigurePhase: a non-zero exit drops the source and prints the catch-up hint
+ok 563 - runConfigurePhase: a non-zero exit drops the source and prints the catch-up hint
+ ---
+ duration_ms: 0.422632
+ type: 'test'
+ ...
+# Subtest: runConfigurePhase: a drop does not abort the phase; later sources still run
+ok 564 - runConfigurePhase: a drop does not abort the phase; later sources still run
+ ---
+ duration_ms: 0.564789
+ type: 'test'
+ ...
+# Subtest: runConfigurePhase: a thrown configure drops the source without rethrowing
+ok 565 - runConfigurePhase: a thrown configure drops the source without rethrowing
+ ---
+ duration_ms: 0.464766
+ type: 'test'
+ ...
+# Subtest: runConfigurePhase: --print-commands threads onto the invoked command argv
+ok 566 - runConfigurePhase: --print-commands threads onto the invoked command argv
+ ---
+ duration_ms: 0.275058
+ type: 'test'
+ ...
+# Subtest: runConfigurePhase: never runs off a non-interactive opts.picks path
+ok 567 - runConfigurePhase: never runs off a non-interactive opts.picks path
+ ---
+ duration_ms: 0.24334
+ type: 'test'
+ ...
+# Subtest: a needs_setup row the existing config already composed is skipped, not re-run
+ok 568 - a needs_setup row the existing config already composed is skipped, not re-run
+ ---
+ duration_ms: 0.184099
+ type: 'test'
+ ...
+# Subtest: a newly picked needs_setup row still runs when another row is carried
+ok 569 - a newly picked needs_setup row still runs when another row is carried
+ ---
+ duration_ms: 0.556846
+ type: 'test'
+ ...
+# Subtest: the gate lists the rows it will record, and the accept row names the act on them
+ok 570 - the gate lists the rows it will record, and the accept row names the act on them
+ ---
+ duration_ms: 4.469454
+ type: 'test'
+ ...
+# Subtest: the gate claims a server only when told it has one
+ok 571 - the gate claims a server only when told it has one
+ ---
+ duration_ms: 3.295258
+ type: 'test'
+ ...
+# Subtest: declining runs the lanes as they are; back and cancel are their own answers
+ok 572 - declining runs the lanes as they are; back and cancel are their own answers
+ ---
+ duration_ms: 7.341338
+ type: 'test'
+ ...
+# Subtest: the sync lane auto-accepts by narrating the same split and writing the same store
+ok 573 - the sync lane auto-accepts by narrating the same split and writing the same store
+ ---
+ duration_ms: 8.124358
+ type: 'test'
+ ...
+# Subtest: the new-folder lane auto-accepts to the default and records it
+ok 574 - the new-folder lane auto-accepts to the default and records it
+ ---
+ duration_ms: 1.791112
+ type: 'test'
+ ...
+# Subtest: narrateAcceptedGate prints the gate title and its items verbatim, led by a blank line
+ok 575 - narrateAcceptedGate prints the gate title and its items verbatim, led by a blank line
+ ---
+ duration_ms: 0.119522
+ type: 'test'
+ ...
+# Subtest: resolveOnPath: finds an executable on PATH, ignores a non-executable match
+ok 576 - resolveOnPath: finds an executable on PATH, ignores a non-executable match
+ ---
+ duration_ms: 6.372476
+ type: 'test'
+ ...
+# Subtest: resolveLaunchers: picked and resolvable only; a launch-less client is never offered
+ok 577 - resolveLaunchers: picked and resolvable only; a launch-less client is never offered
+ ---
+ duration_ms: 0.935261
+ type: 'test'
+ ...
+# Subtest: resolveLaunchers: an unpicked client is not offered even when it resolves
+ok 578 - resolveLaunchers: an unpicked client is not offered even when it resolves
+ ---
+ duration_ms: 0.167144
+ type: 'test'
+ ...
+# Subtest: runWizardFirstAsk: a pick spawns the client with the question as argv
+ok 579 - runWizardFirstAsk: a pick spawns the client with the question as argv
+ ---
+ duration_ms: 1.977636
+ type: 'test'
+ ...
+# Subtest: runWizardFirstAsk: every `{prompt}` slot in a manifest arg template is filled
+ok 580 - runWizardFirstAsk: every `{prompt}` slot in a manifest arg template is filled
+ ---
+ duration_ms: 0.404785
+ type: 'test'
+ ...
+# Subtest: runWizardFirstAsk: no launchable client prints the list and launches nothing
+ok 581 - runWizardFirstAsk: no launchable client prints the list and launches nothing
+ ---
+ duration_ms: 0.503665
+ type: 'test'
+ ...
+# Subtest: runWizardFirstAsk: the ask is framed, so it reads as a screen and not as more output
+ok 582 - runWizardFirstAsk: the ask is framed, so it reads as a screen and not as more output
+ ---
+ duration_ms: 0.443625
+ type: 'test'
+ ...
+# Subtest: runWizardFirstAsk: an empty cache suppresses the launch and says why
+ok 583 - runWizardFirstAsk: an empty cache suppresses the launch and says why
+ ---
+ duration_ms: 0.425016
+ type: 'test'
+ ...
+# Subtest: runWizardFirstAsk: an unknown row count never withholds the offer
+ok 584 - runWizardFirstAsk: an unknown row count never withholds the offer
+ ---
+ duration_ms: 0.529705
+ type: 'test'
+ ...
+# Subtest: runWizardFirstAsk: "Not now" and a cancelled prompt both decline, and keep the list
+ok 585 - runWizardFirstAsk: "Not now" and a cancelled prompt both decline, and keep the list
+ ---
+ duration_ms: 0.89508
+ type: 'test'
+ ...
+# Subtest: runWizardFirstAsk: a non-interactive run prints the list and never prompts
+ok 586 - runWizardFirstAsk: a non-interactive run prints the list and never prompts
+ ---
+ duration_ms: 0.474801
+ type: 'test'
+ ...
+# Subtest: runWizardFirstAsk: a spawn failure degrades to the list, never a throw
+ok 587 - runWizardFirstAsk: a spawn failure degrades to the list, never a throw
+ ---
+ duration_ms: 0.469744
+ type: 'test'
+ ...
+# Subtest: runWizardFirstAsk: an unforeseen error is contained
+ok 588 - runWizardFirstAsk: an unforeseen error is contained
+ ---
+ duration_ms: 0.359677
+ type: 'test'
+ ...
+# Subtest: runWizardFirstAsk: two launchable clients ask which one answers
+ok 589 - runWizardFirstAsk: two launchable clients ask which one answers
+ ---
+ duration_ms: 0.380979
+ type: 'test'
+ ...
+# Subtest: the suggested prompts are a short list of distinct, routable questions
+ok 590 - the suggested prompts are a short list of distinct, routable questions
+ ---
+ duration_ms: 0.351454
+ type: 'test'
+ ...
+# Subtest: every suggested label fits a narrow terminal without wrapping
+ok 591 - every suggested label fits a narrow terminal without wrapping
+ ---
+ duration_ms: 0.138471
+ type: 'test'
+ ...
+# Subtest: runWizardFirstLook: writes the two setup sections, names the fuller command, reports row counts
+ok 592 - runWizardFirstLook: writes the two setup sections, names the fuller command, reports row counts
+ ---
+ duration_ms: 3.960459
+ type: 'test'
+ ...
+# Subtest: runWizardFirstLook: an expired deadline keeps the sections that finished
+ok 593 - runWizardFirstLook: an expired deadline keeps the sections that finished
+ ---
+ duration_ms: 123.35644
+ type: 'test'
+ ...
+# Subtest: runWizardFirstLook: a slow cache skips within budget and says what to run
+ok 594 - runWizardFirstLook: a slow cache skips within budget and says what to run
+ ---
+ duration_ms: 40.055961
+ type: 'test'
+ ...
+# Subtest: runWizardFirstLook: a cache inside the budget still renders
+ok 595 - runWizardFirstLook: a cache inside the budget still renders
+ ---
+ duration_ms: 18.696297
+ type: 'test'
+ ...
+# Subtest: runWizardFirstLook: an unregistered dataset skips silently
+ok 596 - runWizardFirstLook: an unregistered dataset skips silently
+ ---
+ duration_ms: 0.555344
+ type: 'test'
+ ...
+# Subtest: runWizardFirstLook: a query failure degrades to a skipped step, not a throw
+ok 597 - runWizardFirstLook: a query failure degrades to a skipped step, not a throw
+ ---
+ duration_ms: 0.4016
+ type: 'test'
+ ...
+# Subtest: runWizardFirstLook: a synchronous write failure cannot escape and fail a finished install
+ok 598 - runWizardFirstLook: a synchronous write failure cannot escape and fail a finished install
+ ---
+ duration_ms: 0.56592
+ type: 'test'
+ ...
+# Subtest: runWizardFirstLook: a render failure is contained too
+ok 599 - runWizardFirstLook: a render failure is contained too
+ ---
+ duration_ms: 0.460119
+ type: 'test'
+ ...
+# Subtest: firstLookNoticeSink: discloses withheld rows, drops the freshness line
+ok 600 - firstLookNoticeSink: discloses withheld rows, drops the freshness line
+ ---
+ duration_ms: 3.382671
+ type: 'test'
+ ...
+# Subtest: firstLookNoticeSink: a closed sink drops a late disclosure
+ok 601 - firstLookNoticeSink: a closed sink drops a late disclosure
+ ---
+ duration_ms: 0.415462
+ type: 'test'
+ ...
+# Subtest: runWizardFirstLook: no runner (no query registry) skips
+ok 602 - runWizardFirstLook: no runner (no query registry) skips
+ ---
+ duration_ms: 0.409242
+ type: 'test'
+ ...
+# Subtest: sync leads, is the bare-enter default, and both rows state their consequence
+ok 603 - sync leads, is the bare-enter default, and both rows state their consequence
+ ---
+ duration_ms: 12.41538
+ type: 'test'
+ ...
+# Subtest: choosing the ask buys the per-folder question and says how to undo it
+ok 604 - choosing the ask buys the per-folder question and says how to undo it
+ ---
+ duration_ms: 1.588865
+ type: 'test'
+ ...
+# Subtest: the answer is recorded even when it matches the default, so status can read it back
+ok 605 - the answer is recorded even when it matches the default, so status can read it back
+ ---
+ duration_ms: 1.906428
+ type: 'test'
+ ...
+# Subtest: a re-run defaults to the standing answer instead of resetting it
+ok 606 - a re-run defaults to the standing answer instead of resetting it
+ ---
+ duration_ms: 20.708404
+ type: 'test'
+ ...
+# Subtest: cancel and back leave the standing answer untouched
+ok 607 - cancel and back leave the standing answer untouched
+ ---
+ duration_ms: 3.673884
+ type: 'test'
+ ...
+# Subtest: autoAccept states the question and records the default without prompting (LLP 0201)
+ok 608 - autoAccept states the question and records the default without prompting (LLP 0201)
+ ---
+ duration_ms: 1.945527
+ type: 'test'
+ ...
+# Subtest: an unwritable preference warns and leaves the previous mode standing
+ok 609 - an unwritable preference warns and leaves the previous mode standing
+ ---
+ duration_ms: 3.148916
+ type: 'test'
+ ...
+# Subtest: the two options are exactly sync and ask
+ok 610 - the two options are exactly sync and ask
+ ---
+ duration_ms: 0.23005
+ type: 'test'
+ ...
+# Subtest: buildForkOptions: team, local, quit, in that order
+ok 611 - buildForkOptions: team, local, quit, in that order
+ ---
+ duration_ms: 1.312165
+ type: 'test'
+ ...
+# Subtest: runWizardFork: a bare enter takes the default (quit)
+ok 612 - runWizardFork: a bare enter takes the default (quit)
+ ---
+ duration_ms: 3.985938
+ type: 'test'
+ ...
+# Subtest: runWizardFork: choosing 1 forks to the team pathway
+ok 613 - runWizardFork: choosing 1 forks to the team pathway
+ ---
+ duration_ms: 1.182708
+ type: 'test'
+ ...
+# Subtest: runWizardFork: choosing 2 forks to the local pathway
+ok 614 - runWizardFork: choosing 2 forks to the local pathway
+ ---
+ duration_ms: 0.721306
+ type: 'test'
+ ...
+# Subtest: runWizardFork: an out-of-range answer quits rather than guessing
+ok 615 - runWizardFork: an out-of-range answer quits rather than guessing
+ ---
+ duration_ms: 0.55321
+ type: 'test'
+ ...
+# Subtest: legacyForkPrompt: matches runWizardFork on the same input (direct call, no TUI routing)
+ok 616 - legacyForkPrompt: matches runWizardFork on the same input (direct call, no TUI routing)
+ ---
+ duration_ms: 0.505789
+ type: 'test'
+ ...
+# Subtest: buildReturningGateOptions: one Reconfigure, the same three rows for every machine
+ok 617 - buildReturningGateOptions: one Reconfigure, the same three rows for every machine
+ ---
+ duration_ms: 0.147834
+ type: 'test'
+ ...
+# Subtest: evaluateReturningGate: no config yet is the first-run path, not the gate
+ok 618 - evaluateReturningGate: no config yet is the first-run path, not the gate
+ ---
+ duration_ms: 0.266445
+ type: 'test'
+ ...
+# Subtest: evaluateReturningGate: an invalid config is also first-run
+ok 619 - evaluateReturningGate: an invalid config is also first-run
+ ---
+ duration_ms: 0.376472
+ type: 'test'
+ ...
+# Subtest: evaluateReturningGate: a managed machine with an invalid config is still managed on the first-run path
+ok 620 - evaluateReturningGate: a managed machine with an invalid config is still managed on the first-run path
+ ---
+ duration_ms: 0.477987
+ type: 'test'
+ ...
+# Subtest: evaluateReturningGate: a managed machine with no config at all is still managed
+ok 621 - evaluateReturningGate: a managed machine with no config at all is still managed
+ ---
+ duration_ms: 0.226104
+ type: 'test'
+ ...
+# Subtest: evaluateReturningGate: managed machine, Reconfigure is the same row it is on a solo machine
+ok 622 - evaluateReturningGate: managed machine, Reconfigure is the same row it is on a solo machine
+ ---
+ duration_ms: 0.780135
+ type: 'test'
+ ...
+# Subtest: evaluateReturningGate: managed machine, a bare enter still quits (never reconfigures by accident)
+ok 623 - evaluateReturningGate: managed machine, a bare enter still quits (never reconfigures by accident)
+ ---
+ duration_ms: 0.456083
+ type: 'test'
+ ...
+# Subtest: evaluateReturningGate: solo machine, Reconfigure re-enters the full fork
+ok 624 - evaluateReturningGate: solo machine, Reconfigure re-enters the full fork
+ ---
+ duration_ms: 0.496574
+ type: 'test'
+ ...
+# Subtest: evaluateReturningGate: solo machine, a bare enter takes the default (quit)
+ok 625 - evaluateReturningGate: solo machine, a bare enter takes the default (quit)
+ ---
+ duration_ms: 0.667564
+ type: 'test'
+ ...
+# Subtest: evaluateReturningGate: either machine kind can still choose status
+ok 626 - evaluateReturningGate: either machine kind can still choose status
+ ---
+ duration_ms: 0.871414
+ type: 'test'
+ ...
+# Subtest: legacyReturningGatePrompt: default title is the plain "what would you like to do" prompt
+ok 627 - legacyReturningGatePrompt: default title is the plain "what would you like to do" prompt
+ ---
+ duration_ms: 0.333226
+ type: 'test'
+ ...
+# Subtest: runInitWizard: gate quit exits 0 without running any phase
+ok 628 - runInitWizard: gate quit exits 0 without running any phase
+ ---
+ duration_ms: 25.313254
+ type: 'test'
+ ...
+# Subtest: runInitWizard: gate status delegates to runStatus and returns its code
+ok 629 - runInitWizard: gate status delegates to runStatus and returns its code
+ ---
+ duration_ms: 9.71789
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a managed machine reconfigures through the fork, carrying managed into the picker
+ok 630 - runInitWizard: a managed machine reconfigures through the fork, carrying managed into the picker
+ ---
+ duration_ms: 25.860736
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a managed machine on the local pathway keeps the 90-day default, not the local 120
+ok 631 - runInitWizard: a managed machine on the local pathway keeps the 90-day default, not the local 120
+ ---
+ duration_ms: 11.736027
+ type: 'test'
+ ...
+# Subtest: runInitWizard: managed + local + stay connected keeps the locked rows and the sync lane
+ok 632 - runInitWizard: managed + local + stay connected keeps the locked rows and the sync lane
+ ---
+ duration_ms: 7.938365
+ type: 'test'
+ ...
+# Subtest: runInitWizard: managed + local + disconnect runs hyp leave and continues as a solo install
+ok 633 - runInitWizard: managed + local + disconnect runs hyp leave and continues as a solo install
+ ---
+ duration_ms: 1.205673
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a failed hyp leave returns to the fork still connected
+ok 634 - runInitWizard: a failed hyp leave returns to the fork still connected
+ ---
+ duration_ms: 0.956313
+ type: 'test'
+ ...
+# Subtest: runInitWizard: cancelling the disconnect question ends the run without disconnecting
+ok 635 - runInitWizard: cancelling the disconnect question ends the run without disconnecting
+ ---
+ duration_ms: 1.049005
+ type: 'test'
+ ...
+# Subtest: runInitWizard: an unmanaged machine choosing local is never asked about disconnecting
+ok 636 - runInitWizard: an unmanaged machine choosing local is never asked about disconnecting
+ ---
+ duration_ms: 1.577057
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a managed machine can still re-join a team from the gate
+ok 637 - runInitWizard: a managed machine can still re-join a team from the gate
+ ---
+ duration_ms: 14.273174
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a managed first run locks the org rows from the on-disk central layer
+ok 638 - runInitWizard: a managed first run locks the org rows from the on-disk central layer
+ ---
+ duration_ms: 7.747025
+ type: 'test'
+ ...
+# Subtest: runInitWizard: accepting the express gate auto-accepts every lane and states no positions
+ok 639 - runInitWizard: accepting the express gate auto-accepts every lane and states no positions
+ ---
+ duration_ms: 15.352384
+ type: 'test'
+ ...
+# Subtest: runInitWizard: with nothing detected and nothing locked, no express gate is shown
+ok 640 - runInitWizard: with nothing detected and nothing locked, no express gate is shown
+ ---
+ duration_ms: 4.78956
+ type: 'test'
+ ...
+# Subtest: runInitWizard: declining the express gate leaves the lanes prompting, positions and all
+ok 641 - runInitWizard: declining the express gate leaves the lanes prompting, positions and all
+ ---
+ duration_ms: 9.148585
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a cancelled express gate exits 130 before any lane runs
+ok 642 - runInitWizard: a cancelled express gate exits 130 before any lane runs
+ ---
+ duration_ms: 1.86831
+ type: 'test'
+ ...
+# Subtest: runInitWizard: back at the express gate re-presents the fork
+ok 643 - runInitWizard: back at the express gate re-presents the fork
+ ---
+ duration_ms: 5.851594
+ type: 'test'
+ ...
+# Subtest: runInitWizard: the unenrolled local pathway shows no express gate; the pick gate is the one question
+ok 644 - runInitWizard: the unenrolled local pathway shows no express gate; the pick gate is the one question
+ ---
+ duration_ms: 0.938627
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a managed machine reconfiguring down the local pathway still gets the express gate
+ok 645 - runInitWizard: a managed machine reconfiguring down the local pathway still gets the express gate
+ ---
+ duration_ms: 1.271392
+ type: 'test'
+ ...
+# Subtest: runInitWizard: the team pathway runs the sync-scope and new-folder steps between pick and configure
+ok 646 - runInitWizard: the team pathway runs the sync-scope and new-folder steps between pick and configure
+ ---
+ duration_ms: 12.762257
+ type: 'test'
+ ...
+# Subtest: runInitWizard: the sync-scope step receives the locked descriptors so it can state the whole sync picture
+ok 647 - runInitWizard: the sync-scope step receives the locked descriptors so it can state the whole sync picture
+ ---
+ duration_ms: 18.226623
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a managed machine on the local pathway also runs the sync-scope step
+ok 648 - runInitWizard: a managed machine on the local pathway also runs the sync-scope step
+ ---
+ duration_ms: 12.169977
+ type: 'test'
+ ...
+# Subtest: runInitWizard: an unmanaged local run never sees the sync-scope step
+ok 649 - runInitWizard: an unmanaged local run never sees the sync-scope step
+ ---
+ duration_ms: 0.758843
+ type: 'test'
+ ...
+# Subtest: runInitWizard: non-interactive picks skip the sync-scope step (default-sync is the scripted outcome)
+ok 650 - runInitWizard: non-interactive picks skip the sync-scope step (default-sync is the scripted outcome)
+ ---
+ duration_ms: 0.686995
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a cancelled sync-scope step exits 130 and runs nothing further
+ok 651 - runInitWizard: a cancelled sync-scope step exits 130 and runs nothing further
+ ---
+ duration_ms: 5.115004
+ type: 'test'
+ ...
+# Subtest: runInitWizard: local pathway runs pick -> configure -> finale, no join
+ok 652 - runInitWizard: local pathway runs pick -> configure -> finale, no join
+ ---
+ duration_ms: 0.757401
+ type: 'test'
+ ...
+# Subtest: runInitWizard: fork quit exits 0 before the pick phase
+ok 653 - runInitWizard: fork quit exits 0 before the pick phase
+ ---
+ duration_ms: 0.691641
+ type: 'test'
+ ...
+# Subtest: runInitWizard: team pathway threads locked + managed into the pick phase
+ok 654 - runInitWizard: team pathway threads locked + managed into the pick phase
+ ---
+ duration_ms: 10.363281
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a failed join explains and returns to the fork
+ok 655 - runInitWizard: a failed join explains and returns to the fork
+ ---
+ duration_ms: 1.028042
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a multi-org join failure points at hyp remote login --org
+ok 656 - runInitWizard: a multi-org join failure points at hyp remote login --org
+ ---
+ duration_ms: 0.712022
+ type: 'test'
+ ...
+# Subtest: runInitWizard: an abandoned join is retriable and re-presents the fork
+ok 657 - runInitWizard: an abandoned join is retriable and re-presents the fork
+ ---
+ duration_ms: 9.987309
+ type: 'test'
+ ...
+# Subtest: runInitWizard: pre-baked picks skip gate, fork, and join entirely
+ok 658 - runInitWizard: pre-baked picks skip gate, fork, and join entirely
+ ---
+ duration_ms: 0.55268
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a cancelled pick returns 130 and runs nothing further
+ok 659 - runInitWizard: a cancelled pick returns 130 and runs nothing further
+ ---
+ duration_ms: 0.477706
+ type: 'test'
+ ...
+# Subtest: runInitWizard: an overwrite refusal returns the pick phase exit 1
+ok 660 - runInitWizard: an overwrite refusal returns the pick phase exit 1
+ ---
+ duration_ms: 0.438677
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a pending config lands on disk after the sync lane, before configure
+ok 661 - runInitWizard: a pending config lands on disk after the sync lane, before configure
+ ---
+ duration_ms: 16.380066
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a declined commit exits 1, runs nothing further, and narrates on the team pathway
+ok 662 - runInitWizard: a declined commit exits 1, runs nothing further, and narrates on the team pathway
+ ---
+ duration_ms: 8.506549
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a scripted pick result without configPending is never committed by the orchestrator
+ok 663 - runInitWizard: a scripted pick result without configPending is never committed by the orchestrator
+ ---
+ duration_ms: 0.850763
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a team-path overwrite refusal narrates the enrolled state and the deadline
+ok 664 - runInitWizard: a team-path overwrite refusal narrates the enrolled state and the deadline
+ ---
+ duration_ms: 18.495992
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a team-path pick cancel narrates the enrolled state; no hold means no deadline claim
+ok 665 - runInitWizard: a team-path pick cancel narrates the enrolled state; no hold means no deadline claim
+ ---
+ duration_ms: 5.226393
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a team-path sync-scope cancel narrates that default-sync stands
+ok 666 - runInitWizard: a team-path sync-scope cancel narrates that default-sync stands
+ ---
+ duration_ms: 9.155336
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a local-path abort stays quiet - nothing enrolled this run
+ok 667 - runInitWizard: a local-path abort stays quiet - nothing enrolled this run
+ ---
+ duration_ms: 2.341549
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a cancelled finale returns 130 with the cancel notice
+ok 668 - runInitWizard: a cancelled finale returns 130 with the cancel notice
+ ---
+ duration_ms: 1.163919
+ type: 'test'
+ ...
+# Subtest: runInitWizard: prints the run summary with the written config path
+ok 669 - runInitWizard: prints the run summary with the written config path
+ ---
+ duration_ms: 0.672542
+ type: 'test'
+ ...
+# Subtest: runInitWizard: an attended run ends on the first look, before the privacy narration
+ok 670 - runInitWizard: an attended run ends on the first look, before the privacy narration
+ ---
+ duration_ms: 21.219361
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a non-interactive or dry run skips the first look
+ok 671 - runInitWizard: a non-interactive or dry run skips the first look
+ ---
+ duration_ms: 1.044257
+ type: 'test'
+ ...
+# Subtest: runInitWizard: the first ask comes last, after the privacy narration
+ok 672 - runInitWizard: the first ask comes last, after the privacy narration
+ ---
+ duration_ms: 30.620741
+ type: 'test'
+ ...
+# Subtest: runInitWizard: an enrolled run is offered the first sync, after the narration and before the first ask
+ok 673 - runInitWizard: an enrolled run is offered the first sync, after the narration and before the first ask
+ ---
+ duration_ms: 12.392606
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a local install with no hold is never offered a sync
+ok 674 - runInitWizard: a local install with no hold is never offered a sync
+ ---
+ duration_ms: 0.787728
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a first look with no rows suppresses the launch
+ok 675 - runInitWizard: a first look with no rows suppresses the launch
+ ---
+ duration_ms: 0.775839
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a first look with no gateway dataset suppresses the launch too
+ok 676 - runInitWizard: a first look with no gateway dataset suppresses the launch too
+ ---
+ duration_ms: 0.625541
+ type: 'test'
+ ...
+# Subtest: firstLookHadRows: a slow first look still reports hasRows true, so the launch is not suppressed
+ok 677 - firstLookHadRows: a slow first look still reports hasRows true, so the launch is not suppressed
+ ---
+ duration_ms: 0.084028
+ type: 'test'
+ ...
+# Subtest: firstLookHadRows: an absent or errored first look reports hasRows undefined, not false, so the offer is never withheld
+ok 678 - firstLookHadRows: an absent or errored first look reports hasRows undefined, not false, so the offer is never withheld
+ ---
+ duration_ms: 0.060452
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a launched client does not change the wizard exit code
+ok 679 - runInitWizard: a launched client does not change the wizard exit code
+ ---
+ duration_ms: 1.434961
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a non-interactive or dry run never launches anything
+ok 680 - runInitWizard: a non-interactive or dry run never launches anything
+ ---
+ duration_ms: 1.272724
+ type: 'test'
+ ...
+# Subtest: runInitWizard: team pathway with a live first-sync hold narrates the deadline
+ok 681 - runInitWizard: team pathway with a live first-sync hold narrates the deadline
+ ---
+ duration_ms: 10.297671
+ type: 'test'
+ ...
+# Subtest: runInitWizard: local pathway never narrates the first-sync hold
+ok 682 - runInitWizard: local pathway never narrates the first-sync hold
+ ---
+ duration_ms: 1.46727
+ type: 'test'
+ ...
+# Subtest: classifyLoginFailure: no_membership is a definitive rejection -> failed
+ok 683 - classifyLoginFailure: no_membership is a definitive rejection -> failed
+ ---
+ duration_ms: 0.929984
+ type: 'test'
+ ...
+# Subtest: classifyLoginFailure: org_not_permitted is a definitive rejection -> failed
+ok 684 - classifyLoginFailure: org_not_permitted is a definitive rejection -> failed
+ ---
+ duration_ms: 0.122737
+ type: 'test'
+ ...
+# Subtest: classifyLoginFailure: org_selection_required (multi-org account) -> failed
+ok 685 - classifyLoginFailure: org_selection_required (multi-org account) -> failed
+ ---
+ duration_ms: 0.116698
+ type: 'test'
+ ...
+# Subtest: classifyLoginFailure: retriable reasons -> abandoned
+ok 686 - classifyLoginFailure: retriable reasons -> abandoned
+ ---
+ duration_ms: 0.136187
+ type: 'test'
+ ...
+# Subtest: classifyLoginFailure: the message text is not consulted
+ok 687 - classifyLoginFailure: the message text is not consulted
+ ---
+ duration_ms: 0.188667
+ type: 'test'
+ ...
+# Subtest: classifyLoginFailure: a missing reason defaults to abandoned
+ok 688 - classifyLoginFailure: a missing reason defaults to abandoned
+ ---
+ duration_ms: 0.113412
+ type: 'test'
+ ...
+# Subtest: runWizardJoin: a non-zero login exit returns the classified failure and never waits
+ok 689 - runWizardJoin: a non-zero login exit returns the classified failure and never waits
+ ---
+ duration_ms: 2.079942
+ type: 'test'
+ ...
+# Subtest: runWizardJoin: a transient login failure returns abandoned
+ok 690 - runWizardJoin: a transient login failure returns abandoned
+ ---
+ duration_ms: 0.343532
+ type: 'test'
+ ...
+# Subtest: runWizardJoin: on convergence, locks exactly the central-layer picker rows
+ok 691 - runWizardJoin: on convergence, locks exactly the central-layer picker rows
+ ---
+ duration_ms: 0.877684
+ type: 'test'
+ ...
+# Subtest: runWizardJoin: convergence with no central-owned rows locks nothing
+ok 692 - runWizardJoin: convergence with no central-owned rows locks nothing
+ ---
+ duration_ms: 0.488863
+ type: 'test'
+ ...
+# Subtest: runWizardJoin: a convergence timeout narrates and returns an empty lock set
+ok 693 - runWizardJoin: a convergence timeout narrates and returns an empty lock set
+ ---
+ duration_ms: 0.346136
+ type: 'test'
+ ...
+# Subtest: runWizardJoin: passes the org-config wait budget through to the converge helper
+ok 694 - runWizardJoin: passes the org-config wait budget through to the converge helper
+ ---
+ duration_ms: 0.40763
+ type: 'test'
+ ...
+# Subtest: runWizardPick: pre-baked picks skip prompting and compose the same config
+ok 695 - runWizardPick: pre-baked picks skip prompting and compose the same config
+ ---
+ duration_ms: 29.174763
+ type: 'test'
+ ...
+# Subtest: runWizardPick: non-interactive path does not run detection
+ok 696 - runWizardPick: non-interactive path does not run detection
+ ---
+ duration_ms: 13.156767
+ type: 'test'
+ ...
+# Subtest: runWizardPick: interactive prompt options pre-check detected sources
+ok 697 - runWizardPick: interactive prompt options pre-check detected sources
+ ---
+ duration_ms: 14.822428
+ type: 'test'
+ ...
+# Subtest: runWizardPick: a detected needs_setup row arrives unchecked, labeled detected
+ok 698 - runWizardPick: a detected needs_setup row arrives unchecked, labeled detected
+ ---
+ duration_ms: 8.691981
+ type: 'test'
+ ...
+# Subtest: runWizardPick: the defaults gate omits a detected needs_setup row, and accept does not pick it
+ok 699 - runWizardPick: the defaults gate omits a detected needs_setup row, and accept does not pick it
+ ---
+ duration_ms: 5.608194
+ type: 'test'
+ ...
+# Subtest: runWizardPick: a reconfigure reports carried picks in previouslyConfigured
+ok 700 - runWizardPick: a reconfigure reports carried picks in previouslyConfigured
+ ---
+ duration_ms: 9.453188
+ type: 'test'
+ ...
+# Subtest: runWizardPick: a fresh pick reports nothing as previously configured
+ok 701 - runWizardPick: a fresh pick reports nothing as previously configured
+ ---
+ duration_ms: 9.298173
+ type: 'test'
+ ...
+# Subtest: runWizardPick: a seeded needs_setup row on the gate carries the needs-extra-setup suffix
+ok 702 - runWizardPick: a seeded needs_setup row on the gate carries the needs-extra-setup suffix
+ ---
+ duration_ms: 4.76322
+ type: 'test'
+ ...
+# Subtest: runWizardPick: accepting the defaults gate picks exactly the detected sources, no menu
+ok 703 - runWizardPick: accepting the defaults gate picks exactly the detected sources, no menu
+ ---
+ duration_ms: 11.905635
+ type: 'test'
+ ...
+# Subtest: runWizardPick: the gate names locked sources as fleet-managed and accept keeps them
+ok 704 - runWizardPick: the gate names locked sources as fleet-managed and accept keeps them
+ ---
+ duration_ms: 6.400788
+ type: 'test'
+ ...
+# Subtest: runWizardPick: autoAccept takes the gate rows and prints what the gate would have said
+ok 705 - runWizardPick: autoAccept takes the gate rows and prints what the gate would have said
+ ---
+ duration_ms: 2.888941
+ type: 'test'
+ ...
+# Subtest: runWizardPick: autoAccept with no default to take still opens the menu
+ok 706 - runWizardPick: autoAccept with no default to take still opens the menu
+ ---
+ duration_ms: 3.932717
+ type: 'test'
+ ...
+# Subtest: runWizardPick: no gate when nothing is detected and nothing is locked
+ok 707 - runWizardPick: no gate when nothing is detected and nothing is locked
+ ---
+ duration_ms: 7.208035
+ type: 'test'
+ ...
+# Subtest: runWizardPick: menu back with allowBack and no gate propagates to the caller, never loops
+ok 708 - runWizardPick: menu back with allowBack and no gate propagates to the caller, never loops
+ ---
+ duration_ms: 3.807457
+ type: 'test'
+ ...
+# Subtest: runWizardPick: a cancelled gate returns the deterministic cancel result
+ok 709 - runWizardPick: a cancelled gate returns the deterministic cancel result
+ ---
+ duration_ms: 3.400729
+ type: 'test'
+ ...
+# Subtest: runWizardPick: interactive runs take the 90-day default without a retention prompt
+ok 710 - runWizardPick: interactive runs take the 90-day default without a retention prompt
+ ---
+ duration_ms: 8.917945
+ type: 'test'
+ ...
+# Subtest: runWizardPick: retentionDefault (the local pathway) lands in the composed config
+ok 711 - runWizardPick: retentionDefault (the local pathway) lands in the composed config
+ ---
+ duration_ms: 4.925036
+ type: 'test'
+ ...
+# Subtest: runWizardPick: pre-baked picks override retentionDefault
+ok 712 - runWizardPick: pre-baked picks override retentionDefault
+ ---
+ duration_ms: 3.863012
+ type: 'test'
+ ...
+# Subtest: runWizardPick: options come from catalog.pickerDescriptors, not a hardcoded table
+ok 713 - runWizardPick: options come from catalog.pickerDescriptors, not a hardcoded table
+ ---
+ duration_ms: 7.685401
+ type: 'test'
+ ...
+# Subtest: runWizardPick: a locked row renders checked, disabled, and fleet-labeled
+ok 714 - runWizardPick: a locked row renders checked, disabled, and fleet-labeled
+ ---
+ duration_ms: 4.274387
+ type: 'test'
+ ...
+# Subtest: runWizardPick: a locked source is filtered out of the returned picks and composition
+ok 715 - runWizardPick: a locked source is filtered out of the returned picks and composition
+ ---
+ duration_ms: 6.038197
+ type: 'test'
+ ...
+# Subtest: runWizardPick: an unknown locked id is ignored, not surfaced as a row
+ok 716 - runWizardPick: an unknown locked id is ignored, not surfaced as a row
+ ---
+ duration_ms: 4.297472
+ type: 'test'
+ ...
+# Subtest: runWizardPick: a fully fleet-managed machine still reports its locked clients as picked so the finale installs skills/agents
+ok 717 - runWizardPick: a fully fleet-managed machine still reports its locked clients as picked so the finale installs skills/agents
+ ---
+ duration_ms: 3.122836
+ type: 'test'
+ ...
+# Subtest: runWizardPick: a managed machine no longer labels non-locked rows "stays on this machine"
+ok 718 - runWizardPick: a managed machine no longer labels non-locked rows "stays on this machine"
+ ---
+ duration_ms: 3.102015
+ type: 'test'
+ ...
+# Subtest: runWizardPick: an unmanaged (solo) machine never shows a local-only suffix either
+ok 719 - runWizardPick: an unmanaged (solo) machine never shows a local-only suffix either
+ ---
+ duration_ms: 5.418235
+ type: 'test'
+ ...
+# Subtest: runWizardPick: refuses to clobber an existing config without --force (exit 1, not cancelled)
+ok 720 - runWizardPick: refuses to clobber an existing config without --force (exit 1, not cancelled)
+ ---
+ duration_ms: 5.51998
+ type: 'test'
+ ...
+# Subtest: runWizardPick: --force overwrites an existing config after backing it up
+ok 721 - runWizardPick: --force overwrites an existing config after backing it up
+ ---
+ duration_ms: 10.173913
+ type: 'test'
+ ...
+# Subtest: runWizardPick: deferWrite composes but never writes, guards, or prompts to overwrite
+ok 722 - runWizardPick: deferWrite composes but never writes, guards, or prompts to overwrite
+ ---
+ duration_ms: 3.104789
+ type: 'test'
+ ...
+# Subtest: commitWizardPickedConfig: writes the config, backing up an existing one first
+ok 723 - commitWizardPickedConfig: writes the config, backing up an existing one first
+ ---
+ duration_ms: 1.208577
+ type: 'test'
+ ...
+# Subtest: commitWizardPickedConfig: a declined overwrite refuses without touching the config
+ok 724 - commitWizardPickedConfig: a declined overwrite refuses without touching the config
+ ---
+ duration_ms: 0.73724
+ type: 'test'
+ ...
+# Subtest: runWizardPick: a cancelled prompt returns the deterministic cancel result
+ok 725 - runWizardPick: a cancelled prompt returns the deterministic cancel result
+ ---
+ duration_ms: 3.167203
+ type: 'test'
+ ...
+# Subtest: runWizardPick: a picked openclaw reaches clientsPicked; a clientless pick does not
+ok 726 - runWizardPick: a picked openclaw reaches clientsPicked; a clientless pick does not
+ ---
+ duration_ms: 4.141575
+ type: 'test'
+ ...
+# Subtest: derivePickedClients: the derived set over every bundled picker row is pinned
+ok 727 - derivePickedClients: the derived set over every bundled picker row is pinned
+ ---
+ duration_ms: 6.635115
+ type: 'test'
+ ...
+# Subtest: runWizardPick: a reconfigure pre-checks the undetectable otel row it already collects
+ok 728 - runWizardPick: a reconfigure pre-checks the undetectable otel row it already collects
+ ---
+ duration_ms: 8.433839
+ type: 'test'
+ ...
+# Subtest: runWizardPick: a reconfigure leaves a deliberately excluded client unchecked even when it is detected
+ok 729 - runWizardPick: a reconfigure leaves a deliberately excluded client unchecked even when it is detected
+ ---
+ duration_ms: 3.90188
+ type: 'test'
+ ...
+# Subtest: runWizardPick: a 120-day retention survives a team-path reconfigure
+ok 730 - runWizardPick: a 120-day retention survives a team-path reconfigure
+ ---
+ duration_ms: 6.721577
+ type: 'test'
+ ...
+# Subtest: runWizardPick: a first run still seeds from detection and takes the pathway retention default
+ok 731 - runWizardPick: a first run still seeds from detection and takes the pathway retention default
+ ---
+ duration_ms: 5.512098
+ type: 'test'
+ ...
+# Subtest: runWizardPick: a reconfigure carries forward plugins and sink edits the picker does not own
+ok 732 - runWizardPick: a reconfigure carries forward plugins and sink edits the picker does not own
+ ---
+ duration_ms: 14.519759
+ type: 'test'
+ ...
+# Subtest: runWizardPick: a reconfigure of a cache-only install does not silently add an export sink
+ok 733 - runWizardPick: a reconfigure of a cache-only install does not silently add an export sink
+ ---
+ duration_ms: 4.407038
+ type: 'test'
+ ...
+# Subtest: runWizardPick: unchecking a row still removes its plugin and its gateway upstream
+ok 734 - runWizardPick: unchecking a row still removes its plugin and its gateway upstream
+ ---
+ duration_ms: 4.789009
+ type: 'test'
+ ...
+# Subtest: runWizardPick: a disabled plugin reads as an off row, and re-picking it turns it back on
+ok 735 - runWizardPick: a disabled plugin reads as an off row, and re-picking it turns it back on
+ ---
+ duration_ms: 7.374779
+ type: 'test'
+ ...
+# Subtest: runWizardPick: a reconfigure does not add a second export sink beside a renamed one
+ok 736 - runWizardPick: a reconfigure does not add a second export sink beside a renamed one
+ ---
+ duration_ms: 5.465958
+ type: 'test'
+ ...
+# Subtest: runWizardPick: a request sink parked on the composer sink id is not folded into a mixed shape
+ok 737 - runWizardPick: a request sink parked on the composer sink id is not folded into a mixed shape
+ ---
+ duration_ms: 5.878805
+ type: 'test'
+ ...
+# Subtest: runWizardPick: a differently written blob sink parked on the composer sink id is not rewritten
+ok 738 - runWizardPick: a differently written blob sink parked on the composer sink id is not rewritten
+ ---
+ duration_ms: 13.468781
+ type: 'test'
+ ...
+# Subtest: defaultOverwriteConfirmFactory: the prompt says the config is regenerated from the picks
+ok 739 - defaultOverwriteConfirmFactory: the prompt says the config is regenerated from the picks
+ ---
+ duration_ms: 1.764191
+ type: 'test'
+ ...
+# Subtest: defaultOverwriteConfirmFactory: bare enter proceeds, an explicit no declines
+ok 740 - defaultOverwriteConfirmFactory: bare enter proceeds, an explicit no declines
+ ---
+ duration_ms: 0.646683
+ type: 'test'
+ ...
+# Subtest: runWizardPick: a hidden row is absent from the defaults gate as well as the menu
+ok 741 - runWizardPick: a hidden row is absent from the defaults gate as well as the menu
+ ---
+ duration_ms: 7.284151
+ type: 'test'
+ ...
+# Subtest: runWizardPick: a raw-only config survives a reconfigure that picks nothing new
+ok 742 - runWizardPick: a raw-only config survives a reconfigure that picks nothing new
+ ---
+ duration_ms: 6.95476
+ type: 'test'
+ ...
+# Subtest: runWizardPick: the express path carries a raw-only config and never states the hidden row
+ok 743 - runWizardPick: the express path carries a raw-only config and never states the hidden row
+ ---
+ duration_ms: 13.590045
+ type: 'test'
+ ...
+# Subtest: runWizardPick: a hidden row seeded only derivatively does not resurrect an unchecked upstream
+ok 744 - runWizardPick: a hidden row seeded only derivatively does not resurrect an unchecked upstream
+ ---
+ duration_ms: 8.745212
+ type: 'test'
+ ...
+# Subtest: runWizardPick: --source still composes a hidden row (no prompt involved)
+ok 745 - runWizardPick: --source still composes a hidden row (no prompt involved)
+ ---
+ duration_ms: 7.424515
+ type: 'test'
+ ...
+# Subtest: runWizardPick: a hidden row that is merely detected is not carried on a first run
+ok 746 - runWizardPick: a hidden row that is merely detected is not carried on a first run
+ ---
+ duration_ms: 6.807216
+ type: 'test'
+ ...
+# Subtest: runWizardPick: a carried hidden row survives a re-entry that adds a visible row
+ok 747 - runWizardPick: a carried hidden row survives a re-entry that adds a visible row
+ ---
+ duration_ms: 8.454209
+ type: 'test'
+ ...
+# Subtest: wizardStepProgress: the team pathway counts join, pick, sync, folders and finale
+ok 748 - wizardStepProgress: the team pathway counts join, pick, sync, folders and finale
+ ---
+ duration_ms: 1.560201
+ type: 'test'
+ ...
+# Subtest: wizardStepProgress: the local pathway counts two steps
+ok 749 - wizardStepProgress: the local pathway counts two steps
+ ---
+ duration_ms: 0.165982
+ type: 'test'
+ ...
+# Subtest: wizardStepProgress: a managed machine on the local pathway gains both enrolled lanes (LLP 0188, LLP 0200)
+ok 750 - wizardStepProgress: a managed machine on the local pathway gains both enrolled lanes (LLP 0188, LLP 0200)
+ ---
+ duration_ms: 0.236589
+ type: 'test'
+ ...
+# Subtest: wizardStepProgress: an uncommitted pathway has no denominator
+ok 751 - wizardStepProgress: an uncommitted pathway has no denominator
+ ---
+ duration_ms: 0.133352
+ type: 'test'
+ ...
+# Subtest: runInitWizard: the local pathway reads step 1 of 2 then step 2 of 2
+ok 752 - runInitWizard: the local pathway reads step 1 of 2 then step 2 of 2
+ ---
+ duration_ms: 10.043846
+ type: 'test'
+ ...
+# Subtest: runInitWizard: the team pathway reads step 1/2/3/4/5 across join, pick, sync, folders and finale
+ok 753 - runInitWizard: the team pathway reads step 1/2/3/4/5 across join, pick, sync, folders and finale
+ ---
+ duration_ms: 40.529801
+ type: 'test'
+ ...
+# Subtest: runInitWizard: the fork never carries a counter, before or after a failed join
+ok 754 - runInitWizard: the fork never carries a counter, before or after a failed join
+ ---
+ duration_ms: 0.983925
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a managed re-entry counts the pathway the fork returns, plus both enrolled lanes
+ok 755 - runInitWizard: a managed re-entry counts the pathway the fork returns, plus both enrolled lanes
+ ---
+ duration_ms: 8.551878
+ type: 'test'
+ ...
+# Subtest: runInitWizard: a non-interactive run carries no breadcrumb anywhere
+ok 756 - runInitWizard: a non-interactive run carries no breadcrumb anywhere
+ ---
+ duration_ms: 0.886818
+ type: 'test'
+ ...
+# Subtest: runWizardJoin: prints its position above the joining narration
+ok 757 - runWizardJoin: prints its position above the joining narration
+ ---
+ duration_ms: 0.711842
+ type: 'test'
+ ...
+# Subtest: runWizardJoin: without a position it narrates exactly as it does today
+ok 758 - runWizardJoin: without a position it narrates exactly as it does today
+ ---
+ duration_ms: 0.219494
+ type: 'test'
+ ...
+# Subtest: runPickerFinale: states its position once, where the lane starts
+ok 759 - runPickerFinale: states its position once, where the lane starts
+ ---
+ duration_ms: 8.284281
+ type: 'test'
+ ...
+# Subtest: runPickerFinale: without a position it writes exactly what it writes today
+ok 760 - runPickerFinale: without a position it writes exactly what it writes today
+ ---
+ duration_ms: 13.866786
+ type: 'test'
+ ...
+# Subtest: the legacy numbered picker prompt prints the breadcrumb as plain text
+ok 761 - the legacy numbered picker prompt prints the breadcrumb as plain text
+ ---
+ duration_ms: 0.914039
+ type: 'test'
+ ...
+# Subtest: render: the breadcrumb is its own line above the title, never folded into it
+ok 762 - render: the breadcrumb is its own line above the title, never folded into it
+ ---
+ duration_ms: 0.222819
+ type: 'test'
+ ...
+# Subtest: render: a spec without a breadcrumb renders exactly as it does today
+ok 763 - render: a spec without a breadcrumb renders exactly as it does today
+ ---
+ duration_ms: 0.110959
+ type: 'test'
+ ...
+# Subtest: no hold means no question: an unenrolled install is never asked to sync
+ok 764 - no hold means no question: an unenrolled install is never asked to sync
+ ---
+ duration_ms: 2.283811
+ type: 'test'
+ ...
+# Subtest: a non-interactive run is never asked, and never sends
+ok 765 - a non-interactive run is never asked, and never sends
+ ---
+ duration_ms: 0.374509
+ type: 'test'
+ ...
+# Subtest: the question offers wait first, as the default, and declining spawns nothing
+ok 766 - the question offers wait first, as the default, and declining spawns nothing
+ ---
+ duration_ms: 16.015442
+ type: 'test'
+ ...
+# Subtest: send now spawns `hyp sync` on the inherited terminal and reports the release
+ok 767 - send now spawns `hyp sync` on the inherited terminal and reports the release
+ ---
+ duration_ms: 1.031578
+ type: 'test'
+ ...
+# Subtest: a child that exits 0 without releasing is reported as not sent
+ok 768 - a child that exits 0 without releasing is reported as not sent
+ ---
+ duration_ms: 0.814298
+ type: 'test'
+ ...
+# Subtest: a re-read that throws is reported as not sent, not as a release
+ok 769 - a re-read that throws is reported as not sent, not as a release
+ ---
+ duration_ms: 0.727506
+ type: 'test'
+ ...
+# Subtest: a spawn failure never fails the install, and restates the wait
+ok 770 - a spawn failure never fails the install, and restates the wait
+ ---
+ duration_ms: 0.605671
+ type: 'test'
+ ...
+# Subtest: the read-back resolves a real hold marker from the environment: left in place
+ok 771 - the read-back resolves a real hold marker from the environment: left in place
+ ---
+ duration_ms: 11.700935
+ type: 'test'
+ ...
+# Subtest: the read-back resolves a real hold marker from the environment: cleared by the child
+ok 772 - the read-back resolves a real hold marker from the environment: cleared by the child
+ ---
+ duration_ms: 19.452456
+ type: 'test'
+ ...
+# Subtest: a cancelled prompt is a wait, not an error
+ok 773 - a cancelled prompt is a wait, not an error
+ ---
+ duration_ms: 1.097428
+ type: 'test'
+ ...
+# Subtest: a fresh enrolled run: the gate states what will sync and accepting opts nothing out
+ok 774 - a fresh enrolled run: the gate states what will sync and accepting opts nothing out
+ ---
+ duration_ms: 13.375249
+ type: 'test'
+ ...
+# Subtest: the menu checks what syncs: everything checked by default on a fresh run
+ok 775 - the menu checks what syncs: everything checked by default on a fresh run
+ ---
+ duration_ms: 12.693042
+ type: 'test'
+ ...
+# Subtest: non-TTY: opening the menu and pressing enter keeps the defaults, not opt-everything-out
+ok 776 - non-TTY: opening the menu and pressing enter keeps the defaults, not opt-everything-out
+ ---
+ duration_ms: 3.459598
+ type: 'test'
+ ...
+# Subtest: non-TTY: a bare enter at the menu round-trips a standing opt-out instead of resetting it
+ok 777 - non-TTY: a bare enter at the menu round-trips a standing opt-out instead of resetting it
+ ---
+ duration_ms: 5.670579
+ type: 'test'
+ ...
+# Subtest: unchecking a source writes its opt-out and names the follow-up command
+ok 778 - unchecking a source writes its opt-out and names the follow-up command
+ ---
+ duration_ms: 1.580111
+ type: 'test'
+ ...
+# Subtest: a re-entry states the split on the gate and accepting keeps it
+ok 779 - a re-entry states the split on the gate and accepting keeps it
+ ---
+ duration_ms: 3.872806
+ type: 'test'
+ ...
+# Subtest: a re-entry renders existing opt-outs unchecked and re-checking removes them
+ok 780 - a re-entry renders existing opt-outs unchecked and re-checking removes them
+ ---
+ duration_ms: 2.926267
+ type: 'test'
+ ...
+# Subtest: editor semantics: an entry for a source not shown this run is kept
+ok 781 - editor semantics: an entry for a source not shown this run is kept
+ ---
+ duration_ms: 1.645431
+ type: 'test'
+ ...
+# Subtest: locked sources lead the gate list fleet-suffixed and the menu as read-only rows
+ok 782 - locked sources lead the gate list fleet-suffixed and the menu as read-only rows
+ ---
+ duration_ms: 1.744461
+ type: 'test'
+ ...
+# Subtest: a locked source never enters the opt-out computation
+ok 783 - a locked source never enters the opt-out computation
+ ---
+ duration_ms: 3.235959
+ type: 'test'
+ ...
+# Subtest: zero candidates: prints the position and the fleet line, prompts nothing, writes nothing
+ok 784 - zero candidates: prints the position and the fleet line, prompts nothing, writes nothing
+ ---
+ duration_ms: 0.889212
+ type: 'test'
+ ...
+# Subtest: a cancelled gate returns cancelled and writes nothing
+ok 785 - a cancelled gate returns cancelled and writes nothing
+ ---
+ duration_ms: 0.976804
+ type: 'test'
+ ...
+# Subtest: a cancelled menu returns cancelled and writes nothing
+ok 786 - a cancelled menu returns cancelled and writes nothing
+ ---
+ duration_ms: 1.640203
+ type: 'test'
+ ...
+# Subtest: a corrupt store skips the step with a warning and is never overwritten
+ok 787 - a corrupt store skips the step with a warning and is never overwritten
+ ---
+ duration_ms: 3.367198
+ type: 'test'
+ ...
+# Subtest: agents.register validates contribution shape
+ok 788 - agents.register validates contribution shape
+ ---
+ duration_ms: 3.530456
+ type: 'test'
+ ...
+# Subtest: agents.register rejects path-traversal names
+ok 789 - agents.register rejects path-traversal names
+ ---
+ duration_ms: 0.553822
+ type: 'test'
+ ...
+# Subtest: skills.register rejects path-traversal names
+ok 790 - skills.register rejects path-traversal names
+ ---
+ duration_ms: 0.428961
+ type: 'test'
+ ...
+# Subtest: hyp skills install materializes skills and subagents in one command
+ok 791 - hyp skills install materializes skills and subagents in one command
+ ---
+ duration_ms: 44.107199
+ type: 'test'
+ ...
+# Subtest: hyp agents install is gone: agents is not a command
+ok 792 - hyp agents install is gone: agents is not a command
+ ---
+ duration_ms: 10.114273
+ type: 'test'
+ ...
+# Subtest: hyp skills install skips a client with no directory for that asset kind
+ok 793 - hyp skills install skips a client with no directory for that asset kind
+ ---
+ duration_ms: 12.531907
+ type: 'test'
+ ...
+# Subtest: hyp skills install warns when a contribution names an unknown client
+ok 794 - hyp skills install warns when a contribution names an unknown client
+ ---
+ duration_ms: 6.994782
+ type: 'test'
+ ...
+# Subtest: hyp skills install respects --client filtering
+ok 795 - hyp skills install respects --client filtering
+ ---
+ duration_ms: 7.097127
+ type: 'test'
+ ...
+# Subtest: bundled @hypaware/claude manifest declares the hypaware-analyst agent
+ok 796 - bundled @hypaware/claude manifest declares the hypaware-analyst agent
+ ---
+ duration_ms: 1.008192
+ type: 'test'
+ ...
+# Subtest: hyp skills install removes a skill the current manifests no longer declare
+ok 797 - hyp skills install removes a skill the current manifests no longer declare
+ ---
+ duration_ms: 48.802355
+ type: 'test'
+ ...
+# Subtest: a retired subagent file is removed the same way a skill directory is
+ok 798 - a retired subagent file is removed the same way a skill directory is
+ ---
+ duration_ms: 43.482328
+ type: 'test'
+ ...
+# Subtest: a user's own skill in the same directory is never removed
+ok 799 - a user's own skill in the same directory is never removed
+ ---
+ duration_ms: 18.194855
+ type: 'test'
+ ...
+# Subtest: a retired skill the user edited is left in place and named, never deleted
+ok 800 - a retired skill the user edited is left in place and named, never deleted
+ ---
+ duration_ms: 22.861086
+ type: 'test'
+ ...
+# Subtest: an asset only the attach marker records is named, never removed (pre-ledger installs)
+ok 801 - an asset only the attach marker records is named, never removed (pre-ledger installs)
+ ---
+ duration_ms: 17.346034
+ type: 'test'
+ ...
+# Subtest: a skill the user authored at a path the attach marker still names is never removed
+ok 802 - a skill the user authored at a path the attach marker still names is never removed
+ ---
+ duration_ms: 19.94197
+ type: 'test'
+ ...
+# Subtest: a ledger record whose digest is not a string never becomes an unconditional delete
+ok 803 - a ledger record whose digest is not a string never becomes an unconditional delete
+ ---
+ duration_ms: 17.374938
+ type: 'test'
+ ...
+# Subtest: a ledger record with no digest at all is withheld and reported
+ok 804 - a ledger record with no digest at all is withheld and reported
+ ---
+ duration_ms: 21.155284
+ type: 'test'
+ ...
+# Subtest: a retired asset that cannot be read is named and kept on the books
+ok 805 - a retired asset that cannot be read is named and kept on the books
+ ---
+ duration_ms: 24.723007
+ type: 'test'
+ ...
+# Subtest: an ENOENT raised reading inside a retired asset, not at dest itself, is reported as unreadable
+ok 806 - an ENOENT raised reading inside a retired asset, not at dest itself, is reported as unreadable
+ ---
+ duration_ms: 25.807715
+ type: 'test'
+ ...
+# Subtest: a destination another client in the same run planned is never pruned
+ok 807 - a destination another client in the same run planned is never pruned
+ ---
+ duration_ms: 26.108332
+ type: 'test'
+ ...
+# Subtest: a recorded destination outside the client asset directories is refused out loud
+ok 808 - a recorded destination outside the client asset directories is refused out loud
+ ---
+ duration_ms: 19.98838
+ type: 'test'
+ ...
+# Subtest: a recorded destination deeper than a direct child is refused, digest or no digest
+ok 809 - a recorded destination deeper than a direct child is refused, digest or no digest
+ ---
+ duration_ms: 16.276679
+ type: 'test'
+ ...
+# Subtest: a recorded destination whose basename begins with ".." is refused, not read as a direct child
+ok 810 - a recorded destination whose basename begins with ".." is refused, not read as a direct child
+ ---
+ duration_ms: 26.42298
+ type: 'test'
+ ...
+# Subtest: removeClientAssets refuses a basename beginning with ".." on posix and win32 alike
+ok 811 - removeClientAssets refuses a basename beginning with ".." on posix and win32 alike
+ ---
+ duration_ms: 2.955462
+ type: 'test'
+ ...
+# Subtest: nothing is removed when this run installs nothing
+ok 812 - nothing is removed when this run installs nothing
+ ---
+ duration_ms: 10.740965
+ type: 'test'
+ ...
+# Subtest: a boot where a plugin failed to activate prunes nothing
+ok 813 - a boot where a plugin failed to activate prunes nothing
+ ---
+ duration_ms: 22.432605
+ type: 'test'
+ ...
+# Subtest: a skill the boot profile withheld is never read as retired
+ok 814 - a skill the boot profile withheld is never read as retired
+ ---
+ duration_ms: 24.811491
+ type: 'test'
+ ...
+# Subtest: a config-enabled plugin that is no longer installed is retired, and its skill prunes
+ok 815 - a config-enabled plugin that is no longer installed is retired, and its skill prunes
+ ---
+ duration_ms: 25.647632
+ type: 'test'
+ ...
+# Subtest: a directory and a file never share a content digest
+ok 816 - a directory and a file never share a content digest
+ ---
+ duration_ms: 2.946028
+ type: 'test'
+ ...
+# Subtest: a user's file that collides with a retired skill's digest is left in place
+ok 817 - a user's file that collides with a retired skill's digest is left in place
+ ---
+ duration_ms: 18.568482
+ type: 'test'
+ ...
+# Subtest: a user's empty file at an empty retired skill's path is left in place
+ok 818 - a user's empty file at an empty retired skill's path is left in place
+ ---
+ duration_ms: 22.58064
+ type: 'test'
+ ...
+# Subtest: the wizard finale says how many retired assets it removed
+ok 819 - the wizard finale says how many retired assets it removed
+ ---
+ duration_ms: 19.459587
+ type: 'test'
+ ...
+# Subtest: a record survives a dest moving to a client whose copy failed
+ok 820 - a record survives a dest moving to a client whose copy failed
+ ---
+ duration_ms: 23.774666
+ type: 'test'
+ ...
+# Subtest: claude undo restores a pre-existing foreign base URL byte-for-byte
+ok 821 - claude undo restores a pre-existing foreign base URL byte-for-byte
+ ---
+ duration_ms: 32.798771
+ type: 'test'
+ ...
+# Subtest: claude undo of a no-pre-existing-URL attach round-trips to empty
+ok 822 - claude undo of a no-pre-existing-URL attach round-trips to empty
+ ---
+ duration_ms: 9.71102
+ type: 'test'
+ ...
+# Subtest: claude undo removes managed ENABLE_TOOL_SEARCH without stamping the restored base URL onto it
+ok 823 - claude undo removes managed ENABLE_TOOL_SEARCH without stamping the restored base URL onto it
+ ---
+ duration_ms: 13.401158
+ type: 'test'
+ ...
+# Subtest: claude attach + undo leave a non-string user-owned env value byte-for-byte intact
+ok 824 - claude attach + undo leave a non-string user-owned env value byte-for-byte intact
+ ---
+ duration_ms: 9.4284
+ type: 'test'
+ ...
+# Subtest: claude undo removes the managed _CLAUDE_CODE_ASSUME_FIRST_PARTY_BASE_URL
+ok 825 - claude undo removes the managed _CLAUDE_CODE_ASSUME_FIRST_PARTY_BASE_URL
+ ---
+ duration_ms: 9.739553
+ type: 'test'
+ ...
+# Subtest: claude undo leaves a user-owned ENABLE_TOOL_SEARCH in place
+ok 826 - claude undo leaves a user-owned ENABLE_TOOL_SEARCH in place
+ ---
+ duration_ms: 12.13877
+ type: 'test'
+ ...
+# Subtest: claude undo strips marker + managed keys/hooks from a hand-written fixture (no plugin loaded)
+ok 827 - claude undo strips marker + managed keys/hooks from a hand-written fixture (no plugin loaded)
+ ---
+ duration_ms: 6.092079
+ type: 'test'
+ ...
+# Subtest: claude undo of a LEGACY pre-upgrade marker (no managed record) detaches fully
+ok 828 - claude undo of a LEGACY pre-upgrade marker (no managed record) detaches fully
+ ---
+ duration_ms: 5.685622
+ type: 'test'
+ ...
+# Subtest: claude undo of a LEGACY marker preserves a user hook and an externally-overridden base URL
+ok 829 - claude undo of a LEGACY marker preserves a user hook and an externally-overridden base URL
+ ---
+ duration_ms: 7.287006
+ type: 'test'
+ ...
+# Subtest: claude undo leaves an externally-overridden base URL in place with a warning
+ok 830 - claude undo leaves an externally-overridden base URL in place with a warning
+ ---
+ duration_ms: 17.156075
+ type: 'test'
+ ...
+# Subtest: claude undo preserves a user-owned non-managed hook for a managed event
+ok 831 - claude undo preserves a user-owned non-managed hook for a managed event
+ ---
+ duration_ms: 10.749968
+ type: 'test'
+ ...
+# Subtest: claude undo is a no-op when the marker is absent
+ok 832 - claude undo is a no-op when the marker is absent
+ ---
+ duration_ms: 2.777311
+ type: 'test'
+ ...
+# Subtest: claude undo is a no-op when the settings file is absent
+ok 833 - claude undo is a no-op when the settings file is absent
+ ---
+ duration_ms: 3.394449
+ type: 'test'
+ ...
+# Subtest: codex undo strips the managed blocks and restores model_provider byte-for-byte
+ok 834 - codex undo strips the managed blocks and restores model_provider byte-for-byte
+ ---
+ duration_ms: 8.965066
+ type: 'test'
+ ...
+# Subtest: codex undo of a no-previous-provider attach round-trips to empty
+ok 835 - codex undo of a no-previous-provider attach round-trips to empty
+ ---
+ duration_ms: 5.453249
+ type: 'test'
+ ...
+# Subtest: codex undo preserves unrelated config alongside the restored provider
+ok 836 - codex undo preserves unrelated config alongside the restored provider
+ ---
+ duration_ms: 6.492038
+ type: 'test'
+ ...
+# Subtest: codex undo strips a hand-written marked block (no plugin loaded)
+ok 837 - codex undo strips a hand-written marked block (no plugin loaded)
+ ---
+ duration_ms: 9.075023
+ type: 'test'
+ ...
+# Subtest: codex undo is a no-op when no managed block is present
+ok 838 - codex undo is a no-op when no managed block is present
+ ---
+ duration_ms: 2.022163
+ type: 'test'
+ ...
+# Subtest: codex undo warning carries the user value verbatim, so `warning` is never splittable
+ok 839 - codex undo warning carries the user value verbatim, so `warning` is never splittable
+ ---
+ duration_ms: 7.445056
+ type: 'test'
+ ...
+# Subtest: undo clears exactly what probeClientAttached detects, for both formats
+ok 840 - undo clears exactly what probeClientAttached detects, for both formats
+ ---
+ duration_ms: 15.551296
+ type: 'test'
+ ...
+# Subtest: undo is a no-op for a descriptor without an attachProbe
+ok 841 - undo is a no-op for a descriptor without an attachProbe
+ ---
+ duration_ms: 0.440189
+ type: 'test'
+ ...
+# Subtest: the atomic write unlinks the temp file on a partial write - no orphaned .tmp
+ok 842 - the atomic write unlinks the temp file on a partial write - no orphaned .tmp
+ ---
+ duration_ms: 10.535303
+ type: 'test'
+ ...
+# Subtest: claude undo names EVERY externally-overridden managed key in the warning, not just the last
+ok 843 - claude undo names EVERY externally-overridden managed key in the warning, not just the last
+ ---
+ duration_ms: 18.321057
+ type: 'test'
+ ...
+# Subtest: claude undo reports a single overridden key without the join separator
+ok 844 - claude undo reports a single overridden key without the join separator
+ ---
+ duration_ms: 9.130767
+ type: 'test'
+ ...
+# Subtest: claude undo reports a managed key the user overrode with a non-string
+ok 845 - claude undo reports a managed key the user overrode with a non-string
+ ---
+ duration_ms: 14.095384
+ type: 'test'
+ ...
+# Subtest: claude undo stays silent about a managed key the user deleted outright
+ok 846 - claude undo stays silent about a managed key the user deleted outright
+ ---
+ duration_ms: 12.67852
+ type: 'test'
+ ...
+# Subtest: claude undo of a LEGACY marker reports a base URL the user overrode with a non-string
+ok 847 - claude undo of a LEGACY marker reports a base URL the user overrode with a non-string
+ ---
+ duration_ms: 9.090978
+ type: 'test'
+ ...
+# Subtest: claude undo of a LEGACY marker stays silent when the base URL is absent
+ok 848 - claude undo of a LEGACY marker stays silent when the base URL is absent
+ ---
+ duration_ms: 14.222046
+ type: 'test'
+ ...
+# Subtest: claude undo does not report an Object.prototype-named managed key that is absent from settings
+ok 849 - claude undo does not report an Object.prototype-named managed key that is absent from settings
+ ---
+ duration_ms: 6.545629
+ type: 'test'
+ ...
+# Subtest: claude attach + undo restore a 8080 base URL byte-for-byte
+ok 850 - claude attach + undo restore a 8080 base URL byte-for-byte
+ ---
+ duration_ms: 14.450092
+ type: 'test'
+ ...
+# Subtest: claude attach + undo restore a false base URL byte-for-byte
+ok 851 - claude attach + undo restore a false base URL byte-for-byte
+ ---
+ duration_ms: 14.262607
+ type: 'test'
+ ...
+# Subtest: claude attach + undo restore a null base URL byte-for-byte
+ok 852 - claude attach + undo restore a null base URL byte-for-byte
+ ---
+ duration_ms: 8.166212
+ type: 'test'
+ ...
+# Subtest: claude re-attach carries a non-string base URL backup forward, and undo restores it
+ok 853 - claude re-attach carries a non-string base URL backup forward, and undo restores it
+ ---
+ duration_ms: 26.240933
+ type: 'test'
+ ...
+# Subtest: \#500 finding 3: a restored malformed-block backup is reported by path, not silently
+ok 854 - \#500 finding 3: a restored malformed-block backup is reported by path, not silently
+ ---
+ duration_ms: 19.635624
+ type: 'test'
+ ...
+# Subtest: \#500 finding 2: the delete-then-detach resurrection is announced, not silent
+ok 855 - \#500 finding 2: the delete-then-detach resurrection is announced, not silent
+ ---
+ duration_ms: 12.439336
+ type: 'test'
+ ...
+# Subtest: \#500 finding 1: the legacy branch replays a prev_malformed backup instead of dropping it
+ok 856 - \#500 finding 1: the legacy branch replays a prev_malformed backup instead of dropping it
+ ---
+ duration_ms: 20.518947
+ type: 'test'
+ ...
+# Subtest: \#500 finding 1: the legacy branch reports a prev_malformed backup it cannot put back
+ok 857 - \#500 finding 1: the legacy branch reports a prev_malformed backup it cannot put back
+ ---
+ duration_ms: 11.698921
+ type: 'test'
+ ...
+# Subtest: \#500 finding 1: the legacy branch restores prev_base_url rather than deleting the key
+ok 858 - \#500 finding 1: the legacy branch restores prev_base_url rather than deleting the key
+ ---
+ duration_ms: 14.828267
+ type: 'test'
+ ...
+# Subtest: \#500 finding 1: a GENUINE pre-record marker is reversed exactly as before, and says nothing new
+ok 859 - \#500 finding 1: a GENUINE pre-record marker is reversed exactly as before, and says nothing new
+ ---
+ duration_ms: 8.726834
+ type: 'test'
+ ...
+# Subtest: \#500 finding 3: `hyp detach` prints the restored block, and never its contents
+ok 860 - \#500 finding 3: `hyp detach` prints the restored block, and never its contents
+ ---
+ duration_ms: 29.066178
+ type: 'test'
+ ...
+# Subtest: \#500 finding 3: `hyp detach --json` echoes restored_paths, and never the contents
+ok 861 - \#500 finding 3: `hyp detach --json` echoes restored_paths, and never the contents
+ ---
+ duration_ms: 25.923641
+ type: 'test'
+ ...
+# Subtest: json_path undo deletes the two entries the gateway wrote
+ok 862 - json_path undo deletes the two entries the gateway wrote
+ ---
+ duration_ms: 13.735186
+ type: 'test'
+ ...
+# Subtest: json_path undo leaves a present-but-not-ours entry in place and warns by path
+ok 863 - json_path undo leaves a present-but-not-ours entry in place and warns by path
+ ---
+ duration_ms: 6.15787
+ type: 'test'
+ ...
+# Subtest: json_path undo does not touch or remark on a config that was never attached
+ok 864 - json_path undo does not touch or remark on a config that was never attached
+ ---
+ duration_ms: 7.383121
+ type: 'test'
+ ...
+# Subtest: json_path undo rerun finishes a cache purge the first pass left behind
+ok 865 - json_path undo rerun finishes a cache purge the first pass left behind
+ ---
+ duration_ms: 18.063675
+ type: 'test'
+ ...
+# Subtest: json_path undo rerun purges marked residue beside a user entry it leaves alone
+ok 866 - json_path undo rerun purges marked residue beside a user entry it leaves alone
+ ---
+ duration_ms: 14.867928
+ type: 'test'
+ ...
+# Subtest: json_path undo leaves a never-attached machine with no managed settings keys and marker-less cache rows untouched
+ok 867 - json_path undo leaves a never-attached machine with no managed settings keys and marker-less cache rows untouched
+ ---
+ duration_ms: 4.070748
+ type: 'test'
+ ...
+# Subtest: json_path undo deletes a stale entry from an old port: the signature is the whole test
+ok 868 - json_path undo deletes a stale entry from an old port: the signature is the whole test
+ ---
+ duration_ms: 5.88233
+ type: 'test'
+ ...
+# Subtest: json_path undo is a no-op when the settings file is absent
+ok 869 - json_path undo is a no-op when the settings file is absent
+ ---
+ duration_ms: 0.935472
+ type: 'test'
+ ...
+# Subtest: json_path undo keeps the cache row for a user entry it left in the settings
+ok 870 - json_path undo keeps the cache row for a user entry it left in the settings
+ ---
+ duration_ms: 11.452246
+ type: 'test'
+ ...
+# Subtest: json_path undo purges the derived caches, skipping one that will not parse
+ok 871 - json_path undo purges the derived caches, skipping one that will not parse
+ ---
+ duration_ms: 15.806524
+ type: 'test'
+ ...
+# Subtest: json_path undo purges the caches under a nested $OPENCLAW_HOME
+ok 872 - json_path undo purges the caches under a nested $OPENCLAW_HOME
+ ---
+ duration_ms: 13.338513
+ type: 'test'
+ ...
+# Subtest: the enable write is additive: unrelated plugins and keys survive, and the prior config is backed up
+ok 873 - the enable write is additive: unrelated plugins and keys survive, and the prior config is backed up
+ ---
+ duration_ms: 22.968219
+ type: 'test'
+ ...
+# Subtest: an entry the central layer already names is not duplicated into the local layer
+ok 874 - an entry the central layer already names is not duplicated into the local layer
+ ---
+ duration_ms: 3.876883
+ type: 'test'
+ ...
+# Subtest: an entry already in the local layer is not appended a second time
+ok 875 - an entry already in the local layer is not appended a second time
+ ---
+ duration_ms: 6.100843
+ type: 'test'
+ ...
+# Subtest: with no daemon service installed, restart and the bind wait are both skipped
+ok 876 - with no daemon service installed, restart and the bind wait are both skipped
+ ---
+ duration_ms: 3.008052
+ type: 'test'
+ ...
+# Subtest: with a daemon installed, the write is followed by a restart and a wait that sees the bound port
+ok 877 - with a daemon installed, the write is followed by a restart and a wait that sees the bound port
+ ---
+ duration_ms: 5.55956
+ type: 'test'
+ ...
+# Subtest: a restart failure is reported as the restart step, with the write and its backup intact
+ok 878 - a restart failure is reported as the restart step, with the write and its backup intact
+ ---
+ duration_ms: 2.487962
+ type: 'test'
+ ...
+# Subtest: a gateway that never binds times out into a reported wait failure, not a throw
+ok 879 - a gateway that never binds times out into a reported wait failure, not a throw
+ ---
+ duration_ms: 31.943841
+ type: 'test'
+ ...
+# Subtest: waitForGatewayBind returns as soon as status.json reports a bound port
+ok 880 - waitForGatewayBind returns as soon as status.json reports a bound port
+ ---
+ duration_ms: 1.945978
+ type: 'test'
+ ...
+# Subtest: waitForGatewayBind polls until the port appears
+ok 881 - waitForGatewayBind polls until the port appears
+ ---
+ duration_ms: 1.057778
+ type: 'test'
+ ...
+# Subtest: waitForGatewayBind returns { bound: false } on timeout instead of throwing
+ok 882 - waitForGatewayBind returns { bound: false } on timeout instead of throwing
+ ---
+ duration_ms: 26.979226
+ type: 'test'
+ ...
+# Subtest: waitForGatewayBind treats a throwing probe as "not yet", not as a failure
+ok 883 - waitForGatewayBind treats a throwing probe as "not yet", not as a failure
+ ---
+ duration_ms: 2.529935
+ type: 'test'
+ ...
+# Subtest: the bundled CLI clients declare a launch spec; each carries {prompt}
+ok 884 - the bundled CLI clients declare a launch spec; each carries {prompt}
+ ---
+ duration_ms: 26.859214
+ type: 'test'
+ ...
+# Subtest: a launch spec without {prompt} is dropped: launchable and mute is worse than not launchable
+ok 885 - a launch spec without {prompt} is dropped: launchable and mute is worse than not launchable
+ ---
+ duration_ms: 0.260697
+ type: 'test'
+ ...
+# Subtest: a malformed launch spec is dropped, and never fails catalog construction
+ok 886 - a malformed launch spec is dropped, and never fails catalog construction
+ ---
+ duration_ms: 0.226544
+ type: 'test'
+ ...
+# Subtest: a client with no launch spec stays unlaunchable (Claude Desktop has no prompt argument)
+ok 887 - a client with no launch spec stays unlaunchable (Claude Desktop has no prompt argument)
+ ---
+ duration_ms: 11.834528
+ type: 'test'
+ ...
+# Subtest: the bundled CLI clients' launch args never carry a permission-widening flag
+ok 888 - the bundled CLI clients' launch args never carry a permission-widening flag
+ ---
+ duration_ms: 6.857503
+ type: 'test'
+ ...
+# Subtest: the permission-widening denylist covers the documented equivalents
+ok 889 - the permission-widening denylist covers the documented equivalents
+ ---
+ duration_ms: 0.26981
+ type: 'test'
+ ...
+# Subtest: attach Claude (capitalized) resolves to the lowercase claude adapter
+ok 890 - attach Claude (capitalized) resolves to the lowercase claude adapter
+ ---
+ duration_ms: 21.845363
+ type: 'test'
+ ...
+# Subtest: attach ALL (uppercase sentinel) expands to every registered client
+ok 891 - attach ALL (uppercase sentinel) expands to every registered client
+ ---
+ duration_ms: 19.447599
+ type: 'test'
+ ...
+# Subtest: attach claude (already lowercase) is unchanged
+ok 892 - attach claude (already lowercase) is unchanged
+ ---
+ duration_ms: 7.219513
+ type: 'test'
+ ...
+# Subtest: detach Claude (capitalized) resolves via the client-descriptor map
+ok 893 - detach Claude (capitalized) resolves via the client-descriptor map
+ ---
+ duration_ms: 7.483024
+ type: 'test'
+ ...
+# Subtest: 'central': the owning plugin is declared by the central layer
+ok 894 - 'central': the owning plugin is declared by the central layer
+ ---
+ duration_ms: 1.003175
+ type: 'test'
+ ...
+# Subtest: 'local': in the effective config but not in the central layer
+ok 895 - 'local': in the effective config but not in the central layer
+ ---
+ duration_ms: 0.182958
+ type: 'test'
+ ...
+# Subtest: 'absent': the owning plugin is not in the effective config at all
+ok 896 - 'absent': the owning plugin is not in the effective config at all
+ ---
+ duration_ms: 0.892857
+ type: 'test'
+ ...
+# Subtest: 'absent': a source id that resolves to no plugin (conservative default)
+ok 897 - 'absent': a source id that resolves to no plugin (conservative default)
+ ---
+ duration_ms: 0.124109
+ type: 'test'
+ ...
+# Subtest: resolution falls back to clientDescriptors when no picker row matches
+ok 898 - resolution falls back to clientDescriptors when no picker row matches
+ ---
+ duration_ms: 0.127173
+ type: 'test'
+ ...
+# Subtest: picker descriptors are authoritative over a same-named client descriptor
+ok 899 - picker descriptors are authoritative over a same-named client descriptor
+ ---
+ duration_ms: 0.167645
+ type: 'test'
+ ...
+# Subtest: a solo host (no central layer) classifies every present source as local
+ok 900 - a solo host (no central layer) classifies every present source as local
+ ---
+ duration_ms: 0.125711
+ type: 'test'
+ ...
+# Subtest: a present-but-central source stays central even when central also enables it
+ok 901 - a present-but-central source stays central even when central also enables it
+ ---
+ duration_ms: 0.121094
+ type: 'test'
+ ...
+# Subtest: empty layered config: an in-catalog source with no config is absent
+ok 902 - empty layered config: an in-catalog source with no config is absent
+ ---
+ duration_ms: 0.372976
+ type: 'test'
+ ...
+# Subtest: clientSyncListPath derives /usage-policy/client-sync.json
+ok 903 - clientSyncListPath derives /usage-policy/client-sync.json
+ ---
+ duration_ms: 0.916012
+ type: 'test'
+ ...
+# Subtest: clientSyncListPath requires a stateDir
+ok 904 - clientSyncListPath requires a stateDir
+ ---
+ duration_ms: 0.283651
+ type: 'test'
+ ...
+# Subtest: readClientSyncEntries returns null when the store has never been written
+ok 905 - readClientSyncEntries returns null when the store has never been written
+ ---
+ duration_ms: 4.785604
+ type: 'test'
+ ...
+# Subtest: an empty stamped store reads as [], distinct from absent
+ok 906 - an empty stamped store reads as [], distinct from absent
+ ---
+ duration_ms: 3.100652
+ type: 'test'
+ ...
+# Subtest: write then read round-trips the entry set
+ok 907 - write then read round-trips the entry set
+ ---
+ duration_ms: 1.784041
+ type: 'test'
+ ...
+# Subtest: writeClientSyncEntries persists the version-1 shape
+ok 908 - writeClientSyncEntries persists the version-1 shape
+ ---
+ duration_ms: 2.4481
+ type: 'test'
+ ...
+# Subtest: writeClientSyncEntries dedupes by source (later wins) and sorts
+ok 909 - writeClientSyncEntries dedupes by source (later wins) and sorts
+ ---
+ duration_ms: 1.517176
+ type: 'test'
+ ...
+# Subtest: optedOutClientSourceIds lists sources from entries and treats null as none
+ok 910 - optedOutClientSourceIds lists sources from entries and treats null as none
+ ---
+ duration_ms: 0.200234
+ type: 'test'
+ ...
+# Subtest: readClientSyncEntries throws ClientSyncListUnreadableError on unparseable JSON
+ok 911 - readClientSyncEntries throws ClientSyncListUnreadableError on unparseable JSON
+ ---
+ duration_ms: 2.088274
+ type: 'test'
+ ...
+# Subtest: readClientSyncEntries throws on a wrong-shape file
+ok 912 - readClientSyncEntries throws on a wrong-shape file
+ ---
+ duration_ms: 3.451816
+ type: 'test'
+ ...
+# Subtest: readClientSyncEntries throws when an entry has an unknown class or empty source
+ok 913 - readClientSyncEntries throws when an entry has an unknown class or empty source
+ ---
+ duration_ms: 1.78251
+ type: 'test'
+ ...
+# Subtest: seedClientSyncStoreIfAbsent stamps an empty store once and is idempotent
+ok 914 - seedClientSyncStoreIfAbsent stamps an empty store once and is idempotent
+ ---
+ duration_ms: 1.659783
+ type: 'test'
+ ...
+# Subtest: seedClientSyncStoreIfAbsent never clobbers an existing entry list
+ok 915 - seedClientSyncStoreIfAbsent never clobbers an existing entry list
+ ---
+ duration_ms: 1.742668
+ type: 'test'
+ ...
+# Subtest: writeClientSyncEntries is atomic write-rename and leaves no temp files
+ok 916 - writeClientSyncEntries is atomic write-rename and leaves no temp files
+ ---
+ duration_ms: 1.28334
+ type: 'test'
+ ...
+# Subtest: a legacy rollout with no session_id reports none: the thread id is never back-filled
+ok 917 - a legacy rollout with no session_id reports none: the thread id is never back-filled
+ ---
+ duration_ms: 1.128585
+ type: 'test'
+ ...
+# Subtest: a subagent rollout keeps the container and the thread apart
+ok 918 - a subagent rollout keeps the container and the thread apart
+ ---
+ duration_ms: 0.212834
+ type: 'test'
+ ...
+# Subtest: a line that is not JSON at all resolves nothing
+ok 919 - a line that is not JSON at all resolves nothing
+ ---
+ duration_ms: 0.14479
+ type: 'test'
+ ...
+# Subtest: a JSON line that is not an object resolves nothing
+ok 920 - a JSON line that is not an object resolves nothing
+ ---
+ duration_ms: 0.129046
+ type: 'test'
+ ...
+# Subtest: another envelope type carrying id/session_id/cwd is not the header
+ok 921 - another envelope type carrying id/session_id/cwd is not the header
+ ---
+ duration_ms: 1.485588
+ type: 'test'
+ ...
+# Subtest: a missing, blank, or non-string envelope type is not session_meta either
+ok 922 - a missing, blank, or non-string envelope type is not session_meta either
+ ---
+ duration_ms: 0.160424
+ type: 'test'
+ ...
+# Subtest: a blank or non-string field is absent, never an empty value passed on
+ok 923 - a blank or non-string field is absent, never an empty value passed on
+ ---
+ duration_ms: 0.272394
+ type: 'test'
+ ...
+# Subtest: a session_meta header with no payload resolves no field
+ok 924 - a session_meta header with no payload resolves no field
+ ---
+ duration_ms: 0.668967
+ type: 'test'
+ ...
+# Subtest: a field that survives the blank test is returned byte-identical
+ok 925 - a field that survives the blank test is returned byte-identical
+ ---
+ duration_ms: 0.303031
+ type: 'test'
+ ...
+# Subtest: a relative session_meta.cwd is no cwd, not a path resolved against the daemon
+ok 926 - a relative session_meta.cwd is no cwd, not a path resolved against the daemon
+ ---
+ duration_ms: 0.450194
+ type: 'test'
+ ...
+# Subtest: an absolute cwd still resolves, and the ids are never path-tested
+ok 927 - an absolute cwd still resolves, and the ids are never path-tested
+ ---
+ duration_ms: 0.13785
+ type: 'test'
+ ...
+# Subtest: sessionMetaCwd is the one cwd predicate, usable by a caller that cannot delegate
+ok 928 - sessionMetaCwd is the one cwd predicate, usable by a caller that cannot delegate
+ ---
+ duration_ms: 0.081363
+ type: 'test'
+ ...
+# Subtest: readRolloutSessionMeta reads the first line and ignores the rest of the rollout
+ok 929 - readRolloutSessionMeta reads the first line and ignores the rest of the rollout
+ ---
+ duration_ms: 1.716249
+ type: 'test'
+ ...
+# Subtest: a rollout whose first line is longer than the read bound resolves nothing rather than half a line
+ok 930 - a rollout whose first line is longer than the read bound resolves nothing rather than half a line
+ ---
+ duration_ms: 0.598349
+ type: 'test'
+ ...
+# Subtest: an unreadable, empty, or absent rollout resolves nothing
+ok 931 - an unreadable, empty, or absent rollout resolves nothing
+ ---
+ duration_ms: 0.537407
+ type: 'test'
+ ...
+# Subtest: a rollout with no trailing newline is still one whole first line
+ok 932 - a rollout with no trailing newline is still one whole first line
+ ---
+ duration_ms: 0.305404
+ type: 'test'
+ ...
+# Subtest: Claude session-context hook exits 0 without --state-file
+ok 933 - Claude session-context hook exits 0 without --state-file
+ ---
+ duration_ms: 15.63254
+ type: 'test'
+ ...
+# Subtest: Claude session-context hook appends one JSONL record per event to --state-file
+ok 934 - Claude session-context hook appends one JSONL record per event to --state-file
+ ---
+ duration_ms: 43.404811
+ type: 'test'
+ ...
+# Subtest: Claude session-context hook ignores events without session context
+ok 935 - Claude session-context hook ignores events without session context
+ ---
+ duration_ms: 6.283481
+ type: 'test'
+ ...
+# Subtest: legacy Claude session-context hook --port writes the default plugin state file
+ok 936 - legacy Claude session-context hook --port writes the default plugin state file
+ ---
+ duration_ms: 15.737339
+ type: 'test'
+ ...
+# Subtest: hidden Claude hook command is omitted from top-level help
+ok 937 - hidden Claude hook command is omitted from top-level help
+ ---
+ duration_ms: 13.2904
+ type: 'test'
+ ...
+# Subtest: top-level help lists commands declared by config-active plugins
+ok 938 - top-level help lists commands declared by config-active plugins
+ ---
+ duration_ms: 6.909061
+ type: 'test'
+ ...
+# Subtest: top-level help lists a local plugin addition on a fleet-joined host
+ok 939 - top-level help lists a local plugin addition on a fleet-joined host
+ ---
+ duration_ms: 9.024767
+ type: 'test'
+ ...
+# Subtest: top-level help omits plugin commands when the plugin is disabled
+ok 940 - top-level help omits plugin commands when the plugin is disabled
+ ---
+ duration_ms: 6.079741
+ type: 'test'
+ ...
+# Subtest: top-level help lists the installed plugin that replaces an excluded bundled skeleton, not the skeleton it shadows
+ok 941 - top-level help lists the installed plugin that replaces an excluded bundled skeleton, not the skeleton it shadows
+ ---
+ duration_ms: 7.530476
+ type: 'test'
+ ...
+# Subtest: top-level help advertises no commands for an installed plugin that shadows a bundled first-party name
+ok 942 - top-level help advertises no commands for an installed plugin that shadows a bundled first-party name
+ ---
+ duration_ms: 12.450223
+ type: 'test'
+ ...
+# Subtest: bare group with no command of its own renders synthesized group help
+ok 943 - bare group with no command of its own renders synthesized group help
+ ---
+ duration_ms: 0.687876
+ type: 'test'
+ ...
+# Subtest: group --help renders synthesized group help
+ok 944 - group --help renders synthesized group help
+ ---
+ duration_ms: 0.253005
+ type: 'test'
+ ...
+# Subtest: group with an unknown subcommand reports it and exits 2
+ok 945 - group with an unknown subcommand reports it and exits 2
+ ---
+ duration_ms: 0.323271
+ type: 'test'
+ ...
+# Subtest: top-level help collapses subcommands into one row per group
+ok 946 - top-level help collapses subcommands into one row per group
+ ---
+ duration_ms: 16.623816
+ type: 'test'
+ ...
+# Subtest: group --help lists subcommands with their registry summaries
+ok 947 - group --help lists subcommands with their registry summaries
+ ---
+ duration_ms: 0.759926
+ type: 'test'
+ ...
+# Subtest: an action command with subcommands gets group help on --help
+ok 948 - an action command with subcommands gets group help on --help
+ ---
+ duration_ms: 0.64445
+ type: 'test'
+ ...
+# Subtest: leaf command --help renders summary, usage, and long help
+ok 949 - leaf command --help renders summary, usage, and long help
+ ---
+ duration_ms: 0.425076
+ type: 'test'
+ ...
+# Subtest: a leaf subcommand --help documents every flag the command accepts
+ok 950 - a leaf subcommand --help documents every flag the command accepts
+ ---
+ duration_ms: 3.494041
+ type: 'test'
+ ...
+# Subtest: bare group command with an unknown subcommand reports the registry children
+ok 951 - bare group command with an unknown subcommand reports the registry children
+ ---
+ duration_ms: 0.527542
+ type: 'test'
+ ...
+# Subtest: a token that is neither a command nor a group prefix still errors
+ok 952 - a token that is neither a command nor a group prefix still errors
+ ---
+ duration_ms: 8.143437
+ type: 'test'
+ ...
+# Subtest: dispatch surfaces boot-path sink materialization warnings
+ok 953 - dispatch surfaces boot-path sink materialization warnings
+ ---
+ duration_ms: 12.008201
+ type: 'test'
+ ...
+# Subtest: zero-plugin lifecycle commands skip sink materialization warnings; config-profile commands still warn
+ok 954 - zero-plugin lifecycle commands skip sink materialization warnings; config-profile commands still warn
+ ---
+ duration_ms: 20.682556
+ type: 'test'
+ ...
+# Subtest: walkthrough boot skips sink warnings for config-named plugins its profile excludes; unnamed ones still warn
+ok 955 - walkthrough boot skips sink warnings for config-named plugins its profile excludes; unnamed ones still warn
+ ---
+ duration_ms: 133.775225
+ type: 'test'
+ ...
+# Subtest: walkthrough boot still warns when a config-named sink plugin is uninstalled or unresolvable
+ok 956 - walkthrough boot still warns when a config-named sink plugin is uninstalled or unresolvable
+ ---
+ duration_ms: 41.796847
+ type: 'test'
+ ...
+# Subtest: attach accepts a positional client name
+ok 957 - attach accepts a positional client name
+ ---
+ duration_ms: 7.545188
+ type: 'test'
+ ...
+# Subtest: unattach alias routes a positional client through the core disk undo
+ok 958 - unattach alias routes a positional client through the core disk undo
+ ---
+ duration_ms: 12.482613
+ type: 'test'
+ ...
+# Subtest: attach rejects conflicting positional and flag client names
+ok 959 - attach rejects conflicting positional and flag client names
+ ---
+ duration_ms: 1.201787
+ type: 'test'
+ ...
+# Subtest: dispatch forwards a real stdin to command run when the caller omits opts.stdin
+ok 960 - dispatch forwards a real stdin to command run when the caller omits opts.stdin
+ ---
+ duration_ms: 0.281458
+ type: 'test'
+ ...
+# Subtest: ctx.commands.run dispatches a registered command with the same exit code and output as the CLI path
+ok 961 - ctx.commands.run dispatches a registered command with the same exit code and output as the CLI path
+ ---
+ duration_ms: 0.644199
+ type: 'test'
+ ...
+# Subtest: ctx.commands.run activates a config-enabled plugin the boot profile skipped, so its command dispatches
+ok 962 - ctx.commands.run activates a config-enabled plugin the boot profile skipped, so its command dispatches
+ ---
+ duration_ms: 20.703357
+ type: 'test'
+ ...
+# Subtest: ctx.commands.run does not activate a plugin the effective config leaves out
+ok 963 - ctx.commands.run does not activate a plugin the effective config leaves out
+ ---
+ duration_ms: 12.031187
+ type: 'test'
+ ...
+# Subtest: askableClients returns only attached clients when the probe succeeds
+ok 964 - askableClients returns only attached clients when the probe succeeds
+ ---
+ duration_ms: 0.891625
+ type: 'test'
+ ...
+# Subtest: askableClients returns an empty list when the probe succeeds with nothing attached, rather than falling back
+ok 965 - askableClients returns an empty list when the probe succeeds with nothing attached, rather than falling back
+ ---
+ duration_ms: 0.109776
+ type: 'test'
+ ...
+# Subtest: askableClients falls back to launchable clients only when the probe throws
+ok 966 - askableClients falls back to launchable clients only when the probe throws
+ ---
+ duration_ms: 24.86991
+ type: 'test'
+ ...
+# Subtest: runAsk: no-launcher exits 1 on a fresh install with nothing attached
+ok 967 - runAsk: no-launcher exits 1 on a fresh install with nothing attached
+ ---
+ duration_ms: 20.566119
+ type: 'test'
+ ...
+# Subtest: runAsk: --list exits 0 regardless of launchability
+ok 968 - runAsk: --list exits 0 regardless of launchability
+ ---
+ duration_ms: 17.078517
+ type: 'test'
+ ...
+# Subtest: runAsk: --list on a host with nothing launchable prints the manual fallback, not a launch promise
+ok 969 - runAsk: --list on a host with nothing launchable prints the manual fallback, not a launch promise
+ ---
+ duration_ms: 12.92157
+ type: 'test'
+ ...
+# Subtest: claude alone composes the gateway + anthropic upstream + claude adapter
+ok 970 - claude alone composes the gateway + anthropic upstream + claude adapter
+ ---
+ duration_ms: 20.900967
+ type: 'test'
+ ...
+# Subtest: codex alone composes the gateway + openai + chatgpt upstreams + codex adapter
+ok 971 - codex alone composes the gateway + openai + chatgpt upstreams + codex adapter
+ ---
+ duration_ms: 11.862881
+ type: 'test'
+ ...
+# Subtest: raw-anthropic alone composes only the gateway + anthropic upstream (no adapter plugin)
+ok 972 - raw-anthropic alone composes only the gateway + anthropic upstream (no adapter plugin)
+ ---
+ duration_ms: 8.381639
+ type: 'test'
+ ...
+# Subtest: raw-openai alone composes only the gateway + openai upstream (no chatgpt, no adapter plugin)
+ok 973 - raw-openai alone composes only the gateway + openai upstream (no chatgpt, no adapter plugin)
+ ---
+ duration_ms: 4.955512
+ type: 'test'
+ ...
+# Subtest: otel alone composes the otel receiver, no gateway
+ok 974 - otel alone composes the otel receiver, no gateway
+ ---
+ duration_ms: 4.432707
+ type: 'test'
+ ...
+# Subtest: openclaw alone composes the gateway + anthropic upstream + openclaw adapter
+ok 975 - openclaw alone composes the gateway + anthropic upstream + openclaw adapter
+ ---
+ duration_ms: 6.203589
+ type: 'test'
+ ...
+# Subtest: hermes alone composes the gateway (no upstreams) + hermes adapter
+ok 976 - hermes alone composes the gateway (no upstreams) + hermes adapter
+ ---
+ duration_ms: 2.856821
+ type: 'test'
+ ...
+# Subtest: claude + hermes share the gateway; hermes adds no upstream
+ok 977 - claude + hermes share the gateway; hermes adds no upstream
+ ---
+ duration_ms: 2.735648
+ type: 'test'
+ ...
+# Subtest: claude + codex union the anthropic/openai/chatgpt upstreams and both adapters
+ok 978 - claude + codex union the anthropic/openai/chatgpt upstreams and both adapters
+ ---
+ duration_ms: 4.476393
+ type: 'test'
+ ...
+# Subtest: all five sources dedupe upstreams by name and order otel before the export sinks
+ok 979 - all five sources dedupe upstreams by name and order otel before the export sinks
+ ---
+ duration_ms: 10.069796
+ type: 'test'
+ ...
+# Subtest: no sources picked still writes a valid config with just the export sinks
+ok 980 - no sources picked still writes a valid config with just the export sinks
+ ---
+ duration_ms: 11.641554
+ type: 'test'
+ ...
+# Subtest: keep-local export omits the sink plugins and sinks block
+ok 981 - keep-local export omits the sink plugins and sinks block
+ ---
+ duration_ms: 4.400629
+ type: 'test'
+ ...
+# Subtest: configure-later export behaves like keep-local (no sinks block)
+ok 982 - configure-later export behaves like keep-local (no sinks block)
+ ---
+ duration_ms: 3.980801
+ type: 'test'
+ ...
+# Subtest: real claude/codex picker rows carry the settings_file detect probe
+ok 983 - real claude/codex picker rows carry the settings_file detect probe
+ ---
+ duration_ms: 8.161224
+ type: 'test'
+ ...
+# Subtest: real openclaw/hermes picker rows carry the settings_file detect probe
+ok 984 - real openclaw/hermes picker rows carry the settings_file detect probe
+ ---
+ duration_ms: 4.077017
+ type: 'test'
+ ...
+# Subtest: no bundled plugin manifest fails validation
+ok 985 - no bundled plugin manifest fails validation
+ ---
+ duration_ms: 5.749088
+ type: 'test'
+ ...
+# Subtest: claude-desktop composes the gateway, the credential plugin, and its own adapter
+ok 986 - claude-desktop composes the gateway, the credential plugin, and its own adapter
+ ---
+ duration_ms: 8.627603
+ type: 'test'
+ ...
+# Subtest: the claude-desktop row composes its required-capability provider, not just its adapter
+ok 987 - the claude-desktop row composes its required-capability provider, not just its adapter
+ ---
+ duration_ms: 8.360728
+ type: 'test'
+ ...
+# Subtest: claude + claude-desktop share one anthropic upstream and one gateway
+ok 988 - claude + claude-desktop share one anthropic upstream and one gateway
+ ---
+ duration_ms: 5.464305
+ type: 'test'
+ ...
+# Subtest: every needs_setup picker row composes the plugin that owns its configure_command
+ok 989 - every needs_setup picker row composes the plugin that owns its configure_command
+ ---
+ duration_ms: 6.610558
+ type: 'test'
+ ...
+# Subtest: picking a gateway client composes the graph pair with it
+ok 990 - picking a gateway client composes the graph pair with it
+ ---
+ duration_ms: 12.66549
+ type: 'test'
+ ...
+# Subtest: a gateway-free pick composes neither graph plugin
+ok 991 - a gateway-free pick composes neither graph plugin
+ ---
+ duration_ms: 4.66475
+ type: 'test'
+ ...
+# Subtest: a reconfigure that unpicks the gateway drops the graph pair
+ok 992 - a reconfigure that unpicks the gateway drops the graph pair
+ ---
+ duration_ms: 3.774286
+ type: 'test'
+ ...
+# Subtest: a hand-added non-rider plugin survives a reconfigure that composes riders
+ok 993 - a hand-added non-rider plugin survives a reconfigure that composes riders
+ ---
+ duration_ms: 5.012188
+ type: 'test'
+ ...
+# Subtest: a rider that rides another rider still composes
+ok 994 - a rider that rides another rider still composes
+ ---
+ duration_ms: 4.456664
+ type: 'test'
+ ...
+# Subtest: no composeWith map means no riders
+ok 995 - no composeWith map means no riders
+ ---
+ duration_ms: 4.178581
+ type: 'test'
+ ...
+# Subtest: a user's `enabled: false` on a rider survives a reconfigure
+ok 996 - a user's `enabled: false` on a rider survives a reconfigure
+ ---
+ duration_ms: 4.117018
+ type: 'test'
+ ...
+# Subtest: a picked plugin still loses a stale `enabled: false`
+ok 997 - a picked plugin still loses a stale `enabled: false`
+ ---
+ duration_ms: 4.231681
+ type: 'test'
+ ...
+# Subtest: an excluded plugin declaring compose_with is not composed
+ok 998 - an excluded plugin declaring compose_with is not composed
+ ---
+ duration_ms: 2.402982
+ type: 'test'
+ ...
+# Subtest: an injected catalog cannot smuggle an excluded rider through resolvePickSeeding
+ok 999 - an injected catalog cannot smuggle an excluded rider through resolvePickSeeding
+ ---
+ duration_ms: 3.215227
+ type: 'test'
+ ...
+# Subtest: no excluded bundled manifest declares compose_with
+ok 1000 - no excluded bundled manifest declares compose_with
+ ---
+ duration_ms: 10.262678
+ type: 'test'
+ ...
+# Subtest: bundled plugin: pinned version mismatch is a bundled_version_mismatch failure
+ok 1001 - bundled plugin: pinned version mismatch is a bundled_version_mismatch failure
+ ---
+ duration_ms: 12.208506
+ type: 'test'
+ ...
+# Subtest: bundled plugin: matching version pin is satisfied without an install; hash is not checked
+ok 1002 - bundled plugin: matching version pin is satisfied without an install; hash is not checked
+ ---
+ duration_ms: 7.239944
+ type: 'test'
+ ...
+# Subtest: bundled plugin: an unpinned entry is satisfied by any bundled version
+ok 1003 - bundled plugin: an unpinned entry is satisfied by any bundled version
+ ---
+ duration_ms: 4.784252
+ type: 'test'
+ ...
+# Subtest: disabled entries are skipped entirely
+ok 1004 - disabled entries are skipped entirely
+ ---
+ duration_ms: 2.968192
+ type: 'test'
+ ...
+# Subtest: an installed lock entry matching version + hash is satisfied without re-install
+ok 1005 - an installed lock entry matching version + hash is satisfied without re-install
+ ---
+ duration_ms: 28.114722
+ type: 'test'
+ ...
+# Subtest: a fetched artifact failing its hash pin is an artifact_hash_mismatch and nothing is installed
+ok 1006 - a fetched artifact failing its hash pin is an artifact_hash_mismatch and nothing is installed
+ ---
+ duration_ms: 146.804769
+ type: 'test'
+ ...
+# Subtest: a correct hash pin installs, and validation then sees the plugin it could not know before
+ok 1007 - a correct hash pin installs, and validation then sees the plugin it could not know before
+ ---
+ duration_ms: 224.221554
+ type: 'test'
+ ...
+# Subtest: an expired first apply with no distinct previous slot does not mark the active etag bad
+ok 1008 - an expired first apply with no distinct previous slot does not mark the active etag bad
+ ---
+ duration_ms: 7.664079
+ type: 'test'
+ ...
+# Subtest: the watchdog does not loop-restart when a no-op rollback has no target
+ok 1009 - the watchdog does not loop-restart when a no-op rollback has no target
+ ---
+ duration_ms: 103.075625
+ type: 'test'
+ ...
+# Subtest: boot recovers an already-wedged active slot by clearing the contradictory bad_etag (re-pull)
+ok 1010 - boot recovers an already-wedged active slot by clearing the contradictory bad_etag (re-pull)
+ ---
+ duration_ms: 2.483935
+ type: 'test'
+ ...
+# Subtest: boot recovers a wedged active slot by falling back to the seed when one survives
+ok 1011 - boot recovers a wedged active slot by falling back to the seed when one survives
+ ---
+ duration_ms: 2.575254
+ type: 'test'
+ ...
+# Subtest: apply validation rejects a malformed plugin backfill block via the live section validator
+ok 1012 - apply validation rejects a malformed plugin backfill block via the live section validator
+ ---
+ duration_ms: 113.143918
+ type: 'test'
+ ...
+# Subtest: apply validates a backfill block for a plugin the document INTRODUCES but is not active yet
+ok 1013 - apply validates a backfill block for a plugin the document INTRODUCES but is not active yet
+ ---
+ duration_ms: 37.832632
+ type: 'test'
+ ...
+# Subtest: introduced-plugin discovery rejects a malformed block even without the live registry
+ok 1014 - introduced-plugin discovery rejects a malformed block even without the live registry
+ ---
+ duration_ms: 19.709056
+ type: 'test'
+ ...
+# Subtest: stage applies a document: slot persisted, pointer flipped, etag staged, probation armed, restart requested
+ok 1015 - stage applies a document: slot persisted, pointer flipped, etag staged, probation armed, restart requested
+ ---
+ duration_ms: 14.722967
+ type: 'test'
+ ...
+# Subtest: probation window is max(3 Ă— poll interval, floor) from the staged document
+ok 1016 - probation window is max(3 Ă— poll interval, floor) from the staged document
+ ---
+ duration_ms: 3.550777
+ type: 'test'
+ ...
+# Subtest: stage before attachApplyDeps fails closed
+ok 1017 - stage before attachApplyDeps fails closed
+ ---
+ duration_ms: 1.445156
+ type: 'test'
+ ...
+# Subtest: validation failure remembers the bad etag and leaves the central layer untouched
+ok 1018 - validation failure remembers the bad etag and leaves the central layer untouched
+ ---
+ duration_ms: 2.337783
+ type: 'test'
+ ...
+# Subtest: pinned plugins install before full validation, so a config can name a not-yet-installed plugin
+ok 1019 - pinned plugins install before full validation, so a config can name a not-yet-installed plugin
+ ---
+ duration_ms: 2.141194
+ type: 'test'
+ ...
+# Subtest: a shape-invalid document is rejected before any install runs
+ok 1020 - a shape-invalid document is rejected before any install runs
+ ---
+ duration_ms: 2.017226
+ type: 'test'
+ ...
+# Subtest: a remembered bad etag backs off re-apply until the etag changes
+ok 1021 - a remembered bad etag backs off re-apply until the etag changes
+ ---
+ duration_ms: 3.056155
+ type: 'test'
+ ...
+# Subtest: pinned install hash mismatch is an apply failure with a structured reason
+ok 1022 - pinned install hash mismatch is an apply failure with a structured reason
+ ---
+ duration_ms: 3.46715
+ type: 'test'
+ ...
+# Subtest: oversized documents are rejected before validation
+ok 1023 - oversized documents are rejected before validation
+ ---
+ duration_ms: 12.515412
+ type: 'test'
+ ...
+# Subtest: staging the running etag is a no-op
+ok 1024 - staging the running etag is a no-op
+ ---
+ duration_ms: 4.537116
+ type: 'test'
+ ...
+# Subtest: a second stage in the same process is refused while a restart is pending
+ok 1025 - a second stage in the same process is refused while a restart is pending
+ ---
+ duration_ms: 5.536244
+ type: 'test'
+ ...
+# Subtest: confirmPoll clears probation
+ok 1026 - confirmPoll clears probation
+ ---
+ duration_ms: 3.114233
+ type: 'test'
+ ...
+# Subtest: onConfirmed fires exactly on the probation active→cleared edge, not on a no-probation poll
+ok 1027 - onConfirmed fires exactly on the probation active→cleared edge, not on a no-probation poll
+ ---
+ duration_ms: 4.021202
+ type: 'test'
+ ...
+# Subtest: chained applies alternate slots and roll back one revision
+ok 1028 - chained applies alternate slots and roll back one revision
+ ---
+ duration_ms: 3.200614
+ type: 'test'
+ ...
+# Subtest: evaluateAtBoot rolls an expired first apply back onto the seed
+ok 1029 - evaluateAtBoot rolls an expired first apply back onto the seed
+ ---
+ duration_ms: 2.472157
+ type: 'test'
+ ...
+# Subtest: evaluateAtBoot keeps an unexpired probation marker
+ok 1030 - evaluateAtBoot keeps an unexpired probation marker
+ ---
+ duration_ms: 1.581063
+ type: 'test'
+ ...
+# Subtest: evaluateAtBoot discards a probation marker whose flip never committed
+ok 1031 - evaluateAtBoot discards a probation marker whose flip never committed
+ ---
+ duration_ms: 1.8686
+ type: 'test'
+ ...
+# Subtest: the probation watchdog rolls back and requests a restart on expiry
+ok 1032 - the probation watchdog rolls back and requests a restart on expiry
+ ---
+ duration_ms: 102.666563
+ type: 'test'
+ ...
+# Subtest: a confirmed poll disarms the watchdog before it fires
+ok 1033 - a confirmed poll disarms the watchdog before it fires
+ ---
+ duration_ms: 107.563166
+ type: 'test'
+ ...
+# Subtest: readConfigControlStatus reads without an engine and tolerates a fresh install
+ok 1034 - readConfigControlStatus reads without an engine and tolerates a fresh install
+ ---
+ duration_ms: 3.614193
+ type: 'test'
+ ...
+# Subtest: parseConfigShape accepts and validates plugin pin fields
+ok 1035 - parseConfigShape accepts and validates plugin pin fields
+ ---
+ duration_ms: 0.238743
+ type: 'test'
+ ...
+# Subtest: no central layer: effective is the local layer verbatim
+ok 1036 - no central layer: effective is the local layer verbatim
+ ---
+ duration_ms: 0.920378
+ type: 'test'
+ ...
+# Subtest: both layers absent: effective is an empty v2 config
+ok 1037 - both layers absent: effective is an empty v2 config
+ ---
+ duration_ms: 0.219554
+ type: 'test'
+ ...
+# Subtest: local adds plugins/sinks the central layer does not name
+ok 1038 - local adds plugins/sinks the central layer does not name
+ ---
+ duration_ms: 0.157319
+ type: 'test'
+ ...
+# Subtest: central wins and locks: a colliding local plugin/sink is dropped
+ok 1039 - central wins and locks: a colliding local plugin/sink is dropped
+ ---
+ duration_ms: 5.110808
+ type: 'test'
+ ...
+# Subtest: query is local-only: the local block wins, a central query block is ignored
+ok 1040 - query is local-only: the local block wins, a central query block is ignored
+ ---
+ duration_ms: 0.141415
+ type: 'test'
+ ...
+# Subtest: resolveLayeredConfig: a valid-in-isolation local addition that invalidates the merge is dropped
+ok 1041 - resolveLayeredConfig: a valid-in-isolation local addition that invalidates the merge is dropped
+ ---
+ duration_ms: 0.319054
+ type: 'test'
+ ...
+# Subtest: resolveLayeredConfig: an error the central layer carries alone never drops a local entry
+ok 1042 - resolveLayeredConfig: an error the central layer carries alone never drops a local entry
+ ---
+ duration_ms: 0.166483
+ type: 'test'
+ ...
+# Subtest: resolveLayeredConfig: collisions and invalid additions both surface as drops
+ok 1043 - resolveLayeredConfig: collisions and invalid additions both surface as drops
+ ---
+ duration_ms: 0.153503
+ type: 'test'
+ ...
+# Subtest: resolveLayeredConfig: no central layer is a pure passthrough (never validated)
+ok 1044 - resolveLayeredConfig: no central layer is a pure passthrough (never validated)
+ ---
+ duration_ms: 0.265263
+ type: 'test'
+ ...
+# Subtest: defaultConfigPath resolves the v2 config basename under HYP_HOME
+ok 1045 - defaultConfigPath resolves the v2 config basename under HYP_HOME
+ ---
+ duration_ms: 1.890574
+ type: 'test'
+ ...
+# Subtest: parseConfigShape accepts the supported v2 config shape
+ok 1046 - parseConfigShape accepts the supported v2 config shape
+ ---
+ duration_ms: 0.759254
+ type: 'test'
+ ...
+# Subtest: parseConfigShape reports stable pointers for malformed config
+ok 1047 - parseConfigShape reports stable pointers for malformed config
+ ---
+ duration_ms: 0.575414
+ type: 'test'
+ ...
+# Subtest: parseConfigShape preserves query.cache.maintenance.compact_batch_bytes
+ok 1048 - parseConfigShape preserves query.cache.maintenance.compact_batch_bytes
+ ---
+ duration_ms: 0.224591
+ type: 'test'
+ ...
+# Subtest: parseConfigShape rejects a negative compact_batch_bytes
+ok 1049 - parseConfigShape rejects a negative compact_batch_bytes
+ ---
+ duration_ms: 0.138661
+ type: 'test'
+ ...
+# Subtest: validateConfig catches cross-plugin and schedule errors
+ok 1050 - validateConfig catches cross-plugin and schedule errors
+ ---
+ duration_ms: 1.890063
+ type: 'test'
+ ...
+# Subtest: validateConfig dispatches plugin-specific section validators
+ok 1051 - validateConfig dispatches plugin-specific section validators
+ ---
+ duration_ms: 0.464286
+ type: 'test'
+ ...
+# Subtest: isCronExpression accepts narrow standard cron and rejects aliases
+ok 1052 - isCronExpression accepts narrow standard cron and rejects aliases
+ ---
+ duration_ms: 0.356371
+ type: 'test'
+ ...
+# Subtest: diagnoseV1Config reports advisory product wiring gaps
+ok 1053 - diagnoseV1Config reports advisory product wiring gaps
+ ---
+ duration_ms: 32.220322
+ type: 'test'
+ ...
+# Subtest: diagnoseV1Config falls back to first-party client descriptors
+ok 1054 - diagnoseV1Config falls back to first-party client descriptors
+ ---
+ duration_ms: 0.612681
+ type: 'test'
+ ...
+# Subtest: diagnoseV1Config emits descriptor-derived upstream diagnostic kinds
+ok 1055 - diagnoseV1Config emits descriptor-derived upstream diagnostic kinds
+ ---
+ duration_ms: 0.295289
+ type: 'test'
+ ...
+# Subtest: buildPluginCatalog derives capability metadata from bundled manifests
+ok 1056 - buildPluginCatalog derives capability metadata from bundled manifests
+ ---
+ duration_ms: 6.831553
+ type: 'test'
+ ...
+# Subtest: buildPluginCatalog extracts client descriptors from manifests
+ok 1057 - buildPluginCatalog extracts client descriptors from manifests
+ ---
+ duration_ms: 12.824932
+ type: 'test'
+ ...
+# Subtest: buildPluginCatalog reads contributes.picker into pickerDescriptors, keyed by row name
+ok 1058 - buildPluginCatalog reads contributes.picker into pickerDescriptors, keyed by row name
+ ---
+ duration_ms: 0.527572
+ type: 'test'
+ ...
+# Subtest: buildPluginCatalog picker descriptors are first-manifest-wins on a name collision
+ok 1059 - buildPluginCatalog picker descriptors are first-manifest-wins on a name collision
+ ---
+ duration_ms: 0.117369
+ type: 'test'
+ ...
+# Subtest: buildPluginCatalog collects known datasets from manifest contributions
+ok 1060 - buildPluginCatalog collects known datasets from manifest contributions
+ ---
+ duration_ms: 8.608383
+ type: 'test'
+ ...
+# Subtest: buildPluginCatalog includes excluded gascity plugin as a catalog entry
+ok 1061 - buildPluginCatalog includes excluded gascity plugin as a catalog entry
+ ---
+ duration_ms: 3.018478
+ type: 'test'
+ ...
+# Subtest: validateConfig uses catalog-derived metadata for sink validation
+ok 1062 - validateConfig uses catalog-derived metadata for sink validation
+ ---
+ duration_ms: 6.166302
+ type: 'test'
+ ...
+# Subtest: validateConfig with catalog rejects @hypaware/central as blob-sink writer
+ok 1063 - validateConfig with catalog rejects @hypaware/central as blob-sink writer
+ ---
+ duration_ms: 8.501181
+ type: 'test'
+ ...
+# Subtest: validateConfig with catalog accepts @hypaware/central as request sink
+ok 1064 - validateConfig with catalog accepts @hypaware/central as request sink
+ ---
+ duration_ms: 3.313677
+ type: 'test'
+ ...
+# Subtest: buildPluginCatalog merges installed manifests with bundled
+ok 1065 - buildPluginCatalog merges installed manifests with bundled
+ ---
+ duration_ms: 0.179633
+ type: 'test'
+ ...
+# Subtest: buildPluginCatalog bundled wins over installed on name collision
+ok 1066 - buildPluginCatalog bundled wins over installed on name collision
+ ---
+ duration_ms: 0.093822
+ type: 'test'
+ ...
+# Subtest: diagnoseV1Config treats ChatGPT as a valid Codex upstream
+ok 1067 - diagnoseV1Config treats ChatGPT as a valid Codex upstream
+ ---
+ duration_ms: 2.902221
+ type: 'test'
+ ...
+# Subtest: isSafeContributionName accepts plain basenames
+ok 1068 - isSafeContributionName accepts plain basenames
+ ---
+ duration_ms: 0.799585
+ type: 'test'
+ ...
+# Subtest: isSafeContributionName rejects traversal and separators
+ok 1069 - isSafeContributionName rejects traversal and separators
+ ---
+ duration_ms: 0.214155
+ type: 'test'
+ ...
+# Subtest: isSafeContributionName rejects non-strings
+ok 1070 - isSafeContributionName rejects non-strings
+ ---
+ duration_ms: 0.117959
+ type: 'test'
+ ...
+# Subtest: isWithinDir accepts the base dir and paths beneath it
+ok 1071 - isWithinDir accepts the base dir and paths beneath it
+ ---
+ duration_ms: 1.253445
+ type: 'test'
+ ...
+# Subtest: isWithinDir rejects paths that escape the base dir
+ok 1072 - isWithinDir rejects paths that escape the base dir
+ ---
+ duration_ms: 0.295339
+ type: 'test'
+ ...
+# Subtest: seam records the proven-bound localEndpoint as the daemon attach endpoint
+ok 1073 - seam records the proven-bound localEndpoint as the daemon attach endpoint
+ ---
+ duration_ms: 0.873407
+ type: 'test'
+ ...
+# Subtest: seam does NOT fall back to the configured listen when localEndpoint() throws (no URL for an unbound port)
+ok 1074 - seam does NOT fall back to the configured listen when localEndpoint() throws (no URL for an unbound port)
+ ---
+ duration_ms: 0.179052
+ type: 'test'
+ ...
+# Subtest: seam is inert (no clients/endpoint) when the ai-gateway capability is absent
+ok 1075 - seam is inert (no clients/endpoint) when the ai-gateway capability is absent
+ ---
+ duration_ms: 0.142556
+ type: 'test'
+ ...
+# Subtest: the daemon tick runs a sweep-bearing backfill contribution
+ok 1076 - the daemon tick runs a sweep-bearing backfill contribution
+ ---
+ duration_ms: 410.27635
+ type: 'test'
+ ...
+# Subtest: tick fires only the sweep-bearing contributions that are cron-due
+ok 1077 - tick fires only the sweep-bearing contributions that are cron-due
+ ---
+ duration_ms: 2.080883
+ type: 'test'
+ ...
+# Subtest: tick fires nothing when no contribution is due, and both when both are
+ok 1078 - tick fires nothing when no contribution is due, and both when both are
+ ---
+ duration_ms: 0.489925
+ type: 'test'
+ ...
+# Subtest: the fired run gets the narrowed runner context, built from the daemon runtime fields
+ok 1079 - the fired run gets the narrowed runner context, built from the daemon runtime fields
+ ---
+ duration_ms: 0.257402
+ type: 'test'
+ ...
+# Subtest: a rejected sweep run neither throws out of tick nor becomes an unhandled rejection
+ok 1080 - a rejected sweep run neither throws out of tick nor becomes an unhandled rejection
+ ---
+ duration_ms: 13.015212
+ type: 'test'
+ ...
+# Subtest: tick does not block on a run that never settles
+ok 1081 - tick does not block on a run that never settles
+ ---
+ duration_ms: 0.369992
+ type: 'test'
+ ...
+# Subtest: a second tick fires nothing while the first run is still in flight
+ok 1082 - a second tick fires nothing while the first run is still in flight
+ ---
+ duration_ms: 0.425076
+ type: 'test'
+ ...
+# Subtest: a rejected run clears the in-flight guard so the next due tick still fires
+ok 1083 - a rejected run clears the in-flight guard so the next due tick still fires
+ ---
+ duration_ms: 4.22405
+ type: 'test'
+ ...
+# Subtest: a malformed sweep cron is skipped, not thrown, and later providers still fire
+ok 1084 - a malformed sweep cron is skipped, not thrown, and later providers still fire
+ ---
+ duration_ms: 0.36816
+ type: 'test'
+ ...
+# Subtest: createBackfillSweepDriver refuses to build without the registries it fires through
+ok 1085 - createBackfillSweepDriver refuses to build without the registries it fires through
+ ---
+ duration_ms: 0.629497
+ type: 'test'
+ ...
+# Subtest: installDaemon upgrades an _npx binPath to a durable global bin before writing the service unit
+ok 1086 - installDaemon upgrades an _npx binPath to a durable global bin before writing the service unit
+ ---
+ duration_ms: 8.231191
+ type: 'test'
+ ...
+# Subtest: explicit --bin bypasses the durable upgrade even for an _npx-looking path
+ok 1087 - explicit --bin bypasses the durable upgrade even for an _npx-looking path
+ ---
+ duration_ms: 1.10536
+ type: 'test'
+ ...
+# Subtest: dry-run render never triggers the durable upgrade (renders the _npx path as-is)
+ok 1088 - dry-run render never triggers the durable upgrade (renders the _npx path as-is)
+ ---
+ duration_ms: 0.37449
+ type: 'test'
+ ...
+# Subtest: hyp daemon install (no --bin) upgrades the process argv _npx bin to a durable global bin
+ok 1089 - hyp daemon install (no --bin) upgrades the process argv _npx bin to a durable global bin
+ ---
+ duration_ms: 2.849721
+ type: 'test'
+ ...
+# Subtest: runDaemonInstall dry-run still surfaces the _npx bin without a global install (escape hatch)
+ok 1090 - runDaemonInstall dry-run still surfaces the _npx bin without a global install (escape hatch)
+ ---
+ duration_ms: 1.170459
+ type: 'test'
+ ...
+# Subtest: reinstall over a loaded agent boots out, waits for release, then bootstraps once
+ok 1091 - reinstall over a loaded agent boots out, waits for release, then bootstraps once
+ ---
+ duration_ms: 5.443564
+ type: 'test'
+ ...
+# Subtest: transient EIO (error 5) on bootstrap is retried until it succeeds
+ok 1092 - transient EIO (error 5) on bootstrap is retried until it succeeds
+ ---
+ duration_ms: 0.820648
+ type: 'test'
+ ...
+# Subtest: a persistent EIO still throws after a bounded number of retries
+ok 1093 - a persistent EIO still throws after a bounded number of retries
+ ---
+ duration_ms: 1.25671
+ type: 'test'
+ ...
+# Subtest: a genuine (non-transient) bootstrap failure fails fast without retry
+ok 1094 - a genuine (non-transient) bootstrap failure fails fast without retry
+ ---
+ duration_ms: 0.820186
+ type: 'test'
+ ...
+# Subtest: a degraded maintenance tick sets the span status code, not just the attribute
+ok 1095 - a degraded maintenance tick sets the span status code, not just the attribute
+ ---
+ duration_ms: 154.822565
+ type: 'test'
+ ...
+# Subtest: createReconcilePassScheduler runs exactly one pass per idle edge
+ok 1096 - createReconcilePassScheduler runs exactly one pass per idle edge
+ ---
+ duration_ms: 0.969233
+ type: 'test'
+ ...
+# Subtest: createReconcilePassScheduler is single-flight and coalesces concurrent edges into one rerun
+ok 1097 - createReconcilePassScheduler is single-flight and coalesces concurrent edges into one rerun
+ ---
+ duration_ms: 6.014161
+ type: 'test'
+ ...
+# Subtest: createReconcilePassScheduler.settle resolves immediately when no pass is in flight
+ok 1098 - createReconcilePassScheduler.settle resolves immediately when no pass is in flight
+ ---
+ duration_ms: 0.22991
+ type: 'test'
+ ...
+# Subtest: createReconcilePassScheduler keeps scheduling after a pass throws
+ok 1099 - createReconcilePassScheduler keeps scheduling after a pass throws
+ ---
+ duration_ms: 0.229439
+ type: 'test'
+ ...
+# Subtest: runDaemon runs the boot already-confirmed pass when a central layer is present and no probation is active
+ok 1100 - runDaemon runs the boot already-confirmed pass when a central layer is present and no probation is active
+ ---
+ duration_ms: 45.238068
+ type: 'test'
+ ...
+# Subtest: runDaemon does not run the boot pass on a non-joined host (no central layer)
+ok 1101 - runDaemon does not run the boot pass on a non-joined host (no central layer)
+ ---
+ duration_ms: 17.611708
+ type: 'test'
+ ...
+# Subtest: runDaemon does not run the boot pass while probation is still active (fresh-join case)
+ok 1102 - runDaemon does not run the boot pass while probation is still active (fresh-join case)
+ ---
+ duration_ms: 18.557185
+ type: 'test'
+ ...
+# Subtest: the confirmation edge during active probation drives exactly one reconcile pass (fresh-join path)
+ok 1103 - the confirmation edge during active probation drives exactly one reconcile pass (fresh-join path)
+ ---
+ duration_ms: 24.078467
+ type: 'test'
+ ...
+# Subtest: the daemon registers [attachHandler, backfillHandler] - attach first (LLP 0045 §Module / seam breakdown item 7)
+ok 1104 - the daemon registers [attachHandler, backfillHandler] - attach first (LLP 0045 §Module / seam breakdown item 7)
+ ---
+ duration_ms: 1.647134
+ type: 'test'
+ ...
+# Subtest: the daemon resolves clientDescriptors/clients/endpoint from boot when the gateway is enabled
+ok 1105 - the daemon resolves clientDescriptors/clients/endpoint from boot when the gateway is enabled
+ ---
+ duration_ms: 72.909616
+ type: 'test'
+ ...
+# Subtest: the daemon refreshes source details on every tick, so accruing details reach status.json
+ok 1106 - the daemon refreshes source details on every tick, so accruing details reach status.json
+ ---
+ duration_ms: 153.614769
+ type: 'test'
+ ...
+# Subtest: a daemon that never reached a tick still refreshes source details before it stops
+ok 1107 - a daemon that never reached a tick still refreshes source details before it stops
+ ---
+ duration_ms: 29.009182
+ type: 'test'
+ ...
+# Subtest: a source whose status() never settles cannot freeze the status file or the shutdown
+ok 1108 - a source whose status() never settles cannot freeze the status file or the shutdown
+ ---
+ duration_ms: 123.231649
+ type: 'test'
+ ...
+# Subtest: the uninstall sweep reverses every attached client, claude and codex alike
+ok 1109 - the uninstall sweep reverses every attached client, claude and codex alike
+ ---
+ duration_ms: 54.389937
+ type: 'test'
+ ...
+# Subtest: the uninstall sweep reports nothing on a machine with no client attached
+ok 1110 - the uninstall sweep reports nothing on a machine with no client attached
+ ---
+ duration_ms: 9.873397
+ type: 'test'
+ ...
+# Subtest: one client attached is detached without the untouched one being reported
+ok 1111 - one client attached is detached without the untouched one being reported
+ ---
+ duration_ms: 25.980989
+ type: 'test'
+ ...
+# Subtest: the sweep detaches openclaw like any other client, with nothing resolved from a daemon
+ok 1112 - the sweep detaches openclaw like any other client, with nothing resolved from a daemon
+ ---
+ duration_ms: 13.147022
+ type: 'test'
+ ...
+# Subtest: a never-attached openclaw config with its own providers is an honest no-op, not a failure
+ok 1113 - a never-attached openclaw config with its own providers is an honest no-op, not a failure
+ ---
+ duration_ms: 9.167804
+ type: 'test'
+ ...
+# Subtest: uninstall detaches openclaw on a machine whose daemon was already stopped
+ok 1114 - uninstall detaches openclaw on a machine whose daemon was already stopped
+ ---
+ duration_ms: 20.390972
+ type: 'test'
+ ...
+# Subtest: a failed teardown detaches nothing: the gateway still answers, so the attaches stay
+ok 1115 - a failed teardown detaches nothing: the gateway still answers, so the attaches stay
+ ---
+ duration_ms: 19.723478
+ type: 'test'
+ ...
+# Subtest: one wedged client is a collected failure with a remedy; the rest still detach
+ok 1116 - one wedged client is a collected failure with a remedy; the rest still detach
+ ---
+ duration_ms: 21.868187
+ type: 'test'
+ ...
+# Subtest: the undo warnings the quiet sweep collects reach the uninstall output
+ok 1117 - the undo warnings the quiet sweep collects reach the uninstall output
+ ---
+ duration_ms: 21.134753
+ type: 'test'
+ ...
+# Subtest: writeStatusFile writes an atomic readable status snapshot
+ok 1118 - writeStatusFile writes an atomic readable status snapshot
+ ---
+ duration_ms: 4.728307
+ type: 'test'
+ ...
+# Subtest: readStatusFile returns null before the daemon has written status
+ok 1119 - readStatusFile returns null before the daemon has written status
+ ---
+ duration_ms: 1.805634
+ type: 'test'
+ ...
+# Subtest: resolveClientSettingsPath sanitizes client env override names
+ok 1120 - resolveClientSettingsPath sanitizes client env override names
+ ---
+ duration_ms: 0.338895
+ type: 'test'
+ ...
+# Subtest: resolveClientSettingsPath rejects an absolute settings_file rather than re-anchoring it
+ok 1121 - resolveClientSettingsPath rejects an absolute settings_file rather than re-anchoring it
+ ---
+ duration_ms: 0.586241
+ type: 'test'
+ ...
+# Subtest: resolveClientSettingsPath rejects a settings_file that climbs out of its base
+ok 1122 - resolveClientSettingsPath rejects a settings_file that climbs out of its base
+ ---
+ duration_ms: 0.338935
+ type: 'test'
+ ...
+# Subtest: resolveClientSettingsPath does not mistake a prefix-sharing sibling for the base
+ok 1123 - resolveClientSettingsPath does not mistake a prefix-sharing sibling for the base
+ ---
+ duration_ms: 0.404014
+ type: 'test'
+ ...
+# Subtest: resolveClientSettingsPath returns the absolute path it checked
+ok 1124 - resolveClientSettingsPath returns the absolute path it checked
+ ---
+ duration_ms: 0.188386
+ type: 'test'
+ ...
+# Subtest: probeClientAttachFromDescriptor errors on an absolute settings_file instead of probing $HOME
+ok 1125 - probeClientAttachFromDescriptor errors on an absolute settings_file instead of probing $HOME
+ ---
+ duration_ms: 9.199262
+ type: 'test'
+ ...
+# Subtest: probeClientAttachFromDescriptor reads JSON attach markers
+ok 1126 - probeClientAttachFromDescriptor reads JSON attach markers
+ ---
+ duration_ms: 2.993419
+ type: 'test'
+ ...
+# Subtest: probeClientAttachFromDescriptor honors sanitized TOML home overrides
+ok 1127 - probeClientAttachFromDescriptor honors sanitized TOML home overrides
+ ---
+ duration_ms: 1.596146
+ type: 'test'
+ ...
+# Subtest: probeClientAttachFromDescriptor reads json_path attach markers when the entry is present
+ok 1128 - probeClientAttachFromDescriptor reads json_path attach markers when the entry is present
+ ---
+ duration_ms: 4.920088
+ type: 'test'
+ ...
+# Subtest: probeClientAttachFromDescriptor reports json_path as not attached when the entry is absent
+ok 1129 - probeClientAttachFromDescriptor reports json_path as not attached when the entry is absent
+ ---
+ duration_ms: 1.362431
+ type: 'test'
+ ...
+# Subtest: probeClientAttachFromDescriptor reports json_path as not attached when the marker header is wrong
+ok 1130 - probeClientAttachFromDescriptor reports json_path as not attached when the marker header is wrong
+ ---
+ duration_ms: 1.44702
+ type: 'test'
+ ...
+# Subtest: renderDaemonInstall renders a deterministic systemd dry-run payload
+ok 1131 - renderDaemonInstall renders a deterministic systemd dry-run payload
+ ---
+ duration_ms: 0.828489
+ type: 'test'
+ ...
+# Subtest: renderDaemonInstall renders a deterministic LaunchAgent dry-run payload
+ok 1132 - renderDaemonInstall renders a deterministic LaunchAgent dry-run payload
+ ---
+ duration_ms: 0.491397
+ type: 'test'
+ ...
+# Subtest: installers default to relaunch-on-exit (staged restart requirement, LLP 0017)
+ok 1133 - installers default to relaunch-on-exit (staged restart requirement, LLP 0017)
+ ---
+ duration_ms: 3.46726
+ type: 'test'
+ ...
+# Subtest: serviceDaemonStatus reports "not installed" without probing the service manager
+ok 1134 - serviceDaemonStatus reports "not installed" without probing the service manager
+ ---
+ duration_ms: 2.328109
+ type: 'test'
+ ...
+# Subtest: serviceDaemonStatus degrades to "not loaded" when the service manager cannot run
+ok 1135 - serviceDaemonStatus degrades to "not loaded" when the service manager cannot run
+ ---
+ duration_ms: 2.550276
+ type: 'test'
+ ...
+# Subtest: the staged-restart exit code is distinct from success and error exits
+ok 1136 - the staged-restart exit code is distinct from success and error exits
+ ---
+ duration_ms: 0.255679
+ type: 'test'
+ ...
+# Subtest: runDaemon reload refreshes plugin config before source.reload
+ok 1137 - runDaemon reload refreshes plugin config before source.reload
+ ---
+ duration_ms: 58.64195
+ type: 'test'
+ ...
+# Subtest: runDaemon reload re-merges the central layer (does not re-read local alone) - \#111 regression
+ok 1138 - runDaemon reload re-merges the central layer (does not re-read local alone) - \#111 regression
+ ---
+ duration_ms: 30.141082
+ type: 'test'
+ ...
+# Subtest: runDaemon health event derives from aggregate state and excludes failed sources (\#138)
+ok 1139 - runDaemon health event derives from aggregate state and excludes failed sources (\#138)
+ ---
+ duration_ms: 27.866544
+ type: 'test'
+ ...
+# Subtest: manual `hyp detach` retracts the attach marker so a later join re-attaches (\#217)
+ok 1140 - manual `hyp detach` retracts the attach marker so a later join re-attaches (\#217)
+ ---
+ duration_ms: 56.761332
+ type: 'test'
+ ...
+# Subtest: detach of a probe-HAVING client with already-clean settings (changed:false) still clears its stale marker
+ok 1141 - detach of a probe-HAVING client with already-clean settings (changed:false) still clears its stale marker
+ ---
+ duration_ms: 9.194124
+ type: 'test'
+ ...
+# Subtest: detach of a probe-LESS client does NOT clear its marker (mirrors reverse()'s \#212 exception)
+ok 1142 - detach of a probe-LESS client does NOT clear its marker (mirrors reverse()'s \#212 exception)
+ ---
+ duration_ms: 19.762757
+ type: 'test'
+ ...
+# Subtest: detach still succeeds when the marker retraction throws (best-effort, not a detach failure)
+ok 1143 - detach still succeeds when the marker retraction throws (best-effort, not a detach failure)
+ ---
+ duration_ms: 17.04716
+ type: 'test'
+ ...
+# Subtest: detach removes the assets its attach marker records, and leaves manual copies alone
+ok 1144 - detach removes the assets its attach marker records, and leaves manual copies alone
+ ---
+ duration_ms: 9.068223
+ type: 'test'
+ ...
+# Subtest: detach names the assets it refuses to remove, and does not keep the marker for them
+ok 1145 - detach names the assets it refuses to remove, and does not keep the marker for them
+ ---
+ duration_ms: 5.834398
+ type: 'test'
+ ...
+# Subtest: detects claude when ~/.claude exists
+ok 1146 - detects claude when ~/.claude exists
+ ---
+ duration_ms: 5.994501
+ type: 'test'
+ ...
+# Subtest: detects codex when ~/.codex exists
+ok 1147 - detects codex when ~/.codex exists
+ ---
+ duration_ms: 5.596046
+ type: 'test'
+ ...
+# Subtest: detects both when both config homes exist
+ok 1148 - detects both when both config homes exist
+ ---
+ duration_ms: 1.593662
+ type: 'test'
+ ...
+# Subtest: detects nothing in an empty home
+ok 1149 - detects nothing in an empty home
+ ---
+ duration_ms: 0.719664
+ type: 'test'
+ ...
+# Subtest: honors $CODEX_HOME override for codex detection
+ok 1150 - honors $CODEX_HOME override for codex detection
+ ---
+ duration_ms: 1.036916
+ type: 'test'
+ ...
+# Subtest: a plain file (not a directory) at the config-home path does not count
+ok 1151 - a plain file (not a directory) at the config-home path does not count
+ ---
+ duration_ms: 2.404364
+ type: 'test'
+ ...
+# Subtest: a picker row with no detect probe is never detected
+ok 1152 - a picker row with no detect probe is never detected
+ ---
+ duration_ms: 0.606922
+ type: 'test'
+ ...
+# Subtest: an app_bundle probe detects presence by stat
+ok 1153 - an app_bundle probe detects presence by stat
+ ---
+ duration_ms: 0.862541
+ type: 'test'
+ ...
+# Subtest: an app_bundle probe is not present when the path does not exist
+ok 1154 - an app_bundle probe is not present when the path does not exist
+ ---
+ duration_ms: 1.650599
+ type: 'test'
+ ...
+# Subtest: a path probe detects a literal directory
+ok 1155 - a path probe detects a literal directory
+ ---
+ duration_ms: 1.152652
+ type: 'test'
+ ...
+# Subtest: a path probe honors a $FOO_HOME-style env override of its literal path
+ok 1156 - a path probe honors a $FOO_HOME-style env override of its literal path
+ ---
+ duration_ms: 0.865325
+ type: 'test'
+ ...
+# Subtest: a probe failure does not surface, and other rows still detect
+ok 1157 - a probe failure does not surface, and other rows still detect
+ ---
+ duration_ms: 3.222397
+ type: 'test'
+ ...
+# Subtest: dispatch miss on an inactive bundled plugin command reports unavailable + repair, not unknown
+ok 1158 - dispatch miss on an inactive bundled plugin command reports unavailable + repair, not unknown
+ ---
+ duration_ms: 26.586379
+ type: 'test'
+ ...
+# Subtest: dispatch miss on a genuine typo still gets the generic unknown-command message
+ok 1159 - dispatch miss on a genuine typo still gets the generic unknown-command message
+ ---
+ duration_ms: 4.80312
+ type: 'test'
+ ...
+# Subtest: dispatch miss on a plugin present-but-disabled in the local config advises enabling it, not adding it
+ok 1160 - dispatch miss on a plugin present-but-disabled in the local config advises enabling it, not adding it
+ ---
+ duration_ms: 4.397204
+ type: 'test'
+ ...
+# Subtest: dispatch miss on a plugin disabled by the central layer says it cannot be enabled locally
+ok 1161 - dispatch miss on a plugin disabled by the central layer says it cannot be enabled locally
+ ---
+ duration_ms: 7.541663
+ type: 'test'
+ ...
+# Subtest: a command whose plugin IS active is unaffected (renders group help, no availability error)
+ok 1162 - a command whose plugin IS active is unaffected (renders group help, no availability error)
+ ---
+ duration_ms: 6.898114
+ type: 'test'
+ ...
+# Subtest: deadline is the next local 11:59pm when that is comfortably away
+ok 1163 - deadline is the next local 11:59pm when that is comfortably away
+ ---
+ duration_ms: 1.090998
+ type: 'test'
+ ...
+# Subtest: deadline rolls to the following day when same-day 11:59pm is under the 4-hour floor
+ok 1164 - deadline rolls to the following day when same-day 11:59pm is under the 4-hour floor
+ ---
+ duration_ms: 1.048714
+ type: 'test'
+ ...
+# Subtest: deadline exactly at the 4-hour boundary does not roll (floor is strict "less than")
+ok 1165 - deadline exactly at the 4-hour boundary does not roll (floor is strict "less than")
+ ---
+ duration_ms: 0.162427
+ type: 'test'
+ ...
+# Subtest: an enrollment after 11:59pm rolls to the next day (past same-day deadline is under the floor)
+ok 1166 - an enrollment after 11:59pm rolls to the next day (past same-day deadline is under the floor)
+ ---
+ duration_ms: 0.122566
+ type: 'test'
+ ...
+# Subtest: the deadline always lands on a local 23:59 and is strictly in the future (DST-agnostic invariant)
+ok 1167 - the deadline always lands on a local 23:59 and is strictly in the future (DST-agnostic invariant)
+ ---
+ duration_ms: 0.234456
+ type: 'test'
+ ...
+# Subtest: the rendered deadline names its time zone, never a bare wall-clock time (LLP 0100 R1)
+ok 1168 - the rendered deadline names its time zone, never a bare wall-clock time (LLP 0100 R1)
+ ---
+ duration_ms: 17.056955
+ type: 'test'
+ ...
+# Subtest: the rendered deadline reads as a full local date, time and zone in a pinned zone
+ok 1169 - the rendered deadline reads as a full local date, time and zone in a pinned zone
+ ---
+ duration_ms: 0.905897
+ type: 'test'
+ ...
+# Subtest: a written marker reads back its future deadline; the deadline is stored, not derived from mtime
+ok 1170 - a written marker reads back its future deadline; the deadline is stored, not derived from mtime
+ ---
+ duration_ms: 6.997636
+ type: 'test'
+ ...
+# Subtest: a touched marker (mtime bumped) keeps its original deadline - incidental writes cannot shorten or extend the hold
+ok 1171 - a touched marker (mtime bumped) keeps its original deadline - incidental writes cannot shorten or extend the hold
+ ---
+ duration_ms: 1.647313
+ type: 'test'
+ ...
+# Subtest: a past deadline reads as absent and is opportunistically unlinked (bounded hold, LLP 0101)
+ok 1172 - a past deadline reads as absent and is opportunistically unlinked (bounded hold, LLP 0101)
+ ---
+ duration_ms: 2.199473
+ type: 'test'
+ ...
+# Subtest: an unreadable marker reads as absent, never a wedge (fail-open by design)
+ok 1173 - an unreadable marker reads as absent, never a wedge (fail-open by design)
+ ---
+ duration_ms: 0.768137
+ type: 'test'
+ ...
+# Subtest: a malformed marker (bad JSON or missing deadline) reads as absent (fail-open)
+ok 1174 - a malformed marker (bad JSON or missing deadline) reads as absent (fail-open)
+ ---
+ duration_ms: 1.896632
+ type: 'test'
+ ...
+# Subtest: a live first-sync hold holds the whole sink tick (no sink exports); the tick after the deadline exports
+ok 1175 - a live first-sync hold holds the whole sink tick (no sink exports); the tick after the deadline exports
+ ---
+ duration_ms: 8.505858
+ type: 'test'
+ ...
+# Subtest: an expired marker does not hold the tick (a bounded hold can never stall exports past its deadline)
+ok 1176 - an expired marker does not hold the tick (a bounded hold can never stall exports past its deadline)
+ ---
+ duration_ms: 11.682797
+ type: 'test'
+ ...
+# Subtest: no marker means no hold: exports proceed on the first tick
+ok 1177 - no marker means no hold: exports proceed on the first tick
+ ---
+ duration_ms: 1.016605
+ type: 'test'
+ ...
+# Subtest: resolves immediately when nothing is buffered
+ok 1178 - resolves immediately when nothing is buffered
+ ---
+ duration_ms: 0.718792
+ type: 'test'
+ ...
+# Subtest: resolves via the write callback once buffered output drains
+ok 1179 - resolves via the write callback once buffered output drains
+ ---
+ duration_ms: 0.203088
+ type: 'test'
+ ...
+# Subtest: resolves on error (EPIPE) instead of hanging
+ok 1180 - resolves on error (EPIPE) instead of hanging
+ ---
+ duration_ms: 0.141886
+ type: 'test'
+ ...
+# Subtest: does not double-resolve when both error and write callback fire
+ok 1181 - does not double-resolve when both error and write callback fire
+ ---
+ duration_ms: 0.247626
+ type: 'test'
+ ...
+# Subtest: the preference lives beside its sibling stores under HYP_HOME state
+ok 1182 - the preference lives beside its sibling stores under HYP_HOME state
+ ---
+ duration_ms: 5.178722
+ type: 'test'
+ ...
+# Subtest: an absent preference reads as the product default: new folders sync, nobody is asked
+ok 1183 - an absent preference reads as the product default: new folders sync, nobody is asked
+ ---
+ duration_ms: 2.527562
+ type: 'test'
+ ...
+# Subtest: a written preference round-trips, and both modes are writable
+ok 1184 - a written preference round-trips, and both modes are writable
+ ---
+ duration_ms: 4.668215
+ type: 'test'
+ ...
+# Subtest: an unknown mode is refused at the write, never persisted
+ok 1185 - an unknown mode is refused at the write, never persisted
+ ---
+ duration_ms: 0.498168
+ type: 'test'
+ ...
+# Subtest: a corrupt or unrecognized file throws rather than resolving to a mode by accident
+ok 1186 - a corrupt or unrecognized file throws rather than resolving to a mode by accident
+ ---
+ duration_ms: 10.077536
+ type: 'test'
+ ...
+# Subtest: the unreadable error carries the file path and an error_kind for telemetry
+ok 1187 - the unreadable error carries the file path and an error_kind for telemetry
+ ---
+ duration_ms: 1.119772
+ type: 'test'
+ ...
+# Subtest: resolveEncodeSettings defaults to SNAPPY with no explicit compressors
+ok 1188 - resolveEncodeSettings defaults to SNAPPY with no explicit compressors
+ ---
+ duration_ms: 0.770331
+ type: 'test'
+ ...
+# Subtest: resolveEncodeSettings honours codec=ZSTD (case-insensitive) when zstd is available
+ok 1189 - resolveEncodeSettings honours codec=ZSTD (case-insensitive) when zstd is available
+ ---
+ duration_ms: 1.269219
+ type: 'test'
+ ...
+# Subtest: resolveEncodeSettings falls back to SNAPPY and warns when ZSTD is unavailable
+ok 1190 - resolveEncodeSettings falls back to SNAPPY and warns when ZSTD is unavailable # SKIP
+ ---
+ duration_ms: 0.082626
+ type: 'test'
+ ...
+# Subtest: resolveEncodeSettings warns and falls back on an unknown codec
+ok 1191 - resolveEncodeSettings warns and falls back on an unknown codec
+ ---
+ duration_ms: 0.200876
+ type: 'test'
+ ...
+# Subtest: resolveEncodeSettings passes through a positive page_size and ignores invalid ones
+ok 1192 - resolveEncodeSettings passes through a positive page_size and ignores invalid ones
+ ---
+ duration_ms: 0.287848
+ type: 'test'
+ ...
+# Subtest: isNpxBinPath detects npm _npx cache entries
+ok 1193 - isNpxBinPath detects npm _npx cache entries
+ ---
+ duration_ms: 0.775539
+ type: 'test'
+ ...
+# Subtest: ensureDurableBinForNpx installs the current package globally and returns the global bin
+ok 1194 - ensureDurableBinForNpx installs the current package globally and returns the global bin
+ ---
+ duration_ms: 8.793885
+ type: 'test'
+ ...
+# Subtest: ensureDurableBinForNpx leaves stable bin paths untouched
+ok 1195 - ensureDurableBinForNpx leaves stable bin paths untouched
+ ---
+ duration_ms: 0.259965
+ type: 'test'
+ ...
+# Subtest: ensureDurableBinForNpx reports npm install failures with a repair command
+ok 1196 - ensureDurableBinForNpx reports npm install failures with a repair command
+ ---
+ duration_ms: 7.598288
+ type: 'test'
+ ...
+# Subtest: a verb with help projects it onto the command registration
+ok 1197 - a verb with help projects it onto the command registration
+ ---
+ duration_ms: 0.884024
+ type: 'test'
+ ...
+# Subtest: a verb without help contributes no help key at all
+ok 1198 - a verb without help contributes no help key at all
+ ---
+ duration_ms: 0.17768
+ type: 'test'
+ ...
+# Subtest: the verb help passthrough does not disturb summary or usage
+ok 1199 - the verb help passthrough does not disturb summary or usage
+ ---
+ duration_ms: 0.176268
+ type: 'test'
+ ...
+# Subtest: registerGroup stores a description without adding a command
+ok 1200 - registerGroup stores a description without adding a command
+ ---
+ duration_ms: 0.182267
+ type: 'test'
+ ...
+# Subtest: registerGroup rejects a missing name and non-string prose
+ok 1201 - registerGroup rejects a missing name and non-string prose
+ ---
+ duration_ms: 0.348219
+ type: 'test'
+ ...
+# Subtest: re-registering a group replaces it rather than throwing
+ok 1202 - re-registering a group replaces it rather than throwing
+ ---
+ duration_ms: 0.127524
+ type: 'test'
+ ...
+# Subtest: group help renders the header and paragraph above the subcommand table
+ok 1203 - group help renders the header and paragraph above the subcommand table
+ ---
+ duration_ms: 0.283911
+ type: 'test'
+ ...
+# Subtest: a group with help but no summary renders no header line
+ok 1204 - a group with help but no summary renders no header line
+ ---
+ duration_ms: 0.133793
+ type: 'test'
+ ...
+# Subtest: an undescribed group still renders its table, as before
+ok 1205 - an undescribed group still renders its table, as before
+ ---
+ duration_ms: 0.278453
+ type: 'test'
+ ...
+# Subtest: no tracked file carries an em dash
+ok 1206 - no tracked file carries an em dash
+ ---
+ duration_ms: 67.684795
+ type: 'test'
+ ...
+# Subtest: the scan actually reads the tree
+ok 1207 - the scan actually reads the tree
+ ---
+ duration_ms: 3.381449
+ type: 'test'
+ ...
+# Subtest: cache and parquet backends answer the corpus identically, and answer it the way SQL does
+ok 1208 - cache and parquet backends answer the corpus identically, and answer it the way SQL does
+ ---
+ duration_ms: 150.165617
+ type: 'test'
+ ...
+# Subtest: filtered aggregates take the same NULL semantics as the row scan
+ok 1209 - filtered aggregates take the same NULL semantics as the row scan
+ ---
+ duration_ms: 10.974249
+ type: 'test'
+ ...
+# Subtest: the cache column stream reports appliedWhere honestly
+ok 1210 - the cache column stream reports appliedWhere honestly
+ ---
+ duration_ms: 6.900027
+ type: 'test'
+ ...
+# Subtest: both backends agree on which predicates are converted and which are declined
+ok 1211 - both backends agree on which predicates are converted and which are declined
+ ---
+ duration_ms: 5.120373
+ type: 'test'
+ ...
+# Subtest: LIMIT and OFFSET are held back under a WHERE and the slice still lands on the matching rows
+ok 1212 - LIMIT and OFFSET are held back under a WHERE and the slice still lands on the matching rows
+ ---
+ duration_ms: 12.121864
+ type: 'test'
+ ...
+# Subtest: a filtered cache scan still prunes whole data files
+ok 1213 - a filtered cache scan still prunes whole data files
+ ---
+ duration_ms: 8.635765
+ type: 'test'
+ ...
+# Subtest: hyp ignore writes a self-documenting .hypignore at the git repo root
+ok 1214 - hyp ignore writes a self-documenting .hypignore at the git repo root
+ ---
+ duration_ms: 9.326376
+ type: 'test'
+ ...
+# Subtest: hyp ignore without a repo writes .hypignore at the cwd
+ok 1215 - hyp ignore without a repo writes .hypignore at the cwd
+ ---
+ duration_ms: 2.698431
+ type: 'test'
+ ...
+# Subtest: hyp ignore [path] writes exactly at the explicit path, overriding the repo root
+ok 1216 - hyp ignore [path] writes exactly at the explicit path, overriding the repo root
+ ---
+ duration_ms: 12.094893
+ type: 'test'
+ ...
+# Subtest: hyp ignore is idempotent: re-ignoring an already-ignored path is a no-op success
+ok 1217 - hyp ignore is idempotent: re-ignoring an already-ignored path is a no-op success
+ ---
+ duration_ms: 2.663068
+ type: 'test'
+ ...
+# Subtest: hyp unignore removes the governing .hypignore and is idempotent
+ok 1218 - hyp unignore removes the governing .hypignore and is idempotent
+ ---
+ duration_ms: 2.494782
+ type: 'test'
+ ...
+# Subtest: hyp ignore --check reports an ignored path, its governor, and residual count
+ok 1219 - hyp ignore --check reports an ignored path, its governor, and residual count
+ ---
+ duration_ms: 5.636407
+ type: 'test'
+ ...
+# Subtest: hyp ignore --check reports a clean path as not ignored with zero residue
+ok 1220 - hyp ignore --check reports a clean path as not ignored with zero residue
+ ---
+ duration_ms: 1.241548
+ type: 'test'
+ ...
+# Subtest: hyp ignore --check --json emits a machine-readable status
+ok 1221 - hyp ignore --check --json emits a machine-readable status
+ ---
+ duration_ms: 2.024107
+ type: 'test'
+ ...
+# Subtest: hyp ignore --check counts already-cached rows under the scope (LIKE superset, refined exactly)
+ok 1222 - hyp ignore --check counts already-cached rows under the scope (LIKE superset, refined exactly)
+ ---
+ duration_ms: 6.823261
+ type: 'test'
+ ...
+# Subtest: hyp ignore --local-only adds the git repo root to the machine-local list, never touching the repo
+ok 1223 - hyp ignore --local-only adds the git repo root to the machine-local list, never touching the repo
+ ---
+ duration_ms: 28.289377
+ type: 'test'
+ ...
+# Subtest: hyp ignore --local-only [path] overrides the repo-root default
+ok 1224 - hyp ignore --local-only [path] overrides the repo-root default
+ ---
+ duration_ms: 6.511978
+ type: 'test'
+ ...
+# Subtest: hyp ignore --local-only accepts a nonexistent, non-repo path (R4)
+ok 1225 - hyp ignore --local-only accepts a nonexistent, non-repo path (R4)
+ ---
+ duration_ms: 2.919467
+ type: 'test'
+ ...
+# Subtest: hyp ignore --local-only is idempotent: adding the same directory twice is a no-op success
+ok 1226 - hyp ignore --local-only is idempotent: adding the same directory twice is a no-op success
+ ---
+ duration_ms: 5.78296
+ type: 'test'
+ ...
+# Subtest: hyp ignore --local-only on a directory under an already-listed ancestor is a no-op (ancestor-governed)
+ok 1227 - hyp ignore --local-only on a directory under an already-listed ancestor is a no-op (ancestor-governed)
+ ---
+ duration_ms: 4.905897
+ type: 'test'
+ ...
+# Subtest: hyp ignore --local-only on a path already governed by a stricter .hypignore is a no-op naming the dotfile
+ok 1228 - hyp ignore --local-only on a path already governed by a stricter .hypignore is a no-op naming the dotfile
+ ---
+ duration_ms: 1.926769
+ type: 'test'
+ ...
+# Subtest: hyp unignore --local-only removes an exact entry and is idempotent
+ok 1229 - hyp unignore --local-only removes an exact entry and is idempotent
+ ---
+ duration_ms: 4.856331
+ type: 'test'
+ ...
+# Subtest: hyp unignore --local-only [path] removes every governing (equal-or-ancestor) entry
+ok 1230 - hyp unignore --local-only [path] removes every governing (equal-or-ancestor) entry
+ ---
+ duration_ms: 3.885395
+ type: 'test'
+ ...
+# Subtest: hyp unignore --local-only does not remove a sibling that merely shares a string prefix
+ok 1231 - hyp unignore --local-only does not remove a sibling that merely shares a string prefix
+ ---
+ duration_ms: 2.945958
+ type: 'test'
+ ...
+# Subtest: hyp ignore --private on the real path upgrades an entry declared by its symlink spelling, not duplicating it
+ok 1232 - hyp ignore --private on the real path upgrades an entry declared by its symlink spelling, not duplicating it
+ ---
+ duration_ms: 5.445016
+ type: 'test'
+ ...
+# Subtest: hyp unignore --local-only by symlink spelling removes an entry declared canonically
+ok 1233 - hyp unignore --local-only by symlink spelling removes an entry declared canonically
+ ---
+ duration_ms: 5.762248
+ type: 'test'
+ ...
+# Subtest: hyp ignore --check reports the local-only class and the list file as the governor
+ok 1234 - hyp ignore --check reports the local-only class and the list file as the governor
+ ---
+ duration_ms: 6.709218
+ type: 'test'
+ ...
+# Subtest: hyp ignore --check --json reports local-only class + governedBy pointing at the list file
+ok 1235 - hyp ignore --check --json reports local-only class + governedBy pointing at the list file
+ ---
+ duration_ms: 5.938185
+ type: 'test'
+ ...
+# Subtest: hyp ignore --check still reports the dotfile ignore class + its governing file (unaffected by an empty list)
+ok 1236 - hyp ignore --check still reports the dotfile ignore class + its governing file (unaffected by an empty list)
+ ---
+ duration_ms: 1.567052
+ type: 'test'
+ ...
+# Subtest: hyp ignore --check counts residual cached rows for a local-only scope (recorded, withheld from forwarding)
+ok 1237 - hyp ignore --check counts residual cached rows for a local-only scope (recorded, withheld from forwarding)
+ ---
+ duration_ms: 9.886206
+ type: 'test'
+ ...
+# Subtest: hyp ignore --check on a clean path with a populated-but-non-matching list reports full/no residue
+ok 1238 - hyp ignore --check on a clean path with a populated-but-non-matching list reports full/no residue
+ ---
+ duration_ms: 4.325314
+ type: 'test'
+ ...
+# Subtest: hyp ignore --check scopes the residual count to the entry the gate used, not the longest declared string
+ok 1239 - hyp ignore --check scopes the residual count to the entry the gate used, not the longest declared string
+ ---
+ duration_ms: 5.056445
+ type: 'test'
+ ...
+# Subtest: hyp ignore --private marks the repo root ignore in the machine-local store, never touching the repo
+ok 1240 - hyp ignore --private marks the repo root ignore in the machine-local store, never touching the repo
+ ---
+ duration_ms: 23.263979
+ type: 'test'
+ ...
+# Subtest: hyp ignore --private is idempotent: marking twice is a no-op success
+ok 1241 - hyp ignore --private is idempotent: marking twice is a no-op success
+ ---
+ duration_ms: 3.468292
+ type: 'test'
+ ...
+# Subtest: hyp ignore --private upgrades an existing local-only entry to ignore
+ok 1242 - hyp ignore --private upgrades an existing local-only entry to ignore
+ ---
+ duration_ms: 4.027311
+ type: 'test'
+ ...
+# Subtest: hyp ignore --private on a path already governed by a stricter .hypignore is a no-op naming the dotfile
+ok 1243 - hyp ignore --private on a path already governed by a stricter .hypignore is a no-op naming the dotfile
+ ---
+ duration_ms: 1.750891
+ type: 'test'
+ ...
+# Subtest: hyp ignore --sync writes an explicit full entry (the "asked; syncs" marker)
+ok 1244 - hyp ignore --sync writes an explicit full entry (the "asked; syncs" marker)
+ ---
+ duration_ms: 6.355821
+ type: 'test'
+ ...
+# Subtest: hyp ignore --sync is idempotent against an existing explicit full entry, but not against the mere implicit default
+ok 1245 - hyp ignore --sync is idempotent against an existing explicit full entry, but not against the mere implicit default
+ ---
+ duration_ms: 4.586911
+ type: 'test'
+ ...
+# Subtest: hyp ignore --sync downgrades an existing ignore entry back to full (re-marking is not destructive of cached rows)
+ok 1246 - hyp ignore --sync downgrades an existing ignore entry back to full (re-marking is not destructive of cached rows)
+ ---
+ duration_ms: 6.979628
+ type: 'test'
+ ...
+# Subtest: hyp ignore --sync keeps its exact deprecated-alias confirmation, internal class and store path included
+ok 1247 - hyp ignore --sync keeps its exact deprecated-alias confirmation, internal class and store path included
+ ---
+ duration_ms: 2.989343
+ type: 'test'
+ ...
+# Subtest: hyp ignore --check keeps its exact deprecated-alias human output (resolver class, real store path)
+ok 1248 - hyp ignore --check keeps its exact deprecated-alias human output (resolver class, real store path)
+ ---
+ duration_ms: 3.46743
+ type: 'test'
+ ...
+# Subtest: hyp ignore --check on an unmarked directory keeps its exact bare class line, no implicit-default suffix
+ok 1249 - hyp ignore --check on an unmarked directory keeps its exact bare class line, no implicit-default suffix
+ ---
+ duration_ms: 1.366577
+ type: 'test'
+ ...
+# Subtest: hyp ignore rejects combining --local-only, --private, and --sync
+ok 1250 - hyp ignore rejects combining --local-only, --private, and --sync
+ ---
+ duration_ms: 0.556136
+ type: 'test'
+ ...
+# Subtest: hyp unignore --private removes an ignore entry and is idempotent, leaving other classes untouched
+ok 1251 - hyp unignore --private removes an ignore entry and is idempotent, leaving other classes untouched
+ ---
+ duration_ms: 10.448341
+ type: 'test'
+ ...
+# Subtest: hyp unignore --sync removes an explicit full entry and is idempotent
+ok 1252 - hyp unignore --sync removes an explicit full entry and is idempotent
+ ---
+ duration_ms: 4.647774
+ type: 'test'
+ ...
+# Subtest: hyp ignore --check names the machine-local source for a --private mark
+ok 1253 - hyp ignore --check names the machine-local source for a --private mark
+ ---
+ duration_ms: 7.712482
+ type: 'test'
+ ...
+# Subtest: hyp ignore --check names the dotfile source when a .hypignore governs
+ok 1254 - hyp ignore --check names the dotfile source when a .hypignore governs
+ ---
+ duration_ms: 6.179312
+ type: 'test'
+ ...
+# Subtest: hyp ignore --check names no source when nothing governs (the implicit default)
+ok 1255 - hyp ignore --check names no source when nothing governs (the implicit default)
+ ---
+ duration_ms: 0.734316
+ type: 'test'
+ ...
+# Subtest: bare hyp ignore [path] (no flags) still writes the LLP 0049 dotfile, unaffected by --private/--sync existing
+ok 1256 - bare hyp ignore [path] (no flags) still writes the LLP 0049 dotfile, unaffected by --private/--sync existing
+ ---
+ duration_ms: 2.308659
+ type: 'test'
+ ...
+# Subtest: allocator hands out a strictly increasing run from 1
+ok 1257 - allocator hands out a strictly increasing run from 1
+ ---
+ duration_ms: 14.923062
+ type: 'test'
+ ...
+# Subtest: reserve-before-stamp: persisted nextSeq is always ahead of the last issued seq
+ok 1258 - reserve-before-stamp: persisted nextSeq is always ahead of the last issued seq
+ ---
+ duration_ms: 6.092319
+ type: 'test'
+ ...
+# Subtest: allocator never regresses across a restart and skips the abandoned block tail
+ok 1259 - allocator never regresses across a restart and skips the abandoned block tail
+ ---
+ duration_ms: 3.2108
+ type: 'test'
+ ...
+# Subtest: concurrent next() calls never collide (single allocator, parallel flushes)
+ok 1260 - concurrent next() calls never collide (single allocator, parallel flushes)
+ ---
+ duration_ms: 24.871362
+ type: 'test'
+ ...
+# Subtest: default block size is a positive integer and rejects bad input
+ok 1261 - default block size is a positive integer and rejects bad input
+ ---
+ duration_ms: 0.938657
+ type: 'test'
+ ...
+# Subtest: streamFlushFile stamps a monotonic _hyp_ingest_seq and adds the column
+ok 1262 - streamFlushFile stamps a monotonic _hyp_ingest_seq and adds the column
+ ---
+ duration_ms: 5.611549
+ type: 'test'
+ ...
+# Subtest: streamFlushFile leaves seq null and still declares the column when no allocator is wired
+ok 1263 - streamFlushFile leaves seq null and still declares the column when no allocator is wired
+ ---
+ duration_ms: 4.340897
+ type: 'test'
+ ...
+# Subtest: seq survives a flush into Iceberg, increases per row, and is stripped from readRows
+ok 1264 - seq survives a flush into Iceberg, increases per row, and is stripped from readRows
+ ---
+ duration_ms: 46.683885
+ type: 'test'
+ ...
+# Subtest: renderConfigSummary: a local install reads as set up, not fleet-managed
+ok 1265 - renderConfigSummary: a local install reads as set up, not fleet-managed
+ ---
+ duration_ms: 1.004366
+ type: 'test'
+ ...
+# Subtest: renderConfigSummary: a fleet-managed install marks each client synced vs local only
+ok 1266 - renderConfigSummary: a fleet-managed install marks each client synced vs local only
+ ---
+ duration_ms: 0.222198
+ type: 'test'
+ ...
+# Subtest: hyp init on a configured install fronts the picker with the summary menu
+ok 1267 - hyp init on a configured install fronts the picker with the summary menu
+ ---
+ duration_ms: 166.028587
+ type: 'test'
+ ...
+# Subtest: hyp init first run presents the pathway fork; a bare enter quits untouched
+ok 1268 - hyp init first run presents the pathway fork; a bare enter quits untouched
+ ---
+ duration_ms: 28.187442
+ type: 'test'
+ ...
+# Subtest: hyp init: choosing "See full status" renders the status report and exits 0
+ok 1269 - hyp init: choosing "See full status" renders the status report and exits 0
+ ---
+ duration_ms: 37.924282
+ type: 'test'
+ ...
+# Subtest: omitting --export defaults to local-parquet (origin=default)
+ok 1270 - omitting --export defaults to local-parquet (origin=default)
+ ---
+ duration_ms: 1.113182
+ type: 'test'
+ ...
+# Subtest: --yes no longer changes the omitted-export default
+ok 1271 - --yes no longer changes the omitted-export default
+ ---
+ duration_ms: 0.128345
+ type: 'test'
+ ...
+# Subtest: explicit --export keep-local is honored (origin=user)
+ok 1272 - explicit --export keep-local is honored (origin=user)
+ ---
+ duration_ms: 0.09852
+ type: 'test'
+ ...
+# Subtest: explicit --export configure-later is honored (origin=user)
+ok 1273 - explicit --export configure-later is honored (origin=user)
+ ---
+ duration_ms: 0.091449
+ type: 'test'
+ ...
+# Subtest: explicit --export local-parquet still reports origin=user
+ok 1274 - explicit --export local-parquet still reports origin=user
+ ---
+ duration_ms: 0.154375
+ type: 'test'
+ ...
+# Subtest: core's default gateway endpoint tracks the ai-gateway plugin's DEFAULT_LISTEN
+ok 1275 - core's default gateway endpoint tracks the ai-gateway plugin's DEFAULT_LISTEN
+ ---
+ duration_ms: 0.756911
+ type: 'test'
+ ...
+# Subtest: the picker writer leaves the gateway listen unset so the fixed default applies
+ok 1276 - the picker writer leaves the gateway listen unset so the fixed default applies
+ ---
+ duration_ms: 24.53448
+ type: 'test'
+ ...
+# Subtest: the claude-and-otel-local preset leaves the gateway listen unset so the fixed default applies
+ok 1277 - the claude-and-otel-local preset leaves the gateway listen unset so the fixed default applies
+ ---
+ duration_ms: 4.144148
+ type: 'test'
+ ...
+# Subtest: init --from-file into a fresh home writes the config
+ok 1278 - init --from-file into a fresh home writes the config
+ ---
+ duration_ms: 177.157271
+ type: 'test'
+ ...
+# Subtest: init --from-file refuses to clobber an existing local config without --force
+ok 1279 - init --from-file refuses to clobber an existing local config without --force
+ ---
+ duration_ms: 22.882509
+ type: 'test'
+ ...
+# Subtest: init --from-file --force backs up then overwrites
+ok 1280 - init --from-file --force backs up then overwrites
+ ---
+ duration_ms: 33.151667
+ type: 'test'
+ ...
+# Subtest: init --yes refuses to clobber an existing local config without --force
+ok 1281 - init --yes refuses to clobber an existing local config without --force
+ ---
+ duration_ms: 29.911282
+ type: 'test'
+ ...
+# Subtest: interactive init: declining the overwrite prompt aborts with the config intact
+ok 1282 - interactive init: declining the overwrite prompt aborts with the config intact
+ ---
+ duration_ms: 18.806995
+ type: 'test'
+ ...
+# Subtest: interactive init: confirming the overwrite prompt backs up then writes
+ok 1283 - interactive init: confirming the overwrite prompt backs up then writes
+ ---
+ duration_ms: 10.290781
+ type: 'test'
+ ...
+# Subtest: init rejects an unrecognized flag as a flag, not a preset
+ok 1284 - init rejects an unrecognized flag as a flag, not a preset
+ ---
+ duration_ms: 15.280135
+ type: 'test'
+ ...
+# Subtest: buildPickerBackfillRunner: sweeping derives from the real provider contributions
+ok 1285 - buildPickerBackfillRunner: sweeping derives from the real provider contributions
+ ---
+ duration_ms: 7.705742
+ type: 'test'
+ ...
+# Subtest: the claude-and-otel-local preset composes the graph pair beside its gateway
+ok 1286 - the claude-and-otel-local preset composes the graph pair beside its gateway
+ ---
+ duration_ms: 22.085839
+ type: 'test'
+ ...
+# Subtest: the preset composes the graph engine before the connector that requires it
+ok 1287 - the preset composes the graph engine before the connector that requires it
+ ---
+ duration_ms: 2.360478
+ type: 'test'
+ ...
+# Subtest: attach returns the parsed structured result
+ok 1288 - attach returns the parsed structured result
+ ---
+ duration_ms: 32.89055
+ type: 'test'
+ ...
+# Subtest: detach returns the parsed structured result (core disk undo)
+ok 1289 - detach returns the parsed structured result (core disk undo)
+ ---
+ duration_ms: 11.498747
+ type: 'test'
+ ...
+# Subtest: detach reverses a client whose adapter was dropped from the live gateway (LLP 0045 §Part 3)
+ok 1290 - detach reverses a client whose adapter was dropped from the live gateway (LLP 0045 §Part 3)
+ ---
+ duration_ms: 9.314877
+ type: 'test'
+ ...
+# Subtest: detach resolves an INSTALLED (non-bundled) client adapter from the bundled+installed descriptor map (LLP 0045 §Part 3)
+ok 1291 - detach resolves an INSTALLED (non-bundled) client adapter from the bundled+installed descriptor map (LLP 0045 §Part 3)
+ ---
+ duration_ms: 11.866005
+ type: 'test'
+ ...
+# Subtest: detach works with the @hypaware/ai-gateway capability absent (disk-driven undo, LLP 0045 §Part 3)
+ok 1292 - detach works with the @hypaware/ai-gateway capability absent (disk-driven undo, LLP 0045 §Part 3)
+ ---
+ duration_ms: 7.992018
+ type: 'test'
+ ...
+# Subtest: attach stays gated on the @hypaware/ai-gateway capability (adapter_not_enabled)
+ok 1293 - attach stays gated on the @hypaware/ai-gateway capability (adapter_not_enabled)
+ ---
+ duration_ms: 5.907088
+ type: 'test'
+ ...
+# Subtest: attach throws HypAwareCommandError for an unknown client
+ok 1294 - attach throws HypAwareCommandError for an unknown client
+ ---
+ duration_ms: 6.443734
+ type: 'test'
+ ...
+# Subtest: join writes the central seed and returns success
+ok 1295 - join writes the central seed and returns success
+ ---
+ duration_ms: 26.374406
+ type: 'test'
+ ...
+# Subtest: join throws HypAwareCommandError for an invalid url
+ok 1296 - join throws HypAwareCommandError for an invalid url
+ ---
+ duration_ms: 6.92166
+ type: 'test'
+ ...
+# Subtest: join rejects dryRun instead of silently writing the seed
+ok 1297 - join rejects dryRun instead of silently writing the seed
+ ---
+ duration_ms: 1.027342
+ type: 'test'
+ ...
+# Subtest: attach rejects the "all" target instead of dropping results
+ok 1298 - attach rejects the "all" target instead of dropping results
+ ---
+ duration_ms: 0.908911
+ type: 'test'
+ ...
+# Subtest: detach rejects the "all" target instead of dropping results
+ok 1299 - detach rejects the "all" target instead of dropping results
+ ---
+ duration_ms: 1.01932
+ type: 'test'
+ ...
+# Subtest: run is the escape hatch for multi-client attach (every client surfaces)
+ok 1300 - run is the escape hatch for multi-client attach (every client surfaces)
+ ---
+ duration_ms: 9.497436
+ type: 'test'
+ ...
+# Subtest: run().json recovers the JSON object past trailing non-JSON prose
+ok 1301 - run().json recovers the JSON object past trailing non-JSON prose
+ ---
+ duration_ms: 16.160492
+ type: 'test'
+ ...
+# Subtest: run exposes raw code and captured streams
+ok 1302 - run exposes raw code and captured streams
+ ---
+ duration_ms: 12.489533
+ type: 'test'
+ ...
+# Subtest: join writes the central seed (mode 0600) and skips daemon install with --no-daemon
+ok 1303 - join writes the central seed (mode 0600) and skips daemon install with --no-daemon
+ ---
+ duration_ms: 46.247291
+ type: 'test'
+ ...
+# Subtest: join never touches an existing local config (\#111 regression)
+ok 1304 - join never touches an existing local config (\#111 regression)
+ ---
+ duration_ms: 68.490278
+ type: 'test'
+ ...
+# Subtest: join supersedes a stale active slot so the fresh token is honored (\#139)
+ok 1305 - join supersedes a stale active slot so the fresh token is honored (\#139)
+ ---
+ duration_ms: 21.386796
+ type: 'test'
+ ...
+# Subtest: join reads the token from --token-file
+ok 1306 - join reads the token from --token-file
+ ---
+ duration_ms: 17.202205
+ type: 'test'
+ ...
+# Subtest: join reads the token from stdin when piped
+ok 1307 - join reads the token from stdin when piped
+ ---
+ duration_ms: 19.821186
+ type: 'test'
+ ...
+# Subtest: join rejects missing url, bad url, missing token, and conflicting token sources
+ok 1308 - join rejects missing url, bad url, missing token, and conflicting token sources
+ ---
+ duration_ms: 27.897952
+ type: 'test'
+ ...
+# Subtest: join help exits 0 and documents token sources
+ok 1309 - join help exits 0 and documents token sources
+ ---
+ duration_ms: 6.664059
+ type: 'test'
+ ...
+# Subtest: leave when not connected is a friendly no-op
+ok 1310 - leave when not connected is a friendly no-op
+ ---
+ duration_ms: 32.578155
+ type: 'test'
+ ...
+# Subtest: leave after join removes the seed and reports the server
+ok 1311 - leave after join removes the seed and reports the server
+ ---
+ duration_ms: 205.975893
+ type: 'test'
+ ...
+# Subtest: leave clears an applied central slot, not just the seed
+ok 1312 - leave clears an applied central slot, not just the seed
+ ---
+ duration_ms: 20.368498
+ type: 'test'
+ ...
+# Subtest: leave reverses org-driven attaches and drops the forward identity
+ok 1313 - leave reverses org-driven attaches and drops the forward identity
+ ---
+ duration_ms: 60.061498
+ type: 'test'
+ ...
+# Subtest: leave never edits the local layer, and says so when a local central sink exists
+ok 1314 - leave never edits the local layer, and says so when a local central sink exists
+ ---
+ duration_ms: 13.034341
+ type: 'test'
+ ...
+# Subtest: leave after join also warns about a local central sink that keeps forwarding
+ok 1315 - leave after join also warns about a local central sink that keeps forwarding
+ ---
+ duration_ms: 41.993787
+ type: 'test'
+ ...
+# Subtest: leave is idempotent: a second leave is the not-connected no-op
+ok 1316 - leave is idempotent: a second leave is the not-connected no-op
+ ---
+ duration_ms: 31.9625
+ type: 'test'
+ ...
+# Subtest: leave tears down a central layer whose active-slot pointer does not resolve (\#623)
+ok 1317 - leave tears down a central layer whose active-slot pointer does not resolve (\#623)
+ ---
+ duration_ms: 25.817099
+ type: 'test'
+ ...
+# Subtest: leave reports a central layer it cannot remove instead of throwing (\#623)
+ok 1318 - leave reports a central layer it cannot remove instead of throwing (\#623)
+ ---
+ duration_ms: 6.15216
+ type: 'test'
+ ...
+# Subtest: leave still tears down when only a stale attach marker survives a prior partial leave
+ok 1319 - leave still tears down when only a stale attach marker survives a prior partial leave
+ ---
+ duration_ms: 20.335328
+ type: 'test'
+ ...
+# Subtest: leave removes the assets its attach marker records, and leaves manual copies alone
+ok 1320 - leave removes the assets its attach marker records, and leaves manual copies alone
+ ---
+ duration_ms: 36.147461
+ type: 'test'
+ ...
+# Subtest: leave self-heals an org attach whose plugin is gone: drops the marker, warns, stays clean
+ok 1321 - leave self-heals an org attach whose plugin is gone: drops the marker, warns, stays clean
+ ---
+ duration_ms: 42.898762
+ type: 'test'
+ ...
+# Subtest: leave help exits 0 and rejects unknown arguments
+ok 1322 - leave help exits 0 and rejects unknown arguments
+ ---
+ duration_ms: 9.407058
+ type: 'test'
+ ...
+# Subtest: the scan finds the corpus and its annotations
+ok 1323 - the scan finds the corpus and its annotations
+ ---
+ duration_ms: 0.895611
+ type: 'test'
+ ...
+# Subtest: every @ref resolves to a live LLP document and one of its anchors
+ok 1324 - every @ref resolves to a live LLP document and one of its anchors
+ ---
+ duration_ms: 2.462633
+ type: 'test'
+ ...
+# Subtest: every tolerated reference forgives no more than is still broken
+ok 1325 - every tolerated reference forgives no more than is still broken
+ ---
+ duration_ms: 1.40777
+ type: 'test'
+ ...
+# Subtest: a tolerated reference is tolerated only as often as it is listed
+ok 1326 - a tolerated reference is tolerated only as often as it is listed
+ ---
+ duration_ms: 0.211422
+ type: 'test'
+ ...
+# Subtest: no @ref annotation carries an em dash, on its first line or a later one
+ok 1327 - no @ref annotation carries an em dash, on its first line or a later one
+ ---
+ duration_ms: 0.635596
+ type: 'test'
+ ...
+# Subtest: a gloss spans its continuation lines, and stops where the gloss stops
+ok 1328 - a gloss spans its continuation lines, and stops where the gloss stops
+ ---
+ duration_ms: 2.256991
+ type: 'test'
+ ...
+# Subtest: no LLP number is claimed by two documents
+ok 1329 - no LLP number is claimed by two documents
+ ---
+ duration_ms: 0.228647
+ type: 'test'
+ ...
+# Subtest: the ignore markers hide illustrative annotations without hiding live ones
+ok 1330 - the ignore markers hide illustrative annotations without hiding live ones
+ ---
+ duration_ms: 0.213525
+ type: 'test'
+ ...
+# Subtest: documenting a marker does not activate it
+ok 1331 - documenting a marker does not activate it
+ ---
+ duration_ms: 0.275649
+ type: 'test'
+ ...
+# Subtest: a marker shown as documentation or as a code sample does not activate it
+ok 1332 - a marker shown as documentation or as a code sample does not activate it
+ ---
+ duration_ms: 0.568665
+ type: 'test'
+ ...
+# Subtest: suppression is confined to the syntax documentation and every region closes
+ok 1333 - suppression is confined to the syntax documentation and every region closes
+ ---
+ duration_ms: 170.031922
+ type: 'test'
+ ...
+# Subtest: listCapturedDirectories: groups by cwd, carries repo_root/rows/last_seen, most-recent first
+ok 1334 - listCapturedDirectories: groups by cwd, carries repo_root/rows/last_seen, most-recent first
+ ---
+ duration_ms: 11.42857
+ type: 'test'
+ ...
+# Subtest: listCapturedDirectories: a cwd is not offered when it has never been captured
+ok 1335 - listCapturedDirectories: a cwd is not offered when it has never been captured
+ ---
+ duration_ms: 1.324634
+ type: 'test'
+ ...
+# Subtest: listCapturedDirectories: best-effort - a broken registry resolves to null, never throws
+ok 1336 - listCapturedDirectories: best-effort - a broken registry resolves to null, never throws
+ ---
+ duration_ms: 0.773375
+ type: 'test'
+ ...
+# Subtest: compose_with is optional
+ok 1337 - compose_with is optional
+ ---
+ duration_ms: 0.798674
+ type: 'test'
+ ...
+# Subtest: compose_with survives validation as a plugin name array
+ok 1338 - compose_with survives validation as a plugin name array
+ ---
+ duration_ms: 0.545959
+ type: 'test'
+ ...
+# Subtest: compose_with rejects non-arrays, non-strings, and the empty array
+ok 1339 - compose_with rejects non-arrays, non-strings, and the empty array
+ ---
+ duration_ms: 0.203369
+ type: 'test'
+ ...
+# Subtest: the empty array is rejected rather than treated as no condition
+ok 1340 - the empty array is rejected rather than treated as no condition
+ ---
+ duration_ms: 0.107894
+ type: 'test'
+ ...
+# Subtest: compose_with rejects a self-reference
+ok 1341 - compose_with rejects a self-reference
+ ---
+ duration_ms: 0.155256
+ type: 'test'
+ ...
+# Subtest: a self-reference is rejected even alongside a real condition
+ok 1342 - a self-reference is rejected even alongside a real condition
+ ---
+ duration_ms: 0.090027
+ type: 'test'
+ ...
+# Subtest: a mutual compose_with pair each validate on their own
+ok 1343 - a mutual compose_with pair each validate on their own
+ ---
+ duration_ms: 0.172132
+ type: 'test'
+ ...
+# Subtest: a typo in a compose_with name is a silent no-op at every layer
+ok 1344 - a typo in a compose_with name is a silent no-op at every layer
+ ---
+ duration_ms: 16.887357
+ type: 'test'
+ ...
+# Subtest: the catalog surfaces compose_with from the shipped graph manifests
+ok 1345 - the catalog surfaces compose_with from the shipped graph manifests
+ ---
+ duration_ms: 9.437323
+ type: 'test'
+ ...
+# Subtest: plugins without the field are absent from the rider map
+ok 1346 - plugins without the field are absent from the rider map
+ ---
+ duration_ms: 10.98066
+ type: 'test'
+ ...
+# Subtest: validateManifest accepts the plugin manifest fields the kernel consumes
+ok 1347 - validateManifest accepts the plugin manifest fields the kernel consumes
+ ---
+ duration_ms: 0.951136
+ type: 'test'
+ ...
+# Subtest: validateManifest rejects malformed nested maps
+ok 1348 - validateManifest rejects malformed nested maps
+ ---
+ duration_ms: 0.175597
+ type: 'test'
+ ...
+# Subtest: validateManifest accepts a picker array with all three probe variants
+ok 1349 - validateManifest accepts a picker array with all three probe variants
+ ---
+ duration_ms: 0.162436
+ type: 'test'
+ ...
+# Subtest: validateManifest accepts a picker row without a detect probe
+ok 1350 - validateManifest accepts a picker row without a detect probe
+ ---
+ duration_ms: 0.097999
+ type: 'test'
+ ...
+# Subtest: validateManifest keeps unknown picker fields opaque (e.g. compose)
+ok 1351 - validateManifest keeps unknown picker fields opaque (e.g. compose)
+ ---
+ duration_ms: 0.156287
+ type: 'test'
+ ...
+# Subtest: validateManifest rejects a non-array picker
+ok 1352 - validateManifest rejects a non-array picker
+ ---
+ duration_ms: 0.095465
+ type: 'test'
+ ...
+# Subtest: validateManifest rejects a picker row missing a name
+ok 1353 - validateManifest rejects a picker row missing a name
+ ---
+ duration_ms: 0.144089
+ type: 'test'
+ ...
+# Subtest: validateManifest rejects a picker row missing a label
+ok 1354 - validateManifest rejects a picker row missing a label
+ ---
+ duration_ms: 0.096277
+ type: 'test'
+ ...
+# Subtest: validateManifest rejects a detect probe with no recognized variant
+ok 1355 - validateManifest rejects a detect probe with no recognized variant
+ ---
+ duration_ms: 0.251633
+ type: 'test'
+ ...
+# Subtest: validateManifest rejects a detect probe with more than one variant
+ok 1356 - validateManifest rejects a detect probe with more than one variant
+ ---
+ duration_ms: 0.36189
+ type: 'test'
+ ...
+# Subtest: validateManifest rejects a non-string probe path
+ok 1357 - validateManifest rejects a non-string probe path
+ ---
+ duration_ms: 0.150008
+ type: 'test'
+ ...
+# Subtest: validateManifest rejects a non-boolean needs_setup
+ok 1358 - validateManifest rejects a non-boolean needs_setup
+ ---
+ duration_ms: 0.083808
+ type: 'test'
+ ...
+# Subtest: validateManifest rejects a non-boolean hidden
+ok 1359 - validateManifest rejects a non-boolean hidden
+ ---
+ duration_ms: 0.071048
+ type: 'test'
+ ...
+# Subtest: validateManifest accepts a hidden picker row
+ok 1360 - validateManifest accepts a hidden picker row
+ ---
+ duration_ms: 0.068474
+ type: 'test'
+ ...
+# Subtest: matchesSemverRange covers exact, wildcard, caret, tilde, and comparisons
+ok 1361 - matchesSemverRange covers exact, wildcard, caret, tilde, and comparisons
+ ---
+ duration_ms: 0.280016
+ type: 'test'
+ ...
+# Subtest: matchesSemverRange preserves zero-major caret behavior
+ok 1362 - matchesSemverRange preserves zero-major caret behavior
+ ---
+ duration_ms: 0.068715
+ type: 'test'
+ ...
+# Subtest: initialize advertises serverInfo + tool/resource capabilities, echoing the client protocol
+ok 1363 - initialize advertises serverInfo + tool/resource capabilities, echoing the client protocol
+ ---
+ duration_ms: 1.127123
+ type: 'test'
+ ...
+# Subtest: notifications get no response
+ok 1364 - notifications get no response
+ ---
+ duration_ms: 1.274036
+ type: 'test'
+ ...
+# Subtest: tools/list on stdio shows read + operator + local-only (local-user trust)
+ok 1365 - tools/list on stdio shows read + operator + local-only (local-user trust)
+ ---
+ duration_ms: 0.569175
+ type: 'test'
+ ...
+# Subtest: tools/list on an http transport without operator scope hides operator + local-only
+ok 1366 - tools/list on an http transport without operator scope hides operator + local-only
+ ---
+ duration_ms: 0.126372
+ type: 'test'
+ ...
+# Subtest: tools/call runs the operation and sanitizes BigInt for JSON transport
+ok 1367 - tools/call runs the operation and sanitizes BigInt for JSON transport
+ ---
+ duration_ms: 0.493089
+ type: 'test'
+ ...
+# Subtest: tools/call on an operator-gated server cannot reach an operator tool
+ok 1368 - tools/call on an operator-gated server cannot reach an operator tool
+ ---
+ duration_ms: 0.161456
+ type: 'test'
+ ...
+# Subtest: tools/call with bad arguments is an invalid-params error
+ok 1369 - tools/call with bad arguments is an invalid-params error
+ ---
+ duration_ms: 0.17765
+ type: 'test'
+ ...
+# Subtest: a thrown operation becomes an isError tool result, not a protocol error
+ok 1370 - a thrown operation becomes an isError tool result, not a protocol error
+ ---
+ duration_ms: 0.218692
+ type: 'test'
+ ...
+# Subtest: resources expose dataset schemas; read returns the columns
+ok 1371 - resources expose dataset schemas; read returns the columns
+ ---
+ duration_ms: 0.421661
+ type: 'test'
+ ...
+# Subtest: unknown method and unknown resource are proper errors
+ok 1372 - unknown method and unknown resource are proper errors
+ ---
+ duration_ms: 0.290472
+ type: 'test'
+ ...
+# Subtest: query seam: a null-cwd ai_gateway_messages row is visible to the fail-closed unknown caller
+ok 1373 - query seam: a null-cwd ai_gateway_messages row is visible to the fail-closed unknown caller
+ ---
+ duration_ms: 10.351373
+ type: 'test'
+ ...
+# Subtest: query seam: a null-cwd ai_gateway_messages row is visible to a restricted full-class caller
+ok 1374 - query seam: a null-cwd ai_gateway_messages row is visible to a restricted full-class caller
+ ---
+ duration_ms: 1.48054
+ type: 'test'
+ ...
+# Subtest: export seam: a null-cwd ai_gateway_messages row is forwarded (full by construction)
+ok 1375 - export seam: a null-cwd ai_gateway_messages row is forwarded (full by construction)
+ ---
+ duration_ms: 50.499454
+ type: 'test'
+ ...
+# Subtest: whereToParquetFilter converts simple comparisons
+ok 1376 - whereToParquetFilter converts simple comparisons
+ ---
+ duration_ms: 3.802269
+ type: 'test'
+ ...
+# Subtest: whereToParquetFilter mirrors flipped operands (literal on the left)
+ok 1377 - whereToParquetFilter mirrors flipped operands (literal on the left)
+ ---
+ duration_ms: 0.326997
+ type: 'test'
+ ...
+# Subtest: whereToParquetFilter handles AND / OR / NOT
+ok 1378 - whereToParquetFilter handles AND / OR / NOT
+ ---
+ duration_ms: 0.838875
+ type: 'test'
+ ...
+# Subtest: whereToParquetFilter handles IN / NOT IN / IS NULL
+ok 1379 - whereToParquetFilter handles IN / NOT IN / IS NULL
+ ---
+ duration_ms: 0.352155
+ type: 'test'
+ ...
+# Subtest: whereToParquetFilter folds typed literals (TIMESTAMP casts)
+ok 1380 - whereToParquetFilter folds typed literals (TIMESTAMP casts)
+ ---
+ duration_ms: 0.477706
+ type: 'test'
+ ...
+# Subtest: whereToParquetFilter only unwraps truthiness-preserving casts
+ok 1381 - whereToParquetFilter only unwraps truthiness-preserving casts
+ ---
+ duration_ms: 0.206704
+ type: 'test'
+ ...
+# Subtest: whereToParquetFilter returns undefined for non-convertible predicates
+ok 1382 - whereToParquetFilter returns undefined for non-convertible predicates
+ ---
+ duration_ms: 0.209749
+ type: 'test'
+ ...
+# Subtest: whereToParquetFilter declines NULL-literal comparisons to the engine
+ok 1383 - whereToParquetFilter declines NULL-literal comparisons to the engine
+ ---
+ duration_ms: 0.337513
+ type: 'test'
+ ...
+# Subtest: whereToParquetFilter handles NULL members of an IN list
+ok 1384 - whereToParquetFilter handles NULL members of an IN list
+ ---
+ duration_ms: 0.412266
+ type: 'test'
+ ...
+# Subtest: pushed-down comparisons do not leak NULL rows (issue \#728)
+ok 1385 - pushed-down comparisons do not leak NULL rows (issue \#728)
+ ---
+ duration_ms: 63.237875
+ type: 'test'
+ ...
+# Subtest: comparison against a NULL literal matches no rows (issue \#728)
+ok 1386 - comparison against a NULL literal matches no rows (issue \#728)
+ ---
+ duration_ms: 7.34276
+ type: 'test'
+ ...
+# Subtest: negated comparisons against a NULL literal match no rows (issue \#734)
+ok 1387 - negated comparisons against a NULL literal match no rows (issue \#734)
+ ---
+ duration_ms: 31.99494
+ type: 'test'
+ ...
+# Subtest: predicates that are not always-UNKNOWN keep their ordinary handling (issue \#734)
+ok 1388 - predicates that are not always-UNKNOWN keep their ordinary handling (issue \#734)
+ ---
+ duration_ms: 8.462262
+ type: 'test'
+ ...
+# Subtest: a NULL member in an IN list does not cost row-group pruning (issue \#734)
+ok 1389 - a NULL member in an IN list does not cost row-group pruning (issue \#734)
+ ---
+ duration_ms: 2.850553
+ type: 'test'
+ ...
+# Subtest: parquetDataSource exposes schema columns and row count
+ok 1390 - parquetDataSource exposes schema columns and row count
+ ---
+ duration_ms: 0.633623
+ type: 'test'
+ ...
+# Subtest: SELECT * returns every row across row groups
+ok 1391 - SELECT * returns every row across row groups
+ ---
+ duration_ms: 0.850373
+ type: 'test'
+ ...
+# Subtest: WHERE with pushed-down filter returns matching rows
+ok 1392 - WHERE with pushed-down filter returns matching rows
+ ---
+ duration_ms: 0.851795
+ type: 'test'
+ ...
+# Subtest: WHERE on a non-projected column still filters correctly
+ok 1393 - WHERE on a non-projected column still filters correctly
+ ---
+ duration_ms: 0.524597
+ type: 'test'
+ ...
+# Subtest: a pushed-down filter does not widen the projection
+ok 1394 - a pushed-down filter does not widen the projection
+ ---
+ duration_ms: 0.816561
+ type: 'test'
+ ...
+# Subtest: range WHERE (AND) returns the inclusive window
+ok 1395 - range WHERE (AND) returns the inclusive window
+ ---
+ duration_ms: 0.563166
+ type: 'test'
+ ...
+# Subtest: timestamp day bounds filter correctly through the pushed-down scan
+ok 1396 - timestamp day bounds filter correctly through the pushed-down scan
+ ---
+ duration_ms: 0.615165
+ type: 'test'
+ ...
+# Subtest: a folded timestamp bound is actually pushed down, not left to the engine
+ok 1397 - a folded timestamp bound is actually pushed down, not left to the engine
+ ---
+ duration_ms: 0.244943
+ type: 'test'
+ ...
+# Subtest: a timestamp bound matching no rows returns none (and one matching all returns all)
+ok 1398 - a timestamp bound matching no rows returns none (and one matching all returns all)
+ ---
+ duration_ms: 1.357183
+ type: 'test'
+ ...
+# Subtest: LIKE falls back to engine filtering (not pushed down)
+ok 1399 - LIKE falls back to engine filtering (not pushed down)
+ ---
+ duration_ms: 0.560552
+ type: 'test'
+ ...
+# Subtest: LIMIT/OFFSET without WHERE is pushed down
+ok 1400 - LIMIT/OFFSET without WHERE is pushed down
+ ---
+ duration_ms: 1.306246
+ type: 'test'
+ ...
+# Subtest: ORDER BY ... LIMIT sees all rows before limiting
+ok 1401 - ORDER BY ... LIMIT sees all rows before limiting
+ ---
+ duration_ms: 1.260766
+ type: 'test'
+ ...
+# Subtest: WHERE + LIMIT applies the limit over the filtered stream
+ok 1402 - WHERE + LIMIT applies the limit over the filtered stream
+ ---
+ duration_ms: 0.720776
+ type: 'test'
+ ...
+# Subtest: aggregate over the source
+ok 1403 - aggregate over the source
+ ---
+ duration_ms: 1.040281
+ type: 'test'
+ ...
+# Subtest: decideConfirmation: --yes returns auto_yes proceed
+ok 1404 - decideConfirmation: --yes returns auto_yes proceed
+ ---
+ duration_ms: 0.653663
+ type: 'test'
+ ...
+# Subtest: decideConfirmation: --yes wins even with a tty (no prompt)
+ok 1405 - decideConfirmation: --yes wins even with a tty (no prompt)
+ ---
+ duration_ms: 0.135286
+ type: 'test'
+ ...
+# Subtest: decideConfirmation: non-tty without --yes returns non_tty_no_yes
+ok 1406 - decideConfirmation: non-tty without --yes returns non_tty_no_yes
+ ---
+ duration_ms: 0.077317
+ type: 'test'
+ ...
+# Subtest: decideConfirmation: tty + ask returning true => confirmed/proceed
+ok 1407 - decideConfirmation: tty + ask returning true => confirmed/proceed
+ ---
+ duration_ms: 0.081925
+ type: 'test'
+ ...
+# Subtest: decideConfirmation: tty + ask returning false => rejected/abort
+ok 1408 - decideConfirmation: tty + ask returning false => rejected/abort
+ ---
+ duration_ms: 0.13191
+ type: 'test'
+ ...
+# Subtest: buildTtyPrompt: real readline returns the trimmed answer (yes)
+ok 1409 - buildTtyPrompt: real readline returns the trimmed answer (yes)
+ ---
+ duration_ms: 1.204861
+ type: 'test'
+ ...
+# Subtest: buildTtyPrompt: real readline returns false when the user types anything else
+ok 1410 - buildTtyPrompt: real readline returns false when the user types anything else
+ ---
+ duration_ms: 0.440119
+ type: 'test'
+ ...
+# Subtest: buildTtyPrompt: real readline trims whitespace before deciding
+ok 1411 - buildTtyPrompt: real readline trims whitespace before deciding
+ ---
+ duration_ms: 0.318865
+ type: 'test'
+ ...
+# Subtest: sourceIsUnpinnedBranch: missing ref counts as unpinned
+ok 1412 - sourceIsUnpinnedBranch: missing ref counts as unpinned
+ ---
+ duration_ms: 0.333507
+ type: 'test'
+ ...
+# Subtest: sourceIsUnpinnedBranch: branch name counts as unpinned
+ok 1413 - sourceIsUnpinnedBranch: branch name counts as unpinned
+ ---
+ duration_ms: 0.433289
+ type: 'test'
+ ...
+# Subtest: sourceIsUnpinnedBranch: semver tag counts as pinned
+ok 1414 - sourceIsUnpinnedBranch: semver tag counts as pinned
+ ---
+ duration_ms: 0.198021
+ type: 'test'
+ ...
+# Subtest: sourceIsUnpinnedBranch: commit sha counts as pinned
+ok 1415 - sourceIsUnpinnedBranch: commit sha counts as pinned
+ ---
+ duration_ms: 0.09248
+ type: 'test'
+ ...
+# Subtest: sourceIsUnpinnedBranch: local-dir is not a branch
+ok 1416 - sourceIsUnpinnedBranch: local-dir is not a branch
+ ---
+ duration_ms: 0.075585
+ type: 'test'
+ ...
+# Subtest: buildWarnings: broad permission `network` triggers a warning
+ok 1417 - buildWarnings: broad permission `network` triggers a warning
+ ---
+ duration_ms: 0.185412
+ type: 'test'
+ ...
+# Subtest: buildWarnings: unpinned branch triggers a warning
+ok 1418 - buildWarnings: unpinned branch triggers a warning
+ ---
+ duration_ms: 0.120553
+ type: 'test'
+ ...
+# Subtest: buildWarnings: pinned tag emits no warning
+ok 1419 - buildWarnings: pinned tag emits no warning
+ ---
+ duration_ms: 0.536365
+ type: 'test'
+ ...
+# Subtest: renderConfirmationSummary: install header lists permissions, entrypoint, content_hash, resolved_ref
+ok 1420 - renderConfirmationSummary: install header lists permissions, entrypoint, content_hash, resolved_ref
+ ---
+ duration_ms: 0.279094
+ type: 'test'
+ ...
+# Subtest: renderConfirmationSummary: update header shows diff arrows when version/ref/hash change
+ok 1421 - renderConfirmationSummary: update header shows diff arrows when version/ref/hash change
+ ---
+ duration_ms: 0.20399
+ type: 'test'
+ ...
+# Subtest: installPlugin (git): non-tty without confirm callback succeeds (legacy direct caller)
+ok 1422 - installPlugin (git): non-tty without confirm callback succeeds (legacy direct caller)
+ ---
+ duration_ms: 171.84001
+ type: 'test'
+ ...
+# Subtest: installPlugin (git): confirm returning non_tty_no_yes fails with remote_install_confirmation_required
+ok 1423 - installPlugin (git): confirm returning non_tty_no_yes fails with remote_install_confirmation_required
+ ---
+ duration_ms: 141.537864
+ type: 'test'
+ ...
+# Subtest: installPlugin (git): confirm returning auto_yes proceeds and stamps confirmation
+ok 1424 - installPlugin (git): confirm returning auto_yes proceeds and stamps confirmation
+ ---
+ duration_ms: 153.513966
+ type: 'test'
+ ...
+# Subtest: installPlugin (git): confirm sees manifest, source, hashes, resolved_ref before commit
+ok 1425 - installPlugin (git): confirm sees manifest, source, hashes, resolved_ref before commit
+ ---
+ duration_ms: 150.071725
+ type: 'test'
+ ...
+# Subtest: installPlugin (local-dir): does not invoke the confirm callback
+ok 1426 - installPlugin (local-dir): does not invoke the confirm callback
+ ---
+ duration_ms: 5.892336
+ type: 'test'
+ ...
+# Subtest: updatePlugin: returns plugin_not_installed for an unknown name
+ok 1427 - updatePlugin: returns plugin_not_installed for an unknown name
+ ---
+ duration_ms: 3.964255
+ type: 'test'
+ ...
+# Subtest: updatePlugin (git): re-fetches with same source, exposes previous to confirm
+ok 1428 - updatePlugin (git): re-fetches with same source, exposes previous to confirm
+ ---
+ duration_ms: 200.689579
+ type: 'test'
+ ...
+# Subtest: updatePlugin (git): rejection leaves the prior install untouched
+ok 1429 - updatePlugin (git): rejection leaves the prior install untouched
+ ---
+ duration_ms: 187.3612
+ type: 'test'
+ ...
+# Subtest: parseGitSource accepts https GitHub URLs with and without .git suffix
+ok 1430 - parseGitSource accepts https GitHub URLs with and without .git suffix
+ ---
+ duration_ms: 1.945617
+ type: 'test'
+ ...
+# Subtest: parseGitSource extracts ref from URL fragment
+ok 1431 - parseGitSource extracts ref from URL fragment
+ ---
+ duration_ms: 0.136368
+ type: 'test'
+ ...
+# Subtest: parseGitSource normalizes github: shorthand to HTTPS clone URL
+ok 1432 - parseGitSource normalizes github: shorthand to HTTPS clone URL
+ ---
+ duration_ms: 0.156337
+ type: 'test'
+ ...
+# Subtest: parseGitSource normalizes git@github.com SSH shorthand to HTTPS clone URL
+ok 1433 - parseGitSource normalizes git@github.com SSH shorthand to HTTPS clone URL
+ ---
+ duration_ms: 0.195698
+ type: 'test'
+ ...
+# Subtest: parseGitSource passes through non-GitHub git URLs untouched
+ok 1434 - parseGitSource passes through non-GitHub git URLs untouched
+ ---
+ duration_ms: 0.390974
+ type: 'test'
+ ...
+# Subtest: applyGitSourceFlags rejects --ref when a URL fragment was already supplied
+ok 1435 - applyGitSourceFlags rejects --ref when a URL fragment was already supplied
+ ---
+ duration_ms: 0.398075
+ type: 'test'
+ ...
+# Subtest: applyGitSourceFlags adopts --ref when the URL has no fragment
+ok 1436 - applyGitSourceFlags adopts --ref when the URL has no fragment
+ ---
+ duration_ms: 0.116337
+ type: 'test'
+ ...
+# Subtest: applyGitSourceFlags rejects --path subdir with git_subdir_unsupported
+ok 1437 - applyGitSourceFlags rejects --path subdir with git_subdir_unsupported
+ ---
+ duration_ms: 0.153544
+ type: 'test'
+ ...
+# Subtest: resolveSource forwards --ref into the resolved git source spec
+ok 1438 - resolveSource forwards --ref into the resolved git source spec
+ ---
+ duration_ms: 0.50637
+ type: 'test'
+ ...
+# Subtest: resolveSource throws git_subdir_unsupported when --path is provided
+ok 1439 - resolveSource throws git_subdir_unsupported when --path is provided
+ ---
+ duration_ms: 0.44064
+ type: 'test'
+ ...
+# Subtest: resolveSource throws source_ambiguous when --ref conflicts with URL fragment
+ok 1440 - resolveSource throws source_ambiguous when --ref conflicts with URL fragment
+ ---
+ duration_ms: 0.180945
+ type: 'test'
+ ...
+# Subtest: resolveSource still routes github: shorthand through the git path
+ok 1441 - resolveSource still routes github: shorthand through the git path
+ ---
+ duration_ms: 0.103126
+ type: 'test'
+ ...
+# Subtest: provenanceFromUrl extracts host/owner/repo from HTTPS clone URL
+ok 1442 - provenanceFromUrl extracts host/owner/repo from HTTPS clone URL
+ ---
+ duration_ms: 0.166923
+ type: 'test'
+ ...
+# Subtest: validateEntrypoint rejects absolute paths
+ok 1443 - validateEntrypoint rejects absolute paths
+ ---
+ duration_ms: 0.113753
+ type: 'test'
+ ...
+# Subtest: validateEntrypoint rejects parent-directory traversal
+ok 1444 - validateEntrypoint rejects parent-directory traversal
+ ---
+ duration_ms: 0.120853
+ type: 'test'
+ ...
+# Subtest: validateEntrypoint accepts a relative path that stays inside the artifact root
+ok 1445 - validateEntrypoint accepts a relative path that stays inside the artifact root
+ ---
+ duration_ms: 0.093763
+ type: 'test'
+ ...
+# Subtest: findSymlink reports the first symlink encountered in the tree
+ok 1446 - findSymlink reports the first symlink encountered in the tree
+ ---
+ duration_ms: 16.777159
+ type: 'test'
+ ...
+# Subtest: findSymlink returns null for a tree with no symlinks
+ok 1447 - findSymlink returns null for a tree with no symlinks
+ ---
+ duration_ms: 2.233334
+ type: 'test'
+ ...
+# Subtest: hashArtifactTree is stable across two equal trees
+ok 1448 - hashArtifactTree is stable across two equal trees
+ ---
+ duration_ms: 7.049685
+ type: 'test'
+ ...
+# Subtest: hashArtifactTree changes when file content changes
+ok 1449 - hashArtifactTree changes when file content changes
+ ---
+ duration_ms: 2.393898
+ type: 'test'
+ ...
+# Subtest: parseGitSource rejects raw input that begins with a dash
+ok 1450 - parseGitSource rejects raw input that begins with a dash
+ ---
+ duration_ms: 0.183809
+ type: 'test'
+ ...
+# Subtest: parseGitSource rejects URL fragment refs that begin with a dash
+ok 1451 - parseGitSource rejects URL fragment refs that begin with a dash
+ ---
+ duration_ms: 0.115295
+ type: 'test'
+ ...
+# Subtest: parseGitSource strips userinfo from passthrough URLs
+ok 1452 - parseGitSource strips userinfo from passthrough URLs
+ ---
+ duration_ms: 0.141185
+ type: 'test'
+ ...
+# Subtest: parseGitSource strips userinfo from passthrough URLs with a fragment
+ok 1453 - parseGitSource strips userinfo from passthrough URLs with a fragment
+ ---
+ duration_ms: 0.087874
+ type: 'test'
+ ...
+# Subtest: parseGitSource accepts GitHub HTTPS URLs that carry userinfo and ignores it
+ok 1454 - parseGitSource accepts GitHub HTTPS URLs that carry userinfo and ignores it
+ ---
+ duration_ms: 0.148616
+ type: 'test'
+ ...
+# Subtest: applyGitSourceFlags rejects --ref values that start with a dash
+ok 1455 - applyGitSourceFlags rejects --ref values that start with a dash
+ ---
+ duration_ms: 0.139301
+ type: 'test'
+ ...
+# Subtest: applyGitSourceFlags rejects --path values that start with a dash before reporting unsupported
+ok 1456 - applyGitSourceFlags rejects --path values that start with a dash before reporting unsupported
+ ---
+ duration_ms: 0.127824
+ type: 'test'
+ ...
+# Subtest: redactGitUrl strips user:pass@ userinfo
+ok 1457 - redactGitUrl strips user:pass@ userinfo
+ ---
+ duration_ms: 0.102426
+ type: 'test'
+ ...
+# Subtest: redactGitUrl is a no-op on URLs without userinfo
+ok 1458 - redactGitUrl is a no-op on URLs without userinfo
+ ---
+ duration_ms: 0.084519
+ type: 'test'
+ ...
+# Subtest: redactGitUrl preserves port and path through the redaction
+ok 1459 - redactGitUrl preserves port and path through the redaction
+ ---
+ duration_ms: 0.084709
+ type: 'test'
+ ...
+# Subtest: redactRawSource strips userinfo while preserving the \#ref fragment
+ok 1460 - redactRawSource strips userinfo while preserving the \#ref fragment
+ ---
+ duration_ms: 0.083978
+ type: 'test'
+ ...
+# Subtest: resolveSource persists redacted raw + gitUrl for a passthrough URL with credentials
+ok 1461 - resolveSource persists redacted raw + gitUrl for a passthrough URL with credentials
+ ---
+ duration_ms: 0.11124
+ type: 'test'
+ ...
+# Subtest: resolveSource rejects rawSource that begins with a dash
+ok 1462 - resolveSource rejects rawSource that begins with a dash
+ ---
+ duration_ms: 0.167104
+ type: 'test'
+ ...
+# Subtest: pickLsRemoteSha prefers the peeled commit for an annotated tag
+ok 1463 - pickLsRemoteSha prefers the peeled commit for an annotated tag
+ ---
+ duration_ms: 0.221837
+ type: 'test'
+ ...
+# Subtest: pickLsRemoteSha returns the lightweight tag SHA when no peeled line exists
+ok 1464 - pickLsRemoteSha returns the lightweight tag SHA when no peeled line exists
+ ---
+ duration_ms: 0.128465
+ type: 'test'
+ ...
+# Subtest: pickLsRemoteSha prefers the HEAD line when HEAD was requested
+ok 1465 - pickLsRemoteSha prefers the HEAD line when HEAD was requested
+ ---
+ duration_ms: 0.092881
+ type: 'test'
+ ...
+# Subtest: pickLsRemoteSha returns undefined when no commit-shaped SHA is present
+ok 1466 - pickLsRemoteSha returns undefined when no commit-shaped SHA is present
+ ---
+ duration_ms: 0.068093
+ type: 'test'
+ ...
+# Subtest: hyp policy set ignore marks the path ignore in the machine-local store, never touching the repo
+ok 1467 - hyp policy set ignore marks the path ignore in the machine-local store, never touching the repo
+ ---
+ duration_ms: 27.88319
+ type: 'test'
+ ...
+# Subtest: hyp policy set ignore is idempotent: marking twice is a no-op success
+ok 1468 - hyp policy set ignore is idempotent: marking twice is a no-op success
+ ---
+ duration_ms: 3.844363
+ type: 'test'
+ ...
+# Subtest: hyp policy set ignore upgrades an existing local-only entry to ignore
+ok 1469 - hyp policy set ignore upgrades an existing local-only entry to ignore
+ ---
+ duration_ms: 5.187654
+ type: 'test'
+ ...
+# Subtest: hyp policy set ignore on a path already governed by a stricter .hypignore is a no-op naming the dotfile
+ok 1470 - hyp policy set ignore on a path already governed by a stricter .hypignore is a no-op naming the dotfile
+ ---
+ duration_ms: 1.937595
+ type: 'test'
+ ...
+# Subtest: hyp policy set sync writes an explicit full entry (the sync -> full token mapping)
+ok 1471 - hyp policy set sync writes an explicit full entry (the sync -> full token mapping)
+ ---
+ duration_ms: 6.740175
+ type: 'test'
+ ...
+# Subtest: hyp policy set sync is idempotent against an existing explicit full entry, but not against the mere implicit default
+ok 1472 - hyp policy set sync is idempotent against an existing explicit full entry, but not against the mere implicit default
+ ---
+ duration_ms: 3.34888
+ type: 'test'
+ ...
+# Subtest: hyp policy set sync confirms in the public vocabulary and never names the backing store file
+ok 1473 - hyp policy set sync confirms in the public vocabulary and never names the backing store file
+ ---
+ duration_ms: 2.0326
+ type: 'test'
+ ...
+# Subtest: hyp policy set ignore also names the marking as machine-local
+ok 1474 - hyp policy set ignore also names the marking as machine-local
+ ---
+ duration_ms: 1.886668
+ type: 'test'
+ ...
+# Subtest: hyp policy set sync twice reports the existing mark in the public vocabulary, naming the store neutrally
+ok 1475 - hyp policy set sync twice reports the existing mark in the public vocabulary, naming the store neutrally
+ ---
+ duration_ms: 4.213414
+ type: 'test'
+ ...
+# Subtest: hyp policy set local-only marks the path local-only
+ok 1476 - hyp policy set local-only marks the path local-only
+ ---
+ duration_ms: 3.4938
+ type: 'test'
+ ...
+# Subtest: hyp policy set rejects an unknown class token with a usage error naming the three valid tokens
+ok 1477 - hyp policy set rejects an unknown class token with a usage error naming the three valid tokens
+ ---
+ duration_ms: 1.32963
+ type: 'test'
+ ...
+# Subtest: hyp policy set requires a path (bare class token alone is ambiguous, so it is rejected)
+ok 1478 - hyp policy set requires a path (bare class token alone is ambiguous, so it is rejected)
+ ---
+ duration_ms: 0.632171
+ type: 'test'
+ ...
+# Subtest: hyp policy show [path] --json is byte-compatible with hyp ignore --check --json for a machine-local mark
+ok 1479 - hyp policy show [path] --json is byte-compatible with hyp ignore --check --json for a machine-local mark
+ ---
+ duration_ms: 8.982512
+ type: 'test'
+ ...
+# Subtest: hyp policy show defaults to cwd and names the dotfile source when a .hypignore governs
+ok 1480 - hyp policy show defaults to cwd and names the dotfile source when a .hypignore governs
+ ---
+ duration_ms: 1.254897
+ type: 'test'
+ ...
+# Subtest: hyp policy show (human) renders a stored full entry as sync and names the store neutrally
+ok 1481 - hyp policy show (human) renders a stored full entry as sync and names the store neutrally
+ ---
+ duration_ms: 3.035603
+ type: 'test'
+ ...
+# Subtest: hyp policy show (human) keeps naming a governing dotfile by its real path
+ok 1482 - hyp policy show (human) keeps naming a governing dotfile by its real path
+ ---
+ duration_ms: 1.317713
+ type: 'test'
+ ...
+# Subtest: hyp policy show --json keeps the stored vocabulary and the store path (unchanged machine contract)
+ok 1483 - hyp policy show --json keeps the stored vocabulary and the store path (unchanged machine contract)
+ ---
+ duration_ms: 9.012197
+ type: 'test'
+ ...
+# Subtest: hyp policy show names no source when nothing governs (the implicit default)
+ok 1484 - hyp policy show names no source when nothing governs (the implicit default)
+ ---
+ duration_ms: 0.865095
+ type: 'test'
+ ...
+# Subtest: hyp policy show (human) flags the implicit default so it cannot be mistaken for an explicit sync mark
+ok 1485 - hyp policy show (human) flags the implicit default so it cannot be mistaken for an explicit sync mark
+ ---
+ duration_ms: 0.878876
+ type: 'test'
+ ...
+# Subtest: hyp policy unset ignore (scoped) removes an ignore entry and is idempotent, leaving other classes untouched
+ok 1486 - hyp policy unset ignore (scoped) removes an ignore entry and is idempotent, leaving other classes untouched
+ ---
+ duration_ms: 7.008152
+ type: 'test'
+ ...
+# Subtest: hyp policy unset sync (scoped) removes an explicit full entry and is idempotent
+ok 1487 - hyp policy unset sync (scoped) removes an explicit full entry and is idempotent
+ ---
+ duration_ms: 3.227786
+ type: 'test'
+ ...
+# Subtest: hyp policy unset sync reports removal and the no-op in the public vocabulary
+ok 1488 - hyp policy unset sync reports removal and the no-op in the public vocabulary
+ ---
+ duration_ms: 3.232523
+ type: 'test'
+ ...
+# Subtest: hyp policy unset (class-neutral) names each removed entry class in the public vocabulary
+ok 1489 - hyp policy unset (class-neutral) names each removed entry class in the public vocabulary
+ ---
+ duration_ms: 2.607833
+ type: 'test'
+ ...
+# Subtest: hyp policy unset (no trailing class token) is class-neutral: removes every machine-local entry governing the target
+ok 1490 - hyp policy unset (no trailing class token) is class-neutral: removes every machine-local entry governing the target
+ ---
+ duration_ms: 3.196358
+ type: 'test'
+ ...
+# Subtest: hyp policy unset (no trailing class token) removes multiple entries of different classes governing the same target
+ok 1491 - hyp policy unset (no trailing class token) removes multiple entries of different classes governing the same target
+ ---
+ duration_ms: 4.529164
+ type: 'test'
+ ...
+# Subtest: hyp policy unset (no trailing class token) on an already-clean path is a class-neutral no-op success
+ok 1492 - hyp policy unset (no trailing class token) on an already-clean path is a class-neutral no-op success
+ ---
+ duration_ms: 0.766325
+ type: 'test'
+ ...
+# Subtest: hyp policy unset rejects an unknown trailing class token with a usage error naming the three valid tokens
+ok 1493 - hyp policy unset rejects an unknown trailing class token with a usage error naming the three valid tokens
+ ---
+ duration_ms: 0.42129
+ type: 'test'
+ ...
+# Subtest: hyp policy unset requires a path
+ok 1494 - hyp policy unset requires a path
+ ---
+ duration_ms: 0.43413
+ type: 'test'
+ ...
+# Subtest: hyp policy list --json enumerates every machine-local entry with the store path
+ok 1495 - hyp policy list --json enumerates every machine-local entry with the store path
+ ---
+ duration_ms: 5.630418
+ type: 'test'
+ ...
+# Subtest: hyp policy list --json on an empty store lists zero entries successfully
+ok 1496 - hyp policy list --json on an empty store lists zero entries successfully
+ ---
+ duration_ms: 1.22338
+ type: 'test'
+ ...
+# Subtest: hyp policy list (human) renders a full entry as sync and labels the store path
+ok 1497 - hyp policy list (human) renders a full entry as sync and labels the store path
+ ---
+ duration_ms: 2.030867
+ type: 'test'
+ ...
+# Subtest: hyp policy list (human) on an empty store reports no entries without error, labelling the store path
+ok 1498 - hyp policy list (human) on an empty store reports no entries without error, labelling the store path
+ ---
+ duration_ms: 1.024257
+ type: 'test'
+ ...
+# Subtest: hyp policy show on a corrupt store fails with the policy-store wording, not "local-only list"
+ok 1499 - hyp policy show on a corrupt store fails with the policy-store wording, not "local-only list"
+ ---
+ duration_ms: 1.275378
+ type: 'test'
+ ...
+# Subtest: hyp policy list on a corrupt store fails with the policy-store wording, not "local-only list"
+ok 1500 - hyp policy list on a corrupt store fails with the policy-store wording, not "local-only list"
+ ---
+ duration_ms: 6.727986
+ type: 'test'
+ ...
+# Subtest: hyp policy set on a corrupt store fails with the policy-store wording, not "local-only list"
+ok 1501 - hyp policy set on a corrupt store fails with the policy-store wording, not "local-only list"
+ ---
+ duration_ms: 1.284071
+ type: 'test'
+ ...
+# Subtest: hyp policy unset on a corrupt store fails with the policy-store wording, not "local-only list"
+ok 1502 - hyp policy unset on a corrupt store fails with the policy-store wording, not "local-only list"
+ ---
+ duration_ms: 2.858915
+ type: 'test'
+ ...
+# Subtest: hyp policy on a corrupt store tags the command span with the local_only_list_unreadable error_kind
+ok 1503 - hyp policy on a corrupt store tags the command span with the local_only_list_unreadable error_kind
+ ---
+ duration_ms: 11.46774
+ type: 'test'
+ ...
+# Subtest: hyp policy (bare) renders group help listing set/show/unset/list
+ok 1504 - hyp policy (bare) renders group help listing set/show/unset/list
+ ---
+ duration_ms: 0.983304
+ type: 'test'
+ ...
+# Subtest: hyp policy client: empty store lists nothing opted out and names the store
+ok 1505 - hyp policy client: empty store lists nothing opted out and names the store
+ ---
+ duration_ms: 14.818613
+ type: 'test'
+ ...
+# Subtest: hyp policy client local-only writes the opt-out; show and list reflect it; sync removes it
+ok 1506 - hyp policy client local-only writes the opt-out; show and list reflect it; sync removes it
+ ---
+ duration_ms: 25.737869
+ type: 'test'
+ ...
+# Subtest: hyp policy client --json: both shapes are stable, before and after an opt-out
+ok 1507 - hyp policy client --json: both shapes are stable, before and after an opt-out
+ ---
+ duration_ms: 28.623726
+ type: 'test'
+ ...
+# Subtest: hyp policy client with an unknown name exits 2 and names known ids
+ok 1508 - hyp policy client with an unknown name exits 2 and names known ids
+ ---
+ duration_ms: 5.154144
+ type: 'test'
+ ...
+# Subtest: hyp policy client refuses to opt out a central-configured source (LLP 0188 \#locked)
+ok 1509 - hyp policy client refuses to opt out a central-configured source (LLP 0188 \#locked)
+ ---
+ duration_ms: 14.767546
+ type: 'test'
+ ...
+# Subtest: hyp policy client fails loudly on a corrupt store
+ok 1510 - hyp policy client fails loudly on a corrupt store
+ ---
+ duration_ms: 4.49998
+ type: 'test'
+ ...
+# Subtest: hyp policy list includes the clients section, text and --json
+ok 1511 - hyp policy list includes the clients section, text and --json
+ ---
+ duration_ms: 15.123466
+ type: 'test'
+ ...
+# Subtest: hyp policy folders: with nothing set, reports the sync default and names the store
+ok 1512 - hyp policy folders: with nothing set, reports the sync default and names the store
+ ---
+ duration_ms: 0.990596
+ type: 'test'
+ ...
+# Subtest: hyp policy folders ask buys the per-folder question; folders sync retires it again
+ok 1513 - hyp policy folders ask buys the per-folder question; folders sync retires it again
+ ---
+ duration_ms: 2.832285
+ type: 'test'
+ ...
+# Subtest: hyp policy folders is idempotent and never touches the directory store
+ok 1514 - hyp policy folders is idempotent and never touches the directory store
+ ---
+ duration_ms: 3.554462
+ type: 'test'
+ ...
+# Subtest: hyp policy folders --json emits the mode and the store path, both shapes
+ok 1515 - hyp policy folders --json emits the mode and the store path, both shapes
+ ---
+ duration_ms: 3.051218
+ type: 'test'
+ ...
+# Subtest: hyp policy folders rejects an unknown mode with a usage error
+ok 1516 - hyp policy folders rejects an unknown mode with a usage error
+ ---
+ duration_ms: 0.570607
+ type: 'test'
+ ...
+# Subtest: hyp policy folders fails loudly on a corrupt preference and names the repair
+ok 1517 - hyp policy folders fails loudly on a corrupt preference and names the repair
+ ---
+ duration_ms: 2.476233
+ type: 'test'
+ ...
+# Subtest: hyp policy list surfaces the per-folder ask when it is on, text and --json
+ok 1518 - hyp policy list surfaces the per-folder ask when it is on, text and --json
+ ---
+ duration_ms: 6.53979
+ type: 'test'
+ ...
+# Subtest: purge subtree deletes rows equal-or-descendant, leaves siblings
+ok 1519 - purge subtree deletes rows equal-or-descendant, leaves siblings
+ ---
+ duration_ms: 49.201872
+ type: 'test'
+ ...
+# Subtest: purge subtree is segment-aware: /home/u/repoA does not match /home/u/repoA-other
+ok 1520 - purge subtree is segment-aware: /home/u/repoA does not match /home/u/repoA-other
+ ---
+ duration_ms: 41.295706
+ type: 'test'
+ ...
+# Subtest: purge subtree matches a row recorded under a symlink spelling of the target
+ok 1521 - purge subtree matches a row recorded under a symlink spelling of the target
+ ---
+ duration_ms: 23.806343
+ type: 'test'
+ ...
+# Subtest: purge --session deletes only that session
+ok 1522 - purge --session deletes only that session
+ ---
+ duration_ms: 28.220132
+ type: 'test'
+ ...
+# Subtest: purge --ignored deletes only rows whose cwd resolves ignore
+ok 1523 - purge --ignored deletes only rows whose cwd resolves ignore
+ ---
+ duration_ms: 16.502032
+ type: 'test'
+ ...
+# Subtest: purge --all deletes every row
+ok 1524 - purge --all deletes every row
+ ---
+ duration_ms: 18.991035
+ type: 'test'
+ ...
+# Subtest: purge reports distinct purged cwds for the resurrection warning
+ok 1525 - purge reports distinct purged cwds for the resurrection warning
+ ---
+ duration_ms: 11.832595
+ type: 'test'
+ ...
+# Subtest: purge preserves surviving rows' part_id (dedupe identity) and does not resurrect on re-scan
+ok 1526 - purge preserves surviving rows' part_id (dedupe identity) and does not resurrect on re-scan
+ ---
+ duration_ms: 15.905625
+ type: 'test'
+ ...
+# Subtest: purge updates the partition cursor rowCount to the live count
+ok 1527 - purge updates the partition cursor rowCount to the live count
+ ---
+ duration_ms: 11.087011
+ type: 'test'
+ ...
+# Subtest: purge is idempotent: re-purging the same target deletes nothing new
+ok 1528 - purge is idempotent: re-purging the same target deletes nothing new
+ ---
+ duration_ms: 11.031127
+ type: 'test'
+ ...
+# Subtest: deleteMatchingRows on an empty/absent table is a no-op
+ok 1529 - deleteMatchingRows on an empty/absent table is a no-op
+ ---
+ duration_ms: 0.795018
+ type: 'test'
+ ...
+# Subtest: runPurge: bare purge (no target) errors
+ok 1530 - runPurge: bare purge (no target) errors
+ ---
+ duration_ms: 1.686393
+ type: 'test'
+ ...
+# Subtest: runPurge: two targets error
+ok 1531 - runPurge: two targets error
+ ---
+ duration_ms: 2.51952
+ type: 'test'
+ ...
+# Subtest: runPurge: non-TTY without --yes refuses
+ok 1532 - runPurge: non-TTY without --yes refuses
+ ---
+ duration_ms: 5.972608
+ type: 'test'
+ ...
+# Subtest: runPurge --all --yes deletes everything and reports counts
+ok 1533 - runPurge --all --yes deletes everything and reports counts
+ ---
+ duration_ms: 14.846595
+ type: 'test'
+ ...
+# Subtest: runPurge subtree warns about resurrection when the dir still resolves full
+ok 1534 - runPurge subtree warns about resurrection when the dir still resolves full
+ ---
+ duration_ms: 12.269157
+ type: 'test'
+ ...
+# Subtest: runPurge --ignored is durable: an ignored dir does not warn
+ok 1535 - runPurge --ignored is durable: an ignored dir does not warn
+ ---
+ duration_ms: 17.680913
+ type: 'test'
+ ...
+# Subtest: runPurge --json emits machine-readable counts and resurrectable dirs
+ok 1536 - runPurge --json emits machine-readable counts and resurrectable dirs
+ ---
+ duration_ms: 19.148544
+ type: 'test'
+ ...
+# Subtest: \#485 premise: the two fixture spellings are different strings that NFC folds together
+ok 1537 - \#485 premise: the two fixture spellings are different strings that NFC folds together
+ ---
+ duration_ms: 0.199704
+ type: 'test'
+ ...
+# Subtest: purge subtree deletes a row recorded NFD when the target is typed NFC, on a volume that folds them
+ok 1538 - purge subtree deletes a row recorded NFD when the target is typed NFC, on a volume that folds them
+ ---
+ duration_ms: 18.786513
+ type: 'test'
+ ...
+# Subtest: purge subtree deletes a row recorded NFC when the target is typed NFD, on a volume that folds them
+ok 1539 - purge subtree deletes a row recorded NFC when the target is typed NFD, on a volume that folds them
+ ---
+ duration_ms: 20.86344
+ type: 'test'
+ ...
+# Subtest: purge subtree deletes a row recorded under a case variant, on a case-insensitive volume
+ok 1540 - purge subtree deletes a row recorded under a case variant, on a case-insensitive volume
+ ---
+ duration_ms: 20.191569
+ type: 'test'
+ ...
+# Subtest: purge subtree never widens onto a spelling the volume says is a different directory
+ok 1541 - purge subtree never widens onto a spelling the volume says is a different directory
+ ---
+ duration_ms: 11.311612
+ type: 'test'
+ ...
+# Subtest: purge subtree agrees with the real filesystem about an NFC/NFD pair, and never over-deletes
+ok 1542 - purge subtree agrees with the real filesystem about an NFC/NFD pair, and never over-deletes
+ ---
+ duration_ms: 14.28324
+ type: 'test'
+ ...
+# Subtest: purge subtree agrees with the real filesystem about a case variant, and never over-deletes
+ok 1543 - purge subtree agrees with the real filesystem about a case variant, and never over-deletes
+ ---
+ duration_ms: 9.80968
+ type: 'test'
+ ...
+# Subtest: runPurge says so when it leaves a lookalike spelling in place, instead of exiting 0 in silence
+ok 1544 - runPurge says so when it leaves a lookalike spelling in place, instead of exiting 0 in silence
+ ---
+ duration_ms: 8.101463
+ type: 'test'
+ ...
+# Subtest: scopeGovernance stays unwidened for callers that do not opt in
+ok 1545 - scopeGovernance stays unwidened for callers that do not opt in
+ ---
+ duration_ms: 0.610928
+ type: 'test'
+ ...
+# Subtest: the near-miss note does not claim a verdict an ENOENT never gave
+ok 1546 - the near-miss note does not claim a verdict an ENOENT never gave
+ ---
+ duration_ms: 7.423683
+ type: 'test'
+ ...
+# Subtest: the near-miss note agrees in number with the rows it is counting
+ok 1547 - the near-miss note agrees in number with the rows it is counting
+ ---
+ duration_ms: 7.905786
+ type: 'test'
+ ...
+# Subtest: the near-miss note does not say "no longer on disk" of an alias that is on disk but unreadable
+ok 1548 - the near-miss note does not say "no longer on disk" of an alias that is on disk but unreadable
+ ---
+ duration_ms: 15.462241
+ type: 'test'
+ ...
+# Subtest: no stat errno makes an unprovable alias deletable
+ok 1549 - no stat errno makes an unprovable alias deletable
+ ---
+ duration_ms: 1.168566
+ type: 'test'
+ ...
+# Subtest: cell truncation clips long strings with a sized marker, leaves short ones
+ok 1550 - cell truncation clips long strings with a sized marker, leaves short ones
+ ---
+ duration_ms: 1.021613
+ type: 'test'
+ ...
+# Subtest: cell truncation recurses into nested objects/arrays and preserves JSON validity
+ok 1551 - cell truncation recurses into nested objects/arrays and preserves JSON validity
+ ---
+ duration_ms: 0.360998
+ type: 'test'
+ ...
+# Subtest: cell truncation counts code points, never splitting a multibyte char
+ok 1552 - cell truncation counts code points, never splitting a multibyte char
+ ---
+ duration_ms: 0.147584
+ type: 'test'
+ ...
+# Subtest: maxCell = 0 disables truncation
+ok 1553 - maxCell = 0 disables truncation
+ ---
+ duration_ms: 0.142196
+ type: 'test'
+ ...
+# Subtest: byte budget drops trailing rows and emits a notice naming the counts
+ok 1554 - byte budget drops trailing rows and emits a notice naming the counts
+ ---
+ duration_ms: 0.430174
+ type: 'test'
+ ...
+# Subtest: byte budget always keeps at least one row even if it alone exceeds the budget
+ok 1555 - byte budget always keeps at least one row even if it alone exceeds the budget
+ ---
+ duration_ms: 0.195818
+ type: 'test'
+ ...
+# Subtest: no notice when nothing is dropped
+ok 1556 - no notice when nothing is dropped
+ ---
+ duration_ms: 0.201577
+ type: 'test'
+ ...
+# Subtest: input result is not mutated
+ok 1557 - input result is not mutated
+ ---
+ duration_ms: 0.15776
+ type: 'test'
+ ...
+# Subtest: truncation is lazy: rows past the budget are never touched
+ok 1558 - truncation is lazy: rows past the budget are never touched
+ ---
+ duration_ms: 0.409663
+ type: 'test'
+ ...
+# Subtest: table format escapes every control and bidi character in a cell
+ok 1559 - table format escapes every control and bidi character in a cell
+ ---
+ duration_ms: 1.132842
+ type: 'test'
+ ...
+# Subtest: markdown format escapes control and bidi, and still escapes pipes
+ok 1560 - markdown format escapes control and bidi, and still escapes pipes
+ ---
+ duration_ms: 0.289921
+ type: 'test'
+ ...
+# Subtest: a newline in a table cell cannot forge a row
+ok 1561 - a newline in a table cell cannot forge a row
+ ---
+ duration_ms: 0.166503
+ type: 'test'
+ ...
+# Subtest: tab and carriage return get their familiar spellings
+ok 1562 - tab and carriage return get their familiar spellings
+ ---
+ duration_ms: 0.200085
+ type: 'test'
+ ...
+# Subtest: json and jsonl stay byte-exact, control characters included
+ok 1563 - json and jsonl stay byte-exact, control characters included
+ ---
+ duration_ms: 1.037016
+ type: 'test'
+ ...
+# Subtest: ordinary non-ASCII text is not touched by either human format
+ok 1564 - ordinary non-ASCII text is not touched by either human format
+ ---
+ duration_ms: 0.244962
+ type: 'test'
+ ...
+# Subtest: column widths and alignment survive an escaped cell
+ok 1565 - column widths and alignment survive an escaped cell
+ ---
+ duration_ms: 0.189628
+ type: 'test'
+ ...
+# Subtest: table column width is measured on the escaped header, not the raw column name
+ok 1566 - table column width is measured on the escaped header, not the raw column name
+ ---
+ duration_ms: 0.156768
+ type: 'test'
+ ...
+# Subtest: the spill receipt escapes its preview but the file it wrote does not
+ok 1567 - the spill receipt escapes its preview but the file it wrote does not
+ ---
+ duration_ms: 0.600443
+ type: 'test'
+ ...
+# Subtest: caller-class lattice truth table: include iff caller class >= row class
+ok 1568 - caller-class lattice truth table: include iff caller class >= row class
+ ---
+ duration_ms: 13.095875
+ type: 'test'
+ ...
+# Subtest: an omitted callerCwd behaves exactly like null: the fail-closed backstop
+ok 1569 - an omitted callerCwd behaves exactly like null: the fail-closed backstop
+ ---
+ duration_ms: 1.376402
+ type: 'test'
+ ...
+# Subtest: includeLocalOnly bypasses the filter and reports it unfiltered
+ok 1570 - includeLocalOnly bypasses the filter and reports it unfiltered
+ ---
+ duration_ms: 2.106151
+ type: 'test'
+ ...
+# Subtest: a projection that omits cwd cannot blind the filter, and the forced cwd never leaks out
+ok 1571 - a projection that omits cwd cannot blind the filter, and the forced cwd never leaks out
+ ---
+ duration_ms: 0.815009
+ type: 'test'
+ ...
+# Subtest: rows without a cwd value in a plain cwd-bearing dataset pass through (export-seam parity)
+ok 1572 - rows without a cwd value in a plain cwd-bearing dataset pass through (export-seam parity)
+ ---
+ duration_ms: 0.92709
+ type: 'test'
+ ...
+# Subtest: COUNT(*) cannot use numRows around the filter and counts only visible rows
+ok 1573 - COUNT(*) cannot use numRows around the filter and counts only visible rows
+ ---
+ duration_ms: 1.751452
+ type: 'test'
+ ...
+# Subtest: the scanColumn fast path is withheld from a filtered caller and stays lit for a top-of-lattice one
+ok 1574 - the scanColumn fast path is withheld from a filtered caller and stays lit for a top-of-lattice one
+ ---
+ duration_ms: 2.672502
+ type: 'test'
+ ...
+# Subtest: a source that eagerly applies LIMIT cannot under-return visible rows
+ok 1575 - a source that eagerly applies LIMIT cannot under-return visible rows
+ ---
+ duration_ms: 0.959338
+ type: 'test'
+ ...
+# Subtest: suppression: unprovenanced rows in a content-declaring dataset expose structure, never content
+ok 1576 - suppression: unprovenanced rows in a content-declaring dataset expose structure, never content
+ ---
+ duration_ms: 1.24988
+ type: 'test'
+ ...
+# Subtest: suppression is skipped when the scan touches no declared content column
+ok 1577 - suppression is skipped when the scan touches no declared content column
+ ---
+ duration_ms: 0.725343
+ type: 'test'
+ ...
+# Subtest: a WHERE over a suppressed column cannot reveal content by row presence
+ok 1578 - a WHERE over a suppressed column cannot reveal content by row presence
+ ---
+ duration_ms: 3.28325
+ type: 'test'
+ ...
+# Subtest: an ignore-classed caller sees unprovenanced content unsuppressed
+ok 1579 - an ignore-classed caller sees unprovenanced content unsuppressed
+ ---
+ duration_ms: 0.51272
+ type: 'test'
+ ...
+# Subtest: a resolver failure propagates: the query fails loudly, never silently unfiltered
+ok 1580 - a resolver failure propagates: the query fails loudly, never silently unfiltered
+ ---
+ duration_ms: 1.051469
+ type: 'test'
+ ...
+# Subtest: datasets with neither cwd nor a content declaration are untouched
+ok 1581 - datasets with neither cwd nor a content declaration are untouched
+ ---
+ duration_ms: 0.53977
+ type: 'test'
+ ...
+# Subtest: hyp query maintain writes a stderr line when the walk loses a partition
+ok 1582 - hyp query maintain writes a stderr line when the walk loses a partition
+ ---
+ duration_ms: 60.253349
+ type: 'test'
+ ...
+# Subtest: hyp query maintain writes nothing to stderr on a clean tick
+ok 1583 - hyp query maintain writes nothing to stderr on a clean tick
+ ---
+ duration_ms: 39.829416
+ type: 'test'
+ ...
+# Subtest: renderOverview: aligns columns, groups thousands, and bars the largest row widest
+ok 1584 - renderOverview: aligns columns, groups thousands, and bars the largest row widest
+ ---
+ duration_ms: 1.969534
+ type: 'test'
+ ...
+# Subtest: buildOverviewSql: ranked tables sort by exactly what their bar charts
+ok 1585 - buildOverviewSql: ranked tables sort by exactly what their bar charts
+ ---
+ duration_ms: 0.123718
+ type: 'test'
+ ...
+# Subtest: renderOverview: bars descend with the row order in ranked tables
+ok 1586 - renderOverview: bars descend with the row order in ranked tables
+ ---
+ duration_ms: 0.192302
+ type: 'test'
+ ...
+# Subtest: renderOverview: token bars split input from output and ignore cache
+ok 1587 - renderOverview: token bars split input from output and ignore cache
+ ---
+ duration_ms: 0.33554
+ type: 'test'
+ ...
+# Subtest: renderOverview: a component that exists never vanishes from its bar
+ok 1588 - renderOverview: a component that exists never vanishes from its bar
+ ---
+ duration_ms: 3.286284
+ type: 'test'
+ ...
+# Subtest: renderOverview: SQL is hidden behind --sql, and pointed at when hidden
+ok 1589 - renderOverview: SQL is hidden behind --sql, and pointed at when hidden
+ ---
+ duration_ms: 0.277902
+ type: 'test'
+ ...
+# Subtest: renderOverview: a labelled model with no usage is counted out loud
+ok 1590 - renderOverview: a labelled model with no usage is counted out loud
+ ---
+ duration_ms: 0.158531
+ type: 'test'
+ ...
+# Subtest: renderOverview: unlabelled groups are omitted silently, never called models
+ok 1591 - renderOverview: unlabelled groups are omitted silently, never called models
+ ---
+ duration_ms: 0.102025
+ type: 'test'
+ ...
+# Subtest: renderOverview: an unlabelled group that DID carry tokens is named, not dropped
+ok 1592 - renderOverview: an unlabelled group that DID carry tokens is named, not dropped
+ ---
+ duration_ms: 0.329972
+ type: 'test'
+ ...
+# Subtest: renderOverview: traffic with no token counts at all says so instead of an empty table
+ok 1593 - renderOverview: traffic with no token counts at all says so instead of an empty table
+ ---
+ duration_ms: 0.301328
+ type: 'test'
+ ...
+# Subtest: renderOverview: nothing but unlabelled groups reports no counts, not "0 models"
+ok 1594 - renderOverview: nothing but unlabelled groups reports no counts, not "0 models"
+ ---
+ duration_ms: 0.124038
+ type: 'test'
+ ...
+# Subtest: renderOverview: a cache-only model counts as measured, not as missing usage
+ok 1595 - renderOverview: a cache-only model counts as measured, not as missing usage
+ ---
+ duration_ms: 0.133262
+ type: 'test'
+ ...
+# Subtest: renderOverview: the caller names the heading (setup milestone vs standing command)
+ok 1596 - renderOverview: the caller names the heading (setup milestone vs standing command)
+ ---
+ duration_ms: 0.093722
+ type: 'test'
+ ...
+# Subtest: renderOverview: no rows renders the empty state with what to do next
+ok 1597 - renderOverview: no rows renders the empty state with what to do next
+ ---
+ duration_ms: 0.058158
+ type: 'test'
+ ...
+# Subtest: renderOverview: folds the tail of a long provider list into a count line
+ok 1598 - renderOverview: folds the tail of a long provider list into a count line
+ ---
+ duration_ms: 0.122116
+ type: 'test'
+ ...
+# Subtest: renderOverview: the caption and key wear the shades they describe
+ok 1599 - renderOverview: the caption and key wear the shades they describe
+ ---
+ duration_ms: 0.142947
+ type: 'test'
+ ...
+# Subtest: renderOverview: color=true wraps in ANSI, the default emits none
+ok 1600 - renderOverview: color=true wraps in ANSI, the default emits none
+ ---
+ duration_ms: 0.124479
+ type: 'test'
+ ...
+# Subtest: renderRepoMix: shortens paths, ranks by token volume, and counts repo-less sessions
+ok 1601 - renderRepoMix: shortens paths, ranks by token volume, and counts repo-less sessions
+ ---
+ duration_ms: 0.372015
+ type: 'test'
+ ...
+# Subtest: renderRepoMix: no repo at all says so rather than printing an empty table
+ok 1602 - renderRepoMix: no repo at all says so rather than printing an empty table
+ ---
+ duration_ms: 0.100643
+ type: 'test'
+ ...
+# Subtest: renderToolMix: ranks by calls and shows the session spread
+ok 1603 - renderToolMix: ranks by calls and shows the session spread
+ ---
+ duration_ms: 0.265944
+ type: 'test'
+ ...
+# Subtest: buildOverviewSql: every section carries the same window, and tools filters tool_call
+ok 1604 - buildOverviewSql: every section carries the same window, and tools filters tool_call
+ ---
+ duration_ms: 0.072971
+ type: 'test'
+ ...
+# Subtest: chooseOverviewWindow: takes the widest span that fits the row budget
+ok 1605 - chooseOverviewWindow: takes the widest span that fits the row budget
+ ---
+ duration_ms: 0.518378
+ type: 'test'
+ ...
+# Subtest: chooseOverviewWindow: everything fits when the cache is small
+ok 1606 - chooseOverviewWindow: everything fits when the cache is small
+ ---
+ duration_ms: 0.075225
+ type: 'test'
+ ...
+# Subtest: chooseOverviewWindow: one oversized day is still shown, never nothing
+ok 1607 - chooseOverviewWindow: one oversized day is still shown, never nothing
+ ---
+ duration_ms: 0.068293
+ type: 'test'
+ ...
+# Subtest: chooseOverviewWindow: the same data yields a smaller window on a slower machine
+ok 1608 - chooseOverviewWindow: the same data yields a smaller window on a slower machine
+ ---
+ duration_ms: 0.117799
+ type: 'test'
+ ...
+# Subtest: chooseOverviewWindow: the probe is charged to the budget it shares
+ok 1609 - chooseOverviewWindow: the probe is charged to the budget it shares
+ ---
+ duration_ms: 0.162317
+ type: 'test'
+ ...
+# Subtest: chooseOverviewWindow: a probe that overran the budget still yields a window
+ok 1610 - chooseOverviewWindow: a probe that overran the budget still yields a window
+ ---
+ duration_ms: 0.091619
+ type: 'test'
+ ...
+# Subtest: chooseOverviewWindow: the row cap still binds a fast machine
+ok 1611 - chooseOverviewWindow: the row cap still binds a fast machine
+ ---
+ duration_ms: 0.075634
+ type: 'test'
+ ...
+# Subtest: chooseOverviewWindow: with no probe timing, the row cap decides alone
+ok 1612 - chooseOverviewWindow: with no probe timing, the row cap decides alone
+ ---
+ duration_ms: 0.070677
+ type: 'test'
+ ...
+# Subtest: chooseOverviewWindow: the plan charges for the sections it will run, not all four
+ok 1613 - chooseOverviewWindow: the plan charges for the sections it will run, not all four
+ ---
+ duration_ms: 0.126983
+ type: 'test'
+ ...
+# Subtest: chooseOverviewWindow: an explicit --days request outranks both caps
+ok 1614 - chooseOverviewWindow: an explicit --days request outranks both caps
+ ---
+ duration_ms: 0.086482
+ type: 'test'
+ ...
+# Subtest: chooseOverviewWindow: unordered probe rows and an empty cache
+ok 1615 - chooseOverviewWindow: unordered probe rows and an empty cache
+ ---
+ duration_ms: 0.092411
+ type: 'test'
+ ...
+# Subtest: describeWindow: states the period, and the lever - never the reason
+ok 1616 - describeWindow: states the period, and the lever - never the reason
+ ---
+ duration_ms: 0.101284
+ type: 'test'
+ ...
+# Subtest: renderOverview: the window is stated under the title, always
+ok 1617 - renderOverview: the window is stated under the title, always
+ ---
+ duration_ms: 0.11216
+ type: 'test'
+ ...
+# Subtest: collectOverview: probes first, then runs only the requested sections
+ok 1618 - collectOverview: probes first, then runs only the requested sections
+ ---
+ duration_ms: 0.301758
+ type: 'test'
+ ...
+# Subtest: collectOverview: the window is planned for the sections actually requested
+ok 1619 - collectOverview: the window is planned for the sections actually requested
+ ---
+ duration_ms: 0.23684
+ type: 'test'
+ ...
+# Subtest: missingSections: a section nobody asked for is not reported as unfinished
+ok 1620 - missingSections: a section nobody asked for is not reported as unfinished
+ ---
+ duration_ms: 0.26324
+ type: 'test'
+ ...
+# Subtest: collectOverview: runs all four sections by default, in display order
+ok 1621 - collectOverview: runs all four sections by default, in display order
+ ---
+ duration_ms: 0.207725
+ type: 'test'
+ ...
+# Subtest: collectOverview: an empty cache runs no section at all
+ok 1622 - collectOverview: an empty cache runs no section at all
+ ---
+ duration_ms: 0.163889
+ type: 'test'
+ ...
+# Subtest: overviewRunnerFromCtx: aggregates through the real query seam
+ok 1623 - overviewRunnerFromCtx: aggregates through the real query seam
+ ---
+ duration_ms: 24.27791
+ type: 'test'
+ ...
+# Subtest: overviewRunnerFromCtx: a withheld row is reported, once, not once per section
+ok 1624 - overviewRunnerFromCtx: a withheld row is reported, once, not once per section
+ ---
+ duration_ms: 3.824773
+ type: 'test'
+ ...
+# Subtest: overviewRunnerFromCtx: nothing to withhold says nothing
+ok 1625 - overviewRunnerFromCtx: nothing to withhold says nothing
+ ---
+ duration_ms: 12.117528
+ type: 'test'
+ ...
+# Subtest: overviewRunnerFromCtx: no query registry yields no runner
+ok 1626 - overviewRunnerFromCtx: no query registry yields no runner
+ ---
+ duration_ms: 0.11843
+ type: 'test'
+ ...
+# Subtest: hyp query overview: renders all four sections from real rows
+ok 1627 - hyp query overview: renders all four sections from real rows
+ ---
+ duration_ms: 11.332814
+ type: 'test'
+ ...
+# Subtest: hyp query overview: a withheld row is disclosed on stderr, off the block
+ok 1628 - hyp query overview: a withheld row is disclosed on stderr, off the block
+ ---
+ duration_ms: 1.932037
+ type: 'test'
+ ...
+# Subtest: hyp query overview --sql: prints the statement above each table
+ok 1629 - hyp query overview --sql: prints the statement above each table
+ ---
+ duration_ms: 6.675036
+ type: 'test'
+ ...
+# Subtest: hyp query overview --json: emits both result sets for scripting
+ok 1630 - hyp query overview --json: emits both result sets for scripting
+ ---
+ duration_ms: 9.111488
+ type: 'test'
+ ...
+# Subtest: hyp query overview: no capture configured reports that, not a schema name
+ok 1631 - hyp query overview: no capture configured reports that, not a schema name
+ ---
+ duration_ms: 0.239043
+ type: 'test'
+ ...
+# Subtest: hyp query overview: an empty dataset renders the empty state, not an error
+ok 1632 - hyp query overview: an empty dataset renders the empty state, not an error
+ ---
+ duration_ms: 0.429983
+ type: 'test'
+ ...
+# Subtest: formatCount: groups thousands and passes non-numbers through
+ok 1633 - formatCount: groups thousands and passes non-numbers through
+ ---
+ duration_ms: 0.073141
+ type: 'test'
+ ...
+# Subtest: renderRepoMix: the fold count is the real tail, and the repo-less line survives it
+ok 1634 - renderRepoMix: the fold count is the real tail, and the repo-less line survives it
+ ---
+ duration_ms: 0.346827
+ type: 'test'
+ ...
+# Subtest: buildOverviewSql: no LIMIT on the sections whose tails are counted
+ok 1635 - buildOverviewSql: no LIMIT on the sections whose tails are counted
+ ---
+ duration_ms: 0.115706
+ type: 'test'
+ ...
+# Subtest: renderDailyActivity: a window longer than the table says how many days it folded
+ok 1636 - renderDailyActivity: a window longer than the table says how many days it folded
+ ---
+ duration_ms: 0.202748
+ type: 'test'
+ ...
+# Subtest: renderDailyActivity: a window that fits says nothing
+ok 1637 - renderDailyActivity: a window that fits says nothing
+ ---
+ duration_ms: 0.122447
+ type: 'test'
+ ...
+# Subtest: buildOverviewSql: rejects a since that is not a plain date
+ok 1638 - buildOverviewSql: rejects a since that is not a plain date
+ ---
+ duration_ms: 0.346647
+ type: 'test'
+ ...
+# Subtest: hyp query overview: --days=7 is honored, not silently ignored
+ok 1639 - hyp query overview: --days=7 is honored, not silently ignored
+ ---
+ duration_ms: 3.872415
+ type: 'test'
+ ...
+# Subtest: hyp query overview: an unknown flag is refused, not ignored
+ok 1640 - hyp query overview: an unknown flag is refused, not ignored
+ ---
+ duration_ms: 0.216689
+ type: 'test'
+ ...
+# Subtest: hyp query overview: --days rejects non-positive and non-integer values
+ok 1641 - hyp query overview: --days rejects non-positive and non-integer values
+ ---
+ duration_ms: 0.372557
+ type: 'test'
+ ...
+# Subtest: hyp query overview: --help prints usage and exits 0
+ok 1642 - hyp query overview: --help prints usage and exits 0
+ ---
+ duration_ms: 0.163178
+ type: 'test'
+ ...
+# Subtest: hyp query overview: NO_COLOR suppresses ANSI even on a TTY
+ok 1643 - hyp query overview: NO_COLOR suppresses ANSI even on a TTY
+ ---
+ duration_ms: 5.200434
+ type: 'test'
+ ...
+# Subtest: hyp query overview: --include-local-only is accepted, and does what the disclosure promises
+ok 1644 - hyp query overview: --include-local-only is accepted, and does what the disclosure promises
+ ---
+ duration_ms: 6.811633
+ type: 'test'
+ ...
+# Subtest: hyp query overview: the usage line lists every flag the codec accepts
+ok 1645 - hyp query overview: the usage line lists every flag the codec accepts
+ ---
+ duration_ms: 0.227285
+ type: 'test'
+ ...
+# Subtest: renderOverview escapes captured columns and keeps its own colour
+ok 1646 - renderOverview escapes captured columns and keeps its own colour
+ ---
+ duration_ms: 6.810861
+ type: 'test'
+ ...
+# Subtest: valid query.remotes + default_remote parses through
+ok 1647 - valid query.remotes + default_remote parses through
+ ---
+ duration_ms: 1.3026
+ type: 'test'
+ ...
+# Subtest: remotes coexists with cache in the same query block
+ok 1648 - remotes coexists with cache in the same query block
+ ---
+ duration_ms: 0.268939
+ type: 'test'
+ ...
+# Subtest: a non-http(s) url is rejected
+ok 1649 - a non-http(s) url is rejected
+ ---
+ duration_ms: 0.177099
+ type: 'test'
+ ...
+# Subtest: a remote target without a url is rejected
+ok 1650 - a remote target without a url is rejected
+ ---
+ duration_ms: 0.121134
+ type: 'test'
+ ...
+# Subtest: default_remote must name a defined target
+ok 1651 - default_remote must name a defined target
+ ---
+ duration_ms: 0.12529
+ type: 'test'
+ ...
+# Subtest: a query whose heap growth exceeds the execution budget refuses with the typed error
+ok 1652 - a query whose heap growth exceeds the execution budget refuses with the typed error
+ ---
+ duration_ms: 74.373691
+ type: 'test'
+ ...
+# Subtest: resolveHeapBudgetBytes resolves the effective ceiling and never disables on a blank env
+ok 1653 - resolveHeapBudgetBytes resolves the effective ceiling and never disables on a blank env
+ ---
+ duration_ms: 0.359606
+ type: 'test'
+ ...
+# Subtest: maxHeapBytes 0 disables the budget entirely
+ok 1654 - maxHeapBytes 0 disables the budget entirely
+ ---
+ duration_ms: 1.775819
+ type: 'test'
+ ...
+# Subtest: a pre-aborted caller signal aborts execution before rows flow
+ok 1655 - a pre-aborted caller signal aborts execution before rows flow
+ ---
+ duration_ms: 0.978427
+ type: 'test'
+ ...
+# Subtest: the streaming-aggregate scanColumn fast path stays lit through the budget decoration
+ok 1656 - the streaming-aggregate scanColumn fast path stays lit through the budget decoration
+ ---
+ duration_ms: 2.060031
+ type: 'test'
+ ...
+# Subtest: transient scan garbage does not trip the budget; only retained growth refuses
+ok 1657 - transient scan garbage does not trip the budget; only retained growth refuses
+ ---
+ duration_ms: 587.597931
+ type: 'test'
+ ...
+# Subtest: the budget decoration forwards WHERE to scanColumn and preserves the applied flags
+ok 1658 - the budget decoration forwards WHERE to scanColumn and preserves the applied flags
+ ---
+ duration_ms: 1.59871
+ type: 'test'
+ ...
+# Subtest: parse errors surface the squirreling message verbatim, unwrapped
+ok 1659 - parse errors surface the squirreling message verbatim, unwrapped
+ ---
+ duration_ms: 4.479349
+ type: 'test'
+ ...
+# Subtest: non-SELECT statements surface the parser message without extra framing
+ok 1660 - non-SELECT statements surface the parser message without extra framing
+ ---
+ duration_ms: 0.68443
+ type: 'test'
+ ...
+# Subtest: empty SQL is reported as required
+ok 1661 - empty SQL is reported as required
+ ---
+ duration_ms: 0.341169
+ type: 'test'
+ ...
+# Subtest: spill mode: file content is the full lossless result, stdout is a receipt
+ok 1662 - spill mode: file content is the full lossless result, stdout is a receipt
+ ---
+ duration_ms: 1.314437
+ type: 'test'
+ ...
+# Subtest: spill receipt preview clips cells even though the file does not
+ok 1663 - spill receipt preview clips cells even though the file does not
+ ---
+ duration_ms: 0.18408
+ type: 'test'
+ ...
+# Subtest: inline mode: small result renders in full to stdout, no notice
+ok 1664 - inline mode: small result renders in full to stdout, no notice
+ ---
+ duration_ms: 0.649838
+ type: 'test'
+ ...
+# Subtest: inline mode: over-budget result caps rows, stdout stays valid JSON, notice to stderr
+ok 1665 - inline mode: over-budget result caps rows, stdout stays valid JSON, notice to stderr
+ ---
+ duration_ms: 1.708957
+ type: 'test'
+ ...
+# Subtest: inline mode: long cells are truncated in stdout with a marker
+ok 1666 - inline mode: long cells are truncated in stdout with a marker
+ ---
+ duration_ms: 0.220826
+ type: 'test'
+ ...
+# Subtest: fork menu takes its printed default on a stdin that ends without a line
+ok 1667 - fork menu takes its printed default on a stdin that ends without a line
+ ---
+ duration_ms: 3.290672
+ type: 'test'
+ ...
+# Subtest: fork menu takes its printed default on a stdin that was already spent
+ok 1668 - fork menu takes its printed default on a stdin that was already spent
+ ---
+ duration_ms: 3.437034
+ type: 'test'
+ ...
+# Subtest: fork menu still honours an explicit pick
+ok 1669 - fork menu still honours an explicit pick
+ ---
+ duration_ms: 0.687725
+ type: 'test'
+ ...
+# Subtest: returning gate menu takes its printed default on a stdin that ends without a line
+ok 1670 - returning gate menu takes its printed default on a stdin that ends without a line
+ ---
+ duration_ms: 0.581864
+ type: 'test'
+ ...
+# Subtest: askYesNo declines on a stdin that ends without a line
+ok 1671 - askYesNo declines on a stdin that ends without a line
+ ---
+ duration_ms: 1.663448
+ type: 'test'
+ ...
+# Subtest: askYesNo declines on a stdin that was already spent
+ok 1672 - askYesNo declines on a stdin that was already spent
+ ---
+ duration_ms: 0.628785
+ type: 'test'
+ ...
+# Subtest: askYesNo still honours an explicit yes
+ok 1673 - askYesNo still honours an explicit yes
+ ---
+ duration_ms: 0.359146
+ type: 'test'
+ ...
+# Subtest: askYesNo takes an answer delivered in the same burst as the EOF
+ok 1674 - askYesNo takes an answer delivered in the same burst as the EOF
+ ---
+ duration_ms: 1.234787
+ type: 'test'
+ ...
+# Subtest: askYesNo takes a final answer with no trailing newline
+ok 1675 - askYesNo takes a final answer with no trailing newline
+ ---
+ duration_ms: 1.405807
+ type: 'test'
+ ...
+# Subtest: askYesNo answers with the line that answered it, not one that arrived after
+ok 1676 - askYesNo answers with the line that answered it, not one that arrived after
+ ---
+ duration_ms: 0.816411
+ type: 'test'
+ ...
+# Subtest: askYesNo keeps its answer when an unterminated line follows it
+ok 1677 - askYesNo keeps its answer when an unterminated line follows it
+ ---
+ duration_ms: 0.457585
+ type: 'test'
+ ...
+# Subtest: plugin install confirm declines on a stdin that ends without a line
+ok 1678 - plugin install confirm declines on a stdin that ends without a line
+ ---
+ duration_ms: 0.706514
+ type: 'test'
+ ...
+# Subtest: plugin install confirm declines on a stdin that was already spent
+ok 1679 - plugin install confirm declines on a stdin that was already spent
+ ---
+ duration_ms: 3.584428
+ type: 'test'
+ ...
+# Subtest: plugin install confirm still honours an explicit yes
+ok 1680 - plugin install confirm still honours an explicit yes
+ ---
+ duration_ms: 1.096467
+ type: 'test'
+ ...
+# Subtest: a second read with no change skips re-reading the file (parse cache)
+ok 1681 - a second read with no change skips re-reading the file (parse cache)
+ ---
+ duration_ms: 10.057326
+ type: 'test'
+ ...
+# Subtest: a write is visible to the very next read (cache is busted on write)
+ok 1682 - a write is visible to the very next read (cache is busted on write)
+ ---
+ duration_ms: 5.838994
+ type: 'test'
+ ...
+# Subtest: isRefreshable is true only for an oidc record read from the file
+ok 1683 - isRefreshable is true only for an oidc record read from the file
+ ---
+ duration_ms: 0.253195
+ type: 'test'
+ ...
+# Subtest: an oidc session round-trips with kind: oidc
+ok 1684 - an oidc session round-trips with kind: oidc
+ ---
+ duration_ms: 2.721536
+ type: 'test'
+ ...
+# Subtest: a record with a refreshToken but no accessJwt still yields its usable static token
+ok 1685 - a record with a refreshToken but no accessJwt still yields its usable static token
+ ---
+ duration_ms: 1.410384
+ type: 'test'
+ ...
+# Subtest: an oidc record with a refresh token but no cached accessJwt is kept (refreshable)
+ok 1686 - an oidc record with a refresh token but no cached accessJwt is kept (refreshable)
+ ---
+ duration_ms: 2.946969
+ type: 'test'
+ ...
+# Subtest: a static record with an empty token is dropped on read (not reported as stored)
+ok 1687 - a static record with an empty token is dropped on read (not reported as stored)
+ ---
+ duration_ms: 1.404755
+ type: 'test'
+ ...
+# Subtest: an oidc record with neither a refresh token nor a static token is dropped on read
+ok 1688 - an oidc record with neither a refresh token nor a static token is dropped on read
+ ---
+ duration_ms: 1.759574
+ type: 'test'
+ ...
+# Subtest: resolveToken returns an oidc record cached access JWT as-is
+ok 1689 - resolveToken returns an oidc record cached access JWT as-is
+ ---
+ duration_ms: 7.202347
+ type: 'test'
+ ...
+# Subtest: resolveAccessJwt refreshes a JWT inside the skew window (not yet past)
+ok 1690 - resolveAccessJwt refreshes a JWT inside the skew window (not yet past)
+ ---
+ duration_ms: 7.715898
+ type: 'test'
+ ...
+# Subtest: a legacy token-only record reads as kind: static
+ok 1691 - a legacy token-only record reads as kind: static
+ ---
+ duration_ms: 1.183228
+ type: 'test'
+ ...
+# Subtest: writeToken now stamps kind: static
+ok 1692 - writeToken now stamps kind: static
+ ---
+ duration_ms: 8.216798
+ type: 'test'
+ ...
+# Subtest: writing one target preserves a sibling record that does not normalize
+ok 1693 - writing one target preserves a sibling record that does not normalize
+ ---
+ duration_ms: 3.203519
+ type: 'test'
+ ...
+# Subtest: removeToken drops a record that does not normalize and keeps the rest
+ok 1694 - removeToken drops a record that does not normalize and keeps the rest
+ ---
+ duration_ms: 4.03334
+ type: 'test'
+ ...
+# Subtest: removeToken clears an oidc record too
+ok 1695 - removeToken clears an oidc record too
+ ---
+ duration_ms: 2.884524
+ type: 'test'
+ ...
+# Subtest: resolveAccessJwt returns a static token unchanged
+ok 1696 - resolveAccessJwt returns a static token unchanged
+ ---
+ duration_ms: 4.899907
+ type: 'test'
+ ...
+# Subtest: resolveAccessJwt honors the per-target env override over the file
+ok 1697 - resolveAccessJwt honors the per-target env override over the file
+ ---
+ duration_ms: 1.649497
+ type: 'test'
+ ...
+# Subtest: resolveAccessJwt returns a fresh oidc JWT without calling refresh
+ok 1698 - resolveAccessJwt returns a fresh oidc JWT without calling refresh
+ ---
+ duration_ms: 1.512248
+ type: 'test'
+ ...
+# Subtest: resolveAccessJwt refreshes a stale oidc JWT and persists the new one
+ok 1699 - resolveAccessJwt refreshes a stale oidc JWT and persists the new one
+ ---
+ duration_ms: 6.267566
+ type: 'test'
+ ...
+# Subtest: resolveAccessJwt persists a rotated refresh token from the refresh response
+ok 1700 - resolveAccessJwt persists a rotated refresh token from the refresh response
+ ---
+ duration_ms: 3.537887
+ type: 'test'
+ ...
+# Subtest: resolveAccessJwt keeps the stored refresh token when the server does not rotate
+ok 1701 - resolveAccessJwt keeps the stored refresh token when the server does not rotate
+ ---
+ duration_ms: 4.299474
+ type: 'test'
+ ...
+# Subtest: concurrent resolveAccessJwt is single-flight: the loser adopts the winner with no second token call
+ok 1702 - concurrent resolveAccessJwt is single-flight: the loser adopts the winner with no second token call
+ ---
+ duration_ms: 29.928759
+ type: 'test'
+ ...
+# Subtest: resolveAccessJwt does not resurrect a session removed before the refresh
+ok 1703 - resolveAccessJwt does not resurrect a session removed before the refresh
+ ---
+ duration_ms: 3.045559
+ type: 'test'
+ ...
+# Subtest: a refresh does not resurrect a record removed during the network call (commit is a compare-and-swap)
+ok 1704 - a refresh does not resurrect a record removed during the network call (commit is a compare-and-swap)
+ ---
+ duration_ms: 9.318393
+ type: 'test'
+ ...
+# Subtest: a refresh does not clobber a record a concurrent login replaced during the network call
+ok 1705 - a refresh does not clobber a record a concurrent login replaced during the network call
+ ---
+ duration_ms: 7.88847
+ type: 'test'
+ ...
+# Subtest: resolveAccessJwt keeps the stored org when a refresh response omits it
+ok 1706 - resolveAccessJwt keeps the stored org when a refresh response omits it
+ ---
+ duration_ms: 3.557106
+ type: 'test'
+ ...
+# Subtest: resolveAccessJwt errors with login guidance when no record exists
+ok 1707 - resolveAccessJwt errors with login guidance when no record exists
+ ---
+ duration_ms: 0.542314
+ type: 'test'
+ ...
+# Subtest: resolveAccessJwt with forceRefresh refreshes even when the cached JWT is still clock-fresh
+ok 1708 - resolveAccessJwt with forceRefresh refreshes even when the cached JWT is still clock-fresh
+ ---
+ duration_ms: 3.038839
+ type: 'test'
+ ...
+# Subtest: resolveAccessJwt refreshes with the freshest stored refresh token in one shot
+ok 1709 - resolveAccessJwt refreshes with the freshest stored refresh token in one shot
+ ---
+ duration_ms: 5.505257
+ type: 'test'
+ ...
+# Subtest: a write breaks a lock left stale by a crashed holder
+ok 1710 - a write breaks a lock left stale by a crashed holder
+ ---
+ duration_ms: 28.854696
+ type: 'test'
+ ...
+# Subtest: resolveAccessJwt propagates a refresh failure (invalid_grant)
+ok 1711 - resolveAccessJwt propagates a refresh failure (invalid_grant)
+ ---
+ duration_ms: 3.054733
+ type: 'test'
+ ...
+# Subtest: env var name is per-target and sanitized
+ok 1712 - env var name is per-target and sanitized
+ ---
+ duration_ms: 0.641555
+ type: 'test'
+ ...
+# Subtest: missing file reads as an empty map (not an error)
+ok 1713 - missing file reads as an empty map (not an error)
+ ---
+ duration_ms: 4.442402
+ type: 'test'
+ ...
+# Subtest: writeToken persists, is 0600, and round-trips
+ok 1714 - writeToken persists, is 0600, and round-trips
+ ---
+ duration_ms: 4.078229
+ type: 'test'
+ ...
+# Subtest: writeToken merges, removeToken drops only the named target
+ok 1715 - writeToken merges, removeToken drops only the named target
+ ---
+ duration_ms: 9.871704
+ type: 'test'
+ ...
+# Subtest: resolveToken order: env overrides file
+ok 1716 - resolveToken order: env overrides file
+ ---
+ duration_ms: 3.142146
+ type: 'test'
+ ...
+# Subtest: resolveToken errors with guidance when neither env nor file has a token
+ok 1717 - resolveToken errors with guidance when neither env nor file has a token
+ ---
+ duration_ms: 1.444896
+ type: 'test'
+ ...
+# Subtest: a corrupt credentials file throws rather than silently masking
+ok 1718 - a corrupt credentials file throws rather than silently masking
+ ---
+ duration_ms: 1.795279
+ type: 'test'
+ ...
+# Subtest: sessionExpiredMessage names the target for re-login
+ok 1719 - sessionExpiredMessage names the target for re-login
+ ---
+ duration_ms: 0.84821
+ type: 'test'
+ ...
+# Subtest: exchangeCode posts the authorization_code grant and maps the response
+ok 1720 - exchangeCode posts the authorization_code grant and maps the response
+ ---
+ duration_ms: 1.350072
+ type: 'test'
+ ...
+# Subtest: exchangeCode sends the host label with the authorization_code grant when given
+ok 1721 - exchangeCode sends the host label with the authorization_code grant when given
+ ---
+ duration_ms: 0.23038
+ type: 'test'
+ ...
+# Subtest: exchangeCode omits host from the body when none is given
+ok 1722 - exchangeCode omits host from the body when none is given
+ ---
+ duration_ms: 0.25701
+ type: 'test'
+ ...
+# Subtest: exchangeCode captures the login-minted gateway credential (LLP 0061 D1)
+ok 1723 - exchangeCode captures the login-minted gateway credential (LLP 0061 D1)
+ ---
+ duration_ms: 0.345445
+ type: 'test'
+ ...
+# Subtest: exchangeCode against a server without login-gateway support carries no gateway
+ok 1724 - exchangeCode against a server without login-gateway support carries no gateway
+ ---
+ duration_ms: 0.212943
+ type: 'test'
+ ...
+# Subtest: a partial gateway_* set is a contract violation, not a silent drop
+ok 1725 - a partial gateway_* set is a contract violation, not a silent drop
+ ---
+ duration_ms: 0.567883
+ type: 'test'
+ ...
+# Subtest: an ISO gateway_expires_at converts down to an epoch second (identity.json stores epochs)
+ok 1726 - an ISO gateway_expires_at converts down to an epoch second (identity.json stores epochs)
+ ---
+ duration_ms: 0.197229
+ type: 'test'
+ ...
+# Subtest: exchangeCode accepts the server epoch-second expires_at and normalizes it to ISO
+ok 1727 - exchangeCode accepts the server epoch-second expires_at and normalizes it to ISO
+ ---
+ duration_ms: 0.614184
+ type: 'test'
+ ...
+# Subtest: refreshSession accepts the server epoch-second expires_at and normalizes it to ISO
+ok 1728 - refreshSession accepts the server epoch-second expires_at and normalizes it to ISO
+ ---
+ duration_ms: 0.690279
+ type: 'test'
+ ...
+# Subtest: refreshSession posts the refresh_token grant and maps the response
+ok 1729 - refreshSession posts the refresh_token grant and maps the response
+ ---
+ duration_ms: 0.322329
+ type: 'test'
+ ...
+# Subtest: refreshSession returns a rotated refresh_token when the server issues one
+ok 1730 - refreshSession returns a rotated refresh_token when the server issues one
+ ---
+ duration_ms: 1.255729
+ type: 'test'
+ ...
+# Subtest: refreshSession tolerates a response that omits org (returns org: "")
+ok 1731 - refreshSession tolerates a response that omits org (returns org: "")
+ ---
+ duration_ms: 0.215878
+ type: 'test'
+ ...
+# Subtest: a 401 invalid_grant surfaces a typed InvalidGrantError
+ok 1732 - a 401 invalid_grant surfaces a typed InvalidGrantError
+ ---
+ duration_ms: 0.260336
+ type: 'test'
+ ...
+# Subtest: a 401 on the authorization_code grant does not borrow the refresh-token wording
+ok 1733 - a 401 on the authorization_code grant does not borrow the refresh-token wording
+ ---
+ duration_ms: 0.319596
+ type: 'test'
+ ...
+# Subtest: a 401 with an empty body still surfaces InvalidGrantError (re-login guidance)
+ok 1734 - a 401 with an empty body still surfaces InvalidGrantError (re-login guidance)
+ ---
+ duration_ms: 0.196729
+ type: 'test'
+ ...
+# Subtest: a 401 with a non-JSON body still surfaces InvalidGrantError
+ok 1735 - a 401 with a non-JSON body still surfaces InvalidGrantError
+ ---
+ duration_ms: 0.265434
+ type: 'test'
+ ...
+# Subtest: a non-invalid_grant error throws a generic error, not InvalidGrantError
+ok 1736 - a non-invalid_grant error throws a generic error, not InvalidGrantError
+ ---
+ duration_ms: 0.194495
+ type: 'test'
+ ...
+# Subtest: a response missing access_jwt is rejected
+ok 1737 - a response missing access_jwt is rejected
+ ---
+ duration_ms: 0.156628
+ type: 'test'
+ ...
+# Subtest: a 2xx with an empty body fails as transient, not a misleading missing-field error
+ok 1738 - a 2xx with an empty body fails as transient, not a misleading missing-field error
+ ---
+ duration_ms: 0.201176
+ type: 'test'
+ ...
+# Subtest: a non-date expires_at string is rejected at refresh time, not stored to loop forever
+ok 1739 - a non-date expires_at string is rejected at refresh time, not stored to loop forever
+ ---
+ duration_ms: 0.225372
+ type: 'test'
+ ...
+# Subtest: describeRefreshError maps invalid_grant to session-expired re-login guidance
+ok 1740 - describeRefreshError maps invalid_grant to session-expired re-login guidance
+ ---
+ duration_ms: 0.161966
+ type: 'test'
+ ...
+# Subtest: describeRefreshError passes a non-invalid_grant error through as a generic message
+ok 1741 - describeRefreshError passes a non-invalid_grant error through as a generic message
+ ---
+ duration_ms: 0.095935
+ type: 'test'
+ ...
+# Subtest: deriveIdentityBase yields /v1/identity
+ok 1742 - deriveIdentityBase yields /v1/identity
+ ---
+ duration_ms: 1.433729
+ type: 'test'
+ ...
+# Subtest: browser mode forwards --org and the derived identity base, then stores the session
+ok 1743 - browser mode forwards --org and the derived identity base, then stores the session
+ ---
+ duration_ms: 25.986397
+ type: 'test'
+ ...
+# Subtest: a successful sign-in whose session write fails reports a store failure, not a login failure
+ok 1744 - a successful sign-in whose session write fails reports a store failure, not a login failure
+ ---
+ duration_ms: 2.072661
+ type: 'test'
+ ...
+# Subtest: a server refusal is reported as its own reason, not just an exit code
+ok 1745 - a server refusal is reported as its own reason, not just an exit code
+ ---
+ duration_ms: 6.975643
+ type: 'test'
+ ...
+# Subtest: a local failure with no server code is retriable, and a success is ok
+ok 1746 - a local failure with no server code is retriable, and a success is ok
+ ---
+ duration_ms: 8.635976
+ type: 'test'
+ ...
+# Subtest: post-auth failures name their step rather than collapsing into the login failure
+ok 1747 - post-auth failures name their step rather than collapsing into the login failure
+ ---
+ duration_ms: 25.284731
+ type: 'test'
+ ...
+# Subtest: a usage error and the exclusivity gate are distinguishable, both exit 2
+ok 1748 - a usage error and the exclusivity gate are distinguishable, both exit 2
+ ---
+ duration_ms: 5.590477
+ type: 'test'
+ ...
+# Subtest: runRemoteLogin stays the exit-code adapter over the same run
+ok 1749 - runRemoteLogin stays the exit-code adapter over the same run
+ ---
+ duration_ms: 3.335529
+ type: 'test'
+ ...
+# Subtest: --no-browser passes noBrowser through to the flow
+ok 1750 - --no-browser passes noBrowser through to the flow
+ ---
+ duration_ms: 4.555314
+ type: 'test'
+ ...
+# Subtest: --no-browser still uses browser mode when stdin is non-TTY
+ok 1751 - --no-browser still uses browser mode when stdin is non-TTY
+ ---
+ duration_ms: 5.557357
+ type: 'test'
+ ...
+# Subtest: a login-minted gateway credential seeds the matching central sink (LLP 0061 D2/D5)
+ok 1752 - a login-minted gateway credential seeds the matching central sink (LLP 0061 D2/D5)
+ ---
+ duration_ms: 4.518007
+ type: 'test'
+ ...
+# Subtest: a configured persisted_path is honored and non-matching central sinks are not seeded
+ok 1753 - a configured persisted_path is honored and non-matching central sinks are not seeded
+ ---
+ duration_ms: 4.491867
+ type: 'test'
+ ...
+# Subtest: a gateway credential with no matching central sink provisions one, forwarding from one command (LLP 0063 D2)
+ok 1754 - a gateway credential with no matching central sink provisions one, forwarding from one command (LLP 0063 D2)
+ ---
+ duration_ms: 5.554292
+ type: 'test'
+ ...
+# Subtest: an enrolling login waits for the reconcile and reports the clients that actually attached
+ok 1755 - an enrolling login waits for the reconcile and reports the clients that actually attached
+ ---
+ duration_ms: 8.549505
+ type: 'test'
+ ...
+# Subtest: an enrolling login into an org with no config times out the wait and points at hyp status
+ok 1756 - an enrolling login into an org with no config times out the wait and points at hyp status
+ ---
+ duration_ms: 3.707906
+ type: 'test'
+ ...
+# Subtest: a failed daemon install reports it and does not wait for attach
+ok 1757 - a failed daemon install reports it and does not wait for attach
+ ---
+ duration_ms: 7.999218
+ type: 'test'
+ ...
+# Subtest: an enrolling login whose attach poll throws still reports the timeout fallback, not a failure (Major 1)
+ok 1758 - an enrolling login whose attach poll throws still reports the timeout fallback, not a failure (Major 1)
+ ---
+ duration_ms: 3.715397
+ type: 'test'
+ ...
+# Subtest: the enrolling login announces the attach wait on stderr before polling (Major 2)
+ok 1759 - the enrolling login announces the attach wait on stderr before polling (Major 2)
+ ---
+ duration_ms: 3.234326
+ type: 'test'
+ ...
+# Subtest: waitForClientAttach returns attached client names as soon as the reconcile lands
+ok 1760 - waitForClientAttach returns attached client names as soon as the reconcile lands
+ ---
+ duration_ms: 0.213985
+ type: 'test'
+ ...
+# Subtest: waitForClientAttach returns empty on timeout without hanging
+ok 1761 - waitForClientAttach returns empty on timeout without hanging
+ ---
+ duration_ms: 0.110949
+ type: 'test'
+ ...
+# Subtest: waitForClientAttach swallows a probe that throws mid-poll and still times out to empty (Major 1)
+ok 1762 - waitForClientAttach swallows a probe that throws mid-poll and still times out to empty (Major 1)
+ ---
+ duration_ms: 0.151581
+ type: 'test'
+ ...
+# Subtest: waitForCentralConverge: the applied slot is convergence (ok:true)
+ok 1763 - waitForCentralConverge: the applied slot is convergence (ok:true)
+ ---
+ duration_ms: 0.227296
+ type: 'test'
+ ...
+# Subtest: waitForCentralConverge: a timeout is the no-org-config steady state (ok:false)
+ok 1764 - waitForCentralConverge: a timeout is the no-org-config steady state (ok:false)
+ ---
+ duration_ms: 0.101805
+ type: 'test'
+ ...
+# Subtest: waitForCentralConverge: a throwing probe is "not converged this tick", polled to timeout
+ok 1765 - waitForCentralConverge: a throwing probe is "not converged this tick", polled to timeout
+ ---
+ duration_ms: 0.162186
+ type: 'test'
+ ...
+# Subtest: waitForCentralConverge: default probe converges on the active slot, never the seed
+ok 1766 - waitForCentralConverge: default probe converges on the active slot, never the seed
+ ---
+ duration_ms: 3.149447
+ type: 'test'
+ ...
+# Subtest: waitForCentralConverge: an unreadable active pointer reaches the probe-error branch
+ok 1767 - waitForCentralConverge: an unreadable active pointer reaches the probe-error branch
+ ---
+ duration_ms: 1.426118
+ type: 'test'
+ ...
+# Subtest: --no-forward signs in for queries only and provisions nothing (LLP 0063 D3)
+ok 1768 - --no-forward signs in for queries only and provisions nothing (LLP 0063 D3)
+ ---
+ duration_ms: 2.748788
+ type: 'test'
+ ...
+# Subtest: login to a different server than the one this machine is enrolled to is rejected before the browser (LLP 0063 D4)
+ok 1769 - login to a different server than the one this machine is enrolled to is rejected before the browser (LLP 0063 D4)
+ ---
+ duration_ms: 1.960069
+ type: 'test'
+ ...
+# Subtest: an unreadable central layer fails the D4 gate CLOSED: login to a different server is rejected (LLP 0063 D4)
+ok 1770 - an unreadable central layer fails the D4 gate CLOSED: login to a different server is rejected (LLP 0063 D4)
+ ---
+ duration_ms: 1.939648
+ type: 'test'
+ ...
+# Subtest: an unreadable central layer also refuses a same-origin re-login: the gate cannot tell it is the same (LLP 0063 D4)
+ok 1771 - an unreadable central layer also refuses a same-origin re-login: the gate cannot tell it is the same (LLP 0063 D4)
+ ---
+ duration_ms: 1.675146
+ type: 'test'
+ ...
+# Subtest: an active-slot pointer naming a file that is gone is unreadable, not absent (LLP 0063 D4)
+ok 1772 - an active-slot pointer naming a file that is gone is unreadable, not absent (LLP 0063 D4)
+ ---
+ duration_ms: 12.207545
+ type: 'test'
+ ...
+# Subtest: a central layer whose PATH cannot be resolved fails the D4 gate CLOSED too (LLP 0063 D4)
+ok 1773 - a central layer whose PATH cannot be resolved fails the D4 gate CLOSED too (LLP 0063 D4)
+ ---
+ duration_ms: 6.590196
+ type: 'test'
+ ...
+# Subtest: a central layer directory that cannot be listed fails the D4 gate CLOSED (LLP 0063 D4)
+ok 1774 - a central layer directory that cannot be listed fails the D4 gate CLOSED (LLP 0063 D4)
+ ---
+ duration_ms: 4.995803
+ type: 'test'
+ ...
+# Subtest: an unresolvable pointer does not overshoot: control-dir residue that is not a layer still permits login (LLP 0063 D4)
+ok 1775 - an unresolvable pointer does not overshoot: control-dir residue that is not a layer still permits login (LLP 0063 D4)
+ ---
+ duration_ms: 6.438836
+ type: 'test'
+ ...
+# Subtest: an ABSENT central layer is not an enrollment and still permits login (LLP 0063 D4)
+ok 1776 - an ABSENT central layer is not an enrollment and still permits login (LLP 0063 D4)
+ ---
+ duration_ms: 2.761708
+ type: 'test'
+ ...
+# Subtest: a PARSEABLE central layer keeps its D4 behavior: same origin re-logs in, different origin is rejected (LLP 0063 D4)
+ok 1777 - a PARSEABLE central layer keeps its D4 behavior: same origin re-logs in, different origin is rejected (LLP 0063 D4)
+ ---
+ duration_ms: 7.070406
+ type: 'test'
+ ...
+# Subtest: a hand-authored LOCAL central sink is not an enrollment and does not block login to a different server (LLP 0063 D4)
+ok 1778 - a hand-authored LOCAL central sink is not an enrollment and does not block login to a different server (LLP 0063 D4)
+ ---
+ duration_ms: 3.684541
+ type: 'test'
+ ...
+# Subtest: --no-forward on an already-enrolled machine reports the truth (stays enrolled), not "not enrolled" (LLP 0063 D3)
+ok 1779 - --no-forward on an already-enrolled machine reports the truth (stays enrolled), not "not enrolled" (LLP 0063 D3)
+ ---
+ duration_ms: 3.507701
+ type: 'test'
+ ...
+# Subtest: a failure seeding the identity rolls the provisioned seed back so no credential-less sink lingers (LLP 0063)
+ok 1780 - a failure seeding the identity rolls the provisioned seed back so no credential-less sink lingers (LLP 0063)
+ ---
+ duration_ms: 6.807276
+ type: 'test'
+ ...
+# Subtest: a session without a gateway credential seeds nothing and prints no forwarding output
+ok 1781 - a session without a gateway credential seeds nothing and prints no forwarding output
+ ---
+ duration_ms: 3.668836
+ type: 'test'
+ ...
+# Subtest: replacing a bootstrap-minted identity is reported, never silent (LLP 0061 D4)
+ok 1782 - replacing a bootstrap-minted identity is reported, never silent (LLP 0061 D4)
+ ---
+ duration_ms: 3.195697
+ type: 'test'
+ ...
+# Subtest: a seed write failure reports signed-in-but-not-seeded, not a login failure
+ok 1783 - a seed write failure reports signed-in-but-not-seeded, not a login failure
+ ---
+ duration_ms: 6.028793
+ type: 'test'
+ ...
+# Subtest: the host label defaults to the machine hostname and --host overrides it (LLP 0061 D6)
+ok 1784 - the host label defaults to the machine hostname and --host overrides it (LLP 0061 D6)
+ ---
+ duration_ms: 6.441992
+ type: 'test'
+ ...
+# Subtest: --host as the last arg with no value is a usage error
+ok 1785 - --host as the last arg with no value is a usage error
+ ---
+ duration_ms: 0.563196
+ type: 'test'
+ ...
+# Subtest: a callback error maps to a clear org-selection message
+ok 1786 - a callback error maps to a clear org-selection message
+ ---
+ duration_ms: 1.326556
+ type: 'test'
+ ...
+# Subtest: a browser login timeout points at the headless escape hatches
+ok 1787 - a browser login timeout points at the headless escape hatches
+ ---
+ duration_ms: 1.268748
+ type: 'test'
+ ...
+# Subtest: a server callback error does not append the headless hint (it is already actionable)
+ok 1788 - a server callback error does not append the headless hint (it is already actionable)
+ ---
+ duration_ms: 4.175647
+ type: 'test'
+ ...
+# Subtest: no_membership maps to its own message
+ok 1789 - no_membership maps to its own message
+ ---
+ duration_ms: 1.68471
+ type: 'test'
+ ...
+# Subtest: browser mode on an unconfigured target refuses before any flow
+ok 1790 - browser mode on an unconfigured target refuses before any flow
+ ---
+ duration_ms: 0.897704
+ type: 'test'
+ ...
+# Subtest: the static --token-file path is unchanged (stores kind: static)
+ok 1791 - the static --token-file path is unchanged (stores kind: static)
+ ---
+ duration_ms: 2.924695
+ type: 'test'
+ ...
+# Subtest: a static login write failure keeps the friendly hyp remote login: message
+ok 1792 - a static login write failure keeps the friendly hyp remote login: message
+ ---
+ duration_ms: 1.390364
+ type: 'test'
+ ...
+# Subtest: a remove whose token removal fails reports the partial state, not a raw throw
+ok 1793 - a remove whose token removal fails reports the partial state, not a raw throw
+ ---
+ duration_ms: 1.887689
+ type: 'test'
+ ...
+# Subtest: a piped stdin token still takes the static path
+ok 1794 - a piped stdin token still takes the static path
+ ---
+ duration_ms: 2.854299
+ type: 'test'
+ ...
+# Subtest: an empty piped stdin (no token) points at --browser instead of just "empty token"
+ok 1795 - an empty piped stdin (no token) points at --browser instead of just "empty token"
+ ---
+ duration_ms: 0.815019
+ type: 'test'
+ ...
+# Subtest: --no-browser takes the browser flow even with a piped token (the flag wins)
+ok 1796 - --no-browser takes the browser flow even with a piped token (the flag wins)
+ ---
+ duration_ms: 2.94711
+ type: 'test'
+ ...
+# Subtest: --browser overrides a piped stdin token and takes the browser flow
+ok 1797 - --browser overrides a piped stdin token and takes the browser flow
+ ---
+ duration_ms: 2.411345
+ type: 'test'
+ ...
+# Subtest: a missing target name resolves the default (built-in) target; a value flag is not misread as the name
+ok 1798 - a missing target name resolves the default (built-in) target; a value flag is not misread as the name
+ ---
+ duration_ms: 9.045568
+ type: 'test'
+ ...
+# Subtest: --org as the last arg with no value is a usage error
+ok 1799 - --org as the last arg with no value is a usage error
+ ---
+ duration_ms: 0.842481
+ type: 'test'
+ ...
+# Subtest: --org is noted as ignored when a static token forces the static path
+ok 1800 - --org is noted as ignored when a static token forces the static path
+ ---
+ duration_ms: 2.732383
+ type: 'test'
+ ...
+# Subtest: --org=acme (equals form) is honored, not silently dropped
+ok 1801 - --org=acme (equals form) is honored, not silently dropped
+ ---
+ duration_ms: 2.527241
+ type: 'test'
+ ...
+# Subtest: --token-file=path (equals form) takes the static path, not the browser flow
+ok 1802 - --token-file=path (equals form) takes the static path, not the browser flow
+ ---
+ duration_ms: 7.246754
+ type: 'test'
+ ...
+# Subtest: --org= (equals form, empty value) is a usage error
+ok 1803 - --org= (equals form, empty value) is a usage error
+ ---
+ duration_ms: 0.890183
+ type: 'test'
+ ...
+# Subtest: a --no-daemon login prints the durable hint and provisions the sink (LLP 0102)
+ok 1804 - a --no-daemon login prints the durable hint and provisions the sink (LLP 0102)
+ ---
+ duration_ms: 19.357862
+ type: 'test'
+ ...
+# Subtest: a fresh enroll prints the durable hint and never polls a capture wait (LLP 0102)
+ok 1805 - a fresh enroll prints the durable hint and never polls a capture wait (LLP 0102)
+ ---
+ duration_ms: 3.607594
+ type: 'test'
+ ...
+# Subtest: a failed daemon install still prints the durable hint before returning (LLP 0102)
+ok 1806 - a failed daemon install still prints the durable hint before returning (LLP 0102)
+ ---
+ duration_ms: 3.876582
+ type: 'test'
+ ...
+# Subtest: a re-login (already-enrolled, re-seed path) prints the durable hint (LLP 0102)
+ok 1807 - a re-login (already-enrolled, re-seed path) prints the durable hint (LLP 0102)
+ ---
+ duration_ms: 8.089305
+ type: 'test'
+ ...
+# Subtest: a fresh enroll writes the first-sync hold BEFORE enrollCentralSink, with a future deadline (LLP 0101)
+ok 1808 - a fresh enroll writes the first-sync hold BEFORE enrollCentralSink, with a future deadline (LLP 0101)
+ ---
+ duration_ms: 5.442813
+ type: 'test'
+ ...
+# Subtest: a fresh enroll on a TTY prints the deadline message on stderr (LLP 0100 R1)
+ok 1809 - a fresh enroll on a TTY prints the deadline message on stderr (LLP 0100 R1)
+ ---
+ duration_ms: 4.362581
+ type: 'test'
+ ...
+# Subtest: a fresh enroll on non-TTY stdin prints the same deadline message on stderr (LLP 0100 R1)
+ok 1810 - a fresh enroll on non-TTY stdin prints the same deadline message on stderr (LLP 0100 R1)
+ ---
+ duration_ms: 4.253825
+ type: 'test'
+ ...
+# Subtest: --no-daemon still prints the deadline message: the hold is already committed regardless of the daemon install
+ok 1811 - --no-daemon still prints the deadline message: the hold is already committed regardless of the daemon install
+ ---
+ duration_ms: 9.765773
+ type: 'test'
+ ...
+# Subtest: an enrolling login names the server and prints no URL: terminals autolink one, and the server root is not browsable (\#391)
+ok 1812 - an enrolling login names the server and prints no URL: terminals autolink one, and the server root is not browsable (\#391)
+ ---
+ duration_ms: 8.110447
+ type: 'test'
+ ...
+# Subtest: a bare 'hyp remote login' names the default target it resolved, and that name is still recoverable
+ok 1813 - a bare 'hyp remote login' names the default target it resolved, and that name is still recoverable
+ ---
+ duration_ms: 5.04608
+ type: 'test'
+ ...
+# Subtest: a re-login (already-enrolled) prints no deadline message: there is no first sync to defer
+ok 1814 - a re-login (already-enrolled) prints no deadline message: there is no first sync to defer
+ ---
+ duration_ms: 4.781588
+ type: 'test'
+ ...
+# Subtest: a fresh enroll whose enrollment throws still holds (the marker landed pre-enroll and is never cleared) (LLP 0101)
+ok 1815 - a fresh enroll whose enrollment throws still holds (the marker landed pre-enroll and is never cleared) (LLP 0101)
+ ---
+ duration_ms: 2.594324
+ type: 'test'
+ ...
+# Subtest: a re-login (already-enrolled, re-seed path) writes no first-sync hold (LLP 0101 \#which)
+ok 1816 - a re-login (already-enrolled, re-seed path) writes no first-sync hold (LLP 0101 \#which)
+ ---
+ duration_ms: 2.550056
+ type: 'test'
+ ...
+# Subtest: a query-only login (no gateway credential minted) writes no first-sync hold
+ok 1817 - a query-only login (no gateway credential minted) writes no first-sync hold
+ ---
+ duration_ms: 4.985697
+ type: 'test'
+ ...
+# Subtest: --no-forward writes no first-sync hold (declines enrollment entirely)
+ok 1818 - --no-forward writes no first-sync hold (declines enrollment entirely)
+ ---
+ duration_ms: 13.965096
+ type: 'test'
+ ...
+# Subtest: a matching state resolves { code } and closes the listener
+ok 1819 - a matching state resolves { code } and closes the listener
+ ---
+ duration_ms: 29.009441
+ type: 'test'
+ ...
+# Subtest: a mismatched state is ignored, not consumed: the flow keeps waiting
+ok 1820 - a mismatched state is ignored, not consumed: the flow keeps waiting
+ ---
+ duration_ms: 8.436052
+ type: 'test'
+ ...
+# Subtest: an error= callback rejects with the error code attached
+ok 1821 - an error= callback rejects with the error code attached
+ ---
+ duration_ms: 4.364383
+ type: 'test'
+ ...
+# Subtest: an admission refusal names the reason and links contact on a managed target
+ok 1822 - an admission refusal names the reason and links contact on a managed target
+ ---
+ duration_ms: 3.11845
+ type: 'test'
+ ...
+# Subtest: a self-hosted target sends the reader to their own admin, not to us
+ok 1823 - a self-hosted target sends the reader to their own admin, not to us
+ ---
+ duration_ms: 6.078709
+ type: 'test'
+ ...
+# Subtest: a contact URL that is not a plain https link is dropped, not rendered
+ok 1824 - a contact URL that is not a plain https link is dropped, not rendered
+ ---
+ duration_ms: 6.508472
+ type: 'test'
+ ...
+# Subtest: a provider denial says so instead of falling through to the generic page
+ok 1825 - a provider denial says so instead of falling through to the generic page
+ ---
+ duration_ms: 5.032749
+ type: 'test'
+ ...
+# Subtest: an unknown error code still gets the generic page
+ok 1826 - an unknown error code still gets the generic page
+ ---
+ duration_ms: 2.427199
+ type: 'test'
+ ...
+# Subtest: an org refusal points at --org rather than enumerating the account orgs
+ok 1827 - an org refusal points at --org rather than enumerating the account orgs
+ ---
+ duration_ms: 8.39555
+ type: 'test'
+ ...
+# Subtest: an error code that names an Object.prototype key still gets the generic page
+ok 1828 - an error code that names an Object.prototype key still gets the generic page
+ ---
+ duration_ms: 7.314638
+ type: 'test'
+ ...
+# Subtest: an error= callback with no state is ignored, not surfaced (anti-DoS)
+ok 1829 - an error= callback with no state is ignored, not surfaced (anti-DoS)
+ ---
+ duration_ms: 3.236309
+ type: 'test'
+ ...
+# Subtest: a timeout rejects
+ok 1830 - a timeout rejects
+ ---
+ duration_ms: 51.145316
+ type: 'test'
+ ...
+# Subtest: close() before a code arrives rejects a pending waitForCode (no hang)
+ok 1831 - close() before a code arrives rejects a pending waitForCode (no hang)
+ ---
+ duration_ms: 0.537437
+ type: 'test'
+ ...
+# Subtest: a malformed request target returns 400 without crashing or settling the flow
+ok 1832 - a malformed request target returns 400 without crashing or settling the flow
+ ---
+ duration_ms: 4.983424
+ type: 'test'
+ ...
+# Subtest: a request to a path other than /callback does not consume the single shot
+ok 1833 - a request to a path other than /callback does not consume the single shot
+ ---
+ duration_ms: 3.126973
+ type: 'test'
+ ...
+# Subtest: deriveMcpEndpoint: derive-from-base and back-compat forms
+ok 1834 - deriveMcpEndpoint: derive-from-base and back-compat forms
+ ---
+ duration_ms: 0.873056
+ type: 'test'
+ ...
+# Subtest: a base-URL target sends the verb MCP call to /v1/mcp
+ok 1835 - a base-URL target sends the verb MCP call to /v1/mcp
+ ---
+ duration_ms: 11.681264
+ type: 'test'
+ ...
+# Subtest: a base-URL target with a trailing slash still lands on /v1/mcp
+ok 1836 - a base-URL target with a trailing slash still lands on /v1/mcp
+ ---
+ duration_ms: 3.584098
+ type: 'test'
+ ...
+# Subtest: a URL that already ends in /v1/mcp is used verbatim (back-compat)
+ok 1837 - a URL that already ends in /v1/mcp is used verbatim (back-compat)
+ ---
+ duration_ms: 7.628534
+ type: 'test'
+ ...
+# Subtest: the stdio proxy forwards a base-URL target to /v1/mcp
+ok 1838 - the stdio proxy forwards a base-URL target to /v1/mcp
+ ---
+ duration_ms: 5.181325
+ type: 'test'
+ ...
+# Subtest: drives PKCE -> loopback -> exchange and returns the session
+ok 1839 - drives PKCE -> loopback -> exchange and returns the session
+ ---
+ duration_ms: 3.707585
+ type: 'test'
+ ...
+# Subtest: the loopback receiver gets a contact URL only for a Hyperparam-run target
+ok 1840 - the loopback receiver gets a contact URL only for a Hyperparam-run target
+ ---
+ duration_ms: 1.524037
+ type: 'test'
+ ...
+# Subtest: --no-browser prints the URL instead of opening it
+ok 1841 - --no-browser prints the URL instead of opening it
+ ---
+ duration_ms: 0.417925
+ type: 'test'
+ ...
+# Subtest: closes the loopback even when the flow rejects
+ok 1842 - closes the loopback even when the flow rejects
+ ---
+ duration_ms: 0.646443
+ type: 'test'
+ ...
+# Subtest: buildStartUrl omits org when not given
+ok 1843 - buildStartUrl omits org when not given
+ ---
+ duration_ms: 0.201376
+ type: 'test'
+ ...
+# Subtest: darwin uses `open`
+ok 1844 - darwin uses `open`
+ ---
+ duration_ms: 1.272084
+ type: 'test'
+ ...
+# Subtest: linux uses `xdg-open`
+ok 1845 - linux uses `xdg-open`
+ ---
+ duration_ms: 0.141365
+ type: 'test'
+ ...
+# Subtest: win32 uses rundll32 so `&` in the URL is not a cmd separator
+ok 1846 - win32 uses rundll32 so `&` in the URL is not a cmd separator
+ ---
+ duration_ms: 0.188456
+ type: 'test'
+ ...
+# Subtest: a synchronous spawn throw returns false (caller prints the URL)
+ok 1847 - a synchronous spawn throw returns false (caller prints the URL)
+ ---
+ duration_ms: 0.153293
+ type: 'test'
+ ...
+# Subtest: an async spawn ENOENT (missing opener) does not crash the process
+ok 1848 - an async spawn ENOENT (missing opener) does not crash the process
+ ---
+ duration_ms: 10.371554
+ type: 'test'
+ ...
+# Subtest: challenge is the base64url SHA-256 of the verifier
+ok 1849 - challenge is the base64url SHA-256 of the verifier
+ ---
+ duration_ms: 1.363322
+ type: 'test'
+ ...
+# Subtest: verifier and challenge are base64url (no +/= padding chars)
+ok 1850 - verifier and challenge are base64url (no +/= padding chars)
+ ---
+ duration_ms: 0.286746
+ type: 'test'
+ ...
+# Subtest: two pairs differ (fresh randomness per flow)
+ok 1851 - two pairs differ (fresh randomness per flow)
+ ---
+ duration_ms: 0.273706
+ type: 'test'
+ ...
+# Subtest: proxy refreshes an oidc session on a live 401 and retries (no longer dies on a stale JWT)
+ok 1852 - proxy refreshes an oidc session on a live 401 and retries (no longer dies on a stale JWT)
+ ---
+ duration_ms: 20.892575
+ type: 'test'
+ ...
+# Subtest: a stale JWT at startup refreshes once (lazily on the first message), not twice
+ok 1853 - a stale JWT at startup refreshes once (lazily on the first message), not twice
+ ---
+ duration_ms: 5.630187
+ type: 'test'
+ ...
+# Subtest: proxy surfaces a failed forced refresh instead of a bare HTTP 401
+ok 1854 - proxy surfaces a failed forced refresh instead of a bare HTTP 401
+ ---
+ duration_ms: 4.951065
+ type: 'test'
+ ...
+# Subtest: proxy gives re-login guidance, not a bare 401, when a clean refresh is still rejected
+ok 1855 - proxy gives re-login guidance, not a bare 401, when a clean refresh is still rejected
+ ---
+ duration_ms: 6.712652
+ type: 'test'
+ ...
+# Subtest: proxy surfaces re-login guidance when the refresh is rejected (invalid_grant)
+ok 1856 - proxy surfaces re-login guidance when the refresh is rejected (invalid_grant)
+ ---
+ duration_ms: 5.343973
+ type: 'test'
+ ...
+# Subtest: a stale stored JWT is refreshed and persisted before the call
+ok 1857 - a stale stored JWT is refreshed and persisted before the call
+ ---
+ duration_ms: 15.458846
+ type: 'test'
+ ...
+# Subtest: a 401 mid-flight triggers exactly one refresh + retry
+ok 1858 - a 401 mid-flight triggers exactly one refresh + retry
+ ---
+ duration_ms: 6.130758
+ type: 'test'
+ ...
+# Subtest: a notification 401 or 403 during handshake triggers exactly one refresh + retry
+ # Subtest: HTTP 401
+ ok 1 - HTTP 401
+ ---
+ duration_ms: 7.248907
+ type: 'test'
+ ...
+ # Subtest: HTTP 403
+ ok 2 - HTTP 403
+ ---
+ duration_ms: 4.8559
+ type: 'test'
+ ...
+ 1..2
+ok 1859 - a notification 401 or 403 during handshake triggers exactly one refresh + retry
+ ---
+ duration_ms: 12.729818
+ type: 'test'
+ ...
+# Subtest: a refresh that fails invalid_grant surfaces the re-login guidance
+ok 1860 - a refresh that fails invalid_grant surfaces the re-login guidance
+ ---
+ duration_ms: 12.035753
+ type: 'test'
+ ...
+# Subtest: a stale JWT whose pre-call refresh fails maps to re-login (not an unhandled throw)
+ok 1861 - a stale JWT whose pre-call refresh fails maps to re-login (not an unhandled throw)
+ ---
+ duration_ms: 4.372676
+ type: 'test'
+ ...
+# Subtest: a static token that 401s is not retried (cannot refresh)
+ok 1862 - a static token that 401s is not retried (cannot refresh)
+ ---
+ duration_ms: 4.31622
+ type: 'test'
+ ...
+# Subtest: an env-override token that 401s does not advise a re-login it cannot fix
+ok 1863 - an env-override token that 401s does not advise a re-login it cannot fix
+ ---
+ duration_ms: 1.068194
+ type: 'test'
+ ...
+# Subtest: an oidc session whose freshly-refreshed JWT is still rejected gets re-login guidance (exit 2)
+ok 1864 - an oidc session whose freshly-refreshed JWT is still rejected gets re-login guidance (exit 2)
+ ---
+ duration_ms: 7.148254
+ type: 'test'
+ ...
+# Subtest: the canonical asset set is complete and lives with the renderer
+ok 1865 - the canonical asset set is complete and lives with the renderer
+ ---
+ duration_ms: 1.193544
+ type: 'test'
+ ...
+# Subtest: the renderer is repo-owned code, not a script in a user working tree
+ok 1866 - the renderer is repo-owned code, not a script in a user working tree
+ ---
+ duration_ms: 0.218182
+ type: 'test'
+ ...
+# Subtest: no bundled skill ships its own copy of the renderer assets
+ok 1867 - no bundled skill ships its own copy of the renderer assets
+ ---
+ duration_ms: 2.698412
+ type: 'test'
+ ...
+# Subtest: deriveReportsEndpoint maps a base URL to /v1/reports
+ok 1868 - deriveReportsEndpoint maps a base URL to /v1/reports
+ ---
+ duration_ms: 1.045339
+ type: 'test'
+ ...
+# Subtest: deriveReportsEndpoint strips a trailing /v1/mcp (the originally-documented form)
+ok 1869 - deriveReportsEndpoint strips a trailing /v1/mcp (the originally-documented form)
+ ---
+ duration_ms: 0.137279
+ type: 'test'
+ ...
+# Subtest: deriveReportsEndpoint returns an unparseable URL unchanged
+ok 1870 - deriveReportsEndpoint returns an unparseable URL unchanged
+ ---
+ duration_ms: 0.125591
+ type: 'test'
+ ...
+# Subtest: publish sends a single .md file with kind/period/title params and the content hash
+ok 1871 - publish sends a single .md file with kind/period/title params and the content hash
+ ---
+ duration_ms: 32.868417
+ type: 'test'
+ ...
+# Subtest: publish reports a 200 dedup hit as already published
+ok 1872 - publish reports a 200 dedup hit as already published
+ ---
+ duration_ms: 1.935232
+ type: 'test'
+ ...
+# Subtest: publish packs a folder as a gzipped ustar bundle
+ok 1873 - publish packs a folder as a gzipped ustar bundle
+ ---
+ duration_ms: 9.5006
+ type: 'test'
+ ...
+# Subtest: publish rejects a folder without an entry document before any upload
+ok 1874 - publish rejects a folder without an entry document before any upload
+ ---
+ duration_ms: 1.998518
+ type: 'test'
+ ...
+# Subtest: publish rejects an invalid kind before any network call
+ok 1875 - publish rejects an invalid kind before any network call
+ ---
+ duration_ms: 1.196628
+ type: 'test'
+ ...
+# Subtest: publish rejects a single file that is neither .html nor .md
+ok 1876 - publish rejects a single file that is neither .html nor .md
+ ---
+ duration_ms: 1.313497
+ type: 'test'
+ ...
+# Subtest: publish surfaces the quota error with its make-room guidance
+ok 1877 - publish surfaces the quota error with its make-room guidance
+ ---
+ duration_ms: 1.754456
+ type: 'test'
+ ...
+# Subtest: publish forwards an explicit --org (the admin-token form)
+ok 1878 - publish forwards an explicit --org (the admin-token form)
+ ---
+ duration_ms: 1.800557
+ type: 'test'
+ ...
+# Subtest: list renders the index newest first and passes filters through
+ok 1879 - list renders the index newest first and passes filters through
+ ---
+ duration_ms: 0.754427
+ type: 'test'
+ ...
+# Subtest: list --json prints the raw records
+ok 1880 - list --json prints the raw records
+ ---
+ duration_ms: 0.894259
+ type: 'test'
+ ...
+# Subtest: list with no reports points at publish
+ok 1881 - list with no reports points at publish
+ ---
+ duration_ms: 0.340788
+ type: 'test'
+ ...
+# Subtest: an unknown remote target is rejected before any network call
+ok 1882 - an unknown remote target is rejected before any network call
+ ---
+ duration_ms: 0.244081
+ type: 'test'
+ ...
+# Subtest: a 401 on an env-override token explains that re-login cannot fix it
+ok 1883 - a 401 on an env-override token explains that re-login cannot fix it
+ ---
+ duration_ms: 0.430584
+ type: 'test'
+ ...
+# Subtest: get fetches the entry document to stdout
+ok 1884 - get fetches the entry document to stdout
+ ---
+ duration_ms: 0.474311
+ type: 'test'
+ ...
+# Subtest: get fetches a named artifact and saves it with --output
+ok 1885 - get fetches a named artifact and saves it with --output
+ ---
+ duration_ms: 2.626392
+ type: 'test'
+ ...
+# Subtest: get reports an unknown report from the server error body
+ok 1886 - get reports an unknown report from the server error body
+ ---
+ duration_ms: 1.441241
+ type: 'test'
+ ...
+# Subtest: delete refuses without --yes when stdin is not a TTY
+ok 1887 - delete refuses without --yes when stdin is not a TTY
+ ---
+ duration_ms: 0.28278
+ type: 'test'
+ ...
+# Subtest: delete with --yes issues the DELETE and confirms
+ok 1888 - delete with --yes issues the DELETE and confirms
+ ---
+ duration_ms: 0.259815
+ type: 'test'
+ ...
+# Subtest: extractStats reads every metric, not just the first
+ok 1889 - extractStats reads every metric, not just the first
+ ---
+ duration_ms: 1.73008
+ type: 'test'
+ ...
+# Subtest: extractStats keeps each judgment exactly
+ok 1890 - extractStats keeps each judgment exactly
+ ---
+ duration_ms: 0.23703
+ type: 'test'
+ ...
+# Subtest: extractStats keeps labels verbatim rather than rewriting them
+ok 1891 - extractStats keeps labels verbatim rather than rewriting them
+ ---
+ duration_ms: 0.146222
+ type: 'test'
+ ...
+# Subtest: extractStats keeps the value markup so a worded unit stays spaced
+ok 1892 - extractStats keeps the value markup so a worded unit stays spaced
+ ---
+ duration_ms: 0.165071
+ type: 'test'
+ ...
+# Subtest: extractStats drops notes
+ok 1893 - extractStats drops notes
+ ---
+ duration_ms: 0.202628
+ type: 'test'
+ ...
+# Subtest: extractStats returns nothing for a report with no metric grid
+ok 1894 - extractStats returns nothing for a report with no metric grid
+ ---
+ duration_ms: 0.138751
+ type: 'test'
+ ...
+# Subtest: extractKicker prefers the eyebrow, then a Source line, then a subtitle
+ok 1895 - extractKicker prefers the eyebrow, then a Source line, then a subtitle
+ ---
+ duration_ms: 0.369732
+ type: 'test'
+ ...
+# Subtest: extractTitle takes the first heading, else the fallback
+ok 1896 - extractTitle takes the first heading, else the fallback
+ ---
+ duration_ms: 0.185712
+ type: 'test'
+ ...
+# Subtest: cards are newest first, with a companion card for ranked changes
+ok 1897 - cards are newest first, with a companion card for ranked changes
+ ---
+ duration_ms: 3.365105
+ type: 'test'
+ ...
+# Subtest: the companion card falls back to the report scope when it has no eyebrow
+ok 1898 - the companion card falls back to the report scope when it has no eyebrow
+ ---
+ duration_ms: 1.404705
+ type: 'test'
+ ...
+# Subtest: renderLandingPage links explicit index.html, never a bare directory
+ok 1899 - renderLandingPage links explicit index.html, never a bare directory
+ ---
+ duration_ms: 1.463704
+ type: 'test'
+ ...
+# Subtest: renderLandingPage links theme.css only when the tree has one
+ok 1900 - renderLandingPage links theme.css only when the tree has one
+ ---
+ duration_ms: 2.307868
+ type: 'test'
+ ...
+# Subtest: renderLandingPage is reproducible across runs
+ok 1901 - renderLandingPage is reproducible across runs
+ ---
+ duration_ms: 1.083226
+ type: 'test'
+ ...
+# Subtest: report titles and labels are HTML-escaped
+ok 1902 - report titles and labels are HTML-escaped
+ ---
+ duration_ms: 0.866947
+ type: 'test'
+ ...
+# Subtest: rewriteHrefs index: own section flattens, losing the slug directory
+ok 1903 - rewriteHrefs index: own section flattens, losing the slug directory
+ ---
+ duration_ms: 0.747687
+ type: 'test'
+ ...
+# Subtest: rewriteHrefs index: own section keeps its fragment
+ok 1904 - rewriteHrefs index: own section keeps its fragment
+ ---
+ duration_ms: 0.118791
+ type: 'test'
+ ...
+# Subtest: rewriteHrefs index: another report's section routes up and back down
+ok 1905 - rewriteHrefs index: another report's section routes up and back down
+ ---
+ duration_ms: 0.07199
+ type: 'test'
+ ...
+# Subtest: rewriteHrefs index: another report's one-pager resolves to its index
+ok 1906 - rewriteHrefs index: another report's one-pager resolves to its index
+ ---
+ duration_ms: 0.061764
+ type: 'test'
+ ...
+# Subtest: rewriteHrefs index: another report's one-pager keeps its fragment
+ok 1907 - rewriteHrefs index: another report's one-pager keeps its fragment
+ ---
+ duration_ms: 0.055174
+ type: 'test'
+ ...
+# Subtest: rewriteHrefs section: back-reference to its own one-pager becomes index.html
+ok 1908 - rewriteHrefs section: back-reference to its own one-pager becomes index.html
+ ---
+ duration_ms: 0.163618
+ type: 'test'
+ ...
+# Subtest: rewriteHrefs section: back-reference keeps its fragment
+ok 1909 - rewriteHrefs section: back-reference keeps its fragment
+ ---
+ duration_ms: 0.088084
+ type: 'test'
+ ...
+# Subtest: rewriteHrefs section: another report's one-pager resolves to its index
+ok 1910 - rewriteHrefs section: another report's one-pager resolves to its index
+ ---
+ duration_ms: 0.103777
+ type: 'test'
+ ...
+# Subtest: rewriteHrefs section: another report's section keeps its own path
+ok 1911 - rewriteHrefs section: another report's section keeps its own path
+ ---
+ duration_ms: 0.33582
+ type: 'test'
+ ...
+# Subtest: rewriteHrefs section: a sibling section in the same report stays flat
+ok 1912 - rewriteHrefs section: a sibling section in the same report stays flat
+ ---
+ duration_ms: 0.337763
+ type: 'test'
+ ...
+# Subtest: rewriteHrefs index: an absolute URL ending in .md is left alone
+ok 1913 - rewriteHrefs index: an absolute URL ending in .md is left alone
+ ---
+ duration_ms: 0.138801
+ type: 'test'
+ ...
+# Subtest: rewriteHrefs section: an absolute URL ending in .md is left alone
+ok 1914 - rewriteHrefs section: an absolute URL ending in .md is left alone
+ ---
+ duration_ms: 0.076206
+ type: 'test'
+ ...
+# Subtest: rewriteHrefs index: data-src is not href, so copy buttons keep their raw .md target
+ok 1915 - rewriteHrefs index: data-src is not href, so copy buttons keep their raw .md target
+ ---
+ duration_ms: 0.066211
+ type: 'test'
+ ...
+# Subtest: rewriteHrefs section: data-src is not href, so copy buttons keep their raw .md target
+ok 1916 - rewriteHrefs section: data-src is not href, so copy buttons keep their raw .md target
+ ---
+ duration_ms: 0.065099
+ type: 'test'
+ ...
+# Subtest: rewriteHrefs index: a non-markdown asset link is untouched
+ok 1917 - rewriteHrefs index: a non-markdown asset link is untouched
+ ---
+ duration_ms: 0.058539
+ type: 'test'
+ ...
+# Subtest: rewriteHrefs rewrites links inside raw-HTML components, not just Markdown links
+ok 1918 - rewriteHrefs rewrites links inside raw-HTML components, not just Markdown links
+ ---
+ duration_ms: 0.092861
+ type: 'test'
+ ...
+# Subtest: rewriteHrefs handles every occurrence on a line, not just the first
+ok 1919 - rewriteHrefs handles every occurrence on a line, not just the first
+ ---
+ duration_ms: 0.0925
+ type: 'test'
+ ...
+# Subtest: rewriteHrefs treats a slug with regex metacharacters literally
+ok 1920 - rewriteHrefs treats a slug with regex metacharacters literally
+ ---
+ duration_ms: 0.125651
+ type: 'test'
+ ...
+# Subtest: docLabel states the slug's date, else the generic wording
+ok 1921 - docLabel states the slug's date, else the generic wording
+ ---
+ duration_ms: 0.904585
+ type: 'test'
+ ...
+# Subtest: pageTitle takes the first heading, trimmed, else the fallback
+ok 1922 - pageTitle takes the first heading, trimmed, else the fallback
+ ---
+ duration_ms: 0.225543
+ type: 'test'
+ ...
+# Subtest: masthead carries the brand, the doc label, and the nav slot
+ok 1923 - masthead carries the brand, the doc label, and the nav slot
+ ---
+ duration_ms: 0.187175
+ type: 'test'
+ ...
+# Subtest: discoverSections lists a report's section files sorted, and nothing without a section dir
+ok 1924 - discoverSections lists a report's section files sorted, and nothing without a section dir
+ ---
+ duration_ms: 1.807257
+ type: 'test'
+ ...
+# Subtest: renderReports builds every report and no stale output
+ok 1925 - renderReports builds every report and no stale output
+ ---
+ duration_ms: 31.843539
+ type: 'test'
+ ...
+# Subtest: no built page keeps a .md href
+ok 1926 - no built page keeps a .md href
+ ---
+ duration_ms: 8.482883
+ type: 'test'
+ ...
+# Subtest: every page carries a copy action, and every report a full.md payload
+ok 1927 - every page carries a copy action, and every report a full.md payload
+ ---
+ duration_ms: 11.473108
+ type: 'test'
+ ...
+# Subtest: one-pagers link back to the landing page, sections back to their report
+ok 1928 - one-pagers link back to the landing page, sections back to their report
+ ---
+ duration_ms: 8.570937
+ type: 'test'
+ ...
+# Subtest: theme.css is created once, never overwritten, and reaches every page
+ok 1929 - theme.css is created once, never overwritten, and reaches every page
+ ---
+ duration_ms: 20.704559
+ type: 'test'
+ ...
+# Subtest: the authoring vocabulary converts to the markup pandoc emitted
+ok 1930 - the authoring vocabulary converts to the markup pandoc emitted
+ ---
+ duration_ms: 4.361028
+ type: 'test'
+ ...
+# Subtest: every in-page anchor resolves to an id on the page
+ok 1931 - every in-page anchor resolves to an id on the page
+ ---
+ duration_ms: 4.456403
+ type: 'test'
+ ...
+# Subtest: heading ids NFC-normalize and keep combining marks, matching pandoc 3.1.11
+ok 1932 - heading ids NFC-normalize and keep combining marks, matching pandoc 3.1.11
+ ---
+ duration_ms: 7.038488
+ type: 'test'
+ ...
+# Subtest: numeric and named HTML entities decode before the slug rule runs, matching pandoc 3.1.11
+ok 1933 - numeric and named HTML entities decode before the slug rule runs, matching pandoc 3.1.11
+ ---
+ duration_ms: 3.971396
+ type: 'test'
+ ...
+# Subtest: an authored ’ heading resolves its own in-page anchor
+ok 1934 - an authored ’ heading resolves its own in-page anchor
+ ---
+ duration_ms: 6.027361
+ type: 'test'
+ ...
+# Subtest: a heading that reduces to nothing emits no id attribute, matching pandoc 3.1.11
+ok 1935 - a heading that reduces to nothing emits no id attribute, matching pandoc 3.1.11
+ ---
+ duration_ms: 4.773195
+ type: 'test'
+ ...
+# Subtest: an out-of-range numeric entity in a heading renders instead of crashing, matching pandoc 3.1.11
+ok 1936 - an out-of-range numeric entity in a heading renders instead of crashing, matching pandoc 3.1.11
+ ---
+ duration_ms: 3.814207
+ type: 'test'
+ ...
+# Subtest: an uppercase hex numeric entity decodes like its lowercase spelling, matching pandoc 3.1.11
+ok 1937 - an uppercase hex numeric entity decodes like its lowercase spelling, matching pandoc 3.1.11
+ ---
+ duration_ms: 3.969113
+ type: 'test'
+ ...
+# Subtest: the HTML5 ASCII-punctuation entity names decode, matching pandoc 3.1.11
+ok 1938 - the HTML5 ASCII-punctuation entity names decode, matching pandoc 3.1.11
+ ---
+ duration_ms: 5.391675
+ type: 'test'
+ ...
+# Subtest: a
in a heading yields a space, matching pandoc 3.1.11
+ok 1939 - a
in a heading yields a space, matching pandoc 3.1.11
+ ---
+ duration_ms: 9.756168
+ type: 'test'
+ ...
+# Subtest: every raw tag whose name starts with `br` yields a space, matching pandoc 3.1.11
+ok 1940 - every raw tag whose name starts with `br` yields a space, matching pandoc 3.1.11
+ ---
+ duration_ms: 5.60598
+ type: 'test'
+ ...
+# Subtest: heading ids do not trim after the punctuation strip, matching pandoc 3.1.11
+ok 1941 - heading ids do not trim after the punctuation strip, matching pandoc 3.1.11
+ ---
+ duration_ms: 6.987099
+ type: 'test'
+ ...
+# Subtest: a malformed entity in one report does not destroy the pages of the reports after it
+ok 1942 - a malformed entity in one report does not destroy the pages of the reports after it
+ ---
+ duration_ms: 18.56748
+ type: 'test'
+ ...
+# Subtest: a loose task list still loses its bullet, matching pandoc's suppressed marker
+ok 1943 - a loose task list still loses its bullet, matching pandoc's suppressed marker
+ ---
+ duration_ms: 4.155326
+ type: 'test'
+ ...
+# Subtest: resolveSingleSourceEnablement resolves the claude descriptor to ai-gateway + claude
+ok 1944 - resolveSingleSourceEnablement resolves the claude descriptor to ai-gateway + claude
+ ---
+ duration_ms: 14.207734
+ type: 'test'
+ ...
+# Subtest: resolveSingleSourceEnablement resolves the openclaw descriptor to ai-gateway + openclaw
+ok 1945 - resolveSingleSourceEnablement resolves the openclaw descriptor to ai-gateway + openclaw
+ ---
+ duration_ms: 23.46823
+ type: 'test'
+ ...
+# Subtest: resolveSingleSourceEnablement resolves a gateway-independent descriptor with just its own plugin
+ok 1946 - resolveSingleSourceEnablement resolves a gateway-independent descriptor with just its own plugin
+ ---
+ duration_ms: 4.819095
+ type: 'test'
+ ...
+# Subtest: resolveSingleSourceEnablement returns empty entries for a descriptor with no compose block
+ok 1947 - resolveSingleSourceEnablement returns empty entries for a descriptor with no compose block
+ ---
+ duration_ms: 0.18421
+ type: 'test'
+ ...
+# Subtest: run-tests forwards node --test flags before discovered test files
+ok 1948 - run-tests forwards node --test flags before discovered test files
+ ---
+ duration_ms: 1.226545
+ type: 'test'
+ ...
+# Subtest: run-tests appends discovered test files when no flags are forwarded
+ok 1949 - run-tests appends discovered test files when no flags are forwarded
+ ---
+ duration_ms: 0.14518
+ type: 'test'
+ ...
+# Subtest: isValidSemver accepts X.Y.Z and rejects junk
+ok 1950 - isValidSemver accepts X.Y.Z and rejects junk
+ ---
+ duration_ms: 0.869932
+ type: 'test'
+ ...
+# Subtest: isValidRange accepts the operators the kernel matcher understands
+ok 1951 - isValidRange accepts the operators the kernel matcher understands
+ ---
+ duration_ms: 0.297151
+ type: 'test'
+ ...
+# Subtest: isValidRange rejects empty and unparseable ranges
+ok 1952 - isValidRange rejects empty and unparseable ranges
+ ---
+ duration_ms: 0.134604
+ type: 'test'
+ ...
+# Subtest: discovery finds the fixtures that install a real service label
+ok 1953 - discovery finds the fixtures that install a real service label
+ ---
+ duration_ms: 0.597679
+ type: 'test'
+ ...
+# Subtest: the attach fixtures never reach a real service manager, even when one is on PATH
+ok 1954 - the attach fixtures never reach a real service manager, even when one is on PATH
+ ---
+ duration_ms: 422.131158
+ type: 'test'
+ ...
+# Subtest: running a fixture directly, with no --test, still reaches no service manager
+ok 1955 - running a fixture directly, with no --test, still reaches no service manager
+ ---
+ duration_ms: 315.466207
+ type: 'test'
+ ...
+# Subtest: running the fixtures without test isolation still reaches no service manager
+ok 1956 - running the fixtures without test isolation still reaches no service manager
+ ---
+ duration_ms: 291.997448
+ type: 'test'
+ ...
+# Subtest: runServiceCommand refuses to spawn under the test runner
+ok 1957 - runServiceCommand refuses to spawn under the test runner
+ ---
+ duration_ms: 0.594384
+ type: 'test'
+ ...
+# Subtest: the explicit opt-in still spawns
+ok 1958 - the explicit opt-in still spawns
+ ---
+ duration_ms: 47.589422
+ type: 'test'
+ ...
+# Subtest: forward sink: ≈0 bytes on a no-new-rows tick, ≈N on an N-new tick
+ok 1959 - forward sink: ≈0 bytes on a no-new-rows tick, ≈N on an N-new tick
+ ---
+ duration_ms: 112.31601
+ type: 'test'
+ ...
+# Subtest: blob sink: no blob on a no-new-rows tick, exactly N on an N-new tick
+ok 1960 - blob sink: no blob on a no-new-rows tick, exactly N on an N-new tick
+ ---
+ duration_ms: 61.342314
+ type: 'test'
+ ...
+# Subtest: forward sink: exactly-once across a retention front-prune
+ok 1961 - forward sink: exactly-once across a retention front-prune
+ ---
+ duration_ms: 62.006904
+ type: 'test'
+ ...
+# Subtest: blob sink: exactly-once across a retention front-prune
+ok 1962 - blob sink: exactly-once across a retention front-prune
+ ---
+ duration_ms: 50.988427
+ type: 'test'
+ ...
+# Subtest: forward sink: exactly-once across a compaction generation swap
+ok 1963 - forward sink: exactly-once across a compaction generation swap
+ ---
+ duration_ms: 40.316246
+ type: 'test'
+ ...
+# Subtest: blob sink: exactly-once across a compaction generation swap
+ok 1964 - blob sink: exactly-once across a compaction generation swap
+ ---
+ duration_ms: 57.430068
+ type: 'test'
+ ...
+# Subtest: forward sink: watermark composes with the driver-outbox respool (suffix-only replay)
+ok 1965 - forward sink: watermark composes with the driver-outbox respool (suffix-only replay)
+ ---
+ duration_ms: 49.47073
+ type: 'test'
+ ...
+# Subtest: blob sink: a lost watermark after a durable PUT re-PUTs the same object key (idempotent overwrite)
+ok 1966 - blob sink: a lost watermark after a durable PUT re-PUTs the same object key (idempotent overwrite)
+ ---
+ duration_ms: 32.894897
+ type: 'test'
+ ...
+# Subtest: forward sink: a pure-legacy partition re-exports the null-seq backlog exactly once
+ok 1967 - forward sink: a pure-legacy partition re-exports the null-seq backlog exactly once
+ ---
+ duration_ms: 17.937973
+ type: 'test'
+ ...
+# Subtest: forward sink: a mixed legacy+real partition ships everything once, then steady-state
+ok 1968 - forward sink: a mixed legacy+real partition ships everything once, then steady-state
+ ---
+ duration_ms: 17.576775
+ type: 'test'
+ ...
+# Subtest: blob sink: a pure-legacy partition writes one blob, then no blob
+ok 1969 - blob sink: a pure-legacy partition writes one blob, then no blob
+ ---
+ duration_ms: 15.379275
+ type: 'test'
+ ...
+# Subtest: blob sink: a mixed legacy+real partition writes one blob, then no blob
+ok 1970 - blob sink: a mixed legacy+real partition writes one blob, then no blob
+ ---
+ duration_ms: 10.583856
+ type: 'test'
+ ...
+# Subtest: forward sink: two instances on one partition keep independent watermarks (no cross-instance skip)
+ok 1971 - forward sink: two instances on one partition keep independent watermarks (no cross-instance skip)
+ ---
+ duration_ms: 30.467507
+ type: 'test'
+ ...
+# Subtest: withSeqRangeFilename inserts the range before the final extension
+ok 1972 - withSeqRangeFilename inserts the range before the final extension
+ ---
+ duration_ms: 0.836281
+ type: 'test'
+ ...
+# Subtest: withSeqRangeFilename is deterministic and preserves dots in the base name
+ok 1973 - withSeqRangeFilename is deterministic and preserves dots in the base name
+ ---
+ duration_ms: 0.134304
+ type: 'test'
+ ...
+# Subtest: openIncrementalRows reports empty for a missing table (no blob written)
+ok 1974 - openIncrementalRows reports empty for a missing table (no blob written)
+ ---
+ duration_ms: 0.227475
+ type: 'test'
+ ...
+# Subtest: openIncrementalRows reports empty for a partition with no tablePath
+ok 1975 - openIncrementalRows reports empty for a partition with no tablePath
+ ---
+ duration_ms: 0.939638
+ type: 'test'
+ ...
+# Subtest: openIncrementalRows tracks rowCount and the high-water lastAfter as the encoder drains
+ok 1976 - openIncrementalRows tracks rowCount and the high-water lastAfter as the encoder drains
+ ---
+ duration_ms: 1.056886
+ type: 'test'
+ ...
+# Subtest: openIncrementalRows honours the since filter (only seq > since)
+ok 1977 - openIncrementalRows honours the since filter (only seq > since)
+ ---
+ duration_ms: 0.2109
+ type: 'test'
+ ...
+# Subtest: openIncrementalRows reports empty when since already covers every row
+ok 1978 - openIncrementalRows reports empty when since already covers every row
+ ---
+ duration_ms: 0.228197
+ type: 'test'
+ ...
+# Subtest: openIncrementalRows: null-seq legacy rows are emitted but never advance lastAfter
+ok 1979 - openIncrementalRows: null-seq legacy rows are emitted but never advance lastAfter
+ ---
+ duration_ms: 0.198993
+ type: 'test'
+ ...
+# Subtest: watermarkKeyFor returns null without a tablePath and the logical key otherwise
+ok 1980 - watermarkKeyFor returns null without a tablePath and the logical key otherwise
+ ---
+ duration_ms: 0.42093
+ type: 'test'
+ ...
+# Subtest: createInstanceWatermarkStore isolates instances under one plugin stateDir
+ok 1981 - createInstanceWatermarkStore isolates instances under one plugin stateDir
+ ---
+ duration_ms: 29.498725
+ type: 'test'
+ ...
+# Subtest: createInstanceWatermarkStore requires stateDir and instanceName
+ok 1982 - createInstanceWatermarkStore requires stateDir and instanceName
+ ---
+ duration_ms: 0.52006
+ type: 'test'
+ ...
+# Subtest: sink maintain without --compact never rewrites and exits 0
+ok 1983 - sink maintain without --compact never rewrites and exits 0
+ ---
+ duration_ms: 32.052687
+ type: 'test'
+ ...
+# Subtest: sink maintain --compact rewrites past the configured threshold and exits 0
+ok 1984 - sink maintain --compact rewrites past the configured threshold and exits 0
+ ---
+ duration_ms: 35.57455
+ type: 'test'
+ ...
+# Subtest: sink maintain --compact reports a commit conflict and exits 0
+ok 1985 - sink maintain --compact reports a commit conflict and exits 0
+ ---
+ duration_ms: 12.92185
+ type: 'test'
+ ...
+# Subtest: sink maintain --compact exits 1 when the rewrite fails
+ok 1986 - sink maintain --compact exits 1 when the rewrite fails
+ ---
+ duration_ms: 10.051998
+ type: 'test'
+ ...
+# Subtest: sink maintain rejects unknown flags with exit 2
+ok 1987 - sink maintain rejects unknown flags with exit 2
+ ---
+ duration_ms: 0.906948
+ type: 'test'
+ ...
+# Subtest: fromProvider returns the capability value from the specified provider
+ok 1988 - fromProvider returns the capability value from the specified provider
+ ---
+ duration_ms: 2.321949
+ type: 'test'
+ ...
+# Subtest: fromProvider returns undefined when the provider has not registered the capability
+ok 1989 - fromProvider returns undefined when the provider has not registered the capability
+ ---
+ duration_ms: 0.291924
+ type: 'test'
+ ...
+# Subtest: fromProvider respects semver range
+ok 1990 - fromProvider respects semver range
+ ---
+ duration_ms: 0.330402
+ type: 'test'
+ ...
+# Subtest: materializeSinks returns empty when config has no sinks
+ok 1991 - materializeSinks returns empty when config has no sinks
+ ---
+ duration_ms: 0.891895
+ type: 'test'
+ ...
+# Subtest: materializeSinks returns empty when config is null
+ok 1992 - materializeSinks returns empty when config is null
+ ---
+ duration_ms: 0.399898
+ type: 'test'
+ ...
+# Subtest: materializeSinks materializes a request sink from a plugin with one contribution
+ok 1993 - materializeSinks materializes a request sink from a plugin with one contribution
+ ---
+ duration_ms: 2.043526
+ type: 'test'
+ ...
+# Subtest: materializeSinks errors when request sink plugin is not active
+ok 1994 - materializeSinks errors when request sink plugin is not active
+ ---
+ duration_ms: 0.567903
+ type: 'test'
+ ...
+# Subtest: materializeSinks errors when request sink plugin has no contributions
+ok 1995 - materializeSinks errors when request sink plugin has no contributions
+ ---
+ duration_ms: 0.409593
+ type: 'test'
+ ...
+# Subtest: materializeSinks errors when request sink plugin has multiple contributions
+ok 1996 - materializeSinks errors when request sink plugin has multiple contributions
+ ---
+ duration_ms: 0.808028
+ type: 'test'
+ ...
+# Subtest: materializeSinks materializes a blob sink (encoder writer + destination)
+ok 1997 - materializeSinks materializes a blob sink (encoder writer + destination)
+ ---
+ duration_ms: 1.080542
+ type: 'test'
+ ...
+# Subtest: materializeSinks materializes a table-format sink
+ok 1998 - materializeSinks materializes a table-format sink
+ ---
+ duration_ms: 1.087423
+ type: 'test'
+ ...
+# Subtest: materializeSinks table-format sink uses config.encoder pin
+ok 1999 - materializeSinks table-format sink uses config.encoder pin
+ ---
+ duration_ms: 0.753705
+ type: 'test'
+ ...
+# Subtest: materializeSinks errors when writer plugin is not active
+ok 2000 - materializeSinks errors when writer plugin is not active
+ ---
+ duration_ms: 0.511998
+ type: 'test'
+ ...
+# Subtest: materializeSinks errors when destination plugin is not active
+ok 2001 - materializeSinks errors when destination plugin is not active
+ ---
+ duration_ms: 0.408831
+ type: 'test'
+ ...
+# Subtest: materializeSinks errors when writer provides neither encoder nor table-format
+ok 2002 - materializeSinks errors when writer provides neither encoder nor table-format
+ ---
+ duration_ms: 0.289139
+ type: 'test'
+ ...
+# Subtest: materializeSinks errors when table-format destination has no blob-store
+ok 2003 - materializeSinks errors when table-format destination has no blob-store
+ ---
+ duration_ms: 0.360608
+ type: 'test'
+ ...
+# Subtest: materializeSinks errors when table-format encoder pin is not active
+ok 2004 - materializeSinks errors when table-format encoder pin is not active
+ ---
+ duration_ms: 1.609085
+ type: 'test'
+ ...
+# Subtest: materializeSinks continues past failures and reports all errors
+ok 2005 - materializeSinks continues past failures and reports all errors
+ ---
+ duration_ms: 0.314338
+ type: 'test'
+ ...
+# Subtest: materializeSinks destination contribution missing for blob sink
+ok 2006 - materializeSinks destination contribution missing for blob sink
+ ---
+ duration_ms: 0.290521
+ type: 'test'
+ ...
+# Subtest: readRows back-compat: no opts is unchanged, internal fields never leak
+ok 2007 - readRows back-compat: no opts is unchanged, internal fields never leak
+ ---
+ duration_ms: 49.591514
+ type: 'test'
+ ...
+# Subtest: readRowsSince pairs each row with a monotonic after token and strips the seq
+ok 2008 - readRowsSince pairs each row with a monotonic after token and strips the seq
+ ---
+ duration_ms: 68.668901
+ type: 'test'
+ ...
+# Subtest: null-seq (legacy) rows are always treated as new and never skipped
+ok 2009 - null-seq (legacy) rows are always treated as new and never skipped
+ ---
+ duration_ms: 16.319214
+ type: 'test'
+ ...
+# Subtest: a table with no seq column at all yields everything (pure legacy)
+ok 2010 - a table with no seq column at all yields everything (pure legacy)
+ ---
+ duration_ms: 10.746614
+ type: 'test'
+ ...
+# Subtest: an invalid continuation token is rejected
+ok 2011 - an invalid continuation token is rejected
+ ---
+ duration_ms: 21.328998
+ type: 'test'
+ ...
+# Subtest: deriveWatermarkKey splits dataset from the partition path
+ok 2012 - deriveWatermarkKey splits dataset from the partition path
+ ---
+ duration_ms: 1.536766
+ type: 'test'
+ ...
+# Subtest: deriveWatermarkKey is keyed by the LOGICAL path, independent of tableDir
+ok 2013 - deriveWatermarkKey is keyed by the LOGICAL path, independent of tableDir
+ ---
+ duration_ms: 0.42751
+ type: 'test'
+ ...
+# Subtest: deriveWatermarkKey preserves nested partition segments
+ok 2014 - deriveWatermarkKey preserves nested partition segments
+ ---
+ duration_ms: 0.233115
+ type: 'test'
+ ...
+# Subtest: deriveWatermarkKey sanitizes unsafe segment characters
+ok 2015 - deriveWatermarkKey sanitizes unsafe segment characters
+ ---
+ duration_ms: 0.145742
+ type: 'test'
+ ...
+# Subtest: deriveWatermarkKey falls back to a sentinel when no partition segment
+ok 2016 - deriveWatermarkKey falls back to a sentinel when no partition segment
+ ---
+ duration_ms: 0.213094
+ type: 'test'
+ ...
+# Subtest: deriveWatermarkKey rejects paths outside the datasets root
+ok 2017 - deriveWatermarkKey rejects paths outside the datasets root
+ ---
+ duration_ms: 2.650088
+ type: 'test'
+ ...
+# Subtest: read returns null when no watermark has been written
+ok 2018 - read returns null when no watermark has been written
+ ---
+ duration_ms: 6.176918
+ type: 'test'
+ ...
+# Subtest: write then read round-trips the continuation and row count
+ok 2019 - write then read round-trips the continuation and row count
+ ---
+ duration_ms: 4.726164
+ type: 'test'
+ ...
+# Subtest: write advances the watermark in place (latest wins)
+ok 2020 - write advances the watermark in place (latest wins)
+ ---
+ duration_ms: 6.280406
+ type: 'test'
+ ...
+# Subtest: write is atomic write-rename and leaves no temp files
+ok 2021 - write is atomic write-rename and leaves no temp files
+ ---
+ duration_ms: 1.450565
+ type: 'test'
+ ...
+# Subtest: write rejects a malformed continuation before touching disk
+ok 2022 - write rejects a malformed continuation before touching disk
+ ---
+ duration_ms: 1.074974
+ type: 'test'
+ ...
+# Subtest: read returns null on a corrupt watermark file (safe re-export, never silent skip)
+ok 2023 - read returns null on a corrupt watermark file (safe re-export, never silent skip)
+ ---
+ duration_ms: 4.623397
+ type: 'test'
+ ...
+# Subtest: keyFor matches deriveWatermarkKey
+ok 2024 - keyFor matches deriveWatermarkKey
+ ---
+ duration_ms: 0.244251
+ type: 'test'
+ ...
+# Subtest: createSinkWatermarkStore requires a stateDir
+ok 2025 - createSinkWatermarkStore requires a stateDir
+ ---
+ duration_ms: 0.113442
+ type: 'test'
+ ...
+# Subtest: instantiate table-format sink wires blobStore + encoder + config into createSink
+ok 2026 - instantiate table-format sink wires blobStore + encoder + config into createSink
+ ---
+ duration_ms: 2.765564
+ type: 'test'
+ ...
+# Subtest: instantiate table-format sink intersects supports tags between provider and encoder
+ok 2027 - instantiate table-format sink intersects supports tags between provider and encoder
+ ---
+ duration_ms: 0.443024
+ type: 'test'
+ ...
+# Subtest: instantiate table-format sink rejects missing blobStore / encoder / provider
+ok 2028 - instantiate table-format sink rejects missing blobStore / encoder / provider
+ ---
+ duration_ms: 0.601755
+ type: 'test'
+ ...
+# Subtest: validateConfig accepts table-format writer + blob destination
+ok 2029 - validateConfig accepts table-format writer + blob destination
+ ---
+ duration_ms: 0.93417
+ type: 'test'
+ ...
+# Subtest: validateConfig rejects table-format writer without blob-store destination
+ok 2030 - validateConfig rejects table-format writer without blob-store destination
+ ---
+ duration_ms: 0.424566
+ type: 'test'
+ ...
+# Subtest: validateConfig rejects writer providing neither encoder nor table-format
+ok 2031 - validateConfig rejects writer providing neither encoder nor table-format
+ ---
+ duration_ms: 0.520311
+ type: 'test'
+ ...
+# Subtest: validateConfig rejects table-format sink with unknown inner encoder pin
+ok 2032 - validateConfig rejects table-format sink with unknown inner encoder pin
+ ---
+ duration_ms: 0.26291
+ type: 'test'
+ ...
+# Subtest: validateConfig rejects table-format sink whose encoder pin does not provide hypaware.encoder
+ok 2033 - validateConfig rejects table-format sink whose encoder pin does not provide hypaware.encoder
+ ---
+ duration_ms: 0.248698
+ type: 'test'
+ ...
+# Subtest: first-party metadata still routes encoder writers through the legacy sink_pair_incompatible code
+ok 2034 - first-party metadata still routes encoder writers through the legacy sink_pair_incompatible code
+ ---
+ duration_ms: 0.476073
+ type: 'test'
+ ...
+# Subtest: no central layer -> undefined (solo machines have nothing to withhold from)
+ok 2035 - no central layer -> undefined (solo machines have nothing to withhold from)
+ ---
+ duration_ms: 8.368169
+ type: 'test'
+ ...
+# Subtest: enrolled machine with an empty (or absent) store withholds nothing: default-sync
+ok 2036 - enrolled machine with an empty (or absent) store withholds nothing: default-sync
+ ---
+ duration_ms: 0.942573
+ type: 'test'
+ ...
+# Subtest: opt-out entries feed the withheld set
+ok 2037 - opt-out entries feed the withheld set
+ ---
+ duration_ms: 4.441301
+ type: 'test'
+ ...
+# Subtest: a central-classified source cannot be withheld: a stale entry is inert (LLP 0188 \#locked)
+ok 2038 - a central-classified source cannot be withheld: a stale entry is inert (LLP 0188 \#locked)
+ ---
+ duration_ms: 1.383403
+ type: 'test'
+ ...
+# Subtest: the store is re-read after the TTL, so an opt-out lands in a running daemon
+ok 2039 - the store is re-read after the TTL, so an opt-out lands in a running daemon
+ ---
+ duration_ms: 1.960149
+ type: 'test'
+ ...
+# Subtest: a corrupt store throws ClientSyncListUnreadableError from shouldWithhold (fail closed)
+ok 2040 - a corrupt store throws ClientSyncListUnreadableError from shouldWithhold (fail closed)
+ ---
+ duration_ms: 1.213033
+ type: 'test'
+ ...
+# Subtest: dataset-scoped withholding: an unattributed dataset drops wholesale only when every owning source is opted out
+ok 2041 - dataset-scoped withholding: an unattributed dataset drops wholesale only when every owning source is opted out
+ ---
+ duration_ms: 1.233484
+ type: 'test'
+ ...
+# Subtest: fail-closed: any opted-out owner of an attributed dataset withholds its unattributed rows
+ok 2042 - fail-closed: any opted-out owner of an attributed dataset withholds its unattributed rows
+ ---
+ duration_ms: 1.045729
+ type: 'test'
+ ...
+# Subtest: fail-closed arming set matches the real bundled manifests: the raw rows own ai_gateway_messages
+ok 2043 - fail-closed arming set matches the real bundled manifests: the raw rows own ai_gateway_messages
+ ---
+ duration_ms: 16.363431
+ type: 'test'
+ ...
+# Subtest: fail-closed: inert with nothing opted out, and inert for an opt-out on a non-owner
+ok 2044 - fail-closed: inert with nothing opted out, and inert for an opt-out on a non-owner
+ ---
+ duration_ms: 2.100011
+ type: 'test'
+ ...
+# Subtest: datasetAttributionColumnsFromCatalog folds declared attribution columns, first writer wins
+ok 2045 - datasetAttributionColumnsFromCatalog folds declared attribution columns, first writer wins
+ ---
+ duration_ms: 0.306386
+ type: 'test'
+ ...
+# Subtest: datasetOwnedSourceIdsFromCatalog maps each dataset to its owning picker ids
+ok 2046 - datasetOwnedSourceIdsFromCatalog maps each dataset to its owning picker ids
+ ---
+ duration_ms: 0.122847
+ type: 'test'
+ ...
+# Subtest: datasetOwnedSourceIdsFromCatalog unions owners across every plugin contributing a dataset
+ok 2047 - datasetOwnedSourceIdsFromCatalog unions owners across every plugin contributing a dataset
+ ---
+ duration_ms: 0.156538
+ type: 'test'
+ ...
+# Subtest: a shared dataset is not withheld wholesale while a locked co-owner still syncs
+ok 2048 - a shared dataset is not withheld wholesale while a locked co-owner still syncs
+ ---
+ duration_ms: 2.23048
+ type: 'test'
+ ...
+# Subtest: migration: central layer + absent store materializes the local-classified set as opt-outs
+ok 2049 - migration: central layer + absent store materializes the local-classified set as opt-outs
+ ---
+ duration_ms: 1.915101
+ type: 'test'
+ ...
+# Subtest: migration: an existing store (even empty) is the new-era marker, no-op
+ok 2050 - migration: an existing store (even empty) is the new-era marker, no-op
+ ---
+ duration_ms: 1.31612
+ type: 'test'
+ ...
+# Subtest: migration: no central layer, no-op (solo machine never migrates)
+ok 2051 - migration: no central layer, no-op (solo machine never migrates)
+ ---
+ duration_ms: 5.113853
+ type: 'test'
+ ...
+# Subtest: migration: a corrupt store is left untouched (never overwrite a privacy signal)
+ok 2052 - migration: a corrupt store is left untouched (never overwrite a privacy signal)
+ ---
+ duration_ms: 10.050135
+ type: 'test'
+ ...
+# Subtest: migration is idempotent: a second boot after materialization changes nothing
+ok 2053 - migration is idempotent: a second boot after materialization changes nothing
+ ---
+ duration_ms: 5.188546
+ type: 'test'
+ ...
+# Subtest: readRowsSince: rows attributed to a withheld source are dropped from the payload but the cursor advances across them
+ok 2054 - readRowsSince: rows attributed to a withheld source are dropped from the payload but the cursor advances across them
+ ---
+ duration_ms: 96.296481
+ type: 'test'
+ ...
+# Subtest: readRowsSince: a dataset with no declared attribution_column is never subject to source-scoped withholding
+ok 2055 - readRowsSince: a dataset with no declared attribution_column is never subject to source-scoped withholding
+ ---
+ duration_ms: 42.838441
+ type: 'test'
+ ...
+# Subtest: readRowsSince: with no sourceWithholdResolver configured, nothing is ever withheld on attribution
+ok 2056 - readRowsSince: with no sourceWithholdResolver configured, nothing is ever withheld on attribution
+ ---
+ duration_ms: 21.999136
+ type: 'test'
+ ...
+# Subtest: readRowsSince: a `columns` projection omitting the attribution column still withholds, and shipped rows come back without it
+ok 2057 - readRowsSince: a `columns` projection omitting the attribution column still withholds, and shipped rows come back without it
+ ---
+ duration_ms: 22.060269
+ type: 'test'
+ ...
+# Subtest: readRowsSince: a dataset with no attribution column whose every owning source is withheld is dropped wholesale, cursor still advancing
+ok 2058 - readRowsSince: a dataset with no attribution column whose every owning source is withheld is dropped wholesale, cursor still advancing
+ ---
+ duration_ms: 21.068131
+ type: 'test'
+ ...
+# Subtest: readRowsSince: unattributed rows in an attributed dataset are withheld once any owning source is opted out (fail closed)
+ok 2059 - readRowsSince: unattributed rows in an attributed dataset are withheld once any owning source is opted out (fail closed)
+ ---
+ duration_ms: 39.325842
+ type: 'test'
+ ...
+# Subtest: readRowsSince: with no opt-out standing, unattributed rows still ship (the fail-closed rule is inert)
+ok 2060 - readRowsSince: with no opt-out standing, unattributed rows still ship (the fail-closed rule is inert)
+ ---
+ duration_ms: 13.3824
+ type: 'test'
+ ...
+# Subtest: readRowsSince: the provider form of withheldSourceIds is consulted live, so an opt-out lands mid-run without a rebuild
+ok 2061 - readRowsSince: the provider form of withheldSourceIds is consulted live, so an opt-out lands mid-run without a rebuild
+ ---
+ duration_ms: 20.656576
+ type: 'test'
+ ...
+# Subtest: readRowsSince: cwd-based and source-scoped withholding compose independently
+ok 2062 - readRowsSince: cwd-based and source-scoped withholding compose independently
+ ---
+ duration_ms: 23.281396
+ type: 'test'
+ ...
+# Subtest: mixed done/failed/pending/n-a reads cleanly off the marker store + config
+ok 2063 - mixed done/failed/pending/n-a reads cleanly off the marker store + config
+ ---
+ duration_ms: 39.37142
+ type: 'test'
+ ...
+# Subtest: a malformed on_join block renders n/a (not pending) on a joined host
+ok 2064 - a malformed on_join block renders n/a (not pending) on a joined host
+ ---
+ duration_ms: 25.90316
+ type: 'test'
+ ...
+# Subtest: a default-on backfill target (enabled client, no explicit block) shows pending on a joined host
+ok 2065 - a default-on backfill target (enabled client, no explicit block) shows pending on a joined host
+ ---
+ duration_ms: 13.218891
+ type: 'test'
+ ...
+# Subtest: a default-on client on a NON-joined host keeps the V1 surface (no spurious action)
+ok 2066 - a default-on client on a NON-joined host keeps the V1 surface (no spurious action)
+ ---
+ duration_ms: 12.797872
+ type: 'test'
+ ...
+# Subtest: a failed backfill does not flip overall to degraded
+ok 2067 - a failed backfill does not flip overall to degraded
+ ---
+ duration_ms: 9.215537
+ type: 'test'
+ ...
+# Subtest: a mixed done/failed/refused marker store renders all three; overall stays healthy (LLP 0186)
+ok 2068 - a mixed done/failed/refused marker store renders all three; overall stays healthy (LLP 0186)
+ ---
+ duration_ms: 18.094712
+ type: 'test'
+ ...
+# Subtest: an ordinary host with no markers reports clientActions null (V1 surface unchanged)
+ok 2069 - an ordinary host with no markers reports clientActions null (V1 surface unchanged)
+ ---
+ duration_ms: 7.919547
+ type: 'test'
+ ...
+# Subtest: JSON renderer emits a stable client_actions block
+ok 2070 - JSON renderer emits a stable client_actions block
+ ---
+ duration_ms: 15.630197
+ type: 'test'
+ ...
+# Subtest: text renderer prints the client actions section with per-state detail
+ok 2071 - text renderer prints the client actions section with per-state detail
+ ---
+ duration_ms: 5.884754
+ type: 'test'
+ ...
+# Subtest: a refused marker with no reason still renders the repair hint, never a bare [refused]
+ok 2072 - a refused marker with no reason still renders the repair hint, never a bare [refused]
+ ---
+ duration_ms: 7.803891
+ type: 'test'
+ ...
+# Subtest: attach declared targets read mixed done/failed/pending/n-a cleanly (T9)
+ok 2073 - attach declared targets read mixed done/failed/pending/n-a cleanly (T9)
+ ---
+ duration_ms: 6.839385
+ type: 'test'
+ ...
+# Subtest: attach renders n/a for on_join:false and for a non-joined explicit target; bare local stays V1 (T9)
+ok 2074 - attach renders n/a for on_join:false and for a non-joined explicit target; bare local stays V1 (T9)
+ ---
+ duration_ms: 28.938403
+ type: 'test'
+ ...
+# Subtest: a failed attach does not flip overall to degraded (T9)
+ok 2075 - a failed attach does not flip overall to degraded (T9)
+ ---
+ duration_ms: 14.606421
+ type: 'test'
+ ...
+# Subtest: a done attach marker renders attached and collapses with the declared target - no double row (T9)
+ok 2076 - a done attach marker renders attached and collapses with the declared target - no double row (T9)
+ ---
+ duration_ms: 7.868209
+ type: 'test'
+ ...
+# Subtest: a client attached with no plugin enabling it is a warning naming the detach
+ok 2077 - a client attached with no plugin enabling it is a warning naming the detach
+ ---
+ duration_ms: 20.463873
+ type: 'test'
+ ...
+# Subtest: a configured attached client draws no stranded diagnostic
+ok 2078 - a configured attached client draws no stranded diagnostic
+ ---
+ duration_ms: 20.844061
+ type: 'test'
+ ...
+# Subtest: a managed host leaves the reverse lane to the reconciler and stays quiet
+ok 2079 - a managed host leaves the reverse lane to the reconciler and stays quiet
+ ---
+ duration_ms: 21.432045
+ type: 'test'
+ ...
+# Subtest: an unreadable local config is not read as an instruction to detach
+ok 2080 - an unreadable local config is not read as an instruction to detach
+ ---
+ duration_ms: 13.148334
+ type: 'test'
+ ...
+# Subtest: a missing config still names the client its marker strands
+ok 2081 - a missing config still names the client its marker strands
+ ---
+ duration_ms: 10.550476
+ type: 'test'
+ ...
+# Subtest: the text renderer prints a client probe error instead of a bare not-attached
+ok 2082 - the text renderer prints a client probe error instead of a bare not-attached
+ ---
+ duration_ms: 23.366185
+ type: 'test'
+ ...
+# Subtest: a client carrying an error is never collapsed into the clients "(none)" line
+ok 2083 - a client carrying an error is never collapsed into the clients "(none)" line
+ ---
+ duration_ms: 26.779823
+ type: 'test'
+ ...
+# Subtest: an all-clean host keeps the "(none)" clients collapse (surface unchanged)
+ok 2084 - an all-clean host keeps the "(none)" clients collapse (surface unchanged)
+ ---
+ duration_ms: 18.931254
+ type: 'test'
+ ...
+# Subtest: the JSON renderer carries the same client error
+ok 2085 - the JSON renderer carries the same client error
+ ---
+ duration_ms: 11.383823
+ type: 'test'
+ ...
+# Subtest: an enrolled host with nothing opted out shows every configured source syncing (default-sync)
+ok 2086 - an enrolled host with nothing opted out shows every configured source syncing (default-sync)
+ ---
+ duration_ms: 23.406315
+ type: 'test'
+ ...
+# Subtest: an opted-out local source shows local-only; a stale central opt-out entry is inert
+ok 2087 - an opted-out local source shows local-only; a stale central opt-out entry is inert
+ ---
+ duration_ms: 30.167571
+ type: 'test'
+ ...
+# Subtest: a solo host leaves the split null and the V1 surface unchanged
+ok 2088 - a solo host leaves the split null and the V1 surface unchanged
+ ---
+ duration_ms: 11.095313
+ type: 'test'
+ ...
+# Subtest: a corrupt opt-out store degrades to a null split plus a warning diagnostic
+ok 2089 - a corrupt opt-out store degrades to a null split plus a warning diagnostic
+ ---
+ duration_ms: 21.999077
+ type: 'test'
+ ...
+# Subtest: no hold marker: the deadline is null and text/JSON stay quiet
+ok 2090 - no hold marker: the deadline is null and text/JSON stay quiet
+ ---
+ duration_ms: 25.849659
+ type: 'test'
+ ...
+# Subtest: a live hold surfaces its deadline in text and JSON (LLP 0100 R9)
+ok 2091 - a live hold surfaces its deadline in text and JSON (LLP 0100 R9)
+ ---
+ duration_ms: 41.695553
+ type: 'test'
+ ...
+# Subtest: an expired hold reads as absent, exactly as the sink driver sees it
+ok 2092 - an expired hold reads as absent, exactly as the sink driver sees it
+ ---
+ duration_ms: 14.876101
+ type: 'test'
+ ...
+# Subtest: a corrupt marker fails open (absent, no diagnostic, no degrade) - LLP 0101 fail-open polarity
+ok 2093 - a corrupt marker fails open (absent, no diagnostic, no degrade) - LLP 0101 fail-open polarity
+ ---
+ duration_ms: 9.426598
+ type: 'test'
+ ...
+# Subtest: gatewaySourceDetails surfaces the fallback marker from status.json
+ok 2094 - gatewaySourceDetails surfaces the fallback marker from status.json
+ ---
+ duration_ms: 0.783431
+ type: 'test'
+ ...
+# Subtest: a fallback boot emits a non-degrading gateway_port_fallback warning
+ok 2095 - a fallback boot emits a non-degrading gateway_port_fallback warning
+ ---
+ duration_ms: 30.662143
+ type: 'test'
+ ...
+# Subtest: a default-port boot emits no gateway_port_fallback diagnostic
+ok 2096 - a default-port boot emits no gateway_port_fallback diagnostic
+ ---
+ duration_ms: 29.642985
+ type: 'test'
+ ...
+# Subtest: an idle gateway that was configured with upstreams warns
+ok 2097 - an idle gateway that was configured with upstreams warns
+ ---
+ duration_ms: 45.513497
+ type: 'test'
+ ...
+# Subtest: an idle gateway whose configured upstream has no name warns
+ok 2098 - an idle gateway whose configured upstream has no name warns
+ ---
+ duration_ms: 12.880187
+ type: 'test'
+ ...
+# Subtest: an idle gateway whose configured upstream has an empty name warns
+ok 2099 - an idle gateway whose configured upstream has an empty name warns
+ ---
+ duration_ms: 12.944063
+ type: 'test'
+ ...
+# Subtest: an idle gateway with no configured upstreams stays quiet and healthy
+ok 2100 - an idle gateway with no configured upstreams stays quiet and healthy
+ ---
+ duration_ms: 9.916742
+ type: 'test'
+ ...
+# Subtest: an idle gateway with no upstreams key at all stays quiet and healthy
+ok 2101 - an idle gateway with no upstreams key at all stays quiet and healthy
+ ---
+ duration_ms: 8.538608
+ type: 'test'
+ ...
+# Subtest: a degenerate upstreams detail does not crash or warn
+ok 2102 - a degenerate upstreams detail does not crash or warn
+ ---
+ duration_ms: 8.583727
+ type: 'test'
+ ...
+# Subtest: a status file with names but no count still warns
+ok 2103 - a status file with names but no count still warns
+ ---
+ duration_ms: 13.611238
+ type: 'test'
+ ...
+# Subtest: a listening gateway never warns, however many upstreams it has
+ok 2104 - a listening gateway never warns, however many upstreams it has
+ ---
+ duration_ms: 5.732853
+ type: 'test'
+ ...
+# Subtest: the idle warning does not degrade overall health
+ok 2105 - the idle warning does not degrade overall health
+ ---
+ duration_ms: 6.49418
+ type: 'test'
+ ...
+# Subtest: a stopped daemon does not warn off a stale status snapshot
+ok 2106 - a stopped daemon does not warn off a stale status snapshot
+ ---
+ duration_ms: 11.739914
+ type: 'test'
+ ...
+# Subtest: a gateway that lost one of two configured upstreams warns while listening
+ok 2107 - a gateway that lost one of two configured upstreams warns while listening
+ ---
+ duration_ms: 20.8829
+ type: 'test'
+ ...
+# Subtest: a dropped upstream whose name a registered adapter preset backfills is not reported as silence
+ok 2108 - a dropped upstream whose name a registered adapter preset backfills is not reported as silence
+ ---
+ duration_ms: 8.156317
+ type: 'test'
+ ...
+# Subtest: a partial loss with no usable name still warns off the count
+ok 2109 - a partial loss with no usable name still warns off the count
+ ---
+ duration_ms: 7.564537
+ type: 'test'
+ ...
+# Subtest: a total loss still warns, through the same comparison
+ok 2110 - a total loss still warns, through the same comparison
+ ---
+ duration_ms: 27.625618
+ type: 'test'
+ ...
+# Subtest: a fully valid gateway config stays quiet
+ok 2111 - a fully valid gateway config stays quiet
+ ---
+ duration_ms: 6.575444
+ type: 'test'
+ ...
+# Subtest: a hermes-only gateway stays quiet through the same comparison
+ok 2112 - a hermes-only gateway stays quiet through the same comparison
+ ---
+ duration_ms: 6.070547
+ type: 'test'
+ ...
+# Subtest: a status file without the dropped count does not guess at a partial loss
+ok 2113 - a status file without the dropped count does not guess at a partial loss
+ ---
+ duration_ms: 6.623728
+ type: 'test'
+ ...
+# Subtest: a dropped upstream covered by no preset is reported as silent, definitively
+ok 2114 - a dropped upstream covered by no preset is reported as silent, definitively
+ ---
+ duration_ms: 16.191879
+ type: 'test'
+ ...
+# Subtest: a dropped upstream a registered preset covers is reported as backfilled, definitively
+ok 2115 - a dropped upstream a registered preset covers is reported as backfilled, definitively
+ ---
+ duration_ms: 11.923272
+ type: 'test'
+ ...
+# Subtest: a mixed drop separates the covered name from the silent one
+ok 2116 - a mixed drop separates the covered name from the silent one
+ ---
+ duration_ms: 8.804732
+ type: 'test'
+ ...
+# Subtest: the dropped names do not read as the configured set
+ok 2117 - the dropped names do not read as the configured set
+ ---
+ duration_ms: 5.662597
+ type: 'test'
+ ...
+# Subtest: a status file with no preset list keeps the hedge
+ok 2118 - a status file with no preset list keeps the hedge
+ ---
+ duration_ms: 11.180333
+ type: 'test'
+ ...
+# Subtest: an unattributable drop keeps the hedge even with a preset list
+ok 2119 - an unattributable drop keeps the hedge even with a preset list
+ ---
+ duration_ms: 7.947419
+ type: 'test'
+ ...
+# Subtest: a silent dropped name is not reported as a dead path, because a surviving catch-all still takes its traffic
+ok 2120 - a silent dropped name is not reported as a dead path, because a surviving catch-all still takes its traffic
+ ---
+ duration_ms: 5.680734
+ type: 'test'
+ ...
+# Subtest: a covered dropped name is reported as losing its whole entry, not only its base_url
+ok 2121 - a covered dropped name is reported as losing its whole entry, not only its base_url
+ ---
+ duration_ms: 6.517917
+ type: 'test'
+ ...
+# Subtest: two covered names read as two presets
+ok 2122 - two covered names read as two presets
+ ---
+ duration_ms: 7.023765
+ type: 'test'
+ ...
+# Subtest: the hedged branch does not claim the traffic is dead either, because a catch-all still takes it
+ok 2123 - the hedged branch does not claim the traffic is dead either, because a catch-all still takes it
+ ---
+ duration_ms: 8.34944
+ type: 'test'
+ ...
+# Subtest: the hedged branch pluralises for a multi-entry drop
+ok 2124 - the hedged branch pluralises for a multi-entry drop
+ ---
+ duration_ms: 9.116295
+ type: 'test'
+ ...
+# Subtest: a covered name is not claimed to be proxied, because a surviving upstream can shadow the preset
+ok 2125 - a covered name is not claimed to be proxied, because a surviving upstream can shadow the preset
+ ---
+ duration_ms: 14.401249
+ type: 'test'
+ ...
+# Subtest: a covered name does not claim path_prefix is in force, which a match()-carrying preset never routes on
+ok 2126 - a covered name does not claim path_prefix is in force, which a match()-carrying preset never routes on
+ ---
+ duration_ms: 6.518487
+ type: 'test'
+ ...
+# Subtest: the hedged branch pluralises names off the names, not off the entry count
+ok 2127 - the hedged branch pluralises names off the names, not off the entry count
+ ---
+ duration_ms: 12.997124
+ type: 'test'
+ ...
+# Subtest: a never-joined host reports no layering (V1 surface unchanged)
+ok 2128 - a never-joined host reports no layering (V1 surface unchanged)
+ ---
+ duration_ms: 23.380215
+ type: 'test'
+ ...
+# Subtest: a joined host surfaces provenance and the dropped-local section
+ok 2129 - a joined host surfaces provenance and the dropped-local section
+ ---
+ duration_ms: 19.646461
+ type: 'test'
+ ...
+# Subtest: status JSON renders per-row provenance and the config_layers block
+ok 2130 - status JSON renders per-row provenance and the config_layers block
+ ---
+ duration_ms: 29.313994
+ type: 'test'
+ ...
+# Subtest: status text renders provenance tags and the dropped-local section
+ok 2131 - status text renders provenance tags and the dropped-local section
+ ---
+ duration_ms: 14.175596
+ type: 'test'
+ ...
+# Subtest: a never-joined host renders no provenance tags or layers block
+ok 2132 - a never-joined host renders no provenance tags or layers block
+ ---
+ duration_ms: 9.933558
+ type: 'test'
+ ...
+# Subtest: no local-only list yet: count is 0 and the text/JSON surfaces stay quiet
+ok 2133 - no local-only list yet: count is 0 and the text/JSON surfaces stay quiet
+ ---
+ duration_ms: 24.792773
+ type: 'test'
+ ...
+# Subtest: an empty (but present) list also hides the line
+ok 2134 - an empty (but present) list also hides the line
+ ---
+ duration_ms: 22.465395
+ type: 'test'
+ ...
+# Subtest: N > 0 renders the withholding line in text and the count in JSON
+ok 2135 - N > 0 renders the withholding line in text and the count in JSON
+ ---
+ duration_ms: 12.348478
+ type: 'test'
+ ...
+# Subtest: a corrupt local-only list surfaces a diagnostic and a null usagePolicy, never a silent 0
+ok 2136 - a corrupt local-only list surfaces a diagnostic and a null usagePolicy, never a silent 0
+ ---
+ duration_ms: 14.832844
+ type: 'test'
+ ...
+# Subtest: the new-folder line is enrolled-only, and states either mode
+ok 2137 - the new-folder line is enrolled-only, and states either mode
+ ---
+ duration_ms: 14.405776
+ type: 'test'
+ ...
+# Subtest: an enabled claude-desktop with no attach marker warns, naming hyp claude-desktop install
+ok 2138 - an enabled claude-desktop with no attach marker warns, naming hyp claude-desktop install
+ ---
+ duration_ms: 26.736647
+ type: 'test'
+ ...
+# Subtest: with claude-desktop not in the config, no such warning fires
+ok 2139 - with claude-desktop not in the config, no such warning fires
+ ---
+ duration_ms: 20.291952
+ type: 'test'
+ ...
+# Subtest: recentEntrypointsFromSources lifts the gateway detail, newest first
+ok 2140 - recentEntrypointsFromSources lifts the gateway detail, newest first
+ ---
+ duration_ms: 3.364614
+ type: 'test'
+ ...
+# Subtest: a malformed or partial entry is dropped, not repaired into a name no query can reproduce
+ok 2141 - a malformed or partial entry is dropped, not repaired into a name no query can reproduce
+ ---
+ duration_ms: 0.2043
+ type: 'test'
+ ...
+# Subtest: a daemon with no gateway source, or an older daemon, yields an empty list
+ok 2142 - a daemon with no gateway source, or an older daemon, yields an empty list
+ ---
+ duration_ms: 0.123809
+ type: 'test'
+ ...
+# Subtest: hyp status surfaces Codex Desktop traffic from status.json, with no cache read
+ok 2143 - hyp status surfaces Codex Desktop traffic from status.json, with no cache read
+ ---
+ duration_ms: 36.174672
+ type: 'test'
+ ...
+# Subtest: an install that has never captured keeps the V1 text surface unchanged
+ok 2144 - an install that has never captured keeps the V1 text surface unchanged
+ ---
+ duration_ms: 10.216919
+ type: 'test'
+ ...
+# Subtest: last-seen survives its daemon: a stopped daemon still reports what it saw
+ok 2145 - last-seen survives its daemon: a stopped daemon still reports what it saw
+ ---
+ duration_ms: 14.211089
+ type: 'test'
+ ...
+# Subtest: formatEntrypointAge is coarse, and never renders a negative age
+ok 2146 - formatEntrypointAge is coarse, and never renders a negative age
+ ---
+ duration_ms: 0.20444
+ type: 'test'
+ ...
+# Subtest: a client with no client_name renders without inventing one
+ok 2147 - a client with no client_name renders without inventing one
+ ---
+ duration_ms: 9.792313
+ type: 'test'
+ ...
+# Subtest: recentEntrypointsFromSources cleans labels a foreign status file supplies
+ok 2148 - recentEntrypointsFromSources cleans labels a foreign status file supplies
+ ---
+ duration_ms: 0.564989
+ type: 'test'
+ ...
+# Subtest: a rendered recent-clients block cannot be forged by a hostile entrypoint
+ok 2149 - a rendered recent-clients block cannot be forged by a hostile entrypoint
+ ---
+ duration_ms: 8.340086
+ type: 'test'
+ ...
+# Subtest: a status file with an absurd number of entrypoints is capped on read
+ok 2150 - a status file with an absurd number of entrypoints is capped on read
+ ---
+ duration_ms: 6.88229
+ type: 'test'
+ ...
+# Subtest: streaming reader handles a large file without loading it into memory
+ok 2151 - streaming reader handles a large file without loading it into memory
+ ---
+ duration_ms: 733.986568
+ type: 'test'
+ ...
+# Subtest: partial trailing line is preserved and correctly handled
+ok 2152 - partial trailing line is preserved and correctly handled
+ ---
+ duration_ms: 3.589486
+ type: 'test'
+ ...
+# Subtest: malformed JSON lines are counted, logged, and skipped without aborting
+ok 2153 - malformed JSON lines are counted, logged, and skipped without aborting
+ ---
+ duration_ms: 6.911575
+ type: 'test'
+ ...
+# Subtest: resume cursor updates correctly and restart from cursor produces identical results
+ok 2154 - resume cursor updates correctly and restart from cursor produces identical results
+ ---
+ duration_ms: 9.859385
+ type: 'test'
+ ...
+# Subtest: resume offset does not advance past a partially flushed line
+ok 2155 - resume offset does not advance past a partially flushed line
+ ---
+ duration_ms: 2.983605
+ type: 'test'
+ ...
+# Subtest: batch boundaries respect row-count threshold
+ok 2156 - batch boundaries respect row-count threshold
+ ---
+ duration_ms: 4.708527
+ type: 'test'
+ ...
+# Subtest: batch boundaries respect byte-size threshold
+ok 2157 - batch boundaries respect byte-size threshold
+ ---
+ duration_ms: 5.383493
+ type: 'test'
+ ...
+# Subtest: rows are decorated with internal fields
+ok 2158 - rows are decorated with internal fields
+ ---
+ duration_ms: 2.80995
+ type: 'test'
+ ...
+# Subtest: identical rows produce the same _hyp_cache_row_id
+ok 2159 - identical rows produce the same _hyp_cache_row_id
+ ---
+ duration_ms: 1.90137
+ type: 'test'
+ ...
+# Subtest: progress file write and read roundtrip
+ok 2160 - progress file write and read roundtrip
+ ---
+ duration_ms: 2.714776
+ type: 'test'
+ ...
+# Subtest: empty file yields no batches
+ok 2161 - empty file yields no batches
+ ---
+ duration_ms: 1.265844
+ type: 'test'
+ ...
+# Subtest: no TTY and no --yes: refuses, exports nothing, and leaves the hold standing
+ok 2162 - no TTY and no --yes: refuses, exports nothing, and leaves the hold standing
+ ---
+ duration_ms: 25.595763
+ type: 'test'
+ ...
+# Subtest: declining at the prompt cancels, exports nothing, and leaves the hold standing
+ok 2163 - declining at the prompt cancels, exports nothing, and leaves the hold standing
+ ---
+ duration_ms: 7.782178
+ type: 'test'
+ ...
+# Subtest: confirming during the review window ends it and exports
+ok 2164 - confirming during the review window ends it and exports
+ ---
+ duration_ms: 8.365014
+ type: 'test'
+ ...
+# Subtest: the held prompt states the window, the irreversibility, and the way out
+ok 2165 - the held prompt states the window, the irreversibility, and the way out
+ ---
+ duration_ms: 2.898065
+ type: 'test'
+ ...
+# Subtest: --dry-run prints the plan, exports nothing, and keeps the window open
+ok 2166 - --dry-run prints the plan, exports nothing, and keeps the window open
+ ---
+ duration_ms: 2.591278
+ type: 'test'
+ ...
+# Subtest: the plan names each destination and whether it leaves the machine
+ok 2167 - the plan names each destination and whether it leaves the machine
+ ---
+ duration_ms: 2.743199
+ type: 'test'
+ ...
+# Subtest: an unnamed server falls back to its host, still not a linkifiable URL
+ok 2168 - an unnamed server falls back to its host, still not a linkifiable URL
+ ---
+ duration_ms: 1.000941
+ type: 'test'
+ ...
+# Subtest: a named instance cannot release the hold: the plan it showed was not the hold's scope
+ok 2169 - a named instance cannot release the hold: the plan it showed was not the hold's scope
+ ---
+ duration_ms: 1.908992
+ type: 'test'
+ ...
+# Subtest: --yes cannot release the hold: \#no-release licenses an attended confirmation only
+ok 2170 - --yes cannot release the hold: \#no-release licenses an attended confirmation only
+ ---
+ duration_ms: 8.068363
+ type: 'test'
+ ...
+# Subtest: a hold that cannot be cleared fails loudly instead of exiting 0 with nothing sent
+ok 2171 - a hold that cannot be cleared fails loudly instead of exiting 0 with nothing sent
+ ---
+ duration_ms: 4.101835
+ type: 'test'
+ ...
+# Subtest: the plan counts the directories being withheld
+ok 2172 - the plan counts the directories being withheld
+ ---
+ duration_ms: 2.13161
+ type: 'test'
+ ...
+# Subtest: the plan names the clients kept local-only (LLP 0188 \#never-silent)
+ok 2173 - the plan names the clients kept local-only (LLP 0188 \#never-silent)
+ ---
+ duration_ms: 3.154064
+ type: 'test'
+ ...
+# Subtest: with nothing marked, the plan says so in one line covering both stores
+ok 2174 - with nothing marked, the plan says so in one line covering both stores
+ ---
+ duration_ms: 2.066091
+ type: 'test'
+ ...
+# Subtest: with no hold, --yes exports without inventing a review window
+ok 2175 - with no hold, --yes exports without inventing a review window
+ ---
+ duration_ms: 2.312875
+ type: 'test'
+ ...
+# Subtest: an unknown instance names the ones that exist
+ok 2176 - an unknown instance names the ones that exist
+ ---
+ duration_ms: 0.802309
+ type: 'test'
+ ...
+# Subtest: an instance argument ticks only that sink (no hold in play)
+ok 2177 - an instance argument ticks only that sink (no hold in play)
+ ---
+ duration_ms: 4.278974
+ type: 'test'
+ ...
+# Subtest: no sinks at all is a no-op, not an error
+ok 2178 - no sinks at all is a no-op, not an error
+ ---
+ duration_ms: 0.782058
+ type: 'test'
+ ...
+# Subtest: a failed export reports a nonzero exit
+ok 2179 - a failed export reports a nonzero exit
+ ---
+ duration_ms: 1.65145
+ type: 'test'
+ ...
+# Subtest: unionSources unions columns and sums numRows
+ok 2180 - unionSources unions columns and sums numRows
+ ---
+ duration_ms: 1.555294
+ type: 'test'
+ ...
+# Subtest: unionSources does not forward limit/offset to sub-sources
+ok 2181 - unionSources does not forward limit/offset to sub-sources
+ ---
+ duration_ms: 0.54588
+ type: 'test'
+ ...
+# Subtest: unionSources forwards where/columns to sub-sources that have the predicate columns
+ok 2182 - unionSources forwards where/columns to sub-sources that have the predicate columns
+ ---
+ duration_ms: 0.383453
+ type: 'test'
+ ...
+# Subtest: unionSources over two real parquet partitions filters correctly through executeSql (WHERE column folded into projection)
+ok 2183 - unionSources over two real parquet partitions filters correctly through executeSql (WHERE column folded into projection)
+ ---
+ duration_ms: 16.000339
+ type: 'test'
+ ...
+# Subtest: unionSources drops where for a partition that lacks a predicate column but keeps it for one that has it
+ok 2184 - unionSources drops where for a partition that lacks a predicate column but keeps it for one that has it
+ ---
+ duration_ms: 0.30981
+ type: 'test'
+ ...
+# Subtest: unionSources does not push qualified where predicates to sub-sources
+ok 2185 - unionSources does not push qualified where predicates to sub-sources
+ ---
+ duration_ms: 0.211832
+ type: 'test'
+ ...
+# Subtest: unionSources does not push a non-enumerable where (subquery) to any sub-source
+ok 2186 - unionSources does not push a non-enumerable where (subquery) to any sub-source
+ ---
+ duration_ms: 0.353697
+ type: 'test'
+ ...
+# Subtest: unionSources tolerates a scan with no options
+ok 2187 - unionSources tolerates a scan with no options
+ ---
+ duration_ms: 0.199093
+ type: 'test'
+ ...
+# Subtest: unionSources omits scanColumn unless every partition can stream the column
+ok 2188 - unionSources omits scanColumn unless every partition can stream the column
+ ---
+ duration_ms: 0.379216
+ type: 'test'
+ ...
+# Subtest: unionSources scanColumn concatenates partitions and owns limit/offset over the merged stream
+ok 2189 - unionSources scanColumn concatenates partitions and owns limit/offset over the merged stream
+ ---
+ duration_ms: 0.780416
+ type: 'test'
+ ...
+# Subtest: unionSources scanColumn skips a whole partition its numRows proves is inside the offset
+ok 2190 - unionSources scanColumn skips a whole partition its numRows proves is inside the offset
+ ---
+ duration_ms: 0.270521
+ type: 'test'
+ ...
+# Subtest: unionSources scanColumn forwards where per partition and reports the merged appliedWhere
+ok 2191 - unionSources scanColumn forwards where per partition and reports the merged appliedWhere
+ ---
+ duration_ms: 0.482042
+ type: 'test'
+ ...
+# Subtest: unionSources scanColumn drops where for a partition lacking a predicate column and reports appliedWhere false
+ok 2192 - unionSources scanColumn drops where for a partition lacking a predicate column and reports appliedWhere false
+ ---
+ duration_ms: 0.232093
+ type: 'test'
+ ...
+# Subtest: unionSources scanColumn reports appliedWhere false over a legacy bare-iterable partition
+ok 2193 - unionSources scanColumn reports appliedWhere false over a legacy bare-iterable partition
+ ---
+ duration_ms: 0.28929
+ type: 'test'
+ ...
+# Subtest: normalizeScanColumn passes a flagged result through and shims a legacy iterable
+ok 2194 - normalizeScanColumn passes a flagged result through and shims a legacy iterable
+ ---
+ duration_ms: 0.158441
+ type: 'test'
+ ...
+# Subtest: emptySource advertises the given columns and yields no rows
+ok 2195 - emptySource advertises the given columns and yields no rows
+ ---
+ duration_ms: 0.183449
+ type: 'test'
+ ...
+# Subtest: verbArgvForClass maps each class to its hyp policy set token (LLP 0111 \#teaching)
+ok 2196 - verbArgvForClass maps each class to its hyp policy set token (LLP 0111 \#teaching)
+ ---
+ duration_ms: 1.670088
+ type: 'test'
+ ...
+# Subtest: the three choices are presented least-to-most restrictive with their tokens
+ok 2197 - the three choices are presented least-to-most restrictive with their tokens
+ ---
+ duration_ms: 0.184891
+ type: 'test'
+ ...
+# Subtest: buildClassificationPrompt names the folder, all three classes, and each policy set command
+ok 2198 - buildClassificationPrompt names the folder, all three classes, and each policy set command
+ ---
+ duration_ms: 0.357914
+ type: 'test'
+ ...
+# Subtest: the prompt names its own off switch (LLP 0200 \#escape-hatch)
+ok 2199 - the prompt names its own off switch (LLP 0200 \#escape-hatch)
+ ---
+ duration_ms: 0.131169
+ type: 'test'
+ ...
+# Subtest: decideClassification: with the ask on, prompt only when enrolled AND interactive AND unclassified
+ok 2200 - decideClassification: with the ask on, prompt only when enrolled AND interactive AND unclassified
+ ---
+ duration_ms: 0.25656
+ type: 'test'
+ ...
+# Subtest: decideClassification: the default sync mode means no ask at all (LLP 0200 \#default)
+ok 2201 - decideClassification: the default sync mode means no ask at all (LLP 0200 \#default)
+ ---
+ duration_ms: 0.127724
+ type: 'test'
+ ...
+# Subtest: evaluateCwdClassification honors a standing sync preference and reports it
+ok 2202 - evaluateCwdClassification honors a standing sync preference and reports it
+ ---
+ duration_ms: 29.996651
+ type: 'test'
+ ...
+# Subtest: a machine that never answered is not asked: the default is sync (LLP 0200 \#default)
+ok 2203 - a machine that never answered is not asked: the default is sync (LLP 0200 \#default)
+ ---
+ duration_ms: 1.358104
+ type: 'test'
+ ...
+# Subtest: turning the ask on restores the per-folder question
+ok 2204 - turning the ask on restores the per-folder question
+ ---
+ duration_ms: 2.370603
+ type: 'test'
+ ...
+# Subtest: a corrupt preference costs a question, never a silent sync
+ok 2205 - a corrupt preference costs a question, never a silent sync
+ ---
+ duration_ms: 3.545189
+ type: 'test'
+ ...
+# Subtest: evaluateCwdClassification prompts for an enrolled, interactive, unclassified cwd with the ask on
+ok 2206 - evaluateCwdClassification prompts for an enrolled, interactive, unclassified cwd with the ask on
+ ---
+ duration_ms: 0.35535
+ type: 'test'
+ ...
+# Subtest: evaluateCwdClassification is inert on an unenrolled machine
+ok 2207 - evaluateCwdClassification is inert on an unenrolled machine
+ ---
+ duration_ms: 0.941771
+ type: 'test'
+ ...
+# Subtest: evaluateCwdClassification does not prompt once the folder is classified
+ok 2208 - evaluateCwdClassification does not prompt once the folder is classified
+ ---
+ duration_ms: 0.201536
+ type: 'test'
+ ...
+# Subtest: evaluateCwdClassification passes a non-interactive session through
+ok 2209 - evaluateCwdClassification passes a non-interactive session through
+ ---
+ duration_ms: 0.218042
+ type: 'test'
+ ...
+# Subtest: evaluateCwdClassification never fails the session on a corrupt list or a broken enrollment read
+ok 2210 - evaluateCwdClassification never fails the session on a corrupt list or a broken enrollment read
+ ---
+ duration_ms: 1.176328
+ type: 'test'
+ ...
+# Subtest: the classification answer lands via the real hyp policy set verb (LLP 0106 -> LLP 0111 -> LLP 0103)
+ok 2211 - the classification answer lands via the real hyp policy set verb (LLP 0106 -> LLP 0111 -> LLP 0103)
+ ---
+ duration_ms: 6.99384
+ type: 'test'
+ ...
+# Subtest: readRowsSince: local-only cwd rows are dropped from the payload but the cursor advances across them; full and cwd-less rows pass
+ok 2212 - readRowsSince: local-only cwd rows are dropped from the payload but the cursor advances across them; full and cwd-less rows pass
+ ---
+ duration_ms: 43.577464
+ type: 'test'
+ ...
+# Subtest: readRowsSince: a `columns` projection omitting cwd still withholds local-only rows, and full rows come back without a cwd field
+ok 2213 - readRowsSince: a `columns` projection omitting cwd still withholds local-only rows, and full rows come back without a cwd field
+ ---
+ duration_ms: 17.29071
+ type: 'test'
+ ...
+# Subtest: readRowsSince: a corrupt list (resolver throws) fails the partition read rather than silently skipping
+ok 2214 - readRowsSince: a corrupt list (resolver throws) fails the partition read rather than silently skipping
+ ---
+ duration_ms: 35.7853
+ type: 'test'
+ ...
+# Subtest: readRowsSince: with no resolver configured, nothing is ever dropped (cwd is ignored)
+ok 2215 - readRowsSince: with no resolver configured, nothing is ever dropped (cwd is ignored)
+ ---
+ duration_ms: 22.839574
+ type: 'test'
+ ...
+# Subtest: openIncrementalRows: leading drops are skipped from the encoder stream but advance lastAfter
+ok 2216 - openIncrementalRows: leading drops are skipped from the encoder stream but advance lastAfter
+ ---
+ duration_ms: 0.827538
+ type: 'test'
+ ...
+# Subtest: openIncrementalRows: trailing drops still advance lastAfter past the last real row
+ok 2217 - openIncrementalRows: trailing drops still advance lastAfter past the last real row
+ ---
+ duration_ms: 0.334488
+ type: 'test'
+ ...
+# Subtest: openIncrementalRows: an all-drops partition is empty yet exposes droppedRowCount and an advanced lastAfter
+ok 2218 - openIncrementalRows: an all-drops partition is empty yet exposes droppedRowCount and an advanced lastAfter
+ ---
+ duration_ms: 0.254367
+ type: 'test'
+ ...
+# Subtest: openIncrementalRows: interleaved drops are skipped, real rows kept in order
+ok 2219 - openIncrementalRows: interleaved drops are skipped, real rows kept in order
+ ---
+ duration_ms: 0.27641
+ type: 'test'
+ ...
+# Subtest: the two fixture spellings are genuinely different strings that NFC folds together
+ok 2220 - the two fixture spellings are genuinely different strings that NFC folds together
+ ---
+ duration_ms: 0.906368
+ type: 'test'
+ ...
+# Subtest: resolve: a local-only entry declared NFC still governs a cwd that arrives NFD
+ok 2221 - resolve: a local-only entry declared NFC still governs a cwd that arrives NFD
+ ---
+ duration_ms: 2.067392
+ type: 'test'
+ ...
+# Subtest: resolve: an ignore entry declared NFD still governs a cwd that arrives NFC
+ok 2222 - resolve: an ignore entry declared NFD still governs a cwd that arrives NFC
+ ---
+ duration_ms: 2.854478
+ type: 'test'
+ ...
+# Subtest: resolve: the fold is on the ancestor segment, not only the leaf
+ok 2223 - resolve: the fold is on the ancestor segment, not only the leaf
+ ---
+ duration_ms: 0.589506
+ type: 'test'
+ ...
+# Subtest: foldPath distributes over the path separator, so a prefix test stays segment-aware
+ok 2224 - foldPath distributes over the path separator, so a prefix test stays segment-aware
+ ---
+ duration_ms: 0.205082
+ type: 'test'
+ ...
+# Subtest: resolve: a sibling whose name merely shares a folded prefix is still NOT matched
+ok 2225 - resolve: a sibling whose name merely shares a folded prefix is still NOT matched
+ ---
+ duration_ms: 0.474832
+ type: 'test'
+ ...
+# Subtest: resolve: a carve-out that gains reach only by folding does not punch a hole in a broader restrictive entry
+ok 2226 - resolve: a carve-out that gains reach only by folding does not punch a hole in a broader restrictive entry
+ ---
+ duration_ms: 1.528543
+ type: 'test'
+ ...
+# Subtest: resolve: an entry that gains reach by folding overrides a shallower explicit full marker
+ok 2227 - resolve: an entry that gains reach by folding overrides a shallower explicit full marker
+ ---
+ duration_ms: 0.410123
+ type: 'test'
+ ...
+# Subtest: resolve: a carve-out declared in the same spelling as the entry it carves out of is still honored
+ok 2228 - resolve: a carve-out declared in the same spelling as the entry it carves out of is still honored
+ ---
+ duration_ms: 0.591469
+ type: 'test'
+ ...
+# Subtest: resolve: nearest-governs is measured on the folded spelling, not on the declared one
+ok 2229 - resolve: nearest-governs is measured on the folded spelling, not on the declared one
+ ---
+ duration_ms: 0.761338
+ type: 'test'
+ ...
+# Subtest: resolve: folding never loosens, over every arrangement of a nested pair
+ok 2230 - resolve: folding never loosens, over every arrangement of a nested pair
+ ---
+ duration_ms: 18.397282
+ type: 'test'
+ ...
+# Subtest: resolve: case is NOT folded by default, because this volume is case-sensitive
+ok 2231 - resolve: case is NOT folded by default, because this volume is case-sensitive
+ ---
+ duration_ms: 0.272934
+ type: 'test'
+ ...
+# Subtest: resolve: case IS folded when the volume verdict says the volume is case-insensitive
+ok 2232 - resolve: case IS folded when the volume verdict says the volume is case-insensitive
+ ---
+ duration_ms: 0.275128
+ type: 'test'
+ ...
+# Subtest: resolve: a case-insensitive volume verdict still does not let a carve-out loosen a broader entry
+ok 2233 - resolve: a case-insensitive volume verdict still does not let a carve-out loosen a broader entry
+ ---
+ duration_ms: 0.332696
+ type: 'test'
+ ...
+# Subtest: resolve: the case verdict is asked per entry, so a per-volume answer applies per entry
+ok 2234 - resolve: the case verdict is asked per entry, so a per-volume answer applies per entry
+ ---
+ duration_ms: 0.889312
+ type: 'test'
+ ...
+# Subtest: createVolumeCaseProbe is inert off darwin: constant false, and it issues no syscall
+ok 2235 - createVolumeCaseProbe is inert off darwin: constant false, and it issues no syscall
+ ---
+ duration_ms: 0.104268
+ type: 'test'
+ ...
+# Subtest: createVolumeCaseProbe memoizes a definite verdict per volume, not per path
+ok 2236 - createVolumeCaseProbe memoizes a definite verdict per volume, not per path
+ ---
+ duration_ms: 0.203359
+ type: 'test'
+ ...
+# Subtest: createVolumeCaseProbe does not memoize an undetermined answer as the volume verdict
+ok 2237 - createVolumeCaseProbe does not memoize an undetermined answer as the volume verdict
+ ---
+ duration_ms: 0.163518
+ type: 'test'
+ ...
+# Subtest: createVolumeCaseProbe does not memoize a non-ENOENT failure of the flipped stat
+ok 2238 - createVolumeCaseProbe does not memoize a non-ENOENT failure of the flipped stat
+ ---
+ duration_ms: 0.188687
+ type: 'test'
+ ...
+# Subtest: createVolumeCaseProbe reports case-sensitive when the flipped spelling does not exist
+ok 2239 - createVolumeCaseProbe reports case-sensitive when the flipped spelling does not exist
+ ---
+ duration_ms: 0.12499
+ type: 'test'
+ ...
+# Subtest: createVolumeCaseProbe fails toward the pre-fold behaviour and logs a hashed skip
+ok 2240 - createVolumeCaseProbe fails toward the pre-fold behaviour and logs a hashed skip
+ ---
+ duration_ms: 0.271002
+ type: 'test'
+ ...
+# Subtest: resolve emits a hashed usage_policy.fold_tightened only when folding changed the verdict
+ok 2241 - resolve emits a hashed usage_policy.fold_tightened only when folding changed the verdict
+ ---
+ duration_ms: 0.543436
+ type: 'test'
+ ...
+# Subtest: resolve: folding happens on the cache miss only, so a repeated cwd re-reads nothing
+ok 2242 - resolve: folding happens on the cache miss only, so a repeated cwd re-reads nothing
+ ---
+ duration_ms: 0.498528
+ type: 'test'
+ ...
+# Subtest: localOnlyListPath derives /usage-policy/local-only.json
+ok 2243 - localOnlyListPath derives /usage-policy/local-only.json
+ ---
+ duration_ms: 0.643688
+ type: 'test'
+ ...
+# Subtest: localOnlyListPath requires a stateDir
+ok 2244 - localOnlyListPath requires a stateDir
+ ---
+ duration_ms: 0.194115
+ type: 'test'
+ ...
+# Subtest: readLocalOnlyDirs returns [] when the list has never been written (the common case)
+ok 2245 - readLocalOnlyDirs returns [] when the list has never been written (the common case)
+ ---
+ duration_ms: 5.077458
+ type: 'test'
+ ...
+# Subtest: writeLocalOnlyDirs then readLocalOnlyDirs round-trips the directory set
+ok 2246 - writeLocalOnlyDirs then readLocalOnlyDirs round-trips the directory set
+ ---
+ duration_ms: 2.141135
+ type: 'test'
+ ...
+# Subtest: writeLocalOnlyDirs persists the LLP 0103 version-2 class-per-entry shape
+ok 2247 - writeLocalOnlyDirs persists the LLP 0103 version-2 class-per-entry shape
+ ---
+ duration_ms: 1.005919
+ type: 'test'
+ ...
+# Subtest: writeLocalOnlyDirs normalizes to absolute paths, dedupes, and sorts
+ok 2248 - writeLocalOnlyDirs normalizes to absolute paths, dedupes, and sorts
+ ---
+ duration_ms: 3.347398
+ type: 'test'
+ ...
+# Subtest: readLocalOnlyDirs re-normalizes a hand-edited file with duplicates/relative-looking entries
+ok 2249 - readLocalOnlyDirs re-normalizes a hand-edited file with duplicates/relative-looking entries
+ ---
+ duration_ms: 1.260897
+ type: 'test'
+ ...
+# Subtest: readLocalOnlyDirs throws LocalOnlyListUnreadableError on unparseable JSON
+ok 2250 - readLocalOnlyDirs throws LocalOnlyListUnreadableError on unparseable JSON
+ ---
+ duration_ms: 1.511257
+ type: 'test'
+ ...
+# Subtest: readLocalOnlyDirs throws LocalOnlyListUnreadableError on a wrong-shape file
+ok 2251 - readLocalOnlyDirs throws LocalOnlyListUnreadableError on a wrong-shape file
+ ---
+ duration_ms: 1.549144
+ type: 'test'
+ ...
+# Subtest: readLocalOnlyDirs throws LocalOnlyListUnreadableError when dirs is not a string array
+ok 2252 - readLocalOnlyDirs throws LocalOnlyListUnreadableError when dirs is not a string array
+ ---
+ duration_ms: 3.424495
+ type: 'test'
+ ...
+# Subtest: writeLocalOnlyDirs is atomic write-rename and leaves no temp files
+ok 2253 - writeLocalOnlyDirs is atomic write-rename and leaves no temp files
+ ---
+ duration_ms: 1.169117
+ type: 'test'
+ ...
+# Subtest: writeLocalOnlyDirs replaces the file in place (latest wins, single file)
+ok 2254 - writeLocalOnlyDirs replaces the file in place (latest wins, single file)
+ ---
+ duration_ms: 3.595795
+ type: 'test'
+ ...
+# Subtest: writeLocalOnlyDirs mkdir -p s the usage-policy parent directory on demand
+ok 2255 - writeLocalOnlyDirs mkdir -p s the usage-policy parent directory on demand
+ ---
+ duration_ms: 1.462593
+ type: 'test'
+ ...
+# Subtest: resolve: an ignored directory reached by its symlink spelling is still ignored
+ok 2256 - resolve: an ignored directory reached by its symlink spelling is still ignored
+ ---
+ duration_ms: 4.418305
+ type: 'test'
+ ...
+# Subtest: resolve: a descendant of a symlink that points into an ignored tree is ignored
+ok 2257 - resolve: a descendant of a symlink that points into an ignored tree is ignored
+ ---
+ duration_ms: 1.792404
+ type: 'test'
+ ...
+# Subtest: resolve: a `.hypignore` governing the symlink's own ancestors still governs (the converse spelling is not lost)
+ok 2258 - resolve: a `.hypignore` governing the symlink's own ancestors still governs (the converse spelling is not lost)
+ ---
+ duration_ms: 1.383943
+ type: 'test'
+ ...
+# Subtest: resolve: a local-only entry declared by its symlink spelling governs the real directory
+ok 2259 - resolve: a local-only entry declared by its symlink spelling governs the real directory
+ ---
+ duration_ms: 3.673894
+ type: 'test'
+ ...
+# Subtest: resolve: a local-only entry declared canonically governs a cwd that arrives by symlink
+ok 2260 - resolve: a local-only entry declared canonically governs a cwd that arrives by symlink
+ ---
+ duration_ms: 1.689688
+ type: 'test'
+ ...
+# Subtest: resolve: nested entries keep nearest-governs when the outer one matched by its canonical spelling
+ok 2261 - resolve: nested entries keep nearest-governs when the outer one matched by its canonical spelling
+ ---
+ duration_ms: 1.502814
+ type: 'test'
+ ...
+# Subtest: resolve: a carve-out that gains reach by canonicalization does not punch a hole in a broader restrictive entry
+ok 2262 - resolve: a carve-out that gains reach by canonicalization does not punch a hole in a broader restrictive entry
+ ---
+ duration_ms: 1.631319
+ type: 'test'
+ ...
+# Subtest: resolve: between two entries that both reach only by canonicalization, the deeper carve-out governs
+ok 2263 - resolve: between two entries that both reach only by canonicalization, the deeper carve-out governs
+ ---
+ duration_ms: 2.035374
+ type: 'test'
+ ...
+# Subtest: governingListEntry names the entry whose verdict the gate used, not the longest declared string
+ok 2264 - governingListEntry names the entry whose verdict the gate used, not the longest declared string
+ ---
+ duration_ms: 4.615706
+ type: 'test'
+ ...
+# Subtest: resolve: a cwd reached through a dangling symlink keeps the class its as-given spelling produces
+ok 2265 - resolve: a cwd reached through a dangling symlink keeps the class its as-given spelling produces
+ ---
+ duration_ms: 3.266245
+ type: 'test'
+ ...
+# Subtest: resolve: a symlinked cwd is still not matched by a mere string-prefix sibling (segment-aware after canonicalization)
+ok 2266 - resolve: a symlinked cwd is still not matched by a mere string-prefix sibling (segment-aware after canonicalization)
+ ---
+ duration_ms: 1.624208
+ type: 'test'
+ ...
+# Subtest: resolve: a list entry whose declared target has been deleted keeps governing its declared spelling
+ok 2267 - resolve: a list entry whose declared target has been deleted keeps governing its declared spelling
+ ---
+ duration_ms: 1.166052
+ type: 'test'
+ ...
+# Subtest: resolve: an unresolvable cwd under a symlinked ancestor still meets the .hypignore governing its real tree
+ok 2268 - resolve: an unresolvable cwd under a symlinked ancestor still meets the .hypignore governing its real tree
+ ---
+ duration_ms: 1.356452
+ type: 'test'
+ ...
+# Subtest: resolve: canonicalization costs one realpath per cwd per TTL window, not one per call
+ok 2269 - resolve: canonicalization costs one realpath per cwd per TTL window, not one per call
+ ---
+ duration_ms: 0.836211
+ type: 'test'
+ ...
+# Subtest: canonicalizeDirSync: full, partial, and unresolvable outcomes
+ok 2270 - canonicalizeDirSync: full, partial, and unresolvable outcomes
+ ---
+ duration_ms: 1.696218
+ type: 'test'
+ ...
+# Subtest: scopeGoverns / sameDirectory: spelling-agnostic, still segment-aware
+ok 2271 - scopeGoverns / sameDirectory: spelling-agnostic, still segment-aware
+ ---
+ duration_ms: 2.56617
+ type: 'test'
+ ...
+# Subtest: parseHypignore: empty body => ignore (the empty-file opt-out)
+ok 2272 - parseHypignore: empty body => ignore (the empty-file opt-out)
+ ---
+ duration_ms: 2.002623
+ type: 'test'
+ ...
+# Subtest: parseHypignore: comment-only/blank body => ignore
+ok 2273 - parseHypignore: comment-only/blank body => ignore
+ ---
+ duration_ms: 0.212733
+ type: 'test'
+ ...
+# Subtest: parseHypignore: recognized `ignore` token => ignore, no warn
+ok 2274 - parseHypignore: recognized `ignore` token => ignore, no warn
+ ---
+ duration_ms: 0.140663
+ type: 'test'
+ ...
+# Subtest: parseHypignore: unknown token => ignore + warn (fail-safe)
+ok 2275 - parseHypignore: unknown token => ignore + warn (fail-safe)
+ ---
+ duration_ms: 0.212323
+ type: 'test'
+ ...
+# Subtest: parseHypignore: implemented `local-only` token resolves to local-only, no warn (LLP 0070)
+ok 2276 - parseHypignore: implemented `local-only` token resolves to local-only, no warn (LLP 0070)
+ ---
+ duration_ms: 0.186143
+ type: 'test'
+ ...
+# Subtest: parseHypignore: a still-unimplemented token => ignore + warn (fail-safe)
+ok 2277 - parseHypignore: a still-unimplemented token => ignore + warn (fail-safe)
+ ---
+ duration_ms: 0.13186
+ type: 'test'
+ ...
+# Subtest: parseHypignore: first token wins; trailing path patterns are parsed-but-ignored
+ok 2278 - parseHypignore: first token wins; trailing path patterns are parsed-but-ignored
+ ---
+ duration_ms: 0.161836
+ type: 'test'
+ ...
+# Subtest: resolve: no .hypignore anywhere => full, governedBy null
+ok 2279 - resolve: no .hypignore anywhere => full, governedBy null
+ ---
+ duration_ms: 1.2174
+ type: 'test'
+ ...
+# Subtest: resolve: nearest ancestor .hypignore wins
+ok 2280 - resolve: nearest ancestor .hypignore wins
+ ---
+ duration_ms: 0.696198
+ type: 'test'
+ ...
+# Subtest: resolve: walks all the way to the filesystem root
+ok 2281 - resolve: walks all the way to the filesystem root
+ ---
+ duration_ms: 0.564488
+ type: 'test'
+ ...
+# Subtest: resolve: dotfile `local-only` token is honored (implemented, LLP 0070), not clamped to ignore
+ok 2282 - resolve: dotfile `local-only` token is honored (implemented, LLP 0070), not clamped to ignore
+ ---
+ duration_ms: 0.285113
+ type: 'test'
+ ...
+# Subtest: resolve: a still-unimplemented class in a governing file fails safe to ignore
+ok 2283 - resolve: a still-unimplemented class in a governing file fails safe to ignore
+ ---
+ duration_ms: 0.224511
+ type: 'test'
+ ...
+# Subtest: resolve: a present-but-unreadable .hypignore fails closed to ignore (privacy-protecting)
+ok 2284 - resolve: a present-but-unreadable .hypignore fails closed to ignore (privacy-protecting)
+ ---
+ duration_ms: 0.254317
+ type: 'test'
+ ...
+# Subtest: resolve: per-cwd cache is stable and reads the file once
+ok 2285 - resolve: per-cwd cache is stable and reads the file once
+ ---
+ duration_ms: 0.566241
+ type: 'test'
+ ...
+# Subtest: resolve: relative cwd is normalized before caching
+ok 2286 - resolve: relative cwd is normalized before caching
+ ---
+ duration_ms: 0.176599
+ type: 'test'
+ ...
+# Subtest: resolve: a .hypignore written after a cwd was cached `full` is honored once the TTL elapses
+ok 2287 - resolve: a .hypignore written after a cwd was cached `full` is honored once the TTL elapses
+ ---
+ duration_ms: 0.48703
+ type: 'test'
+ ...
+# Subtest: resolve: removing a .hypignore is honored once the TTL elapses (unignore)
+ok 2288 - resolve: removing a .hypignore is honored once the TTL elapses (unignore)
+ ---
+ duration_ms: 0.33609
+ type: 'test'
+ ...
+# Subtest: resolve: no localOnlyListPath configured => resolver behaves exactly as before
+ok 2289 - resolve: no localOnlyListPath configured => resolver behaves exactly as before
+ ---
+ duration_ms: 0.279655
+ type: 'test'
+ ...
+# Subtest: resolve: cwd equal to a listed dir is local-only, governed by the list path
+ok 2290 - resolve: cwd equal to a listed dir is local-only, governed by the list path
+ ---
+ duration_ms: 0.579841
+ type: 'test'
+ ...
+# Subtest: resolve: cwd descendant of a listed dir is local-only
+ok 2291 - resolve: cwd descendant of a listed dir is local-only
+ ---
+ duration_ms: 0.344825
+ type: 'test'
+ ...
+# Subtest: resolve: sibling-prefix directory is NOT matched (segment-aware: /a/bc vs /a/b)
+ok 2292 - resolve: sibling-prefix directory is NOT matched (segment-aware: /a/bc vs /a/b)
+ ---
+ duration_ms: 0.511097
+ type: 'test'
+ ...
+# Subtest: resolve: dotfile `ignore` beats a list `local-only` match (most-restrictive wins)
+ok 2293 - resolve: dotfile `ignore` beats a list `local-only` match (most-restrictive wins)
+ ---
+ duration_ms: 0.318154
+ type: 'test'
+ ...
+# Subtest: resolve: list `local-only` beats an unlisted `full` dotfile default (most-restrictive wins)
+ok 2294 - resolve: list `local-only` beats an unlisted `full` dotfile default (most-restrictive wins)
+ ---
+ duration_ms: 0.277842
+ type: 'test'
+ ...
+# Subtest: resolve: cwd not in the list and no governing dotfile => full
+ok 2295 - resolve: cwd not in the list and no governing dotfile => full
+ ---
+ duration_ms: 0.268488
+ type: 'test'
+ ...
+# Subtest: resolve: a missing local-only list file is "no exclusions" ([])
+ok 2296 - resolve: a missing local-only list file is "no exclusions" ([])
+ ---
+ duration_ms: 0.167515
+ type: 'test'
+ ...
+# Subtest: resolve: list parse is memoized (TTL) independent of per-cwd caching, and re-read picks up an edited list
+ok 2297 - resolve: list parse is memoized (TTL) independent of per-cwd caching, and re-read picks up an edited list
+ ---
+ duration_ms: 0.662927
+ type: 'test'
+ ...
+# Subtest: resolve: a corrupt local-only list throws, not silently "no exclusions" (fail-safe)
+ok 2298 - resolve: a corrupt local-only list throws, not silently "no exclusions" (fail-safe)
+ ---
+ duration_ms: 0.53285
+ type: 'test'
+ ...
+# Subtest: resolve: a v2 list entry resolves with its own recorded class, not a hardcoded local-only
+ok 2299 - resolve: a v2 list entry resolves with its own recorded class, not a hardcoded local-only
+ ---
+ duration_ms: 0.3356
+ type: 'test'
+ ...
+# Subtest: resolve: an explicit v2 "full" entry resolves to full, governed by the list (the "asked; syncs" marker)
+ok 2300 - resolve: an explicit v2 "full" entry resolves to full, governed by the list (the "asked; syncs" marker)
+ ---
+ duration_ms: 0.286766
+ type: 'test'
+ ...
+# Subtest: resolve: among nested v2 list entries the most specific (longest) dir wins
+ok 2301 - resolve: among nested v2 list entries the most specific (longest) dir wins
+ ---
+ duration_ms: 0.610998
+ type: 'test'
+ ...
+# Subtest: resolve: a v1 list is migrated on read, and continues to resolve as local-only
+ok 2302 - resolve: a v1 list is migrated on read, and continues to resolve as local-only
+ ---
+ duration_ms: 0.268367
+ type: 'test'
+ ...
+# Subtest: resolve: a v2 list entry with an unknown class throws, not silently "no exclusions" (fail-safe)
+ok 2303 - resolve: a v2 list entry with an unknown class throws, not silently "no exclusions" (fail-safe)
+ ---
+ duration_ms: 0.201517
+ type: 'test'
+ ...
+# Subtest: createUsagePolicyResolver defaults fs to node:fs when none injected
+ok 2304 - createUsagePolicyResolver defaults fs to node:fs when none injected
+ ---
+ duration_ms: 0.188797
+ type: 'test'
+ ...
+# Subtest: findRepoRoot: nearest ancestor with a .git entry is the repo root
+ok 2305 - findRepoRoot: nearest ancestor with a .git entry is the repo root
+ ---
+ duration_ms: 0.161746
+ type: 'test'
+ ...
+# Subtest: findRepoRoot: the start dir itself can be the repo root
+ok 2306 - findRepoRoot: the start dir itself can be the repo root
+ ---
+ duration_ms: 0.140574
+ type: 'test'
+ ...
+# Subtest: findRepoRoot: returns null when no ancestor has a .git
+ok 2307 - findRepoRoot: returns null when no ancestor has a .git
+ ---
+ duration_ms: 0.113242
+ type: 'test'
+ ...
+# Subtest: findRepoRoot: defaults fs to node:fs without throwing
+ok 2308 - findRepoRoot: defaults fs to node:fs without throwing
+ ---
+ duration_ms: 0.10519
+ type: 'test'
+ ...
+# Subtest: atomicWriteFile writes content and creates parent directories
+ok 2309 - atomicWriteFile writes content and creates parent directories
+ ---
+ duration_ms: 9.053791
+ type: 'test'
+ ...
+# Subtest: atomicWriteFile leaves no temp file behind on success or failure
+ok 2310 - atomicWriteFile leaves no temp file behind on success or failure
+ ---
+ duration_ms: 10.070907
+ type: 'test'
+ ...
+# Subtest: atomicWriteFile applies the requested file mode
+ok 2311 - atomicWriteFile applies the requested file mode
+ ---
+ duration_ms: 6.34925
+ type: 'test'
+ ...
+# Subtest: atomicWriteFile enforces expectedMtimeMs (CONCURRENT_EDIT)
+ok 2312 - atomicWriteFile enforces expectedMtimeMs (CONCURRENT_EDIT)
+ ---
+ duration_ms: 10.64555
+ type: 'test'
+ ...
+# Subtest: atomicWriteFileSync writes atomically and cleans up on failure
+ok 2313 - atomicWriteFileSync writes atomically and cleans up on failure
+ ---
+ duration_ms: 2.084719
+ type: 'test'
+ ...
+# Subtest: atomicWriteJson round-trips through readJsonIfExists
+ok 2314 - atomicWriteJson round-trips through readJsonIfExists
+ ---
+ duration_ms: 3.357743
+ type: 'test'
+ ...
+# Subtest: readFileIfExists/readJsonIfExists return null only for ENOENT
+ok 2315 - readFileIfExists/readJsonIfExists return null only for ENOENT
+ ---
+ duration_ms: 2.328509
+ type: 'test'
+ ...
+# Subtest: isPlainObject accepts records, rejects null/arrays/primitives
+ok 2316 - isPlainObject accepts records, rejects null/arrays/primitives
+ ---
+ duration_ms: 0.962212
+ type: 'test'
+ ...
+# Subtest: stringValue passes through non-empty strings only
+ok 2317 - stringValue passes through non-empty strings only
+ ---
+ duration_ms: 0.188417
+ type: 'test'
+ ...
+# Subtest: parseMaybeJson parses strings, passes everything else through
+ok 2318 - parseMaybeJson parses strings, passes everything else through
+ ---
+ duration_ms: 0.619121
+ type: 'test'
+ ...
+# Subtest: canonicalJson is key-order independent
+ok 2319 - canonicalJson is key-order independent
+ ---
+ duration_ms: 0.26333
+ type: 'test'
+ ...
+# Subtest: sortKeys deep-copies without mutating the input
+ok 2320 - sortKeys deep-copies without mutating the input
+ ---
+ duration_ms: 0.166173
+ type: 'test'
+ ...
+# Subtest: sha256Hex matches the known digest of an empty string
+ok 2321 - sha256Hex matches the known digest of an empty string
+ ---
+ duration_ms: 0.484076
+ type: 'test'
+ ...
+# Subtest: errCode extracts string codes and nothing else
+ok 2322 - errCode extracts string codes and nothing else
+ ---
+ duration_ms: 0.23025
+ type: 'test'
+ ...
+# Subtest: sanitizeLabel strips exactly the code points it stripped before LLP 0225
+ok 2323 - sanitizeLabel strips exactly the code points it stripped before LLP 0225
+ ---
+ duration_ms: 23.176897
+ type: 'test'
+ ...
+# Subtest: escapeForDisplay replaces control characters with visible escapes
+ok 2324 - escapeForDisplay replaces control characters with visible escapes
+ ---
+ duration_ms: 0.38189
+ type: 'test'
+ ...
+# Subtest: escapeForDisplay escapes bidi formatting and leaves zero-width formatting alone
+ok 2325 - escapeForDisplay escapes bidi formatting and leaves zero-width formatting alone
+ ---
+ duration_ms: 0.320236
+ type: 'test'
+ ...
+# Subtest: escapeForDisplay never truncates, never drops, and passes clean text through
+ok 2326 - escapeForDisplay never truncates, never drops, and passes clean text through
+ ---
+ duration_ms: 0.172743
+ type: 'test'
+ ...
+# Subtest: control flags: defaults when none given
+ok 2327 - control flags: defaults when none given
+ ---
+ duration_ms: 2.478417
+ type: 'test'
+ ...
+# Subtest: control flags: strip render/transport flags, keep the verb tail in rest
+ok 2328 - control flags: strip render/transport flags, keep the verb tail in rest
+ ---
+ duration_ms: 0.292965
+ type: 'test'
+ ...
+# Subtest: control flags: bare --remote selects the default target (empty sentinel)
+ok 2329 - control flags: bare --remote selects the default target (empty sentinel)
+ ---
+ duration_ms: 0.125852
+ type: 'test'
+ ...
+# Subtest: control flags: --refresh sets refreshExplicit (for the --remote conflict check)
+ok 2330 - control flags: --refresh sets refreshExplicit (for the --remote conflict check)
+ ---
+ duration_ms: 0.115245
+ type: 'test'
+ ...
+# Subtest: control flags: --format and --refresh validate their values
+ok 2331 - control flags: --format and --refresh validate their values
+ ---
+ duration_ms: 0.199954
+ type: 'test'
+ ...
+# Subtest: codec: greedy positional joins all remaining tokens (SQL)
+ok 2332 - codec: greedy positional joins all remaining tokens (SQL)
+ ---
+ duration_ms: 0.313016
+ type: 'test'
+ ...
+# Subtest: codec: positional + typed flags, kebab→snake, array split, defaults
+ok 2333 - codec: positional + typed flags, kebab→snake, array split, defaults
+ ---
+ duration_ms: 0.351334
+ type: 'test'
+ ...
+# Subtest: codec: integer minimum, enum, unknown flag, extra positional
+ok 2334 - codec: integer minimum, enum, unknown flag, extra positional
+ ---
+ duration_ms: 0.217861
+ type: 'test'
+ ...
+# Subtest: codec: required missing is reported
+ok 2335 - codec: required missing is reported
+ ---
+ duration_ms: 0.315569
+ type: 'test'
+ ...
+# Subtest: codec: --flag=value inline form
+ok 2336 - codec: --flag=value inline form
+ ---
+ duration_ms: 0.364294
+ type: 'test'
+ ...
+# Subtest: toJsonSchema strips CLI-only greedy/positional hints
+ok 2337 - toJsonSchema strips CLI-only greedy/positional hints
+ ---
+ duration_ms: 0.219964
+ type: 'test'
+ ...
+# Subtest: usageForVerb renders positionals then flags
+ok 2338 - usageForVerb renders positionals then flags
+ ---
+ duration_ms: 0.232473
+ type: 'test'
+ ...
+# Subtest: validateToolArguments coerces, applies defaults, enforces required + unknown
+ok 2339 - validateToolArguments coerces, applies defaults, enforces required + unknown
+ ---
+ duration_ms: 0.249149
+ type: 'test'
+ ...
+# Subtest: register projects a CLI command into the command registry
+ok 2340 - register projects a CLI command into the command registry
+ ---
+ duration_ms: 0.915801
+ type: 'test'
+ ...
+# Subtest: getByTool and get resolve the same verb; list is sorted
+ok 2341 - getByTool and get resolve the same verb; list is sorted
+ ---
+ duration_ms: 7.363672
+ type: 'test'
+ ...
+# Subtest: duplicate verb name and duplicate tool name are both rejected
+ok 2342 - duplicate verb name and duplicate tool name are both rejected
+ ---
+ duration_ms: 0.482313
+ type: 'test'
+ ...
+# Subtest: projection is idempotent when a command of that name already exists
+ok 2343 - projection is idempotent when a command of that name already exists
+ ---
+ duration_ms: 0.21724
+ type: 'test'
+ ...
+# Subtest: exposure and auth-class default to cli+mcp / read
+ok 2344 - exposure and auth-class default to cli+mcp / read
+ ---
+ duration_ms: 0.241597
+ type: 'test'
+ ...
+# Subtest: validation rejects malformed verbs
+ok 2345 - validation rejects malformed verbs
+ ---
+ duration_ms: 0.17124
+ type: 'test'
+ ...
+# Subtest: --remote runs the remote tool and renders with the same render path
+ok 2346 - --remote runs the remote tool and renders with the same render path
+ ---
+ duration_ms: 7.366857
+ type: 'test'
+ ...
+# Subtest: server-cap truncation is surfaced as its own stderr line
+ok 2347 - server-cap truncation is surfaced as its own stderr line
+ ---
+ duration_ms: 0.844393
+ type: 'test'
+ ...
+# Subtest: bare --remote routes to the shipped default target (central server)
+ok 2348 - bare --remote routes to the shipped default target (central server)
+ ---
+ duration_ms: 0.941782
+ type: 'test'
+ ...
+# Subtest: --remote with --refresh is a hard error (server owns its freshness)
+ok 2349 - --remote with --refresh is a hard error (server owns its freshness)
+ ---
+ duration_ms: 0.331894
+ type: 'test'
+ ...
+# Subtest: an unknown remote target is rejected before any network call
+ok 2350 - an unknown remote target is rejected before any network call
+ ---
+ duration_ms: 0.75699
+ type: 'test'
+ ...
+# Subtest: a missing token errors with login guidance
+ok 2351 - a missing token errors with login guidance
+ ---
+ duration_ms: 5.235147
+ type: 'test'
+ ...
+# Subtest: a remote isError result maps to a nonzero exit with the message
+ok 2352 - a remote isError result maps to a nonzero exit with the message
+ ---
+ duration_ms: 0.760366
+ type: 'test'
+ ...
+# Subtest: the finale reports asset counts per client, never a line per copy
+ok 2353 - the finale reports asset counts per client, never a line per copy
+ ---
+ duration_ms: 81.514535
+ type: 'test'
+ ...
+# Subtest: picking claude-desktop records a not-applicable attach, not a failure
+ok 2354 - picking claude-desktop records a not-applicable attach, not a failure
+ ---
+ duration_ms: 36.489921
+ type: 'test'
+ ...
+# Subtest: a registered adapter that throws still reports a real attach failure
+ok 2355 - a registered adapter that throws still reports a real attach failure
+ ---
+ duration_ms: 26.13952
+ type: 'test'
+ ...
+# Subtest: defaultBackfillConsentPromptFactory, imported directly, asks the exact finale copy for a sample question
+ok 2356 - defaultBackfillConsentPromptFactory, imported directly, asks the exact finale copy for a sample question
+ ---
+ duration_ms: 2.26347
+ type: 'test'
+ ...
+# Subtest: defaultBackfillConsentPromptFactory renders the multi-provider title identically and honors an explicit no
+ok 2357 - defaultBackfillConsentPromptFactory renders the multi-provider title identically and honors an explicit no
+ ---
+ duration_ms: 0.409513
+ type: 'test'
+ ...
+# Subtest: onboarding with claude selected runs the backfill step and records stats
+ok 2358 - onboarding with claude selected runs the backfill step and records stats
+ ---
+ duration_ms: 46.467706
+ type: 'test'
+ ...
+# Subtest: --dry-run onboarding includes the backfill plan but writes nothing
+ok 2359 - --dry-run onboarding includes the backfill plan but writes nothing
+ ---
+ duration_ms: 20.558788
+ type: 'test'
+ ...
+# Subtest: --yes mode runs bounded backfill automatically without a consent prompt
+ok 2360 - --yes mode runs bounded backfill automatically without a consent prompt
+ ---
+ duration_ms: 18.655295
+ type: 'test'
+ ...
+# Subtest: --no-daemon still backfills - it is a local file import
+ok 2361 - --no-daemon still backfills - it is a local file import
+ ---
+ duration_ms: 19.23798
+ type: 'test'
+ ...
+# Subtest: interactive onboarding defaults backfill to enabled (consent yes runs it)
+ok 2362 - interactive onboarding defaults backfill to enabled (consent yes runs it)
+ ---
+ duration_ms: 19.279352
+ type: 'test'
+ ...
+# Subtest: interactive onboarding lets the user decline backfill
+ok 2363 - interactive onboarding lets the user decline backfill
+ ---
+ duration_ms: 26.138879
+ type: 'test'
+ ...
+# Subtest: interactive onboarding maps cancelled backfill consent to the cancel exit path
+ok 2364 - interactive onboarding maps cancelled backfill consent to the cancel exit path
+ ---
+ duration_ms: 20.980849
+ type: 'test'
+ ...
+# Subtest: picked clients without a registered backfill provider are skipped
+ok 2365 - picked clients without a registered backfill provider are skipped
+ ---
+ duration_ms: 12.372395
+ type: 'test'
+ ...
+# Subtest: a throwing backfill runner is caught and recorded as failed
+ok 2366 - a throwing backfill runner is caught and recorded as failed
+ ---
+ duration_ms: 16.879806
+ type: 'test'
+ ...
+# Subtest: the finale runs no backfill when no backfill runner is injected
+ok 2367 - the finale runs no backfill when no backfill runner is injected
+ ---
+ duration_ms: 16.456172
+ type: 'test'
+ ...
+# Subtest: onboarding with codex selected runs the backfill step and records stats
+ok 2368 - onboarding with codex selected runs the backfill step and records stats
+ ---
+ duration_ms: 14.736438
+ type: 'test'
+ ...
+# Subtest: onboarding with both claude and codex selected runs both providers
+ok 2369 - onboarding with both claude and codex selected runs both providers
+ ---
+ duration_ms: 18.348487
+ type: 'test'
+ ...
+# Subtest: interactive onboarding prompts codex backfill consent and runs it on yes
+ok 2370 - interactive onboarding prompts codex backfill consent and runs it on yes
+ ---
+ duration_ms: 18.241035
+ type: 'test'
+ ...
+# Subtest: a failing provider does not abort the other selected providers
+ok 2371 - a failing provider does not abort the other selected providers
+ ---
+ duration_ms: 14.998386
+ type: 'test'
+ ...
+# Subtest: a sweep-backed provider is disclosed and runs even when consent is declined
+ok 2372 - a sweep-backed provider is disclosed and runs even when consent is declined
+ ---
+ duration_ms: 31.070174
+ type: 'test'
+ ...
+# Subtest: an openclaw-only pick asks no backfill question but still runs the first import
+ok 2373 - an openclaw-only pick asks no backfill question but still runs the first import
+ ---
+ duration_ms: 17.868078
+ type: 'test'
+ ...
+# Subtest: cancelling consent skips sweep-backed providers too
+ok 2374 - cancelling consent skips sweep-backed providers too
+ ---
+ duration_ms: 14.46121
+ type: 'test'
+ ...
+# Subtest: picker pre-checks detected sources, labels them, and defaults export to local-parquet
+ok 2375 - picker pre-checks detected sources, labels them, and defaults export to local-parquet
+ ---
+ duration_ms: 47.446023
+ type: 'test'
+ ...
+# Subtest: nothing detected → no source pre-checked, export still defaults to local-parquet
+ok 2376 - nothing detected → no source pre-checked, export still defaults to local-parquet
+ ---
+ duration_ms: 20.559589
+ type: 'test'
+ ...
+# Subtest: non-interactive picks skip detection entirely
+ok 2377 - non-interactive picks skip detection entirely
+ ---
+ duration_ms: 13.414809
+ type: 'test'
+ ...
+# Subtest: picker prompt prints context under source options and defaults export to local-parquet
+ok 2378 - picker prompt prints context under source options and defaults export to local-parquet
+ ---
+ duration_ms: 48.14135
+ type: 'test'
+ ...
+# Subtest: enterKeepsChecked: the fallback renders the checked state and a bare enter keeps it
+ok 2379 - enterKeepsChecked: the fallback renders the checked state and a bare enter keeps it
+ ---
+ duration_ms: 0.81592
+ type: 'test'
+ ...
+# Subtest: enterKeepsChecked: typed indices still replace the checked set
+ok 2380 - enterKeepsChecked: typed indices still replace the checked set
+ ---
+ duration_ms: 0.317753
+ type: 'test'
+ ...
+# Subtest: enterKeepsChecked: an answer naming no row re-asks once, and a correction in the same chunk still wins
+ok 2381 - enterKeepsChecked: an answer naming no row re-asks once, and a correction in the same chunk still wins
+ ---
+ duration_ms: 1.419347
+ type: 'test'
+ ...
+# Subtest: enterKeepsChecked: endless invalid input stops after one re-ask instead of looping
+ok 2382 - enterKeepsChecked: endless invalid input stops after one re-ask instead of looping
+ ---
+ duration_ms: 0.511868
+ type: 'test'
+ ...
+# Subtest: enterKeepsChecked: an invalid answer then EOF resolves with the checked defaults instead of hanging
+ok 2383 - enterKeepsChecked: an invalid answer then EOF resolves with the checked defaults instead of hanging
+ ---
+ duration_ms: 0.520832
+ type: 'test'
+ ...
+# Subtest: EOF with no answer at all settles rather than hanging, on both paths
+ok 2384 - EOF with no answer at all settles rather than hanging, on both paths
+ ---
+ duration_ms: 0.94752
+ type: 'test'
+ ...
+# Subtest: "none" is the explicit empty selection on both paths
+ok 2385 - "none" is the explicit empty selection on both paths
+ ---
+ duration_ms: 0.916573
+ type: 'test'
+ ...
+# Subtest: enterKeepsChecked: a partially valid answer wins without a re-ask
+ok 2386 - enterKeepsChecked: a partially valid answer wins without a re-ask
+ ---
+ duration_ms: 0.517437
+ type: 'test'
+ ...
+# Subtest: without enterKeepsChecked an answer naming no row still selects nothing, asked exactly once
+ok 2387 - without enterKeepsChecked an answer naming no row still selects nothing, asked exactly once
+ ---
+ duration_ms: 0.504467
+ type: 'test'
+ ...
+# Subtest: back and all survive on both paths
+ok 2388 - back and all survive on both paths
+ ---
+ duration_ms: 1.048353
+ type: 'test'
+ ...
+# Subtest: enterKeepsChecked: a spent re-ask budget keeps the checked rows and says which
+ok 2389 - enterKeepsChecked: a spent re-ask budget keeps the checked rows and says which
+ ---
+ duration_ms: 0.354288
+ type: 'test'
+ ...
+# Subtest: enterKeepsChecked: a spent budget with nothing checked says the selection is empty
+ok 2390 - enterKeepsChecked: a spent budget with nothing checked says the selection is empty
+ ---
+ duration_ms: 0.350843
+ type: 'test'
+ ...
+# Subtest: without enterKeepsChecked a closed stdin cancels instead of selecting nothing
+ok 2391 - without enterKeepsChecked a closed stdin cancels instead of selecting nothing
+ ---
+ duration_ms: 0.242389
+ type: 'test'
+ ...
+# Subtest: a dropped terminal at the source picker cancels the run instead of installing with no sources
+ok 2392 - a dropped terminal at the source picker cancels the run instead of installing with no sources
+ ---
+ duration_ms: 12.100482
+ type: 'test'
+ ...
+# Subtest: every later ask on a spent stdin settles instead of hanging
+ok 2393 - every later ask on a spent stdin settles instead of hanging
+ ---
+ duration_ms: 0.570557
+ type: 'test'
+ ...
+# Subtest: without enterKeepsChecked a bare enter still selects nothing and no state is rendered
+ok 2394 - without enterKeepsChecked a bare enter still selects nothing and no state is rendered
+ ---
+ duration_ms: 0.238392
+ type: 'test'
+ ...
+# Subtest: runPickerWalkthrough drives the TUI multiselect end-to-end when stdin+stdout are TTYs
+ok 2395 - runPickerWalkthrough drives the TUI multiselect end-to-end when stdin+stdout are TTYs
+ ---
+ duration_ms: 43.914236
+ type: 'test'
+ ...
+# Subtest: runPickerWalkthrough returns a deterministic cancel exit code when the user cancels at the source prompt
+ok 2396 - runPickerWalkthrough returns a deterministic cancel exit code when the user cancels at the source prompt
+ ---
+ duration_ms: 26.532577
+ type: 'test'
+ ...
+# Subtest: runPickerWalkthrough falls back to the legacy numbered prompt under HYP_NO_TUI=1
+ok 2397 - runPickerWalkthrough falls back to the legacy numbered prompt under HYP_NO_TUI=1
+ ---
+ duration_ms: 21.996172
+ type: 'test'
+ ...
+# Subtest: shouldUseTui returns true when stdin and stdout are TTYs and HYP_NO_TUI unset
+ok 2398 - shouldUseTui returns true when stdin and stdout are TTYs and HYP_NO_TUI unset
+ ---
+ duration_ms: 1.12506
+ type: 'test'
+ ...
+# Subtest: shouldUseTui returns false when HYP_NO_TUI=1 even with both TTYs
+ok 2399 - shouldUseTui returns false when HYP_NO_TUI=1 even with both TTYs
+ ---
+ duration_ms: 0.261708
+ type: 'test'
+ ...
+# Subtest: shouldUseTui returns false when stdin is non-TTY
+ok 2400 - shouldUseTui returns false when stdin is non-TTY
+ ---
+ duration_ms: 0.196198
+ type: 'test'
+ ...
+# Subtest: shouldUseTui returns false when stdout is non-TTY
+ok 2401 - shouldUseTui returns false when stdout is non-TTY
+ ---
+ duration_ms: 0.141735
+ type: 'test'
+ ...
+# Subtest: shouldUseTui returns false when stdout is a duck-typed write sink (no isTTY)
+ok 2402 - shouldUseTui returns false when stdout is a duck-typed write sink (no isTTY)
+ ---
+ duration_ms: 0.191501
+ type: 'test'
+ ...
+# Subtest: shouldUseTui falls back to process.stdin when opts.stdin is omitted
+ok 2403 - shouldUseTui falls back to process.stdin when opts.stdin is omitted
+ ---
+ duration_ms: 0.200364
+ type: 'test'
+ ...
+# Subtest: shouldUseTui treats HYP_NO_TUI=0 as not-set (only the literal "1" disables)
+ok 2404 - shouldUseTui treats HYP_NO_TUI=0 as not-set (only the literal "1" disables)
+ ---
+ duration_ms: 0.201766
+ type: 'test'
+ ...
+# Subtest: shouldUseTui honors opts.env over process.env when both are set
+ok 2405 - shouldUseTui honors opts.env over process.env when both are set
+ ---
+ duration_ms: 0.148846
+ type: 'test'
+ ...
+# Subtest: shouldUseTui ignores process.env.HYP_NO_TUI when opts.env is supplied without it
+ok 2406 - shouldUseTui ignores process.env.HYP_NO_TUI when opts.env is supplied without it
+ ---
+ duration_ms: 0.356773
+ type: 'test'
+ ...
+# Subtest: isTty rejects undefined, null, and primitives
+ok 2407 - isTty rejects undefined, null, and primitives
+ ---
+ duration_ms: 0.346887
+ type: 'test'
+ ...
+# Subtest: isTty accepts only objects with isTTY === true (not truthy)
+ok 2408 - isTty accepts only objects with isTTY === true (not truthy)
+ ---
+ duration_ms: 0.157149
+ type: 'test'
+ ...
+# Subtest: the finale names a still-attached client the new config no longer collects
+ok 2409 - the finale names a still-attached client the new config no longer collects
+ ---
+ duration_ms: 63.258226
+ type: 'test'
+ ...
+# Subtest: a picked client that stays configured draws no stranded-attach warning
+ok 2410 - a picked client that stays configured draws no stranded-attach warning
+ ---
+ duration_ms: 21.878934
+ type: 'test'
+ ...
+# Subtest: an unattached client the picker skipped is not warned about
+ok 2411 - an unattached client the picker skipped is not warned about
+ ---
+ duration_ms: 20.523544
+ type: 'test'
+ ...
+# Subtest: a client the central layer names is not stranded by an unpicking run
+ok 2412 - a client the central layer names is not stranded by an unpicking run
+ ---
+ duration_ms: 19.528211
+ type: 'test'
+ ...
+# Subtest: a plugin entry left in the config with enabled false does not count as configured
+ok 2413 - a plugin entry left in the config with enabled false does not count as configured
+ ---
+ duration_ms: 4.788208
+ type: 'test'
+ ...
+# Subtest: registerExchangeProjector accepts a complete projector record
+ok 2414 - registerExchangeProjector accepts a complete projector record
+ ---
+ duration_ms: 1.727916
+ type: 'test'
+ ...
+# Subtest: registerExchangeProjector assigns _seq in registration order
+ok 2415 - registerExchangeProjector assigns _seq in registration order
+ ---
+ duration_ms: 0.793607
+ type: 'test'
+ ...
+# Subtest: registerExchangeProjector rejects missing or non-string name
+ok 2416 - registerExchangeProjector rejects missing or non-string name
+ ---
+ duration_ms: 0.40158
+ type: 'test'
+ ...
+# Subtest: registerExchangeProjector rejects missing match()
+ok 2417 - registerExchangeProjector rejects missing match()
+ ---
+ duration_ms: 0.183299
+ type: 'test'
+ ...
+# Subtest: registerExchangeProjector rejects missing project()
+ok 2418 - registerExchangeProjector rejects missing project()
+ ---
+ duration_ms: 0.497947
+ type: 'test'
+ ...
+# Subtest: registerExchangeProjector preserves a missing priority as undefined
+ok 2419 - registerExchangeProjector preserves a missing priority as undefined
+ ---
+ duration_ms: 0.135165
+ type: 'test'
+ ...
+# Subtest: registerUpstreamPreset stores presets by name and rejects invalid records
+ok 2420 - registerUpstreamPreset stores presets by name and rejects invalid records
+ ---
+ duration_ms: 0.27668
+ type: 'test'
+ ...
+# Subtest: registerUpstreamPreset replaces an existing entry with the same name
+ok 2421 - registerUpstreamPreset replaces an existing entry with the same name
+ ---
+ duration_ms: 0.285514
+ type: 'test'
+ ...
+# Subtest: registerClient validates name, defaultUpstream, and attach
+ok 2422 - registerClient validates name, defaultUpstream, and attach
+ ---
+ duration_ms: 0.438597
+ type: 'test'
+ ...
+# Subtest: getClient and listClients expose the registered clients
+ok 2423 - getClient and listClients expose the registered clients
+ ---
+ duration_ms: 0.405877
+ type: 'test'
+ ...
+# Subtest: localEndpoint throws before the source starts
+ok 2424 - localEndpoint throws before the source starts
+ ---
+ duration_ms: 0.202668
+ type: 'test'
+ ...
+# Subtest: localEndpoint returns the bound host:port once the source has started
+ok 2425 - localEndpoint returns the bound host:port once the source has started
+ ---
+ duration_ms: 0.121524
+ type: 'test'
+ ...
+# Subtest: localEndpoint appends pathPrefix, normalizing a missing leading slash
+ok 2426 - localEndpoint appends pathPrefix, normalizing a missing leading slash
+ ---
+ duration_ms: 0.092951
+ type: 'test'
+ ...
+# Subtest: localEndpoint brackets IPv6 hosts so URL parsers do not choke
+ok 2427 - localEndpoint brackets IPv6 hosts so URL parsers do not choke
+ ---
+ duration_ms: 0.082806
+ type: 'test'
+ ...
+# Subtest: materializer parity with live projector: native message ids
+ok 2428 - materializer parity with live projector: native message ids
+ ---
+ duration_ms: 7.18476
+ type: 'test'
+ ...
+# Subtest: a per-message provider overrides the exchange provider row by row
+ok 2429 - a per-message provider overrides the exchange provider row by row
+ ---
+ duration_ms: 0.62516
+ type: 'test'
+ ...
+# Subtest: materializer parity with live projector: fallback identity and chain
+ok 2430 - materializer parity with live projector: fallback identity and chain
+ ---
+ duration_ms: 1.273556
+ type: 'test'
+ ...
+# Subtest: materializer ignores malformed payloads and yields no rows
+ok 2431 - materializer ignores malformed payloads and yields no rows
+ ---
+ duration_ms: 0.16462
+ type: 'test'
+ ...
+# Subtest: materializer stamps hashed source-path provenance (raw path not stored)
+ok 2432 - materializer stamps hashed source-path provenance (raw path not stored)
+ ---
+ duration_ms: 0.722979
+ type: 'test'
+ ...
+# Subtest: materialized rows are stripped to the gateway schema columns
+ok 2433 - materialized rows are stripped to the gateway schema columns
+ ---
+ duration_ms: 0.5002
+ type: 'test'
+ ...
+# Subtest: backfill dedupe: a clean rerun writes zero new rows
+ok 2434 - backfill dedupe: a clean rerun writes zero new rows
+ ---
+ duration_ms: 0.775709
+ type: 'test'
+ ...
+# Subtest: backfill dedupe: partial prior write only backfills the missing parts
+ok 2435 - backfill dedupe: partial prior write only backfills the missing parts
+ ---
+ duration_ms: 0.35511
+ type: 'test'
+ ...
+# Subtest: backfill dedupe: matches legacy committed rows that predate part_id via message_id + part_index
+ok 2436 - backfill dedupe: matches legacy committed rows that predate part_id via message_id + part_index
+ ---
+ duration_ms: 0.378365
+ type: 'test'
+ ...
+# Subtest: backfill dedupe: a re-yielded item within the same run is skipped without re-committing
+ok 2437 - backfill dedupe: a re-yielded item within the same run is skipped without re-committing
+ ---
+ duration_ms: 0.558479
+ type: 'test'
+ ...
+# Subtest: backfill dedupe: a storage stub without the read surface skips dedupe entirely
+ok 2438 - backfill dedupe: a storage stub without the read surface skips dedupe entirely
+ ---
+ duration_ms: 0.295309
+ type: 'test'
+ ...
+# Subtest: backfill dedupe: an unreadable partition degrades to no dedupe rather than dropping rows
+ok 2439 - backfill dedupe: an unreadable partition degrades to no dedupe rather than dropping rows
+ ---
+ duration_ms: 0.288468
+ type: 'test'
+ ...
+# Subtest: backfill dedupe: rows pending in the spool are not re-materialized
+ok 2440 - backfill dedupe: rows pending in the spool are not re-materialized
+ ---
+ duration_ms: 0.301008
+ type: 'test'
+ ...
+# Subtest: backfill dedupe: only the spool-overlapping parts are skipped
+ok 2441 - backfill dedupe: only the spool-overlapping parts are skipped
+ ---
+ duration_ms: 0.202538
+ type: 'test'
+ ...
+# Subtest: backfill dedupe: spool dedupe also matches legacy rows via message_id + part_index
+ok 2442 - backfill dedupe: spool dedupe also matches legacy rows via message_id + part_index
+ ---
+ duration_ms: 0.20384
+ type: 'test'
+ ...
+# Subtest: backfill dedupe: committed and spooled part_ids are unioned into one seen-set
+ok 2443 - backfill dedupe: committed and spooled part_ids are unioned into one seen-set
+ ---
+ duration_ms: 0.203359
+ type: 'test'
+ ...
+# Subtest: backfill dedupe: an unreadable spool degrades to committed-only dedupe
+ok 2444 - backfill dedupe: an unreadable spool degrades to committed-only dedupe
+ ---
+ duration_ms: 0.243871
+ type: 'test'
+ ...
+# Subtest: backfill dedupe: a storage stub without readSpooledRows still dedupes against committed
+ok 2445 - backfill dedupe: a storage stub without readSpooledRows still dedupes against committed
+ ---
+ duration_ms: 0.892998
+ type: 'test'
+ ...
+# Subtest: string path: a stringified JSON array of input_image keeps the marker and drops the pixels
+ok 2446 - string path: a stringified JSON array of input_image keeps the marker and drops the pixels
+ ---
+ duration_ms: 2.143758
+ type: 'test'
+ ...
+# Subtest: tool_result string branch: the payload is stripped there too
+ok 2447 - tool_result string branch: the payload is stripped there too
+ ---
+ duration_ms: 0.413388
+ type: 'test'
+ ...
+# Subtest: a data URI embedded mid-prose loses only the payload, not the prose
+ok 2448 - a data URI embedded mid-prose loses only the payload, not the prose
+ ---
+ duration_ms: 0.244251
+ type: 'test'
+ ...
+# Subtest: multiple data URIs in one string are all stripped
+ok 2449 - multiple data URIs in one string are all stripped
+ ---
+ duration_ms: 0.23639
+ type: 'test'
+ ...
+# Subtest: a non-image mime type is stripped as well
+ok 2450 - a non-image mime type is stripped as well
+ ---
+ duration_ms: 0.326746
+ type: 'test'
+ ...
+# Subtest: mime case and mime parameters do not defeat the strip
+ok 2451 - mime case and mime parameters do not defeat the strip
+ ---
+ duration_ms: 0.384595
+ type: 'test'
+ ...
+# Subtest: a base64url payload (containing - and _) is fully stripped, no bare tail survives
+ok 2452 - a base64url payload (containing - and _) is fully stripped, no bare tail survives
+ ---
+ duration_ms: 0.533932
+ type: 'test'
+ ...
+# Subtest: stripping an already-stripped value is a no-op
+ok 2453 - stripping an already-stripped value is a no-op
+ ---
+ duration_ms: 0.266686
+ type: 'test'
+ ...
+# Subtest: content with no data URI passes through byte-identical
+ok 2454 - content with no data URI passes through byte-identical
+ ---
+ duration_ms: 0.571749
+ type: 'test'
+ ...
+# Subtest: the prefix class cannot splice a `data:` to an unrelated `;base64,`
+ok 2455 - the prefix class cannot splice a `data:` to an unrelated `;base64,`
+ ---
+ duration_ms: 0.563917
+ type: 'test'
+ ...
+# Subtest: the array path still drops image blocks entirely and keeps sibling text
+ok 2456 - the array path still drops image blocks entirely and keeps sibling text
+ ---
+ duration_ms: 0.243661
+ type: 'test'
+ ...
+# Subtest: an image-only array block still contributes no content_text
+ok 2457 - an image-only array block still contributes no content_text
+ ---
+ duration_ms: 0.208977
+ type: 'test'
+ ...
+# Subtest: thinking and error blocks are covered by the same pass
+ok 2458 - thinking and error blocks are covered by the same pass
+ ---
+ duration_ms: 0.277732
+ type: 'test'
+ ...
+# Subtest: the marker names the stripped mediatype, not a fixed `image`
+ok 2459 - the marker names the stripped mediatype, not a fixed `image`
+ ---
+ duration_ms: 0.196208
+ type: 'test'
+ ...
+# Subtest: a `+`-bearing mediatype reaches the marker intact
+ok 2460 - a `+`-bearing mediatype reaches the marker intact
+ ---
+ duration_ms: 0.182487
+ type: 'test'
+ ...
+# Subtest: an empty mediatype falls back to application/octet-stream
+ok 2461 - an empty mediatype falls back to application/octet-stream
+ ---
+ duration_ms: 0.186894
+ type: 'test'
+ ...
+# Subtest: mediatype case is recorded as it arrived on the wire
+ok 2462 - mediatype case is recorded as it arrived on the wire
+ ---
+ duration_ms: 0.178311
+ type: 'test'
+ ...
+# Subtest: mediatype parameters are carried into the marker
+ok 2463 - mediatype parameters are carried into the marker
+ ---
+ duration_ms: 0.176117
+ type: 'test'
+ ...
+# Subtest: several mediatypes in one string each keep their own
+ok 2464 - several mediatypes in one string each keep their own
+ ---
+ duration_ms: 0.246094
+ type: 'test'
+ ...
+# Subtest: the marker stays idempotent for every mediatype it can now emit
+ok 2465 - the marker stays idempotent for every mediatype it can now emit
+ ---
+ duration_ms: 1.883593
+ type: 'test'
+ ...
+# Subtest: stripping bounds the row: a multi-megabyte payload lands as a short value
+ok 2466 - stripping bounds the row: a multi-megabyte payload lands as a short value
+ ---
+ duration_ms: 99.81561
+ type: 'test'
+ ...
+# Subtest: the echoed mediatype cannot grow the marker past the regex cap
+ok 2467 - the echoed mediatype cannot grow the marker past the regex cap
+ ---
+ duration_ms: 0.245153
+ type: 'test'
+ ...
+# Subtest: POST adds a session id and reports ignored:true with the running total
+ok 2468 - POST adds a session id and reports ignored:true with the running total
+ ---
+ duration_ms: 26.52795
+ type: 'test'
+ ...
+# Subtest: DELETE removes a session id and reports ignored:false
+ok 2469 - DELETE removes a session id and reports ignored:false
+ ---
+ duration_ms: 7.870352
+ type: 'test'
+ ...
+# Subtest: both verbs are idempotent and .total tracks the set across a sequence
+ok 2470 - both verbs are idempotent and .total tracks the set across a sequence
+ ---
+ duration_ms: 24.924262
+ type: 'test'
+ ...
+# Subtest: session_id whitespace only gates non-emptiness; the STORED token is the raw value verbatim (R5)
+ok 2471 - session_id whitespace only gates non-emptiness; the STORED token is the raw value verbatim (R5)
+ ---
+ duration_ms: 4.483564
+ type: 'test'
+ ...
+# Subtest: a whitespace-padded session_id round-trips byte-identical to what an adapter would resolve and look up (R5)
+ok 2472 - a whitespace-padded session_id round-trips byte-identical to what an adapter would resolve and look up (R5)
+ ---
+ duration_ms: 6.807487
+ type: 'test'
+ ...
+# Subtest: 400 when session_id is missing, empty, or not a string
+ok 2473 - 400 when session_id is missing, empty, or not a string
+ ---
+ duration_ms: 7.598359
+ type: 'test'
+ ...
+# Subtest: 400 on malformed JSON
+ok 2474 - 400 on malformed JSON
+ ---
+ duration_ms: 2.878425
+ type: 'test'
+ ...
+# Subtest: 405 on an unsupported method for the ignore route
+ok 2475 - 405 on an unsupported method for the ignore route
+ ---
+ duration_ms: 6.209548
+ type: 'test'
+ ...
+# Subtest: 404 on an unknown /_hypaware/* control path
+ok 2476 - 404 on an unknown /_hypaware/* control path
+ ---
+ duration_ms: 2.854909
+ type: 'test'
+ ...
+# Subtest: 413 when the request body exceeds the size bound
+ok 2477 - 413 when the request body exceeds the size bound
+ ---
+ duration_ms: 2.860257
+ type: 'test'
+ ...
+# Subtest: a control request emits a structured ignore log carrying the running total
+ok 2478 - a control request emits a structured ignore log carrying the running total
+ ---
+ duration_ms: 2.048363
+ type: 'test'
+ ...
+# Subtest: isControlPath recognizes the reserved prefix at segment boundaries only
+ok 2479 - isControlPath recognizes the reserved prefix at segment boundaries only
+ ---
+ duration_ms: 0.109015
+ type: 'test'
+ ...
+# Subtest: records one entry per entrypoint, counting rows and stamping last-seen
+ok 2480 - records one entry per entrypoint, counting rows and stamping last-seen
+ ---
+ duration_ms: 1.656027
+ type: 'test'
+ ...
+# Subtest: the snapshot is most-recently-seen first, so a stale surface sinks
+ok 2481 - the snapshot is most-recently-seen first, so a stale surface sinks
+ ---
+ duration_ms: 0.257502
+ type: 'test'
+ ...
+# Subtest: rows with no entrypoint are ignored rather than bucketed under a placeholder
+ok 2482 - rows with no entrypoint are ignored rather than bucketed under a placeholder
+ ---
+ duration_ms: 0.878545
+ type: 'test'
+ ...
+# Subtest: a client-supplied entrypoint cannot grow the map without bound
+ok 2483 - a client-supplied entrypoint cannot grow the map without bound
+ ---
+ duration_ms: 0.310271
+ type: 'test'
+ ...
+# Subtest: seeing an old entrypoint again rescues it from eviction
+ok 2484 - seeing an old entrypoint again rescues it from eviction
+ ---
+ duration_ms: 0.181205
+ type: 'test'
+ ...
+# Subtest: an empty or malformed batch is a no-op, not a throw
+ok 2485 - an empty or malformed batch is a no-op, not a throw
+ ---
+ duration_ms: 0.091859
+ type: 'test'
+ ...
+# Subtest: a control-character entrypoint cannot forge lines or move the cursor
+ok 2486 - a control-character entrypoint cannot forge lines or move the cursor
+ ---
+ duration_ms: 0.234897
+ type: 'test'
+ ...
+# Subtest: an over-long entrypoint is clamped, so status.json stays small
+ok 2487 - an over-long entrypoint is clamped, so status.json stays small
+ ---
+ duration_ms: 0.352186
+ type: 'test'
+ ...
+# Subtest: an entrypoint of nothing but control bytes is skipped, not stored blank
+ok 2488 - an entrypoint of nothing but control bytes is skipped, not stored blank
+ ---
+ duration_ms: 0.318324
+ type: 'test'
+ ...
+# Subtest: the default cap is 32 distinct entrypoints
+ok 2489 - the default cap is 32 distinct entrypoints
+ ---
+ duration_ms: 0.541042
+ type: 'test'
+ ...
+# Subtest: a bidi override cannot reorder the rendered status line
+ok 2490 - a bidi override cannot reorder the rendered status line
+ ---
+ duration_ms: 0.23652
+ type: 'test'
+ ...
+# Subtest: invisible characters cannot dilute the eviction cap
+ok 2491 - invisible characters cannot dilute the eviction cap
+ ---
+ duration_ms: 6.27691
+ type: 'test'
+ ...
+# Subtest: clamping an astral entrypoint leaves a well-formed string
+ok 2492 - clamping an astral entrypoint leaves a well-formed string
+ ---
+ duration_ms: 0.171861
+ type: 'test'
+ ...
+# Subtest: the gateway+graph+connector chain activates in order and the connector registers its contract
+ok 2493 - the gateway+graph+connector chain activates in order and the connector registers its contract
+ ---
+ duration_ms: 78.726777
+ type: 'test'
+ ...
+# Subtest: host Repo node converges with the GitHub Repo node
+ok 2494 - host Repo node converges with the GitHub Repo node
+ ---
+ duration_ms: 1.77007
+ type: 'test'
+ ...
+# Subtest: host Commit node converges with the GitHub Commit node (full sha, any case)
+ok 2495 - host Commit node converges with the GitHub Commit node (full sha, any case)
+ ---
+ duration_ms: 0.235728
+ type: 'test'
+ ...
+# Subtest: host File node converges with the GitHub File node via owner/repo:relpath
+ok 2496 - host File node converges with the GitHub File node via owner/repo:relpath
+ ---
+ duration_ms: 0.464666
+ type: 'test'
+ ...
+# Subtest: host Commit -in-> Repo edge converges with the GitHub edge
+ok 2497 - host Commit -in-> Repo edge converges with the GitHub edge
+ ---
+ duration_ms: 1.551088
+ type: 'test'
+ ...
+# Subtest: worktrees of one repo converge on a single File node
+ok 2498 - worktrees of one repo converge on a single File node
+ ---
+ duration_ms: 0.366157
+ type: 'test'
+ ...
+# Subtest: contract carries its source/projector metadata
+ok 2499 - contract carries its source/projector metadata
+ ---
+ duration_ms: 0.966139
+ type: 'test'
+ ...
+# Subtest: PROJECTOR_VERSION is 2 after the additive Program/invoked rules
+ok 2500 - PROJECTOR_VERSION is 2 after the additive Program/invoked rules
+ ---
+ duration_ms: 0.13801
+ type: 'test'
+ ...
+# Subtest: Session rule builds a node keyed on session_id with pruned props
+ok 2501 - Session rule builds a node keyed on session_id with pruned props
+ ---
+ duration_ms: 1.19726
+ type: 'test'
+ ...
+# Subtest: node rules skip rows missing their natural key
+ok 2502 - node rules skip rows missing their natural key
+ ---
+ duration_ms: 0.202999
+ type: 'test'
+ ...
+# Subtest: Session rule with no optional fields builds null props
+ok 2503 - Session rule with no optional fields builds null props
+ ---
+ duration_ms: 0.222488
+ type: 'test'
+ ...
+# Subtest: File rule resolves file_path from file-touching tools only
+ok 2504 - File rule resolves file_path from file-touching tools only
+ ---
+ duration_ms: 0.293956
+ type: 'test'
+ ...
+# Subtest: File rule parses tool_args arriving as a JSON string, skipping malformed JSON
+ok 2505 - File rule parses tool_args arriving as a JSON string, skipping malformed JSON
+ ---
+ duration_ms: 0.228928
+ type: 'test'
+ ...
+# Subtest: File rule falls back to notebook_path
+ok 2506 - File rule falls back to notebook_path
+ ---
+ duration_ms: 0.139832
+ type: 'test'
+ ...
+# Subtest: touched edge wires Session and File node ids and skips partial rows
+ok 2507 - touched edge wires Session and File node ids and skips partial rows
+ ---
+ duration_ms: 0.578749
+ type: 'test'
+ ...
+# Subtest: via and used_model edges skip rows missing either endpoint
+ok 2508 - via and used_model edges skip rows missing either endpoint
+ ---
+ duration_ms: 0.455272
+ type: 'test'
+ ...
+# Subtest: toRow normalizes first_seen from Date and epoch-number timestamps
+ok 2509 - toRow normalizes first_seen from Date and epoch-number timestamps
+ ---
+ duration_ms: 0.455092
+ type: 'test'
+ ...
+# Subtest: numeric natural keys are stringified
+ok 2510 - numeric natural keys are stringified
+ ---
+ duration_ms: 0.136307
+ type: 'test'
+ ...
+# Subtest: the contract declares the aux rowFilter on attributes, and raw rules select it
+ok 2511 - the contract declares the aux rowFilter on attributes, and raw rules select it
+ ---
+ duration_ms: 0.312495
+ type: 'test'
+ ...
+# Subtest: the aux rowFilter drops aux-tagged rows and keeps real ones
+ok 2512 - the aux rowFilter drops aux-tagged rows and keeps real ones
+ ---
+ duration_ms: 0.145061
+ type: 'test'
+ ...
+# Subtest: non-aux rows pass through unchanged (attributes present but no aux_kind)
+ok 2513 - non-aux rows pass through unchanged (attributes present but no aux_kind)
+ ---
+ duration_ms: 0.128535
+ type: 'test'
+ ...
+# Subtest: Repo node keys on owner/repo derived from the git remote
+ok 2514 - Repo node keys on owner/repo derived from the git remote
+ ---
+ duration_ms: 0.301048
+ type: 'test'
+ ...
+# Subtest: Commit node keys on the full HEAD sha and rejects an abbreviated one
+ok 2515 - Commit node keys on the full HEAD sha and rejects an abbreviated one
+ ---
+ duration_ms: 0.200034
+ type: 'test'
+ ...
+# Subtest: File node re-keys an in-repo path to owner/repo:relpath (relpath case preserved)
+ok 2516 - File node re-keys an in-repo path to owner/repo:relpath (relpath case preserved)
+ ---
+ duration_ms: 0.236339
+ type: 'test'
+ ...
+# Subtest: File node falls back to the absolute path when it cannot be relativized
+ok 2517 - File node falls back to the absolute path when it cannot be relativized
+ ---
+ duration_ms: 0.209479
+ type: 'test'
+ ...
+# Subtest: File node falls back (does not mint a bogus bridge key) for a path that escapes the root via `..`
+ok 2518 - File node falls back (does not mint a bogus bridge key) for a path that escapes the root via `..`
+ ---
+ duration_ms: 0.176969
+ type: 'test'
+ ...
+# Subtest: File node still relativizes an in-repo `..` that stays inside the root
+ok 2519 - File node still relativizes an in-repo `..` that stays inside the root
+ ---
+ duration_ms: 0.198452
+ type: 'test'
+ ...
+# Subtest: touched edge re-keys its File endpoint identically to the File node
+ok 2520 - touched edge re-keys its File endpoint identically to the File node
+ ---
+ duration_ms: 0.173914
+ type: 'test'
+ ...
+# Subtest: Session -in-> Repo and Session -at-> Commit wire the bridge nodes
+ok 2521 - Session -in-> Repo and Session -at-> Commit wire the bridge nodes
+ ---
+ duration_ms: 0.251031
+ type: 'test'
+ ...
+# Subtest: Commit -in-> Repo is the second `in` edge and converges with the GitHub edge
+ok 2522 - Commit -in-> Repo is the second `in` edge and converges with the GitHub edge
+ ---
+ duration_ms: 0.169137
+ type: 'test'
+ ...
+# Subtest: Program/invoked rules select only tool_call rows from the two shell tools
+ok 2523 - Program/invoked rules select only tool_call rows from the two shell tools
+ ---
+ duration_ms: 0.187766
+ type: 'test'
+ ...
+# Subtest: Program node keys on the validity-gated basename(argv[0]) of the first command
+ok 2524 - Program node keys on the validity-gated basename(argv[0]) of the first command
+ ---
+ duration_ms: 0.567002
+ type: 'test'
+ ...
+# Subtest: Program node mints nothing when the facet cannot be cleanly bounded (fail-closed)
+ok 2525 - Program node mints nothing when the facet cannot be cleanly bounded (fail-closed)
+ ---
+ duration_ms: 0.155537
+ type: 'test'
+ ...
+# Subtest: Program node parses tool_args arriving as a JSON string
+ok 2526 - Program node parses tool_args arriving as a JSON string
+ ---
+ duration_ms: 0.198622
+ type: 'test'
+ ...
+# Subtest: invoked edge wires Session -> Program ids and carries no props
+ok 2527 - invoked edge wires Session -> Program ids and carries no props
+ ---
+ duration_ms: 0.183689
+ type: 'test'
+ ...
+# Subtest: invoked edge skips rows missing the session or an un-gateable program
+ok 2528 - invoked edge skips rows missing the session or an un-gateable program
+ ---
+ duration_ms: 0.123097
+ type: 'test'
+ ...
+# Subtest: Skill/ran rules declare the strict per-surface filters
+ok 2529 - Skill/ran rules declare the strict per-surface filters
+ ---
+ duration_ms: 0.259665
+ type: 'test'
+ ...
+# Subtest: Skill node from the Skill tool call keys on tool_args.skill
+ok 2530 - Skill node from the Skill tool call keys on tool_args.skill
+ ---
+ duration_ms: 0.198782
+ type: 'test'
+ ...
+# Subtest: Skill node from the marker keys on the base-directory basename
+ok 2531 - Skill node from the marker keys on the base-directory basename
+ ---
+ duration_ms: 0.209248
+ type: 'test'
+ ...
+# Subtest: Skill node from a slash command keys on the de-slashed name
+ok 2532 - Skill node from a slash command keys on the de-slashed name
+ ---
+ duration_ms: 0.178221
+ type: 'test'
+ ...
+# Subtest: Skill node from a Codex exec_command read keys on the .codex/skills//SKILL.md path
+ok 2533 - Skill node from a Codex exec_command read keys on the .codex/skills//SKILL.md path
+ ---
+ duration_ms: 0.310031
+ type: 'test'
+ ...
+# Subtest: all four surfaces converge on one Skill node id (cross-surface identity)
+ok 2534 - all four surfaces converge on one Skill node id (cross-surface identity)
+ ---
+ duration_ms: 0.22339
+ type: 'test'
+ ...
+# Subtest: ran edges wire Session -> Skill with exactly their own dispatch flag
+ok 2535 - ran edges wire Session -> Skill with exactly their own dispatch flag
+ ---
+ duration_ms: 0.407849
+ type: 'test'
+ ...
+# Subtest: ran edges skip rows missing the session or an un-gateable skill
+ok 2536 - ran edges skip rows missing the session or an un-gateable skill
+ ---
+ duration_ms: 0.136307
+ type: 'test'
+ ...
+# Subtest: false-positive matrix: near-miss signals mint nothing
+ok 2537 - false-positive matrix: near-miss signals mint nothing
+ ---
+ duration_ms: 0.158601
+ type: 'test'
+ ...
+# Subtest: aux-tagged skill rows are excluded by the contract rowFilter
+ok 2538 - aux-tagged skill rows are excluded by the contract rowFilter
+ ---
+ duration_ms: 0.106412
+ type: 'test'
+ ...
+# Subtest: commandStringFrom reads Bash.command and exec_command.cmd (fallback command)
+ok 2539 - commandStringFrom reads Bash.command and exec_command.cmd (fallback command)
+ ---
+ duration_ms: 1.384093
+ type: 'test'
+ ...
+# Subtest: commandStringFrom parses tool_args arriving as a JSON string
+ok 2540 - commandStringFrom parses tool_args arriving as a JSON string
+ ---
+ duration_ms: 0.198291
+ type: 'test'
+ ...
+# Subtest: commandStringFrom is null for non-shell tools, bad args, or a missing/non-string command
+ok 2541 - commandStringFrom is null for non-shell tools, bad args, or a missing/non-string command
+ ---
+ duration_ms: 0.146814
+ type: 'test'
+ ...
+# Subtest: programFrom: bare command
+ok 2542 - programFrom: bare command
+ ---
+ duration_ms: 0.414801
+ type: 'test'
+ ...
+# Subtest: programFrom: single token
+ok 2543 - programFrom: single token
+ ---
+ duration_ms: 0.165401
+ type: 'test'
+ ...
+# Subtest: programFrom: absolute path is basenamed
+ok 2544 - programFrom: absolute path is basenamed
+ ---
+ duration_ms: 0.09893
+ type: 'test'
+ ...
+# Subtest: programFrom: relative path is basenamed
+ok 2545 - programFrom: relative path is basenamed
+ ---
+ duration_ms: 0.229209
+ type: 'test'
+ ...
+# Subtest: programFrom: uppercase is lowercased
+ok 2546 - programFrom: uppercase is lowercased
+ ---
+ duration_ms: 0.085279
+ type: 'test'
+ ...
+# Subtest: programFrom: pathed + cased converge
+ok 2547 - programFrom: pathed + cased converge
+ ---
+ duration_ms: 0.274537
+ type: 'test'
+ ...
+# Subtest: programFrom: pipe takes the head
+ok 2548 - programFrom: pipe takes the head
+ ---
+ duration_ms: 0.317753
+ type: 'test'
+ ...
+# Subtest: programFrom: && takes the head
+ok 2549 - programFrom: && takes the head
+ ---
+ duration_ms: 0.141194
+ type: 'test'
+ ...
+# Subtest: programFrom: || takes the head
+ok 2550 - programFrom: || takes the head
+ ---
+ duration_ms: 0.091339
+ type: 'test'
+ ...
+# Subtest: programFrom: ; takes the head
+ok 2551 - programFrom: ; takes the head
+ ---
+ duration_ms: 0.075955
+ type: 'test'
+ ...
+# Subtest: programFrom: newline takes the head
+ok 2552 - programFrom: newline takes the head
+ ---
+ duration_ms: 0.064758
+ type: 'test'
+ ...
+# Subtest: programFrom: single & (background) does not cut
+ok 2553 - programFrom: single & (background) does not cut
+ ---
+ duration_ms: 0.064007
+ type: 'test'
+ ...
+# Subtest: programFrom: quoted connector after argv[0] is head-safe
+ok 2554 - programFrom: quoted connector after argv[0] is head-safe
+ ---
+ duration_ms: 0.059049
+ type: 'test'
+ ...
+# Subtest: programFrom: leading subshell paren stripped
+ok 2555 - programFrom: leading subshell paren stripped
+ ---
+ duration_ms: 0.060201
+ type: 'test'
+ ...
+# Subtest: programFrom: paren with no space stripped
+ok 2556 - programFrom: paren with no space stripped
+ ---
+ duration_ms: 0.059451
+ type: 'test'
+ ...
+# Subtest: programFrom: single env assignment skipped
+ok 2557 - programFrom: single env assignment skipped
+ ---
+ duration_ms: 0.057687
+ type: 'test'
+ ...
+# Subtest: programFrom: multiple env assignments skipped
+ok 2558 - programFrom: multiple env assignments skipped
+ ---
+ duration_ms: 1.055885
+ type: 'test'
+ ...
+# Subtest: programFrom: sudo unwrapped
+ok 2559 - programFrom: sudo unwrapped
+ ---
+ duration_ms: 0.130869
+ type: 'test'
+ ...
+# Subtest: programFrom: sudo no-arg flag skipped
+ok 2560 - programFrom: sudo no-arg flag skipped
+ ---
+ duration_ms: 0.238923
+ type: 'test'
+ ...
+# Subtest: programFrom: env wrapper + assignment skipped
+ok 2561 - programFrom: env wrapper + assignment skipped
+ ---
+ duration_ms: 0.077368
+ type: 'test'
+ ...
+# Subtest: programFrom: nohup unwrapped
+ok 2562 - programFrom: nohup unwrapped
+ ---
+ duration_ms: 0.063016
+ type: 'test'
+ ...
+# Subtest: programFrom: nice + numeric flag arg skipped
+ok 2563 - programFrom: nice + numeric flag arg skipped
+ ---
+ duration_ms: 0.067152
+ type: 'test'
+ ...
+# Subtest: programFrom: time keyword unwrapped
+ok 2564 - programFrom: time keyword unwrapped
+ ---
+ duration_ms: 0.051248
+ type: 'test'
+ ...
+# Subtest: programFrom: command builtin unwrapped
+ok 2565 - programFrom: command builtin unwrapped
+ ---
+ duration_ms: 0.044598
+ type: 'test'
+ ...
+# Subtest: programFrom: stdbuf flags skipped
+ok 2566 - programFrom: stdbuf flags skipped
+ ---
+ duration_ms: 0.049415
+ type: 'test'
+ ...
+# Subtest: programFrom: timeout numeric duration skipped
+ok 2567 - programFrom: timeout numeric duration skipped
+ ---
+ duration_ms: 0.044998
+ type: 'test'
+ ...
+# Subtest: programFrom: timeout suffix duration skipped
+ok 2568 - programFrom: timeout suffix duration skipped
+ ---
+ duration_ms: 0.044768
+ type: 'test'
+ ...
+# Subtest: programFrom: stacked wrappers unwrapped
+ok 2569 - programFrom: stacked wrappers unwrapped
+ ---
+ duration_ms: 0.041904
+ type: 'test'
+ ...
+# Subtest: programFrom: wrapper of pathed program
+ok 2570 - programFrom: wrapper of pathed program
+ ---
+ duration_ms: 0.03912
+ type: 'test'
+ ...
+# Subtest: programFrom: sudo -u with a separate-token value consumes the pair
+ok 2571 - programFrom: sudo -u with a separate-token value consumes the pair
+ ---
+ duration_ms: 0.043045
+ type: 'test'
+ ...
+# Subtest: programFrom: env -C with a separate-token value consumes the pair
+ok 2572 - programFrom: env -C with a separate-token value consumes the pair
+ ---
+ duration_ms: 0.06577
+ type: 'test'
+ ...
+# Subtest: programFrom: timeout --signal with a separate-token value consumes the pair, then the numeric duration
+ok 2573 - programFrom: timeout --signal with a separate-token value consumes the pair, then the numeric duration
+ ---
+ duration_ms: 0.048153
+ type: 'test'
+ ...
+# Subtest: programFrom: stdbuf -o with a separate-token value consumes the pair
+ok 2574 - programFrom: stdbuf -o with a separate-token value consumes the pair
+ ---
+ duration_ms: 0.05961
+ type: 'test'
+ ...
+# Subtest: programFrom: nice -n with a separate-token value consumes the pair
+ok 2575 - programFrom: nice -n with a separate-token value consumes the pair
+ ---
+ duration_ms: 0.045479
+ type: 'test'
+ ...
+# Subtest: programFrom: sudo -u attached value
+ok 2576 - programFrom: sudo -u attached value
+ ---
+ duration_ms: 0.040562
+ type: 'test'
+ ...
+# Subtest: programFrom: env --chdir= attached value
+ok 2577 - programFrom: env --chdir= attached value
+ ---
+ duration_ms: 0.040822
+ type: 'test'
+ ...
+# Subtest: programFrom: sudo with an unrecognized flag+value shape mints nothing
+ok 2578 - programFrom: sudo with an unrecognized flag+value shape mints nothing
+ ---
+ duration_ms: 0.048854
+ type: 'test'
+ ...
+# Subtest: programFrom: env with an unrecognized flag+value shape mints nothing
+ok 2579 - programFrom: env with an unrecognized flag+value shape mints nothing
+ ---
+ duration_ms: 0.057878
+ type: 'test'
+ ...
+# Subtest: programFrom: sudo -- ends options, next token is argv[0]
+ok 2580 - programFrom: sudo -- ends options, next token is argv[0]
+ ---
+ duration_ms: 0.055825
+ type: 'test'
+ ...
+# Subtest: programFrom: env -- ends options
+ok 2581 - programFrom: env -- ends options
+ ---
+ duration_ms: 0.055334
+ type: 'test'
+ ...
+# Subtest: programFrom: bash -lc unwraps the inner command
+ok 2582 - programFrom: bash -lc unwraps the inner command
+ ---
+ duration_ms: 0.103988
+ type: 'test'
+ ...
+# Subtest: programFrom: sh -c unwraps
+ok 2583 - programFrom: sh -c unwraps
+ ---
+ duration_ms: 0.083957
+ type: 'test'
+ ...
+# Subtest: programFrom: zsh -c unwraps
+ok 2584 - programFrom: zsh -c unwraps
+ ---
+ duration_ms: 0.061413
+ type: 'test'
+ ...
+# Subtest: programFrom: bash -c single-quoted inner
+ok 2585 - programFrom: bash -c single-quoted inner
+ ---
+ duration_ms: 0.063056
+ type: 'test'
+ ...
+# Subtest: programFrom: inner command with its own connector takes head
+ok 2586 - programFrom: inner command with its own connector takes head
+ ---
+ duration_ms: 0.066281
+ type: 'test'
+ ...
+# Subtest: programFrom: inner command with a pipe takes head
+ok 2587 - programFrom: inner command with a pipe takes head
+ ---
+ duration_ms: 0.062955
+ type: 'test'
+ ...
+# Subtest: programFrom: bash script.sh (no -c) keeps bash
+ok 2588 - programFrom: bash script.sh (no -c) keeps bash
+ ---
+ duration_ms: 0.055344
+ type: 'test'
+ ...
+# Subtest: programFrom: bash with no -c keeps bash
+ok 2589 - programFrom: bash with no -c keeps bash
+ ---
+ duration_ms: 0.054983
+ type: 'test'
+ ...
+# Subtest: programFrom: nested bash -lc unwraps to depth 2
+ok 2590 - programFrom: nested bash -lc unwraps to depth 2
+ ---
+ duration_ms: 0.062615
+ type: 'test'
+ ...
+# Subtest: programFrom: empty string mints nothing
+ok 2591 - programFrom: empty string mints nothing
+ ---
+ duration_ms: 0.049304
+ type: 'test'
+ ...
+# Subtest: programFrom: whitespace mints nothing
+ok 2592 - programFrom: whitespace mints nothing
+ ---
+ duration_ms: 0.050988
+ type: 'test'
+ ...
+# Subtest: programFrom: all-numeric token mints nothing
+ok 2593 - programFrom: all-numeric token mints nothing
+ ---
+ duration_ms: 0.055003
+ type: 'test'
+ ...
+# Subtest: programFrom: token with a space char fails the gate
+ok 2594 - programFrom: token with a space char fails the gate
+ ---
+ duration_ms: 0.054432
+ type: 'test'
+ ...
+# Subtest: programFrom: token with illegal char fails the gate
+ok 2595 - programFrom: token with illegal char fails the gate
+ ---
+ duration_ms: 0.055384
+ type: 'test'
+ ...
+# Subtest: programFrom: leading connector mints nothing
+ok 2596 - programFrom: leading connector mints nothing
+ ---
+ duration_ms: 0.058969
+ type: 'test'
+ ...
+# Subtest: programFrom rejects an over-long token (PROGRAM_RE cap)
+ok 2597 - programFrom rejects an over-long token (PROGRAM_RE cap)
+ ---
+ duration_ms: 0.137649
+ type: 'test'
+ ...
+# Subtest: programFrom returns null for non-string / null input (from commandStringFrom)
+ok 2598 - programFrom returns null for non-string / null input (from commandStringFrom)
+ ---
+ duration_ms: 0.107284
+ type: 'test'
+ ...
+# Subtest: the depth cap stops shell -c recursion at 2 levels (keeps the innermost shell)
+ok 2599 - the depth cap stops shell -c recursion at 2 levels (keeps the innermost shell)
+ ---
+ duration_ms: 0.538378
+ type: 'test'
+ ...
+# Subtest: skillFromToolArgs reads tool_args.skill, parsed or as a JSON string
+ok 2600 - skillFromToolArgs reads tool_args.skill, parsed or as a JSON string
+ ---
+ duration_ms: 0.173414
+ type: 'test'
+ ...
+# Subtest: skillFromToolArgs is null for missing/bad args or an un-gateable name
+ok 2601 - skillFromToolArgs is null for missing/bad args or an un-gateable name
+ ---
+ duration_ms: 0.178651
+ type: 'test'
+ ...
+# Subtest: skillFromMarker takes the basename of the offset-0 base directory
+ok 2602 - skillFromMarker takes the basename of the offset-0 base directory
+ ---
+ duration_ms: 0.199534
+ type: 'test'
+ ...
+# Subtest: skillFromMarker trims a trailing slash and unwraps a SKILL.md file path
+ok 2603 - skillFromMarker trims a trailing slash and unwraps a SKILL.md file path
+ ---
+ duration_ms: 0.103357
+ type: 'test'
+ ...
+# Subtest: skillFromMarker rejects anything not anchored at offset 0
+ok 2604 - skillFromMarker rejects anything not anchored at offset 0
+ ---
+ duration_ms: 0.093181
+ type: 'test'
+ ...
+# Subtest: skillFromMarker fails closed on an un-gateable basename
+ok 2605 - skillFromMarker fails closed on an un-gateable basename
+ ---
+ duration_ms: 0.103086
+ type: 'test'
+ ...
+# Subtest: skillFromMarker resolves a base directory containing a space to the correct trailing basename
+ok 2606 - skillFromMarker resolves a base directory containing a space to the correct trailing basename
+ ---
+ duration_ms: 0.091129
+ type: 'test'
+ ...
+# Subtest: skillFromMarker still mints nothing for a marker not on the first line (anchor not loosened)
+ok 2607 - skillFromMarker still mints nothing for a marker not on the first line (anchor not loosened)
+ ---
+ duration_ms: 0.084198
+ type: 'test'
+ ...
+# Subtest: skillFromSlash reads an offset-0 command tag, stripping a leading /
+ok 2608 - skillFromSlash reads an offset-0 command tag, stripping a leading /
+ ---
+ duration_ms: 0.175917
+ type: 'test'
+ ...
+# Subtest: skillFromSlash drops Claude Code built-in commands
+ok 2609 - skillFromSlash drops Claude Code built-in commands
+ ---
+ duration_ms: 0.158882
+ type: 'test'
+ ...
+# Subtest: skillFromSlash rejects tags not anchored at offset 0 and malformed tags
+ok 2610 - skillFromSlash rejects tags not anchored at offset 0 and malformed tags
+ ---
+ duration_ms: 0.094674
+ type: 'test'
+ ...
+# Subtest: skillFromCodexRead: sed of a user's home path (LLP 0075 example)
+ok 2611 - skillFromCodexRead: sed of a user's home path (LLP 0075 example)
+ ---
+ duration_ms: 0.156247
+ type: 'test'
+ ...
+# Subtest: skillFromCodexRead: bare read
+ok 2612 - skillFromCodexRead: bare read
+ ---
+ duration_ms: 0.102266
+ type: 'test'
+ ...
+# Subtest: skillFromCodexRead: ~-prefixed path
+ok 2613 - skillFromCodexRead: ~-prefixed path
+ ---
+ duration_ms: 0.077047
+ type: 'test'
+ ...
+# Subtest: skillFromCodexRead: double-quoted path
+ok 2614 - skillFromCodexRead: double-quoted path
+ ---
+ duration_ms: 0.068594
+ type: 'test'
+ ...
+# Subtest: skillFromCodexRead: single-quoted path
+ok 2615 - skillFromCodexRead: single-quoted path
+ ---
+ duration_ms: 0.067732
+ type: 'test'
+ ...
+# Subtest: skillFromCodexRead: namespaced skill name kept
+ok 2616 - skillFromCodexRead: namespaced skill name kept
+ ---
+ duration_ms: 0.080542
+ type: 'test'
+ ...
+# Subtest: skillFromCodexRead: head is a read tool
+ok 2617 - skillFromCodexRead: head is a read tool
+ ---
+ duration_ms: 0.083386
+ type: 'test'
+ ...
+# Subtest: skillFromCodexRead: grep is a read tool
+ok 2618 - skillFromCodexRead: grep is a read tool
+ ---
+ duration_ms: 0.077878
+ type: 'test'
+ ...
+# Subtest: skillFromCodexRead: Claude path (.claude, not .codex) mints nothing
+ok 2619 - skillFromCodexRead: Claude path (.claude, not .codex) mints nothing
+ ---
+ duration_ms: 0.119501
+ type: 'test'
+ ...
+# Subtest: skillFromCodexRead: not reading SKILL.md itself mints nothing
+ok 2620 - skillFromCodexRead: not reading SKILL.md itself mints nothing
+ ---
+ duration_ms: 0.077668
+ type: 'test'
+ ...
+# Subtest: skillFromCodexRead: a bare directory listing (no SKILL.md) mints nothing
+ok 2621 - skillFromCodexRead: a bare directory listing (no SKILL.md) mints nothing
+ ---
+ duration_ms: 0.054933
+ type: 'test'
+ ...
+# Subtest: skillFromCodexRead: no .codex/skills path at all mints nothing
+ok 2622 - skillFromCodexRead: no .codex/skills path at all mints nothing
+ ---
+ duration_ms: 0.050257
+ type: 'test'
+ ...
+# Subtest: skillFromCodexRead: name containing a space fails to close the match
+ok 2623 - skillFromCodexRead: name containing a space fails to close the match
+ ---
+ duration_ms: 0.048323
+ type: 'test'
+ ...
+# Subtest: skillFromCodexRead: empty string mints nothing
+ok 2624 - skillFromCodexRead: empty string mints nothing
+ ---
+ duration_ms: 0.076105
+ type: 'test'
+ ...
+# Subtest: skillFromCodexRead: echo (non-read command) naming the path mints nothing
+ok 2625 - skillFromCodexRead: echo (non-read command) naming the path mints nothing
+ ---
+ duration_ms: 0.064808
+ type: 'test'
+ ...
+# Subtest: skillFromCodexRead: rm (non-read command) naming the path mints nothing
+ok 2626 - skillFromCodexRead: rm (non-read command) naming the path mints nothing
+ ---
+ duration_ms: 0.054723
+ type: 'test'
+ ...
+# Subtest: skillFromCodexRead composes with commandStringFrom (the exec_command wire shape)
+ok 2627 - skillFromCodexRead composes with commandStringFrom (the exec_command wire shape)
+ ---
+ duration_ms: 0.110928
+ type: 'test'
+ ...
+# Subtest: skillFromCodexRead fails closed on an un-gateable captured name and non-string input
+ok 2628 - skillFromCodexRead fails closed on an un-gateable captured name and non-string input
+ ---
+ duration_ms: 0.088535
+ type: 'test'
+ ...
+# Subtest: skillFromCodexRead fails closed on nested/system skill directory layouts (accepted exclusion, not widened)
+ok 2629 - skillFromCodexRead fails closed on nested/system skill directory layouts (accepted exclusion, not widened)
+ ---
+ duration_ms: 0.068725
+ type: 'test'
+ ...
+# Subtest: SKILL_NAME_RE gates to a bounded verbatim-name domain
+ok 2630 - SKILL_NAME_RE gates to a bounded verbatim-name domain
+ ---
+ duration_ms: 0.123818
+ type: 'test'
+ ...
+# Subtest: PROGRAM_RE gates to a bounded, lowercased basename domain
+ok 2631 - PROGRAM_RE gates to a bounded, lowercased basename domain
+ ---
+ duration_ms: 0.106471
+ type: 'test'
+ ...
+# Subtest: compileConfig defaults listen to the fixed well-known port
+ok 2632 - compileConfig defaults listen to the fixed well-known port
+ ---
+ duration_ms: 0.677019
+ type: 'test'
+ ...
+# Subtest: compileConfig marks an explicit listen as configured
+ok 2633 - compileConfig marks an explicit listen as configured
+ ---
+ duration_ms: 0.101955
+ type: 'test'
+ ...
+# Subtest: a defaulted listen falls back to an ephemeral bind on EADDRINUSE
+ok 2634 - a defaulted listen falls back to an ephemeral bind on EADDRINUSE
+ ---
+ duration_ms: 0.755167
+ type: 'test'
+ ...
+# Subtest: a clean default bind never signals fallback
+ok 2635 - a clean default bind never signals fallback
+ ---
+ duration_ms: 0.16459
+ type: 'test'
+ ...
+# Subtest: a configured listen propagates EADDRINUSE instead of falling back
+ok 2636 - a configured listen propagates EADDRINUSE instead of falling back
+ ---
+ duration_ms: 0.495904
+ type: 'test'
+ ...
+# Subtest: a defaulted listen propagates non-EADDRINUSE bind errors
+ok 2637 - a defaulted listen propagates non-EADDRINUSE bind errors
+ ---
+ duration_ms: 0.167665
+ type: 'test'
+ ...
+# Subtest: ai_gateway_messages schema exposes the gateway message columns
+ok 2638 - ai_gateway_messages schema exposes the gateway message columns
+ ---
+ duration_ms: 2.526991
+ type: 'test'
+ ...
+# Subtest: projectExchange returns zero rows when no projector is registered
+ok 2639 - projectExchange returns zero rows when no projector is registered
+ ---
+ duration_ms: 0.478127
+ type: 'test'
+ ...
+# Subtest: projectExchange returns zero rows when no projector matches
+ok 2640 - projectExchange returns zero rows when no projector matches
+ ---
+ duration_ms: 0.230651
+ type: 'test'
+ ...
+# Subtest: first successful projector wins, sorted by descending priority then registration order
+ok 2641 - first successful projector wins, sorted by descending priority then registration order
+ ---
+ duration_ms: 4.709308
+ type: 'test'
+ ...
+# Subtest: throwing projectors are skipped and the next matching projector wins
+ok 2642 - throwing projectors are skipped and the next matching projector wins
+ ---
+ duration_ms: 0.433148
+ type: 'test'
+ ...
+# Subtest: projector returning undefined or an empty messages array is skipped
+ok 2643 - projector returning undefined or an empty messages array is skipped
+ ---
+ duration_ms: 0.402933
+ type: 'test'
+ ...
+# Subtest: a usage-policy drop is terminal: dispatch stops, writes no row, and is logged as a drop (not no_projector_match)
+ok 2644 - a usage-policy drop is terminal: dispatch stops, writes no row, and is logged as a drop (not no_projector_match)
+ ---
+ duration_ms: 0.250451
+ type: 'test'
+ ...
+# Subtest: a bare undefined decline still falls through to the next matching projector (only the drop sentinel is terminal)
+ok 2645 - a bare undefined decline still falls through to the next matching projector (only the drop sentinel is terminal)
+ ---
+ duration_ms: 0.403633
+ type: 'test'
+ ...
+# Subtest: projector returning an invalid shape is skipped and the next one is tried
+ok 2646 - projector returning an invalid shape is skipped and the next one is tried
+ ---
+ duration_ms: 0.703479
+ type: 'test'
+ ...
+# Subtest: all projectors failing returns zero rows and warns once per failure
+ok 2647 - all projectors failing returns zero rows and warns once per failure
+ ---
+ duration_ms: 0.501523
+ type: 'test'
+ ...
+# Subtest: skipping a non-matching projector does not call its project()
+ok 2648 - skipping a non-matching projector does not call its project()
+ ---
+ duration_ms: 0.419338
+ type: 'test'
+ ...
+# Subtest: projector-supplied message_id and previous_message_id are preserved
+ok 2649 - projector-supplied message_id and previous_message_id are preserved
+ ---
+ duration_ms: 0.323942
+ type: 'test'
+ ...
+# Subtest: supplied message_id without history gets the immediate predecessor as previous_message_id
+ok 2650 - supplied message_id without history gets the immediate predecessor as previous_message_id
+ ---
+ duration_ms: 0.271493
+ type: 'test'
+ ...
+# Subtest: fallback identity stamps gateway.identity_source and a linear previous_message_id chain
+ok 2651 - fallback identity stamps gateway.identity_source and a linear previous_message_id chain
+ ---
+ duration_ms: 0.326045
+ type: 'test'
+ ...
+# Subtest: fallback message_id ignores cache_control so identity is stable across replays
+ok 2652 - fallback message_id ignores cache_control so identity is stable across replays
+ ---
+ duration_ms: 0.166983
+ type: 'test'
+ ...
+# Subtest: fallback message_id is scoped by agent_id so subagents do not collide on shared content
+ok 2653 - fallback message_id is scoped by agent_id so subagents do not collide on shared content
+ ---
+ duration_ms: 0.110198
+ type: 'test'
+ ...
+# Subtest: previous_message_id chains are scoped per (conversation_id ?? session_id, agent_id)
+ok 2654 - previous_message_id chains are scoped per (conversation_id ?? session_id, agent_id)
+ ---
+ duration_ms: 0.405356
+ type: 'test'
+ ...
+# Subtest: session_id is the partition key; conversation_id is null for Claude, the thread for Codex
+ok 2655 - session_id is the partition key; conversation_id is null for Claude, the thread for Codex
+ ---
+ duration_ms: 0.28948
+ type: 'test'
+ ...
+# Subtest: dispatcher threads a working isSessionIgnored predicate into the projector ctx
+ok 2656 - dispatcher threads a working isSessionIgnored predicate into the projector ctx
+ ---
+ duration_ms: 0.298193
+ type: 'test'
+ ...
+# Subtest: projector ctx defaults isSessionIgnored to a false predicate when none is supplied
+ok 2657 - projector ctx defaults isSessionIgnored to a false predicate when none is supplied
+ ---
+ duration_ms: 0.221236
+ type: 'test'
+ ...
+# Subtest: a projection without session_id is rejected as an invalid shape
+ok 2658 - a projection without session_id is rejected as an invalid shape
+ ---
+ duration_ms: 0.268058
+ type: 'test'
+ ...
+# Subtest: attributes.gateway carries exchange provenance and dev_run_id
+ok 2659 - attributes.gateway carries exchange provenance and dev_run_id
+ ---
+ duration_ms: 0.231372
+ type: 'test'
+ ...
+# Subtest: row output is stripped to the schema (no extra fields leak)
+ok 2660 - row output is stripped to the schema (no extra fields leak)
+ ---
+ duration_ms: 0.908531
+ type: 'test'
+ ...
+# Subtest: a multi-block usage-bearing message stamps usage on only the last part
+ok 2661 - a multi-block usage-bearing message stamps usage on only the last part
+ ---
+ duration_ms: 0.318154
+ type: 'test'
+ ...
+# Subtest: two Codex threads sharing a session_id keep separate start time and tool lookup
+ok 2662 - two Codex threads sharing a session_id keep separate start time and tool lookup
+ ---
+ duration_ms: 0.492228
+ type: 'test'
+ ...
+# Subtest: per-message model wins over the exchange model; absent it falls back to the exchange model
+ok 2663 - per-message model wins over the exchange model; absent it falls back to the exchange model
+ ---
+ duration_ms: 0.240485
+ type: 'test'
+ ...
+# Subtest: restart replay: seeds seen-set from committed part_ids so prior history re-emits zero rows
+ok 2664 - restart replay: seeds seen-set from committed part_ids so prior history re-emits zero rows
+ ---
+ duration_ms: 0.667545
+ type: 'test'
+ ...
+# Subtest: restart replay: seeding scans each session lazily and at most once per listener
+ok 2665 - restart replay: seeding scans each session lazily and at most once per listener
+ ---
+ duration_ms: 0.286105
+ type: 'test'
+ ...
+# Subtest: seeding: sessions with no committed rows share one index build and skip the per-session scan
+ok 2666 - seeding: sessions with no committed rows share one index build and skip the per-session scan
+ ---
+ duration_ms: 0.392266
+ type: 'test'
+ ...
+# Subtest: committed-session index: a miss past the rebuild window triggers exactly one rebuild
+ok 2667 - committed-session index: a miss past the rebuild window triggers exactly one rebuild
+ ---
+ duration_ms: 0.989584
+ type: 'test'
+ ...
+# Subtest: committed-session index: N concurrent fresh-session misses past the rebuild window share one rebuild
+ok 2668 - committed-session index: N concurrent fresh-session misses past the rebuild window share one rebuild
+ ---
+ duration_ms: 0.550257
+ type: 'test'
+ ...
+# Subtest: committed-session index: a scan that throws degrades the index instead of wedging it
+ok 2669 - committed-session index: a scan that throws degrades the index instead of wedging it
+ ---
+ duration_ms: 15.318362
+ type: 'test'
+ ...
+# Subtest: committed-session index: a scan slower than the rebuild window still serves cache hits
+ok 2670 - committed-session index: a scan slower than the rebuild window still serves cache hits
+ ---
+ duration_ms: 0.876172
+ type: 'test'
+ ...
+# Subtest: restart replay: concurrent first exchanges for one session seed once and emit no duplicates
+ok 2671 - restart replay: concurrent first exchanges for one session seed once and emit no duplicates
+ ---
+ duration_ms: 0.326136
+ type: 'test'
+ ...
+# Subtest: restart replay: a different session is not deduped against another session rows
+ok 2672 - restart replay: a different session is not deduped against another session rows
+ ---
+ duration_ms: 0.26341
+ type: 'test'
+ ...
+# Subtest: restart replay: with no storage, behavior is unchanged (committed history is not seeded)
+ok 2673 - restart replay: with no storage, behavior is unchanged (committed history is not seeded)
+ ---
+ duration_ms: 0.207385
+ type: 'test'
+ ...
+# Subtest: restart replay: a throwing storage degrades to not-seeded and never drops rows
+ok 2674 - restart replay: a throwing storage degrades to not-seeded and never drops rows
+ ---
+ duration_ms: 0.195367
+ type: 'test'
+ ...
+# Subtest: committed-session index: a build that could not scan is not cached as "no committed rows"
+ok 2675 - committed-session index: a build that could not scan is not cached as "no committed rows"
+ ---
+ duration_ms: 0.444606
+ type: 'test'
+ ...
+# Subtest: seed failure: a storage that breaks its discover contract loses no rows and does not poison the session memo
+ok 2676 - seed failure: a storage that breaks its discover contract loses no rows and does not poison the session memo
+ ---
+ duration_ms: 0.598419
+ type: 'test'
+ ...
+# Subtest: forwardHeaders strips x-hypaware-* request headers before proxying upstream
+ok 2677 - forwardHeaders strips x-hypaware-* request headers before proxying upstream
+ ---
+ duration_ms: 0.879777
+ type: 'test'
+ ...
+# Subtest: compileUpstreams sorts by descending priority, then longer prefix, then registration order
+ok 2678 - compileUpstreams sorts by descending priority, then longer prefix, then registration order
+ ---
+ duration_ms: 0.782208
+ type: 'test'
+ ...
+# Subtest: compileUpstreams rejects non-http(s) base_url
+ok 2679 - compileUpstreams rejects non-http(s) base_url
+ ---
+ duration_ms: 0.363402
+ type: 'test'
+ ...
+# Subtest: compileUpstreams rejects unparseable base_url
+ok 2680 - compileUpstreams rejects unparseable base_url
+ ---
+ duration_ms: 0.143127
+ type: 'test'
+ ...
+# Subtest: matchUpstream invokes match() and returns the first upstream whose match() is true
+ok 2681 - matchUpstream invokes match() and returns the first upstream whose match() is true
+ ---
+ duration_ms: 0.28951
+ type: 'test'
+ ...
+# Subtest: matchUpstream short-circuits on the first match - lower-priority match() is not called
+ok 2682 - matchUpstream short-circuits on the first match - lower-priority match() is not called
+ ---
+ duration_ms: 0.167915
+ type: 'test'
+ ...
+# Subtest: matchUpstream ties on priority are broken by registration order
+ok 2683 - matchUpstream ties on priority are broken by registration order
+ ---
+ duration_ms: 0.204681
+ type: 'test'
+ ...
+# Subtest: matchUpstream falls back to path-prefix when no match() is supplied
+ok 2684 - matchUpstream falls back to path-prefix when no match() is supplied
+ ---
+ duration_ms: 0.133222
+ type: 'test'
+ ...
+# Subtest: matchUpstream treats a throwing match() as a non-match and continues to the next upstream
+ok 2685 - matchUpstream treats a throwing match() as a non-match and continues to the next upstream
+ ---
+ duration_ms: 0.358916
+ type: 'test'
+ ...
+# Subtest: matchUpstream returns undefined when nothing matches
+ok 2686 - matchUpstream returns undefined when nothing matches
+ ---
+ duration_ms: 0.391995
+ type: 'test'
+ ...
+# Subtest: matchUpstream hands match() a lowercased, array-valued header view
+ok 2687 - matchUpstream hands match() a lowercased, array-valued header view
+ ---
+ duration_ms: 0.333397
+ type: 'test'
+ ...
+# Subtest: pathMatchesPrefix: catch-all root, exact, segment, and non-match
+ok 2688 - pathMatchesPrefix: catch-all root, exact, segment, and non-match
+ ---
+ duration_ms: 0.101615
+ type: 'test'
+ ...
+# Subtest: a /_hypaware/* control request is handled locally: not forwarded to a catch-all upstream and starts no exchange (R2)
+ok 2689 - a /_hypaware/* control request is handled locally: not forwarded to a catch-all upstream and starts no exchange (R2)
+ ---
+ duration_ms: 41.113879
+ type: 'test'
+ ...
+# Subtest: R2 proxy edge cases: query string and trailing slash stay local; a look-alike path is proxied
+ok 2690 - R2 proxy edge cases: query string and trailing slash stay local; a look-alike path is proxied
+ ---
+ duration_ms: 12.766644
+ type: 'test'
+ ...
+# Subtest: an unknown /_hypaware/* path with no control handler is 404ed locally, not proxied
+ok 2691 - an unknown /_hypaware/* path with no control handler is 404ed locally, not proxied
+ ---
+ duration_ms: 6.72424
+ type: 'test'
+ ...
+# Subtest: decodes a gzip-encoded response body
+ok 2692 - decodes a gzip-encoded response body
+ ---
+ duration_ms: 2.847227
+ type: 'test'
+ ...
+# Subtest: decodes a brotli-encoded response body
+ok 2693 - decodes a brotli-encoded response body
+ ---
+ duration_ms: 1.458237
+ type: 'test'
+ ...
+# Subtest: decodes a deflate-encoded response body (zlib and raw)
+ok 2694 - decodes a deflate-encoded response body (zlib and raw)
+ ---
+ duration_ms: 1.39503
+ type: 'test'
+ ...
+# Subtest: passes through an identity / unencoded response body unchanged
+ok 2695 - passes through an identity / unencoded response body unchanged
+ ---
+ duration_ms: 0.362311
+ type: 'test'
+ ...
+# Subtest: decodes a gzip-encoded request body
+ok 2696 - decodes a gzip-encoded request body
+ ---
+ duration_ms: 0.523105
+ type: 'test'
+ ...
+# Subtest: handles content-encoding header case-insensitively
+ok 2697 - handles content-encoding header case-insensitively
+ ---
+ duration_ms: 0.320227
+ type: 'test'
+ ...
+# Subtest: decodes the body even when content-encoding is a redacted header
+ok 2698 - decodes the body even when content-encoding is a redacted header
+ ---
+ duration_ms: 0.681726
+ type: 'test'
+ ...
+# Subtest: falls back to raw bytes when the encoding is unknown
+ok 2699 - falls back to raw bytes when the encoding is unknown
+ ---
+ duration_ms: 0.222849
+ type: 'test'
+ ...
+# Subtest: falls back to raw bytes when a gzip body is corrupt
+ok 2700 - falls back to raw bytes when a gzip body is corrupt
+ ---
+ duration_ms: 0.481982
+ type: 'test'
+ ...
+# Subtest: parses SSE events from a gzip-encoded stream at finalize
+ok 2701 - parses SSE events from a gzip-encoded stream at finalize
+ ---
+ duration_ms: 0.866578
+ type: 'test'
+ ...
+# Subtest: uncompressed SSE streams still parse incrementally per chunk
+ok 2702 - uncompressed SSE streams still parse incrementally per chunk
+ ---
+ duration_ms: 0.331354
+ type: 'test'
+ ...
+# Subtest: header-blind SSE: a stream with no content-type is sniffed and parsed
+ok 2703 - header-blind SSE: a stream with no content-type is sniffed and parsed
+ ---
+ duration_ms: 0.211932
+ type: 'test'
+ ...
+# Subtest: a JSON response with no content-type stays non-SSE (no false sniff)
+ok 2704 - a JSON response with no content-type stays non-SSE (no false sniff)
+ ---
+ duration_ms: 0.163949
+ type: 'test'
+ ...
+# Subtest: a finalized exchange leaves the recorder active set
+ok 2705 - a finalized exchange leaves the recorder active set
+ ---
+ duration_ms: 1.691411
+ type: 'test'
+ ...
+# Subtest: drain force-finish also empties the active set
+ok 2706 - drain force-finish also empties the active set
+ ---
+ duration_ms: 10.082695
+ type: 'test'
+ ...
+# Subtest: finalize releases the raw chunk buffers while the row keeps the decoded bodies
+ok 2707 - finalize releases the raw chunk buffers while the row keeps the decoded bodies
+ ---
+ duration_ms: 0.377223
+ type: 'test'
+ ...
+# Subtest: a successful ignore receipt does not claim a drop, for an id live traffic never carries
+ok 2708 - a successful ignore receipt does not claim a drop, for an id live traffic never carries
+ ---
+ duration_ms: 22.045086
+ type: 'test'
+ ...
+# Subtest: the --json receipt states its guarantee, so `status: ok` cannot read as "dropped"
+ok 2709 - the --json receipt states its guarantee, so `status: ok` cannot read as "dropped"
+ ---
+ duration_ms: 8.798913
+ type: 'test'
+ ...
+# Subtest: the unignore receipt reports the removal, not a resumption it cannot verify
+ok 2710 - the unignore receipt reports the removal, not a resumption it cannot verify
+ ---
+ duration_ms: 3.193443
+ type: 'test'
+ ...
+# Subtest: the reader carries the same qualifier, so writer and reader cannot drift
+ok 2711 - the reader carries the same qualifier, so writer and reader cannot drift
+ ---
+ duration_ms: 2.462853
+ type: 'test'
+ ...
+# Subtest: claude/skills/hypaware-privacy/SKILL.md checks the control reply is about the session it posted
+ok 2712 - claude/skills/hypaware-privacy/SKILL.md checks the control reply is about the session it posted
+ ---
+ duration_ms: 0.455412
+ type: 'test'
+ ...
+# Subtest: codex/skills/hypaware-privacy/SKILL.md checks the control reply is about the session it posted
+ok 2713 - codex/skills/hypaware-privacy/SKILL.md checks the control reply is about the session it posted
+ ---
+ duration_ms: 0.163769
+ type: 'test'
+ ...
+# Subtest: an impostor that echoes the token is believed - and every answer discloses that it was never authenticated
+ok 2714 - an impostor that echoes the token is believed - and every answer discloses that it was never authenticated
+ ---
+ duration_ms: 22.904722
+ type: 'test'
+ ...
+# Subtest: `hyp session ignore` carries the disclosure too, where a spoofed success reads as done
+ok 2715 - `hyp session ignore` carries the disclosure too, where a spoofed success reads as done
+ ---
+ duration_ms: 6.59955
+ type: 'test'
+ ...
+# Subtest: the disclosure is unconditional: a real gateway answer carries it too
+ok 2716 - the disclosure is unconditional: a real gateway answer carries it too
+ ---
+ duration_ms: 3.205683
+ type: 'test'
+ ...
+# Subtest: an UNKNOWN answer reports the field too, so a reader never has to infer it from absence
+ok 2717 - an UNKNOWN answer reports the field too, so a reader never has to infer it from absence
+ ---
+ duration_ms: 1.36186
+ type: 'test'
+ ...
+# Subtest: the ignored-session set is readable: GET reports current membership
+ok 2718 - the ignored-session set is readable: GET reports current membership
+ ---
+ duration_ms: 28.369569
+ type: 'test'
+ ...
+# Subtest: a gateway restart no longer fails open SILENTLY: the reader reports the resumed recording
+ok 2719 - a gateway restart no longer fails open SILENTLY: the reader reports the resumed recording
+ ---
+ duration_ms: 5.383373
+ type: 'test'
+ ...
+# Subtest: GET without a session_id is a 400, and an unrelated /_hypaware path is still a 404
+ok 2720 - GET without a session_id is a 400, and an unrelated /_hypaware path is still a 404
+ ---
+ duration_ms: 2.54657
+ type: 'test'
+ ...
+# Subtest: GET round-trips a session id verbatim (R5): no trimming, no normalization
+ok 2721 - GET round-trips a session id verbatim (R5): no trimming, no normalization
+ ---
+ duration_ms: 1.620223
+ type: 'test'
+ ...
+# Subtest: hyp session status reports `ignored` against a live gateway
+ok 2722 - hyp session status reports `ignored` against a live gateway
+ ---
+ duration_ms: 3.413899
+ type: 'test'
+ ...
+# Subtest: hyp session status reports `not_ignored` distinctly, with a nonzero exit
+ok 2723 - hyp session status reports `not_ignored` distinctly, with a nonzero exit
+ ---
+ duration_ms: 1.709969
+ type: 'test'
+ ...
+# Subtest: hyp session status FAILS CLOSED when the gateway is unreachable: unknown, never ignored:false
+ok 2724 - hyp session status FAILS CLOSED when the gateway is unreachable: unknown, never ignored:false
+ ---
+ duration_ms: 1.518688
+ type: 'test'
+ ...
+# Subtest: hyp session status fails closed when no gateway endpoint can be resolved at all
+ok 2725 - hyp session status fails closed when no gateway endpoint can be resolved at all
+ ---
+ duration_ms: 0.312305
+ type: 'test'
+ ...
+# Subtest: hyp session status names the folder governor rather than omitting it (R7)
+ok 2726 - hyp session status names the folder governor rather than omitting it (R7)
+ ---
+ duration_ms: 2.449813
+ type: 'test'
+ ...
+# Subtest: hyp session status fails closed on a 200 with no `ignored` field
+ok 2727 - hyp session status fails closed on a 200 with no `ignored` field
+ ---
+ duration_ms: 1.849511
+ type: 'test'
+ ...
+# Subtest: hyp session status fails closed on a 200 whose `ignored` is a string, not a boolean
+ok 2728 - hyp session status fails closed on a 200 whose `ignored` is a string, not a boolean
+ ---
+ duration_ms: 1.517086
+ type: 'test'
+ ...
+# Subtest: hyp session status fails closed on a 200 with no numeric `total`
+ok 2729 - hyp session status fails closed on a 200 with no numeric `total`
+ ---
+ duration_ms: 1.423113
+ type: 'test'
+ ...
+# Subtest: hyp session status fails closed on a 200 JSON array
+ok 2730 - hyp session status fails closed on a 200 JSON array
+ ---
+ duration_ms: 1.252834
+ type: 'test'
+ ...
+# Subtest: hyp session status fails closed on a 200 that is not JSON at all
+ok 2731 - hyp session status fails closed on a 200 that is not JSON at all
+ ---
+ duration_ms: 1.448842
+ type: 'test'
+ ...
+# Subtest: hyp session status fails closed when the answer is about a DIFFERENT session
+ok 2732 - hyp session status fails closed when the answer is about a DIFFERENT session
+ ---
+ duration_ms: 1.442583
+ type: 'test'
+ ...
+# Subtest: hyp session status fails closed on a non-200 from the endpoint
+ok 2733 - hyp session status fails closed on a non-200 from the endpoint
+ ---
+ duration_ms: 1.263831
+ type: 'test'
+ ...
+# Subtest: hyp session ignore does not report a quiet success against a non-gateway responder
+ok 2734 - hyp session ignore does not report a quiet success against a non-gateway responder
+ ---
+ duration_ms: 1.722658
+ type: 'test'
+ ...
+# Subtest: hyp session ignore / unignore round-trip through the control route
+ok 2735 - hyp session ignore / unignore round-trip through the control route
+ ---
+ duration_ms: 2.585059
+ type: 'test'
+ ...
+# Subtest: the ephemerality caveat names the fork that mints a new session id, not only a restart
+ok 2736 - the ephemerality caveat names the fork that mints a new session id, not only a restart
+ ---
+ duration_ms: 2.920959
+ type: 'test'
+ ...
+# Subtest: an explicit session id argument beats the environment
+ok 2737 - an explicit session id argument beats the environment
+ ---
+ duration_ms: 1.482874
+ type: 'test'
+ ...
+# Subtest: resolves a Codex session id from the rollout whose payload.cwd matches the invocation cwd
+ok 2738 - resolves a Codex session id from the rollout whose payload.cwd matches the invocation cwd
+ ---
+ duration_ms: 0.96118
+ type: 'test'
+ ...
+# Subtest: a first line that is not a session_meta record resolves nothing, however much it looks like one
+ok 2739 - a first line that is not a session_meta record resolves nothing, however much it looks like one
+ ---
+ duration_ms: 0.482734
+ type: 'test'
+ ...
+# Subtest: refuses (never guesses newest) when several Codex rollouts match the cwd
+ok 2740 - refuses (never guesses newest) when several Codex rollouts match the cwd
+ ---
+ duration_ms: 0.885626
+ type: 'test'
+ ...
+# Subtest: a cwd match with NO thread id still makes the answer ambiguous: it is not discarded before the count
+ok 2741 - a cwd match with NO thread id still makes the answer ambiguous: it is not discarded before the count
+ ---
+ duration_ms: 0.656698
+ type: 'test'
+ ...
+# Subtest: a LONE cwd match with no thread id refuses rather than resolving the container it states
+ok 2742 - a LONE cwd match with no thread id refuses rather than resolving the container it states
+ ---
+ duration_ms: 0.566471
+ type: 'test'
+ ...
+# Subtest: a header stating NEITHER field is not diagnosed as an old Codex
+ok 2743 - a header stating NEITHER field is not diagnosed as an old Codex
+ ---
+ duration_ms: 0.675456
+ type: 'test'
+ ...
+# Subtest: a rollout that DOES state a thread id but no session_id is still the legacy diagnosis
+ok 2744 - a rollout that DOES state a thread id but no session_id is still the legacy diagnosis
+ ---
+ duration_ms: 1.383072
+ type: 'test'
+ ...
+# Subtest: a stated thread ignores an id-less rollout entirely: that path is identity, not counting
+ok 2745 - a stated thread ignores an id-less rollout entirely: that path is identity, not counting
+ ---
+ duration_ms: 0.771793
+ type: 'test'
+ ...
+# Subtest: refuses when no Codex rollout matches the cwd
+ok 2746 - refuses when no Codex rollout matches the cwd
+ ---
+ duration_ms: 0.543946
+ type: 'test'
+ ...
+# Subtest: CLAUDE_CODE_SESSION_ID wins over any Codex rollout scan
+ok 2747 - CLAUDE_CODE_SESSION_ID wins over any Codex rollout scan
+ ---
+ duration_ms: 0.399347
+ type: 'test'
+ ...
+# Subtest: a truncated rollout scan refuses rather than claiming a unique cwd match
+ok 2748 - a truncated rollout scan refuses rather than claiming a unique cwd match
+ ---
+ duration_ms: 0.748578
+ type: 'test'
+ ...
+# Subtest: a SINGLE STALE rollout refuses: one cwd match is not evidence the session is live
+ok 2749 - a SINGLE STALE rollout refuses: one cwd match is not evidence the session is live
+ ---
+ duration_ms: 0.624099
+ type: 'test'
+ ...
+# Subtest: a fresh rollout still resolves, and reports that the id was inferred from disk
+ok 2750 - a fresh rollout still resolves, and reports that the id was inferred from disk
+ ---
+ duration_ms: 0.596346
+ type: 'test'
+ ...
+# Subtest: the staleness bound is what refuses, not the cwd match: the same rollout resolves under a wider bound
+ok 2751 - the staleness bound is what refuses, not the cwd match: the same rollout resolves under a wider bound
+ ---
+ duration_ms: 0.799876
+ type: 'test'
+ ...
+# Subtest: a disk-inferred id is never presented as if the client had stated it
+ok 2752 - a disk-inferred id is never presented as if the client had stated it
+ ---
+ duration_ms: 9.506859
+ type: 'test'
+ ...
+# Subtest: a Codex SUBAGENT thread resolves the session container, and that is the id the gateway actually drops
+ok 2753 - a Codex SUBAGENT thread resolves the session container, and that is the id the gateway actually drops
+ ---
+ duration_ms: 2.507061
+ type: 'test'
+ ...
+# Subtest: a legacy rollout with no session_id field REFUSES: the back-filled thread id is not the key
+ok 2754 - a legacy rollout with no session_id field REFUSES: the back-filled thread id is not the key
+ ---
+ duration_ms: 1.214916
+ type: 'test'
+ ...
+# Subtest: `hyp session ignore` on a legacy rollout reports no success and exits unknown
+ok 2755 - `hyp session ignore` on a legacy rollout reports no success and exits unknown
+ ---
+ duration_ms: 1.444105
+ type: 'test'
+ ...
+# Subtest: CODEX_THREAD_ID selects the live rollout without the mtime proxy, then the container is read from it
+ok 2756 - CODEX_THREAD_ID selects the live rollout without the mtime proxy, then the container is read from it
+ ---
+ duration_ms: 1.376091
+ type: 'test'
+ ...
+# Subtest: a BLANK session_id is as unusable as an absent one, and refuses the same way
+ok 2757 - a BLANK session_id is as unusable as an absent one, and refuses the same way
+ ---
+ duration_ms: 2.230721
+ type: 'test'
+ ...
+# Subtest: the container is read from the session_meta header, not from any first line that carries the fields
+ok 2758 - the container is read from the session_meta header, not from any first line that carries the fields
+ ---
+ duration_ms: 1.226193
+ type: 'test'
+ ...
+# Subtest: rollouts that disagree about which session contains a thread refuse, naming both
+ok 2759 - rollouts that disagree about which session contains a thread refuse, naming both
+ ---
+ duration_ms: 1.259825
+ type: 'test'
+ ...
+# Subtest: a stated thread still resolves from a rollout whose cwd is unusable: that path never reads cwd
+ok 2760 - a stated thread still resolves from a rollout whose cwd is unusable: that path never reads cwd
+ ---
+ duration_ms: 1.940459
+ type: 'test'
+ ...
+# Subtest: two clients each stating a session refuse rather than picking one
+ok 2761 - two clients each stating a session refuse rather than picking one
+ ---
+ duration_ms: 0.410394
+ type: 'test'
+ ...
+# Subtest: a Codex answer discloses the grain it acts at, and names the thread beside the container
+ok 2762 - a Codex answer discloses the grain it acts at, and names the thread beside the container
+ ---
+ duration_ms: 5.577147
+ type: 'test'
+ ...
+# Subtest: an endpoint nothing proved is the gateway is reported as such
+ok 2763 - an endpoint nothing proved is the gateway is reported as such
+ ---
+ duration_ms: 3.736058
+ type: 'test'
+ ...
+# Subtest: `--` ends flag parsing so a session id beginning with `-` is reachable
+ok 2764 - `--` ends flag parsing so a session id beginning with `-` is reachable
+ ---
+ duration_ms: 1.629747
+ type: 'test'
+ ...
+# Subtest: an oversized control response is refused rather than buffered
+ok 2765 - an oversized control response is refused rather than buffered
+ ---
+ duration_ms: 3.965778
+ type: 'test'
+ ...
+# Subtest: an unresolvable session id fails closed, it does not report not-ignored
+ok 2766 - an unresolvable session id fails closed, it does not report not-ignored
+ ---
+ duration_ms: 0.938877
+ type: 'test'
+ ...
+# Subtest: settle dedupe stops reading once every batch part_id is resolved
+ok 2767 - settle dedupe stops reading once every batch part_id is resolved
+ ---
+ duration_ms: 3.805004
+ type: 'test'
+ ...
+# Subtest: settle dedupe still keeps a row whose part_id is nowhere committed
+ok 2768 - settle dedupe still keeps a row whose part_id is nowhere committed
+ ---
+ duration_ms: 0.967961
+ type: 'test'
+ ...
+# Subtest: source starts with only adapter-registered upstream presets
+ok 2769 - source starts with only adapter-registered upstream presets
+ ---
+ duration_ms: 38.324359
+ type: 'test'
+ ...
+# Subtest: operator configured upstream wins over same-name adapter preset
+ok 2770 - operator configured upstream wins over same-name adapter preset
+ ---
+ duration_ms: 7.848539
+ type: 'test'
+ ...
+# Subtest: the ignored-session set survives a reload() of the same GatewayState
+ok 2771 - the ignored-session set survives a reload() of the same GatewayState
+ ---
+ duration_ms: 9.706393
+ type: 'test'
+ ...
+# Subtest: restart-drops-state: a fresh GatewayState never carries a previous run's opt-outs
+ok 2772 - restart-drops-state: a fresh GatewayState never carries a previous run's opt-outs
+ ---
+ duration_ms: 0.700915
+ type: 'test'
+ ...
+# Subtest: the gateway source a hermes-only picker run composes starts, idle
+ok 2773 - the gateway source a hermes-only picker run composes starts, idle
+ ---
+ duration_ms: 11.393488
+ type: 'test'
+ ...
+# Subtest: an idle gateway binds once a reload brings an upstream
+ok 2774 - an idle gateway binds once a reload brings an upstream
+ ---
+ duration_ms: 4.643708
+ type: 'test'
+ ...
+# Subtest: a reload that removes every upstream tears the listener down and idles
+ok 2775 - a reload that removes every upstream tears the listener down and idles
+ ---
+ duration_ms: 2.680985
+ type: 'test'
+ ...
+# Subtest: status() reports the reloaded config upstreams, not the boot-time ones
+ok 2776 - status() reports the reloaded config upstreams, not the boot-time ones
+ ---
+ duration_ms: 0.2364
+ type: 'test'
+ ...
+# Subtest: status() counts a configured upstream it cannot name
+ok 2777 - status() counts a configured upstream it cannot name
+ ---
+ duration_ms: 0.299645
+ type: 'test'
+ ...
+# Subtest: status() counts zero upstreams for a config that named none
+ok 2778 - status() counts zero upstreams for a config that named none
+ ---
+ duration_ms: 0.287527
+ type: 'test'
+ ...
+# Subtest: the idle log is a warning only when configured upstreams were dropped
+ok 2779 - the idle log is a warning only when configured upstreams were dropped
+ ---
+ duration_ms: 0.215989
+ type: 'test'
+ ...
+# Subtest: a gateway that lost one of two upstreams warns at boot and reports the drop
+ok 2780 - a gateway that lost one of two upstreams warns at boot and reports the drop
+ ---
+ duration_ms: 0.637108
+ type: 'test'
+ ...
+# Subtest: a gateway whose upstreams all compile reports no drop and logs nothing
+ok 2781 - a gateway whose upstreams all compile reports no drop and logs nothing
+ ---
+ duration_ms: 1.781017
+ type: 'test'
+ ...
+# Subtest: claude projector drops a session whose cwd is marked `ignore` in the machine-local list
+ok 2782 - claude projector drops a session whose cwd is marked `ignore` in the machine-local list
+ ---
+ duration_ms: 18.218811
+ type: 'test'
+ ...
+# Subtest: claude projector still records a session whose cwd is marked `local-only` in the machine-local list
+ok 2783 - claude projector still records a session whose cwd is marked `local-only` in the machine-local list
+ ---
+ duration_ms: 15.657969
+ type: 'test'
+ ...
+# Subtest: codex projector drops an exchange whose cwd is marked `ignore` in the machine-local list
+ok 2784 - codex projector drops an exchange whose cwd is marked `ignore` in the machine-local list
+ ---
+ duration_ms: 4.136998
+ type: 'test'
+ ...
+# Subtest: codex projector still records an exchange whose cwd is marked `local-only` in the machine-local list
+ok 2785 - codex projector still records an exchange whose cwd is marked `local-only` in the machine-local list
+ ---
+ duration_ms: 4.049705
+ type: 'test'
+ ...
+# Subtest: claude activate() derives the machine-local list from the shared state root, so an `ignore` cwd drops at capture
+ok 2786 - claude activate() derives the machine-local list from the shared state root, so an `ignore` cwd drops at capture
+ ---
+ duration_ms: 7.063286
+ type: 'test'
+ ...
+# Subtest: claude activate() still records a `local-only` cwd (recorded at capture, withheld only at export)
+ok 2787 - claude activate() still records a `local-only` cwd (recorded at capture, withheld only at export)
+ ---
+ duration_ms: 12.018877
+ type: 'test'
+ ...
+# Subtest: codex activate() derives the machine-local list from the shared state root, so an `ignore` cwd drops at capture
+ok 2788 - codex activate() derives the machine-local list from the shared state root, so an `ignore` cwd drops at capture
+ ---
+ duration_ms: 5.742608
+ type: 'test'
+ ...
+# Subtest: codex activate() still records a `local-only` cwd (recorded at capture, withheld only at export)
+ok 2789 - codex activate() still records a `local-only` cwd (recorded at capture, withheld only at export)
+ ---
+ duration_ms: 5.154515
+ type: 'test'
+ ...
+# Subtest: abortableSleep resolves after the delay when not aborted
+ok 2790 - abortableSleep resolves after the delay when not aborted
+ ---
+ duration_ms: 21.031385
+ type: 'test'
+ ...
+# Subtest: abortableSleep rejects immediately when the signal is already aborted
+ok 2791 - abortableSleep rejects immediately when the signal is already aborted
+ ---
+ duration_ms: 0.666914
+ type: 'test'
+ ...
+# Subtest: abortableSleep rejects promptly when aborted mid-sleep
+ok 2792 - abortableSleep rejects promptly when aborted mid-sleep
+ ---
+ duration_ms: 10.825754
+ type: 'test'
+ ...
+# Subtest: parseRetryAfter and the ladder are the shared canonical source
+ok 2793 - parseRetryAfter and the ladder are the shared canonical source
+ ---
+ duration_ms: 0.732333
+ type: 'test'
+ ...
+# Subtest: start pulls immediately; a 200 confirms the poll and stages the document with its etag
+ok 2794 - start pulls immediately; a 200 confirms the poll and stages the document with its etag
+ ---
+ duration_ms: 21.095053
+ type: 'test'
+ ...
+# Subtest: If-None-Match always presents the running config etag
+ok 2795 - If-None-Match always presents the running config etag
+ ---
+ duration_ms: 0.32262
+ type: 'test'
+ ...
+# Subtest: 401 refreshes the JWT and retries once; a second 401 escalates without staging
+ok 2796 - 401 refreshes the JWT and retries once; a second 401 escalates without staging
+ ---
+ duration_ms: 0.790121
+ type: 'test'
+ ...
+# Subtest: a 200 without an etag header is dropped, not staged
+ok 2797 - a 200 without an etag header is dropped, not staged
+ ---
+ duration_ms: 0.591229
+ type: 'test'
+ ...
+# Subtest: an oversized 200 body is dropped before parsing
+ok 2798 - an oversized 200 body is dropped before parsing
+ ---
+ duration_ms: 2.069546
+ type: 'test'
+ ...
+# Subtest: invalid JSON in a 200 body is dropped
+ok 2799 - invalid JSON in a 200 body is dropped
+ ---
+ duration_ms: 0.587833
+ type: 'test'
+ ...
+# Subtest: 404 takes the legacy backoff branch without confirming probation
+ok 2800 - 404 takes the legacy backoff branch without confirming probation
+ ---
+ duration_ms: 0.301549
+ type: 'test'
+ ...
+# Subtest: the steady timer keeps polling on the configured cadence
+ok 2801 - the steady timer keeps polling on the configured cadence
+ ---
+ duration_ms: 121.167473
+ type: 'test'
+ ...
+# Subtest: stop prevents any further polls
+ok 2802 - stop prevents any further polls
+ ---
+ duration_ms: 64.448465
+ type: 'test'
+ ...
+# Subtest: transport errors back off and keep the loop alive
+ok 2803 - transport errors back off and keep the loop alive
+ ---
+ duration_ms: 0.682908
+ type: 'test'
+ ...
+# Subtest: an oversized Content-Length is rejected without reading the body
+ok 2804 - an oversized Content-Length is rejected without reading the body
+ ---
+ duration_ms: 0.971938
+ type: 'test'
+ ...
+# Subtest: a chunked oversized body is cancelled at the cap, not buffered whole
+ok 2805 - a chunked oversized body is cancelled at the cap, not buffered whole
+ ---
+ duration_ms: 1.197741
+ type: 'test'
+ ...
+# Subtest: stop() aborts a poll stuck on a never-resolving fetch after the drain grace
+ok 2806 - stop() aborts a poll stuck on a never-resolving fetch after the drain grace
+ ---
+ duration_ms: 20.543684
+ type: 'test'
+ ...
+# Subtest: the request deadline aborts a stalled poll and the loop stays alive
+ok 2807 - the request deadline aborts a stalled poll and the loop stays alive
+ ---
+ duration_ms: 100.739574
+ type: 'test'
+ ...
+# Subtest: 429 with Retry-After schedules from the header without confirming the poll
+ok 2808 - 429 with Retry-After schedules from the header without confirming the poll
+ ---
+ duration_ms: 0.480801
+ type: 'test'
+ ...
+# Subtest: 503 with a garbage Retry-After falls back to the backoff ladder
+ok 2809 - 503 with a garbage Retry-After falls back to the backoff ladder
+ ---
+ duration_ms: 0.260196
+ type: 'test'
+ ...
+# Subtest: 429 with Retry-After: 0 reschedules via the ladder, not an immediate re-poll
+ok 2810 - 429 with Retry-After: 0 reschedules via the ladder, not an immediate re-poll
+ ---
+ duration_ms: 50.427294
+ type: 'test'
+ ...
+# Subtest: parseRetryAfter: delta-seconds, HTTP-date, and garbage
+ok 2811 - parseRetryAfter: delta-seconds, HTTP-date, and garbage
+ ---
+ duration_ms: 0.435772
+ type: 'test'
+ ...
+# Subtest: forward sink chunks a large partition into bounded POSTs
+ok 2812 - forward sink chunks a large partition into bounded POSTs
+ ---
+ duration_ms: 92.654256
+ type: 'test'
+ ...
+# Subtest: a partition that fits in one chunk makes exactly one POST
+ok 2813 - a partition that fits in one chunk makes exactly one POST
+ ---
+ duration_ms: 0.373447
+ type: 'test'
+ ...
+# Subtest: chunk batch-ids are deterministic across re-exports (idempotent retry)
+ok 2814 - chunk batch-ids are deterministic across re-exports (idempotent retry)
+ ---
+ duration_ms: 112.389431
+ type: 'test'
+ ...
+# Subtest: a transport failure marks the partition for retry, not the whole batch
+ok 2815 - a transport failure marks the partition for retry, not the whole batch
+ ---
+ duration_ms: 45.014026
+ type: 'test'
+ ...
+# Subtest: empty batch is a no-op success
+ok 2816 - empty batch is a no-op success
+ ---
+ duration_ms: 0.164069
+ type: 'test'
+ ...
+# Subtest: byte-identical chunks get distinct batch-ids (no ledger collision)
+ok 2817 - byte-identical chunks get distinct batch-ids (no ledger collision)
+ ---
+ duration_ms: 43.157034
+ type: 'test'
+ ...
+# Subtest: a dataset with no sourceSignal fails the partition for retry (unknown signal)
+ok 2818 - a dataset with no sourceSignal fails the partition for retry (unknown signal)
+ ---
+ duration_ms: 0.310391
+ type: 'test'
+ ...
+# Subtest: the byte budget splits wide rows even when the row count is tiny
+ok 2819 - the byte budget splits wide rows even when the row count is tiny
+ ---
+ duration_ms: 93.777673
+ type: 'test'
+ ...
+# Subtest: a 401 re-sends the same body + batch-id after one refresh
+ok 2820 - a 401 re-sends the same body + batch-id after one refresh
+ ---
+ duration_ms: 0.614394
+ type: 'test'
+ ...
+# Subtest: each chunk emits central.forward.chunk telemetry
+ok 2821 - each chunk emits central.forward.chunk telemetry
+ ---
+ duration_ms: 52.882486
+ type: 'test'
+ ...
+# Subtest: central.forward.failed names the failing chunk and how many landed
+ok 2822 - central.forward.failed names the failing chunk and how many landed
+ ---
+ duration_ms: 50.21376
+ type: 'test'
+ ...
+# Subtest: 429 honors Retry-After and retries the same chunk to success
+ok 2823 - 429 honors Retry-After and retries the same chunk to success
+ ---
+ duration_ms: 0.420369
+ type: 'test'
+ ...
+# Subtest: 429 without Retry-After falls back to the backoff ladder
+ok 2824 - 429 without Retry-After falls back to the backoff ladder
+ ---
+ duration_ms: 0.206694
+ type: 'test'
+ ...
+# Subtest: 503 is backpressure (retried), not a hard failure
+ok 2825 - 503 is backpressure (retried), not a hard failure
+ ---
+ duration_ms: 0.205893
+ type: 'test'
+ ...
+# Subtest: repeated 429s walk the ladder before succeeding
+ok 2826 - repeated 429s walk the ladder before succeeding
+ ---
+ duration_ms: 0.281658
+ type: 'test'
+ ...
+# Subtest: backpressure beyond the inline budget fails the partition for retry
+ok 2827 - backpressure beyond the inline budget fails the partition for retry
+ ---
+ duration_ms: 0.257662
+ type: 'test'
+ ...
+# Subtest: a non-positive Retry-After (0 / past date) uses the ladder, never a zero-delay spin
+ok 2828 - a non-positive Retry-After (0 / past date) uses the ladder, never a zero-delay spin
+ ---
+ duration_ms: 0.243781
+ type: 'test'
+ ...
+# Subtest: backpressure drains the throttle response body before parking
+ok 2829 - backpressure drains the throttle response body before parking
+ ---
+ duration_ms: 0.287827
+ type: 'test'
+ ...
+# Subtest: each backpressure wait emits central.forward.backpressure telemetry
+ok 2830 - each backpressure wait emits central.forward.backpressure telemetry
+ ---
+ duration_ms: 0.19679
+ type: 'test'
+ ...
+# Subtest: close() aborts a chunk paused on backpressure (no shutdown wedge)
+ok 2831 - close() aborts a chunk paused on backpressure (no shutdown wedge)
+ ---
+ duration_ms: 15.783149
+ type: 'test'
+ ...
+# Subtest: a tick with no new rows transmits zero bytes and zero chunks
+ok 2832 - a tick with no new rows transmits zero bytes and zero chunks
+ ---
+ duration_ms: 0.278292
+ type: 'test'
+ ...
+# Subtest: a tick after N new rows reads/sends only the new suffix and advances the watermark
+ok 2833 - a tick after N new rows reads/sends only the new suffix and advances the watermark
+ ---
+ duration_ms: 0.21065
+ type: 'test'
+ ...
+# Subtest: the watermark advances once, at end-of-partition, to the high-water after
+ok 2834 - the watermark advances once, at end-of-partition, to the high-water after
+ ---
+ duration_ms: 43.263847
+ type: 'test'
+ ...
+# Subtest: a mid-partition failure leaves the watermark unadvanced (no partial checkpoint)
+ok 2835 - a mid-partition failure leaves the watermark unadvanced (no partial checkpoint)
+ ---
+ duration_ms: 29.2257
+ type: 'test'
+ ...
+# Subtest: a respool re-reads the whole partition with STABLE batch-ids (ledger-dedups the acked prefix)
+ok 2836 - a respool re-reads the whole partition with STABLE batch-ids (ledger-dedups the acked prefix)
+ ---
+ duration_ms: 60.344167
+ type: 'test'
+ ...
+# Subtest: a fresh partition (no watermark) reads from the start and advances
+ok 2837 - a fresh partition (no watermark) reads from the start and advances
+ ---
+ duration_ms: 0.199653
+ type: 'test'
+ ...
+# Subtest: an unordered scan never skips a lower-seq row when a later chunk fails (BLOCKER, LLP 0040 §4 risk \#3)
+ok 2838 - an unordered scan never skips a lower-seq row when a later chunk fails (BLOCKER, LLP 0040 §4 risk \#3)
+ ---
+ duration_ms: 34.246822
+ type: 'test'
+ ...
+# Subtest: a drop-only tick POSTs nothing yet checkpoints past the withheld rows
+ok 2839 - a drop-only tick POSTs nothing yet checkpoints past the withheld rows
+ ---
+ duration_ms: 1.582034
+ type: 'test'
+ ...
+# Subtest: a mixed tick ships only the full rows and advances to the partition high-water
+ok 2840 - a mixed tick ships only the full rows and advances to the partition high-water
+ ---
+ duration_ms: 1.517647
+ type: 'test'
+ ...
+# Subtest: a directory un-excluded AFTER a drop-only checkpoint is not re-sent (durably passed)
+ok 2841 - a directory un-excluded AFTER a drop-only checkpoint is not re-sent (durably passed)
+ ---
+ duration_ms: 0.306486
+ type: 'test'
+ ...
+# Subtest: a failed chunk never checkpoints, even when the partition also dropped rows
+ok 2842 - a failed chunk never checkpoints, even when the partition also dropped rows
+ ---
+ duration_ms: 0.537227
+ type: 'test'
+ ...
+# Subtest: a corrupt local-only list fails the tick with the watermark untouched
+ok 2843 - a corrupt local-only list fails the tick with the watermark untouched
+ ---
+ duration_ms: 0.322961
+ type: 'test'
+ ...
+# Subtest: cwd-less datasets are unaffected: a partition with no drops ships and advances exactly as before
+ok 2844 - cwd-less datasets are unaffected: a partition with no drops ships and advances exactly as before
+ ---
+ duration_ms: 1.539389
+ type: 'test'
+ ...
+# Subtest: writeLoginSeed writes a 0600 login-origin identity stamped with the central URL
+ok 2845 - writeLoginSeed writes a 0600 login-origin identity stamped with the central URL
+ ---
+ duration_ms: 1.418386
+ type: 'test'
+ ...
+# Subtest: writeLoginSeed returns the identity it displaced
+ok 2846 - writeLoginSeed returns the identity it displaced
+ ---
+ duration_ms: 0.761567
+ type: 'test'
+ ...
+# Subtest: acquire() loads a login seed with no bootstrap token configured (LLP 0061 D2/D3)
+ok 2847 - acquire() loads a login seed with no bootstrap token configured (LLP 0061 D2/D3)
+ ---
+ duration_ms: 0.728337
+ type: 'test'
+ ...
+# Subtest: a configured bootstrap token does not re-bootstrap over a same-URL login seed (LLP 0061 D3)
+ok 2848 - a configured bootstrap token does not re-bootstrap over a same-URL login seed (LLP 0061 D3)
+ ---
+ duration_ms: 0.432838
+ type: 'test'
+ ...
+# Subtest: a login seed for a different URL still re-bootstraps when a token is configured (LLP 0061 D4)
+ok 2849 - a login seed for a different URL still re-bootstraps when a token is configured (LLP 0061 D4)
+ ---
+ duration_ms: 14.257199
+ type: 'test'
+ ...
+# Subtest: a re-point with no token refuses a login seed and points at re-login, not hyp join
+ok 2850 - a re-point with no token refuses a login seed and points at re-login, not hyp join
+ ---
+ duration_ms: 0.851504
+ type: 'test'
+ ...
+# Subtest: refresh preserves the login origin and central_url on the persisted identity
+ok 2851 - refresh preserves the login origin and central_url on the persisted identity
+ ---
+ duration_ms: 1.341439
+ type: 'test'
+ ...
+# Subtest: first join bootstraps and stamps the minting url + token fingerprint
+ok 2852 - first join bootstraps and stamps the minting url + token fingerprint
+ ---
+ duration_ms: 20.341227
+ type: 'test'
+ ...
+# Subtest: reboot with the same mint reuses the persisted identity (no re-bootstrap)
+ok 2853 - reboot with the same mint reuses the persisted identity (no re-bootstrap)
+ ---
+ duration_ms: 0.933168
+ type: 'test'
+ ...
+# Subtest: re-join with a different token re-bootstraps a fresh gateway identity
+ok 2854 - re-join with a different token re-bootstraps a fresh gateway identity
+ ---
+ duration_ms: 1.231602
+ type: 'test'
+ ...
+# Subtest: re-join pointed at a different central URL re-bootstraps
+ok 2855 - re-join pointed at a different central URL re-bootstraps
+ ---
+ duration_ms: 1.106602
+ type: 'test'
+ ...
+# Subtest: re-pointed at a different central URL with no token refuses to load the old identity
+ok 2856 - re-pointed at a different central URL with no token refuses to load the old identity
+ ---
+ duration_ms: 1.034973
+ type: 'test'
+ ...
+# Subtest: reboot at the same URL with no token still loads (no false mismatch)
+ok 2857 - reboot at the same URL with no token still loads (no false mismatch)
+ ---
+ duration_ms: 0.7035
+ type: 'test'
+ ...
+# Subtest: an identity from an older build (no mint stamp) re-bootstraps when a token is set
+ok 2858 - an identity from an older build (no mint stamp) re-bootstraps when a token is set
+ ---
+ duration_ms: 2.339436
+ type: 'test'
+ ...
+# Subtest: validateClaudeAccountConfig accepts an empty / absent config
+ok 2859 - validateClaudeAccountConfig accepts an empty / absent config
+ ---
+ duration_ms: 1.522423
+ type: 'test'
+ ...
+# Subtest: validateClaudeAccountConfig accepts both modes
+ok 2860 - validateClaudeAccountConfig accepts both modes
+ ---
+ duration_ms: 0.173795
+ type: 'test'
+ ...
+# Subtest: validateClaudeAccountConfig rejects a non-object config
+ok 2861 - validateClaudeAccountConfig rejects a non-object config
+ ---
+ duration_ms: 0.863322
+ type: 'test'
+ ...
+# Subtest: validateClaudeAccountConfig rejects an unknown mode
+ok 2862 - validateClaudeAccountConfig rejects an unknown mode
+ ---
+ duration_ms: 0.128535
+ type: 'test'
+ ...
+# Subtest: validateClaudeAccountConfig requires a key source in org_key mode
+ok 2863 - validateClaudeAccountConfig requires a key source in org_key mode
+ ---
+ duration_ms: 0.301929
+ type: 'test'
+ ...
+# Subtest: validateClaudeAccountConfig rejects api_key together with api_key_env
+ok 2864 - validateClaudeAccountConfig rejects api_key together with api_key_env
+ ---
+ duration_ms: 0.168897
+ type: 'test'
+ ...
+# Subtest: validateClaudeAccountConfig rejects empty strings and unknown keys
+ok 2865 - validateClaudeAccountConfig rejects empty strings and unknown keys
+ ---
+ duration_ms: 0.208667
+ type: 'test'
+ ...
+# Subtest: resolveMode defaults to subscription and honors the config
+ok 2866 - resolveMode defaults to subscription and honors the config
+ ---
+ duration_ms: 0.168256
+ type: 'test'
+ ...
+# Subtest: org_key mode uses the configured key with empty headers
+ok 2867 - org_key mode uses the configured key with empty headers
+ ---
+ duration_ms: 1.102857
+ type: 'test'
+ ...
+# Subtest: org_key mode resolves api_key_env and fails loudly when unset
+ok 2868 - org_key mode resolves api_key_env and fails loudly when unset
+ ---
+ duration_ms: 0.303722
+ type: 'test'
+ ...
+# Subtest: subscription mode errors with login guidance when not signed in
+ok 2869 - subscription mode errors with login guidance when not signed in
+ ---
+ duration_ms: 0.609757
+ type: 'test'
+ ...
+# Subtest: subscription mode returns the stored token with the oauth beta header
+ok 2870 - subscription mode returns the stored token with the oauth beta header
+ ---
+ duration_ms: 0.780867
+ type: 'test'
+ ...
+# Subtest: subscription mode refreshes inside the expiry window and persists the rotated pair
+ok 2871 - subscription mode refreshes inside the expiry window and persists the rotated pair
+ ---
+ duration_ms: 2.652031
+ type: 'test'
+ ...
+# Subtest: subscription refresh failure surfaces as an error, not a stale token
+ok 2872 - subscription refresh failure surfaces as an error, not a stale token
+ ---
+ duration_ms: 0.615676
+ type: 'test'
+ ...
+# Subtest: createAuthorizationAttempt derives the S256 challenge from the verifier
+ok 2873 - createAuthorizationAttempt derives the S256 challenge from the verifier
+ ---
+ duration_ms: 2.119762
+ type: 'test'
+ ...
+# Subtest: buildAuthorizeUrl carries the PKCE and state parameters
+ok 2874 - buildAuthorizeUrl carries the PKCE and state parameters
+ ---
+ duration_ms: 0.348389
+ type: 'test'
+ ...
+# Subtest: buildAuthorizeUrl honors a loopback redirect override
+ok 2875 - buildAuthorizeUrl honors a loopback redirect override
+ ---
+ duration_ms: 1.068604
+ type: 'test'
+ ...
+# Subtest: parsePastedAuthorization handles code\#state and full URLs
+ok 2876 - parsePastedAuthorization handles code\#state and full URLs
+ ---
+ duration_ms: 0.94739
+ type: 'test'
+ ...
+# Subtest: exchangeAuthorizationCode verifies state before spending the code
+ok 2877 - exchangeAuthorizationCode verifies state before spending the code
+ ---
+ duration_ms: 0.285034
+ type: 'test'
+ ...
+# Subtest: exchangeAuthorizationCode maps the grant into a stored record
+ok 2878 - exchangeAuthorizationCode maps the grant into a stored record
+ ---
+ duration_ms: 0.367329
+ type: 'test'
+ ...
+# Subtest: exchangeAuthorizationCode echoes the loopback redirect it authorized with
+ok 2879 - exchangeAuthorizationCode echoes the loopback redirect it authorized with
+ ---
+ duration_ms: 0.20346
+ type: 'test'
+ ...
+# Subtest: exchangeAuthorizationCode rejects an unrecognized token response
+ok 2880 - exchangeAuthorizationCode rejects an unrecognized token response
+ ---
+ duration_ms: 0.13177
+ type: 'test'
+ ...
+# Subtest: paste lane fails, rather than hanging, when stdin ends with no listener to wait on
+ok 2881 - paste lane fails, rather than hanging, when stdin ends with no listener to wait on
+ ---
+ duration_ms: 4.481832
+ type: 'test'
+ ...
+# Subtest: paste lane fails, rather than hanging, on a stdin that was already spent
+ok 2882 - paste lane fails, rather than hanging, on a stdin that was already spent
+ ---
+ duration_ms: 1.476925
+ type: 'test'
+ ...
+# Subtest: paste lane leaves the loopback listener to finish when stdin ends under it
+ok 2883 - paste lane leaves the loopback listener to finish when stdin ends under it
+ ---
+ duration_ms: 23.436732
+ type: 'test'
+ ...
+# Subtest: paste lane parses a code delivered in the same burst as the EOF
+ok 2884 - paste lane parses a code delivered in the same burst as the EOF
+ ---
+ duration_ms: 1.13194
+ type: 'test'
+ ...
+# Subtest: paste lane parses the pasted code, not a line that followed it
+ok 2885 - paste lane parses the pasted code, not a line that followed it
+ ---
+ duration_ms: 0.501993
+ type: 'test'
+ ...
+# Subtest: paste lane still parses a pasted code
+ok 2886 - paste lane still parses a pasted code
+ ---
+ duration_ms: 0.377493
+ type: 'test'
+ ...
+# Subtest: read returns undefined when no credential is stored
+ok 2887 - read returns undefined when no credential is stored
+ ---
+ duration_ms: 0.996044
+ type: 'test'
+ ...
+# Subtest: write / read round-trips and sets 0600
+ok 2888 - write / read round-trips and sets 0600
+ ---
+ duration_ms: 1.362621
+ type: 'test'
+ ...
+# Subtest: read throws on corrupt JSON and on an unrecognized shape
+ok 2889 - read throws on corrupt JSON and on an unrecognized shape
+ ---
+ duration_ms: 0.813687
+ type: 'test'
+ ...
+# Subtest: clear removes the file and tolerates absence
+ok 2890 - clear removes the file and tolerates absence
+ ---
+ duration_ms: 1.379386
+ type: 'test'
+ ...
+# Subtest: tokenFingerprint never contains the token
+ok 2891 - tokenFingerprint never contains the token
+ ---
+ duration_ms: 0.549465
+ type: 'test'
+ ...
+# Subtest: withCredentialLock serializes concurrent critical sections
+ok 2892 - withCredentialLock serializes concurrent critical sections
+ ---
+ duration_ms: 113.613611
+ type: 'test'
+ ...
+# Subtest: withCredentialLock breaks a stale lock
+ok 2893 - withCredentialLock breaks a stale lock
+ ---
+ duration_ms: 0.743319
+ type: 'test'
+ ...
+# Subtest: provider advertises a stable contribution shape
+ok 2894 - provider advertises a stable contribution shape
+ ---
+ duration_ms: 1.662837
+ type: 'test'
+ ...
+# Subtest: fixture transcript projects into canonical ai_gateway_messages rows
+ok 2895 - fixture transcript projects into canonical ai_gateway_messages rows
+ ---
+ duration_ms: 15.883201
+ type: 'test'
+ ...
+# Subtest: assistant token usage is folded into attributes.usage like live capture
+ok 2896 - assistant token usage is folded into attributes.usage like live capture
+ ---
+ duration_ms: 4.15836
+ type: 'test'
+ ...
+# Subtest: usage lands once - on the last block of a split assistant API message
+ok 2897 - usage lands once - on the last block of a split assistant API message
+ ---
+ duration_ms: 4.766123
+ type: 'test'
+ ...
+# Subtest: assistant model is surfaced per message, switches mid-session, and drops
+ok 2898 - assistant model is surfaced per message, switches mid-session, and drops
+ ---
+ duration_ms: 8.912447
+ type: 'test'
+ ...
+# Subtest: token usage merges with subagent spawn provenance
+ok 2899 - token usage merges with subagent spawn provenance
+ ---
+ duration_ms: 26.514079
+ type: 'test'
+ ...
+# Subtest: native DAG identity is preserved verbatim
+ok 2900 - native DAG identity is preserved verbatim
+ ---
+ duration_ms: 13.324061
+ type: 'test'
+ ...
+# Subtest: raw_frame is minimized - never a full transcript copy
+ok 2901 - raw_frame is minimized - never a full transcript copy
+ ---
+ duration_ms: 3.332415
+ type: 'test'
+ ...
+# Subtest: reruns are deterministic (idempotent items and rows)
+ok 2902 - reruns are deterministic (idempotent items and rows)
+ ---
+ duration_ms: 18.604878
+ type: 'test'
+ ...
+# Subtest: sessions are grouped into one item each, across multiple files
+ok 2903 - sessions are grouped into one item each, across multiple files
+ ---
+ duration_ms: 10.104758
+ type: 'test'
+ ...
+# Subtest: grouping keys on the entry session id, not the file name
+ok 2904 - grouping keys on the entry session id, not the file name
+ ---
+ duration_ms: 3.433428
+ type: 'test'
+ ...
+# Subtest: since bound filters out messages older than the window
+ok 2905 - since bound filters out messages older than the window
+ ---
+ duration_ms: 6.299385
+ type: 'test'
+ ...
+# Subtest: subagent rows carry the spawning tool call from the meta sidecar
+ok 2906 - subagent rows carry the spawning tool call from the meta sidecar
+ ---
+ duration_ms: 4.385275
+ type: 'test'
+ ...
+# Subtest: missing transcript root yields nothing without throwing
+ok 2907 - missing transcript root yields nothing without throwing
+ ---
+ duration_ms: 1.110468
+ type: 'test'
+ ...
+# Subtest: recovers git_remote/repo_root from the transcript cwd when the record predates git capture
+ok 2908 - recovers git_remote/repo_root from the transcript cwd when the record predates git capture
+ ---
+ duration_ms: 6.095985
+ type: 'test'
+ ...
+# Subtest: record-provided remote wins; no derivation is attempted
+ok 2909 - record-provided remote wins; no derivation is attempted
+ ---
+ duration_ms: 7.091779
+ type: 'test'
+ ...
+# Subtest: derivation is memoized per cwd across sessions
+ok 2910 - derivation is memoized per cwd across sessions
+ ---
+ duration_ms: 8.708366
+ type: 'test'
+ ...
+# Subtest: recovers git_remote from the record cwd when the record predates git capture
+ok 2911 - recovers git_remote from the record cwd when the record predates git capture
+ ---
+ duration_ms: 4.060562
+ type: 'test'
+ ...
+# Subtest: record repo_root is preserved when only the remote is derived
+ok 2912 - record repo_root is preserved when only the remote is derived
+ ---
+ duration_ms: 6.651981
+ type: 'test'
+ ...
+# Subtest: an already-aborted signal stops the scan before any session is projected
+ok 2913 - an already-aborted signal stops the scan before any session is projected
+ ---
+ duration_ms: 13.552288
+ type: 'test'
+ ...
+# Subtest: transcript toolUseResult is promoted onto the backfilled row
+ok 2914 - transcript toolUseResult is promoted onto the backfilled row
+ ---
+ duration_ms: 3.567903
+ type: 'test'
+ ...
+# Subtest: an unclassified interactive session gets a SessionStart additionalContext prompt
+ok 2915 - an unclassified interactive session gets a SessionStart additionalContext prompt
+ ---
+ duration_ms: 2.612741
+ type: 'test'
+ ...
+# Subtest: a classified / unenrolled / non-interactive evaluation emits nothing
+ok 2916 - a classified / unenrolled / non-interactive evaluation emits nothing
+ ---
+ duration_ms: 1.603176
+ type: 'test'
+ ...
+# Subtest: a session-start event with no cwd is a passthrough (no evaluation, no output)
+ok 2917 - a session-start event with no cwd is a passthrough (no evaluation, no output)
+ ---
+ duration_ms: 0.405547
+ type: 'test'
+ ...
+# Subtest: malformed stdin never throws back into Claude
+ok 2918 - malformed stdin never throws back into Claude
+ ---
+ duration_ms: 0.396493
+ type: 'test'
+ ...
+# Subtest: an evaluation that throws is swallowed with exit 0 and no output
+ok 2919 - an evaluation that throws is swallowed with exit 0 and no output
+ ---
+ duration_ms: 0.432427
+ type: 'test'
+ ...
+# Subtest: --help prints usage and does not read stdin
+ok 2920 - --help prints usage and does not read stdin
+ ---
+ duration_ms: 0.235748
+ type: 'test'
+ ...
+# Subtest: isInteractiveClaudeSession: startup is interactive; compact / CI / escape-hatch are not
+ok 2921 - isInteractiveClaudeSession: startup is interactive; compact / CI / escape-hatch are not
+ ---
+ duration_ms: 0.11202
+ type: 'test'
+ ...
+# Subtest: claudeClientName stamps claude-desktop off the Desktop User-Agent
+ok 2922 - claudeClientName stamps claude-desktop off the Desktop User-Agent
+ ---
+ duration_ms: 1.452228
+ type: 'test'
+ ...
+# Subtest: claudeClientName falls back for CLI and generic SDK traffic
+ok 2923 - claudeClientName falls back for CLI and generic SDK traffic
+ ---
+ duration_ms: 0.09266
+ type: 'test'
+ ...
+# Subtest: claudeClientName honors a non-default fallback
+ok 2924 - claudeClientName honors a non-default fallback
+ ---
+ duration_ms: 0.069155
+ type: 'test'
+ ...
+# Subtest: claudeClientVersion extracts both CLI and Desktop versions
+ok 2925 - claudeClientVersion extracts both CLI and Desktop versions
+ ---
+ duration_ms: 0.161626
+ type: 'test'
+ ...
+# Subtest: validateClaudeConfig accepts an empty / absent config
+ok 2926 - validateClaudeConfig accepts an empty / absent config
+ ---
+ duration_ms: 1.365676
+ type: 'test'
+ ...
+# Subtest: validateClaudeConfig leaves non-backfill keys (e.g. proxy) untouched
+ok 2927 - validateClaudeConfig leaves non-backfill keys (e.g. proxy) untouched
+ ---
+ duration_ms: 0.175376
+ type: 'test'
+ ...
+# Subtest: validateClaudeConfig accepts a full backfill block
+ok 2928 - validateClaudeConfig accepts a full backfill block
+ ---
+ duration_ms: 0.194496
+ type: 'test'
+ ...
+# Subtest: validateClaudeConfig rejects a non-object config
+ok 2929 - validateClaudeConfig rejects a non-object config
+ ---
+ duration_ms: 0.143909
+ type: 'test'
+ ...
+# Subtest: validateClaudeConfig rejects a malformed backfill block
+ok 2930 - validateClaudeConfig rejects a malformed backfill block
+ ---
+ duration_ms: 0.298333
+ type: 'test'
+ ...
+# Subtest: validateBackfillSection mounts pointers under the supplied prefix
+ok 2931 - validateBackfillSection mounts pointers under the supplied prefix
+ ---
+ duration_ms: 0.128295
+ type: 'test'
+ ...
+# Subtest: validateClaudeConfig accepts an attach block
+ok 2932 - validateClaudeConfig accepts an attach block
+ ---
+ duration_ms: 0.194847
+ type: 'test'
+ ...
+# Subtest: validateClaudeConfig rejects a malformed attach block
+ok 2933 - validateClaudeConfig rejects a malformed attach block
+ ---
+ duration_ms: 0.235458
+ type: 'test'
+ ...
+# Subtest: validateAttachSection mounts pointers under the supplied prefix
+ok 2934 - validateAttachSection mounts pointers under the supplied prefix
+ ---
+ duration_ms: 0.298704
+ type: 'test'
+ ...
+# Subtest: the registered claude section drives validatePluginConfig
+ok 2935 - the registered claude section drives validatePluginConfig
+ ---
+ duration_ms: 0.790081
+ type: 'test'
+ ...
+# Subtest: a central-locked backfill.on_join cannot be flipped by a colliding local entry
+ok 2936 - a central-locked backfill.on_join cannot be flipped by a colliding local entry
+ ---
+ duration_ms: 0.303582
+ type: 'test'
+ ...
+# Subtest: a central-locked attach.on_join cannot be flipped by a colliding local entry
+ok 2937 - a central-locked attach.on_join cannot be flipped by a colliding local entry
+ ---
+ duration_ms: 0.12468
+ type: 'test'
+ ...
+# Subtest: findDesktop3pProjectsDirs discovers nested .claude/projects under both container layouts
+ok 2938 - findDesktop3pProjectsDirs discovers nested .claude/projects under both container layouts
+ ---
+ duration_ms: 26.234104
+ type: 'test'
+ ...
+# Subtest: findDesktop3pProjectsDirs is empty when no 3p container exists
+ok 2939 - findDesktop3pProjectsDirs is empty when no 3p container exists
+ ---
+ duration_ms: 1.054342
+ type: 'test'
+ ...
+# Subtest: loadTranscript falls back to the 3p sandbox tree when the shared tree misses
+ok 2940 - loadTranscript falls back to the 3p sandbox tree when the shared tree misses
+ ---
+ duration_ms: 9.190489
+ type: 'test'
+ ...
+# Subtest: loadTranscript does not scan the 3p tree when the shared tree matches
+ok 2941 - loadTranscript does not scan the 3p tree when the shared tree matches
+ ---
+ duration_ms: 8.701786
+ type: 'test'
+ ...
+# Subtest: backfill imports a 3p sandbox session and attributes it to the configured owner
+ok 2942 - backfill imports a 3p sandbox session and attributes it to the configured owner
+ ---
+ duration_ms: 10.156026
+ type: 'test'
+ ...
+# Subtest: backfill gates 3p sessions with absent or unclaimed entrypoints when Desktop is unconfigured
+ok 2943 - backfill gates 3p sessions with absent or unclaimed entrypoints when Desktop is unconfigured
+ ---
+ duration_ms: 14.831041
+ type: 'test'
+ ...
+# Subtest: backfill gates 3p sessions when no owners map or predicate is supplied at all
+ok 2944 - backfill gates 3p sessions when no owners map or predicate is supplied at all
+ ---
+ duration_ms: 5.172622
+ type: 'test'
+ ...
+# Subtest: backfill imports the container for a configured Desktop that declares no entrypoint values
+ok 2945 - backfill imports the container for a configured Desktop that declares no entrypoint values
+ ---
+ duration_ms: 6.188566
+ type: 'test'
+ ...
+# Subtest: backfill stamps subagent provenance from sidecars inside the 3p container
+ok 2946 - backfill stamps subagent provenance from sidecars inside the 3p container
+ ---
+ duration_ms: 10.721515
+ type: 'test'
+ ...
+# Subtest: backfill gates a 3p sandbox session when its owner is not configured
+ok 2947 - backfill gates a 3p sandbox session when its owner is not configured
+ ---
+ duration_ms: 13.495482
+ type: 'test'
+ ...
+# Subtest: createDesktop3pDirsCache serves cached roots within the TTL and re-sweeps after it
+ok 2948 - createDesktop3pDirsCache serves cached roots within the TTL and re-sweeps after it
+ ---
+ duration_ms: 12.466888
+ type: 'test'
+ ...
+# Subtest: loadTranscript finds a sandbox home created after the root cache was primed
+ok 2949 - loadTranscript finds a sandbox home created after the root cache was primed
+ ---
+ duration_ms: 11.681104
+ type: 'test'
+ ...
+# Subtest: the claude-desktop manifest declares no attach_probe
+ok 2950 - the claude-desktop manifest declares no attach_probe
+ ---
+ duration_ms: 2.759224
+ type: 'test'
+ ...
+# Subtest: hyp detach --client claude-desktop is a no-op over the managed plist
+ok 2951 - hyp detach --client claude-desktop is a no-op over the managed plist
+ ---
+ duration_ms: 1.875781
+ type: 'test'
+ ...
+# Subtest: the managed plist never surfaces as a client attach-probe error
+ok 2952 - the managed plist never surfaces as a client attach-probe error
+ ---
+ duration_ms: 1.766795
+ type: 'test'
+ ...
+# Subtest: install-helper writes an executable no-arg wrapper under the state dir
+ok 2953 - install-helper writes an executable no-arg wrapper under the state dir
+ ---
+ duration_ms: 1.83571
+ type: 'test'
+ ...
+# Subtest: the generated wrapper runs its target with no arguments
+ok 2954 - the generated wrapper runs its target with no arguments
+ ---
+ duration_ms: 5.361409
+ type: 'test'
+ ...
+# Subtest: status reports the helper as not installed until install-helper runs
+ok 2955 - status reports the helper as not installed until install-helper runs
+ ---
+ duration_ms: 1.050036
+ type: 'test'
+ ...
+# Subtest: install: org_key mode skips the login step and never calls claude-account login
+ok 2956 - install: org_key mode skips the login step and never calls claude-account login
+ ---
+ duration_ms: 2.425156
+ type: 'test'
+ ...
+# Subtest: install: subscription mode already signed in skips login without calling it
+ok 2957 - install: subscription mode already signed in skips login without calling it
+ ---
+ duration_ms: 0.709428
+ type: 'test'
+ ...
+# Subtest: install: subscription mode not signed in runs login, and a failed login drops the run
+ok 2958 - install: subscription mode not signed in runs login, and a failed login drops the run
+ ---
+ duration_ms: 0.679573
+ type: 'test'
+ ...
+# Subtest: install: no stdin in subscription mode fails the login step without attempting login
+ok 2959 - install: no stdin in subscription mode fails the login step without attempting login
+ ---
+ duration_ms: 0.551759
+ type: 'test'
+ ...
+# Subtest: install: refuses up front on an ephemeral gateway listen, with no side effects
+ok 2960 - install: refuses up front on an ephemeral gateway listen, with no side effects
+ ---
+ duration_ms: 0.520891
+ type: 'test'
+ ...
+# Subtest: install: refuses up front on a non-macOS platform, with no side effects
+ok 2961 - install: refuses up front on a non-macOS platform, with no side effects
+ ---
+ duration_ms: 0.29632
+ type: 'test'
+ ...
+# Subtest: install: --print-commands passes the platform gate, because it applies nothing
+ok 2962 - install: --print-commands passes the platform gate, because it applies nothing
+ ---
+ duration_ms: 0.741437
+ type: 'test'
+ ...
+# Subtest: install: residue directory is backed up and cleared when present
+ok 2963 - install: residue directory is backed up and cleared when present
+ ---
+ duration_ms: 2.071659
+ type: 'test'
+ ...
+# Subtest: install: a re-run with no residue present is a plain skip, not a failure
+ok 2964 - install: a re-run with no residue present is a plain skip, not a failure
+ ---
+ duration_ms: 0.791813
+ type: 'test'
+ ...
+# Subtest: install: an already up-to-date managed plist is skipped, no sudo invoked for it
+ok 2965 - install: an already up-to-date managed plist is skipped, no sudo invoked for it
+ ---
+ duration_ms: 0.812344
+ type: 'test'
+ ...
+# Subtest: install: a stale managed plist is rewritten via sudo cp with the freshly rendered content
+ok 2966 - install: a stale managed plist is rewritten via sudo cp with the freshly rendered content
+ ---
+ duration_ms: 0.669127
+ type: 'test'
+ ...
+# Subtest: install: a failed privileged write drops with a re-run hint, not a thrown error
+ok 2967 - install: a failed privileged write drops with a re-run hint, not a thrown error
+ ---
+ duration_ms: 0.574152
+ type: 'test'
+ ...
+# Subtest: install: --print-commands prints the sudo and killall commands without invoking spawn
+ok 2968 - install: --print-commands prints the sudo and killall commands without invoking spawn
+ ---
+ duration_ms: 0.438536
+ type: 'test'
+ ...
+# Subtest: install: --print-commands applies nothing at all, including the non-privileged steps
+ok 2969 - install: --print-commands applies nothing at all, including the non-privileged steps
+ ---
+ duration_ms: 0.911976
+ type: 'test'
+ ...
+# Subtest: install: --print-commands never asks for consent, because it changes nothing
+ok 2970 - install: --print-commands never asks for consent, because it changes nothing
+ ---
+ duration_ms: 0.416974
+ type: 'test'
+ ...
+# Subtest: install: a bare enter proceeds - disclosure, question, then the steps
+ok 2971 - install: a bare enter proceeds - disclosure, question, then the steps
+ ---
+ duration_ms: 2.532108
+ type: 'test'
+ ...
+# Subtest: install: an explicit no declines - nothing changed, nonzero exit
+ok 2972 - install: an explicit no declines - nothing changed, nonzero exit
+ ---
+ duration_ms: 1.136507
+ type: 'test'
+ ...
+# Subtest: install: org_key mode asks without claiming a browser sign-in
+ok 2973 - install: org_key mode asks without claiming a browser sign-in
+ ---
+ duration_ms: 0.731301
+ type: 'test'
+ ...
+# Subtest: install: --yes skips the question and runs the steps
+ok 2974 - install: --yes skips the question and runs the steps
+ ---
+ duration_ms: 0.592961
+ type: 'test'
+ ...
+# Subtest: install: a stdin that ends without an answer declines instead of hanging
+ok 2975 - install: a stdin that ends without an answer declines instead of hanging
+ ---
+ duration_ms: 0.764933
+ type: 'test'
+ ...
+# Subtest: install: the disclosure names the credential posture and every file it will touch
+ok 2976 - install: the disclosure names the credential posture and every file it will touch
+ ---
+ duration_ms: 0.674155
+ type: 'test'
+ ...
+# Subtest: install: org_key mode disclosure promises no sign-in
+ok 2977 - install: org_key mode disclosure promises no sign-in
+ ---
+ duration_ms: 0.615806
+ type: 'test'
+ ...
+# Subtest: install: consent names the residue clear only when residue is actually present
+ok 2978 - install: consent names the residue clear only when residue is actually present
+ ---
+ duration_ms: 1.201887
+ type: 'test'
+ ...
+# Subtest: install: an already-configured machine is not re-prompted
+ok 2979 - install: an already-configured machine is not re-prompted
+ ---
+ duration_ms: 0.528873
+ type: 'test'
+ ...
+# Subtest: buildPlistWriteCommands renders mkdir, cp, and chmod against the target path
+ok 2980 - buildPlistWriteCommands renders mkdir, cp, and chmod against the target path
+ ---
+ duration_ms: 0.182608
+ type: 'test'
+ ...
+# Subtest: computeDesiredPlistContent matches renderManagedPreferencesPlist(buildManagedProfile(...))
+ok 2981 - computeDesiredPlistContent matches renderManagedPreferencesPlist(buildManagedProfile(...))
+ ---
+ duration_ms: 0.325174
+ type: 'test'
+ ...
+# Subtest: every registered claude-desktop command summary matches its manifest entry
+ok 2982 - every registered claude-desktop command summary matches its manifest entry
+ ---
+ duration_ms: 2.782739
+ type: 'test'
+ ...
+# Subtest: the duplicated stable default listen matches the ai-gateway default (parity)
+ok 2983 - the duplicated stable default listen matches the ai-gateway default (parity)
+ ---
+ duration_ms: 0.909022
+ type: 'test'
+ ...
+# Subtest: resolveGatewayBaseUrl uses the fixed default when the fleet sets no listen
+ok 2984 - resolveGatewayBaseUrl uses the fixed default when the fleet sets no listen
+ ---
+ duration_ms: 0.221787
+ type: 'test'
+ ...
+# Subtest: resolveGatewayBaseUrl honors an explicit fleet listen
+ok 2985 - resolveGatewayBaseUrl honors an explicit fleet listen
+ ---
+ duration_ms: 0.208927
+ type: 'test'
+ ...
+# Subtest: resolveGatewayBaseUrl refuses an ephemeral listen
+ok 2986 - resolveGatewayBaseUrl refuses an ephemeral listen
+ ---
+ duration_ms: 0.383583
+ type: 'test'
+ ...
+# Subtest: resolveGatewayBaseUrl honors and normalizes the endpoint override
+ok 2987 - resolveGatewayBaseUrl honors and normalizes the endpoint override
+ ---
+ duration_ms: 0.21092
+ type: 'test'
+ ...
+# Subtest: buildManagedProfile renders the app schema and carries no secret material
+ok 2988 - buildManagedProfile renders the app schema and carries no secret material
+ ---
+ duration_ms: 0.648395
+ type: 'test'
+ ...
+# Subtest: org_key mode renders under the x-api-key scheme
+ok 2989 - org_key mode renders under the x-api-key scheme
+ ---
+ duration_ms: 0.086511
+ type: 'test'
+ ...
+# Subtest: renderCredentialHelperScript wraps the credential command via the absolute interpreter, no args
+ok 2990 - renderCredentialHelperScript wraps the credential command via the absolute interpreter, no args
+ ---
+ duration_ms: 0.169728
+ type: 'test'
+ ...
+# Subtest: renderCredentialHelperScript shell-quotes a path with spaces
+ok 2991 - renderCredentialHelperScript shell-quotes a path with spaces
+ ---
+ duration_ms: 0.227626
+ type: 'test'
+ ...
+# Subtest: renderCredentialHelperScript embeds a non-default HYP env, omits an empty one
+ok 2992 - renderCredentialHelperScript embeds a non-default HYP env, omits an empty one
+ ---
+ duration_ms: 0.323151
+ type: 'test'
+ ...
+# Subtest: renderManagedPreferencesPlist emits a well-formed dict
+ok 2993 - renderManagedPreferencesPlist emits a well-formed dict
+ ---
+ duration_ms: 0.245003
+ type: 'test'
+ ...
+# Subtest: validateClaudeDesktopConfig accepts valid shapes and rejects typos
+ok 2994 - validateClaudeDesktopConfig accepts valid shapes and rejects typos
+ ---
+ duration_ms: 0.313236
+ type: 'test'
+ ...
+# Subtest: verify: missing plist and clean residue is incomplete but not thrown
+ok 2995 - verify: missing plist and clean residue is incomplete but not thrown
+ ---
+ duration_ms: 1.830883
+ type: 'test'
+ ...
+# Subtest: verify: up-to-date plist and clean residue is a green exit code
+ok 2996 - verify: up-to-date plist and clean residue is a green exit code
+ ---
+ duration_ms: 0.602466
+ type: 'test'
+ ...
+# Subtest: verify: a present but stale plist is reported STALE and fails
+ok 2997 - verify: a present but stale plist is reported STALE and fails
+ ---
+ duration_ms: 0.430565
+ type: 'test'
+ ...
+# Subtest: verify: leftover dialog residue fails even with a correct plist
+ok 2998 - verify: leftover dialog residue fails even with a correct plist
+ ---
+ duration_ms: 0.606542
+ type: 'test'
+ ...
+# Subtest: verify: refuses cleanly (no throw) on an ephemeral gateway listen
+ok 2999 - verify: refuses cleanly (no throw) on an ephemeral gateway listen
+ ---
+ duration_ms: 0.493009
+ type: 'test'
+ ...
+# Subtest: verify: refuses on a non-macOS platform instead of reporting MISSING
+ok 3000 - verify: refuses on a non-macOS platform instead of reporting MISSING
+ ---
+ duration_ms: 0.275479
+ type: 'test'
+ ...
+# Subtest: checkInstallState is a pure read: never mutates the residue directory or plist
+ok 3001 - checkInstallState is a pure read: never mutates the residue directory or plist
+ ---
+ duration_ms: 0.63807
+ type: 'test'
+ ...
+# Subtest: derives redacted remote and repo_root, and never asks for HEAD
+ok 3002 - derives redacted remote and repo_root, and never asks for HEAD
+ ---
+ duration_ms: 2.946338
+ type: 'test'
+ ...
+# Subtest: an SSH remote is left intact (no userinfo to strip)
+ok 3003 - an SSH remote is left intact (no userinfo to strip)
+ ---
+ duration_ms: 0.198692
+ type: 'test'
+ ...
+# Subtest: degrades to empty when the cwd is not a git repo
+ok 3004 - degrades to empty when the cwd is not a git repo
+ ---
+ duration_ms: 0.196128
+ type: 'test'
+ ...
+# Subtest: a repo with no origin still yields repo_root
+ok 3005 - a repo with no origin still yields repo_root
+ ---
+ duration_ms: 0.176829
+ type: 'test'
+ ...
+# Subtest: returns empty for an absent cwd without invoking git
+ok 3006 - returns empty for an absent cwd without invoking git
+ ---
+ duration_ms: 0.130739
+ type: 'test'
+ ...
+# Subtest: native DAG identity: uuid from JSONL transcript becomes message_id and provider_uuid
+ok 3007 - native DAG identity: uuid from JSONL transcript becomes message_id and provider_uuid
+ ---
+ duration_ms: 15.885815
+ type: 'test'
+ ...
+# Subtest: transcript-matched live rows carry the minimized raw_frame, never the full line
+ok 3008 - transcript-matched live rows carry the minimized raw_frame, never the full line
+ ---
+ duration_ms: 7.227665
+ type: 'test'
+ ...
+# Subtest: live: response-level usage lands once, on the last block of a split turn
+ok 3009 - live: response-level usage lands once, on the last block of a split turn
+ ---
+ duration_ms: 3.616166
+ type: 'test'
+ ...
+# Subtest: root message gets previous_message_id = [] when parentUuid is null
+ok 3010 - root message gets previous_message_id = [] when parentUuid is null
+ ---
+ duration_ms: 3.943153
+ type: 'test'
+ ...
+# Subtest: transcript-enriched previous_message_id carries the immediate predecessor, scoped per thread
+ok 3011 - transcript-enriched previous_message_id carries the immediate predecessor, scoped per thread
+ ---
+ duration_ms: 7.222237
+ type: 'test'
+ ...
+# Subtest: subagent transcript under /subagents supplies sidechain identity
+ok 3012 - subagent transcript under /subagents supplies sidechain identity
+ ---
+ duration_ms: 5.397814
+ type: 'test'
+ ...
+# Subtest: transcript_path from session context also loads sibling subagent files
+ok 3013 - transcript_path from session context also loads sibling subagent files
+ ---
+ duration_ms: 6.13884
+ type: 'test'
+ ...
+# Subtest: subagent exchange stamps spawned_by_tool_use_id from the meta sidecar
+ok 3014 - subagent exchange stamps spawned_by_tool_use_id from the meta sidecar
+ ---
+ duration_ms: 6.153873
+ type: 'test'
+ ...
+# Subtest: cache_control on wire blocks and caller on transcript blocks do not break matching
+ok 3015 - cache_control on wire blocks and caller on transcript blocks do not break matching
+ ---
+ duration_ms: 6.13235
+ type: 'test'
+ ...
+# Subtest: multi-block assistant turn splits into per-line uuid messages (LLP 0023)
+ok 3016 - multi-block assistant turn splits into per-line uuid messages (LLP 0023)
+ ---
+ duration_ms: 5.428711
+ type: 'test'
+ ...
+# Subtest: parallel tool_results split one message per result, joined by tool_use_id
+ok 3017 - parallel tool_results split one message per result, joined by tool_use_id
+ ---
+ duration_ms: 3.077697
+ type: 'test'
+ ...
+# Subtest: transcript toolUseResult is promoted onto the matched live row
+ok 3018 - transcript toolUseResult is promoted onto the matched live row
+ ---
+ duration_ms: 2.873017
+ type: 'test'
+ ...
+# Subtest: reminder-wrapped prompt canonicalizes to transcript content + wire_only extra
+ok 3019 - reminder-wrapped prompt canonicalizes to transcript content + wire_only extra
+ ---
+ duration_ms: 5.022033
+ type: 'test'
+ ...
+# Subtest: real text riding with a tool_result is matched, not dumped as wire_only
+ok 3020 - real text riding with a tool_result is matched, not dumped as wire_only
+ ---
+ duration_ms: 4.702237
+ type: 'test'
+ ...
+# Subtest: x-claude-code-agent-id header stamps is_sidechain even without a transcript
+ok 3021 - x-claude-code-agent-id header stamps is_sidechain even without a transcript
+ ---
+ duration_ms: 1.616127
+ type: 'test'
+ ...
+# Subtest: unmatched multi-block assistant turn still splits, with stable per-block fallback ids
+ok 3022 - unmatched multi-block assistant turn still splits, with stable per-block fallback ids
+ ---
+ duration_ms: 7.496284
+ type: 'test'
+ ...
+# Subtest: missing transcript → gateway fallback identity + claude.identity_source marker
+ok 3023 - missing transcript → gateway fallback identity + claude.identity_source marker
+ ---
+ duration_ms: 4.219833
+ type: 'test'
+ ...
+# Subtest: transcript-matched rows do NOT carry a settlement match_key
+ok 3024 - transcript-matched rows do NOT carry a settlement match_key
+ ---
+ duration_ms: 2.974361
+ type: 'test'
+ ...
+# Subtest: harness aux traffic (security monitor) is tagged with aux_kind, not dropped
+ok 3025 - harness aux traffic (security monitor) is tagged with aux_kind, not dropped
+ ---
+ duration_ms: 1.554262
+ type: 'test'
+ ...
+# Subtest: ordinary conversation traffic carries no aux_kind
+ok 3026 - ordinary conversation traffic carries no aux_kind
+ ---
+ duration_ms: 1.427379
+ type: 'test'
+ ...
+# Subtest: session-context state file supplies cwd and git_branch on the row
+ok 3027 - session-context state file supplies cwd and git_branch on the row
+ ---
+ duration_ms: 2.790402
+ type: 'test'
+ ...
+# Subtest: exchange without anthropic signature is skipped by match()
+ok 3028 - exchange without anthropic signature is skipped by match()
+ ---
+ duration_ms: 1.189628
+ type: 'test'
+ ...
+# Subtest: match() accepts /v1/messages path even without anthropic headers
+ok 3029 - match() accepts /v1/messages path even without anthropic headers
+ ---
+ duration_ms: 0.985187
+ type: 'test'
+ ...
+# Subtest: match() accepts requests with anthropic-version header on non-canonical paths
+ok 3030 - match() accepts requests with anthropic-version header on non-canonical paths
+ ---
+ duration_ms: 1.366077
+ type: 'test'
+ ...
+# Subtest: hook → state file → projector roundtrip writes cwd onto the row
+ok 3031 - hook → state file → projector roundtrip writes cwd onto the row
+ ---
+ duration_ms: 48.29877
+ type: 'test'
+ ...
+# Subtest: hook appends the minimal cwd record before the git subprocesses run
+ok 3032 - hook appends the minimal cwd record before the git subprocesses run
+ ---
+ duration_ms: 4.761948
+ type: 'test'
+ ...
+# Subtest: hook still lands the minimal cwd record when git throws
+ok 3033 - hook still lands the minimal cwd record when git throws
+ ---
+ duration_ms: 3.312264
+ type: 'test'
+ ...
+# Subtest: hook captures repo identity for a real git repo and the projector stamps it
+ok 3034 - hook captures repo identity for a real git repo and the projector stamps it
+ ---
+ duration_ms: 55.548048
+ type: 'test'
+ ...
+# Subtest: hook redacts credential userinfo from an https remote before it is recorded (LLP 0032)
+ok 3035 - hook redacts credential userinfo from an https remote before it is recorded (LLP 0032)
+ ---
+ duration_ms: 41.602251
+ type: 'test'
+ ...
+# Subtest: redactRemoteUserinfo strips only the credential-bearing URL form
+ok 3036 - redactRemoteUserinfo strips only the credential-bearing URL form
+ ---
+ duration_ms: 0.266625
+ type: 'test'
+ ...
+# Subtest: pickLatestMatching prefers newer records when multiple share a session_id
+ok 3037 - pickLatestMatching prefers newer records when multiple share a session_id
+ ---
+ duration_ms: 10.178871
+ type: 'test'
+ ...
+# Subtest: pickLatestMatching prefers transcript_path over session_id when both keys hit
+ok 3038 - pickLatestMatching prefers transcript_path over session_id when both keys hit
+ ---
+ duration_ms: 3.191921
+ type: 'test'
+ ...
+# Subtest: defaultSessionContextFile resolves under the plugin state dir
+ok 3039 - defaultSessionContextFile resolves under the plugin state dir
+ ---
+ duration_ms: 0.427359
+ type: 'test'
+ ...
+# Subtest: appendSessionContext compacts large state files to recent records
+ok 3040 - appendSessionContext compacts large state files to recent records
+ ---
+ duration_ms: 11.313676
+ type: 'test'
+ ...
+# Subtest: attach records the managed env + hook entries into the marker undo record
+ok 3041 - attach records the managed env + hook entries into the marker undo record
+ ---
+ duration_ms: 18.354958
+ type: 'test'
+ ...
+# Subtest: attach backs up a pre-existing foreign ANTHROPIC_BASE_URL as prev_base_url
+ok 3042 - attach backs up a pre-existing foreign ANTHROPIC_BASE_URL as prev_base_url
+ ---
+ duration_ms: 12.560481
+ type: 'test'
+ ...
+# Subtest: attach omits prev_base_url when there was no pre-existing base URL
+ok 3043 - attach omits prev_base_url when there was no pre-existing base URL
+ ---
+ duration_ms: 7.170559
+ type: 'test'
+ ...
+# Subtest: attach leaves a user-owned ENABLE_TOOL_SEARCH untouched and unmanaged
+ok 3044 - attach leaves a user-owned ENABLE_TOOL_SEARCH untouched and unmanaged
+ ---
+ duration_ms: 4.681145
+ type: 'test'
+ ...
+# Subtest: re-attach keeps managing an ENABLE_TOOL_SEARCH it owns
+ok 3045 - re-attach keeps managing an ENABLE_TOOL_SEARCH it owns
+ ---
+ duration_ms: 9.267005
+ type: 'test'
+ ...
+# Subtest: attach declares the gateway base URL first-party so the assumed context window is not cut
+ok 3046 - attach declares the gateway base URL first-party so the assumed context window is not cut
+ ---
+ duration_ms: 6.069505
+ type: 'test'
+ ...
+# Subtest: attach leaves a user-owned _CLAUDE_CODE_ASSUME_FIRST_PARTY_BASE_URL untouched and unmanaged
+ok 3047 - attach leaves a user-owned _CLAUDE_CODE_ASSUME_FIRST_PARTY_BASE_URL untouched and unmanaged
+ ---
+ duration_ms: 6.384003
+ type: 'test'
+ ...
+# Subtest: attach leaves a user-owned boolean env value untouched and unmanaged
+ok 3048 - attach leaves a user-owned boolean env value untouched and unmanaged
+ ---
+ duration_ms: 6.998417
+ type: 'test'
+ ...
+# Subtest: attach leaves a user-owned number env value untouched and unmanaged
+ok 3049 - attach leaves a user-owned number env value untouched and unmanaged
+ ---
+ duration_ms: 5.351384
+ type: 'test'
+ ...
+# Subtest: attach leaves a user-owned null env value untouched and unmanaged
+ok 3050 - attach leaves a user-owned null env value untouched and unmanaged
+ ---
+ duration_ms: 6.377053
+ type: 'test'
+ ...
+# Subtest: re-attach keeps managing a _CLAUDE_CODE_ASSUME_FIRST_PARTY_BASE_URL it owns
+ok 3051 - re-attach keeps managing a _CLAUDE_CODE_ASSUME_FIRST_PARTY_BASE_URL it owns
+ ---
+ duration_ms: 8.210789
+ type: 'test'
+ ...
+# Subtest: idempotent re-attach keeps the original prev_base_url, not the gateway URL
+ok 3052 - idempotent re-attach keeps the original prev_base_url, not the gateway URL
+ ---
+ duration_ms: 11.397463
+ type: 'test'
+ ...
+# Subtest: idempotent re-attach does not invent a prev_base_url when none existed
+ok 3053 - idempotent re-attach does not invent a prev_base_url when none existed
+ ---
+ duration_ms: 16.807485
+ type: 'test'
+ ...
+# Subtest: the marker undo record is stable across re-attach (modulo attached_at)
+ok 3054 - the marker undo record is stable across re-attach (modulo attached_at)
+ ---
+ duration_ms: 12.523124
+ type: 'test'
+ ...
+# Subtest: attach backs up a 8080 base URL into prev_base_url
+ok 3055 - attach backs up a 8080 base URL into prev_base_url
+ ---
+ duration_ms: 9.998937
+ type: 'test'
+ ...
+# Subtest: attach backs up a false base URL into prev_base_url
+ok 3056 - attach backs up a false base URL into prev_base_url
+ ---
+ duration_ms: 12.041512
+ type: 'test'
+ ...
+# Subtest: attach backs up a null base URL into prev_base_url
+ok 3057 - attach backs up a null base URL into prev_base_url
+ ---
+ duration_ms: 3.991216
+ type: 'test'
+ ...
+# Subtest: attach backs up a {"url":"x"} base URL into prev_base_url
+ok 3058 - attach backs up a {"url":"x"} base URL into prev_base_url
+ ---
+ duration_ms: 8.724971
+ type: 'test'
+ ...
+# Subtest: attach records no prev_base_url when the base URL is absent, whatever else env holds
+ok 3059 - attach records no prev_base_url when the base URL is absent, whatever else env holds
+ ---
+ duration_ms: 3.558548
+ type: 'test'
+ ...
+# Subtest: attach refuses a JSONC settings.json and marks the error as refused
+ok 3060 - attach refuses a JSONC settings.json and marks the error as refused
+ ---
+ duration_ms: 1.957485
+ type: 'test'
+ ...
+# Subtest: attach on malformed non-JSONC JSON is not marked as refused
+ok 3061 - attach on malformed non-JSONC JSON is not marked as refused
+ ---
+ duration_ms: 1.461381
+ type: 'test'
+ ...
+# Subtest: activate() attach() rethrows the JSONC refusal with the refusal mark intact
+ok 3062 - activate() attach() rethrows the JSONC refusal with the refusal mark intact
+ ---
+ duration_ms: 4.443303
+ type: 'test'
+ ...
+# Subtest: attach backs a non-object env up into the marker instead of discarding it
+ok 3063 - attach backs a non-object env up into the marker instead of discarding it
+ ---
+ duration_ms: 12.48738
+ type: 'test'
+ ...
+# Subtest: attach backs a non-array hooks. up into the marker instead of discarding it
+ok 3064 - attach backs a non-array hooks. up into the marker instead of discarding it
+ ---
+ duration_ms: 8.244922
+ type: 'test'
+ ...
+# Subtest: attach backs up a non-object hooks root, and a null block counts as present
+ok 3065 - attach backs up a non-object hooks root, and a null block counts as present
+ ---
+ duration_ms: 9.471896
+ type: 'test'
+ ...
+# Subtest: a re-attach keeps the first attach backup and stops warning about it
+ok 3066 - a re-attach keeps the first attach backup and stops warning about it
+ ---
+ duration_ms: 9.118399
+ type: 'test'
+ ...
+# Subtest: a second displacement at an already-backed-up path is reported as discarded, not as backed up
+ok 3067 - a second displacement at an already-backed-up path is reported as discarded, not as backed up
+ ---
+ duration_ms: 23.005997
+ type: 'test'
+ ...
+# Subtest: a backed-up null still outranks a later displacement: the collision test is presence, not truthiness
+ok 3068 - a backed-up null still outranks a later displacement: the collision test is presence, not truthiness
+ ---
+ duration_ms: 17.206212
+ type: 'test'
+ ...
+# Subtest: a hooks root and a hooks. backup cannot both go back; the shallower one wins and the other is reported
+ok 3069 - a hooks root and a hooks. backup cannot both go back; the shallower one wins and the other is reported
+ ---
+ duration_ms: 13.557345
+ type: 'test'
+ ...
+# Subtest: the same shallowest-first order keeps the older value when the older break was the shallow one
+ok 3070 - the same shallowest-first order keeps the older value when the older break was the shallow one
+ ---
+ duration_ms: 25.045246
+ type: 'test'
+ ...
+# Subtest: a hand-edited prev_malformed path cannot escape the settings object
+ok 3071 - a hand-edited prev_malformed path cannot escape the settings object
+ ---
+ duration_ms: 12.304602
+ type: 'test'
+ ...
+# Subtest: an absent env/hooks block attaches normally, with no backup and no warning
+ok 3072 - an absent env/hooks block attaches normally, with no backup and no warning
+ ---
+ duration_ms: 8.638049
+ type: 'test'
+ ...
+# Subtest: a present, well-formed but unusual env/hooks block is updated, not backed up
+ok 3073 - a present, well-formed but unusual env/hooks block is updated, not backed up
+ ---
+ duration_ms: 4.659632
+ type: 'test'
+ ...
+# Subtest: detach restores the backed-up env block from the marker
+ok 3074 - detach restores the backed-up env block from the marker
+ ---
+ duration_ms: 14.408931
+ type: 'test'
+ ...
+# Subtest: detach restores a backed-up hooks., recreating the emptied hooks root
+ok 3075 - detach restores a backed-up hooks., recreating the emptied hooks root
+ ---
+ duration_ms: 12.831813
+ type: 'test'
+ ...
+# Subtest: detach leaves a backed-up path alone, and reports it, when something else is using it now
+ok 3076 - detach leaves a backed-up path alone, and reports it, when something else is using it now
+ ---
+ duration_ms: 11.518808
+ type: 'test'
+ ...
+# Subtest: detach of an attach that displaced nothing is unchanged by the restore step
+ok 3077 - detach of an attach that displaced nothing is unchanged by the restore step
+ ---
+ duration_ms: 12.79749
+ type: 'test'
+ ...
+# Subtest: enricher drops a null-cwd row whose late-resolved cwd is .hypignore-governed
+ok 3078 - enricher drops a null-cwd row whose late-resolved cwd is .hypignore-governed
+ ---
+ duration_ms: 11.958716
+ type: 'test'
+ ...
+# Subtest: enricher enriches a null-cwd row whose late-resolved cwd is not ignored
+ok 3079 - enricher enriches a null-cwd row whose late-resolved cwd is not ignored
+ ---
+ duration_ms: 3.480981
+ type: 'test'
+ ...
+# Subtest: enricher leaves a null-cwd row unchanged when its session context never arrives (SDK/headless)
+ok 3080 - enricher leaves a null-cwd row unchanged when its session context never arrives (SDK/headless)
+ ---
+ duration_ms: 2.786665
+ type: 'test'
+ ...
+# Subtest: ignore-then-clean: the opening row resolves against the EARLIER ignored record and is DROPPED
+ok 3081 - ignore-then-clean: the opening row resolves against the EARLIER ignored record and is DROPPED
+ ---
+ duration_ms: 4.59273
+ type: 'test'
+ ...
+# Subtest: clean-then-ignore: the opening row resolves against the EARLIER clean record and is KEPT (not dropped)
+ok 3082 - clean-then-ignore: the opening row resolves against the EARLIER clean record and is KEPT (not dropped)
+ ---
+ duration_ms: 3.38823
+ type: 'test'
+ ...
+# Subtest: per-row selection within one batch: a later row takes the later record, an opening row the earlier one
+ok 3083 - per-row selection within one batch: a later row takes the later record, an opening row the earlier one
+ ---
+ duration_ms: 2.738703
+ type: 'test'
+ ...
+# Subtest: ts tie-break: the enriched record wins over the minimal one from the same fire
+ok 3084 - ts tie-break: the enriched record wins over the minimal one from the same fire
+ ---
+ duration_ms: 2.441881
+ type: 'test'
+ ...
+# Subtest: settleBatch REMOVES a null-cwd row whose late-resolved cwd is ignored
+ok 3085 - settleBatch REMOVES a null-cwd row whose late-resolved cwd is ignored
+ ---
+ duration_ms: 2.169618
+ type: 'test'
+ ...
+# Subtest: settleBatch keeps and enriches a null-cwd row whose late-resolved cwd is clean
+ok 3086 - settleBatch keeps and enriches a null-cwd row whose late-resolved cwd is clean
+ ---
+ duration_ms: 1.976304
+ type: 'test'
+ ...
+# Subtest: settleBatch dispatches null-cwd rows even when no row is a gateway fallback
+ok 3087 - settleBatch dispatches null-cwd rows even when no row is a gateway fallback
+ ---
+ duration_ms: 1.998208
+ type: 'test'
+ ...
+# Subtest: resettleBatch never drops a late-resolved ignore row (compaction is not a purge)
+ok 3088 - resettleBatch never drops a late-resolved ignore row (compaction is not a purge)
+ ---
+ duration_ms: 12.071617
+ type: 'test'
+ ...
+# Subtest: enricher upgrades a fallback row to native transcript identity
+ok 3089 - enricher upgrades a fallback row to native transcript identity
+ ---
+ duration_ms: 15.27715
+ type: 'test'
+ ...
+# Subtest: enricher leaves a row unchanged when no transcript line matches
+ok 3090 - enricher leaves a row unchanged when no transcript line matches
+ ---
+ duration_ms: 3.782809
+ type: 'test'
+ ...
+# Subtest: dataset settleBatch is a pure no-op when the batch has no fallback rows
+ok 3091 - dataset settleBatch is a pure no-op when the batch has no fallback rows
+ ---
+ duration_ms: 0.561353
+ type: 'test'
+ ...
+# Subtest: settleBatch dispatches to the enricher and dedupes the upgraded row against committed part_ids
+ok 3092 - settleBatch dispatches to the enricher and dedupes the upgraded row against committed part_ids
+ ---
+ duration_ms: 4.620012
+ type: 'test'
+ ...
+# Subtest: live projector returns no rows when the exchange cwd is governed by .hypignore
+ok 3093 - live projector returns no rows when the exchange cwd is governed by .hypignore
+ ---
+ duration_ms: 13.522543
+ type: 'test'
+ ...
+# Subtest: live projector records normally when the exchange cwd is not ignored
+ok 3094 - live projector records normally when the exchange cwd is not ignored
+ ---
+ duration_ms: 8.590116
+ type: 'test'
+ ...
+# Subtest: live projector with no resolved cwd records normally (no folder to match)
+ok 3095 - live projector with no resolved cwd records normally (no folder to match)
+ ---
+ duration_ms: 3.646302
+ type: 'test'
+ ...
+# Subtest: live projector returns no rows when the resolved session_id is in the gateway ignored-session set
+ok 3096 - live projector returns no rows when the resolved session_id is in the gateway ignored-session set
+ ---
+ duration_ms: 4.395651
+ type: 'test'
+ ...
+# Subtest: live projector records normally when the resolved session_id is not in the ignored set
+ok 3097 - live projector records normally when the resolved session_id is not in the ignored set
+ ---
+ duration_ms: 11.104828
+ type: 'test'
+ ...
+# Subtest: the session opt-out drop logs policy_source: session_opt_out with the matched session_id
+ok 3098 - the session opt-out drop logs policy_source: session_opt_out with the matched session_id
+ ---
+ duration_ms: 3.297412
+ type: 'test'
+ ...
+# Subtest: independence matrix: .hypignore-governed cwd + session NOT in the ignored set still drops
+ok 3099 - independence matrix: .hypignore-governed cwd + session NOT in the ignored set still drops
+ ---
+ duration_ms: 3.547431
+ type: 'test'
+ ...
+# Subtest: independence matrix: clean cwd + session IN the ignored set still drops
+ok 3100 - independence matrix: clean cwd + session IN the ignored set still drops
+ ---
+ duration_ms: 3.505688
+ type: 'test'
+ ...
+# Subtest: independence matrix: .hypignore-governed cwd AND session in the ignored set both drop (no double-count, no interaction)
+ok 3101 - independence matrix: .hypignore-governed cwd AND session in the ignored set both drop (no double-count, no interaction)
+ ---
+ duration_ms: 3.918265
+ type: 'test'
+ ...
+# Subtest: independence matrix: clean cwd + session NOT in the ignored set records normally
+ok 3102 - independence matrix: clean cwd + session NOT in the ignored set records normally
+ ---
+ duration_ms: 8.068874
+ type: 'test'
+ ...
+# Subtest: backfill skips an ignored session and yields only the clean one
+ok 3103 - backfill skips an ignored session and yields only the clean one
+ ---
+ duration_ms: 6.497966
+ type: 'test'
+ ...
+# Subtest: backfill imports every session when none are ignored
+ok 3104 - backfill imports every session when none are ignored
+ ---
+ duration_ms: 7.546781
+ type: 'test'
+ ...
+# Subtest: claude: the manifest and the registration code name the same skills
+ok 3105 - claude: the manifest and the registration code name the same skills
+ ---
+ duration_ms: 152.464781
+ type: 'test'
+ ...
+# Subtest: claude: every registered skill exists on disk
+ok 3106 - claude: every registered skill exists on disk
+ ---
+ duration_ms: 0.525679
+ type: 'test'
+ ...
+# Subtest: claude: every skill on disk is registered
+ok 3107 - claude: every skill on disk is registered
+ ---
+ duration_ms: 0.399136
+ type: 'test'
+ ...
+# Subtest: claude: the manifest description does not name a skill it no longer ships
+ok 3108 - claude: the manifest description does not name a skill it no longer ships
+ ---
+ duration_ms: 0.260746
+ type: 'test'
+ ...
+# Subtest: codex: the manifest and the registration code name the same skills
+ok 3109 - codex: the manifest and the registration code name the same skills
+ ---
+ duration_ms: 12.1912
+ type: 'test'
+ ...
+# Subtest: codex: every registered skill exists on disk
+ok 3110 - codex: every registered skill exists on disk
+ ---
+ duration_ms: 0.44056
+ type: 'test'
+ ...
+# Subtest: codex: every skill on disk is registered
+ok 3111 - codex: every skill on disk is registered
+ ---
+ duration_ms: 0.349141
+ type: 'test'
+ ...
+# Subtest: codex: the manifest description does not name a skill it no longer ships
+ok 3112 - codex: the manifest description does not name a skill it no longer ships
+ ---
+ duration_ms: 0.17808
+ type: 'test'
+ ...
+# Subtest: readCodexAuthMode returns an explicit auth_mode verbatim
+ok 3113 - readCodexAuthMode returns an explicit auth_mode verbatim
+ ---
+ duration_ms: 8.851543
+ type: 'test'
+ ...
+# Subtest: readCodexAuthMode infers chatgpt from tokens without an API key
+ok 3114 - readCodexAuthMode infers chatgpt from tokens without an API key
+ ---
+ duration_ms: 6.470605
+ type: 'test'
+ ...
+# Subtest: readCodexAuthMode does not infer chatgpt when an API key is present
+ok 3115 - readCodexAuthMode does not infer chatgpt when an API key is present
+ ---
+ duration_ms: 1.181286
+ type: 'test'
+ ...
+# Subtest: readCodexAuthMode returns undefined for missing or malformed files
+ok 3116 - readCodexAuthMode returns undefined for missing or malformed files
+ ---
+ duration_ms: 2.772163
+ type: 'test'
+ ...
+# Subtest: providerRouteForAuthMode maps chatgpt to the backend-api route
+ok 3117 - providerRouteForAuthMode maps chatgpt to the backend-api route
+ ---
+ duration_ms: 0.738573
+ type: 'test'
+ ...
+# Subtest: provider advertises a stable contribution shape
+ok 3118 - provider advertises a stable contribution shape
+ ---
+ duration_ms: 1.28986
+ type: 'test'
+ ...
+# Subtest: modern rollout projects into canonical ai_gateway_messages rows
+ok 3119 - modern rollout projects into canonical ai_gateway_messages rows
+ ---
+ duration_ms: 16.135384
+ type: 'test'
+ ...
+# Subtest: backfill skips a session whose cwd is .hypignore-ignored
+ok 3120 - backfill skips a session whose cwd is .hypignore-ignored
+ ---
+ duration_ms: 9.456733
+ type: 'test'
+ ...
+# Subtest: backfill is unaffected when a different cwd is ignored
+ok 3121 - backfill is unaffected when a different cwd is ignored
+ ---
+ duration_ms: 9.212973
+ type: 'test'
+ ...
+# Subtest: a blank or relative rollout cwd never reaches the usage-policy gate
+ok 3122 - a blank or relative rollout cwd never reaches the usage-policy gate
+ ---
+ duration_ms: 16.103646
+ type: 'test'
+ ...
+# Subtest: token_count event folds per-turn usage (net of cache) onto the turn assistant message
+ok 3123 - token_count event folds per-turn usage (net of cache) onto the turn assistant message
+ ---
+ duration_ms: 15.991045
+ type: 'test'
+ ...
+# Subtest: multi-turn token_count: each turn stamps its own per-turn delta on its own last assistant row
+ok 3124 - multi-turn token_count: each turn stamps its own per-turn delta on its own last assistant row
+ ---
+ duration_ms: 6.943294
+ type: 'test'
+ ...
+# Subtest: backfill redacts credential userinfo from the git remote (LLP 0032)
+ok 3125 - backfill redacts credential userinfo from the git remote (LLP 0032)
+ ---
+ duration_ms: 6.191631
+ type: 'test'
+ ...
+# Subtest: a subagent rollout partitions on session_meta.session_id, with the thread in conversation_id
+ok 3126 - a subagent rollout partitions on session_meta.session_id, with the thread in conversation_id
+ ---
+ duration_ms: 13.690558
+ type: 'test'
+ ...
+# Subtest: a rollout with no session_meta.session_id keeps the thread as its partition key
+ok 3127 - a rollout with no session_meta.session_id keeps the thread as its partition key
+ ---
+ duration_ms: 4.04013
+ type: 'test'
+ ...
+# Subtest: native ids are preserved verbatim; sidechain inferred from thread_source
+ok 3128 - native ids are preserved verbatim; sidechain inferred from thread_source
+ ---
+ duration_ms: 8.381458
+ type: 'test'
+ ...
+# Subtest: sessions are grouped one-per-file across nested date partitions
+ok 3129 - sessions are grouped one-per-file across nested date partitions
+ ---
+ duration_ms: 5.249199
+ type: 'test'
+ ...
+# Subtest: legacy single-document rollouts parse version-defensively
+ok 3130 - legacy single-document rollouts parse version-defensively
+ ---
+ duration_ms: 2.728136
+ type: 'test'
+ ...
+# Subtest: app/browser storage is flagged via unsupported_location, never parsed
+ok 3131 - app/browser storage is flagged via unsupported_location, never parsed
+ ---
+ duration_ms: 6.903573
+ type: 'test'
+ ...
+# Subtest: encrypted reasoning is never projected; only plaintext summary is kept
+ok 3132 - encrypted reasoning is never projected; only plaintext summary is kept
+ ---
+ duration_ms: 11.256138
+ type: 'test'
+ ...
+# Subtest: since bound filters out items older than the window
+ok 3133 - since bound filters out items older than the window
+ ---
+ duration_ms: 5.479268
+ type: 'test'
+ ...
+# Subtest: reruns are deterministic (idempotent items and rows)
+ok 3134 - reruns are deterministic (idempotent items and rows)
+ ---
+ duration_ms: 7.420599
+ type: 'test'
+ ...
+# Subtest: missing sessions root yields nothing without throwing
+ok 3135 - missing sessions root yields nothing without throwing
+ ---
+ duration_ms: 3.510917
+ type: 'test'
+ ...
+# Subtest: diagnostic-only history source is detected but not used as canonical
+ok 3136 - diagnostic-only history source is detected but not used as canonical
+ ---
+ duration_ms: 6.64526
+ type: 'test'
+ ...
+# Subtest: an unclassified interactive session prints a plain-text classification nag
+ok 3137 - an unclassified interactive session prints a plain-text classification nag
+ ---
+ duration_ms: 2.659693
+ type: 'test'
+ ...
+# Subtest: no prompt -> no output
+ok 3138 - no prompt -> no output
+ ---
+ duration_ms: 0.96758
+ type: 'test'
+ ...
+# Subtest: falls back to ctx.cwd when no event cwd is piped
+ok 3139 - falls back to ctx.cwd when no event cwd is piped
+ ---
+ duration_ms: 0.528944
+ type: 'test'
+ ...
+# Subtest: an evaluation that throws is swallowed with exit 0
+ok 3140 - an evaluation that throws is swallowed with exit 0
+ ---
+ duration_ms: 0.51316
+ type: 'test'
+ ...
+# Subtest: isInteractiveCodexSession honors CI and the escape hatch
+ok 3141 - isInteractiveCodexSession honors CI and the escape hatch
+ ---
+ duration_ms: 0.128656
+ type: 'test'
+ ...
+# Subtest: validateCodexConfig accepts an empty / absent config
+ok 3142 - validateCodexConfig accepts an empty / absent config
+ ---
+ duration_ms: 1.307977
+ type: 'test'
+ ...
+# Subtest: validateCodexConfig accepts a full backfill block
+ok 3143 - validateCodexConfig accepts a full backfill block
+ ---
+ duration_ms: 0.182748
+ type: 'test'
+ ...
+# Subtest: validateCodexConfig rejects a non-object config
+ok 3144 - validateCodexConfig rejects a non-object config
+ ---
+ duration_ms: 0.124459
+ type: 'test'
+ ...
+# Subtest: validateCodexConfig rejects a malformed backfill block
+ok 3145 - validateCodexConfig rejects a malformed backfill block
+ ---
+ duration_ms: 0.228437
+ type: 'test'
+ ...
+# Subtest: validateBackfillSection mounts pointers under the supplied prefix
+ok 3146 - validateBackfillSection mounts pointers under the supplied prefix
+ ---
+ duration_ms: 0.092761
+ type: 'test'
+ ...
+# Subtest: validateCodexConfig accepts an attach block
+ok 3147 - validateCodexConfig accepts an attach block
+ ---
+ duration_ms: 0.168666
+ type: 'test'
+ ...
+# Subtest: validateCodexConfig rejects a malformed attach block
+ok 3148 - validateCodexConfig rejects a malformed attach block
+ ---
+ duration_ms: 0.172392
+ type: 'test'
+ ...
+# Subtest: validateAttachSection mounts pointers under the supplied prefix
+ok 3149 - validateAttachSection mounts pointers under the supplied prefix
+ ---
+ duration_ms: 0.101054
+ type: 'test'
+ ...
+# Subtest: the registered codex section drives validatePluginConfig
+ok 3150 - the registered codex section drives validatePluginConfig
+ ---
+ duration_ms: 0.736139
+ type: 'test'
+ ...
+# Subtest: the codex picker names Codex Desktop, not just "Codex conversations"
+ok 3151 - the codex picker names Codex Desktop, not just "Codex conversations"
+ ---
+ duration_ms: 3.942472
+ type: 'test'
+ ...
+# Subtest: the codex plugin description names both Codex surfaces
+ok 3152 - the codex plugin description names both Codex surfaces
+ ---
+ duration_ms: 0.922552
+ type: 'test'
+ ...
+# Subtest: the Codex app-container unsupported_location says what IS still captured
+ok 3153 - the Codex app-container unsupported_location says what IS still captured
+ ---
+ duration_ms: 5.751422
+ type: 'test'
+ ...
+# Subtest: project() returns no projection when the exchange cwd is .hypignore-ignored
+ok 3154 - project() returns no projection when the exchange cwd is .hypignore-ignored
+ ---
+ duration_ms: 2.554493
+ type: 'test'
+ ...
+# Subtest: project() is unaffected when the exchange cwd is not ignored
+ok 3155 - project() is unaffected when the exchange cwd is not ignored
+ ---
+ duration_ms: 1.017116
+ type: 'test'
+ ...
+# Subtest: project() emits a usage_policy_drop log on an ignored cwd
+ok 3156 - project() emits a usage_policy_drop log on an ignored cwd
+ ---
+ duration_ms: 0.359416
+ type: 'test'
+ ...
+# Subtest: project() escalates a fail-safe clamp to a warn-level drop with the declared token (R3)
+ok 3157 - project() escalates a fail-safe clamp to a warn-level drop with the declared token (R3)
+ ---
+ duration_ms: 0.435582
+ type: 'test'
+ ...
+# Subtest: project() computes no .hypignore verdict from a RELATIVE in-band cwd
+ok 3158 - project() computes no .hypignore verdict from a RELATIVE in-band cwd
+ ---
+ duration_ms: 0.290001
+ type: 'test'
+ ...
+# Subtest: project() computes no .hypignore verdict from a BLANK in-band cwd
+ok 3159 - project() computes no .hypignore verdict from a BLANK in-band cwd
+ ---
+ duration_ms: 0.212413
+ type: 'test'
+ ...
+# Subtest: project() logs an unusable in-band cwd rather than skipping the gate silently
+ok 3160 - project() logs an unusable in-band cwd rather than skipping the gate silently
+ ---
+ duration_ms: 0.235077
+ type: 'test'
+ ...
+# Subtest: the in-band cwd seam answers exactly as the shared sessionMetaCwd predicate does
+ok 3161 - the in-band cwd seam answers exactly as the shared sessionMetaCwd predicate does
+ ---
+ duration_ms: 0.851294
+ type: 'test'
+ ...
+# Subtest: project() drops every conversation_id thread under one ignored session_id (documents the over-drop, R8)
+ok 3162 - project() drops every conversation_id thread under one ignored session_id (documents the over-drop, R8)
+ ---
+ duration_ms: 0.619191
+ type: 'test'
+ ...
+# Subtest: project() leaves a different session in the same run unaffected
+ok 3163 - project() leaves a different session in the same run unaffected
+ ---
+ duration_ms: 0.752734
+ type: 'test'
+ ...
+# Subtest: project() emits a usage_policy_drop log with policy_source: session_opt_out and the matched session_id
+ok 3164 - project() emits a usage_policy_drop log with policy_source: session_opt_out and the matched session_id
+ ---
+ duration_ms: 0.298454
+ type: 'test'
+ ...
+# Subtest: the session opt-out drop is also visible through the gateway message-projector dispatcher (parity with Claude)
+ok 3165 - the session opt-out drop is also visible through the gateway message-projector dispatcher (parity with Claude)
+ ---
+ duration_ms: 0.54553
+ type: 'test'
+ ...
+# Subtest: match() accepts the three transports it owns and rejects others
+ok 3166 - match() accepts the three transports it owns and rejects others
+ ---
+ duration_ms: 0.136327
+ type: 'test'
+ ...
+# Subtest: match() also accepts non-codex paths tagged with x-codex-turn-metadata
+ok 3167 - match() also accepts non-codex paths tagged with x-codex-turn-metadata
+ ---
+ duration_ms: 0.115776
+ type: 'test'
+ ...
+# Subtest: OpenAI Chat projection: request+response messages roll up into user+assistant
+ok 3168 - OpenAI Chat projection: request+response messages roll up into user+assistant
+ ---
+ duration_ms: 0.244411
+ type: 'test'
+ ...
+# Subtest: OpenAI Chat projection normalizes usage onto the assistant response
+ok 3169 - OpenAI Chat projection normalizes usage onto the assistant response
+ ---
+ duration_ms: 0.360959
+ type: 'test'
+ ...
+# Subtest: OpenAI Chat tool messages map to tool_result blocks
+ok 3170 - OpenAI Chat tool messages map to tool_result blocks
+ ---
+ duration_ms: 0.217791
+ type: 'test'
+ ...
+# Subtest: OpenAI Responses with output_text in the body produces an assistant message
+ok 3171 - OpenAI Responses with output_text in the body produces an assistant message
+ ---
+ duration_ms: 1.335219
+ type: 'test'
+ ...
+# Subtest: OpenAI Responses body usage is normalized onto one assistant response item
+ok 3172 - OpenAI Responses body usage is normalized onto one assistant response item
+ ---
+ duration_ms: 0.300176
+ type: 'test'
+ ...
+# Subtest: OpenAI Responses captures top-level instructions into system_text
+ok 3173 - OpenAI Responses captures top-level instructions into system_text
+ ---
+ duration_ms: 0.224942
+ type: 'test'
+ ...
+# Subtest: OpenAI Chat system field still wins over instructions
+ok 3174 - OpenAI Chat system field still wins over instructions
+ ---
+ duration_ms: 0.133243
+ type: 'test'
+ ...
+# Subtest: OpenAI Responses SSE deltas reconstruct the assistant body
+ok 3175 - OpenAI Responses SSE deltas reconstruct the assistant body
+ ---
+ duration_ms: 0.359467
+ type: 'test'
+ ...
+# Subtest: OpenAI Responses SSE completed usage is normalized onto the assistant response
+ok 3176 - OpenAI Responses SSE completed usage is normalized onto the assistant response
+ ---
+ duration_ms: 0.202297
+ type: 'test'
+ ...
+# Subtest: OpenAI Responses function_call in input becomes an assistant tool_use message
+ok 3177 - OpenAI Responses function_call in input becomes an assistant tool_use message
+ ---
+ duration_ms: 0.23683
+ type: 'test'
+ ...
+# Subtest: OpenAI Responses bare-string content array entries project as text blocks
+ok 3178 - OpenAI Responses bare-string content array entries project as text blocks
+ ---
+ duration_ms: 0.122025
+ type: 'test'
+ ...
+# Subtest: OpenAI Responses reasoning items in input replay project as assistant thinking
+ok 3179 - OpenAI Responses reasoning items in input replay project as assistant thinking
+ ---
+ duration_ms: 0.246785
+ type: 'test'
+ ...
+# Subtest: OpenAI Responses custom_tool_call uses payload.input when arguments is missing
+ok 3180 - OpenAI Responses custom_tool_call uses payload.input when arguments is missing
+ ---
+ duration_ms: 0.137929
+ type: 'test'
+ ...
+# Subtest: OpenAI Responses fans out response.output items into per-item assistant messages
+ok 3181 - OpenAI Responses fans out response.output items into per-item assistant messages
+ ---
+ duration_ms: 0.138751
+ type: 'test'
+ ...
+# Subtest: OpenAI Responses turn-1 response shape matches turn-2 input replay shape (dedupe)
+ok 3182 - OpenAI Responses turn-1 response shape matches turn-2 input replay shape (dedupe)
+ ---
+ duration_ms: 0.155006
+ type: 'test'
+ ...
+# Subtest: OpenAI Responses SSE captures tool_use from response.output_item.done
+ok 3183 - OpenAI Responses SSE captures tool_use from response.output_item.done
+ ---
+ duration_ms: 0.260837
+ type: 'test'
+ ...
+# Subtest: OpenAI Responses SSE prefers full response.completed body when present
+ok 3184 - OpenAI Responses SSE prefers full response.completed body when present
+ ---
+ duration_ms: 0.273616
+ type: 'test'
+ ...
+# Subtest: OpenAI Responses SSE merges streamed text into a tool-only completed body
+ok 3185 - OpenAI Responses SSE merges streamed text into a tool-only completed body
+ ---
+ duration_ms: 0.194355
+ type: 'test'
+ ...
+# Subtest: Codex turn metadata + headers project into first-class columns and codex.* attributes
+ok 3186 - Codex turn metadata + headers project into first-class columns and codex.* attributes
+ ---
+ duration_ms: 0.6249
+ type: 'test'
+ ...
+# Subtest: live projector redacts credential userinfo from the turn-metadata remote (LLP 0032)
+ok 3187 - live projector redacts credential userinfo from the turn-metadata remote (LLP 0032)
+ ---
+ duration_ms: 0.347648
+ type: 'test'
+ ...
+# Subtest: thread_source=subagent flips is_sidechain to true
+ok 3188 - thread_source=subagent flips is_sidechain to true
+ ---
+ duration_ms: 0.307007
+ type: 'test'
+ ...
+# Subtest: subagent turn metadata captures parent_thread_id (lineage)
+ok 3189 - subagent turn metadata captures parent_thread_id (lineage)
+ ---
+ duration_ms: 0.219213
+ type: 'test'
+ ...
+# Subtest: Codex workspace selection prefers recorded cwd over first metadata key
+ok 3190 - Codex workspace selection prefers recorded cwd over first metadata key
+ ---
+ duration_ms: 0.355831
+ type: 'test'
+ ...
+# Subtest: the .hypignore gate uses the request cwd, not a substituted workspace key (\#476 case a)
+ok 3191 - the .hypignore gate uses the request cwd, not a substituted workspace key (\#476 case a)
+ ---
+ duration_ms: 0.288819
+ type: 'test'
+ ...
+# Subtest: an unrelated ignored workspace key does not drop a session it never covered (\#476 case b)
+ok 3192 - an unrelated ignored workspace key does not drop a session it never covered (\#476 case b)
+ ---
+ duration_ms: 0.287928
+ type: 'test'
+ ...
+# Subtest: a refused workspace substitution is logged with hashed paths, not silently applied (\#476 case c)
+ok 3193 - a refused workspace substitution is logged with hashed paths, not silently applied (\#476 case c)
+ ---
+ duration_ms: 0.28292
+ type: 'test'
+ ...
+# Subtest: a refused workspace substitution still enriches the row from the workspace key (\#476)
+ok 3194 - a refused workspace substitution still enriches the row from the workspace key (\#476)
+ ---
+ duration_ms: 0.25006
+ type: 'test'
+ ...
+# Subtest: the workspace key still supplies the gate cwd when the request states none (\#476)
+ok 3195 - the workspace key still supplies the gate cwd when the request states none (\#476)
+ ---
+ duration_ms: 0.200976
+ type: 'test'
+ ...
+# Subtest: no workspace-cwd refusal is logged when the key matches or the request states no cwd (\#476)
+ok 3196 - no workspace-cwd refusal is logged when the key matches or the request states no cwd (\#476)
+ ---
+ duration_ms: 0.303341
+ type: 'test'
+ ...
+# Subtest: an ordinary subdirectory-of-workspace session logs no workspace-cwd refusal (\#481)
+ok 3197 - an ordinary subdirectory-of-workspace session logs no workspace-cwd refusal (\#481)
+ ---
+ duration_ms: 1.24349
+ type: 'test'
+ ...
+# Subtest: a workspace key off the session cwd ancestry is still a refusal (\#481)
+ok 3198 - a workspace key off the session cwd ancestry is still a refusal (\#481)
+ ---
+ duration_ms: 1.222238
+ type: 'test'
+ ...
+# Subtest: an ancestor key that resolves MORE restrictively than the cwd is silent (documents the residue, \#481)
+ok 3199 - an ancestor key that resolves MORE restrictively than the cwd is silent (documents the residue, \#481)
+ ---
+ duration_ms: 1.544397
+ type: 'test'
+ ...
+# Subtest: non-codex provider has no codex turn metadata but still stamps identity_source for symmetry
+ok 3200 - non-codex provider has no codex turn metadata but still stamps identity_source for symmetry
+ ---
+ duration_ms: 0.192663
+ type: 'test'
+ ...
+# Subtest: project() returns undefined when the request body is missing or malformed
+ok 3201 - project() returns undefined when the request body is missing or malformed
+ ---
+ duration_ms: 0.136889
+ type: 'test'
+ ...
+# Subtest: project() returns undefined when no messages can be extracted
+ok 3202 - project() returns undefined when no messages can be extracted
+ ---
+ duration_ms: 1.110127
+ type: 'test'
+ ...
+# Subtest: conversation_id falls back to a stable hash when no codex metadata or session id is present
+ok 3203 - conversation_id falls back to a stable hash when no codex metadata or session id is present
+ ---
+ duration_ms: 0.133232
+ type: 'test'
+ ...
+# Subtest: Codex lineage resolves from the durable body client_metadata when no lineage header is sent
+ok 3204 - Codex lineage resolves from the durable body client_metadata when no lineage header is sent
+ ---
+ duration_ms: 0.145661
+ type: 'test'
+ ...
+# Subtest: body client_metadata alone identifies a Codex exchange on a generic responses path
+ok 3205 - body client_metadata alone identifies a Codex exchange on a generic responses path
+ ---
+ duration_ms: 0.240005
+ type: 'test'
+ ...
+# Subtest: Codex lineage resolves from the compatibility headers Codex actually sends
+ok 3206 - Codex lineage resolves from the compatibility headers Codex actually sends
+ ---
+ duration_ms: 0.205833
+ type: 'test'
+ ...
+# Subtest: a bare lineage header name Codex never sends resolves to nothing, not a wrong value
+ok 3207 - a bare lineage header name Codex never sends resolves to nothing, not a wrong value
+ ---
+ duration_ms: 0.421791
+ type: 'test'
+ ...
+# Subtest: body client_metadata wins over the turn-metadata blob when the two disagree
+ok 3208 - body client_metadata wins over the turn-metadata blob when the two disagree
+ ---
+ duration_ms: 0.322901
+ type: 'test'
+ ...
+# Subtest: agreeing lineage surfaces record no lineage_conflict
+ok 3209 - agreeing lineage surfaces record no lineage_conflict
+ ---
+ duration_ms: 0.184981
+ type: 'test'
+ ...
+# Subtest: a lineage field only one surface states is not a conflict
+ok 3210 - a lineage field only one surface states is not a conflict
+ ---
+ duration_ms: 0.181746
+ type: 'test'
+ ...
+# Subtest: lineage_source names the surface the thread actually came from
+ok 3211 - lineage_source names the surface the thread actually came from
+ ---
+ duration_ms: 0.183499
+ type: 'test'
+ ...
+# Subtest: a non-Codex client sending only a flat client_metadata identity pair is not treated as Codex
+ok 3212 - a non-Codex client sending only a flat client_metadata identity pair is not treated as Codex
+ ---
+ duration_ms: 0.489754
+ type: 'test'
+ ...
+# Subtest: a transport-corroborated Codex request still resolves lineage from a flat-only client_metadata
+ok 3213 - a transport-corroborated Codex request still resolves lineage from a flat-only client_metadata
+ ---
+ duration_ms: 0.154985
+ type: 'test'
+ ...
+# Subtest: every route Codex posts to is matched, so a body-only Codex request is never dropped at the gate
+ok 3214 - every route Codex posts to is matched, so a body-only Codex request is never dropped at the gate
+ ---
+ duration_ms: 0.170419
+ type: 'test'
+ ...
+# Subtest: part_id and message_id stay byte-identical for the turn-metadata shape already recorded
+ok 3215 - part_id and message_id stay byte-identical for the turn-metadata shape already recorded
+ ---
+ duration_ms: 1.851464
+ type: 'test'
+ ...
+# Subtest: strips user:token userinfo from an https remote
+ok 3216 - strips user:token userinfo from an https remote
+ ---
+ duration_ms: 0.88822
+ type: 'test'
+ ...
+# Subtest: strips a token-only userinfo from an https remote
+ok 3217 - strips a token-only userinfo from an https remote
+ ---
+ duration_ms: 0.18436
+ type: 'test'
+ ...
+# Subtest: strips userinfo from an ssh:// URL (key-auth, but harmless to drop)
+ok 3218 - strips userinfo from an ssh:// URL (key-auth, but harmless to drop)
+ ---
+ duration_ms: 0.112661
+ type: 'test'
+ ...
+# Subtest: leaves the scp-like SSH form intact (git@ is the conventional user, no secret)
+ok 3219 - leaves the scp-like SSH form intact (git@ is the conventional user, no secret)
+ ---
+ duration_ms: 1.25023
+ type: 'test'
+ ...
+# Subtest: leaves a credential-free https remote unchanged
+ok 3220 - leaves a credential-free https remote unchanged
+ ---
+ duration_ms: 0.139412
+ type: 'test'
+ ...
+# Subtest: passes undefined / empty through unchanged
+ok 3221 - passes undefined / empty through unchanged
+ ---
+ duration_ms: 0.110618
+ type: 'test'
+ ...
+# Subtest: does not mistake an @ in the path for userinfo
+ok 3222 - does not mistake an @ in the path for userinfo
+ ---
+ duration_ms: 0.096617
+ type: 'test'
+ ...
+# Subtest: Step 1 sends the session container, never a thread id
+ok 3223 - Step 1 sends the session container, never a thread id
+ ---
+ duration_ms: 0.852015
+ type: 'test'
+ ...
+# Subtest: Step 1 resolves the rollout by cwd and refuses rather than guessing
+ok 3224 - Step 1 resolves the rollout by cwd and refuses rather than guessing
+ ---
+ duration_ms: 0.206574
+ type: 'test'
+ ...
+# Subtest: Step 1 reports the id as inferred and names both ways the opt-out lapses
+ok 3225 - Step 1 reports the id as inferred and names both ways the opt-out lapses
+ ---
+ duration_ms: 1.040902
+ type: 'test'
+ ...
+# Subtest: subscription-route Codex with no in-band cwd is .hypignore-dropped via the rollout cwd
+ok 3226 - subscription-route Codex with no in-band cwd is .hypignore-dropped via the rollout cwd
+ ---
+ duration_ms: 2.923714
+ type: 'test'
+ ...
+# Subtest: subscription-route Codex records the rollout cwd on the row (live/backfill parity)
+ok 3227 - subscription-route Codex records the rollout cwd on the row (live/backfill parity)
+ ---
+ duration_ms: 0.994271
+ type: 'test'
+ ...
+# Subtest: an in-band cwd stays the fast path and short-circuits the rollout lookup
+ok 3228 - an in-band cwd stays the fast path and short-circuits the rollout lookup
+ ---
+ duration_ms: 0.647124
+ type: 'test'
+ ...
+# Subtest: a workspaces key does not preempt the rollout session_meta.cwd (\#480)
+ok 3229 - a workspaces key does not preempt the rollout session_meta.cwd (\#480)
+ ---
+ duration_ms: 0.425957
+ type: 'test'
+ ...
+# Subtest: the rollout cwd is stamped on the row while the workspace key still enriches it (\#480)
+ok 3230 - the rollout cwd is stamped on the row while the workspace key still enriches it (\#480)
+ ---
+ duration_ms: 0.581123
+ type: 'test'
+ ...
+# Subtest: the workspace key still gates when there is no rollout to outrank it (\#480)
+ok 3231 - the workspace key still gates when there is no rollout to outrank it (\#480)
+ ---
+ duration_ms: 0.383463
+ type: 'test'
+ ...
+# Subtest: a relative workspace key is refused rather than resolved against the daemon (\#480, \#471)
+ok 3232 - a relative workspace key is refused rather than resolved against the daemon (\#480, \#471)
+ ---
+ duration_ms: 0.377394
+ type: 'test'
+ ...
+# Subtest: createRolloutCwdResolver reads session_meta.cwd from the session rollout
+ok 3233 - createRolloutCwdResolver reads session_meta.cwd from the session rollout
+ ---
+ duration_ms: 10.904804
+ type: 'test'
+ ...
+# Subtest: createRolloutCwdResolver caches per session id (bounded fs on the hot path)
+ok 3234 - createRolloutCwdResolver caches per session id (bounded fs on the hot path)
+ ---
+ duration_ms: 2.344674
+ type: 'test'
+ ...
+# Subtest: createRolloutCwdResolver returns undefined when the sessions root is missing
+ok 3235 - createRolloutCwdResolver returns undefined when the sessions root is missing
+ ---
+ duration_ms: 0.413399
+ type: 'test'
+ ...
+# Subtest: a rollout whose first line is a different envelope type yields no cwd, even carrying one
+ok 3236 - a rollout whose first line is a different envelope type yields no cwd, even carrying one
+ ---
+ duration_ms: 0.461111
+ type: 'test'
+ ...
+# Subtest: a blank session_meta.cwd is no cwd, not a blank path handed to the policy matcher
+ok 3237 - a blank session_meta.cwd is no cwd, not a blank path handed to the policy matcher
+ ---
+ duration_ms: 0.397404
+ type: 'test'
+ ...
+# Subtest: a relative session_meta.cwd is no cwd: the matcher would resolve it against the daemon
+ok 3238 - a relative session_meta.cwd is no cwd: the matcher would resolve it against the daemon
+ ---
+ duration_ms: 0.929763
+ type: 'test'
+ ...
+# Subtest: a missing-then-present rollout is re-resolved after the negative TTL, but a miss is cached within it
+ok 3239 - a missing-then-present rollout is re-resolved after the negative TTL, but a miss is cached within it
+ ---
+ duration_ms: 1.94715
+ type: 'test'
+ ...
+# Subtest: a transient read error is retried rather than cached as a permanent miss
+ok 3240 - a transient read error is retried rather than cached as a permanent miss
+ ---
+ duration_ms: 1.306466
+ type: 'test'
+ ...
+# Subtest: a newest-dir rollout is found without descending the older-date branch; an older-dir rollout still resolves
+ok 3241 - a newest-dir rollout is found without descending the older-date branch; an older-dir rollout still resolves
+ ---
+ duration_ms: 3.780436
+ type: 'test'
+ ...
+# Subtest: a subagent turn is .hypignore-dropped by ITS OWN rollout cwd, not the root thread's
+ok 3242 - a subagent turn is .hypignore-dropped by ITS OWN rollout cwd, not the root thread's
+ ---
+ duration_ms: 2.160944
+ type: 'test'
+ ...
+# Subtest: a subagent turn outside an ignored root is recorded, with its own cwd on the row
+ok 3243 - a subagent turn outside an ignored root is recorded, with its own cwd on the row
+ ---
+ duration_ms: 4.11238
+ type: 'test'
+ ...
+# Subtest: the root thread of the same session still resolves its own cwd
+ok 3244 - the root thread of the same session still resolves its own cwd
+ ---
+ duration_ms: 3.387588
+ type: 'test'
+ ...
+# Subtest: a legacy rollout pair with no session_id still resolves each thread's own cwd
+ok 3245 - a legacy rollout pair with no session_id still resolves each thread's own cwd
+ ---
+ duration_ms: 3.731372
+ type: 'test'
+ ...
+# Subtest: a rollout whose body disagrees with its filename is refused, not silently used
+ok 3246 - a rollout whose body disagrees with its filename is refused, not silently used
+ ---
+ duration_ms: 1.05951
+ type: 'test'
+ ...
+# Subtest: a subagent turn that states lineage but not its own thread id resolves no cwd
+ok 3247 - a subagent turn that states lineage but not its own thread id resolves no cwd
+ ---
+ duration_ms: 1.449843
+ type: 'test'
+ ...
+# Subtest: a turn whose metadata states thread_source=subagent but no thread id resolves no cwd
+ok 3248 - a turn whose metadata states thread_source=subagent but no thread id resolves no cwd
+ ---
+ duration_ms: 2.695286
+ type: 'test'
+ ...
+# Subtest: a turn stating lineage only via x-codex-parent-thread-id resolves no cwd
+ok 3249 - a turn stating lineage only via x-codex-parent-thread-id resolves no cwd
+ ---
+ duration_ms: 1.720616
+ type: 'test'
+ ...
+# Subtest: a turn stating lineage only via x-openai-subagent resolves no cwd
+ok 3250 - a turn stating lineage only via x-openai-subagent resolves no cwd
+ ---
+ duration_ms: 2.073652
+ type: 'test'
+ ...
+# Subtest: DOCUMENTED MIRROR: x-openai-subagent=review with no thread id refuses a container the root would have resolved
+ok 3251 - DOCUMENTED MIRROR: x-openai-subagent=review with no thread id refuses a container the root would have resolved
+ ---
+ duration_ms: 3.472649
+ type: 'test'
+ ...
+# Subtest: x-openai-subagent=review does NOT cost anything once the turn states its thread
+ok 3252 - x-openai-subagent=review does NOT cost anything once the turn states its thread
+ ---
+ duration_ms: 2.286295
+ type: 'test'
+ ...
+# Subtest: DOCUMENTED MIRROR: x-openai-subagent=compact with no thread id refuses a container the root would have resolved
+ok 3253 - DOCUMENTED MIRROR: x-openai-subagent=compact with no thread id refuses a container the root would have resolved
+ ---
+ duration_ms: 1.566501
+ type: 'test'
+ ...
+# Subtest: x-openai-subagent=compact does NOT cost anything once the turn states its thread
+ok 3254 - x-openai-subagent=compact does NOT cost anything once the turn states its thread
+ ---
+ duration_ms: 3.12516
+ type: 'test'
+ ...
+# Subtest: DOCUMENTED MIRROR: x-openai-subagent=memory_consolidation with no thread id refuses a container the root would have resolved
+ok 3255 - DOCUMENTED MIRROR: x-openai-subagent=memory_consolidation with no thread id refuses a container the root would have resolved
+ ---
+ duration_ms: 2.01978
+ type: 'test'
+ ...
+# Subtest: x-openai-subagent=memory_consolidation does NOT cost anything once the turn states its thread
+ok 3256 - x-openai-subagent=memory_consolidation does NOT cost anything once the turn states its thread
+ ---
+ duration_ms: 1.817102
+ type: 'test'
+ ...
+# Subtest: a memory-consolidation turn is answered by the body map, whose id pair the blob withholds
+ok 3257 - a memory-consolidation turn is answered by the body map, whose id pair the blob withholds
+ ---
+ duration_ms: 1.653413
+ type: 'test'
+ ...
+# Subtest: a session_meta line with a cwd but no payload.id is refused, not matched by its filename
+ok 3258 - a session_meta line with a cwd but no payload.id is refused, not matched by its filename
+ ---
+ duration_ms: 1.198201
+ type: 'test'
+ ...
+# Subtest: DOCUMENTED GAP: a turn stating a container and NO lineage at all is taken as its root thread
+ok 3259 - DOCUMENTED GAP: a turn stating a container and NO lineage at all is taken as its root thread
+ ---
+ duration_ms: 2.333867
+ type: 'test'
+ ...
+# Subtest: prepareAttach inserts managed Codex provider blocks and preserves previous provider
+ok 3260 - prepareAttach inserts managed Codex provider blocks and preserves previous provider
+ ---
+ duration_ms: 3.224491
+ type: 'test'
+ ...
+# Subtest: prepareAttach is idempotent for an already managed config
+ok 3261 - prepareAttach is idempotent for an already managed config
+ ---
+ duration_ms: 0.404455
+ type: 'test'
+ ...
+# Subtest: prepareAttach records the prior model_provider in the marked block undo record
+ok 3262 - prepareAttach records the prior model_provider in the marked block undo record
+ ---
+ duration_ms: 0.323762
+ type: 'test'
+ ...
+# Subtest: re-attach keeps the original previous_model_provider, not the managed one
+ok 3263 - re-attach keeps the original previous_model_provider, not the managed one
+ ---
+ duration_ms: 0.277451
+ type: 'test'
+ ...
+# Subtest: prepareDetach removes managed Codex blocks and restores previous provider
+ok 3264 - prepareDetach removes managed Codex blocks and restores previous provider
+ ---
+ duration_ms: 0.451026
+ type: 'test'
+ ...
+# Subtest: prepareDetach is a no-op when HypAware did not manage the config
+ok 3265 - prepareDetach is a no-op when HypAware did not manage the config
+ ---
+ duration_ms: 0.623558
+ type: 'test'
+ ...
+# Subtest: managed marker parsing rejects unterminated blocks
+ok 3266 - managed marker parsing rejects unterminated blocks
+ ---
+ duration_ms: 0.429373
+ type: 'test'
+ ...
+# Subtest: complete sends x-api-key + anthropic-version, lifts system, maps tools, parses blocks
+ok 3267 - complete sends x-api-key + anthropic-version, lifts system, maps tools, parses blocks
+ ---
+ duration_ms: 2.576707
+ type: 'test'
+ ...
+# Subtest: complete picks per-request model over the provider default (tiering)
+ok 3268 - complete picks per-request model over the provider default (tiering)
+ ---
+ duration_ms: 0.404245
+ type: 'test'
+ ...
+# Subtest: complete merges params (thinking/output_config) and lifts betas to the anthropic-beta header
+ok 3269 - complete merges params (thinking/output_config) and lifts betas to the anthropic-beta header
+ ---
+ duration_ms: 0.32865
+ type: 'test'
+ ...
+# Subtest: complete returns a refusal as stopReason without throwing (HTTP 200)
+ok 3270 - complete returns a refusal as stopReason without throwing (HTTP 200)
+ ---
+ duration_ms: 0.261798
+ type: 'test'
+ ...
+# Subtest: complete translates the neutral toolChoice to the Anthropic shape
+ok 3271 - complete translates the neutral toolChoice to the Anthropic shape
+ ---
+ duration_ms: 0.714226
+ type: 'test'
+ ...
+# Subtest: complete without the env var sends no x-api-key (localhost proxies)
+ok 3272 - complete without the env var sends no x-api-key (localhost proxies)
+ ---
+ duration_ms: 0.248858
+ type: 'test'
+ ...
+# Subtest: complete maps a 401 without a key to a hint and never leaks the key or provider body
+ok 3273 - complete maps a 401 without a key to a hint and never leaks the key or provider body
+ ---
+ duration_ms: 4.853347
+ type: 'test'
+ ...
+# Subtest: complete rejects an empty messages array
+ok 3274 - complete rejects an empty messages array
+ ---
+ duration_ms: 0.272394
+ type: 'test'
+ ...
+# Subtest: stream yields text deltas then a terminal stopReason + usage
+ok 3275 - stream yields text deltas then a terminal stopReason + usage
+ ---
+ duration_ms: 1.261507
+ type: 'test'
+ ...
+# Subtest: parseAnthropicMessageResponse rejects a payload with no content array
+ok 3276 - parseAnthropicMessageResponse rejects a payload with no content array
+ ---
+ duration_ms: 0.413969
+ type: 'test'
+ ...
+# Subtest: batch.submit posts {requests:[{custom_id,params}]} to /v1/messages/batches with auth
+ok 3277 - batch.submit posts {requests:[{custom_id,params}]} to /v1/messages/batches with auth
+ ---
+ duration_ms: 1.099521
+ type: 'test'
+ ...
+# Subtest: batch.poll returns the normalized status with counts
+ok 3278 - batch.poll returns the normalized status with counts
+ ---
+ duration_ms: 0.315851
+ type: 'test'
+ ...
+# Subtest: batch.results normalizes succeeded (incl. refusal) and surfaces only the error category
+ok 3279 - batch.results normalizes succeeded (incl. refusal) and surfaces only the error category
+ ---
+ duration_ms: 0.666583
+ type: 'test'
+ ...
+# Subtest: batch.results returns [] while the job is still in progress (caller polls again)
+ok 3280 - batch.results returns [] while the job is still in progress (caller polls again)
+ ---
+ duration_ms: 0.220886
+ type: 'test'
+ ...
+# Subtest: batch.submit rejects an empty request list
+ok 3281 - batch.submit rejects an empty request list
+ ---
+ duration_ms: 0.176899
+ type: 'test'
+ ...
+# Subtest: validateAnthropicCompletionConfig defaults to Anthropic with ANTHROPIC_API_KEY and Opus
+ok 3282 - validateAnthropicCompletionConfig defaults to Anthropic with ANTHROPIC_API_KEY and Opus
+ ---
+ duration_ms: 0.57868
+ type: 'test'
+ ...
+# Subtest: validateAnthropicCompletionConfig accepts a proxy/localhost override
+ok 3283 - validateAnthropicCompletionConfig accepts a proxy/localhost override
+ ---
+ duration_ms: 0.161465
+ type: 'test'
+ ...
+# Subtest: validateAnthropicCompletionConfig rejects a non-object config
+ok 3284 - validateAnthropicCompletionConfig rejects a non-object config
+ ---
+ duration_ms: 0.103638
+ type: 'test'
+ ...
+# Subtest: validateAnthropicCompletionConfig rejects a non-http base_url
+ok 3285 - validateAnthropicCompletionConfig rejects a non-http base_url
+ ---
+ duration_ms: 0.791473
+ type: 'test'
+ ...
+# Subtest: validateAnthropicCompletionConfig rejects non-positive numeric fields
+ok 3286 - validateAnthropicCompletionConfig rejects non-positive numeric fields
+ ---
+ duration_ms: 0.208927
+ type: 'test'
+ ...
+# Subtest: messagesEndpoint appends /v1/messages to a bare origin and does not double /v1
+ok 3287 - messagesEndpoint appends /v1/messages to a bare origin and does not double /v1
+ ---
+ duration_ms: 0.124089
+ type: 'test'
+ ...
+# Subtest: complete sends Bearer key, leading system message, function tools; parses content + tool_calls
+ok 3288 - complete sends Bearer key, leading system message, function tools; parses content + tool_calls
+ ---
+ duration_ms: 2.986609
+ type: 'test'
+ ...
+# Subtest: complete uses the per-request model over the default (tiering)
+ok 3289 - complete uses the per-request model over the default (tiering)
+ ---
+ duration_ms: 0.363783
+ type: 'test'
+ ...
+# Subtest: complete passes responseFormat and params (tool_choice) through
+ok 3290 - complete passes responseFormat and params (tool_choice) through
+ ---
+ duration_ms: 0.304763
+ type: 'test'
+ ...
+# Subtest: complete translates the neutral toolChoice to OpenAI shape (wins over params)
+ok 3291 - complete translates the neutral toolChoice to OpenAI shape (wins over params)
+ ---
+ duration_ms: 0.634825
+ type: 'test'
+ ...
+# Subtest: complete without the env var sends no Authorization (localhost servers)
+ok 3292 - complete without the env var sends no Authorization (localhost servers)
+ ---
+ duration_ms: 0.32278
+ type: 'test'
+ ...
+# Subtest: complete maps a 401 without a key to a hint and never leaks key or provider body
+ok 3293 - complete maps a 401 without a key to a hint and never leaks key or provider body
+ ---
+ duration_ms: 0.73699
+ type: 'test'
+ ...
+# Subtest: complete rejects an empty messages array
+ok 3294 - complete rejects an empty messages array
+ ---
+ duration_ms: 0.261928
+ type: 'test'
+ ...
+# Subtest: stream yields text deltas then a terminal stopReason + usage, stopping at [DONE]
+ok 3295 - stream yields text deltas then a terminal stopReason + usage, stopping at [DONE]
+ ---
+ duration_ms: 0.66442
+ type: 'test'
+ ...
+# Subtest: parseOpenAiChatResponse rejects a payload with no choices
+ok 3296 - parseOpenAiChatResponse rejects a payload with no choices
+ ---
+ duration_ms: 0.319235
+ type: 'test'
+ ...
+# Subtest: validateOpenAiCompletionConfig defaults to OpenAI with OPENAI_API_KEY
+ok 3297 - validateOpenAiCompletionConfig defaults to OpenAI with OPENAI_API_KEY
+ ---
+ duration_ms: 0.604098
+ type: 'test'
+ ...
+# Subtest: validateOpenAiCompletionConfig accepts an Ollama-shaped localhost override
+ok 3298 - validateOpenAiCompletionConfig accepts an Ollama-shaped localhost override
+ ---
+ duration_ms: 0.132871
+ type: 'test'
+ ...
+# Subtest: validateOpenAiCompletionConfig rejects a non-object config
+ok 3299 - validateOpenAiCompletionConfig rejects a non-object config
+ ---
+ duration_ms: 0.129336
+ type: 'test'
+ ...
+# Subtest: validateOpenAiCompletionConfig rejects a non-http base_url
+ok 3300 - validateOpenAiCompletionConfig rejects a non-http base_url
+ ---
+ duration_ms: 0.095536
+ type: 'test'
+ ...
+# Subtest: chatCompletionsEndpoint appends /v1/chat/completions and does not double /v1
+ok 3301 - chatCompletionsEndpoint appends /v1/chat/completions and does not double /v1
+ ---
+ duration_ms: 0.154625
+ type: 'test'
+ ...
+# Subtest: activate provides the context-graph capability and registers node/edge datasets, the graph commands + graph_neighbors verb, its group help, and no skill
+ok 3302 - activate provides the context-graph capability and registers node/edge datasets, the graph commands + graph_neighbors verb, its group help, and no skill
+ ---
+ duration_ms: 1.420038
+ type: 'test'
+ ...
+# Subtest: graph project with no contracts registered reports cleanly and exits 0
+ok 3303 - graph project with no contracts registered reports cleanly and exits 0
+ ---
+ duration_ms: 2.250621
+ type: 'test'
+ ...
+# Subtest: graph project --source with no matching contract reports cleanly and exits 0
+ok 3304 - graph project --source with no matching contract reports cleanly and exits 0
+ ---
+ duration_ms: 0.203499
+ type: 'test'
+ ...
+# Subtest: graph project --source= (equals form) parses and filters
+ok 3305 - graph project --source= (equals form) parses and filters
+ ---
+ duration_ms: 0.154144
+ type: 'test'
+ ...
+# Subtest: graph project usage errors exit 2 and report the offending argv on stderr
+ok 3306 - graph project usage errors exit 2 and report the offending argv on stderr
+ ---
+ duration_ms: 0.39456
+ type: 'test'
+ ...
+# Subtest: usage errors exit 2 and report the offending argv on stderr
+ok 3307 - usage errors exit 2 and report the offending argv on stderr
+ ---
+ duration_ms: 1.364734
+ type: 'test'
+ ...
+# Subtest: an unresolved seed exits 1 with a not-found note on stderr
+ok 3308 - an unresolved seed exits 1 with a not-found note on stderr
+ ---
+ duration_ms: 63.854412
+ type: 'test'
+ ...
+# Subtest: an ambiguous seed exits 1 and lists the candidates on stderr
+ok 3309 - an ambiguous seed exits 1 and lists the candidates on stderr
+ ---
+ duration_ms: 28.40991
+ type: 'test'
+ ...
+# Subtest: a resolved seed exits 0 and renders neighbors on stdout
+ok 3310 - a resolved seed exits 0 and renders neighbors on stdout
+ ---
+ duration_ms: 34.640861
+ type: 'test'
+ ...
+# Subtest: --limit truncates and reports the true total on stdout (not stderr)
+ok 3311 - --limit truncates and reports the true total on stdout (not stderr)
+ ---
+ duration_ms: 23.92223
+ type: 'test'
+ ...
+# Subtest: --json emits the structured result on stdout
+ok 3312 - --json emits the structured result on stdout
+ ---
+ duration_ms: 26.412374
+ type: 'test'
+ ...
+# Subtest: a synced caller context suppresses graph content, says so on stderr, and --include-local-only restores it
+ok 3313 - a synced caller context suppresses graph content, says so on stderr, and --include-local-only restores it
+ ---
+ duration_ms: 21.438484
+ type: 'test'
+ ...
+# Subtest: TEXT renderer disambiguates two Files sharing a basename into distinct rows
+ok 3314 - TEXT renderer disambiguates two Files sharing a basename into distinct rows
+ ---
+ duration_ms: 0.403243
+ type: 'test'
+ ...
+# Subtest: TEXT renderer keeps deep same-suffix Files distinct when the path tail is truncated
+ok 3315 - TEXT renderer keeps deep same-suffix Files distinct when the path tail is truncated
+ ---
+ duration_ms: 0.172472
+ type: 'test'
+ ...
+# Subtest: TEXT renderer breaks the tie with the full node_id when shortId prefixes also collide
+ok 3316 - TEXT renderer breaks the tie with the full node_id when shortId prefixes also collide
+ ---
+ duration_ms: 0.169007
+ type: 'test'
+ ...
+# Subtest: TEXT renderer leaves a non-colliding label readable (no disambiguator)
+ok 3317 - TEXT renderer leaves a non-colliding label readable (no disambiguator)
+ ---
+ duration_ms: 0.200595
+ type: 'test'
+ ...
+# Subtest: --json output is unchanged by the collision: node.natural_key is the path, labels untouched
+ok 3318 - --json output is unchanged by the collision: node.natural_key is the path, labels untouched
+ ---
+ duration_ms: 0.930424
+ type: 'test'
+ ...
+# Subtest: node: every declared content column is a real schema column
+ok 3319 - node: every declared content column is a real schema column
+ ---
+ duration_ms: 0.778113
+ type: 'test'
+ ...
+# Subtest: node: content and structural allowlists are disjoint
+ok 3320 - node: content and structural allowlists are disjoint
+ ---
+ duration_ms: 0.570787
+ type: 'test'
+ ...
+# Subtest: node: every schema column is classified as content or structural
+ok 3321 - node: every schema column is classified as content or structural
+ ---
+ duration_ms: 0.189619
+ type: 'test'
+ ...
+# Subtest: edge: every declared content column is a real schema column
+ok 3322 - edge: every declared content column is a real schema column
+ ---
+ duration_ms: 0.088574
+ type: 'test'
+ ...
+# Subtest: edge: content and structural allowlists are disjoint
+ok 3323 - edge: content and structural allowlists are disjoint
+ ---
+ duration_ms: 0.086752
+ type: 'test'
+ ...
+# Subtest: edge: every schema column is classified as content or structural
+ok 3324 - edge: every schema column is classified as content or structural
+ ---
+ duration_ms: 0.141014
+ type: 'test'
+ ...
+# Subtest: makeRowBuilders stamps provenance from the passed metadata
+ok 3325 - makeRowBuilders stamps provenance from the passed metadata
+ ---
+ duration_ms: 1.350283
+ type: 'test'
+ ...
+# Subtest: makeRowBuilders normalizes first_seen and prunes empty props to null
+ok 3326 - makeRowBuilders normalizes first_seen and prunes empty props to null
+ ---
+ duration_ms: 0.420388
+ type: 'test'
+ ...
+# Subtest: buildEdge passes props through, prunes empty to null, and leaves absent props null
+ok 3327 - buildEdge passes props through, prunes empty to null, and leaves absent props null
+ ---
+ duration_ms: 0.153623
+ type: 'test'
+ ...
+# Subtest: edge ids are stable across presence/absence of props (ids hash src/type/dst only)
+ok 3328 - edge ids are stable across presence/absence of props (ids hash src/type/dst only)
+ ---
+ duration_ms: 0.151289
+ type: 'test'
+ ...
+# Subtest: the id recipe is source-agnostic - same (type, key) converges across sources
+ok 3329 - the id recipe is source-agnostic - same (type, key) converges across sources
+ ---
+ duration_ms: 0.14455
+ type: 'test'
+ ...
+# Subtest: contract registry registers contracts and lists them name-sorted
+ok 3330 - contract registry registers contracts and lists them name-sorted
+ ---
+ duration_ms: 4.870212
+ type: 'test'
+ ...
+# Subtest: contract registry rejects malformed contracts
+ok 3331 - contract registry rejects malformed contracts
+ ---
+ duration_ms: 0.479089
+ type: 'test'
+ ...
+# Subtest: contract registry rejects malformed rules, naming the offending rule index
+ok 3332 - contract registry rejects malformed rules, naming the offending rule index
+ ---
+ duration_ms: 0.273877
+ type: 'test'
+ ...
+# Subtest: contract registry validates the declarative rule form
+ok 3333 - contract registry validates the declarative rule form
+ ---
+ duration_ms: 1.516385
+ type: 'test'
+ ...
+# Subtest: contract registry validates rowFilter, and raw sql must select its columns
+ok 3334 - contract registry validates rowFilter, and raw sql must select its columns
+ ---
+ duration_ms: 0.785344
+ type: 'test'
+ ...
+# Subtest: contract registry rejects a duplicate (plugin, name)
+ok 3335 - contract registry rejects a duplicate (plugin, name)
+ ---
+ duration_ms: 0.165792
+ type: 'test'
+ ...
+# Subtest: pollUntilEnded polls until the job ends, reporting progress
+ok 3336 - pollUntilEnded polls until the job ends, reporting progress
+ ---
+ duration_ms: 4.359976
+ type: 'test'
+ ...
+# Subtest: runCurateBatch submits the whole pool as a batch, collects, and routes the results
+ok 3337 - runCurateBatch submits the whole pool as a batch, collects, and routes the results
+ ---
+ duration_ms: 4.698872
+ type: 'test'
+ ...
+# Subtest: runCurateBatch falls back to a synchronous tick when the provider has no batch API
+ok 3338 - runCurateBatch falls back to a synchronous tick when the provider has no batch API
+ ---
+ duration_ms: 0.71734
+ type: 'test'
+ ...
+# Subtest: runCurateBatch --dry-run reports the scoped pool + clusters and writes nothing
+ok 3339 - runCurateBatch --dry-run reports the scoped pool + clusters and writes nothing
+ ---
+ duration_ms: 0.462192
+ type: 'test'
+ ...
+# Subtest: runCurateBatch --dry-run scopes the pool to --since anchorKeys
+ok 3340 - runCurateBatch --dry-run scopes the pool to --since anchorKeys
+ ---
+ duration_ms: 1.793075
+ type: 'test'
+ ...
+# Subtest: runCurateBatch resumes a pre-persisted backfill job (collects, does not re-submit)
+ok 3341 - runCurateBatch resumes a pre-persisted backfill job (collects, does not re-submit)
+ ---
+ duration_ms: 4.312625
+ type: 'test'
+ ...
+# Subtest: runCurateBatch refuses to run while a daemon curate job is in flight (no clobber)
+ok 3342 - runCurateBatch refuses to run while a daemon curate job is in flight (no clobber)
+ ---
+ duration_ms: 1.412367
+ type: 'test'
+ ...
+# Subtest: a daemon tick (collectCurateJob) leaves a backfill job untouched
+ok 3343 - a daemon tick (collectCurateJob) leaves a backfill job untouched
+ ---
+ duration_ms: 0.846486
+ type: 'test'
+ ...
+# Subtest: submitCurateJob records the job in the sidecar without collecting; collectCurateJob routes it once ended
+ok 3344 - submitCurateJob records the job in the sidecar without collecting; collectCurateJob routes it once ended
+ ---
+ duration_ms: 3.080291
+ type: 'test'
+ ...
+# Subtest: submitCurateJob is a no-op while a job is already in flight
+ok 3345 - submitCurateJob is a no-op while a job is already in flight
+ ---
+ duration_ms: 3.767556
+ type: 'test'
+ ...
+# Subtest: collectCurateJob waits (no append, job kept) while the batch is still running
+ok 3346 - collectCurateJob waits (no append, job kept) while the batch is still running
+ ---
+ duration_ms: 1.19746
+ type: 'test'
+ ...
+# Subtest: submitCurateJob writes skip resolutions but submits no batch when everything is below salience
+ok 3347 - submitCurateJob writes skip resolutions but submits no batch when everything is below salience
+ ---
+ duration_ms: 0.582485
+ type: 'test'
+ ...
+# Subtest: parseBackfillArgv: bare argv runs both phases
+ok 3348 - parseBackfillArgv: bare argv runs both phases
+ ---
+ duration_ms: 1.464937
+ type: 'test'
+ ...
+# Subtest: parseBackfillArgv: --propose-only selects only propose
+ok 3349 - parseBackfillArgv: --propose-only selects only propose
+ ---
+ duration_ms: 0.143498
+ type: 'test'
+ ...
+# Subtest: parseBackfillArgv: --curate-only selects only curate
+ok 3350 - parseBackfillArgv: --curate-only selects only curate
+ ---
+ duration_ms: 0.124369
+ type: 'test'
+ ...
+# Subtest: parseBackfillArgv: --since scopes the curate pool
+ok 3351 - parseBackfillArgv: --since scopes the curate pool
+ ---
+ duration_ms: 0.169307
+ type: 'test'
+ ...
+# Subtest: parseBackfillArgv: --since= (equals form) is accepted
+ok 3352 - parseBackfillArgv: --since= (equals form) is accepted
+ ---
+ duration_ms: 0.271773
+ type: 'test'
+ ...
+# Subtest: parseBackfillArgv: --dry-run is parsed
+ok 3353 - parseBackfillArgv: --dry-run is parsed
+ ---
+ duration_ms: 0.127865
+ type: 'test'
+ ...
+# Subtest: parseBackfillArgv: a malformed --since date is rejected
+ok 3354 - parseBackfillArgv: a malformed --since date is rejected
+ ---
+ duration_ms: 0.265995
+ type: 'test'
+ ...
+# Subtest: parseBackfillArgv: --since requires a value
+ok 3355 - parseBackfillArgv: --since requires a value
+ ---
+ duration_ms: 1.384123
+ type: 'test'
+ ...
+# Subtest: parseBackfillArgv: --since does not apply to --propose-only
+ok 3356 - parseBackfillArgv: --since does not apply to --propose-only
+ ---
+ duration_ms: 0.314688
+ type: 'test'
+ ...
+# Subtest: parseBackfillArgv: the two phase flags are mutually exclusive
+ok 3357 - parseBackfillArgv: the two phase flags are mutually exclusive
+ ---
+ duration_ms: 0.349972
+ type: 'test'
+ ...
+# Subtest: parseBackfillArgv: an unknown flag is rejected
+ok 3358 - parseBackfillArgv: an unknown flag is rejected
+ ---
+ duration_ms: 0.163959
+ type: 'test'
+ ...
+# Subtest: parseBackfillArgv: a stray positional is rejected (backfill takes no argument)
+ok 3359 - parseBackfillArgv: a stray positional is rejected (backfill takes no argument)
+ ---
+ duration_ms: 0.103377
+ type: 'test'
+ ...
+# Subtest: parseBackfillArgv: an unknown flag is reported even alongside a valid flag
+ok 3360 - parseBackfillArgv: an unknown flag is reported even alongside a valid flag
+ ---
+ duration_ms: 0.091429
+ type: 'test'
+ ...
+# Subtest: inWindowSessions includes a session at exactly the --since UTC midnight and excludes one a second before
+ok 3361 - inWindowSessions includes a session at exactly the --since UTC midnight and excludes one a second before
+ ---
+ duration_ms: 0.626412
+ type: 'test'
+ ...
+# Subtest: inWindowSessions accepts string and epoch-millis timestamps and drops unparseable ones
+ok 3362 - inWindowSessions accepts string and epoch-millis timestamps and drops unparseable ones
+ ---
+ duration_ms: 0.157289
+ type: 'test'
+ ...
+# Subtest: validateEnrichConfig fills source + tier defaults
+ok 3363 - validateEnrichConfig fills source + tier defaults
+ ---
+ duration_ms: 2.843252
+ type: 'test'
+ ...
+# Subtest: validateEnrichConfig accepts the clustering overrides
+ok 3364 - validateEnrichConfig accepts the clustering overrides
+ ---
+ duration_ms: 0.217881
+ type: 'test'
+ ...
+# Subtest: validateEnrichConfig accepts the regime selector overrides
+ok 3365 - validateEnrichConfig accepts the regime selector overrides
+ ---
+ duration_ms: 0.143608
+ type: 'test'
+ ...
+# Subtest: validateEnrichConfig rejects a non-positive settle_cutoff_minutes
+ok 3366 - validateEnrichConfig rejects a non-positive settle_cutoff_minutes
+ ---
+ duration_ms: 0.153453
+ type: 'test'
+ ...
+# Subtest: validateEnrichConfig accepts overrides incl. recall_index
+ok 3367 - validateEnrichConfig accepts overrides incl. recall_index
+ ---
+ duration_ms: 0.257652
+ type: 'test'
+ ...
+# Subtest: validateEnrichConfig rejects a non-object config
+ok 3368 - validateEnrichConfig rejects a non-object config
+ ---
+ duration_ms: 0.123888
+ type: 'test'
+ ...
+# Subtest: validateEnrichConfig rejects an out-of-range confidence_floor
+ok 3369 - validateEnrichConfig rejects an out-of-range confidence_floor
+ ---
+ duration_ms: 0.205362
+ type: 'test'
+ ...
+# Subtest: validateEnrichConfig rejects a non-positive interval
+ok 3370 - validateEnrichConfig rejects a non-positive interval
+ ---
+ duration_ms: 0.137399
+ type: 'test'
+ ...
+# Subtest: validateEnrichConfig rejects a column name that is not a SQL identifier
+ok 3371 - validateEnrichConfig rejects a column name that is not a SQL identifier
+ ---
+ duration_ms: 0.433339
+ type: 'test'
+ ...
+# Subtest: validateEnrichConfig accepts a custom tiebreak_column
+ok 3372 - validateEnrichConfig accepts a custom tiebreak_column
+ ---
+ duration_ms: 0.428531
+ type: 'test'
+ ...
+# Subtest: validateEnrichConfig accepts row-selection overrides incl. an empty exclude list
+ok 3373 - validateEnrichConfig accepts row-selection overrides incl. an empty exclude list
+ ---
+ duration_ms: 0.259855
+ type: 'test'
+ ...
+# Subtest: validateEnrichConfig gates the default tool_result filter to the default source schema
+ok 3374 - validateEnrichConfig gates the default tool_result filter to the default source schema
+ ---
+ duration_ms: 0.186684
+ type: 'test'
+ ...
+# Subtest: validateEnrichConfig rejects a non-string-array exclude_part_types
+ok 3375 - validateEnrichConfig rejects a non-string-array exclude_part_types
+ ---
+ duration_ms: 0.120232
+ type: 'test'
+ ...
+# Subtest: validateEnrichConfig rejects a part_type_column that is not a SQL identifier
+ok 3376 - validateEnrichConfig rejects a part_type_column that is not a SQL identifier
+ ---
+ duration_ms: 0.123428
+ type: 'test'
+ ...
+# Subtest: validateEnrichConfig rejects a non-boolean require_text
+ok 3377 - validateEnrichConfig rejects a non-boolean require_text
+ ---
+ duration_ms: 0.112371
+ type: 'test'
+ ...
+# Subtest: contract reads only the committed dataset
+ok 3378 - contract reads only the committed dataset
+ ---
+ duration_ms: 0.817593
+ type: 'test'
+ ...
+# Subtest: node rule maps item_type→type, item_id→key, folds confidence into props
+ok 3379 - node rule maps item_type→type, item_id→key, folds confidence into props
+ ---
+ duration_ms: 0.720004
+ type: 'test'
+ ...
+# Subtest: edge rule links the anchor (Session) to the enrichment node via produced
+ok 3380 - edge rule links the anchor (Session) to the enrichment node via produced
+ ---
+ duration_ms: 0.19763
+ type: 'test'
+ ...
+# Subtest: toRow parses a JSON-string props column (engine may return JSON as text)
+ok 3381 - toRow parses a JSON-string props column (engine may return JSON as text)
+ ---
+ duration_ms: 0.166433
+ type: 'test'
+ ...
+# Subtest: toRow returns null when required fields are missing
+ok 3382 - toRow returns null when required fields are missing
+ ---
+ duration_ms: 0.143568
+ type: 'test'
+ ...
+# Subtest: routeDecision commit writes a committed row + a resolution, never rejected/merged
+ok 3383 - routeDecision commit writes a committed row + a resolution, never rejected/merged
+ ---
+ duration_ms: 1.584298
+ type: 'test'
+ ...
+# Subtest: routeDecision commit reuses an explicit item_key (convergence) over the label
+ok 3384 - routeDecision commit reuses an explicit item_key (convergence) over the label
+ ---
+ duration_ms: 0.154635
+ type: 'test'
+ ...
+# Subtest: routeDecision deepen also commits an item
+ok 3385 - routeDecision deepen also commits an item
+ ---
+ duration_ms: 0.143729
+ type: 'test'
+ ...
+# Subtest: routeDecision reject commits nothing - a rejected prospect never reaches the graph
+ok 3386 - routeDecision reject commits nothing - a rejected prospect never reaches the graph
+ ---
+ duration_ms: 0.142897
+ type: 'test'
+ ...
+# Subtest: routeDecision treats an omitted decision as an implicit reject
+ok 3387 - routeDecision treats an omitted decision as an implicit reject
+ ---
+ duration_ms: 0.131199
+ type: 'test'
+ ...
+# Subtest: routeDecision merge writes a committed row under the canonical key with the merging session anchor
+ok 3388 - routeDecision merge writes a committed row under the canonical key with the merging session anchor
+ ---
+ duration_ms: 0.229148
+ type: 'test'
+ ...
+# Subtest: routeDecision leaves an under-specified merge pending (no commit, no resolution) - avoids mis-routing the produced edge
+ok 3389 - routeDecision leaves an under-specified merge pending (no commit, no resolution) - avoids mis-routing the produced edge
+ ---
+ duration_ms: 0.120924
+ type: 'test'
+ ...
+# Subtest: routeDecision leaves a merge missing merge_into pending too
+ok 3390 - routeDecision leaves a merge missing merge_into pending too
+ ---
+ duration_ms: 0.109096
+ type: 'test'
+ ...
+# Subtest: cosine: identical → 1, orthogonal → 0, zero vector → 0
+ok 3391 - cosine: identical → 1, orthogonal → 0, zero vector → 0
+ ---
+ duration_ms: 0.282229
+ type: 'test'
+ ...
+# Subtest: greedyCosineClusters groups near-duplicates and separates distinct ones, deterministically
+ok 3392 - greedyCosineClusters groups near-duplicates and separates distinct ones, deterministically
+ ---
+ duration_ms: 0.399186
+ type: 'test'
+ ...
+# Subtest: clusterByRecallRegion buckets warm prospects by their top recalled node id
+ok 3393 - clusterByRecallRegion buckets warm prospects by their top recalled node id
+ ---
+ duration_ms: 0.198171
+ type: 'test'
+ ...
+# Subtest: chunkBySize splits an oversized cluster and leaves a small one whole
+ok 3394 - chunkBySize splits an oversized cluster and leaves a small one whole
+ ---
+ duration_ms: 0.133453
+ type: 'test'
+ ...
+# Subtest: selectPending returns every unresolved prospect when no anchorKeys are given
+ok 3395 - selectPending returns every unresolved prospect when no anchorKeys are given
+ ---
+ duration_ms: 0.71737
+ type: 'test'
+ ...
+# Subtest: selectPending with anchorKeys scopes the queue to in-window sessions, leaving the rest pending
+ok 3396 - selectPending with anchorKeys scopes the queue to in-window sessions, leaving the rest pending
+ ---
+ duration_ms: 0.154044
+ type: 'test'
+ ...
+# Subtest: selectPending excludes already-resolved prospects
+ok 3397 - selectPending excludes already-resolved prospects
+ ---
+ duration_ms: 0.184701
+ type: 'test'
+ ...
+# Subtest: runCurateTick curates pending prospects, writes committed + resolution rows, and skips already-resolved
+ok 3398 - runCurateTick curates pending prospects, writes committed + resolution rows, and skips already-resolved
+ ---
+ duration_ms: 2.845645
+ type: 'test'
+ ...
+# Subtest: runCurateTick merges cross-session duplicates - each contributing session gets a committed row (produced edge)
+ok 3399 - runCurateTick merges cross-session duplicates - each contributing session gets a committed row (produced edge)
+ ---
+ duration_ms: 0.647824
+ type: 'test'
+ ...
+# Subtest: runCurateTick leaves an under-specified merge prospect pending while committing its clustermate
+ok 3400 - runCurateTick leaves an under-specified merge prospect pending while committing its clustermate
+ ---
+ duration_ms: 0.643669
+ type: 'test'
+ ...
+# Subtest: runCurateTick processes a duplicated prospect_id only once (idempotency defense-in-depth)
+ok 3401 - runCurateTick processes a duplicated prospect_id only once (idempotency defense-in-depth)
+ ---
+ duration_ms: 0.489053
+ type: 'test'
+ ...
+# Subtest: runCurateTick auto-skips below-salience prospects with a terminal resolution and no curator call
+ok 3402 - runCurateTick auto-skips below-salience prospects with a terminal resolution and no curator call
+ ---
+ duration_ms: 1.948531
+ type: 'test'
+ ...
+# Subtest: runCurateTick leaves a cluster pending (no resolution) when the curator returns no decisions
+ok 3403 - runCurateTick leaves a cluster pending (no resolution) when the curator returns no decisions
+ ---
+ duration_ms: 0.544438
+ type: 'test'
+ ...
+# Subtest: runCurateTick derefs the source with the shared content filter (T1/T2 parity)
+ok 3404 - runCurateTick derefs the source with the shared content filter (T1/T2 parity)
+ ---
+ duration_ms: 0.724281
+ type: 'test'
+ ...
+# Subtest: prospectId is deterministic for the same inputs
+ok 3405 - prospectId is deterministic for the same inputs
+ ---
+ duration_ms: 1.002564
+ type: 'test'
+ ...
+# Subtest: prospectId changes with any input (anchor, candidate, version)
+ok 3406 - prospectId changes with any input (anchor, candidate, version)
+ ---
+ duration_ms: 0.167304
+ type: 'test'
+ ...
+# Subtest: columnsFor returns the schema for known datasets and throws otherwise
+ok 3407 - columnsFor returns the schema for known datasets and throws otherwise
+ ---
+ duration_ms: 0.251732
+ type: 'test'
+ ...
+# Subtest: buildProposeRequest forces the emit_prospects tool via the neutral toolChoice
+ok 3408 - buildProposeRequest forces the emit_prospects tool via the neutral toolChoice
+ ---
+ duration_ms: 1.158441
+ type: 'test'
+ ...
+# Subtest: buildCurateBatchRequest batches prospects and shares source/neighborhood once
+ok 3409 - buildCurateBatchRequest batches prospects and shares source/neighborhood once
+ ---
+ duration_ms: 1.955222
+ type: 'test'
+ ...
+# Subtest: buildCurateBatchRequest on Anthropic uses thinking + high effort, no forced tool
+ok 3410 - buildCurateBatchRequest on Anthropic uses thinking + high effort, no forced tool
+ ---
+ duration_ms: 0.142727
+ type: 'test'
+ ...
+# Subtest: buildCurateBatchRequest on a non-Anthropic provider forces the tool, no thinking params
+ok 3411 - buildCurateBatchRequest on a non-Anthropic provider forces the tool, no thinking params
+ ---
+ duration_ms: 0.122015
+ type: 'test'
+ ...
+# Subtest: parseProspects keeps valid candidates and drops unknown types / missing labels
+ok 3412 - parseProspects keeps valid candidates and drops unknown types / missing labels
+ ---
+ duration_ms: 0.214075
+ type: 'test'
+ ...
+# Subtest: parseProspects returns [] on a refusal
+ok 3413 - parseProspects returns [] on a refusal
+ ---
+ duration_ms: 0.101244
+ type: 'test'
+ ...
+# Subtest: parseDecisions keeps valid indexed decisions and drops invalid ones
+ok 3414 - parseDecisions keeps valid indexed decisions and drops invalid ones
+ ---
+ duration_ms: 0.187024
+ type: 'test'
+ ...
+# Subtest: parseDecisions returns [] when there is no tool call
+ok 3415 - parseDecisions returns [] when there is no tool call
+ ---
+ duration_ms: 0.099121
+ type: 'test'
+ ...
+# Subtest: buildSessionAggregateQuery ranks the precise latest (ts, tiebreak) per session, applying the content filter
+ok 3416 - buildSessionAggregateQuery ranks the precise latest (ts, tiebreak) per session, applying the content filter
+ ---
+ duration_ms: 1.364043
+ type: 'test'
+ ...
+# Subtest: buildSessionAggregateQuery omits the inner content filter when both filters are off
+ok 3417 - buildSessionAggregateQuery omits the inner content filter when both filters are off
+ ---
+ duration_ms: 0.194185
+ type: 'test'
+ ...
+# Subtest: buildSessionPartsQuery selects all transcript columns for one session, with no LIMIT
+ok 3418 - buildSessionPartsQuery selects all transcript columns for one session, with no LIMIT
+ ---
+ duration_ms: 0.23045
+ type: 'test'
+ ...
+# Subtest: buildSessionPartsQuery escapes a single quote in the session id (no injection surface)
+ok 3419 - buildSessionPartsQuery escapes a single quote in the session id (no injection surface)
+ ---
+ duration_ms: 0.143067
+ type: 'test'
+ ...
+# Subtest: buildSessionPartsQuery for a custom source is the bare anchor predicate (no part_type column)
+ok 3420 - buildSessionPartsQuery for a custom source is the bare anchor predicate (no part_type column)
+ ---
+ duration_ms: 0.287848
+ type: 'test'
+ ...
+# Subtest: orderSessionParts sorts by (timestamp, tiebreak) and coerces Date/ISO timestamps
+ok 3421 - orderSessionParts sorts by (timestamp, tiebreak) and coerces Date/ISO timestamps
+ ---
+ duration_ms: 0.716039
+ type: 'test'
+ ...
+# Subtest: buildTranscript stitches ordered text and dedups provenance ids, skipping empties
+ok 3422 - buildTranscript stitches ordered text and dedups provenance ids, skipping empties
+ ---
+ duration_ms: 0.286966
+ type: 'test'
+ ...
+# Subtest: sessionMark returns the latest ordered part tuple
+ok 3423 - sessionMark returns the latest ordered part tuple
+ ---
+ duration_ms: 0.272084
+ type: 'test'
+ ...
+# Subtest: selectSessions ongoing keeps only settled, past-watermark sessions, oldest first, capped
+ok 3424 - selectSessions ongoing keeps only settled, past-watermark sessions, oldest first, capped
+ ---
+ duration_ms: 0.622456
+ type: 'test'
+ ...
+# Subtest: selectSessions ongoing reselects a same-timestamp session whose latest part advanced past the mark (tiebreak)
+ok 3425 - selectSessions ongoing reselects a same-timestamp session whose latest part advanced past the mark (tiebreak)
+ ---
+ duration_ms: 0.437536
+ type: 'test'
+ ...
+# Subtest: selectSessions ongoing drops a session already enriched through its exact latest (ts, tiebreak)
+ok 3426 - selectSessions ongoing drops a session already enriched through its exact latest (ts, tiebreak)
+ ---
+ duration_ms: 0.218482
+ type: 'test'
+ ...
+# Subtest: selectSessions backfill returns every session, ignoring settle + watermark + cap
+ok 3427 - selectSessions backfill returns every session, ignoring settle + watermark + cap
+ ---
+ duration_ms: 0.242579
+ type: 'test'
+ ...
+# Subtest: collectProspectRows dedups identical (type,label) within a session and shapes the row
+ok 3428 - collectProspectRows dedups identical (type,label) within a session and shapes the row
+ ---
+ duration_ms: 0.623297
+ type: 'test'
+ ...
+# Subtest: collectProspectRows keeps the same label under different sessions as distinct prospects
+ok 3429 - collectProspectRows keeps the same label under different sessions as distinct prospects
+ ---
+ duration_ms: 0.159383
+ type: 'test'
+ ...
+# Subtest: runProposeTick extracts a whole session in one call, appends prospects, and advances its mark
+ok 3430 - runProposeTick extracts a whole session in one call, appends prospects, and advances its mark
+ ---
+ duration_ms: 4.196419
+ type: 'test'
+ ...
+# Subtest: runProposeTick is idempotent across ticks: re-extracting the same session appends no duplicate
+ok 3431 - runProposeTick is idempotent across ticks: re-extracting the same session appends no duplicate
+ ---
+ duration_ms: 1.57242
+ type: 'test'
+ ...
+# Subtest: runProposeTick preserves a curate_job submitted concurrently during its await window (no lost update)
+ok 3432 - runProposeTick preserves a curate_job submitted concurrently during its await window (no lost update)
+ ---
+ duration_ms: 3.171009
+ type: 'test'
+ ...
+# Subtest: runProposeTick ongoing skips a session already enriched through its latest part
+ok 3433 - runProposeTick ongoing skips a session already enriched through its latest part
+ ---
+ duration_ms: 0.905596
+ type: 'test'
+ ...
+# Subtest: runProposeTick drops candidates below the confidence floor before appending
+ok 3434 - runProposeTick drops candidates below the confidence floor before appending
+ ---
+ duration_ms: 1.924786
+ type: 'test'
+ ...
+# Subtest: sqlQuote doubles single quotes and is idempotent under the doubling
+ok 3435 - sqlQuote doubles single quotes and is idempotent under the doubling
+ ---
+ duration_ms: 0.756429
+ type: 'test'
+ ...
+# Subtest: contentFilterClauses emits the require_text + exclude_part_types predicates
+ok 3436 - contentFilterClauses emits the require_text + exclude_part_types predicates
+ ---
+ duration_ms: 0.728337
+ type: 'test'
+ ...
+# Subtest: contentFilterClauses honors each knob independently and sqlQuotes the values
+ok 3437 - contentFilterClauses honors each knob independently and sqlQuotes the values
+ ---
+ duration_ms: 0.153153
+ type: 'test'
+ ...
+# Subtest: isMissingDatasetError matches ENOENT and "unknown dataset", not arbitrary errors
+ok 3438 - isMissingDatasetError matches ENOENT and "unknown dataset", not arbitrary errors
+ ---
+ duration_ms: 0.161695
+ type: 'test'
+ ...
+# Subtest: runSql tolerates a missing dataset only when allowMissing is set (else fail-fast)
+ok 3439 - runSql tolerates a missing dataset only when allowMissing is set (else fail-fast)
+ ---
+ duration_ms: 0.505148
+ type: 'test'
+ ...
+# Subtest: runSql rethrows non-missing errors even with allowMissing
+ok 3440 - runSql rethrows non-missing errors even with allowMissing
+ ---
+ duration_ms: 0.137439
+ type: 'test'
+ ...
+# Subtest: runSql returns the executor rows on success
+ok 3441 - runSql returns the executor rows on success
+ ---
+ duration_ms: 0.20427
+ type: 'test'
+ ...
+# Subtest: readState returns an empty mark map + no job when the sidecar is missing
+ok 3442 - readState returns an empty mark map + no job when the sidecar is missing
+ ---
+ duration_ms: 1.573142
+ type: 'test'
+ ...
+# Subtest: writeState then readState round-trips the per-session marks
+ok 3443 - writeState then readState round-trips the per-session marks
+ ---
+ duration_ms: 0.832826
+ type: 'test'
+ ...
+# Subtest: writeState then readState round-trips the in-flight curate job
+ok 3444 - writeState then readState round-trips the in-flight curate job
+ ---
+ duration_ms: 1.442091
+ type: 'test'
+ ...
+# Subtest: readState reads a legacy curate job with no source as daemon (the original owner)
+ok 3445 - readState reads a legacy curate job with no source as daemon (the original owner)
+ ---
+ duration_ms: 0.632191
+ type: 'test'
+ ...
+# Subtest: readState coerces an unknown curate job source to daemon
+ok 3446 - readState coerces an unknown curate job source to daemon
+ ---
+ duration_ms: 0.649848
+ type: 'test'
+ ...
+# Subtest: writeState creates the state dir and persists atomically (no leftover temp files)
+ok 3447 - writeState creates the state dir and persists atomically (no leftover temp files)
+ ---
+ duration_ms: 0.856511
+ type: 'test'
+ ...
+# Subtest: readState falls back to an empty state on malformed JSON
+ok 3448 - readState falls back to an empty state on malformed JSON
+ ---
+ duration_ms: 0.469073
+ type: 'test'
+ ...
+# Subtest: readState ignores an older schema_version (the global-cursor v2 sidecar is discarded)
+ok 3449 - readState ignores an older schema_version (the global-cursor v2 sidecar is discarded)
+ ---
+ duration_ms: 0.347398
+ type: 'test'
+ ...
+# Subtest: readState drops only the malformed marks, keeping the well-formed ones
+ok 3450 - readState drops only the malformed marks, keeping the well-formed ones
+ ---
+ duration_ms: 0.622416
+ type: 'test'
+ ...
+# Subtest: readState drops a malformed curate job (no clusters array)
+ok 3451 - readState drops a malformed curate job (no clusters array)
+ ---
+ duration_ms: 0.530006
+ type: 'test'
+ ...
+# Subtest: nodeId pins known digests
+ok 3452 - nodeId pins known digests
+ ---
+ duration_ms: 0.987681
+ type: 'test'
+ ...
+# Subtest: nodeId pins the GitHub↔LLM bridge keys
+ok 3453 - nodeId pins the GitHub↔LLM bridge keys
+ ---
+ duration_ms: 0.155927
+ type: 'test'
+ ...
+# Subtest: edgeId pins known digests
+ok 3454 - edgeId pins known digests
+ ---
+ duration_ms: 0.211562
+ type: 'test'
+ ...
+# Subtest: ids are 24 lowercase hex chars and delimiter-collision-free
+ok 3455 - ids are 24 lowercase hex chars and delimiter-collision-free
+ ---
+ duration_ms: 0.272364
+ type: 'test'
+ ...
+# Subtest: compactGraphTables merges cross-partition duplicates into the earliest partition, sorted
+ok 3456 - compactGraphTables merges cross-partition duplicates into the earliest partition, sorted
+ ---
+ duration_ms: 100.270922
+ type: 'test'
+ ...
+# Subtest: compactGraphTables is a no-op on an empty cache
+ok 3457 - compactGraphTables is a no-op on an empty cache
+ ---
+ duration_ms: 2.251652
+ type: 'test'
+ ...
+# Subtest: compactGraphTables refuses to touch a partition whose cursor is corrupt
+ok 3458 - compactGraphTables refuses to touch a partition whose cursor is corrupt
+ ---
+ duration_ms: 21.957253
+ type: 'test'
+ ...
+# Subtest: rewritePartition aborts the swap when the cursor changed during the rewrite window
+ok 3459 - rewritePartition aborts the swap when the cursor changed during the rewrite window
+ ---
+ duration_ms: 23.826254
+ type: 'test'
+ ...
+# Subtest: graph compact CLI surfaces skipped partitions on stderr and exits nonzero for unreadable cursors
+ok 3460 - graph compact CLI surfaces skipped partitions on stderr and exits nonzero for unreadable cursors
+ ---
+ duration_ms: 7.570826
+ type: 'test'
+ ...
+# Subtest: projectGraph runs a contributed contract end to end and is idempotent
+ok 3461 - projectGraph runs a contributed contract end to end and is idempotent
+ ---
+ duration_ms: 142.066217
+ type: 'test'
+ ...
+# Subtest: projectGraph excludes retained Claude aux rows from the graph
+ok 3462 - projectGraph excludes retained Claude aux rows from the graph
+ ---
+ duration_ms: 36.25219
+ type: 'test'
+ ...
+# Subtest: projectGraph with no contracts writes nothing
+ok 3463 - projectGraph with no contracts writes nothing
+ ---
+ duration_ms: 8.547441
+ type: 'test'
+ ...
+# Subtest: bumping projectorVersion does not re-project: committed rows keep their original version
+ok 3464 - bumping projectorVersion does not re-project: committed rows keep their original version
+ ---
+ duration_ms: 23.025106
+ type: 'test'
+ ...
+# Subtest: projectGraph mints Skill/Program nodes and ran/invoked edges from all activation surfaces (\#229/\#230), and is idempotent
+ok 3465 - projectGraph mints Skill/Program nodes and ran/invoked edges from all activation surfaces (\#229/\#230), and is idempotent
+ ---
+ duration_ms: 45.147329
+ type: 'test'
+ ...
+# Subtest: a session sighted via both the marker and slash surfaces merges onto one ran edge with both dispatch flags
+ok 3466 - a session sighted via both the marker and slash surfaces merges onto one ran edge with both dispatch flags
+ ---
+ duration_ms: 28.049141
+ type: 'test'
+ ...
+# Subtest: issue \#229 headline SQL: sessions-per-skill ranking
+ok 3467 - issue \#229 headline SQL: sessions-per-skill ranking
+ ---
+ duration_ms: 37.794684
+ type: 'test'
+ ...
+# Subtest: issue \#230 headline SQL: which sessions ran the `git` program
+ok 3468 - issue \#230 headline SQL: which sessions ran the `git` program
+ ---
+ duration_ms: 34.272561
+ type: 'test'
+ ...
+# Subtest: graph neighbors traversal: --edge-type ran reaches Skill, --edge-type invoked reaches Program
+ok 3469 - graph neighbors traversal: --edge-type ran reaches Skill, --edge-type invoked reaches Program
+ ---
+ duration_ms: 40.051985
+ type: 'test'
+ ...
+# Subtest: shared-scan projection is row-identical to per-rule SQL execution
+ok 3470 - shared-scan projection is row-identical to per-rule SQL execution
+ ---
+ duration_ms: 82.332908
+ type: 'test'
+ ...
+# Subtest: mergeRow keeps the earliest first_seen and unions disjoint props
+ok 3471 - mergeRow keeps the earliest first_seen and unions disjoint props
+ ---
+ duration_ms: 0.964236
+ type: 'test'
+ ...
+# Subtest: mergeRow resolves props conflicts in favor of the earliest row, in any merge order
+ok 3472 - mergeRow resolves props conflicts in favor of the earliest row, in any merge order
+ ---
+ duration_ms: 0.133102
+ type: 'test'
+ ...
+# Subtest: mergeRow is order-independent when a key is absent from the earliest row
+ok 3473 - mergeRow is order-independent when a key is absent from the earliest row
+ ---
+ duration_ms: 0.289731
+ type: 'test'
+ ...
+# Subtest: mergeRow breaks equal-timestamp conflicts by value, order-independently
+ok 3474 - mergeRow breaks equal-timestamp conflicts by value, order-independently
+ ---
+ duration_ms: 0.123027
+ type: 'test'
+ ...
+# Subtest: mergeRow tolerates rows with unparseable or missing first_seen
+ok 3475 - mergeRow tolerates rows with unparseable or missing first_seen
+ ---
+ duration_ms: 0.191831
+ type: 'test'
+ ...
+# Subtest: matchesPredicate likePrefix keeps only matching-prefix string rows
+ok 3476 - matchesPredicate likePrefix keeps only matching-prefix string rows
+ ---
+ duration_ms: 0.196668
+ type: 'test'
+ ...
+# Subtest: firstSeenTime normalizes strings, Dates, and epoch numbers
+ok 3477 - firstSeenTime normalizes strings, Dates, and epoch numbers
+ ---
+ duration_ms: 0.13869
+ type: 'test'
+ ...
+# Subtest: projectGraph passes the default 3 GiB projection budget at all three scan sites when the knob is unset
+ok 3478 - projectGraph passes the default 3 GiB projection budget at all three scan sites when the knob is unset
+ ---
+ duration_ms: 2.980029
+ type: 'test'
+ ...
+# Subtest: HYP_GRAPH_PROJECTION_MAX_HEAP_MB overrides the projection budget at every scan site
+ok 3479 - HYP_GRAPH_PROJECTION_MAX_HEAP_MB overrides the projection budget at every scan site
+ ---
+ duration_ms: 0.445146
+ type: 'test'
+ ...
+# Subtest: resolveProjectionMaxHeapBytes defaults to 3 GiB and never returns 0
+ok 3480 - resolveProjectionMaxHeapBytes defaults to 3 GiB and never returns 0
+ ---
+ duration_ms: 0.22384
+ type: 'test'
+ ...
+# Subtest: depth-1 out from a Session reaches its app/model/tool/file
+ok 3481 - depth-1 out from a Session reaches its app/model/tool/file
+ ---
+ duration_ms: 1.324073
+ type: 'test'
+ ...
+# Subtest: depth-1 in from a File reaches the Sessions that touched it
+ok 3482 - depth-1 in from a File reaches the Sessions that touched it
+ ---
+ duration_ms: 0.284071
+ type: 'test'
+ ...
+# Subtest: depth-2 both from a File yields co-occurrence (file → sessions → resources)
+ok 3483 - depth-2 both from a File yields co-occurrence (file → sessions → resources)
+ ---
+ duration_ms: 0.184861
+ type: 'test'
+ ...
+# Subtest: --edge-type restricts which relations are walked
+ok 3484 - --edge-type restricts which relations are walked
+ ---
+ duration_ms: 0.209418
+ type: 'test'
+ ...
+# Subtest: direction out from a leaf File yields no neighbors (but succeeds)
+ok 3485 - direction out from a leaf File yields no neighbors (but succeeds)
+ ---
+ duration_ms: 0.134755
+ type: 'test'
+ ...
+# Subtest: --limit truncates in BFS order and reports the true reachable total
+ok 3486 - --limit truncates in BFS order and reports the true reachable total
+ ---
+ duration_ms: 0.120323
+ type: 'test'
+ ...
+# Subtest: a node with no visited dedup is never revisited across hops
+ok 3487 - a node with no visited dedup is never revisited across hops
+ ---
+ duration_ms: 0.171311
+ type: 'test'
+ ...
+# Subtest: resolveSeed matches node_id, then natural_key, then label
+ok 3488 - resolveSeed matches node_id, then natural_key, then label
+ ---
+ duration_ms: 0.134003
+ type: 'test'
+ ...
+# Subtest: resolveSeed reports ambiguity with candidates rather than silently picking
+ok 3489 - resolveSeed reports ambiguity with candidates rather than silently picking
+ ---
+ duration_ms: 0.284262
+ type: 'test'
+ ...
+# Subtest: resolveSeed --type narrows the match
+ok 3490 - resolveSeed --type narrows the match
+ ---
+ duration_ms: 0.298564
+ type: 'test'
+ ...
+# Subtest: traverse returns an error shape for an unresolved seed
+ok 3491 - traverse returns an error shape for an unresolved seed
+ ---
+ duration_ms: 0.158832
+ type: 'test'
+ ...
+# Subtest: queryNeighbors reads node/edge through the query surface and walks (integration)
+ok 3492 - queryNeighbors reads node/edge through the query surface and walks (integration)
+ ---
+ duration_ms: 85.879208
+ type: 'test'
+ ...
+# Subtest: queryNeighbors suppresses graph content for an unknown caller and reports it
+ok 3493 - queryNeighbors suppresses graph content for an unknown caller and reports it
+ ---
+ duration_ms: 49.359341
+ type: 'test'
+ ...
+# Subtest: queryNeighbors folds pre-compaction duplicate rows so a natural-key seed still resolves
+ok 3494 - queryNeighbors folds pre-compaction duplicate rows so a natural-key seed still resolves
+ ---
+ duration_ms: 24.675685
+ type: 'test'
+ ...
+# Subtest: queryNeighbors reports an unprojected graph as empty, not as a missing node
+ok 3495 - queryNeighbors reports an unprojected graph as empty, not as a missing node
+ ---
+ duration_ms: 2.095995
+ type: 'test'
+ ...
+# Subtest: a populated graph with an unknown seed is not reported as empty
+ok 3496 - a populated graph with an unknown seed is not reported as empty
+ ---
+ duration_ms: 13.02724
+ type: 'test'
+ ...
+# Subtest: the renderer names `hyp graph project` when the graph is empty
+ok 3497 - the renderer names `hyp graph project` when the graph is empty
+ ---
+ duration_ms: 0.343883
+ type: 'test'
+ ...
+# Subtest: an ordinary not-found still renders its own error and candidates
+ok 3498 - an ordinary not-found still renders its own error and candidates
+ ---
+ duration_ms: 0.124779
+ type: 'test'
+ ...
+# Subtest: embed sends Bearer key from the configured env var and returns aligned vectors
+ok 3499 - embed sends Bearer key from the configured env var and returns aligned vectors
+ ---
+ duration_ms: 3.049916
+ type: 'test'
+ ...
+# Subtest: embed chunks batches larger than max_batch and preserves order
+ok 3500 - embed chunks batches larger than max_batch and preserves order
+ ---
+ duration_ms: 0.603748
+ type: 'test'
+ ...
+# Subtest: embed without the env var sends no Authorization header (localhost servers)
+ok 3501 - embed without the env var sends no Authorization header (localhost servers)
+ ---
+ duration_ms: 0.471276
+ type: 'test'
+ ...
+# Subtest: embed maps a 401 without a key to a hint naming the env var, never the value
+ok 3502 - embed maps a 401 without a key to a hint naming the env var, never the value
+ ---
+ duration_ms: 0.858955
+ type: 'test'
+ ...
+# Subtest: embed error messages never contain the API key
+ok 3503 - embed error messages never contain the API key
+ ---
+ duration_ms: 0.557197
+ type: 'test'
+ ...
+# Subtest: embed errors and logs never contain the provider error body
+ok 3504 - embed errors and logs never contain the provider error body
+ ---
+ duration_ms: 0.418176
+ type: 'test'
+ ...
+# Subtest: embed rejects an empty input batch
+ok 3505 - embed rejects an empty input batch
+ ---
+ duration_ms: 0.261718
+ type: 'test'
+ ...
+# Subtest: embed surfaces a count mismatch as embedder_bad_response
+ok 3506 - embed surfaces a count mismatch as embedder_bad_response
+ ---
+ duration_ms: 0.44707
+ type: 'test'
+ ...
+# Subtest: parseEmbeddingsPayload rejects a malformed entry and a missing index
+ok 3507 - parseEmbeddingsPayload rejects a malformed entry and a missing index
+ ---
+ duration_ms: 0.259524
+ type: 'test'
+ ...
+# Subtest: validateEmbedderConfig defaults to OpenAI with OPENAI_API_KEY
+ok 3508 - validateEmbedderConfig defaults to OpenAI with OPENAI_API_KEY
+ ---
+ duration_ms: 0.950405
+ type: 'test'
+ ...
+# Subtest: validateEmbedderConfig accepts a localhost override (Ollama shape)
+ok 3509 - validateEmbedderConfig accepts a localhost override (Ollama shape)
+ ---
+ duration_ms: 0.225472
+ type: 'test'
+ ...
+# Subtest: validateEmbedderConfig accepts dimensions for v3 shortening
+ok 3510 - validateEmbedderConfig accepts dimensions for v3 shortening
+ ---
+ duration_ms: 1.267877
+ type: 'test'
+ ...
+# Subtest: validateEmbedderConfig rejects a non-object config
+ok 3511 - validateEmbedderConfig rejects a non-object config
+ ---
+ duration_ms: 0.14422
+ type: 'test'
+ ...
+# Subtest: validateEmbedderConfig rejects a non-http base_url
+ok 3512 - validateEmbedderConfig rejects a non-http base_url
+ ---
+ duration_ms: 0.154895
+ type: 'test'
+ ...
+# Subtest: validateEmbedderConfig rejects a malformed base_url
+ok 3513 - validateEmbedderConfig rejects a malformed base_url
+ ---
+ duration_ms: 0.138531
+ type: 'test'
+ ...
+# Subtest: validateEmbedderConfig rejects non-positive numeric fields
+ok 3514 - validateEmbedderConfig rejects non-positive numeric fields
+ ---
+ duration_ms: 0.179552
+ type: 'test'
+ ...
+# Subtest: embeddingsEndpoint appends /v1/embeddings to a bare origin
+ok 3515 - embeddingsEndpoint appends /v1/embeddings to a bare origin
+ ---
+ duration_ms: 0.122837
+ type: 'test'
+ ...
+# Subtest: embeddingsEndpoint does not double /v1 on a /v1-suffixed base
+ok 3516 - embeddingsEndpoint does not double /v1 on a /v1-suffixed base
+ ---
+ duration_ms: 0.321719
+ type: 'test'
+ ...
+# Subtest: without clustering, a wide per-conversation column stays dictionary-encoded (hyparquet-writer\#35)
+ok 3517 - without clustering, a wide per-conversation column stays dictionary-encoded (hyparquet-writer\#35)
+ ---
+ duration_ms: 60.477089
+ type: 'test'
+ ...
+# Subtest: with clustering, the wide column stays dictionary-encoded at no size cost
+ok 3518 - with clustering, the wide column stays dictionary-encoded at no size cost
+ ---
+ duration_ms: 44.46368
+ type: 'test'
+ ...
+# Subtest: the per-group byte cap splits a single high-volume conversation into multiple row groups
+ok 3519 - the per-group byte cap splits a single high-volume conversation into multiple row groups
+ ---
+ duration_ms: 7.91513
+ type: 'test'
+ ...
+# Subtest: a fat row flushes the group before it is added, so no group overshoots the byte cap
+ok 3520 - a fat row flushes the group before it is added, so no group overshoots the byte cap
+ ---
+ duration_ms: 225.217569
+ type: 'test'
+ ...
+# Subtest: JSON object columns are interned so they dictionary-encode AND round-trip as objects
+ok 3521 - JSON object columns are interned so they dictionary-encode AND round-trip as objects
+ ---
+ duration_ms: 8.442662
+ type: 'test'
+ ...
+# Subtest: interning never merges distinct values: BigInt vs same-text string vs sentinel-shaped object
+ok 3522 - interning never merges distinct values: BigInt vs same-text string vs sentinel-shaped object
+ ---
+ duration_ms: 0.594013
+ type: 'test'
+ ...
+# Subtest: clusterColumns leaves row counts and schema unchanged
+ok 3523 - clusterColumns leaves row counts and schema unchanged
+ ---
+ duration_ms: 0.812324
+ type: 'test'
+ ...
+# Subtest: gascity discoverParts surfaces committed source= partitions alongside the spool
+ok 3524 - gascity discoverParts surfaces committed source= partitions alongside the spool
+ ---
+ duration_ms: 30.170746
+ type: 'test'
+ ...
+# Subtest: gascity createDataSource reads rows committed under source= partitions
+ok 3525 - gascity createDataSource reads rows committed under source= partitions
+ ---
+ duration_ms: 19.879375
+ type: 'test'
+ ...
+# Subtest: gascity createDataSource returns an empty source on a cold cache
+ok 3526 - gascity createDataSource returns an empty source on a cold cache
+ ---
+ duration_ms: 1.456443
+ type: 'test'
+ ...
+# Subtest: codex activates before openclaw in the real bundled boot order
+ok 3527 - codex activates before openclaw in the real bundled boot order
+ ---
+ duration_ms: 26.949841
+ type: 'test'
+ ...
+# Subtest: the codex-before-openclaw order does not depend on manifest input order
+ok 3528 - the codex-before-openclaw order does not depend on manifest input order
+ ---
+ duration_ms: 6.623327
+ type: 'test'
+ ...
+# Subtest: the route table still carries the coverage the no-change tests walk
+ok 3529 - the route table still carries the coverage the no-change tests walk
+ ---
+ duration_ms: 0.238672
+ type: 'test'
+ ...
+# Subtest: existing routes are unchanged when the openai slot is won by activation order @hypaware/codex then @hypaware/openclaw
+ok 3530 - existing routes are unchanged when the openai slot is won by activation order @hypaware/codex then @hypaware/openclaw
+ ---
+ duration_ms: 1.872456
+ type: 'test'
+ ...
+# Subtest: a steered openai turn is routable when the openai slot is won by activation order @hypaware/codex then @hypaware/openclaw
+ok 3531 - a steered openai turn is routable when the openai slot is won by activation order @hypaware/codex then @hypaware/openclaw
+ ---
+ duration_ms: 0.363503
+ type: 'test'
+ ...
+# Subtest: existing routes are unchanged when the openai slot is won by activation order @hypaware/openclaw then @hypaware/codex
+ok 3532 - existing routes are unchanged when the openai slot is won by activation order @hypaware/openclaw then @hypaware/codex
+ ---
+ duration_ms: 0.540872
+ type: 'test'
+ ...
+# Subtest: a steered openai turn is routable when the openai slot is won by activation order @hypaware/openclaw then @hypaware/codex
+ok 3533 - a steered openai turn is routable when the openai slot is won by activation order @hypaware/openclaw then @hypaware/codex
+ ---
+ duration_ms: 0.63766
+ type: 'test'
+ ...
+# Subtest: the header rung does not divert anthropic traffic that never sends it
+ok 3534 - the header rung does not divert anthropic traffic that never sends it
+ ---
+ duration_ms: 0.390163
+ type: 'test'
+ ...
+# Subtest: the surviving openai preset does not outrank a config-declared anthropic upstream (@hypaware/codex)
+ok 3535 - the surviving openai preset does not outrank a config-declared anthropic upstream (@hypaware/codex)
+ ---
+ duration_ms: 0.491987
+ type: 'test'
+ ...
+# Subtest: the surviving openai preset does not outrank a config-declared anthropic upstream (@hypaware/codex then @hypaware/openclaw)
+ok 3536 - the surviving openai preset does not outrank a config-declared anthropic upstream (@hypaware/codex then @hypaware/openclaw)
+ ---
+ duration_ms: 0.503705
+ type: 'test'
+ ...
+# Subtest: the surviving openai preset does not outrank a config-declared anthropic upstream (@hypaware/openclaw then @hypaware/codex)
+ok 3537 - the surviving openai preset does not outrank a config-declared anthropic upstream (@hypaware/openclaw then @hypaware/codex)
+ ---
+ duration_ms: 0.26302
+ type: 'test'
+ ...
+# (node:741851) ExperimentalWarning: SQLite is an experimental feature and might change at any time
+# (Use `node --trace-warnings ...` to show where the warning was created)
+# Subtest: provider advertises a stable contribution shape
+ok 3538 - provider advertises a stable contribution shape
+ ---
+ duration_ms: 1.393739
+ type: 'test'
+ ...
+# Subtest: defaultHermesStateDbPath resolves under /.hermes/state.db
+ok 3539 - defaultHermesStateDbPath resolves under /.hermes/state.db
+ ---
+ duration_ms: 0.175948
+ type: 'test'
+ ...
+# Subtest: every session projects into one item, addressed to the projected-exchange materializer
+ok 3540 - every session projects into one item, addressed to the projected-exchange materializer
+ ---
+ duration_ms: 89.634916
+ type: 'test'
+ ...
+# Subtest: provenance carries the state.db path, client name, and native session id
+ok 3541 - provenance carries the state.db path, client name, and native session id
+ ---
+ duration_ms: 67.83999
+ type: 'test'
+ ...
+# Subtest: since bound keeps only sessions started on or after the window, by session (not by message)
+ok 3542 - since bound keeps only sessions started on or after the window, by session (not by message)
+ ---
+ duration_ms: 61.534676
+ type: 'test'
+ ...
+# Subtest: until bound excludes sessions started after the window
+ok 3543 - until bound excludes sessions started after the window
+ ---
+ duration_ms: 48.226329
+ type: 'test'
+ ...
+# Subtest: no window (no since/until/retentionDays) imports every session
+ok 3544 - no window (no since/until/retentionDays) imports every session
+ ---
+ duration_ms: 52.673679
+ type: 'test'
+ ...
+# Subtest: a state.db with zero sessions yields nothing, without throwing
+ok 3545 - a state.db with zero sessions yields nothing, without throwing
+ ---
+ duration_ms: 22.260995
+ type: 'test'
+ ...
+# Subtest: a missing state.db (no hermes installation) yields nothing, without throwing
+ok 3546 - a missing state.db (no hermes installation) yields nothing, without throwing
+ ---
+ duration_ms: 1.060722
+ type: 'test'
+ ...
+# Subtest: a session whose cwd is .hypignore-ignored is skipped, others still import
+ok 3547 - a session whose cwd is .hypignore-ignored is skipped, others still import
+ ---
+ duration_ms: 35.011484
+ type: 'test'
+ ...
+# Subtest: reruns are deterministic: identical items across runs
+ok 3548 - reruns are deterministic: identical items across runs
+ ---
+ duration_ms: 57.002548
+ type: 'test'
+ ...
+# Subtest: validateHermesConfig accepts an empty / absent config
+ok 3549 - validateHermesConfig accepts an empty / absent config
+ ---
+ duration_ms: 1.363803
+ type: 'test'
+ ...
+# Subtest: validateHermesConfig accepts a full valid config
+ok 3550 - validateHermesConfig accepts a full valid config
+ ---
+ duration_ms: 0.238132
+ type: 'test'
+ ...
+# Subtest: validateHermesConfig accepts every documented poll_interval duration suffix
+ok 3551 - validateHermesConfig accepts every documented poll_interval duration suffix
+ ---
+ duration_ms: 0.275639
+ type: 'test'
+ ...
+# Subtest: validateHermesConfig rejects a non-object config
+ok 3552 - validateHermesConfig rejects a non-object config
+ ---
+ duration_ms: 0.15134
+ type: 'test'
+ ...
+# Subtest: validateHermesConfig rejects malformed keys
+ok 3553 - validateHermesConfig rejects malformed keys
+ ---
+ duration_ms: 0.294377
+ type: 'test'
+ ...
+# Subtest: resolveHermesEnabled defaults to true (missing/absent/non-object config)
+ok 3554 - resolveHermesEnabled defaults to true (missing/absent/non-object config)
+ ---
+ duration_ms: 0.138411
+ type: 'test'
+ ...
+# Subtest: resolveHermesEnabled honors an explicit override
+ok 3555 - resolveHermesEnabled honors an explicit override
+ ---
+ duration_ms: 0.168967
+ type: 'test'
+ ...
+# Subtest: the registered hermes section drives validatePluginConfig
+ok 3556 - the registered hermes section drives validatePluginConfig
+ ---
+ duration_ms: 0.536145
+ type: 'test'
+ ...
+# (node:741870) ExperimentalWarning: SQLite is an experimental feature and might change at any time
+# (Use `node --trace-warnings ...` to show where the warning was created)
+# Subtest: activate() registers the hermes source but does not start it (matches claude/codex)
+ok 3557 - activate() registers the hermes source but does not start it (matches claude/codex)
+ ---
+ duration_ms: 4.773335
+ type: 'test'
+ ...
+# Subtest: a re-projection after another writer committed the tail appends zero duplicate rows (spec R2)
+ok 3558 - a re-projection after another writer committed the tail appends zero duplicate rows (spec R2)
+ ---
+ duration_ms: 88.053002
+ type: 'test'
+ ...
+# Subtest: derives redacted remote and repo_root, never asks for HEAD
+ok 3559 - derives redacted remote and repo_root, never asks for HEAD
+ ---
+ duration_ms: 1.398074
+ type: 'test'
+ ...
+# Subtest: an SSH remote is left intact (no userinfo to strip)
+ok 3560 - an SSH remote is left intact (no userinfo to strip)
+ ---
+ duration_ms: 2.812245
+ type: 'test'
+ ...
+# Subtest: degrades to empty when the cwd is not a git repo (deleted worktree, channel scope path)
+ok 3561 - degrades to empty when the cwd is not a git repo (deleted worktree, channel scope path)
+ ---
+ duration_ms: 0.265514
+ type: 'test'
+ ...
+# Subtest: returns empty for an absent cwd without invoking git
+ok 3562 - returns empty for an absent cwd without invoking git
+ ---
+ duration_ms: 0.16462
+ type: 'test'
+ ...
+# Subtest: redactRemoteUserinfo strips credential userinfo from an https remote only
+ok 3563 - redactRemoteUserinfo strips credential userinfo from an https remote only
+ ---
+ duration_ms: 0.15149
+ type: 'test'
+ ...
+# Subtest: hermes manifest loads and validates
+ok 3564 - hermes manifest loads and validates
+ ---
+ duration_ms: 4.682777
+ type: 'test'
+ ...
+# Subtest: hermes requires @hypaware/ai-gateway as a hard plugin dependency
+ok 3565 - hermes requires @hypaware/ai-gateway as a hard plugin dependency
+ ---
+ duration_ms: 0.863041
+ type: 'test'
+ ...
+# Subtest: hermes contributes a config section and a source, no dataset of its own
+ok 3566 - hermes contributes a config section and a source, no dataset of its own
+ ---
+ duration_ms: 1.92797
+ type: 'test'
+ ...
+# Subtest: hermes node_engine requires the node:sqlite floor (LLP 0125)
+ok 3567 - hermes node_engine requires the node:sqlite floor (LLP 0125)
+ ---
+ duration_ms: 0.827668
+ type: 'test'
+ ...
+# Subtest: hermes is bundled and default-activated beside claude and codex
+ok 3568 - hermes is bundled and default-activated beside claude and codex
+ ---
+ duration_ms: 0.183059
+ type: 'test'
+ ...
+# Subtest: golden projection: open interactive session with tool call and reasoning
+ok 3569 - golden projection: open interactive session with tool call and reasoning
+ ---
+ duration_ms: 3.227295
+ type: 'test'
+ ...
+# Subtest: an open session never carries a session-end part
+ok 3570 - an open session never carries a session-end part
+ ---
+ duration_ms: 0.445247
+ type: 'test'
+ ...
+# Subtest: an ended session carries exactly one session-end part with final totals and costs
+ok 3571 - an ended session carries exactly one session-end part with final totals and costs
+ ---
+ duration_ms: 0.485248
+ type: 'test'
+ ...
+# Subtest: projecting the same session twice yields byte-identical identity
+ok 3572 - projecting the same session twice yields byte-identical identity
+ ---
+ duration_ms: 0.768939
+ type: 'test'
+ ...
+# Subtest: a session under an ignored cwd is skipped before any row is built
+ok 3573 - a session under an ignored cwd is skipped before any row is built
+ ---
+ duration_ms: 0.437926
+ type: 'test'
+ ...
+# Subtest: a channel session is stamped with the canonical channel scope path, real cwd preserved
+ok 3574 - a channel session is stamped with the canonical channel scope path, real cwd preserved
+ ---
+ duration_ms: 0.415952
+ type: 'test'
+ ...
+# Subtest: a channel session is governed by a marked channel scope, same as any other cwd
+ok 3575 - a channel session is governed by a marked channel scope, same as any other cwd
+ ---
+ duration_ms: 0.341649
+ type: 'test'
+ ...
+# Subtest: an interactive session with NULL cwd records unconditionally (no scope to match)
+ok 3576 - an interactive session with NULL cwd records unconditionally (no scope to match)
+ ---
+ duration_ms: 0.285994
+ type: 'test'
+ ...
+# Subtest: a session with no messages and no end yields undefined
+ok 3577 - a session with no messages and no end yields undefined
+ ---
+ duration_ms: 0.581484
+ type: 'test'
+ ...
+# Subtest: hermesScopeId and mintHermesMessageId namespace hermes store-scoped integer ids
+ok 3578 - hermesScopeId and mintHermesMessageId namespace hermes store-scoped integer ids
+ ---
+ duration_ms: 0.36162
+ type: 'test'
+ ...
+# Subtest: normalizeHermesProvider prefers a known base_url host, falls back to billing_provider, then unknown
+ok 3579 - normalizeHermesProvider prefers a known base_url host, falls back to billing_provider, then unknown
+ ---
+ duration_ms: 0.191911
+ type: 'test'
+ ...
+# (node:741908) ExperimentalWarning: SQLite is an experimental feature and might change at any time
+# (Use `node --trace-warnings ...` to show where the warning was created)
+# Subtest: a missing state.db idles cleanly: no db opened, no error noise, idle logged once
+ok 3580 - a missing state.db idles cleanly: no db opened, no error noise, idle logged once
+ ---
+ duration_ms: 6.32917
+ type: 'test'
+ ...
+# Subtest: startHermesSource reports ready/idle status when hermes is not installed
+ok 3581 - startHermesSource reports ready/idle status when hermes is not installed
+ ---
+ duration_ms: 1.627154
+ type: 'test'
+ ...
+# Subtest: a poll tick advances the per-session watermark to the store's current state and persists it
+ok 3582 - a poll tick advances the per-session watermark to the store's current state and persists it
+ ---
+ duration_ms: 77.633114
+ type: 'test'
+ ...
+# Subtest: re-projecting a whole session on each tick appends only the new tail (materializer part_id dedupe)
+ok 3583 - re-projecting a whole session on each tick appends only the new tail (materializer part_id dedupe)
+ ---
+ duration_ms: 76.279537
+ type: 'test'
+ ...
+# Subtest: a session ending with no new messages still triggers re-projection and lands the session-end part
+ok 3584 - a session ending with no new messages still triggers re-projection and lands the session-end part
+ ---
+ duration_ms: 63.909927
+ type: 'test'
+ ...
+# Subtest: stop() clears the timer, closes the db, and is safe to call twice
+ok 3585 - stop() clears the timer, closes the db, and is safe to call twice
+ ---
+ duration_ms: 39.565365
+ type: 'test'
+ ...
+# (node:741922) ExperimentalWarning: SQLite is an experimental feature and might change at any time
+# (Use `node --trace-warnings ...` to show where the warning was created)
+# Subtest: openHermesStateDb opens read-only and lists sessions in id order
+ok 3586 - openHermesStateDb opens read-only and lists sessions in id order
+ ---
+ duration_ms: 71.259798
+ type: 'test'
+ ...
+# Subtest: openHermesStateDb.listMessagesForSession returns tool-call and reasoning fields
+ok 3587 - openHermesStateDb.listMessagesForSession returns tool-call and reasoning fields
+ ---
+ duration_ms: 59.862525
+ type: 'test'
+ ...
+# Subtest: the reader connection is genuinely read-only
+ok 3588 - the reader connection is genuinely read-only
+ ---
+ duration_ms: 58.518012
+ type: 'test'
+ ...
+# Subtest: openHermesStateDb refuses cleanly when state.db does not exist
+ok 3589 - openHermesStateDb refuses cleanly when state.db does not exist
+ ---
+ duration_ms: 0.612441
+ type: 'test'
+ ...
+# Subtest: loadSqliteModule turns a missing node:sqlite builtin into a HermesStateDbError
+ok 3590 - loadSqliteModule turns a missing node:sqlite builtin into a HermesStateDbError
+ ---
+ duration_ms: 0.148305
+ type: 'test'
+ ...
+# Subtest: openHermesStateDb propagates the activation refusal via requireFn injection
+ok 3591 - openHermesStateDb propagates the activation refusal via requireFn injection
+ ---
+ duration_ms: 52.237506
+ type: 'test'
+ ...
+# Subtest: isRetryableBusyError classifies real node:sqlite busy/locked errors and nothing else
+ok 3592 - isRetryableBusyError classifies real node:sqlite busy/locked errors and nothing else
+ ---
+ duration_ms: 0.437665
+ type: 'test'
+ ...
+# Subtest: withBusyRetry retries busy errors within the bound then succeeds
+ok 3593 - withBusyRetry retries busy errors within the bound then succeeds
+ ---
+ duration_ms: 0.198071
+ type: 'test'
+ ...
+# Subtest: withBusyRetry gives up after the bound and throws a typed sqlite_busy error
+ok 3594 - withBusyRetry gives up after the bound and throws a typed sqlite_busy error
+ ---
+ duration_ms: 0.352716
+ type: 'test'
+ ...
+# Subtest: withBusyRetry lets a non-busy error propagate immediately, no retry
+ok 3595 - withBusyRetry lets a non-busy error propagate immediately, no retry
+ ---
+ duration_ms: 0.516806
+ type: 'test'
+ ...
+# Subtest: withBusyRetry defaults to DEFAULT_BUSY_RETRY_ATTEMPTS
+ok 3596 - withBusyRetry defaults to DEFAULT_BUSY_RETRY_ATTEMPTS
+ ---
+ duration_ms: 0.293687
+ type: 'test'
+ ...
+# Subtest: HermesStateDb read methods retry through a transiently busy connection
+ok 3597 - HermesStateDb read methods retry through a transiently busy connection
+ ---
+ duration_ms: 0.221086
+ type: 'test'
+ ...
+# Subtest: listChangedSessions flags every session on an empty watermark
+ok 3598 - listChangedSessions flags every session on an empty watermark
+ ---
+ duration_ms: 42.453084
+ type: 'test'
+ ...
+# Subtest: listChangedSessions is empty once the watermark matches current state
+ok 3599 - listChangedSessions is empty once the watermark matches current state
+ ---
+ duration_ms: 43.949919
+ type: 'test'
+ ...
+# Subtest: listChangedSessions catches a new message appended to an open session
+ok 3600 - listChangedSessions catches a new message appended to an open session
+ ---
+ duration_ms: 34.702144
+ type: 'test'
+ ...
+# Subtest: listChangedSessions catches the ended_at NULL -> set transition with no new messages
+ok 3601 - listChangedSessions catches the ended_at NULL -> set transition with no new messages
+ ---
+ duration_ms: 38.440396
+ type: 'test'
+ ...
+# Subtest: cached hermes rows carry stored message times, not the projection wall clock
+ok 3602 - cached hermes rows carry stored message times, not the projection wall clock
+ ---
+ duration_ms: 2.699593
+ type: 'test'
+ ...
+# Subtest: re-projecting the same session yields byte-identical timestamps (dedupe-safe)
+ok 3603 - re-projecting the same session yields byte-identical timestamps (dedupe-safe)
+ ---
+ duration_ms: 1.453529
+ type: 'test'
+ ...
+# Subtest: tableUrlForBlobPrefix normalises slashes and emits blob:// scheme
+ok 3604 - tableUrlForBlobPrefix normalises slashes and emits blob:// scheme
+ ---
+ duration_ms: 1.979869
+ type: 'test'
+ ...
+# Subtest: pathToKey reverses the table URL and accepts subpaths
+ok 3605 - pathToKey reverses the table URL and accepts subpaths
+ ---
+ duration_ms: 0.145761
+ type: 'test'
+ ...
+# Subtest: pathToKey rejects empty input
+ok 3606 - pathToKey rejects empty input
+ ---
+ duration_ms: 0.273086
+ type: 'test'
+ ...
+# Subtest: createBlobStoreIO writer flushes via putObject and applies ifNoneMatch
+ok 3607 - createBlobStoreIO writer flushes via putObject and applies ifNoneMatch
+ ---
+ duration_ms: 29.756737
+ type: 'test'
+ ...
+# Subtest: createBlobStoreIO reader returns AsyncBuffer with byte-faithful slice
+ok 3608 - createBlobStoreIO reader returns AsyncBuffer with byte-faithful slice
+ ---
+ duration_ms: 2.492328
+ type: 'test'
+ ...
+# Subtest: createBlobStoreIO reader surfaces ENOENT for missing objects
+ok 3609 - createBlobStoreIO reader surfaces ENOENT for missing objects
+ ---
+ duration_ms: 0.270251
+ type: 'test'
+ ...
+# Subtest: createBlobStoreIO lister returns immediate basenames sorted
+ok 3610 - createBlobStoreIO lister returns immediate basenames sorted
+ ---
+ duration_ms: 0.405407
+ type: 'test'
+ ...
+# Subtest: collectStream concatenates Node-stream chunks deterministically
+ok 3611 - collectStream concatenates Node-stream chunks deterministically
+ ---
+ duration_ms: 0.66457
+ type: 'test'
+ ...
+# Subtest: createBlobStoreIO refuses BlobStores without putObject
+ok 3612 - createBlobStoreIO refuses BlobStores without putObject
+ ---
+ duration_ms: 0.34232
+ type: 'test'
+ ...
+# Subtest: probeTable reports exists=false when no metadata has been written
+ok 3613 - probeTable reports exists=false when no metadata has been written
+ ---
+ duration_ms: 5.668285
+ type: 'test'
+ ...
+# Subtest: commitBatch creates an Iceberg table on first append and produces a snapshot
+ok 3614 - commitBatch creates an Iceberg table on first append and produces a snapshot
+ ---
+ duration_ms: 28.602493
+ type: 'test'
+ ...
+# Subtest: commitBatch appends keep schema ids stable across batches
+ok 3615 - commitBatch appends keep schema ids stable across batches
+ ---
+ duration_ms: 28.364792
+ type: 'test'
+ ...
+# Subtest: commitBatch retries past a transient metadata precondition collision
+ok 3616 - commitBatch retries past a transient metadata precondition collision
+ ---
+ duration_ms: 59.348183
+ type: 'test'
+ ...
+# Subtest: commitBatch surfaces iceberg_commit_conflict when initial create races
+ok 3617 - commitBatch surfaces iceberg_commit_conflict when initial create races
+ ---
+ duration_ms: 8.291612
+ type: 'test'
+ ...
+# Subtest: probeTable discovers latest snapshot when version-hint.text is stale
+ok 3618 - probeTable discovers latest snapshot when version-hint.text is stale
+ ---
+ duration_ms: 8.850472
+ type: 'test'
+ ...
+# Subtest: probeTable discovers latest snapshot when version-hint.text is missing
+ok 3619 - probeTable discovers latest snapshot when version-hint.text is missing
+ ---
+ duration_ms: 6.581774
+ type: 'test'
+ ...
+# Subtest: markerSubsumedBySnapshot recognises a marker whose snapshot is an ancestor of the current snapshot (no duplicate rows on retry)
+ok 3620 - markerSubsumedBySnapshot recognises a marker whose snapshot is an ancestor of the current snapshot (no duplicate rows on retry)
+ ---
+ duration_ms: 7.800376
+ type: 'test'
+ ...
+# Subtest: probeTable propagates transient metadata read failures instead of masking them as miss
+ok 3621 - probeTable propagates transient metadata read failures instead of masking them as miss
+ ---
+ duration_ms: 0.518989
+ type: 'test'
+ ...
+# Subtest: commitBatch normalises blob_precondition_failed into iceberg_commit_conflict
+ok 3622 - commitBatch normalises blob_precondition_failed into iceberg_commit_conflict
+ ---
+ duration_ms: 0.523766
+ type: 'test'
+ ...
+# Subtest: the committed Iceberg snapshot excludes rows from a local-only directory
+ok 3623 - the committed Iceberg snapshot excludes rows from a local-only directory
+ ---
+ duration_ms: 74.094457
+ type: 'test'
+ ...
+# Subtest: unaffected when no local-only list is configured: every row commits, as before
+ok 3624 - unaffected when no local-only list is configured: every row commits, as before
+ ---
+ duration_ms: 27.273263
+ type: 'test'
+ ...
+# Subtest: normalizeExportRetentionConfig fills defaults
+ok 3625 - normalizeExportRetentionConfig fills defaults
+ ---
+ duration_ms: 0.851524
+ type: 'test'
+ ...
+# Subtest: normalizeExportRetentionConfig honors overrides
+ok 3626 - normalizeExportRetentionConfig honors overrides
+ ---
+ duration_ms: 0.121104
+ type: 'test'
+ ...
+# Subtest: compactExportTable reports no compaction for a missing table
+ok 3627 - compactExportTable reports no compaction for a missing table
+ ---
+ duration_ms: 8.366076
+ type: 'test'
+ ...
+# Subtest: compactExportTable reports a metadata load failure as reason=error, not no-table
+ok 3628 - compactExportTable reports a metadata load failure as reason=error, not no-table
+ ---
+ duration_ms: 3.042675
+ type: 'test'
+ ...
+# Subtest: compactExportTable rewrites a v3 table once the data-file threshold is reached
+ok 3629 - compactExportTable rewrites a v3 table once the data-file threshold is reached
+ ---
+ duration_ms: 24.104376
+ type: 'test'
+ ...
+# Subtest: compactExportTable skips a table whose total-files-size exceeds compact_max_bytes
+ok 3630 - compactExportTable skips a table whose total-files-size exceeds compact_max_bytes
+ ---
+ duration_ms: 10.687744
+ type: 'test'
+ ...
+# Subtest: compactExportTable reports a commit conflict and cleans up the staged files
+ok 3631 - compactExportTable reports a commit conflict and cleans up the staged files
+ ---
+ duration_ms: 16.782508
+ type: 'test'
+ ...
+# Subtest: compactExportTable reports a non-conflict commit failure as reason=error and leaves staged files
+ok 3632 - compactExportTable reports a non-conflict commit failure as reason=error and leaves staged files
+ ---
+ duration_ms: 9.52747
+ type: 'test'
+ ...
+# Subtest: compactExportTable cleans up partial output when staging fails mid-flight
+ok 3633 - compactExportTable cleans up partial output when staging fails mid-flight
+ ---
+ duration_ms: 8.789759
+ type: 'test'
+ ...
+# Subtest: compactExportTable reports success when the commit landed despite a thrown 412
+ok 3634 - compactExportTable reports success when the commit landed despite a thrown 412
+ ---
+ duration_ms: 9.050546
+ type: 'test'
+ ...
+# Subtest: derivePartitioning returns null without a registration
+ok 3635 - derivePartitioning returns null without a registration
+ ---
+ duration_ms: 0.830172
+ type: 'test'
+ ...
+# Subtest: derivePartitioning returns null when no primaryTimestampColumn
+ok 3636 - derivePartitioning returns null when no primaryTimestampColumn
+ ---
+ duration_ms: 0.099361
+ type: 'test'
+ ...
+# Subtest: derivePartitioning returns null when the timestamp column is absent from the schema
+ok 3637 - derivePartitioning returns null when the timestamp column is absent from the schema
+ ---
+ duration_ms: 0.114123
+ type: 'test'
+ ...
+# Subtest: derivePartitioning builds day(primaryTimestampColumn) + conversation sort for ai-gateway
+ok 3638 - derivePartitioning builds day(primaryTimestampColumn) + conversation sort for ai-gateway
+ ---
+ duration_ms: 0.885415
+ type: 'test'
+ ...
+# Subtest: derivePartitioning day grain is independent of cachePartitioning (does not inherit conversation_id partitioning)
+ok 3639 - derivePartitioning day grain is independent of cachePartitioning (does not inherit conversation_id partitioning)
+ ---
+ duration_ms: 0.185732
+ type: 'test'
+ ...
+# Subtest: derivePartitioning yields an empty (unsorted) order when no cachePartitioning is declared
+ok 3640 - derivePartitioning yields an empty (unsorted) order when no cachePartitioning is declared
+ ---
+ duration_ms: 1.057297
+ type: 'test'
+ ...
+# Subtest: derivePartitioning excludes non-identity cachePartitioning fields from the sort
+ok 3641 - derivePartitioning excludes non-identity cachePartitioning fields from the sort
+ ---
+ duration_ms: 0.232203
+ type: 'test'
+ ...
+# Subtest: commitBatch creates a day-partitioned, conversation-sorted table and buckets rows by day
+ok 3642 - commitBatch creates a day-partitioned, conversation-sorted table and buckets rows by day
+ ---
+ duration_ms: 46.711507
+ type: 'test'
+ ...
+# Subtest: commitBatch rejects partition-spec drift on a previously-unpartitioned table
+ok 3643 - commitBatch rejects partition-spec drift on a previously-unpartitioned table
+ ---
+ duration_ms: 10.042884
+ type: 'test'
+ ...
+# Subtest: commitBatch rejects reverse drift: null partitioning onto a partitioned table
+ok 3644 - commitBatch rejects reverse drift: null partitioning onto a partitioned table
+ ---
+ duration_ms: 10.75689
+ type: 'test'
+ ...
+# Subtest: commitBatch accepts a re-commit with the same derived partitioning (no false drift)
+ok 3645 - commitBatch accepts a re-commit with the same derived partitioning (no false drift)
+ ---
+ duration_ms: 20.251861
+ type: 'test'
+ ...
+# Subtest: format-iceberg manifest is valid
+ok 3646 - format-iceberg manifest is valid
+ ---
+ duration_ms: 8.113381
+ type: 'test'
+ ...
+# Subtest: format-iceberg manifest passes the pure validator
+ok 3647 - format-iceberg manifest passes the pure validator
+ ---
+ duration_ms: 1.829831
+ type: 'test'
+ ...
+# Subtest: format-iceberg is on the V1 bundled allowlist
+ok 3648 - format-iceberg is on the V1 bundled allowlist
+ ---
+ duration_ms: 0.258322
+ type: 'test'
+ ...
+# Subtest: discoverBundledPlugins surfaces format-iceberg from the workspace
+ok 3649 - discoverBundledPlugins surfaces format-iceberg from the workspace
+ ---
+ duration_ms: 10.367427
+ type: 'test'
+ ...
+# Subtest: firstPartyPluginMetadata records the iceberg requires/provides matrix
+ok 3650 - firstPartyPluginMetadata records the iceberg requires/provides matrix
+ ---
+ duration_ms: 0.27598
+ type: 'test'
+ ...
+# Subtest: createTableFormatProvider returns the expected capability shape
+ok 3651 - createTableFormatProvider returns the expected capability shape
+ ---
+ duration_ms: 0.139442
+ type: 'test'
+ ...
+# Subtest: TableFormatProvider.createSink rejects missing BlobStore / encoder / non-parquet encoder
+ok 3652 - TableFormatProvider.createSink rejects missing BlobStore / encoder / non-parquet encoder
+ ---
+ duration_ms: 2.024447
+ type: 'test'
+ ...
+# Subtest: writer maps s3 errorKind=blob_precondition_failed -> iceberg iceberg_commit_conflict
+ok 3653 - writer maps s3 errorKind=blob_precondition_failed -> iceberg iceberg_commit_conflict
+ ---
+ duration_ms: 27.997433
+ type: 'test'
+ ...
+# Subtest: writer maps s3 errorKind=s3_access_denied -> iceberg iceberg_blob_store_missing
+ok 3654 - writer maps s3 errorKind=s3_access_denied -> iceberg iceberg_blob_store_missing
+ ---
+ duration_ms: 0.237962
+ type: 'test'
+ ...
+# Subtest: writer maps s3 errorKind=s3_bucket_missing -> iceberg iceberg_blob_store_missing
+ok 3655 - writer maps s3 errorKind=s3_bucket_missing -> iceberg iceberg_blob_store_missing
+ ---
+ duration_ms: 0.198922
+ type: 'test'
+ ...
+# Subtest: writer maps s3 errorKind=s3_credentials_missing -> iceberg iceberg_blob_store_missing
+ok 3656 - writer maps s3 errorKind=s3_credentials_missing -> iceberg iceberg_blob_store_missing
+ ---
+ duration_ms: 0.154876
+ type: 'test'
+ ...
+# Subtest: writer maps s3 errorKind=s3_region_mismatch -> iceberg iceberg_blob_store_missing
+ok 3657 - writer maps s3 errorKind=s3_region_mismatch -> iceberg iceberg_blob_store_missing
+ ---
+ duration_ms: 0.15159
+ type: 'test'
+ ...
+# Subtest: writer maps s3 errorKind=s3_config_invalid -> iceberg iceberg_blob_store_missing
+ok 3658 - writer maps s3 errorKind=s3_config_invalid -> iceberg iceberg_blob_store_missing
+ ---
+ duration_ms: 0.151971
+ type: 'test'
+ ...
+# Subtest: writer maps s3 errorKind=s3_blob_store_unconfigured -> iceberg iceberg_blob_store_missing
+ok 3659 - writer maps s3 errorKind=s3_blob_store_unconfigured -> iceberg iceberg_blob_store_missing
+ ---
+ duration_ms: 0.142166
+ type: 'test'
+ ...
+# Subtest: writer maps s3 errorKind=s3_put_failed -> iceberg iceberg_data_write_failed
+ok 3660 - writer maps s3 errorKind=s3_put_failed -> iceberg iceberg_data_write_failed
+ ---
+ duration_ms: 0.168115
+ type: 'test'
+ ...
+# Subtest: writer maps s3 errorKind=s3_throttled -> iceberg iceberg_data_write_failed
+ok 3661 - writer maps s3 errorKind=s3_throttled -> iceberg iceberg_data_write_failed
+ ---
+ duration_ms: 0.563316
+ type: 'test'
+ ...
+# Subtest: reader maps s3 errorKind=s3_access_denied -> iceberg iceberg_blob_store_missing
+ok 3662 - reader maps s3 errorKind=s3_access_denied -> iceberg iceberg_blob_store_missing
+ ---
+ duration_ms: 0.57264
+ type: 'test'
+ ...
+# Subtest: reader maps s3 errorKind=s3_bucket_missing -> iceberg iceberg_blob_store_missing
+ok 3663 - reader maps s3 errorKind=s3_bucket_missing -> iceberg iceberg_blob_store_missing
+ ---
+ duration_ms: 0.248558
+ type: 'test'
+ ...
+# Subtest: reader maps s3 errorKind=s3_credentials_missing -> iceberg iceberg_blob_store_missing
+ok 3664 - reader maps s3 errorKind=s3_credentials_missing -> iceberg iceberg_blob_store_missing
+ ---
+ duration_ms: 0.216429
+ type: 'test'
+ ...
+# Subtest: reader maps s3 errorKind=s3_put_failed -> iceberg iceberg_metadata_read_failed
+ok 3665 - reader maps s3 errorKind=s3_put_failed -> iceberg iceberg_metadata_read_failed
+ ---
+ duration_ms: 0.198392
+ type: 'test'
+ ...
+# Subtest: reader maps s3 errorKind=s3_throttled -> iceberg iceberg_metadata_read_failed
+ok 3666 - reader maps s3 errorKind=s3_throttled -> iceberg iceberg_metadata_read_failed
+ ---
+ duration_ms: 0.168947
+ type: 'test'
+ ...
+# Subtest: lister maps s3 errorKind=s3_access_denied -> iceberg iceberg_blob_store_missing
+ok 3667 - lister maps s3 errorKind=s3_access_denied -> iceberg iceberg_blob_store_missing
+ ---
+ duration_ms: 0.325124
+ type: 'test'
+ ...
+# Subtest: lister maps s3 errorKind=s3_bucket_missing -> iceberg iceberg_blob_store_missing
+ok 3668 - lister maps s3 errorKind=s3_bucket_missing -> iceberg iceberg_blob_store_missing
+ ---
+ duration_ms: 0.179693
+ type: 'test'
+ ...
+# Subtest: lister maps s3 errorKind=s3_put_failed -> iceberg iceberg_blob_io_list_failed
+ok 3669 - lister maps s3 errorKind=s3_put_failed -> iceberg iceberg_blob_io_list_failed
+ ---
+ duration_ms: 0.177289
+ type: 'test'
+ ...
+# Subtest: writer onWrite observer fires with key + etag for successful puts
+ok 3670 - writer onWrite observer fires with key + etag for successful puts
+ ---
+ duration_ms: 0.291132
+ type: 'test'
+ ...
+# Subtest: writer onWrite observer that throws does not break the commit
+ok 3671 - writer onWrite observer that throws does not break the commit
+ ---
+ duration_ms: 0.239113
+ type: 'test'
+ ...
+# Subtest: icebergSchemaForColumns maps every kernel basic type
+ok 3672 - icebergSchemaForColumns maps every kernel basic type
+ ---
+ duration_ms: 1.279976
+ type: 'test'
+ ...
+# Subtest: mergeFieldIdsFromTable preserves existing field ids and appends nullable additions
+ok 3673 - mergeFieldIdsFromTable preserves existing field ids and appends nullable additions
+ ---
+ duration_ms: 0.371294
+ type: 'test'
+ ...
+# Subtest: mergeFieldIdsFromTable rejects incompatible type changes with iceberg_schema_incompatible
+ok 3674 - mergeFieldIdsFromTable rejects incompatible type changes with iceberg_schema_incompatible
+ ---
+ duration_ms: 0.339977
+ type: 'test'
+ ...
+# Subtest: mergeFieldIdsFromTable rejects new required columns
+ok 3675 - mergeFieldIdsFromTable rejects new required columns
+ ---
+ duration_ms: 0.137779
+ type: 'test'
+ ...
+# Subtest: mergeFieldIdsFromTable rejects column removals
+ok 3676 - mergeFieldIdsFromTable rejects column removals
+ ---
+ duration_ms: 0.420139
+ type: 'test'
+ ...
+# Subtest: mergeFieldIdsFromTable rejects nullable → required tightening
+ok 3677 - mergeFieldIdsFromTable rejects nullable → required tightening
+ ---
+ duration_ms: 0.141846
+ type: 'test'
+ ...
+# Subtest: rowsToIcebergRecords coerces numeric strings and BigInt for INT64
+ok 3678 - rowsToIcebergRecords coerces numeric strings and BigInt for INT64
+ ---
+ duration_ms: 0.257832
+ type: 'test'
+ ...
+# Subtest: rowsToIcebergRecords throws iceberg_data_write_failed on required nulls
+ok 3679 - rowsToIcebergRecords throws iceberg_data_write_failed on required nulls
+ ---
+ duration_ms: 0.146443
+ type: 'test'
+ ...
+# Subtest: rowsToIcebergRecords canonicalizes JSON objects to strings
+ok 3680 - rowsToIcebergRecords canonicalizes JSON objects to strings
+ ---
+ duration_ms: 0.28925
+ type: 'test'
+ ...
+# Subtest: markerKey renders /state/exported-batches///.json
+ok 3681 - markerKey renders /state/exported-batches///.json
+ ---
+ duration_ms: 0.96029
+ type: 'test'
+ ...
+# Subtest: markerKey rejects empty segments with iceberg_state_invalid
+ok 3682 - markerKey rejects empty segments with iceberg_state_invalid
+ ---
+ duration_ms: 1.334879
+ type: 'test'
+ ...
+# Subtest: markerKey sanitizes path-separator characters out of dataset/batch ids
+ok 3683 - markerKey sanitizes path-separator characters out of dataset/batch ids
+ ---
+ duration_ms: 0.117338
+ type: 'test'
+ ...
+# Subtest: writeMarker / loadMarker roundtrips a record verbatim
+ok 3684 - writeMarker / loadMarker roundtrips a record verbatim
+ ---
+ duration_ms: 2.780506
+ type: 'test'
+ ...
+# Subtest: loadMarker returns null for missing markers
+ok 3685 - loadMarker returns null for missing markers
+ ---
+ duration_ms: 0.203659
+ type: 'test'
+ ...
+# Subtest: loadMarker surfaces malformed JSON as iceberg_metadata_read_failed
+ok 3686 - loadMarker surfaces malformed JSON as iceberg_metadata_read_failed
+ ---
+ duration_ms: 0.760986
+ type: 'test'
+ ...
+# Subtest: markerSubsumedBySnapshot returns true when current snapshot matches the marker exactly
+ok 3687 - markerSubsumedBySnapshot returns true when current snapshot matches the marker exactly
+ ---
+ duration_ms: 0.228637
+ type: 'test'
+ ...
+# Subtest: markerSubsumedBySnapshot accepts a probe-state object and resolves equality without metadata
+ok 3688 - markerSubsumedBySnapshot accepts a probe-state object and resolves equality without metadata
+ ---
+ duration_ms: 0.120072
+ type: 'test'
+ ...
+# Subtest: markerSubsumedBySnapshot walks parent-snapshot-id to recognise a superseded ancestor
+ok 3689 - markerSubsumedBySnapshot walks parent-snapshot-id to recognise a superseded ancestor
+ ---
+ duration_ms: 0.305465
+ type: 'test'
+ ...
+# Subtest: markerSubsumedBySnapshot returns false when the marker snapshot is no longer in the snapshot graph (expired)
+ok 3690 - markerSubsumedBySnapshot returns false when the marker snapshot is no longer in the snapshot graph (expired)
+ ---
+ duration_ms: 0.340408
+ type: 'test'
+ ...
+# Subtest: markerSubsumedBySnapshot tolerates a malformed cyclic parent chain without spinning
+ok 3691 - markerSubsumedBySnapshot tolerates a malformed cyclic parent chain without spinning
+ ---
+ duration_ms: 0.174806
+ type: 'test'
+ ...
+# Subtest: local-fs incremental export: ranged filename, watermark advance, skip-empty, then a new range
+ok 3692 - local-fs incremental export: ranged filename, watermark advance, skip-empty, then a new range
+ ---
+ duration_ms: 23.229227
+ type: 'test'
+ ...
+# Subtest: local-fs drop-only tick: no blob is written, but the watermark advances past the withheld rows (LLP 0070)
+ok 3693 - local-fs drop-only tick: no blob is written, but the watermark advances past the withheld rows (LLP 0070)
+ ---
+ duration_ms: 10.604858
+ type: 'test'
+ ...
+# Subtest: attach writes exactly the two provider entries, bare origin vs +/v1
+ok 3694 - attach writes exactly the two provider entries, bare origin vs +/v1
+ ---
+ duration_ms: 11.431785
+ type: 'test'
+ ...
+# Subtest: attach preserves every other key in openclaw.json (R1)
+ok 3695 - attach preserves every other key in openclaw.json (R1)
+ ---
+ duration_ms: 3.627833
+ type: 'test'
+ ...
+# Subtest: attach refuses without writing when a provider key already exists (R2)
+ok 3696 - attach refuses without writing when a provider key already exists (R2)
+ ---
+ duration_ms: 10.436933
+ type: 'test'
+ ...
+# Subtest: a second attach at a moved endpoint rewrites both baseUrls (re-attach on drift)
+ok 3697 - a second attach at a moved endpoint rewrites both baseUrls (re-attach on drift)
+ ---
+ duration_ms: 4.395601
+ type: 'test'
+ ...
+# Subtest: re-attach over a pre-client-header entry succeeds and adds the header
+ok 3698 - re-attach over a pre-client-header entry succeeds and adds the header
+ ---
+ duration_ms: 6.18473
+ type: 'test'
+ ...
+# Subtest: an entry that is not ours still refuses, however close it looks (R2)
+ok 3699 - an entry that is not ours still refuses, however close it looks (R2)
+ ---
+ duration_ms: 11.732372
+ type: 'test'
+ ...
+# Subtest: attach never throws on refusal, so attach-on-join warns instead of failing
+ok 3700 - attach never throws on refusal, so attach-on-join warns instead of failing
+ ---
+ duration_ms: 2.304322
+ type: 'test'
+ ...
+# Subtest: attach prints the openclaw gateway restart instruction on the human path (R4)
+ok 3701 - attach prints the openclaw gateway restart instruction on the human path (R4)
+ ---
+ duration_ms: 2.418867
+ type: 'test'
+ ...
+# Subtest: attach prints the restart instruction on the --json path too (R4)
+ok 3702 - attach prints the restart instruction on the --json path too (R4)
+ ---
+ duration_ms: 2.562474
+ type: 'test'
+ ...
+# Subtest: attach --dry-run reports the write without touching the file
+ok 3703 - attach --dry-run reports the write without touching the file
+ ---
+ duration_ms: 2.524196
+ type: 'test'
+ ...
+# Subtest: attach --dry-run reports the refusal it would hit, not a write it would not do
+ok 3704 - attach --dry-run reports the refusal it would hit, not a write it would not do
+ ---
+ duration_ms: 2.187115
+ type: 'test'
+ ...
+# Subtest: attach resolves openclaw.json through $OPENCLAW_HOME when set
+ok 3705 - attach resolves openclaw.json through $OPENCLAW_HOME when set
+ ---
+ duration_ms: 6.649637
+ type: 'test'
+ ...
+# Subtest: a missing openclaw.json is a hard failure, not a config attach invents
+ok 3706 - a missing openclaw.json is a hard failure, not a config attach invents
+ ---
+ duration_ms: 4.58565
+ type: 'test'
+ ...
+# Subtest: a malformed openclaw.json is a hard failure, and is left alone
+ok 3707 - a malformed openclaw.json is a hard failure, and is left alone
+ ---
+ duration_ms: 4.711051
+ type: 'test'
+ ...
+# Subtest: a trailing slash on the endpoint does not double the openai /v1 separator
+ok 3708 - a trailing slash on the endpoint does not double the openai /v1 separator
+ ---
+ duration_ms: 2.544318
+ type: 'test'
+ ...
+# Subtest: the fixture writes the record shape OpenClaw actually appends
+ok 3709 - the fixture writes the record shape OpenClaw actually appends
+ ---
+ duration_ms: 10.525939
+ type: 'test'
+ ...
+# Subtest: projects one item per session file, with the header cwd and native session id
+ok 3710 - projects one item per session file, with the header cwd and native session id
+ ---
+ duration_ms: 5.805724
+ type: 'test'
+ ...
+# Subtest: backfilled rows carry the session file's own message ids, never a fallback hash
+ok 3711 - backfilled rows carry the session file's own message ids, never a fallback hash
+ ---
+ duration_ms: 4.456333
+ type: 'test'
+ ...
+# Subtest: the gateway chains previous_message_id across the session, rooting the first message
+ok 3712 - the gateway chains previous_message_id across the session, rooting the first message
+ ---
+ duration_ms: 4.404234
+ type: 'test'
+ ...
+# Subtest: assistant usage lands under the gateway-wide token names, whatever spelling the file used
+ok 3713 - assistant usage lands under the gateway-wide token names, whatever spelling the file used
+ ---
+ duration_ms: 4.477015
+ type: 'test'
+ ...
+# Subtest: a toolResult record lands under the role the sibling adapters already write
+ok 3714 - a toolResult record lands under the role the sibling adapters already write
+ ---
+ duration_ms: 8.566751
+ type: 'test'
+ ...
+# Subtest: a backfilled row and a settled live row for the same turn carry identical message_id/part_id
+ok 3715 - a backfilled row and a settled live row for the same turn carry identical message_id/part_id
+ ---
+ duration_ms: 5.75671
+ type: 'test'
+ ...
+# Subtest: a session whose header cwd is policy-ignored projects nothing at all
+ok 3716 - a session whose header cwd is policy-ignored projects nothing at all
+ ---
+ duration_ms: 2.952778
+ type: 'test'
+ ...
+# Subtest: a session with no usable cwd is not gated, matching the existing convention
+ok 3717 - a session with no usable cwd is not gated, matching the existing convention
+ ---
+ duration_ms: 3.028744
+ type: 'test'
+ ...
+# Subtest: an unrelated ignored directory leaves the session projecting
+ok 3718 - an unrelated ignored directory leaves the session projecting
+ ---
+ duration_ms: 2.937905
+ type: 'test'
+ ...
+# Subtest: a claude-cli turn is excluded whole, prompt included, and reported as covered elsewhere
+ok 3719 - a claude-cli turn is excluded whole, prompt included, and reported as covered elsewhere
+ ---
+ duration_ms: 4.213734
+ type: 'test'
+ ...
+# Subtest: a mixed real-shape session partially projects: anthropic turns land, claude-cli turns stay excluded
+ok 3720 - a mixed real-shape session partially projects: anthropic turns land, claude-cli turns stay excluded
+ ---
+ duration_ms: 3.672832
+ type: 'test'
+ ...
+# Subtest: an unrecognized direct-API provider projects, no release needed
+ok 3721 - an unrecognized direct-API provider projects, no release needed
+ ---
+ duration_ms: 1.916743
+ type: 'test'
+ ...
+# Subtest: an ollama turn projects at transcript fidelity, each row under its own provider
+ok 3722 - an ollama turn projects at transcript fidelity, each row under its own provider
+ ---
+ duration_ms: 1.794487
+ type: 'test'
+ ...
+# Subtest: an unrecognized provider stamped api "cli" is excluded, and not covered_by anything
+ok 3723 - an unrecognized provider stamped api "cli" is excluded, and not covered_by anything
+ ---
+ duration_ms: 1.5203
+ type: 'test'
+ ...
+# Subtest: a claude-cli turn stamped api "cli" is excluded and covered by the Claude transcript
+ok 3724 - a claude-cli turn stamped api "cli" is excluded and covered by the Claude transcript
+ ---
+ duration_ms: 1.916093
+ type: 'test'
+ ...
+# Subtest: a prompt whose turn has no anchor is excluded as unknown, not projected under the next turn
+ok 3725 - a prompt whose turn has no anchor is excluded as unknown, not projected under the next turn
+ ---
+ duration_ms: 2.030817
+ type: 'test'
+ ...
+# Subtest: a tool result after a mid-loop abort resolves to its own turn, not the next one
+ok 3726 - a tool result after a mid-loop abort resolves to its own turn, not the next one
+ ---
+ duration_ms: 2.607453
+ type: 'test'
+ ...
+# Subtest: a trailing unanswered prompt is unknown, not the previous turn's backend
+ok 3727 - a trailing unanswered prompt is unknown, not the previous turn's backend
+ ---
+ duration_ms: 2.541694
+ type: 'test'
+ ...
+# Subtest: a window cut between prompt and reply does not change the prompt's attribution
+ok 3728 - a window cut between prompt and reply does not change the prompt's attribution
+ ---
+ duration_ms: 1.999198
+ type: 'test'
+ ...
+# Subtest: a record stating only provider does not inherit a neighbor's api
+ok 3729 - a record stating only provider does not inherit a neighbor's api
+ ---
+ duration_ms: 2.014813
+ type: 'test'
+ ...
+# Subtest: codex and codex-mini are denied by prefix with their covering route; codexcloud is neither denied nor mislabeled
+ok 3730 - codex and codex-mini are denied by prefix with their covering route; codexcloud is neither denied nor mislabeled
+ ---
+ duration_ms: 2.313386
+ type: 'test'
+ ...
+# Subtest: whitespace and case on api or provider do not slip the denylist
+ok 3731 - whitespace and case on api or provider do not slip the denylist
+ ---
+ duration_ms: 2.090147
+ type: 'test'
+ ...
+# Subtest: an api-only record does not block the borrow to the turn's real anchor
+ok 3732 - an api-only record does not block the borrow to the turn's real anchor
+ ---
+ duration_ms: 2.139382
+ type: 'test'
+ ...
+# Subtest: a session that never states a provider projects nothing
+ok 3733 - a session that never states a provider projects nothing
+ ---
+ duration_ms: 2.186374
+ type: 'test'
+ ...
+# Subtest: records outside the resolved window are not projected
+ok 3734 - records outside the resolved window are not projected
+ ---
+ duration_ms: 2.620383
+ type: 'test'
+ ...
+# Subtest: every agent under the agents root is scanned, in a deterministic order
+ok 3735 - every agent under the agents root is scanned, in a deterministic order
+ ---
+ duration_ms: 2.422952
+ type: 'test'
+ ...
+# Subtest: a relocated install is found through OPENCLAW_HOME, the same way settlement finds it
+ok 3736 - a relocated install is found through OPENCLAW_HOME, the same way settlement finds it
+ ---
+ duration_ms: 1.896152
+ type: 'test'
+ ...
+# Subtest: a missing OpenClaw install scans to zero sessions rather than failing the run
+ok 3737 - a missing OpenClaw install scans to zero sessions rather than failing the run
+ ---
+ duration_ms: 0.792664
+ type: 'test'
+ ...
+# Subtest: a session file with no readable header still projects, ungated
+ok 3738 - a session file with no readable header still projects, ungated
+ ---
+ duration_ms: 3.007541
+ type: 'test'
+ ...
+# Subtest: plan() reports the session files a run would scan without projecting them
+ok 3739 - plan() reports the session files a run would scan without projecting them
+ ---
+ duration_ms: 1.678161
+ type: 'test'
+ ...
+# Subtest: reruns are deterministic: the same session yields byte-identical row identity
+ok 3740 - reruns are deterministic: the same session yields byte-identical row identity
+ ---
+ duration_ms: 3.69148
+ type: 'test'
+ ...
+# Subtest: a session file rotated by a reset is scanned, and projects what it projected before the rotation
+ok 3741 - a session file rotated by a reset is scanned, and projects what it projected before the rotation
+ ---
+ duration_ms: 3.821668
+ type: 'test'
+ ...
+# Subtest: a session file rotated by a delete is scanned the same way
+ok 3742 - a session file rotated by a delete is scanned the same way
+ ---
+ duration_ms: 1.770511
+ type: 'test'
+ ...
+# Subtest: a headerless rotated file takes its session id with the .jsonl extension and rotation marker removed
+ok 3743 - a headerless rotated file takes its session id with the .jsonl extension and rotation marker removed
+ ---
+ duration_ms: 1.77622
+ type: 'test'
+ ...
+# Subtest: a trajectory sibling stays distinguishable, and a non-rotation suffix is still skipped
+ok 3744 - a trajectory sibling stays distinguishable, and a non-rotation suffix is still skipped
+ ---
+ duration_ms: 1.972688
+ type: 'test'
+ ...
+# Subtest: a headerless trajectory file resolves its own id, not the session it sits beside
+ok 3745 - a headerless trajectory file resolves its own id, not the session it sits beside
+ ---
+ duration_ms: 1.936313
+ type: 'test'
+ ...
+# Subtest: sweep.cron reads the configured backfill.sweep_cron value
+ok 3746 - sweep.cron reads the configured backfill.sweep_cron value
+ ---
+ duration_ms: 0.599762
+ type: 'test'
+ ...
+# Subtest: sweep.cron falls back to the every-5-minutes default when config is absent
+ok 3747 - sweep.cron falls back to the every-5-minutes default when config is absent
+ ---
+ duration_ms: 0.473119
+ type: 'test'
+ ...
+# Subtest: a session file with mtime inside the quiesce window is excluded from the run
+ok 3748 - a session file with mtime inside the quiesce window is excluded from the run
+ ---
+ duration_ms: 2.689157
+ type: 'test'
+ ...
+# Subtest: a session file with mtime outside the quiesce window is included
+ok 3749 - a session file with mtime outside the quiesce window is included
+ ---
+ duration_ms: 2.088654
+ type: 'test'
+ ...
+# Subtest: the quiesce window defaults to exactly 180000ms when config.backfill.quiesce_ms is absent
+ok 3750 - the quiesce window defaults to exactly 180000ms when config.backfill.quiesce_ms is absent
+ ---
+ duration_ms: 2.048394
+ type: 'test'
+ ...
+# Subtest: the default quiesce window excludes a fresh file and includes one older than three minutes
+ok 3751 - the default quiesce window excludes a fresh file and includes one older than three minutes
+ ---
+ duration_ms: 2.510936
+ type: 'test'
+ ...
+# Subtest: a file outside the quiesce window still goes through the CLI-backend exclusion unchanged
+ok 3752 - a file outside the quiesce window still goes through the CLI-backend exclusion unchanged
+ ---
+ duration_ms: 7.024787
+ type: 'test'
+ ...
+# Subtest: activate() registers the openclaw client and wires attach() to the real write
+ok 3753 - activate() registers the openclaw client and wires attach() to the real write
+ ---
+ duration_ms: 9.335258
+ type: 'test'
+ ...
+# Subtest: activate() threads ctx.config into the backfill provider (sweep_cron and quiesce_ms)
+ok 3754 - activate() threads ctx.config into the backfill provider (sweep_cron and quiesce_ms)
+ ---
+ duration_ms: 1.963405
+ type: 'test'
+ ...
+# Subtest: activate() attach() reports the same write under --json
+ok 3755 - activate() attach() reports the same write under --json
+ ---
+ duration_ms: 4.698391
+ type: 'test'
+ ...
+# Subtest: activate() attach() rethrows a refusal, marked so it is not retried forever
+ok 3756 - activate() attach() rethrows a refusal, marked so it is not retried forever
+ ---
+ duration_ms: 1.778223
+ type: 'test'
+ ...
+# Subtest: activate() attach() rethrows a hard failure unmarked, so it is still retried
+ok 3757 - activate() attach() rethrows a hard failure unmarked, so it is still retried
+ ---
+ duration_ms: 1.52632
+ type: 'test'
+ ...
+# Subtest: a refused openclaw attach is a refused reconciler outcome, not done, and the join continues
+ok 3758 - a refused openclaw attach is a refused reconciler outcome, not done, and the join continues
+ ---
+ duration_ms: 1.816521
+ type: 'test'
+ ...
+# Subtest: activate() registers the settlement enricher right after the exchange projector
+ok 3759 - activate() registers the settlement enricher right after the exchange projector
+ ---
+ duration_ms: 0.27017
+ type: 'test'
+ ...
+# Subtest: hyp attach openclaw resolves the client and does not error unknown client
+ok 3760 - hyp attach openclaw resolves the client and does not error unknown client
+ ---
+ duration_ms: 7.089756
+ type: 'test'
+ ...
+# Subtest: hyp attach openclaw exits nonzero when the attach refuses
+ok 3761 - hyp attach openclaw exits nonzero when the attach refuses
+ ---
+ duration_ms: 1.534011
+ type: 'test'
+ ...
+# Subtest: hyp detach openclaw resolves the client from the real manifest as an honest no-op
+ok 3762 - hyp detach openclaw resolves the client from the real manifest as an honest no-op
+ ---
+ duration_ms: 5.101504
+ type: 'test'
+ ...
+# Subtest: hyp detach openclaw reverses a real attach: the ownership-based json_path undo fires end to end
+ok 3763 - hyp detach openclaw reverses a real attach: the ownership-based json_path undo fires end to end
+ ---
+ duration_ms: 8.652781
+ type: 'test'
+ ...
+# Subtest: hyp clients: the descriptor map behind client listing/status resolves openclaw
+ok 3764 - hyp clients: the descriptor map behind client listing/status resolves openclaw
+ ---
+ duration_ms: 3.097167
+ type: 'test'
+ ...
+# Subtest: buildAttachPluginCatalog returns the full catalog, not just clientDescriptors
+ok 3765 - buildAttachPluginCatalog returns the full catalog, not just clientDescriptors
+ ---
+ duration_ms: 5.054703
+ type: 'test'
+ ...
+# Subtest: validateOpenclawConfig accepts an empty / absent config
+ok 3766 - validateOpenclawConfig accepts an empty / absent config
+ ---
+ duration_ms: 1.409332
+ type: 'test'
+ ...
+# Subtest: validateOpenclawConfig leaves non-attach keys (e.g. proxy) untouched
+ok 3767 - validateOpenclawConfig leaves non-attach keys (e.g. proxy) untouched
+ ---
+ duration_ms: 0.183178
+ type: 'test'
+ ...
+# Subtest: validateOpenclawConfig accepts the attach policy block
+ok 3768 - validateOpenclawConfig accepts the attach policy block
+ ---
+ duration_ms: 0.176608
+ type: 'test'
+ ...
+# Subtest: validateOpenclawConfig rejects a non-object config
+ok 3769 - validateOpenclawConfig rejects a non-object config
+ ---
+ duration_ms: 0.121164
+ type: 'test'
+ ...
+# Subtest: validateOpenclawConfig rejects a malformed attach block
+ok 3770 - validateOpenclawConfig rejects a malformed attach block
+ ---
+ duration_ms: 0.173343
+ type: 'test'
+ ...
+# Subtest: validateAttachSection mounts errors at the caller-supplied pointer
+ok 3771 - validateAttachSection mounts errors at the caller-supplied pointer
+ ---
+ duration_ms: 0.071248
+ type: 'test'
+ ...
+# Subtest: the section name matches the manifest config_sections entry
+ok 3772 - the section name matches the manifest config_sections entry
+ ---
+ duration_ms: 0.094754
+ type: 'test'
+ ...
+# Subtest: validateOpenclawConfig accepts a full backfill block
+ok 3773 - validateOpenclawConfig accepts a full backfill block
+ ---
+ duration_ms: 0.143989
+ type: 'test'
+ ...
+# Subtest: validateOpenclawConfig rejects a malformed backfill block
+ok 3774 - validateOpenclawConfig rejects a malformed backfill block
+ ---
+ duration_ms: 0.383533
+ type: 'test'
+ ...
+# Subtest: validateBackfillSection mounts errors at the caller-supplied pointer
+ok 3775 - validateBackfillSection mounts errors at the caller-supplied pointer
+ ---
+ duration_ms: 0.357213
+ type: 'test'
+ ...
+# Subtest: validateOpenclawConfig accepts sweep_cron and quiesce_ms
+ok 3776 - validateOpenclawConfig accepts sweep_cron and quiesce_ms
+ ---
+ duration_ms: 0.455302
+ type: 'test'
+ ...
+# Subtest: validateOpenclawConfig rejects an invalid sweep_cron
+ok 3777 - validateOpenclawConfig rejects an invalid sweep_cron
+ ---
+ duration_ms: 0.143979
+ type: 'test'
+ ...
+# Subtest: validateOpenclawConfig rejects a negative quiesce_ms
+ok 3778 - validateOpenclawConfig rejects a negative quiesce_ms
+ ---
+ duration_ms: 0.093883
+ type: 'test'
+ ...
+# Subtest: validateOpenclawConfig rejects a non-integer quiesce_ms
+ok 3779 - validateOpenclawConfig rejects a non-integer quiesce_ms
+ ---
+ duration_ms: 0.119611
+ type: 'test'
+ ...
+# Subtest: validateOpenclawConfig still rejects a genuinely unknown backfill key
+ok 3780 - validateOpenclawConfig still rejects a genuinely unknown backfill key
+ ---
+ duration_ms: 0.107724
+ type: 'test'
+ ...
+# Subtest: openclaw manifest loads and validates
+ok 3781 - openclaw manifest loads and validates
+ ---
+ duration_ms: 5.066861
+ type: 'test'
+ ...
+# Subtest: openclaw contributes.client.attach_probe parses to the exact json_path shape
+ok 3782 - openclaw contributes.client.attach_probe parses to the exact json_path shape
+ ---
+ duration_ms: 1.414921
+ type: 'test'
+ ...
+# Subtest: openclaw description and picker summary no longer reference the steering plugin
+ok 3783 - openclaw description and picker summary no longer reference the steering plugin
+ ---
+ duration_ms: 1.742588
+ type: 'test'
+ ...
+# Subtest: openclaw description and picker summary state the two capture tiers directly
+ok 3784 - openclaw description and picker summary state the two capture tiers directly
+ ---
+ duration_ms: 1.50021
+ type: 'test'
+ ...
+# Subtest: claude manifest onboarding copy names the claude-cli OpenClaw case
+ok 3785 - claude manifest onboarding copy names the claude-cli OpenClaw case
+ ---
+ duration_ms: 0.979879
+ type: 'test'
+ ...
+# Subtest: canonicalMatchKey is a pure function of role and tuples
+ok 3786 - canonicalMatchKey is a pure function of role and tuples
+ ---
+ duration_ms: 1.559671
+ type: 'test'
+ ...
+# Subtest: wireMatchKey: same text content on the same role matches regardless of key order
+ok 3787 - wireMatchKey: same text content on the same role matches regardless of key order
+ ---
+ duration_ms: 0.54616
+ type: 'test'
+ ...
+# Subtest: wireMatchKey: a bare string content and its one-block-array equivalent match
+ok 3788 - wireMatchKey: a bare string content and its one-block-array equivalent match
+ ---
+ duration_ms: 0.149497
+ type: 'test'
+ ...
+# Subtest: wireMatchKey: empty string content and empty array content both yield the empty-block key
+ok 3789 - wireMatchKey: empty string content and empty array content both yield the empty-block key
+ ---
+ duration_ms: 0.121635
+ type: 'test'
+ ...
+# Subtest: wireMatchKey: an OpenClaw wire timestamp prefix matches the bare session text
+ok 3790 - wireMatchKey: an OpenClaw wire timestamp prefix matches the bare session text
+ ---
+ duration_ms: 0.256549
+ type: 'test'
+ ...
+# Subtest: wireMatchKey: the timestamp strip tolerates seconds and short zone names
+ok 3791 - wireMatchKey: the timestamp strip tolerates seconds and short zone names
+ ---
+ duration_ms: 0.141656
+ type: 'test'
+ ...
+# Subtest: wireMatchKey: the timestamp strip accepts GMT-offset short zone names
+ok 3792 - wireMatchKey: the timestamp strip accepts GMT-offset short zone names
+ ---
+ duration_ms: 0.292595
+ type: 'test'
+ ...
+# Subtest: wireMatchKey: a stamp on both sides still matches (strip is symmetric)
+ok 3793 - wireMatchKey: a stamp on both sides still matches (strip is symmetric)
+ ---
+ duration_ms: 0.120153
+ type: 'test'
+ ...
+# Subtest: wireMatchKey: near-miss brackets are not stripped
+ok 3794 - wireMatchKey: near-miss brackets are not stripped
+ ---
+ duration_ms: 0.36846
+ type: 'test'
+ ...
+# Subtest: wireMatchKey: only the leading stamp strips, not one mid-text
+ok 3795 - wireMatchKey: only the leading stamp strips, not one mid-text
+ ---
+ duration_ms: 0.292805
+ type: 'test'
+ ...
+# Subtest: wireMatchKey strips volatile block fields before hashing
+ok 3796 - wireMatchKey strips volatile block fields before hashing
+ ---
+ duration_ms: 0.15839
+ type: 'test'
+ ...
+# Subtest: wireMatchKey: tool_use identity is content-based, not id-based
+ok 3797 - wireMatchKey: tool_use identity is content-based, not id-based
+ ---
+ duration_ms: 0.107093
+ type: 'test'
+ ...
+# Subtest: wireMatchKey: tool_result identity depends only on content
+ok 3798 - wireMatchKey: tool_result identity depends only on content
+ ---
+ duration_ms: 0.120023
+ type: 'test'
+ ...
+# Subtest: sessionMatchKey: toolCall session block matches an equivalent wire tool_use block
+ok 3799 - sessionMatchKey: toolCall session block matches an equivalent wire tool_use block
+ ---
+ duration_ms: 0.113782
+ type: 'test'
+ ...
+# Subtest: sessionMatchKey: toolUse and function_call synonyms fold onto the same tool_use identity
+ok 3800 - sessionMatchKey: toolUse and function_call synonyms fold onto the same tool_use identity
+ ---
+ duration_ms: 0.130007
+ type: 'test'
+ ...
+# Subtest: sessionMatchKey: redacted_thinking folds onto the thinking kind
+ok 3801 - sessionMatchKey: redacted_thinking folds onto the thinking kind
+ ---
+ duration_ms: 0.110598
+ type: 'test'
+ ...
+# Subtest: sessionMatchKey: a standalone toolResult record matches the wire tool_result-in-user shape
+ok 3802 - sessionMatchKey: a standalone toolResult record matches the wire tool_result-in-user shape
+ ---
+ duration_ms: 0.115947
+ type: 'test'
+ ...
+# Subtest: sessionMatchKey: toolResult content array and equivalent string content agree with the wire side
+ok 3803 - sessionMatchKey: toolResult content array and equivalent string content agree with the wire side
+ ---
+ duration_ms: 0.882131
+ type: 'test'
+ ...
+# Subtest: sessionMatchKey: toolResult identity ignores toolCallId, mirroring wireMatchKey ignoring tool_use_id
+ok 3804 - sessionMatchKey: toolResult identity ignores toolCallId, mirroring wireMatchKey ignoring tool_use_id
+ ---
+ duration_ms: 0.102376
+ type: 'test'
+ ...
+# Subtest: sessionMatchKey: an OpenAI-shaped role: "tool" record is an accepted residue, not normalized
+ok 3805 - sessionMatchKey: an OpenAI-shaped role: "tool" record is an accepted residue, not normalized
+ ---
+ duration_ms: 0.120443
+ type: 'test'
+ ...
+# Subtest: ordinalFallbackKey combines role and ordinal into one readable string
+ok 3806 - ordinalFallbackKey combines role and ordinal into one readable string
+ ---
+ duration_ms: 0.077979
+ type: 'test'
+ ...
+# Subtest: withRoleOrdinals assigns 1-based ordinals per role in given order
+ok 3807 - withRoleOrdinals assigns 1-based ordinals per role in given order
+ ---
+ duration_ms: 1.470104
+ type: 'test'
+ ...
+# Subtest: matchOrdinalFallback finds the closest-in-time candidate within the window
+ok 3808 - matchOrdinalFallback finds the closest-in-time candidate within the window
+ ---
+ duration_ms: 0.201296
+ type: 'test'
+ ...
+# Subtest: matchOrdinalFallback returns undefined outside the window bound
+ok 3809 - matchOrdinalFallback returns undefined outside the window bound
+ ---
+ duration_ms: 0.143107
+ type: 'test'
+ ...
+# Subtest: matchOrdinalFallback returns undefined for an unknown role/ordinal position
+ok 3810 - matchOrdinalFallback returns undefined for an unknown role/ordinal position
+ ---
+ duration_ms: 0.091499
+ type: 'test'
+ ...
+# Subtest: matchOrdinalFallback picks the nearest candidate when several share a position across replays
+ok 3811 - matchOrdinalFallback picks the nearest candidate when several share a position across replays
+ ---
+ duration_ms: 0.095294
+ type: 'test'
+ ...
+# Subtest: shape detection: path, request body, and response body each suffice
+ok 3812 - shape detection: path, request body, and response body each suffice
+ ---
+ duration_ms: 1.029164
+ type: 'test'
+ ...
+# Subtest: a full Responses exchange projects request turns plus the assistant
+ok 3813 - a full Responses exchange projects request turns plus the assistant
+ ---
+ duration_ms: 3.396092
+ type: 'test'
+ ...
+# Subtest: a bare-string input is one user turn (the shorthand form)
+ok 3814 - a bare-string input is one user turn (the shorthand form)
+ ---
+ duration_ms: 0.330762
+ type: 'test'
+ ...
+# Subtest: function_call and function_call_output items map to tool_use / tool_result turns
+ok 3815 - function_call and function_call_output items map to tool_use / tool_result turns
+ ---
+ duration_ms: 0.24366
+ type: 'test'
+ ...
+# Subtest: request-side reasoning replay items project nothing
+ok 3816 - request-side reasoning replay items project nothing
+ ---
+ duration_ms: 0.145351
+ type: 'test'
+ ...
+# Subtest: instructions and the leading system items fold into system_text; mid-run ones stay turns
+ok 3817 - instructions and the leading system items fold into system_text; mid-run ones stay turns
+ ---
+ duration_ms: 0.266936
+ type: 'test'
+ ...
+# Subtest: a streamed exchange reconstructs from the terminal response.completed event
+ok 3818 - a streamed exchange reconstructs from the terminal response.completed event
+ ---
+ duration_ms: 0.444215
+ type: 'test'
+ ...
+# Subtest: a stream cut before its terminal event degrades to finished items, marked error
+ok 3819 - a stream cut before its terminal event degrades to finished items, marked error
+ ---
+ duration_ms: 0.340227
+ type: 'test'
+ ...
+# Subtest: a stream cut with zero finished items emits no assistant row
+ok 3820 - a stream cut with zero finished items emits no assistant row
+ ---
+ duration_ms: 1.609116
+ type: 'test'
+ ...
+# Subtest: an incomplete response records its incomplete reason as stop_reason
+ok 3821 - an incomplete response records its incomplete reason as stop_reason
+ ---
+ duration_ms: 0.431926
+ type: 'test'
+ ...
+# Subtest: Responses tool-call turns match the session file toolCall shape
+ok 3822 - Responses tool-call turns match the session file toolCall shape
+ ---
+ duration_ms: 0.332466
+ type: 'test'
+ ...
+# Subtest: a projected Responses text turn matches its bare session-file text
+ok 3823 - a projected Responses text turn matches its bare session-file text
+ ---
+ duration_ms: 0.141345
+ type: 'test'
+ ...
+# Subtest: project() falls back to the anthropic shape and provider when the upstream header is absent
+ok 3824 - project() falls back to the anthropic shape and provider when the upstream header is absent
+ ---
+ duration_ms: 2.521873
+ type: 'test'
+ ...
+# Subtest: project() reads the anthropic shape when the upstream header names anthropic
+ok 3825 - project() reads the anthropic shape when the upstream header names anthropic
+ ---
+ duration_ms: 0.429833
+ type: 'test'
+ ...
+# Subtest: project() records an unrecognized upstream verbatim and keeps the anthropic parse
+ok 3826 - project() records an unrecognized upstream verbatim and keeps the anthropic parse
+ ---
+ duration_ms: 0.325475
+ type: 'test'
+ ...
+# Subtest: project() maps a non-streamed OpenAI Chat Completions exchange
+ok 3827 - project() maps a non-streamed OpenAI Chat Completions exchange
+ ---
+ duration_ms: 0.747726
+ type: 'test'
+ ...
+# Subtest: project() keys the OpenAI session on the system-prompt head, like the anthropic shape
+ok 3828 - project() keys the OpenAI session on the system-prompt head, like the anthropic shape
+ ---
+ duration_ms: 0.794178
+ type: 'test'
+ ...
+# Subtest: project() normalizes OpenAI tool calls and tool results into shared blocks
+ok 3829 - project() normalizes OpenAI tool calls and tool results into shared blocks
+ ---
+ duration_ms: 0.389552
+ type: 'test'
+ ...
+# Subtest: project() assembles a streamed OpenAI assistant message from SSE chunks
+ok 3830 - project() assembles a streamed OpenAI assistant message from SSE chunks
+ ---
+ duration_ms: 0.76358
+ type: 'test'
+ ...
+# Subtest: project() accumulates a streamed OpenAI tool call across chunks
+ok 3831 - project() accumulates a streamed OpenAI tool call across chunks
+ ---
+ duration_ms: 0.327538
+ type: 'test'
+ ...
+# Subtest: project() preserves an argument-less streamed OpenAI tool call as empty input
+ok 3832 - project() preserves an argument-less streamed OpenAI tool call as empty input
+ ---
+ duration_ms: 0.710419
+ type: 'test'
+ ...
+# Subtest: project() marks a truncated OpenAI stream stop_reason=error
+ok 3833 - project() marks a truncated OpenAI stream stop_reason=error
+ ---
+ duration_ms: 0.474561
+ type: 'test'
+ ...
+# Subtest: project() emits no assistant row for a cut OpenAI stream that carried no content
+ok 3834 - project() emits no assistant row for a cut OpenAI stream that carried no content
+ ---
+ duration_ms: 0.316541
+ type: 'test'
+ ...
+# Subtest: project() emits no assistant row for a cut Anthropic stream that carried no content
+ok 3835 - project() emits no assistant row for a cut Anthropic stream that carried no content
+ ---
+ duration_ms: 0.399186
+ type: 'test'
+ ...
+# Subtest: project() still records a terminal Anthropic stream whose content is genuinely empty
+ok 3836 - project() still records a terminal Anthropic stream whose content is genuinely empty
+ ---
+ duration_ms: 0.277592
+ type: 'test'
+ ...
+# Subtest: project() still records a terminal OpenAI stream whose content is genuinely empty
+ok 3837 - project() still records a terminal OpenAI stream whose content is genuinely empty
+ ---
+ duration_ms: 0.201005
+ type: 'test'
+ ...
+# Subtest: project() still records a non-streamed OpenAI response whose content is empty
+ok 3838 - project() still records a non-streamed OpenAI response whose content is empty
+ ---
+ duration_ms: 1.196288
+ type: 'test'
+ ...
+# Subtest: project() still records a terminal response whose wire stop reason is literally error
+ok 3839 - project() still records a terminal response whose wire stop reason is literally error
+ ---
+ duration_ms: 0.48052
+ type: 'test'
+ ...
+# Subtest: project() never drops a request-history assistant turn that replays stop_reason error
+ok 3840 - project() never drops a request-history assistant turn that replays stop_reason error
+ ---
+ duration_ms: 0.222488
+ type: 'test'
+ ...
+# Subtest: the cut-stream marker is only ever stamped through markCutStream
+ok 3841 - the cut-stream marker is only ever stamped through markCutStream
+ ---
+ duration_ms: 0.379988
+ type: 'test'
+ ...
+# Subtest: project() stamps openclaw.match_key on every row of both shapes
+ok 3842 - project() stamps openclaw.match_key on every row of both shapes
+ ---
+ duration_ms: 0.421521
+ type: 'test'
+ ...
+# Subtest: an OpenAI-captured tool call carries the same match key as its session-file record
+ok 3843 - an OpenAI-captured tool call carries the same match key as its session-file record
+ ---
+ duration_ms: 0.301278
+ type: 'test'
+ ...
+# Subtest: match() is true iff the x-hypaware-client header says openclaw
+ok 3844 - match() is true iff the x-hypaware-client header says openclaw
+ ---
+ duration_ms: 0.909783
+ type: 'test'
+ ...
+# Subtest: openclaw projector priority is above the claude projector
+ok 3845 - openclaw projector priority is above the claude projector
+ ---
+ duration_ms: 0.273906
+ type: 'test'
+ ...
+# Subtest: project() maps a JSON exchange: model, usage, stop_reason, client identity
+ok 3846 - project() maps a JSON exchange: model, usage, stop_reason, client identity
+ ---
+ duration_ms: 1.279875
+ type: 'test'
+ ...
+# Subtest: project() derives a stable session_id from the system-prompt head
+ok 3847 - project() derives a stable session_id from the system-prompt head
+ ---
+ duration_ms: 0.627053
+ type: 'test'
+ ...
+# Subtest: project() assembles a streamed assistant message from SSE events
+ok 3848 - project() assembles a streamed assistant message from SSE events
+ ---
+ duration_ms: 0.552059
+ type: 'test'
+ ...
+# Subtest: project() preserves an empty-input streamed tool_use call
+ok 3849 - project() preserves an empty-input streamed tool_use call
+ ---
+ duration_ms: 0.3022
+ type: 'test'
+ ...
+# Subtest: project() parses a non-empty-input streamed tool_use call
+ok 3850 - project() parses a non-empty-input streamed tool_use call
+ ---
+ duration_ms: 0.277662
+ type: 'test'
+ ...
+# Subtest: openclawSessionId falls back to the exchange id for a content-less first message
+ok 3851 - openclawSessionId falls back to the exchange id for a content-less first message
+ ---
+ duration_ms: 0.091359
+ type: 'test'
+ ...
+# Subtest: openclawSessionId keys on the first input item for an instruction-less Responses request
+ok 3852 - openclawSessionId keys on the first input item for an instruction-less Responses request
+ ---
+ duration_ms: 0.236329
+ type: 'test'
+ ...
+# Subtest: project() does not drop an exchange whose first message has no content
+ok 3853 - project() does not drop an exchange whose first message has no content
+ ---
+ duration_ms: 0.324223
+ type: 'test'
+ ...
+# Subtest: project() declines an unparseable request body
+ok 3854 - project() declines an unparseable request body
+ ---
+ duration_ms: 0.149227
+ type: 'test'
+ ...
+# Subtest: the anthropic upstream preset is equivalent to the claude plugin preset
+ok 3855 - the anthropic upstream preset is equivalent to the claude plugin preset
+ ---
+ duration_ms: 0.319325
+ type: 'test'
+ ...
+# Subtest: a configured anthropic keeps /v1/messages even with the openclaw openai preset registered
+ok 3856 - a configured anthropic keeps /v1/messages even with the openclaw openai preset registered
+ ---
+ duration_ms: 0.395932
+ type: 'test'
+ ...
+# Subtest: a steered /chat/completions reaches the openai upstream in every config shape
+ok 3857 - a steered /chat/completions reaches the openai upstream in every config shape
+ ---
+ duration_ms: 0.426548
+ type: 'test'
+ ...
+# Subtest: a configured openai upstream replaces the preset, steering rung included
+ok 3858 - a configured openai upstream replaces the preset, steering rung included
+ ---
+ duration_ms: 0.14497
+ type: 'test'
+ ...
+# Subtest: unsteered Claude and Codex traffic routes unchanged through the compiled table
+ok 3859 - unsteered Claude and Codex traffic routes unchanged through the compiled table
+ ---
+ duration_ms: 0.169918
+ type: 'test'
+ ...
+# Subtest: x-hypaware-upstream names a preset provider: match() is unconditional
+ok 3860 - x-hypaware-upstream names a preset provider: match() is unconditional
+ ---
+ duration_ms: 0.126883
+ type: 'test'
+ ...
+# Subtest: regression: unsteered Claude/Codex traffic routes unchanged with both openclaw presets registered
+ok 3861 - regression: unsteered Claude/Codex traffic routes unchanged with both openclaw presets registered
+ ---
+ duration_ms: 0.140434
+ type: 'test'
+ ...
+# Subtest: the manifest declares both required upstreams
+ok 3862 - the manifest declares both required upstreams
+ ---
+ duration_ms: 0.318814
+ type: 'test'
+ ...
+# Subtest: a non-session record type is not read as the header, even carrying id/cwd
+ok 3863 - a non-session record type is not read as the header, even carrying id/cwd
+ ---
+ duration_ms: 1.061103
+ type: 'test'
+ ...
+# Subtest: a missing, blank, or non-string type is not "session" either
+ok 3864 - a missing, blank, or non-string type is not "session" either
+ ---
+ duration_ms: 0.215497
+ type: 'test'
+ ...
+# Subtest: a line that is not JSON, or JSON that is not an object, resolves nothing
+ok 3865 - a line that is not JSON, or JSON that is not an object, resolves nothing
+ ---
+ duration_ms: 0.142637
+ type: 'test'
+ ...
+# Subtest: a blank or non-string field is absent, never a substitute value
+ok 3866 - a blank or non-string field is absent, never a substitute value
+ ---
+ duration_ms: 0.334508
+ type: 'test'
+ ...
+# Subtest: a session header with no other fields resolves no field
+ok 3867 - a session header with no other fields resolves no field
+ ---
+ duration_ms: 0.613643
+ type: 'test'
+ ...
+# Subtest: a field that survives the blank test is returned byte-identical
+ok 3868 - a field that survives the blank test is returned byte-identical
+ ---
+ duration_ms: 0.1313
+ type: 'test'
+ ...
+# Subtest: a relative cwd is no cwd, not a path resolved against the daemon
+ok 3869 - a relative cwd is no cwd, not a path resolved against the daemon
+ ---
+ duration_ms: 0.218271
+ type: 'test'
+ ...
+# Subtest: an absolute cwd resolves
+ok 3870 - an absolute cwd resolves
+ ---
+ duration_ms: 0.106652
+ type: 'test'
+ ...
+# Subtest: openclawSessionCwd is the one cwd predicate, usable by a caller that reads its own record
+ok 3871 - openclawSessionCwd is the one cwd predicate, usable by a caller that reads its own record
+ ---
+ duration_ms: 0.283862
+ type: 'test'
+ ...
+# Subtest: readOpenclawSessionHeader reads the first line and ignores the rest of the file
+ok 3872 - readOpenclawSessionHeader reads the first line and ignores the rest of the file
+ ---
+ duration_ms: 0.76322
+ type: 'test'
+ ...
+# Subtest: a first line longer than the read bound resolves nothing rather than half a line
+ok 3873 - a first line longer than the read bound resolves nothing rather than half a line
+ ---
+ duration_ms: 0.728337
+ type: 'test'
+ ...
+# Subtest: an unreadable, empty, or absent session file resolves nothing
+ok 3874 - an unreadable, empty, or absent session file resolves nothing
+ ---
+ duration_ms: 0.479879
+ type: 'test'
+ ...
+# Subtest: a session file with no trailing newline is still one whole first line
+ok 3875 - a session file with no trailing newline is still one whole first line
+ ---
+ duration_ms: 0.291784
+ type: 'test'
+ ...
+# Subtest: readOpenclawSessionMessages returns only type:"message" records, in file order
+ok 3876 - readOpenclawSessionMessages returns only type:"message" records, in file order
+ ---
+ duration_ms: 9.911985
+ type: 'test'
+ ...
+# Subtest: a message field the nested envelope states is never read off the record line
+ok 3877 - a message field the nested envelope states is never read off the record line
+ ---
+ duration_ms: 1.148465
+ type: 'test'
+ ...
+# Subtest: a record with no nested envelope reads its fields off the record line
+ok 3878 - a record with no nested envelope reads its fields off the record line
+ ---
+ duration_ms: 1.795609
+ type: 'test'
+ ...
+# Subtest: the record line supplies the timestamp when the nested envelope states none
+ok 3879 - the record line supplies the timestamp when the nested envelope states none
+ ---
+ duration_ms: 3.761386
+ type: 'test'
+ ...
+# Subtest: the envelope wins for timestamp, the record line wins for id
+ok 3880 - the envelope wins for timestamp, the record line wins for id
+ ---
+ duration_ms: 1.027331
+ type: 'test'
+ ...
+# Subtest: a record that states its id only in the envelope still resolves an identity
+ok 3881 - a record that states its id only in the envelope still resolves an identity
+ ---
+ duration_ms: 3.531427
+ type: 'test'
+ ...
+# Subtest: a blank or wrong-typed nested field reads as absent, so the record line still supplies it
+ok 3882 - a blank or wrong-typed nested field reads as absent, so the record line still supplies it
+ ---
+ duration_ms: 1.000481
+ type: 'test'
+ ...
+# Subtest: a blank nested field with nothing on the record line is absent, not a substitute
+ok 3883 - a blank nested field with nothing on the record line is absent, not a substitute
+ ---
+ duration_ms: 0.734517
+ type: 'test'
+ ...
+# Subtest: a nulled-out nested content is unstated, so the record line still supplies it
+ok 3884 - a nulled-out nested content is unstated, so the record line still supplies it
+ ---
+ duration_ms: 0.655887
+ type: 'test'
+ ...
+# Subtest: a `message` key that is not an object leaves the record line as the only address
+ok 3885 - a `message` key that is not an object leaves the record line as the only address
+ ---
+ duration_ms: 0.638431
+ type: 'test'
+ ...
+# Subtest: readOpenclawSessionMessages skips blank and unparseable lines without aborting the rest
+ok 3886 - readOpenclawSessionMessages skips blank and unparseable lines without aborting the rest
+ ---
+ duration_ms: 1.448251
+ type: 'test'
+ ...
+# Subtest: readOpenclawSessionMessages treats a blank id/timestamp/model as absent, not a substitute
+ok 3887 - readOpenclawSessionMessages treats a blank id/timestamp/model as absent, not a substitute
+ ---
+ duration_ms: 1.971066
+ type: 'test'
+ ...
+# Subtest: readOpenclawSessionMessages resolves to an empty list for a missing or empty file
+ok 3888 - readOpenclawSessionMessages resolves to an empty list for a missing or empty file
+ ---
+ duration_ms: 1.216308
+ type: 'test'
+ ...
+# Subtest: listOpenclawSessionFiles scans a rotated session file the same way it scans a live one
+ok 3889 - listOpenclawSessionFiles scans a rotated session file the same way it scans a live one
+ ---
+ duration_ms: 2.474582
+ type: 'test'
+ ...
+# Subtest: the enricher registers under the LLP 0161 name and client
+ok 3890 - the enricher registers under the LLP 0161 name and client
+ ---
+ duration_ms: 1.732152
+ type: 'test'
+ ...
+# Subtest: every row of a realistic, fully-written session settles by content match (rate 1.00)
+ok 3891 - every row of a realistic, fully-written session settles by content match (rate 1.00)
+ ---
+ duration_ms: 10.903944
+ type: 'test'
+ ...
+# Subtest: a match_key stored as a JSON attributes string settles the same way
+ok 3892 - a match_key stored as a JSON attributes string settles the same way
+ ---
+ duration_ms: 1.647003
+ type: 'test'
+ ...
+# Subtest: a timestamp-prefixed wire user turn settles onto its bare session record
+ok 3893 - a timestamp-prefixed wire user turn settles onto its bare session record
+ ---
+ duration_ms: 2.293466
+ type: 'test'
+ ...
+# Subtest: an ignore-classed session cwd drops every row of that session
+ok 3894 - an ignore-classed session cwd drops every row of that session
+ ---
+ duration_ms: 2.879426
+ type: 'test'
+ ...
+# Subtest: a row that matches nothing still drops when its session cwd is ignored
+ok 3895 - a row that matches nothing still drops when its session cwd is ignored
+ ---
+ duration_ms: 2.139342
+ type: 'test'
+ ...
+# Subtest: a content miss settles through the ordinal/time fallback when position, role and time align
+ok 3896 - a content miss settles through the ordinal/time fallback when position, role and time align
+ ---
+ duration_ms: 1.990967
+ type: 'test'
+ ...
+# Subtest: the ordinal fallback declines outside its five-minute window
+ok 3897 - the ordinal fallback declines outside its five-minute window
+ ---
+ duration_ms: 1.631961
+ type: 'test'
+ ...
+# Subtest: the ordinal fallback declines when the file records a different role at that position
+ok 3898 - the ordinal fallback declines when the file records a different role at that position
+ ---
+ duration_ms: 1.909241
+ type: 'test'
+ ...
+# Subtest: an ambiguous content key declines to upgrade rather than duplicating a native id
+ok 3899 - an ambiguous content key declines to upgrade rather than duplicating a native id
+ ---
+ duration_ms: 2.244501
+ type: 'test'
+ ...
+# Subtest: rows no session file claims are neither upgraded nor dropped
+ok 3900 - rows no session file claims are neither upgraded nor dropped
+ ---
+ duration_ms: 1.824383
+ type: 'test'
+ ...
+# Subtest: two concurrent sessions each bind to their own file
+ok 3901 - two concurrent sessions each bind to their own file
+ ---
+ duration_ms: 5.302299
+ type: 'test'
+ ...
+# Subtest: a missing agents root settles nothing and throws nothing
+ok 3902 - a missing agents root settles nothing and throws nothing
+ ---
+ duration_ms: 0.651981
+ type: 'test'
+ ...
+# Subtest: an unparseable session file settles nothing and throws nothing
+ok 3903 - an unparseable session file settles nothing and throws nothing
+ ---
+ duration_ms: 1.208126
+ type: 'test'
+ ...
+# Subtest: a session header with no usable cwd upgrades identity without gating
+ok 3904 - a session header with no usable cwd upgrades identity without gating
+ ---
+ duration_ms: 1.930294
+ type: 'test'
+ ...
+# Subtest: an already-populated row cwd is never overwritten by the header
+ok 3905 - an already-populated row cwd is never overwritten by the header
+ ---
+ duration_ms: 1.384605
+ type: 'test'
+ ...
+# Subtest: an empty batch is returned untouched
+ok 3906 - an empty batch is returned untouched
+ ---
+ duration_ms: 0.38833
+ type: 'test'
+ ...
+# Subtest: claude picker summary discloses the attach and the skill install
+ok 3907 - claude picker summary discloses the attach and the skill install
+ ---
+ duration_ms: 6.866336
+ type: 'test'
+ ...
+# Subtest: claude picker summary discloses that a local gateway listener is started
+ok 3908 - claude picker summary discloses that a local gateway listener is started
+ ---
+ duration_ms: 1.073612
+ type: 'test'
+ ...
+# Subtest: codex picker summary discloses the gateway config write and the skill install
+ok 3909 - codex picker summary discloses the gateway config write and the skill install
+ ---
+ duration_ms: 0.887189
+ type: 'test'
+ ...
+# Subtest: otel picker summary discloses that a local receiver is started
+ok 3910 - otel picker summary discloses that a local receiver is started
+ ---
+ duration_ms: 0.63168
+ type: 'test'
+ ...
+# Subtest: openclaw picker summary discloses the gateway-config rewrite
+ok 3911 - openclaw picker summary discloses the gateway-config rewrite
+ ---
+ duration_ms: 1.286395
+ type: 'test'
+ ...
+# Subtest: openclaw picker summary names the manual gateway restart attach requires
+ok 3912 - openclaw picker summary names the manual gateway restart attach requires
+ ---
+ duration_ms: 0.889752
+ type: 'test'
+ ...
+# Subtest: raw-anthropic picker summary discloses that a local gateway listener is started
+ok 3913 - raw-anthropic picker summary discloses that a local gateway listener is started
+ ---
+ duration_ms: 0.820017
+ type: 'test'
+ ...
+# Subtest: raw-openai picker summary discloses that a local gateway listener is started
+ok 3914 - raw-openai picker summary discloses that a local gateway listener is started
+ ---
+ duration_ms: 1.478808
+ type: 'test'
+ ...
+# Subtest: claude-desktop picker summary keeps the asks-before-changing reassurance
+ok 3915 - claude-desktop picker summary keeps the asks-before-changing reassurance
+ ---
+ duration_ms: 1.352746
+ type: 'test'
+ ...
+# Subtest: claude-desktop picker summary discloses that a local gateway listener is started
+ok 3916 - claude-desktop picker summary discloses that a local gateway listener is started
+ ---
+ duration_ms: 1.09252
+ type: 'test'
+ ...
+# Subtest: clean source plugin reports no issues
+ok 3917 - clean source plugin reports no issues
+ ---
+ duration_ms: 12.184028
+ type: 'test'
+ ...
+# Subtest: missing activate export is flagged
+ok 3918 - missing activate export is flagged
+ ---
+ duration_ms: 3.136498
+ type: 'test'
+ ...
+# Subtest: declared-but-unregistered contribution is the headline error
+ok 3919 - declared-but-unregistered contribution is the headline error
+ ---
+ duration_ms: 3.056776
+ type: 'test'
+ ...
+# Subtest: activate that throws is reported as activate_threw
+ok 3920 - activate that throws is reported as activate_threw
+ ---
+ duration_ms: 2.915671
+ type: 'test'
+ ...
+# Subtest: unresolved required capability is an error
+ok 3921 - unresolved required capability is an error
+ ---
+ duration_ms: 3.228467
+ type: 'test'
+ ...
+# Subtest: required capability resolves when a provider is known
+ok 3922 - required capability resolves when a provider is known
+ ---
+ duration_ms: 3.142586
+ type: 'test'
+ ...
+# Subtest: required capability with a known name but unsatisfied range is unresolved
+ok 3923 - required capability with a known name but unsatisfied range is unresolved
+ ---
+ duration_ms: 4.134625
+ type: 'test'
+ ...
+# Subtest: requireCapability and using its handle during activate does not false-fail
+ok 3924 - requireCapability and using its handle during activate does not false-fail
+ ---
+ duration_ms: 9.994652
+ type: 'test'
+ ...
+# Subtest: malformed contributes entry is flagged as contributes_malformed
+ok 3925 - malformed contributes entry is flagged as contributes_malformed
+ ---
+ duration_ms: 8.286715
+ type: 'test'
+ ...
+# Subtest: malformed config_sections entry is flagged as contributes_malformed
+ok 3926 - malformed config_sections entry is flagged as contributes_malformed
+ ---
+ duration_ms: 3.814318
+ type: 'test'
+ ...
+# Subtest: declared-but-never-provided capability is a warning
+ok 3927 - declared-but-never-provided capability is a warning
+ ---
+ duration_ms: 3.153663
+ type: 'test'
+ ...
+# Subtest: a syntax error in the entrypoint surfaces as entrypoint_import_failed
+ok 3928 - a syntax error in the entrypoint surfaces as entrypoint_import_failed
+ ---
+ duration_ms: 3.014201
+ type: 'test'
+ ...
+# Subtest: invalid semver and missing entrypoint are caught statically
+ok 3929 - invalid semver and missing entrypoint are caught statically
+ ---
+ duration_ms: 3.784101
+ type: 'test'
+ ...
+# Subtest: an invalid manifest short-circuits with manifest_invalid
+ok 3930 - an invalid manifest short-circuits with manifest_invalid
+ ---
+ duration_ms: 1.149086
+ type: 'test'
+ ...
+# Subtest: registered-but-undeclared contribution is a warning, not an error
+ok 3931 - registered-but-undeclared contribution is a warning, not an error
+ ---
+ duration_ms: 3.043817
+ type: 'test'
+ ...
+# Subtest: slugFromName strips scope and sanitizes
+ok 3932 - slugFromName strips scope and sanitizes
+ ---
+ duration_ms: 0.961201
+ type: 'test'
+ ...
+# Subtest: scaffold (source) writes files and passes doctor with zero errors
+ok 3933 - scaffold (source) writes files and passes doctor with zero errors
+ ---
+ duration_ms: 12.666261
+ type: 'test'
+ ...
+# Subtest: scaffold (sink) writes files and passes doctor with zero errors
+ok 3934 - scaffold (sink) writes files and passes doctor with zero errors
+ ---
+ duration_ms: 5.279314
+ type: 'test'
+ ...
+# Subtest: scaffold (dataset) writes files and passes doctor with zero errors
+ok 3935 - scaffold (dataset) writes files and passes doctor with zero errors
+ ---
+ duration_ms: 5.521172
+ type: 'test'
+ ...
+# Subtest: scaffold refuses to clobber an existing directory
+ok 3936 - scaffold refuses to clobber an existing directory
+ ---
+ duration_ms: 2.071659
+ type: 'test'
+ ...
+# Subtest: scaffold rejects an unknown kind
+ok 3937 - scaffold rejects an unknown kind
+ ---
+ duration_ms: 0.607764
+ type: 'test'
+ ...
+# Subtest: every client copy of a content-reading skill states that recorded content is data, not instructions
+ok 3938 - every client copy of a content-reading skill states that recorded content is data, not instructions
+ ---
+ duration_ms: 5.894889
+ type: 'test'
+ ...
+# Subtest: a boundary section separates captured content from the changes its skill may propose
+ok 3939 - a boundary section separates captured content from the changes its skill may propose
+ ---
+ duration_ms: 3.53915
+ type: 'test'
+ ...
+# Subtest: hypaware-query restates the boundary in its Guardrails list
+ok 3940 - hypaware-query restates the boundary in its Guardrails list
+ ---
+ duration_ms: 0.883343
+ type: 'test'
+ ...
+# Subtest: a content boundary does not drift between the Claude and Codex copies
+ok 3941 - a content boundary does not drift between the Claude and Codex copies
+ ---
+ duration_ms: 0.970645
+ type: 'test'
+ ...
+# Subtest: hypaware-query frontmatter is identical across both client copies
+ok 3942 - hypaware-query frontmatter is identical across both client copies
+ ---
+ duration_ms: 2.365255
+ type: 'test'
+ ...
+# Subtest: hypaware-query description names the natural session-search intents
+ok 3943 - hypaware-query description names the natural session-search intents
+ ---
+ duration_ms: 0.53303
+ type: 'test'
+ ...
+# Subtest: hypaware-query description keeps its original product vocabulary
+ok 3944 - hypaware-query description keeps its original product vocabulary
+ ---
+ duration_ms: 0.372456
+ type: 'test'
+ ...
+# Subtest: hypaware-query description stays a single-line scalar within budget
+ok 3945 - hypaware-query description stays a single-line scalar within budget
+ ---
+ duration_ms: 0.32254
+ type: 'test'
+ ...
+# Subtest: s3 BlobStore puts and gets a round-trip object honouring prefix
+ok 3946 - s3 BlobStore puts and gets a round-trip object honouring prefix
+ ---
+ duration_ms: 3.366967
+ type: 'test'
+ ...
+# Subtest: s3 BlobStore getObject returns null when AWS reports NotFound
+ok 3947 - s3 BlobStore getObject returns null when AWS reports NotFound
+ ---
+ duration_ms: 0.19722
+ type: 'test'
+ ...
+# Subtest: s3 BlobStore listObjects strips the prefix from emitted keys
+ok 3948 - s3 BlobStore listObjects strips the prefix from emitted keys
+ ---
+ duration_ms: 6.865425
+ type: 'test'
+ ...
+# Subtest: s3 BlobStore listObjects does not leak into sibling-namespace keys (hyp/exports vs hyp/exports2)
+ok 3949 - s3 BlobStore listObjects does not leak into sibling-namespace keys (hyp/exports vs hyp/exports2)
+ ---
+ duration_ms: 0.41413
+ type: 'test'
+ ...
+# Subtest: s3 BlobStore listObjects empty-prefix without a configured prefix lists the whole bucket
+ok 3950 - s3 BlobStore listObjects empty-prefix without a configured prefix lists the whole bucket
+ ---
+ duration_ms: 0.338745
+ type: 'test'
+ ...
+# Subtest: s3 BlobStore putObject ifNoneMatch="*" surfaces blob_precondition_failed on conflict
+ok 3951 - s3 BlobStore putObject ifNoneMatch="*" surfaces blob_precondition_failed on conflict
+ ---
+ duration_ms: 0.448842
+ type: 'test'
+ ...
+# Subtest: s3 BlobStore rejects keys that try to escape the configured prefix
+ok 3952 - s3 BlobStore rejects keys that try to escape the configured prefix
+ ---
+ duration_ms: 0.244802
+ type: 'test'
+ ...
+# Subtest: s3 BlobStore exposes bucket and prefix for downstream telemetry
+ok 3953 - s3 BlobStore exposes bucket and prefix for downstream telemetry
+ ---
+ duration_ms: 0.090337
+ type: 'test'
+ ...
+# Subtest: s3 BlobStore tags AccessDenied with errorKind=s3_access_denied
+ok 3954 - s3 BlobStore tags AccessDenied with errorKind=s3_access_denied
+ ---
+ duration_ms: 0.443144
+ type: 'test'
+ ...
+# Subtest: s3 BlobStore tags NoSuchBucket on listObjects with errorKind=s3_bucket_missing
+ok 3955 - s3 BlobStore tags NoSuchBucket on listObjects with errorKind=s3_bucket_missing
+ ---
+ duration_ms: 0.392656
+ type: 'test'
+ ...
+# Subtest: s3 BlobStore tags getObject errors that are not NotFound
+ok 3956 - s3 BlobStore tags getObject errors that are not NotFound
+ ---
+ duration_ms: 0.227065
+ type: 'test'
+ ...
+# Subtest: s3 BlobStore deleteObject treats NotFound as benign and returns
+ok 3957 - s3 BlobStore deleteObject treats NotFound as benign and returns
+ ---
+ duration_ms: 0.185392
+ type: 'test'
+ ...
+# Subtest: createUnconfiguredS3BlobStore throws actionable s3_blob_store_unconfigured on use
+ok 3958 - createUnconfiguredS3BlobStore throws actionable s3_blob_store_unconfigured on use
+ ---
+ duration_ms: 0.314138
+ type: 'test'
+ ...
+# Subtest: detectCredentialSourceKind prefers explicit profile
+ok 3959 - detectCredentialSourceKind prefers explicit profile
+ ---
+ duration_ms: 0.862681
+ type: 'test'
+ ...
+# Subtest: detectCredentialSourceKind falls through to env vars when no profile is set
+ok 3960 - detectCredentialSourceKind falls through to env vars when no profile is set
+ ---
+ duration_ms: 0.113773
+ type: 'test'
+ ...
+# Subtest: detectCredentialSourceKind detects web identity (EKS IRSA)
+ok 3961 - detectCredentialSourceKind detects web identity (EKS IRSA)
+ ---
+ duration_ms: 0.104209
+ type: 'test'
+ ...
+# Subtest: detectCredentialSourceKind detects SSO
+ok 3962 - detectCredentialSourceKind detects SSO
+ ---
+ duration_ms: 0.129397
+ type: 'test'
+ ...
+# Subtest: detectCredentialSourceKind detects credential_process
+ok 3963 - detectCredentialSourceKind detects credential_process
+ ---
+ duration_ms: 0.116457
+ type: 'test'
+ ...
+# Subtest: detectCredentialSourceKind falls back to metadata when nothing else matches
+ok 3964 - detectCredentialSourceKind falls back to metadata when nothing else matches
+ ---
+ duration_ms: 0.107904
+ type: 'test'
+ ...
+# Subtest: detectCredentialSourceKind never returns the credential material itself
+ok 3965 - detectCredentialSourceKind never returns the credential material itself
+ ---
+ duration_ms: 0.213645
+ type: 'test'
+ ...
+# Subtest: detectCredentialSourceKind ignores empty-string AWS_ACCESS_KEY_ID
+ok 3966 - detectCredentialSourceKind ignores empty-string AWS_ACCESS_KEY_ID
+ ---
+ duration_ms: 0.119762
+ type: 'test'
+ ...
+# Subtest: validateS3SinkConfig accepts a minimum bucket-only config
+ok 3967 - validateS3SinkConfig accepts a minimum bucket-only config
+ ---
+ duration_ms: 1.086672
+ type: 'test'
+ ...
+# Subtest: validateS3SinkConfig accepts a full real-AWS config shape
+ok 3968 - validateS3SinkConfig accepts a full real-AWS config shape
+ ---
+ duration_ms: 0.980941
+ type: 'test'
+ ...
+# Subtest: validateS3SinkConfig accepts an S3-compatible config (MinIO)
+ok 3969 - validateS3SinkConfig accepts an S3-compatible config (MinIO)
+ ---
+ duration_ms: 0.154825
+ type: 'test'
+ ...
+# Subtest: validateS3SinkConfig rejects missing bucket with s3_config_invalid
+ok 3970 - validateS3SinkConfig rejects missing bucket with s3_config_invalid
+ ---
+ duration_ms: 0.141655
+ type: 'test'
+ ...
+# Subtest: validateS3SinkConfig rejects non-string bucket
+ok 3971 - validateS3SinkConfig rejects non-string bucket
+ ---
+ duration_ms: 0.196398
+ type: 'test'
+ ...
+# Subtest: validateS3SinkConfig rejects non-object input
+ok 3972 - validateS3SinkConfig rejects non-object input
+ ---
+ duration_ms: 0.115586
+ type: 'test'
+ ...
+# Subtest: validateS3SinkConfig rejects unknown storage class
+ok 3973 - validateS3SinkConfig rejects unknown storage class
+ ---
+ duration_ms: 0.274237
+ type: 'test'
+ ...
+# Subtest: validateS3SinkConfig rejects malformed endpoint_url
+ok 3974 - validateS3SinkConfig rejects malformed endpoint_url
+ ---
+ duration_ms: 0.163479
+ type: 'test'
+ ...
+# Subtest: validateS3SinkConfig rejects non-http(s) endpoint_url
+ok 3975 - validateS3SinkConfig rejects non-http(s) endpoint_url
+ ---
+ duration_ms: 0.443924
+ type: 'test'
+ ...
+# Subtest: validateS3SinkConfig rejects non-boolean force_path_style
+ok 3976 - validateS3SinkConfig rejects non-boolean force_path_style
+ ---
+ duration_ms: 0.379146
+ type: 'test'
+ ...
+# Subtest: normalizePrefix strips leading and trailing slashes
+ok 3977 - normalizePrefix strips leading and trailing slashes
+ ---
+ duration_ms: 0.165221
+ type: 'test'
+ ...
+# Subtest: classifyAwsError maps CredentialsProviderError to s3_credentials_missing
+ok 3978 - classifyAwsError maps CredentialsProviderError to s3_credentials_missing
+ ---
+ duration_ms: 1.988782
+ type: 'test'
+ ...
+# Subtest: classifyAwsError maps AccessDenied to s3_access_denied
+ok 3979 - classifyAwsError maps AccessDenied to s3_access_denied
+ ---
+ duration_ms: 0.168185
+ type: 'test'
+ ...
+# Subtest: classifyAwsError maps HTTP 403 even when name is generic
+ok 3980 - classifyAwsError maps HTTP 403 even when name is generic
+ ---
+ duration_ms: 0.095284
+ type: 'test'
+ ...
+# Subtest: classifyAwsError maps NoSuchBucket to s3_bucket_missing
+ok 3981 - classifyAwsError maps NoSuchBucket to s3_bucket_missing
+ ---
+ duration_ms: 0.072309
+ type: 'test'
+ ...
+# Subtest: classifyAwsError maps PermanentRedirect to s3_region_mismatch
+ok 3982 - classifyAwsError maps PermanentRedirect to s3_region_mismatch
+ ---
+ duration_ms: 0.076887
+ type: 'test'
+ ...
+# Subtest: classifyAwsError maps AuthorizationHeaderMalformed to s3_region_mismatch
+ok 3983 - classifyAwsError maps AuthorizationHeaderMalformed to s3_region_mismatch
+ ---
+ duration_ms: 0.073902
+ type: 'test'
+ ...
+# Subtest: classifyAwsError maps SlowDown to s3_throttled
+ok 3984 - classifyAwsError maps SlowDown to s3_throttled
+ ---
+ duration_ms: 0.092491
+ type: 'test'
+ ...
+# Subtest: classifyAwsError maps HTTP 503 to s3_throttled
+ok 3985 - classifyAwsError maps HTTP 503 to s3_throttled
+ ---
+ duration_ms: 0.120352
+ type: 'test'
+ ...
+# Subtest: classifyAwsError maps HTTP 429 to s3_throttled
+ok 3986 - classifyAwsError maps HTTP 429 to s3_throttled
+ ---
+ duration_ms: 0.280416
+ type: 'test'
+ ...
+# Subtest: classifyAwsError uses .Code when .name is missing
+ok 3987 - classifyAwsError uses .Code when .name is missing
+ ---
+ duration_ms: 0.350743
+ type: 'test'
+ ...
+# Subtest: classifyAwsError falls back to s3_put_failed for unknown errors
+ok 3988 - classifyAwsError falls back to s3_put_failed for unknown errors
+ ---
+ duration_ms: 0.177049
+ type: 'test'
+ ...
+# Subtest: classifyAwsError survives non-object inputs
+ok 3989 - classifyAwsError survives non-object inputs
+ ---
+ duration_ms: 0.097769
+ type: 'test'
+ ...
+# Subtest: describeS3ErrorKind returns a non-empty diagnostic for every error kind
+ok 3990 - describeS3ErrorKind returns a non-empty diagnostic for every error kind
+ ---
+ duration_ms: 0.242318
+ type: 'test'
+ ...
+# Subtest: exportBatch terminal failure: retryPartitions excludes already-uploaded partitions
+ok 3991 - exportBatch terminal failure: retryPartitions excludes already-uploaded partitions
+ ---
+ duration_ms: 10.914269
+ type: 'test'
+ ...
+# Subtest: exportBatch partial failure: retryPartitions has only the failed partition
+ok 3992 - exportBatch partial failure: retryPartitions has only the failed partition
+ ---
+ duration_ms: 4.40863
+ type: 'test'
+ ...
+# Subtest: exportBatch forwards dataset cluster columns to the encoder
+ok 3993 - exportBatch forwards dataset cluster columns to the encoder
+ ---
+ duration_ms: 2.992528
+ type: 'test'
+ ...
+# Subtest: exportBatch all-success: no retryPartitions field
+ok 3994 - exportBatch all-success: no retryPartitions field
+ ---
+ duration_ms: 2.637328
+ type: 'test'
+ ...
+# Subtest: exportBatch skips a partition with no new rows: no PUT, no blob
+ok 3995 - exportBatch skips a partition with no new rows: no PUT, no blob
+ ---
+ duration_ms: 1.505648
+ type: 'test'
+ ...
+# Subtest: exportBatch embeds the [sinceSeq,lastSeq] range in the object key and advances the watermark
+ok 3996 - exportBatch embeds the [sinceSeq,lastSeq] range in the object key and advances the watermark
+ ---
+ duration_ms: 4.91456
+ type: 'test'
+ ...
+# Subtest: exportBatch re-PUTs the same object key when the watermark is lost (idempotent crash retry)
+ok 3997 - exportBatch re-PUTs the same object key when the watermark is lost (idempotent crash retry)
+ ---
+ duration_ms: 3.955983
+ type: 'test'
+ ...
+# Subtest: drop-only tick: no object is PUT, but the watermark advances past the withheld rows (LLP 0070)
+ok 3998 - drop-only tick: no object is PUT, but the watermark advances past the withheld rows (LLP 0070)
+ ---
+ duration_ms: 3.40781
+ type: 'test'
+ ...
+# Subtest: partitionSegment renders empty partition as "all"
+ok 3999 - partitionSegment renders empty partition as "all"
+ ---
+ duration_ms: 1.899487
+ type: 'test'
+ ...
+# Subtest: partitionSegment joins ordered key=value pairs
+ok 4000 - partitionSegment joins ordered key=value pairs
+ ---
+ duration_ms: 0.217019
+ type: 'test'
+ ...
+# Subtest: partitionSegment strips path-separator characters so the segment cannot escape its dataset directory
+ok 4001 - partitionSegment strips path-separator characters so the segment cannot escape its dataset directory
+ ---
+ duration_ms: 0.21021
+ type: 'test'
+ ...
+# Subtest: renderObjectKey composes prefix/dataset/segment/filename
+ok 4002 - renderObjectKey composes prefix/dataset/segment/filename
+ ---
+ duration_ms: 0.182678
+ type: 'test'
+ ...
+# Subtest: renderObjectKey omits the prefix segment when prefix is empty
+ok 4003 - renderObjectKey omits the prefix segment when prefix is empty
+ ---
+ duration_ms: 0.130218
+ type: 'test'
+ ...
+# Subtest: renderObjectKey normalizes leading and trailing slashes in prefix
+ok 4004 - renderObjectKey normalizes leading and trailing slashes in prefix
+ ---
+ duration_ms: 0.124619
+ type: 'test'
+ ...
+# Subtest: renderObjectKey strips path separators from dataset and filename so the key stays inside the configured prefix
+ok 4005 - renderObjectKey strips path separators from dataset and filename so the key stays inside the configured prefix
+ ---
+ duration_ms: 0.175016
+ type: 'test'
+ ...
+# Subtest: renderObjectKey requires a non-empty dataset
+ok 4006 - renderObjectKey requires a non-empty dataset
+ ---
+ duration_ms: 0.332816
+ type: 'test'
+ ...
+# Subtest: renderObjectKey requires a non-empty filename
+ok 4007 - renderObjectKey requires a non-empty filename
+ ---
+ duration_ms: 0.30904
+ type: 'test'
+ ...
+# Subtest: keyIsWithinPrefix accepts keys under the prefix+dataset namespace
+ok 4008 - keyIsWithinPrefix accepts keys under the prefix+dataset namespace
+ ---
+ duration_ms: 0.353307
+ type: 'test'
+ ...
+# Subtest: keyIsWithinPrefix rejects keys outside the prefix+dataset namespace
+ok 4009 - keyIsWithinPrefix rejects keys outside the prefix+dataset namespace
+ ---
+ duration_ms: 0.165942
+ type: 'test'
+ ...
+# Subtest: keyIsWithinPrefix tolerates an empty prefix
+ok 4010 - keyIsWithinPrefix tolerates an empty prefix
+ ---
+ duration_ms: 0.101224
+ type: 'test'
+ ...
+# Subtest: activate fails at boot when query_sources is malformed
+ok 4011 - activate fails at boot when query_sources is malformed
+ ---
+ duration_ms: 1.292354
+ type: 'test'
+ ...
+# Subtest: activate fails at boot when a query source has no resolvable bucket
+ok 4012 - activate fails at boot when a query source has no resolvable bucket
+ ---
+ duration_ms: 0.415822
+ type: 'test'
+ ...
+# Subtest: activate registers nothing when query_sources is absent
+ok 4013 - activate registers nothing when query_sources is absent
+ ---
+ duration_ms: 0.169228
+ type: 'test'
+ ...
+# Subtest: same-bucket query source inherits the plugin prefix as its root
+ok 4014 - same-bucket query source inherits the plugin prefix as its root
+ ---
+ duration_ms: 0.478468
+ type: 'test'
+ ...
+# Subtest: bucket-override query source drops the plugin prefix and roots at the source prefix
+ok 4015 - bucket-override query source drops the plugin prefix and roots at the source prefix
+ ---
+ duration_ms: 0.20404
+ type: 'test'
+ ...
+# Subtest: activate registers one dataset per valid query source
+ok 4016 - activate registers one dataset per valid query source
+ ---
+ duration_ms: 0.485919
+ type: 'test'
+ ...
+# Subtest: validateS3QuerySources accepts parquet and iceberg sources
+ok 4017 - validateS3QuerySources accepts parquet and iceberg sources
+ ---
+ duration_ms: 2.38845
+ type: 'test'
+ ...
+# Subtest: validateS3QuerySources rejects a non-array
+ok 4018 - validateS3QuerySources rejects a non-array
+ ---
+ duration_ms: 0.211352
+ type: 'test'
+ ...
+# Subtest: validateS3QuerySources reports stable pointers for malformed entries
+ok 4019 - validateS3QuerySources reports stable pointers for malformed entries
+ ---
+ duration_ms: 0.754647
+ type: 'test'
+ ...
+# Subtest: validateS3QuerySources rejects duplicate names
+ok 4020 - validateS3QuerySources rejects duplicate names
+ ---
+ duration_ms: 0.191651
+ type: 'test'
+ ...
+# Subtest: validateS3QuerySources validates declared column types
+ok 4021 - validateS3QuerySources validates declared column types
+ ---
+ duration_ms: 0.234396
+ type: 'test'
+ ...
+# Subtest: validateS3QuerySources treats absent query_sources as caller concern (empty array ok)
+ok 4022 - validateS3QuerySources treats absent query_sources as caller concern (empty array ok)
+ ---
+ duration_ms: 0.132071
+ type: 'test'
+ ...
+# Subtest: validateS3QuerySources rejects a prefix that normalizes to empty
+ok 4023 - validateS3QuerySources rejects a prefix that normalizes to empty
+ ---
+ duration_ms: 0.209919
+ type: 'test'
+ ...
+# Subtest: validateS3QuerySources rejects an invalid endpoint_url at boot
+ok 4024 - validateS3QuerySources rejects an invalid endpoint_url at boot
+ ---
+ duration_ms: 0.207415
+ type: 'test'
+ ...
+# Subtest: validateS3QuerySources accepts a valid endpoint_url
+ok 4025 - validateS3QuerySources accepts a valid endpoint_url
+ ---
+ duration_ms: 1.621765
+ type: 'test'
+ ...
+# Subtest: parquet query source reads a single object back through SQL
+ok 4026 - parquet query source reads a single object back through SQL
+ ---
+ duration_ms: 11.214685
+ type: 'test'
+ ...
+# Subtest: parquet query source unions multiple objects and ignores non-parquet keys
+ok 4027 - parquet query source unions multiple objects and ignores non-parquet keys
+ ---
+ duration_ms: 12.827447
+ type: 'test'
+ ...
+# Subtest: parquet query source with no objects yields an empty result
+ok 4028 - parquet query source with no objects yields an empty result
+ ---
+ duration_ms: 0.272414
+ type: 'test'
+ ...
+# Subtest: iceberg query source with no metadata reads as empty (no throw)
+ok 4029 - iceberg query source with no metadata reads as empty (no throw)
+ ---
+ duration_ms: 0.286906
+ type: 'test'
+ ...
+# Subtest: iceberg query source is NULL-correct through a real BlobStore round trip
+ok 4030 - iceberg query source is NULL-correct through a real BlobStore round trip
+ ---
+ duration_ms: 46.818069
+ type: 'test'
+ ...
+# Subtest: parquet discovery bounds the prefix to a directory, excluding sibling namespaces
+ok 4031 - parquet discovery bounds the prefix to a directory, excluding sibling namespaces
+ ---
+ duration_ms: 1.873928
+ type: 'test'
+ ...
+# Subtest: parquet read rejects when a listed object disappears before read (list→read race)
+ok 4032 - parquet read rejects when a listed object disappears before read (list→read race)
+ ---
+ duration_ms: 0.416112
+ type: 'test'
+ ...
+# Subtest: the constraint inventory is non-empty and has no duplicate ids
+ok 4033 - the constraint inventory is non-empty and has no duplicate ids
+ ---
+ duration_ms: 1.518988
+ type: 'test'
+ ...
+# Subtest: every constraint states the harm of dropping it
+ok 4034 - every constraint states the harm of dropping it
+ ---
+ duration_ms: 0.254307
+ type: 'test'
+ ...
+# Subtest: claude: constraint "one-carrier-rule" survives somewhere in the skill corpus
+ok 4035 - claude: constraint "one-carrier-rule" survives somewhere in the skill corpus
+ ---
+ duration_ms: 0.467581
+ type: 'test'
+ ...
+# Subtest: claude: constraint "usage-not-raw-frame" survives somewhere in the skill corpus
+ok 4036 - claude: constraint "usage-not-raw-frame" survives somewhere in the skill corpus
+ ---
+ duration_ms: 0.207846
+ type: 'test'
+ ...
+# Subtest: claude: constraint "captured-content-is-data" survives somewhere in the skill corpus
+ok 4037 - claude: constraint "captured-content-is-data" survives somewhere in the skill corpus
+ ---
+ duration_ms: 0.283291
+ type: 'test'
+ ...
+# Subtest: claude: constraint "changes-from-behaviour-not-payloads" survives somewhere in the skill corpus
+ok 4038 - claude: constraint "changes-from-behaviour-not-payloads" survives somewhere in the skill corpus
+ ---
+ duration_ms: 0.191551
+ type: 'test'
+ ...
+# Subtest: claude: constraint "privacy-per-item-confirmation" survives somewhere in the skill corpus
+ok 4039 - claude: constraint "privacy-per-item-confirmation" survives somewhere in the skill corpus
+ ---
+ duration_ms: 0.154565
+ type: 'test'
+ ...
+# Subtest: claude: constraint "privacy-session-optout-first" survives somewhere in the skill corpus
+ok 4040 - claude: constraint "privacy-session-optout-first" survives somewhere in the skill corpus
+ ---
+ duration_ms: 0.13156
+ type: 'test'
+ ...
+# Subtest: claude: constraint "graph-derived-facets" survives somewhere in the skill corpus
+ok 4041 - claude: constraint "graph-derived-facets" survives somewhere in the skill corpus
+ ---
+ duration_ms: 0.3224
+ type: 'test'
+ ...
+# Subtest: claude: constraint "graph-project-first" survives somewhere in the skill corpus
+ok 4042 - claude: constraint "graph-project-first" survives somewhere in the skill corpus
+ ---
+ duration_ms: 0.274157
+ type: 'test'
+ ...
+# Subtest: claude: constraint "graph-keys-converge" survives somewhere in the skill corpus
+ok 4043 - claude: constraint "graph-keys-converge" survives somewhere in the skill corpus
+ ---
+ duration_ms: 0.141825
+ type: 'test'
+ ...
+# Subtest: claude: constraint "graph-is-derived-not-truth" survives somewhere in the skill corpus
+ok 4044 - claude: constraint "graph-is-derived-not-truth" survives somewhere in the skill corpus
+ ---
+ duration_ms: 0.100183
+ type: 'test'
+ ...
+# Subtest: codex: constraint "one-carrier-rule" survives somewhere in the skill corpus
+ok 4045 - codex: constraint "one-carrier-rule" survives somewhere in the skill corpus
+ ---
+ duration_ms: 0.286024
+ type: 'test'
+ ...
+# Subtest: codex: constraint "usage-not-raw-frame" survives somewhere in the skill corpus
+ok 4046 - codex: constraint "usage-not-raw-frame" survives somewhere in the skill corpus
+ ---
+ duration_ms: 0.120614
+ type: 'test'
+ ...
+# Subtest: codex: constraint "captured-content-is-data" survives somewhere in the skill corpus
+ok 4047 - codex: constraint "captured-content-is-data" survives somewhere in the skill corpus
+ ---
+ duration_ms: 0.075344
+ type: 'test'
+ ...
+# Subtest: codex: constraint "changes-from-behaviour-not-payloads" survives somewhere in the skill corpus
+ok 4048 - codex: constraint "changes-from-behaviour-not-payloads" survives somewhere in the skill corpus
+ ---
+ duration_ms: 0.061243
+ type: 'test'
+ ...
+# Subtest: codex: constraint "privacy-per-item-confirmation" survives somewhere in the skill corpus
+ok 4049 - codex: constraint "privacy-per-item-confirmation" survives somewhere in the skill corpus
+ ---
+ duration_ms: 0.035294
+ type: 'test'
+ ...
+# Subtest: codex: constraint "privacy-session-optout-first" survives somewhere in the skill corpus
+ok 4050 - codex: constraint "privacy-session-optout-first" survives somewhere in the skill corpus
+ ---
+ duration_ms: 0.035124
+ type: 'test'
+ ...
+# Subtest: codex: constraint "graph-derived-facets" survives somewhere in the skill corpus
+ok 4051 - codex: constraint "graph-derived-facets" survives somewhere in the skill corpus
+ ---
+ duration_ms: 0.052029
+ type: 'test'
+ ...
+# Subtest: codex: constraint "graph-project-first" survives somewhere in the skill corpus
+ok 4052 - codex: constraint "graph-project-first" survives somewhere in the skill corpus
+ ---
+ duration_ms: 0.051178
+ type: 'test'
+ ...
+# Subtest: codex: constraint "graph-keys-converge" survives somewhere in the skill corpus
+ok 4053 - codex: constraint "graph-keys-converge" survives somewhere in the skill corpus
+ ---
+ duration_ms: 0.08537
+ type: 'test'
+ ...
+# Subtest: codex: constraint "graph-is-derived-not-truth" survives somewhere in the skill corpus
+ok 4054 - codex: constraint "graph-is-derived-not-truth" survives somewhere in the skill corpus
+ ---
+ duration_ms: 0.430624
+ type: 'test'
+ ...
+# Subtest: every skill shipped to both hosts is covered by the divergence record
+ok 4055 - every skill shipped to both hosts is covered by the divergence record
+ ---
+ duration_ms: 0.984116
+ type: 'test'
+ ...
+# Subtest: hypaware-privacy: host-specific content matches the recorded surface
+ok 4056 - hypaware-privacy: host-specific content matches the recorded surface
+ ---
+ duration_ms: 0.185712
+ type: 'test'
+ ...
+# Subtest: hypaware-query: host-specific content matches the recorded surface
+ok 4057 - hypaware-query: host-specific content matches the recorded surface
+ ---
+ duration_ms: 0.075815
+ type: 'test'
+ ...
+# Subtest: hypaware-reference: host-specific content matches the recorded surface
+ok 4058 - hypaware-reference: host-specific content matches the recorded surface
+ ---
+ duration_ms: 0.080292
+ type: 'test'
+ ...
+# Subtest: skills recorded as fully shared stay identical across hosts
+ok 4059 - skills recorded as fully shared stay identical across hosts
+ ---
+ duration_ms: 0.168406
+ type: 'test'
+ ...
+# Subtest: validateVectorSearchConfig defaults to no indexes and enabled refresh
+ok 4060 - validateVectorSearchConfig defaults to no indexes and enabled refresh
+ ---
+ duration_ms: 1.203029
+ type: 'test'
+ ...
+# Subtest: refresh interval default is longer than cache maintenance (60m)
+ok 4061 - refresh interval default is longer than cache maintenance (60m)
+ ---
+ duration_ms: 0.931536
+ type: 'test'
+ ...
+# Subtest: index name defaults to dataset.column
+ok 4062 - index name defaults to dataset.column
+ ---
+ duration_ms: 0.280517
+ type: 'test'
+ ...
+# Subtest: explicit index name and id_column are honored
+ok 4063 - explicit index name and id_column are honored
+ ---
+ duration_ms: 0.143779
+ type: 'test'
+ ...
+# Subtest: index names that would escape the state dir are rejected
+ok 4064 - index names that would escape the state dir are rejected
+ ---
+ duration_ms: 0.226945
+ type: 'test'
+ ...
+# Subtest: duplicate index names are rejected
+ok 4065 - duplicate index names are rejected
+ ---
+ duration_ms: 0.234937
+ type: 'test'
+ ...
+# Subtest: index declarations missing dataset or column are rejected with pointers
+ok 4066 - index declarations missing dataset or column are rejected with pointers
+ ---
+ duration_ms: 0.132181
+ type: 'test'
+ ...
+# Subtest: refresh budgets reject non-positive values
+ok 4067 - refresh budgets reject non-positive values
+ ---
+ duration_ms: 0.127995
+ type: 'test'
+ ...
+# Subtest: refresh interval accepts fractional minutes (sub-minute smoke ticks)
+ok 4068 - refresh interval accepts fractional minutes (sub-minute smoke ticks)
+ ---
+ duration_ms: 0.256189
+ type: 'test'
+ ...
+# Subtest: refresh can be disabled
+ok 4069 - refresh can be disabled
+ ---
+ duration_ms: 0.324824
+ type: 'test'
+ ...
+# Subtest: embedder-openai and vector-search manifests load and validate
+ok 4070 - embedder-openai and vector-search manifests load and validate
+ ---
+ duration_ms: 4.193374
+ type: 'test'
+ ...
+# Subtest: both plugins are bundled but excluded from default activation
+ok 4071 - both plugins are bundled but excluded from default activation
+ ---
+ duration_ms: 0.178451
+ type: 'test'
+ ...
+# Subtest: parseVectorSearchArgv parses flags in any order and joins the query
+ok 4072 - parseVectorSearchArgv parses flags in any order and joins the query
+ ---
+ duration_ms: 0.172061
+ type: 'test'
+ ...
+# Subtest: parseVectorSearchArgv rejects a missing query and bad flags
+ok 4073 - parseVectorSearchArgv rejects a missing query and bad flags
+ ---
+ duration_ms: 0.1642
+ type: 'test'
+ ...
+# Subtest: refreshIndexes: an already-expired deadline skips every pending shard and reports exhaustion
+ok 4074 - refreshIndexes: an already-expired deadline skips every pending shard and reports exhaustion
+ ---
+ duration_ms: 1.42732
+ type: 'test'
+ ...
+# Subtest: refreshIndexes: the row budget stops further builds after the shard that crosses it
+ok 4075 - refreshIndexes: the row budget stops further builds after the shard that crosses it
+ ---
+ duration_ms: 66.817065
+ type: 'test'
+ ...
+# Subtest: refreshIndexes: orphans sweep even when the budget is already spent
+ok 4076 - refreshIndexes: orphans sweep even when the budget is already spent
+ ---
+ duration_ms: 0.930334
+ type: 'test'
+ ...
+# Subtest: refreshIndexes: a fresh shard is not rebuilt, and the report says so
+ok 4077 - refreshIndexes: a fresh shard is not rebuilt, and the report says so
+ ---
+ duration_ms: 0.448782
+ type: 'test'
+ ...
+# Subtest: refreshIndexes: a configured embedder dimension that differs from the sidecar forces a rebuild
+ok 4078 - refreshIndexes: a configured embedder dimension that differs from the sidecar forces a rebuild
+ ---
+ duration_ms: 2.430595
+ type: 'test'
+ ...
+# Subtest: collectShardTexts: identical texts collapse to one embedding by content hash
+ok 4079 - collectShardTexts: identical texts collapse to one embedding by content hash
+ ---
+ duration_ms: 0.207736
+ type: 'test'
+ ...
+# Subtest: collectShardTexts: id_column keys rows by id and skips rows without one
+ok 4080 - collectShardTexts: id_column keys rows by id and skips rows without one
+ ---
+ duration_ms: 0.192443
+ type: 'test'
+ ...
+# Subtest: searchVectorIndexes: --no-refresh with a shard built by another model is vector_model_mismatch
+ok 4081 - searchVectorIndexes: --no-refresh with a shard built by another model is vector_model_mismatch
+ ---
+ duration_ms: 58.53609
+ type: 'test'
+ ...
+# Subtest: searchVectorIndexes: --no-refresh with a shard at another dimension is vector_dimension_mismatch
+ok 4082 - searchVectorIndexes: --no-refresh with a shard at another dimension is vector_dimension_mismatch
+ ---
+ duration_ms: 0.82245
+ type: 'test'
+ ...
+# Subtest: searchVectorIndexes: no configured index matching the filter is vector_no_indexes
+ok 4083 - searchVectorIndexes: no configured index matching the filter is vector_no_indexes
+ ---
+ duration_ms: 0.38768
+ type: 'test'
+ ...
+# Subtest: shardFileBase renders a sorted human label plus a partition hash
+ok 4084 - shardFileBase renders a sorted human label plus a partition hash
+ ---
+ duration_ms: 6.978036
+ type: 'test'
+ ...
+# Subtest: shardFileBase: partitions whose labels collide get distinct names
+ok 4085 - shardFileBase: partitions whose labels collide get distinct names
+ ---
+ duration_ms: 0.454941
+ type: 'test'
+ ...
+# Subtest: partitionLabel renders unhashed display labels
+ok 4086 - partitionLabel renders unhashed display labels
+ ---
+ duration_ms: 0.203349
+ type: 'test'
+ ...
+# Subtest: computeShardStates: live partition without a shard is missing
+ok 4087 - computeShardStates: live partition without a shard is missing
+ ---
+ duration_ms: 0.202197
+ type: 'test'
+ ...
+# Subtest: computeShardStates: matching declaration, model, and row count is fresh
+ok 4088 - computeShardStates: matching declaration, model, and row count is fresh
+ ---
+ duration_ms: 0.176508
+ type: 'test'
+ ...
+# Subtest: computeShardStates: model mismatch is stale_model, not an error
+ok 4089 - computeShardStates: model mismatch is stale_model, not an error
+ ---
+ duration_ms: 0.122997
+ type: 'test'
+ ...
+# Subtest: computeShardStates: row count drift is stale_rows
+ok 4090 - computeShardStates: row count drift is stale_rows
+ ---
+ duration_ms: 0.102837
+ type: 'test'
+ ...
+# Subtest: computeShardStates: model mismatch wins over row drift (one rebuild fixes both)
+ok 4091 - computeShardStates: model mismatch wins over row drift (one rebuild fixes both)
+ ---
+ duration_ms: 0.968933
+ type: 'test'
+ ...
+# Subtest: computeShardStates: dataset/column/id_column drift is stale_config even when model and rows match
+ok 4092 - computeShardStates: dataset/column/id_column drift is stale_config even when model and rows match
+ ---
+ duration_ms: 0.408531
+ type: 'test'
+ ...
+# Subtest: computeShardStates: sidecar whose recorded partition disagrees is stale_config
+ok 4093 - computeShardStates: sidecar whose recorded partition disagrees is stale_config
+ ---
+ duration_ms: 0.322029
+ type: 'test'
+ ...
+# Subtest: computeShardStates: dimension drift is stale_dimension when an expected dimension is known
+ok 4094 - computeShardStates: dimension drift is stale_dimension when an expected dimension is known
+ ---
+ duration_ms: 0.159713
+ type: 'test'
+ ...
+# Subtest: computeShardStates: dimension is ignored when unknown, matching, or the shard is empty
+ok 4095 - computeShardStates: dimension is ignored when unknown, matching, or the shard is empty
+ ---
+ duration_ms: 0.142617
+ type: 'test'
+ ...
+# Subtest: computeShardStates: shard for an evicted partition is orphan
+ok 4096 - computeShardStates: shard for an evicted partition is orphan
+ ---
+ duration_ms: 0.193053
+ type: 'test'
+ ...
+# Subtest: mergeTopK merges descending by score across shards
+ok 4097 - mergeTopK merges descending by score across shards
+ ---
+ duration_ms: 0.481312
+ type: 'test'
+ ...
+# Subtest: mergeTopK with fewer hits than topK returns everything
+ok 4098 - mergeTopK with fewer hits than topK returns everything
+ ---
+ duration_ms: 0.057197
+ type: 'test'
+ ...
+# Subtest: contentId is stable and collision-distinct for different texts
+ok 4099 - contentId is stable and collision-distinct for different texts
+ ---
+ duration_ms: 0.089025
+ type: 'test'
+ ...
+# Subtest: gateway fallback id is invariant under every volatile block field
+ok 4100 - gateway fallback id is invariant under every volatile block field
+ ---
+ duration_ms: 1.024036
+ type: 'test'
+ ...
+# Subtest: claude transcript match key is invariant under every volatile block field
+ok 4101 - claude transcript match key is invariant under every volatile block field
+ ---
+ duration_ms: 0.170579
+ type: 'test'
+ ...
+# Subtest: stripVolatileBlockFields removes exactly the canonical list
+ok 4102 - stripVolatileBlockFields removes exactly the canonical list
+ ---
+ duration_ms: 0.418355
+ type: 'test'
+ ...
+1..4102
+# tests 4104
+# suites 0
+# pass 4103
+# fail 0
+# cancelled 0
+# skipped 1
+# todo 0
+# duration_ms 15911.037278
+TEST EXIT=0
diff --git a/x/order.mjs b/x/order.mjs
new file mode 100644
index 00000000..4238d51e
--- /dev/null
+++ b/x/order.mjs
@@ -0,0 +1,10 @@
+import readline from 'node:readline/promises'
+import { Readable } from 'node:stream'
+
+const input = Readable.from(['y\n', '3\n'])
+const out = { write: () => true }
+const rl = readline.createInterface({ input, output: out })
+rl.on('line', (l) => console.log('EVENT line:', JSON.stringify(l)))
+rl.once('close', () => console.log('EVENT close'))
+rl.question('Q? ').then((a) => console.log('EVENT question resolved:', JSON.stringify(a)))
+setTimeout(() => { rl.close(); }, 200)
diff --git a/x/pty_prog.mjs b/x/pty_prog.mjs
new file mode 100644
index 00000000..3a02007f
--- /dev/null
+++ b/x/pty_prog.mjs
@@ -0,0 +1,5 @@
+import process from 'node:process'
+import { askYesNo } from '../src/core/cli/confirm.js'
+const res = await askYesNo({ stdin: process.stdin, stderr: process.stderr }, 'Delete everything? [y/N] ')
+process.stderr.write(`\nRESULT=${res}\n`)
+process.exit(0)
diff --git a/x/pty_variants.mjs b/x/pty_variants.mjs
new file mode 100644
index 00000000..a094624f
--- /dev/null
+++ b/x/pty_variants.mjs
@@ -0,0 +1,20 @@
+import process from 'node:process'
+import readline from 'node:readline/promises'
+
+const variant = process.argv[2]
+const rl = readline.createInterface({ input: process.stdin, output: process.stderr })
+function oldAskLineOnce(rl, input, prompt) {
+ return new Promise((resolve) => {
+ let settled = false
+ const done = (line) => { if (settled) return; settled = true; resolve(line) }
+ rl.once('close', () => done(null))
+ rl.question(prompt).then(done, () => done(null))
+ if (input.readableEnded === true) done(null)
+ })
+}
+let answer
+if (variant === 'old') answer = await oldAskLineOnce(rl, process.stdin, 'Delete everything? [y/N] ')
+else answer = await Promise.race([rl.question('Delete everything? [y/N] '), new Promise(r => setTimeout(() => r(''), 400))])
+rl.close()
+process.stderr.write(`\nANSWER=${JSON.stringify(answer)}\n`)
+process.exit(0)
diff --git a/x/typecheck.log b/x/typecheck.log
new file mode 100644
index 00000000..57685aae
--- /dev/null
+++ b/x/typecheck.log
@@ -0,0 +1,5 @@
+
+> hypaware@1.22.0 typecheck
+> tsc -p tsconfig.json --noEmit
+
+TC EXIT=0