/**
 * A structured operator-facing notice ({@link RunnerDeps.onNotice}) — a fact the engine announces that
 * is neither an error nor part of the task result: today, a configured value that was discarded in
 * favor of another (the loud-bad-value discipline's announcement dialect). Structured so a host can
 * FORWARD it to its own user surface instead of losing it in process stderr.
 */
export interface EngineNotice {
    /** Stable machine-readable family, dot-namespaced. Current families:
     *  - `"config.autocompact_window_clamped"` (design/374 slice 1b) — a model declared
     *    `autoCompactTokens` ABOVE its physical window; the trigger-side geometry was clamped to the
     *    physical window (an autocompact window only ever lowers the trigger) and the bad value is
     *    announced once per prepared task; `detail: { modelId, declaredAutoCompactTokens,
     *    physicalWindow, sessionId, runId }` — `runId` is what makes the "once per prepared task" claim
     *    checkable, the session id beside it being shared by every run of the session.
     *  - `"config.env_timeout_discarded"` — a Bash timeout knob (option or env) held a value that is not
     *    the value in force; `detail: { knob, raw, usedMs }`.
     *  - `"config.materialize_env_discarded"` — `SEMA_TOOL_MATERIALIZE_STRATEGY` held a value outside the
     *    closed set in a seat where it is not in force; `detail: { raw, specStrategy? }`.
     *  - `"config.task_root_not_canonical"` (#759) — the task root this run was handed is not its own
     *    canonical path (a symlinked directory; `/var` → `/private/var` on macOS). Containment is judged
     *    on the CANONICAL spelling, so the deployment and the engine reason about two different
     *    directories: a target the host considers inside the root can be judged outside it. The run is
     *    unchanged — announce, never narrow, never repair — and the fix is to hand the canonical path in.
     *    Audience `operator` (a deployment wiring fact). `detail: { given, canonical, sessionId, runId }`
     *    — both spellings, so a consumer can diff them without guessing which one it holds. Once per
     *    prepared leg, and never for a root that already is its own canonical form (its absence is the
     *    claim that the two agree).
     *  - `"config.write_protection_target_view_absent"` — this leg's write-protection judge was compiled
     *    with NO execution environment while at least one path-confinable write tool is mounted, so it
     *    compares SPELLINGS against its table and nothing else: a write reaching a protected path only
     *    through an alias is cleared. Announce, never narrow — no verdict moves (an environment-less judge
     *    has no filesystem to resolve in). Audience `operator`. `detail: { tools, sessionId, runId }`,
     *    `tools` = the path-confinable write tools on the LEG-START roster, ascending. EXACTLY ONCE per
     *    prepared leg, never per judgment and never re-fired for a mid-run roster delta; absent ⇒ the judge
     *    has its target view, or no covered write tool was mounted AT PREPARE, or there is no judge —
     *    `wiringManifest.writeProtection` reports the judge's reading either way.
     *  - `"config.path_view_unresolved"` (#869; 7.48.0 `config.write_protection_unresolved`) — this leg's write-protection judge HAS a target view
     *    and one SOURCE of a judgment did not answer: the environment refused a spelling, could not follow a link,
     *    threw, or did not answer within the gate's deadline — on the target, its link chain or a root's key — or
     *    a deployment policy's guarded write rows did not come back. A target whose real destination (or chain)
     *    could not be determined is refused where the table has rows; an auxiliary source is absent on its own (a
     *    root exempts and places nothing; rows that did not come back are held, so the writes they could guard are
     *    not placed); every row an answering source met stays met — the judge's absence law. The disclosure is
     *    where a deployment learns its environment is the reason. Announce, never narrow — no verdict moves because of this notice; the
     *    verdict already moved when the environment failed to answer. Audience `operator`.
     *    `detail: { reason, cause, sessionId, runId }` — `reason` is `"unresolved-symlink"` (a link whose
     *    real target is unknowable) or `"unresolvable-target"` (everything else: a refused spelling, an
     *    errored or throwing probe), and `cause` is the environment's own first sentence for that reason.
     *    ONCE PER LEG PER `reason` — the fact is about the ENVIRONMENT, not about a write, so it carries no
     *    call identity and a second failing write of the same shape adds nothing. Absent ⇒ every judgment
     *    this leg made had the views it was compiled for, OR the judge never finished at all (a judgment the
     *    gate's bounded wait gave up on is reported on the hook-error lane instead, `onHookError`).
     *  - `"config.tool_card_undeclared"` (design/388 L4) — a tool minted a `details.type` card its definition
     *    (or its caller declaration's `renderHints.resultCards`) never declared; the card was dropped from
     *    `tool_end.structured`; once per leg per type; `detail: { type, sessionId, runId }`.
     *  - `"config.tool_face_undeclared"` / `"config.tool_face_invalid"` (design/388 §2.7) — an MCP server's
     *    `toolFaces` overlay names a tool the listing does not carry / does not fit the tool's materialized
     *    schema (the tool mounts bare); once per leg per tool; `detail: { server, tool, problem?, sessionId, runId }`.
     *  - `"agents.listing_truncated"` — the agent-type roster frame did not fit the same listing budget:
     *    at least one type is listed name-only (`nameOnly`) or not at all (`dropped`). Audience `operator`.
     *    `detail: { budgetBytes, total, kept, nameOnly: [name], dropped: [name], sessionId }`; the same
     *    firing rule as its skills sibling below (once per delivered frame, on the carrying message's receipt).
     *  - `"permission.allowed_tool_not_granted"` — an allow list holds entries that grant (or refuse) nothing: a loaded skill's
     *    `allowed-tools` / `disallowed-tools`, the request's `TaskSpec.allowedTools`, the deployment's `RunnerDeps.allowedTools`.
     *    `*` (it voids an allow list; on a deny list it reads as nothing and the rest still refuses), `mcp__*` (no server
     *    named), a rule on a tool with no such rule form (an allow by path on a tool other than `Read` / `Edit`, a deny by host),
     *    a shape the rule grammar refuses, a tool name stale against the run's roster. The run goes on and asks for those tools
     *    as usual. Audience `user`; once per prepared task for the skill and request lists (`detail.sessionId` present), once
     *    per Runner for the deployment's (at construction, no session). `detail: { sessionId?, entries: [{ source, entry,
     *    reason, skill?, list? }] }` (`source` ∈ `skill` / `request` / `deployment`; `skill` and `list` only on a skill's;
     *    `reason` ∈ {@link import("./allow-entry.js").ALLOW_ENTRY_INERT_REASONS}; `entry` is the entry as written — the message spells it — when it is at most `NOTICE_RULE_TEXT_MAX` (1 024) code points; a longer one is cut to 1 024 plus the truncation mark, 1 025 in all).
     *  - `"mods.registration_refused"` — an extension registration was not mounted: a registration admitted before it
     *    answered `{ refuse }` on `plugin.register`, or failed while judging it (fail-closed). The run goes on without it.
     *    Audience `user` (the seat is in `detail`). Once per refused registration per run. `detail: { name, seat, tier,
     *    refusedBy, reason, sessionId }` (`seat` ∈ `deployment` / `task`).
     *  - `"skills.listing_truncated"` — the `<skills>` disclosure block did not fit its byte budget, so
     *    at least one skill is listed without its description (`nameOnly`) or, in the pathological case,
     *    not listed at all (`dropped`); the `Skill` tool still serves every one of them. Audience
     *    `operator` — raising `TaskSpec.skillListingBudgetFraction`, lowering `skillListingMaxDescChars`
     *    or declaring fewer skills are all deployment acts. `detail: { budgetBytes, descMaxChars, total,
     *    kept, nameOnly: [name], dropped: [name], sessionId }` — `budgetBytes` IS the budget in force;
     *    no ceiling stands above the declared fraction, so no second number rides beside it.
     *    Announced on the RECEIPT of the message that carried the frame — the leg's first user message, or
     *    the boundary steer that a drift (or a resume leg's initial listing) rides — once per delivered
     *    frame the budget actually degraded, and never for one that fit. Every count is read off that frame, so
     *    `total` is the whole roster on an initial listing and the newly announced set on a drift frame.
     *    A run whose deployment turned the listing off (`attachments.skillsListing: false`) emits nothing,
     *    since nothing is rendered. There is NO dedup key: each notice is one delivered frame`s cost, and
     *    a frame is delivered (and committed) once.
     *  - `"tool_result.offload_put_failed"` (#167) — a clear-with-offload persist's fire-and-forget put
     *    failed; THIS attempt stored nothing (the failure arm reports, it never re-inserts under the
     *    ref) — an earlier attempt of the same idempotent re-put may already have stored the row, so
     *    the notice claims a failed write, not an empty ref;
     *    `detail: { ref, sessionId, cause, occurrenceId }`. Per-occurrence, not
     *    per-process-deduplicated: each failed write is a distinct fact, and `ref` names the ROW rather
     *    than the attempt, so the uuidv7 `occurrenceId` minted at the failure is what tells two genuine
     *    losses of one ref apart. Dedup key: (code, occurrenceId).
     *  - `"tool_result.media_refused"` — an executed call's result carried media blocks (an image or a document, from
     *    an MCP server, a host tool, an engine tool or a `tool_result` hook's replacement) whose bytes the tool-result
     *    seat's admission refused: not a base64 payload, a media type outside the closed set, over the cap, or bytes
     *    without the claimed type's signature. Each such block already stands in the result as a text placeholder naming
     *    the tool and the refusal (never the bytes), so the model, `tool_end.output` and the transcript read that; this is
     *    the operator's account. ONCE PER CALL. `detail: { toolName, toolCallId, refused: [{ type, reason, bytes, limit?,
     *    mimeType? }], sessionId, runId }` — closed-set words, counts and a media type only when it can be one.
     *  - `"workflow.governance_key_stripped"` (#235) — the fields of an LLM-authored workflow `agent(spec)`
     *    that did NOT cross the governed default-deny whitelist: an unrecognized/control-plane field, or a
     *    `readFace` value that is not the containment-tightening one. The spawn PROCEEDS on the deployment
     *    baseline (the strip is not a refusal in this window), which is why the drop is announced;
     *    `detail: { total, stripped: [{ key, reason }], omitted?, occurrenceId }`, the rendered key list
     *    bounded in count and length because the names come from the untrusted script. One aggregated
     *    notice per governed child build, not de-duplicated across builds: each spec is a distinct fact,
     *    and the uuidv7 `occurrenceId` minted at the announcement is what carries that claim to a consumer
     *    that would otherwise read two builds of one spec as one repeated notice. Dedup key: (code, occurrenceId).
     *  - `"workflow.agent_option_ignored"` (#505 ①) — the OPTIONS-object twin of the row above: keys of
     *    `agent(spec, opts)`'s SECOND argument that were not applied (unrecognized, or a known option
     *    whose value its type check rejected — `label: 42`). The spawn PROCEEDS without them, which is
     *    why the drop is announced. A separate code because the two membranes accept different key
     *    sets and the message names its own. `detail: { total, ignored: [key], omitted?, occurrenceId }`,
     *    same bounding and same per-call (never de-duplicated) semantics as the spec-side row; dedup
     *    key: (code, occurrenceId). NOT the whole family: a SPEC field written on the options object
     *    (`model`, `modelName`, `thinking`, …) is REFUSED at the membrane instead — a silently ignored
     *    `model` runs a different agent than the author asked for.
     *  - `"config.forward_sink_failed"` — the deployment's opt-in subagent display sink
     *    (`RunInternals.onForwardEvent`) failed and the frame it was handed was dropped: it threw, or —
     *    since an `async` function satisfies that `=> void` seat — it returned a promise that rejected.
     *    The run is untouched (forwarding is a render channel, and delivery is never awaited, so a sink
     *    that never settles is not a failure and announces nothing). `detail: { site, cause }` — `site` is
     *    the engine-owned site name the failures are counted under, `cause` the thrown value's message.
     *    Audience `operator`: the sink is deployment wiring and nobody else can repair it. Dedup unit:
     *    once per RUN (the counters ride `RunInternals.forwardScope`, which every copy of a run's
     *    internals carries), later failures of the same run counted only — a sink that fails on every
     *    frame therefore costs ONE line, not one per frame.
     *  - `"memory.restore_withheld"` — a memory containment DID NOT write a committed baseline back: another
     *    writer held the path when the restore reached it, and those newer bytes were left standing.
     *    Containment is unaffected and the committed copy is still in the store, so reconciling the two is
     *    the host's act (audience `operator`). `detail: { paths: [{ path, entryId?, rev? }], sessionId? }`,
     *    `rev` = the declined generation. Dedup unit: once per containment PASS, never per path.
     *  - `"memory.project_scope_absent"` — the assembly point DECLARED that it could derive no project
     *    identity for this session's working root (`TaskSpec.memory.projectKey: { absent }`), so the
     *    layering it sent is used exactly as written: entries this session writes go to whatever the
     *    spec named, typically a layer shared with every other project. Audience `operator` — the
     *    reading is about how the deployment resolves working roots, and only an operator can change
     *    it; nothing retries and nothing is refused. `detail: { reason, taskRoot, sessionId,
     *    writeScope? }`, `reason` the deployment's own word (not_git | bare | unreadable),
     *    `writeScope` absent ⇔ a read-only layering. Minted at the DECLARATION rather than at the
     *    mount (the declaration is already the fact, and a mount that later fails must not swallow
     *    it). Dedup unit: once per prepared leg.
     *  - `"mcp.revocation_probe_failed"` (design/338) — the deployment's `mcpRevocations.isRevoked`
     *    probe threw; MCP dispatch FAILS OPEN (revocation is a tightening face) and this announces
     *    once per materialization (a resume re-materializes and may announce again).
     *    `detail: { message, runId }` — still no sessionId (a deployment wiring fact, not
     *    session-attributed); `runId` (#499) makes the once-per-materialization claim CHECKABLE without
     *    attributing the line to a conversation, an opaque invocation id saying which materialization
     *    rather than whose. `"operator"` audience by
     *    its explicit {@link NOTICE_AUDIENCE} row (#433 made the registry total over the catalog —
     *    no engine-minted code is audience-defaulted any more). The refusal itself
     *    (`mcp.server_revoked`) is a tool RESULT code, not a notice.
     *  - `"mcp.server_redialed"` — a host asked this run to RE-DIAL one MCP server (the run
     *    handle's `reconnectMcpServer` verb) and this is what came of it. `detail: { server, outcome,
     *    added, removed, reason?, sessionId, runId }` — `outcome` is the result's own three-word answer
     *    (`reconnected` / `refused` / `not_declared`) and `added`/`removed` are COUNTS of namespaced
     *    tool names, never the names themselves (a server's tool names are server-authored text and
     *    this is an operator line, not a disclosure channel). ONE code rather than a success code and a
     *    failure code: the outcome is data about one event, not two kinds of event, and both would
     *    carry the same presentation tier — a consumer alerting on failures reads one field.
     *    `"operator"` audience: re-dialing is deployment plumbing and a failed one leaves the server
     *    DOWN, which only the operator can act on. Dedup unit: once per ATTEMPT — never folded per run,
     *    because two attempts on one server are two different answers and folding the second would hide
     *    the one that says the server is still down.
     *  - `"mcp.injection_dropped"` (L-167) — an entry of the user's own request-lane MCP injection
     *    was NOT mounted for the leg. The HOST owns the drop sites and supplies the facts; CORE composes
     *    the notice ({@link import("./mcp-injection-drop.js").mcpInjectionDroppedNotice}, the one mint).
     *    `detail: { sessionId, server, reason, field? }` — `reason` is the closed set
     *    `malformed_entry` / `name_reserved_by_deployment` / `gate_closed` / `over_cap`
     *    (`MCP_INJECTION_DROP_REASONS`, CLOSED-SETS), `field` names the bad key for `malformed_entry`
     *    when known. `"user"` audience; `sessionId` REQUIRED at the mint (a user row with no session
     *    routes nowhere — refused, never fabricated). Dedup unit: per leg per server name, the host's.
     *  - `"config.models_swapped"` — `Runner.swapDeps` replaced the model catalog generation
     *    (zero-restart model switching; the `models` / `tiers` seats of the swappable set). `detail:
     *    { models, tiers, occurrenceId }` — key COUNTS only, never the catalog itself. In-flight tasks
     *    finish on the models they resolved at prepare (natural snapshot); every later prepare resolves
     *    against the new generation. A failed swap (illegal tier binding, a refused candidate) throws
     *    atomically and mints nothing. The verb carries no run and no session and the payload is a pair
     *    of counts, so the uuidv7 `occurrenceId` minted at the announcement is what tells two swaps of the
     *    same shape apart. Dedup key: (code, occurrenceId).
     *  - `"config.read_face_swapped"` — `Runner.swapDeps` replaced the deployment's read-face
     *    declaration (`RunnerDeps.readFace`, the `readFace` seat of the swappable set) without a
     *    restart. `detail: { from, to, occurrenceId }` — the seat's previous and new value, each
     *    `"open"` | `"roots"` | `null` (`null` = no declaration; the engine default `roots` applies).
     *    The SEAT is announced, not a leg's resolved face: resolution (task seat, governance, the
     *    read-only mount) still runs per prepare. In-flight legs keep the face they prepared under;
     *    every later prepare resolves against the new declaration. A candidate outside the value gate
     *    (`assertContainmentFaceValue`) throws atomically and mints nothing. Own `occurrenceId` for the same
     *    reason as the catalog line (no run, no session, a two-word payload). Dedup key: (code, occurrenceId).
     *  - `"config.write_face_swapped"` — the same line for the WRITE seat (`RunnerDeps.writeFace`),
     *    same payload, same laws. A SEPARATE code rather than a face field on the line above: the audience
     *    table and every consumer table are keyed by code.
     *  - `"config.read_deny_swapped"` — `Runner.swapDeps` replaced the deployment's read-deny declaration
     *    (any of `readDenyPatterns` / `readDenyBuiltinTiers` / `readDenyBuiltinExclude`) without a restart;
     *    ONE line per swap. `detail: { seats, patterns, tiers, exclude, occurrenceId }` — the seats the swap
     *    named, then the declaration in force after it (additions and removals as counts, the tier selection
     *    by name; `null` = no declaration). In-flight legs keep the deny face they prepared under; every later
     *    prepare compiles against the new declaration. A candidate outside the seats' gates throws atomically
     *    and mints nothing. Dedup key: (code, occurrenceId).
     *  - `"config.context_settings_swapped"` — `Runner.swapDeps` replaced the deployment's per-model
     *    context-window overlay (`RunnerDeps.contextSettings`, the `contextSettings` seat of the swappable
     *    set) without a restart. Unlike every other swappable seat this one is read LIVE: a running leg
     *    measures its next boundary (the compaction trigger, the sanitize clamp, the request guard, the
     *    disclosed `contextUsage`) against the new windows, while its routing stays on the generation it
     *    prepared under. `detail: { words, declared, previousDeclared, occurrenceId }` — the declared model
     *    WORDS (the first eight) and the two counts, never the numbers: an overlay's values are deployment
     *    configuration and this line is an operator breadcrumb, not a config dump. A candidate outside the
     *    value gate (`assertContextSettingsValue`) throws atomically and mints nothing. Own `occurrenceId`
     *    for the same reason as the catalog line. Dedup key: (code, occurrenceId).
     *  - `"route.fallback_to_primary"` (key↔URL pairing, `src/brain/route-adjudicator.ts`) — a
     *    DERIVED-leg model (role/tier/system-default resolution, never a caller-explicit one) failed
     *    the pairing pre-flight and the seat fell back to the primary model instead of sinking the
     *    task; the notice is the loud half of that swap.
     *    `detail: { seat, from, to, cause, fixHint, sessionId?, runId? }` — `cause` is the refusal code
     *    (a `RouteRefusalCode` word, the verdict's own); `sessionId` (#433, additive)
     *    rides when the adjudicating seat knows its session, and `runId` (#499, additive) names the
     *    INVOCATION whose seats were being resolved — both correlation keys only, the audience stays
     *    `"operator"` (see the registry's entitlement-not-routability note), and `runId` is what tells one
     *    task falling back twice apart from two tasks falling back once each.
     *    Explicitly-named models never mint this: they refuse at the brain's request gate instead.
     *  - `"route.classifier_unroutable"` (same pre-flight, the other POSTURE) — the derived classifier seat
     *    failed the same pairing pre-flight while the deployment had declared NO fallback
     *    (`RunnerDeps.autoMode.classifierFallback: "none"`), so the seat was KEPT and its calls are refused at
     *    the request gate rather than served by the primary model. The line is why "no swap" is still not "no
     *    word". `detail: { seat, from, cause, fixHint, action: "refused", sessionId?, runId? }` — the twin
     *    above's keys MINUS `to` (nothing moved, and a `to` naming a model that was not used is the one lie a
     *    consumer cannot detect); `action` is the discriminator, so a reader never infers the posture from that
     *    absence. Audience `operator`, same entitlement reading as its twin. Dedup key: (code, seat) per run.
     *  - `"classifier.fallback"` (the classifier's candidate ladder, `src/core/runner/classifier-ladder.ts`)
     *    — a gated call's classification did NOT rule on the classifier seat and was re-asked on the model
     *    behind it. Sibling of `route.fallback_to_primary` one layer later: that one fires when the derived
     *    model is not ROUTABLE at all, this one when it is routable and still does not answer.
     *    `detail: { from, to, cause, sessionId?, runId? }` — `cause` is the `AutoModeFallbackCause` word, the
     *    two ids are the rungs. Dedup key: (code, from, cause) PER RUN; a second CAUSE on the same rung is
     *    its own line.
     *  - `"classifier.role_defaulted"` (`src/core/runner/classifier-ladder.ts`, the seat resolver) — no
     *    `classifier` role names a model, so another model STANDS IN for the seat. `detail: { model, reason,
     *    thinkingOffSpelled, sessionId?, runId? }` — `reason` ∈ `tier_below_main` (the nearest directly-bound
     *    tier below the run's main model) / `tier_table` (the static `flash` row, a main bound to no tier) /
     *    `role_fallback` (the `summarize → default` chain, no tiers) / `main_only` (the main model is the only
     *    model there is) / `role_unresolvable` (the role named a model the catalog cannot resolve; the main
     *    stands in); `thinkingOffSpelled` = whether that model's wire can carry a disable key. Minted
     *    AFTER the route pre-flight, so it names the seat actually used. Once per run.
     *  - `"classifier.contract_broken"` (same file, the round observer) — ONE model answered
     *    `AUTO_MODE_CLASSIFIER_CONTRACT_BROKEN_AFTER` CONSECUTIVE classifications without a verdict (each already
     *    re-asked once by the leg). `detail: { model, rung, consecutive, sessionId?, runId? }` — `rung` is the
     *    ladder position (0 the seat, 1 the model behind it), since two rungs may share an id. A ruling from
     *    that rung resets its count; an `unavailable` round neither counts nor resets. Once per run per rung,
     *    and by construction before the denial limit's third block hands the user its card.
     *  - `"classifier.unavailable"` (`src/core/runner/classifier-ladder.ts`, `classifierUnavailableNotice`; minted by
     *    both classifier stations — the leg's gate and an ancestor's frozen classifier in the policy fold) — a
     *    classification could NOT RULE (the classifier leg failed, or its per-level budget
     *    `RunnerDeps.autoMode.timeoutMs` ran out) and says where the call went, read off the station's one approval
     *    route (`approvalRouteOf`): `disposition: "asked"` = on to the live approver, or on to the durable park — the
     *    sentence names which route, never whether the park then commits (the handed-on ask carries `requiresRealApproval`, so no automatic lane answers
     *    it; a declared departure from CC, which refuses either way), `"denied"` = refused with the classifier's own
     *    sentence (route `none`: no approver and no park facility).
     *    `detail: { toolName, toolCallId, cause, disposition, sessionId?, runId? }` — `cause` is the
     *    `AutoModeUnavailableCause` word. Once per classification that could not answer (a call judged by an ancestor's
     *    classifier and the leg's own can carry two); not minted while the call is being aborted (a halt, or the run's end). Audience
     *    `user`: on a lane with no approval face this line is the only place the user hears why the call did not
     *    run. It is not a block — the denial limit does not count it.
     *  - `"route.credential_origin_mismatch"` (key↔URL pairing, `src/brain/route-adjudicator.ts`; announced by
     *    `src/core/runner/credential-origin-seat.ts`) — a request was REFUSED, nothing sent, because the per-model
     *    credential it would carry is bound to another origin: the law binds a per-model credential to the origin
     *    (scheme + host + port) of the first request sent carrying it, so an entry whose `baseUrl` moved to another
     *    host while its credential stayed the same is refused (same-origin path moves are not). The request's own
     *    failure carries the same code (`[invalid_request] route.credential_origin_mismatch: …`). Minted only for a
     *    request the gate refused (`routeRefusalError`): a pre-flight or read-face judgment reports nothing, and a derived
     *    seat refused this way falls back as `route.fallback_to_primary` names. Audience `user` — the person whose request
     *    failed hears why; the operator hears it through the sink. `detail: { modelId, boundOrigin, requestOrigin,
     *    sessionId, runId }` — the two origins in display spelling, never a credential or a digest of one. Once per
     *    run per (modelId, boundOrigin, requestOrigin).
     *  - `"route.response_model_mismatch"` — the provider answered a turn naming a model that is NOT the id
     *    the request was sent with (an alias, a pool or a gateway-side substitution looks exactly like this),
     *    so it is advisory, never a refusal: the engine keeps asking for the catalog's id and reports the
     *    served name beside it (`turn_end.responseModel` / `TaskResult.responseModel`). ONCE PER LEG PER
     *    (requested, reported) PAIR — a catalog fact, not a turn fact. Audience `operator`;
     *    `detail: { requested, reported, sessionId, runId }`. Absent ⇒ the provider echoed the requested id
     *    or named nothing — one reading, not two.
     *  - `"route.tool_call_id_folded"` (#1207) — the provider named a tool call with an id outside the shape the
     *    engine keys calls under (visible ASCII, 1–128, no `~`), so the brain keyed it under a deterministic fold
     *    (`ToolCall.id`) and keeps the provider's spelling beside it (`ToolCall.wireId`), handed back only to the
     *    provider; or (#1234) it gave two calls in ONE response the same id, so the brain renamed the later ones on
     *    both faces (every provider requires a request's call ids to be unique). Advisory: every call runs. ONCE PER
     *    RUN, on the first folded call — a gateway fact, not a call fact. Audience `operator`;
     *    `detail: { toolCallId, rawLength?, reason, sessionId, runId }` — `toolCallId` the engine id, `reason`
     *    `"charset"` (a control byte, a space, a non-ASCII unit or `~`), `"length"` (admitted characters, over 128)
     *    or `"duplicate"` (a renamed later call); `rawLength` present for the two shape reasons only; never the raw
     *    bytes. Absent ⇒ no call of the run was folded.
     *  - `"route.finish_reason_unknown"` (#1457) — the provider ended a response with a stream-end word (an
     *    OpenAI-family `finish_reason`) the brain does not read. The turn is kept — its text and the stop its content
     *    implies — and the word rides `AssistantMessage.finishReasonUnknown` (and, when the run completes on that turn,
     *    `terminal.detail`); the notice is what keeps the keep from being a silent clean stop. ONCE PER LEG PER WORD.
     *    Audience `user` (the person reading the answer judges whether it is whole); `detail: { finishReason,
     *    sessionId, runId }` — the word in the provider echo shape, `?` when it had bytes outside it.
     *  - `"route.stream_end_ignored"` (#1457) — the provider's stream sent more after its first terminal statement, on
     *    either wire: a later ending (OpenAI-family `finish_reason`, Anthropic `stop_reason`), error frame or content (a
     *    delta, a block, a new message). The first ending decided the turn and nothing after it was added
     *    (`AssistantMessage.streamEndIgnored`). ONCE PER LEG PER (statement, word). Audience `user`; `detail: {
     *    statement: "ending" | "error" | "content", word, frames, sessionId, runId }` — `word` the first such frame's
     *    (for content, its kind), `frames` how many came.
     *  - `"route.extra_body_reserved_dropped"` (#1520) — a request's `Model.extraBody` carried keys the wire adapter
     *    sets itself (`temperature`, `max_tokens`, `stop`, the thinking-enable keys, `messages`, `tools`, …), so the
     *    adapter stripped them and the request went out with the engine's value (or without the key); core wins, and
     *    that is said. One reading at the one strip site — the model spec of the task, a model spec a host brain
     *    rewrote for a single request, and the leg's internal calls (the compaction summary, mid-task or at the end of
     *    the task, and a side query fired from a tool) are announced alike. Advisory: the turn
     *    runs. ONCE PER LEG PER (api, key set) — a deployment fact, not a request fact. Audience `operator`;
     *    `detail: { keys, api, modelId, sessionId, runId }` — `keys` the key NAMES in `extraBody`'s order (never a
     *    value), `api` the wire the adapter speaks, `modelId` the catalog id the request was sent as.
     *  - `"context.tool_turn_repaired"` (#1224) — a provider request was built from a transcript that breaks the
     *    tool-turn law, and the request view restored it (`engine/llm/tool-turns.ts`; the transcript is never
     *    rewritten): a block after an assistant's tool calls moved ahead of them (`trailing_text`), a result recorded
     *    later moved up behind its call (`late_result`), a call with no result closed with the reconcile family's
     *    interrupted result (`missing_result`), a second result recorded for one call folded into the first
     *    (`duplicate_result`; an interrupted close beside a recorded result is left out, no recorded result is), a second
     *    call with an id its message already used left out — the message keeps the first, whose result carries every
     *    result recorded for the id (`duplicate_call`, #1241), a result no earlier call answers left out (`orphan_result`,
     *    #1242: its call is gone or it was recorded ahead of it; the one repair of the view as a whole, its ids once
     *    each in transcript order). Advisory:
     *    the run proceeds on the repaired request. Audience `operator`; `detail: { repairs: [{ form, toolCallIds }],
     *    sessionId, runId }`. ONCE PER RUN PER (form, calls) — the same broken turn is repaired on every request of
     *    the run and announced on the first. Absent ⇒ every request of the run already had the structure.
     *  - `"task.tool_turn_incomplete"` (#1230) — the person's line for a tool turn the model was handed incomplete, beside
     *    the note the model itself reads. `detail.cause` (closed `TOOL_TURN_INCOMPLETE_CAUSES`): `truncated` — calls whose
     *    arguments were cut before they parsed, dropped (they did not run); `unnamed` — calls that arrived with no tool
     *    name, dropped the same way; `bad_frame` — streamed response frames
     *    that could not be read, dropped (the response may be missing content); `missing_result_synthesized` — a
     *    call of this run with no recorded result, which the request view closed as interrupted, outcome unknown (the
     *    `missing_result` repair above; a call that went unanswered before the run began is the transcript's history and
     *    is on that operator line only). Audience `"user"`; `detail: { cause, count, outcome?, toolCallIds?, sessionId, runId }` —
     *    `count` the calls (or frames) of that cause in the turn or request that first showed it; `outcome` (#1640) on
     *    `truncated` / `unnamed` only, and only where the turn's own message states it: `"terminal"` — the lane failed
     *    the run on those calls (its dropped-calls account is on the failed turn; the terminal's `detail.kind` is
     *    `model_output_invalid`), the sentence says the run ended; `"reissued"` — the turn goes on by the loop's turn
     *    rules (it carries a tool call, or stopped to call a tool, or stopped at the output cap) and the next request
     *    asks the model to re-issue them. ABSENT is a fact, not missing data: the message does not say what became of the
     *    run (a refusal, an abort, a completed turn with nothing to run, a torn stream the loop may resume), and the
     *    sentence then stops at "so they did not run". Once for the cause that first showed it; `toolCallIds` on
     *    `missing_result_synthesized` only, each id in the provider echo shape (a stored id outside it is not shown;
     *    `count` still counts it). Closed-set words, a count and call ids only: never a tool name, an argument or the
     *    note's text. ONCE PER RUN PER CAUSE. Absent ⇒ nothing of that cause reached the model.
     *  - `"config.read_face_deployment_clamped"` (#237) — a deployment-wide `readFace: "open"` is not
     *    in force beside a read-only (verifier) mount: it clamps to "roots" without throwing
     *    (stricter-wins; the clamp verdict stands, only its occurrence was undisclosed). Announced
     *    once per `onNotice` SINK at the prepare station (the verifier lane would otherwise repeat it
     *    every spawn; the sink — not the deps object — is the unit because the Runner rebuilds its
     *    deps and `runTask` builds a Runner per call), unwired console arm once per process;
     *    a library-direct `createHandsToolkit` mount announces at toolkit creation (one per mount,
     *    through the band-local `HandsToolkitOptions.onNotice` seat, absent ⇒ `console.warn`);
     *    `detail: { seat, declared, inForce, cause }`.
     *  - `"config.write_face_deployment_clamped"` — the write seat's twin of the line above, with the
     *    same stations, the same once-per-sink ledger (its OWN ledger row) and the same detail shape. The
     *    cause differs in prose only: that mount carries no write faces at all.
     *  - `"config.tool_model_gate_removed"` (design/277) — the model gate trimmed default-mounted
     *    scaffold entries from a task's roster. One notice per (model, class), de-duplicated once per
     *    `onNotice` SINK on the (modelId, class, canonical sorted removed-name set) line (same unit
     *    as `config.read_face_deployment_clamped`: two deployments hosted in one process each hear
     *    their own trim; unwired console arm once per process) — same class with a different removal
     *    shape is a distinct fact and announces again;
     *    `detail: { modelId, gateClass, removed, restore }` (`restore` names the three valves).
     *  - `"config.tool_model_gate_unknown_class"` (design/277) — a `ToolSpec.modelGate` tag names a
     *    class the merged gate table has no row for: the tag is inert (fail-open — the tool stays
     *    mounted) and this is its loud half (a tag typo must not silently become "never gated" with
     *    nobody told). Once per `onNotice` sink per class (console arm once per process);
     *    `detail: { gateClass }`.
     *  - `"config.tool_model_gate_env_invalid"` (design/277, the NOTICE dialect of the same fact the
     *    refusal code carries) — `SEMA_TOOL_MODEL_GATE` holds a value outside `on|1|true|off|0|false`
     *    in a seat where it is NOT in force (nothing this prepare would gate): announced once per
     *    `onNotice` sink per value (console arm once per process) instead of lying in wait; where the
     *    value IS in force the prepare refuses with the same code as `TaskResult.errorCode` (one
     *    fact, one code, two loudness dialects); `detail: { raw }`.
     *  - `"config.artifact_host_invalid"` — `RunnerDeps.artifactHost` is wired but its `capabilities()`
     *    declaration fails one of the seat's invariants (see `ArtifactHostCapabilities`), or its `onMount`
     *    threw: the `Artifact` tool is not mounted for the task and this is the loud half (an absent seat
     *    is silent; a broken one must not look the same). Once per prepared leg;
     *    `detail: { problem, sessionId, runId }`.
     *  - `"config.execution_env_capability_invalid"` — a remote env DECLARES a boolean capability key
     *    (`isolation`, `suspendable`, `canonicalPathAuthoritative`) with a non-boolean value; ABSENCE is a
     *    supported reading of each and stays silent. Once per key per run; `detail: { key, valueType }`.
     *  - `"config.secret_env_scrubbed"` (#1081) — the D1 fail-closed `scrub` policy, or the deployment's
     *    own credentials (`declareWithheldEnv`: a declared name, or a value equal to a declared or engine-held
     *    credential value), withheld one or more keys from a spawned child's environment (a tool's shell, an
     *    oracle gate, or a language server).
     *    Once per PROCESS, shared by every scrub site ({@link import("./secret-env.js").announceSecretEnvScrub});
     *    `detail: { names, ownCredentials? }` — every withheld key's name, never a value; `ownCredentials`, present
     *    only when one was withheld, the subset withheld as own credentials, by name or by value (under any policy,
     *    `"all"` included).
     *  - `"config.credential_not_withheld"` (#1816) — a credential value the engine was handed (a brain's key or credential
     *    header, a model entry's or a peer's credential header, a consolidation seat's key) is no credential (a placeholder
     *    such as `EMPTY`, fewer than eight characters, one character repeated): it is used as given, but a variable holding
     *    the same string is not withheld from a spawned child. Once per PROCESS per value
     *    ({@link import("./secret-env.js").holdOwnCredentialValues}); `detail: { reason }` — the kind, never the value. Delivered
     *    to `RunnerDeps.onNotice` from the `new Runner` receipt; the intakes with no structured seat (a brain's construction,
     *    a request's route reading, a peer's dial, a directly built distiller seat) print its line on `console.warn`.
     *  - `"config.permission_mode_unavailable"` — the permission mode a task requested is not the mode the leg runs
     *    under: the deployment denies it (`by: "operator_deny"` — `RuntimeCaps.bypassPermissions` / `autoMode` is
     *    `false`), a requested `auto` has no classifier face (`by: "face_absent"` — neither the leg's deps nor its chain carry
     *    one), or a child's declaration was folded under its parent's ceiling (`by: "inherited_ceiling"`). A CHILD's fold, of
     *    any of the three, is minted on the PARENT's stream too (`childSessionId` beside the parent's `sessionId` — a child's
     *    own manifest is not shown to the person). The leg runs in `effective` (always `default` for the first two arms),
     *    announced once per leg and once per steer that folds; `detail: { requested, effective, by, sessionId, runId,
     *    childSessionId? }` — `requested` is the word the fold folded (a child that requested nothing folds the word it
     *    inherited; the sentence says "(inherited)"). Audience `user`.
     *  - `"config.permission_mode_invalid"`, `"config.spec_retired_key"` and `"config.shell_gate_value_retired"`
     *    are ERROR codes, not notices (named here only to keep the mode family's spellings
     *    in one place): a `permissionMode` word outside the closed set (on the spec or on a checkpoint row), a spec
     *    carrying a retired mode key (`autoModeRequested`, `approverPosture`) and a `shellGate` value other than
     *    `"always"`. Each is a door refusal: the
     *    code rides the thrown error (the run fails, or the resume is refused) and never crosses
     *    {@link deliverEngineNotice}, so all three are deliberately absent from {@link ENGINE_NOTICE_CODES} /
     *    {@link NOTICE_AUDIENCE}, like `config.deps_retired_key`. A consumer diffing its own table against the
     *    catalog must not add a row for them; it reads them on the error's `code`.
     *  - `"permission.ask_released_by_mode"` — the `bypassPermissions` mode released a surviving ask instead of
     *    putting it to anyone; once per (mode, askClass) per run, `detail: { mode, askClass, origin?, mandate?, count,
     *    firstToolCallId, sessionId, runId }` — `count` is how many releases of that pair the run had made when the
     *    notice was minted (the first). The per-call record is `tool_end.gate.disposition.released`. Audience `user`.
     *  - `"permission.read_deny_unapplied"` — the mode released a call whose reads the shell read station could not
     *    establish (`unestablishable` / `declined`) while a read-deny table is in force: no row of the table was applied
     *    to what it reads (C-R124, "released and disclosed"). Every such call, `detail: { mode, toolCallId, tiers, rows,
     *    rowCount, sessionId, runId }` (`rows` = the first eight non-built-in rows). Audience `user`.
     *  - `"permission.rule_store_unavailable"` — the person's persisted permission rules could not be read (a read
     *    failure, a corrupt or unreadable store, a read that did not answer in time), so that read's rows were SKIPPED:
     *    none of their allow, ask or deny rows took part in the decisions it served. At `"call"` the call asks a person
     *    meanwhile (a deny row among the rows nobody could read may forbid it; the class is `rule_unreadable`,
     *    which `bypassPermissions` keeps and a headless run refuses); at `"prepare"` the run's path tables lack the rows
     *    for the whole run. `detail.at` names the read that found it first: `"prepare"` (the path rows the run's file and
     *    shell checks take at run start — the next run reads them again) or `"call"` (one call's own rule check — every
     *    later call reads again, so the rows apply as soon as a read succeeds). Once per run, whichever read fails
     *    first; the trace keeps every read's line (`permission.rule_store_unreadable`).
     *    `detail: { at, sessionId, runId }`. Audience `user`.
     *  - `"permission.rule_row_dropped"` — the person's store served project-scoped rows whose scope root is not a
     *    complete absolute path (only a foreign writer or a hand edit makes one; every engine write face refuses it): they
     *    take no part in this run's checks — a deny or ask row among them protects nothing — until they are rewritten or
     *    removed. Once per run, by whichever read met them first. `detail: { rows, count, sessionId, runId }` — `rows`
     *    the first eight `{ rule, scopeRoot }`, `count` all of them; `rule` is the rule text as the store served it, through the same reading
     *    as `permission.allowed_tool_not_granted`'s `entries[].entry` (the original, not a spelling of it, when it is at most 1 024 code points;
     *    a longer one is cut to 1 024 plus the truncation mark, 1 025 in all), `scopeRoot` neutralized and bounded. Audience `user`.
     *  - `"config.session_rules_earlier_grammar"` — a session's stored permission rules (`RunnerDeps.sessionPolicyStore`) hold
     *    entries the rule grammar they were written in reads as nothing: an allow entry admits nothing, and a `toolDeny` entry
     *    this engine's grammar reads as no rule makes those rules refuse every call until they are rewritten. The message names
     *    the session, the bucket, the entries (the first five, the rest counted), the reader's word and the way out. Audience
     *    `user`. Once per distinct entry set per bucket per rule layer (the run's own rules, each ancestor's inherited ones).
     *    `detail: { bucket, storedGrammar, entries: [{ entry, reader }], rewrite: { by, stampsGrammar }, rulesSessionId,
     *    sessionId }` — `entries` every one, `entry` through the same reading as `permission.allowed_tool_not_granted`'s;
     *    `reader` ∈ `no_rule` / `named_no_tool`; `rewrite.by` = `operator_write_without_rev`; `rulesSessionId` the session
     *    whose rules they are (an ancestor's on a delegated child), `sessionId` the run that read them.
     *  - `"config.durable_gate_unavailable"` — a durable-approval MANDATE is in force on a leg with NO
     *    checkpoint store: the per-principal `forceDurableGate` entitlement, or the task's own
     *    `durableApproval` opt-in with no live approver seat (`durableMandateSourceOf`, the predicate the
     *    leg's children inherit). A mandate with no park facility cannot be honored: no ask of the leg can suspend, so each one resolves on the LIVE
     *    chain — a live approver / question face answers in-stream, and with none the ask is denied
     *    fail-closed — and NO durable approval record is written. The ask routing itself is not
     *    changed by the notice (a store-less leg never could park); the notice is its loud half,
     *    disclosed before the first ask of the leg. Audience
     *    `"user"` (the person whose asks will not be recorded is entitled to that; the operator hears
     *    it through the sink as always). Once per prepared task leg — a resume or a delegated child
     *    is its own leg and says so again; `detail: { sessionId, runId, principal?, cause, source,
     *    liveApprover, liveQuestionFace }` — `source` is `"force_durable_gate"` or `"durable_approval"`
     *    (which mandate is unhonored; the recovery differs); `cause` is `"no_deployment_store"` (the deployment wired none) or
     *    `"task_store_disabled"` (`TaskSpec.checkpointStore: "disabled"`, the per-run off switch, on a leg
     *    whose deployment may well have a store: the fix is the task's); `liveApprover` /
     *    `liveQuestionFace`: whether a live seat will EFFECTIVELY answer the leg's permission asks /
     *    questions in-stream (a delegated child under an inherited content mandate reads `false`
     *    even with a face wired — the marker withholds the question), or the fail-closed deny will.
     *
     *  - `"task.user_steer_undrained"` / `"task.user_followup_undrained"` (#259) — user steers /
     *    follow-ups whose receipts said "queued" were still in their queue at agent_end: the run
     *    ended before any turn could drain them. One notice PER family (a consumer routing on `code`
     *    alone must not mistake a stranded follow-up for a stranded steer — same split as the settled
     *    frame's two count keys). They are NOT redelivered (a steer aimed at a finished run must not
     *    fire at the next one — unlike ENGINE notes, which pend per session); the loud half of the
     *    #257 contract's "accepted = enqueued, not consumed" sentence;
     *    `detail: { <countKey>, cause, taskId?, sessionId?, runId? }` (the `code` and the `countKey` of each family are the
     *    table {@link USER_INPUT_UNDRAINED_FAMILIES}, keyed by the lane the input was queued on — `injectionLaneOf` — and the
     *    minter writes exactly those; `cause` on both rows, closed `USER_INPUT_UNDRAINED_CAUSES`: `"leg_end"` — this engine's arm, the
     *    leg ended with it still owed; `"crashed"` — minted by the SERVING LAYER, not this engine, before the
     *    session's next run, for inputs it had accepted whose consumption was not confirmed before the process died: their queue was the
     *    serving layer's, so only it can count them, and it picks the family by the LANE the input was queued on (a `later`
     *    steer rides the follow-up lane). One code per family for both arms, so a consumer de-duplicating on `code` keeps
     *    one row; `sessionId` = #433's routing half for these two `"user"`-audience rows; `runId` = #499's
     *    per-run identity, the key this account is actually about — see
     *    {@link undrainedUserInputNotices}; all omitted when the caller has none, never fabricated). A delegated child
     *    leg is announced on its PARENT's session as well — the direct parent only — as a second row per family,
     *    `detail: { <countKey>, cause, taskId?, sessionId: <the parent's>, childSessionId }` (no `runId`: nothing
     *    carries the parent's), message tail `(a child leg, <childSessionId>)`; `childSessionId` present = a child reported it.
     *    Per-run, at most once per family (the terminal sweep is a single site) — which is why
     *    `taskId`/`sessionId` cannot key it: both answer the same value for two runs of one session.
     *    ① The family fires on the INTERRUPT path too, `abort()` being barred from emptying either queue
     *    before agent_end can count it. ② On a DURABLE PARK the verdict is
     *    settled at the run tail instead of at agent_end, because the tail first carries the still-
     *    undelivered input onto the committed checkpoint's parked-steer queue (the resume delivers it)
     *    — what is announced is the remainder that could NOT be carried, so a fully-migrated park says
     *    nothing at all and a queue-full / no-longer-pending row still says exactly what was lost.
     *
     *  - `"task.turn_interrupted"` (design/373) — a running turn was ACTUALLY CUT at a manufactured
     *    boundary: the in-flight provider stream / tool batch was aborted (finished tool calls keep
     *    their real results, never-started ones settle as paired interrupted results, the interruption
     *    marker lands). **TWO mint lanes, one code** — the CUT is lane-independent, so it is one fact
     *    with one spelling, and `detail.cause` is the arm discriminator (a consumer that must tell
     *    them apart reads that key; one that only cares "a turn was cut" reads neither):
     *      · the STEER-NOW lane (design/373) — a caller-provenance steer with `priority: "now"` cut
     *        the turn and the run CONTINUES with the steer at the queue head.
     *        `detail: { inputId, sessionId, runId, actorId?, taskId? }`, NO `cause` key — `inputId` is
     *        the steer's own correlation key (the `human_input` frame's id) and `actorId` the caller's
     *        asserted identity when one rode the steer.
     *      · the BARE-HALT lane (#504, {@link TaskStream.halt}) — the person's Esc-form stop cut the
     *        turn and the run does NOT continue: it collects to a clean, resumable
     *        `status:"completed"` ending carrying {@link TaskResult.haltedByUser}.
     *        `detail: { cause: "user_halt", sessionId, runId, taskId? }` — no `inputId` and no
     *        `actorId`, because no text entered the model and the verb carries no caller identity. A
     *        consumer keying `detail.inputId` off this row must treat it as ABSENT on this lane.
     *        Fence tension: this notice asserts THE CUT only — when the cut
     *        raced an approval park whose commit was already in flight, the gate's durable leg may
     *        still collect to `suspended` (park wins, row redeemable, the park's abort owns the
     *        ending, no `haltedByUser`, plus a `task.halt_unconsumed`); the notice stands beside that
     *        terminal without contradiction, because a seat really was cut — `haltedByUser` answers
     *        the other question (was the halt CONSUMED), see that seat.
     *    In both lanes: one notice per REAL cut (a `now` that found nothing in flight or whose frame
     *    already rode the imminent boundary, and a halt with nothing in flight, announce nothing — no
     *    false interrupt claims); `runId` is the INVOCATION that was cut; every cut turn counts toward
     *    `maxTurns`, so an interrupt storm's cost is bounded and each of its cuts is on the record.
     *    Audience `"user"`: the person who forced the boundary is the one entitled to see that it landed.
     *    RETIRED here (BREAKING, named): `"task.injection_priority_unimplemented"` — the ladder is
     *    implemented, so the unhonored-knob disclosure it carried has no referent; consumers must
     *    judge ladder support by VERSION, never by that code's absence.
     *
     *  - `"task.halt_unconsumed"` — the user's stop verb ({@link TaskStream.halt}) was ANSWERED (not
     *    refused) but the run's ending was not its doing: the run's own ending stands and the result will
     *    NOT carry `haltedByUser` for it (the seat signs consumption, never acceptance). TWO mint points,
     *    one fact, one spelling, disjoint: the VERB arm — the halt arrived while the run's ending was already
     *    owned by its own abort, or after the loop's end (no turn cut, no boundary stopped — a compaction pass the
     *    run's tail was in is still cut, and says so on its own `compaction_outcome{mooted, reason:"cancelled"}`;
     *    once per such verb call, and only while no halt
     *    has been ACCEPTED on the run — after one was, the settle arm alone speaks for the run);
     *    the SETTLE arm — the halt was ACCEPTED (it may even have cut a live seat: `{turnCut:true}` +
     *    `task.turn_interrupted`) but the run ended for its own reason before the machinery consumed it —
     *    no interrupt settlement took the halt's cut as its cause and no boundary consult stopped the loop
     *    for it (a brain minting its own `aborted` final beside a late halt, a natural completion the halt
     *    arrived too late to prevent, an approval park whose commit won the race and whose abort owned the
     *    ending); once per such run, at the result's settlement. Either way the notice is the halt's trace
     *    beside the verb receipt. `detail: { sessionId, runId, taskId? }` — `runId` is the run whose ending
     *    the halt failed to claim. Audience `"user"`. A REFUSED halt (typed `steering.not_running` throw)
     *    mints nothing: the refusal itself is the loud answer.
     *
     *  - `"task.late_approval"` (design/384 slice 1) — a synchronous ask approval was NOT CONSUMED:
     *    a run or turn interrupt released the wait (the `resolveAsk` race arm detached the approver's
     *    promise; an unconsumed resolve is the approver releasing its wait, never a verdict), so the
     *    tool did NOT run and the approval was not honored. The notice asserts non-consumption ONLY —
     *    never an arrival order: an approval and an abort settling close together read as the abort
     *    (the resolver's documented one-directional residual — the engine never claims a person
     *    decided something it cannot show they did, and it equally never claims which came first).
     *    Minted only for a value that reads as an APPROVAL — an unconsumed
     *    deny/refusal is an ordinary release and leaves no trace, and an unconsumed REJECTION goes to
     *    `onError(phase:"hook")` instead (a failing callback, not an answer). One notice per
     *    unconsumed approval; `detail: { toolName, toolCallId, sessionId, runId, taskId? }`. Audience `"user"`
     *    (whoever answered the card is the one entitled to hear the answer ran nothing); a host may
     *    forward it on its own wire.
     *
     *  - `"task.interrupt_unconsumed"` — a HARD stop ({@link TaskStream.interrupt}, the task's `signal`,
     *    the walltime or the turns limit) landed AFTER the run's last turn had already given its answer
     *    (`stopReason:"stop"`, not partial, and no steer / follow-up of the person's still pending — a
     *    queued continuation makes that "stop" work in progress, and a stop that cuts it stays the
     *    ordinary abort with its undrained-input account) and before the loop's end — what it aborted was the run's
     *    tail (a final-boundary compaction is mooted, a stop hook's wait is cut), never its work. The
     *    answer stands: the run ends `completed` with it as `result`, no `[Request interrupted by user]`
     *    marker is written and no `limits.*` terminal is minted for the axis that fired; this notice is
     *    the stop's only trace. Minted once per such run, after the terminal arbitration (so it never
     *    claims a completed ending the terminal did not give — a budget ceiling crossed by the last
     *    turn's own spend still fails the run and mints nothing here); `detail: { sessionId, runId,
     *    taskId?, origin }` with `origin` ∈ `"interrupt"` (the verb) | `"walltime"` | `"turns"` |
     *    `"signal"` (every other run-level abort: the task's own `signal`, a parent's cancel, a
     *    `destroy()`, a refused preempt — the leg names the three it can tell apart and files the rest
     *    here). Audience `"user"`. A stop that arrives AFTER the loop's end but before `done` aborted no
     *    live loop and mints nothing here: it is answered, the end-of-task compaction pass does not run past
     *    it (cut in flight — `compaction_outcome{mooted, reason:"cancelled"}` — or never started), and the
     *    loop's ending stands as it was. A stop that arrives once the result is out is refused typed (`steering.not_running`, every
     *    verb alike): the refusal itself is the loud answer.
     *
     *  - `"approval.read_root_granted"` / `"approval.read_root_grant_rejected"` — an approver's `readRootGrant`
     *    (`AskOutcome`). GRANTED: it equalled the card's `readRootCandidate.dir` beside `allow: true`; this run's READ
     *    directories now include it (never the write roots; this run only); `detail: { dir, covers?, root, toolName,
     *    toolCallId, sessionId, runId, taskId? }`, `root` = the canonical spelling held. REJECTED: nothing changed,
     *    the verdict stands; `detail: { reason, offered?, requested?, …same ids }`, `reason` ∈ `on_refusal` |
     *    `not_a_string` | `no_candidate` | `mismatch` | `unresolvable` | `resolves_elsewhere` | `admits_artifact_store`
     *    (the directory contains the wired local artifact store, lies inside it or is it) | `no_read_roots`. Once per
     *    answered ask. Audience `"user"`.
     *
     *  - `"checkpoint.execution_outcome_unrecorded"` — the resumed leg's pending-call resolver disposed
     *    the decided action (executed it, delivered the refusal, or vetoed the person's yes) and filed
     *    the frame's record through `CheckpointStore.recordExecutionOutcome`, and the store answered
     *    `not_resolved` or `absent` — the row was reopened, expired or removed under the leg by another
     *    actor, so the row now reads `unknown` on its execution axis (`executionVerdict`). The
     *    execution result is NOT changed by the answer (the record is an account of what ran, never a
     *    gate on it); the notice is the loud half. Once per such resolution;
     *    `detail: { sessionId, runId, scope, checkpointId?, word }` (`word` = the store's answer; the
     *    secret token never rides a notice). A DIFFERENT record already on file is not this notice — it
     *    is the `checkpoint.execution_outcome_conflict` throw (an engine defect, propagated).
     *
     *  - `"classifier.denial_limit"` (#548, CC 2.1.250 `FO`/"too many classifier denials in headless
     *    mode") — the auto-mode classifier's DENIAL LIMIT was reached (3 consecutive blocks, or 20 in the
     *    run; `RunnerDeps.autoMode.denialLimit`) and the fallback ask it turns into was never JUDGED by
     *    anyone. Two lanes reach it. SYNCHRONOUS: no approver to go to — none wired, a blanket
     *    `onAsk:"allow"` (refused for a real-approval ask), or an approver that reported unavailable with
     *    no durable park to take it. DURABLE: the fallback PARKED, and the decide that resolved the row
     *    reported a settlement the nobody-judged table reads as such (`hostDecision.decidedBy:"policy"` ⇒
     *    `policy_refused`) — a machine answered where the bound had asked for a person, so the last resort
     *    is the same one. A person's refusal (`"person"` ⇒ `human_refused`) is NOT this notice on either
     *    lane: someone looked. The run was STOPPED — a `failed` terminal, `code` the same code, `message`
     *    the limit sentence — rather than kept spending on a model the classifier denies without end; the
     *    triggering call's own result is the deny that stood. The two lanes' sentences differ in ONE
     *    clause (which of the two ways nobody answered), and the durable one states the counts the PARKED
     *    row carried, so a resume in a fresh process reports the streak that actually happened. Once per
     *    run leg (the first such fallback owns the terminal);
     *    `detail: { sessionId, runId, toolName, toolCallId, consecutive, total, limit }` — `limit` is
     *    `"consecutive"` | `"total"`, the bound that tripped. Audience `"user"` (the person whose run
     *    ended is the one entitled to hear why, and to review the transcript the sentence points at).
     *    With an approver wired the same bound mints NO notice — the fallback ask reaches the person
     *    instead (`AskRequest.denialLimitFallback`).
     *
     *  - `"steering.parked_input_blocked"` (design/373 §4.3) — a PARKED steer entry was withheld when
     *    a resume redelivered it (any resume kind that drains parked steers — wake included) by the
     *    deployment's `userPromptSubmit` screen (block verdict, or a fail-closed non-answer/crash):
     *    the row was consumed with the checkpoint, the frame never reached the model, and the resume
     *    itself proceeds (a blocked instruction must never wedge a wake). The lifecycle half of the
     *    same fact is the entry's own `human_input` account with `delivery: "blocked"` — the two share
     *    the inputId, so "accepted at park, screened at redelivery" is auditable end to end. Audience
     *    `"user"` (the person whose instruction was withheld is the one entitled to re-issue it);
     *    `detail: { inputId?, sessionId, runId, taskId? }` — `inputId` is the parked entry's stored
     *    correlation key (OPTIONAL: a row parked without one exists), and `runId` names the resume leg
     *    that withheld it, so a session resumed twice does not report two withholdings as one.
     *
     *  - `"memory.session_polluted"` (design/178 §3, #324a; message mode-aware since design/336) —
     *    this session's memory crossed into the one-way externally-exposed state (a tool classified
     *    as an external content source was invoked, directly or through a delegated child). Under
     *    `memoryProvenance: "carry"` (default) the message states the tag-admit semantics (ordinary
     *    writes commit with an origin marker; instruction-form files are withheld); under `"off"` it
     *    keeps the pre-336 sentence — writes no longer eligible for the long-term library. The
     *    notice states that VERDICT plus what a harvest does when it collects — it does
     *    not promise quarantine, because a `writeScope`-null layering and a declared-unavailable
     *    session both reach the zero-admission harvest arm (nothing collected ⇒ nothing captured), and
     *    a mark landing after the pre-commit pollution read leaves already-committed rows to the
     *    challenge sweep; what a harvest actually contained is the other code below. The challenge
     *    clause is a policy statement, not a receipt — a sweep that cannot run announces
     *    `"memory.challenge_sweep_failed"` on the engine's incident seat. Announced ONCE PER
     *    SESSION, at the mark seat: a repeat mark inside the process and a resumed session whose
     *    durable marker already exists stay quiet (the state is one-way, so a second line would carry
     *    no new fact); a pollution state that cannot be read announces rather than assuming it was
     *    already said. `detail: { reason, sessionId? }` — `reason` names the invoked tool and is
     *    neutralized/length-bounded (the name comes from the host/protocol roster).
     *  - `"memory.harvest_quarantined"` (design/178 §3, #324a) — a polluted session's harvest ran its
     *    containment: `count` ENTRY files written or changed in the session were withheld from the
     *    library, `moved` of them were physically moved into the control-plane quarantine directory,
     *    and `escalated` of them carry a `HarvestReport.quarantineFailures` row (a failed capture, a
     *    failed removal, or a file tombstoned in place — which counts as moved AND escalated, so the
     *    two numbers are read off the report's rows, never subtracted from each other). One notice PER
     *    HARVEST that withheld at least one entry file (a checkpoint harvest and the terminal harvest
     *    are distinct facts), never minted for a clean session; `detail: { count, moved, escalated,
     *    reportId?, reason?, sessionId? }` (`reason` absent ⇔ the pollution marker could not be re-read
     *    at report time — the withheld count stays true either way). `reportId` (#479) is the minting
     *    harvest's own occurrence identity ({@link import("./memory-engine/types.js").HarvestReport}
     *    `.reportId`, uuidv7): two harvests whose counts and paths coincide carry DISTINCT ids, a
     *    durable replay of the same notice carries the SAME id, and the hold family below shares this
     *    harvest's id — dedup on `(code, detail.reportId)`, never on `reportId` alone (absent only for
     *    a hand-built report; every engine-minted report carries it). The formerly registered index-only gap is
     *    CLOSED under `memoryProvenance: "carry"` (design/336 §6.3, #331): the mint condition reads
     *    `HarvestReport.containment`, so a containment whose only act was the derived-index rollback
     *    announces with `count: 0` and `detail.indexRolledBack: true`; under `"off"` the pre-336
     *    count>0 condition (and its silence on index-only containment) is kept byte-level.
     *  - `"memory.hold_opened"` / `"memory.hold_released"` / `"memory.hold_disposed"` (design/336
     *    §4/§6.3) — the instruction-hold lifecycle, derived from the same structured
     *    `HarvestReport.containment` signal (hold seats are only ever filled under
     *    `memoryProvenance: "carry"`): a PENDING session's instruction-form entry files were captured
     *    off the model-visible plane (`hold_opened`, `detail: { count, paths, reportId?, sessionId? }`
     *    — paths neutralized/length-bounded); previously held entries re-walked the full gate set and
     *    committed after their writer session settled clean or a host valve released them
     *    (`hold_released`, same detail shape); held entries moved to control-plane quarantine
     *    (`hold_disposed`, `detail.disposed: [{ path, terminal }]` with the closed terminal set
     *    dirty/expired/conflict/capture_lost/discarded — an `expired` terminal is explicitly a
     *    TIMEOUT, not a conviction, and the message names `resolveHold` as the recovery valve). At
     *    most one notice per family per harvest report; wording is factual, never threat-flavored
     *    (the design/336 §13-2 model-psyche guardrail). `detail.reportId` (#479) is shared by all
     *    three families AND `"memory.harvest_quarantined"` when they derive from the same harvest
     *    report — that sharing is the contract (one report, several distinct facts): dedup on
     *    `(code, detail.reportId)`; a repeat with the same pair is a replay, a new pair is a new fact.
     *  - `"memory.delegation_static_mark_waived"` (design/324, #324 ruling ①) — the deployment set
     *    {@link RunnerDeps.memoryDelegationEvidence} to `"attested-only"` and a delegation call whose
     *    STATIC tool-face verdict would have marked this session's memory polluted (attestation
     *    missing/unknown + face can reach external content) was not marked: the waiver is announced —
     *    the explicit dual of `"memory.session_polluted"` for this arm, so the arm has a voice where a
     *    mark used to land. MINT-side guarantee only: delivery rides the shared guarded form
     *    ({@link deliverEngineNotice} — a wired sink that throws owns that loss, exactly as at every
     *    de-duplicating station), and the leg latch is consumed at mint. Minted only when a pollution
     *    face is mounted (a
     *    recorder-only child's mark is a no-op — nothing is waived there), at most ONCE PER PREPARED
     *    TASK LEG (a resume leg may announce again — a new leg's audit stream is a new fact;
     *    deliberately no durable once-per-session state), never on the `"static-face"` default, and
     *    never for a delivered `"external"` attestation (that mark still lands);
     *    `detail: { reason, subagentType?, sessionId?, runId }` — `runId` (#499) is what makes the
     *    ONCE-PER-LEG guarantee above checkable, since a resume leg may legitimately announce again and
     *    the session id cannot tell the two legs apart; `reason` is the same sentence the waived
     *    mark would have carried, neutralized/length-bounded (tool and agent-type names are
     *    host/model-controlled inputs).
     *  - `"memory.content_class_declared"` (design/378) — an `McpServerSpec` entry carries an explicit
     *    {@link McpServerSpec.contentOrigin}: the deployment declared this server's whole tool set to
     *    be inside (or explicitly outside) its trust boundary, so the protocol namespace's structural
     *    `"external"` no longer decides. The AUDIT line for a trust boundary an operator drew by
     *    configuration — which is why it announces on the explicit `"external"` value too: pinning is
     *    not a no-op (a declaration beats the `trustedTools` allowlist, so it forecloses the per-name
     *    exemption for this server's tools). One line per DECLARED ENTRY per prepared task leg, minted
     *    once at preparation and never per call (the declaration's whole effect is that calls stop
     *    marking; narrating each call would trade the saved mark for equal noise). Armed on the SAME
     *    condition as the classification it talks about — a leg with no engine-memory session and no
     *    provenance recorder classifies nothing, so there is no posture to report and no line is
     *    minted. `detail: { server,
     *    contentOrigin, toolCount, execIsExternalContent?, sessionId?, runId }` — `runId` (#499) names
     *    the LEG, which is what "one line per declared entry per prepared task leg" is a claim about:
     *    the session id beside it is shared by every leg of the session, so two legs reading one
     *    declaration used to mint byte-identical rows. `server` is the
     *    host-authored entry name, neutralized/length-bounded; `toolCount` is what this entry actually
     *    mounted (0 for a server that failed to connect — the declaration still stands and is still
     *    disclosed); `execIsExternalContent` rides the `"execution"` value only and reports the strict
     *    knob AS RESOLVED FOR THIS RUN, because that arm's posture depends on it. The message's closing
     *    clause is written per value so it can never describe a posture the run does not have. The
     *    entry's TRANSPORT is deliberately absent: it is a claim about a mutable host-owned object made
     *    long after the dial, and D-10 already rules topology is not evidence of the lineage this line
     *    audits — connection facts live on `MaterializedMcp.statuses` instead.
     *
     *  - `"memory.capture_opted_out"` (design/383 §2.8) — memory capture is OFF for this session and
     *    the switch provably TOOK: nothing the session produces will be committed to the long-term
     *    store and its contributions no longer enter consolidation. The user must be able to SEE the
     *    switch land rather than infer it from silence, which is why it is announced rather than left
     *    implicit. States three facts and an exit, and deliberately overclaims none of them: entries
     *    committed BEFORE the opt-out remain in the library and stay retrievable, entries already
     *    folded into consolidation products leave those products unaffected, and deleting committed
     *    content is a separate host action (the erase valve), never a side effect of this switch.
     *    `detail: { sessionId, origin }` — `origin` is the closed set
     *    `"declaration" | "flip-verb" | "standing-record" | "delegation-floor" | "fork-origin"`, and
     *    the message is written per value (a delegated child's inherited floor and a fork's inherited
     *    one-way record say so, rather than claiming the session declared it). Audience `"user"`.
     *    Dedup unit: once per SESSION — key `(code, detail.sessionId)`, so the standing-record resume
     *    arm folds into the standing entry instead of double-announcing.
     *  - `"memory.capture_optout_unpersisted"` (design/383 §2.8) — the OPPOSITE receipt, and a
     *    separate code on purpose ("it is off for you" and "it could not be turned off" must never
     *    share a spelling): the opt-out could NOT be durably recorded (the control-plane write
     *    failed), so it is REFUSED rather than honored in-process under a switch that would silently
     *    stop holding at the next resume. Minted only beside that refusal — the throw carrying the
     *    same code — never beside a success, and no success notice is minted on this arm.
     *    `detail: { sessionId }`. Audience `"user"` (the operator repairs the store, but the
     *    entitlement to the fact belongs to whoever asked for the privacy). **TWO ingresses, and they
     *    settle differently — a consumer must not read one as the other:**
     *      · PREPARE-TIME (a declared/inherited opt-out, before the run starts) — the refusal is
     *        TERMINAL: the task never runs and nothing is captured. Dedup unit: once per task, since
     *        the refusal it rides settles the task.
     *      · the LIVE FLIP VERB ({@link TaskStream.optOutMemoryCapture} mid-run) — the refusal is
     *        SCOPED TO THAT CALL: it rejects the verb's promise and nothing else. The run keeps going
     *        and capture is still ENABLED (the switch did not take), which is exactly why the notice
     *        is owed. Because no record was retained, a retry can fail the same way, so this ingress
     *        can announce ONCE PER ATTEMPT — a consumer de-duplicating per task would hide later
     *        failures of a switch the person is still trying to throw.
     *    Read the run's own outcome (or the verb's rejection) to tell the two apart; this code alone
     *    does not say whether anything stopped.
     *  - `"memory.consolidation_incomplete"` (design/376, LLM consolidation driver) — a driver run
     *    settled without reaching the fixpoint: `detail` names the stop reason (the closed
     *    `ConsolidationRunStopReason` set), cycles done, and the residue (write-failure or
     *    fuse-refused groups by name). Advisory: committed cycles stand (add-only, never rolled
     *    back); the recovery verb is re-running the host driver, which resumes the same pending run.
     *  - `"memory.consolidation_withheld"` (design/383 §1.4) — the exposure partition engaged on a
     *    consolidation run, so the CLEAN arm's authoring prompts were composed WITHOUT the marked
     *    rows: anything this run's clean products say was written without them, and their silence on a
     *    topic therefore does not mean the library is silent on it (the withheld rows still stand and
     *    are still readable). OPERATOR audience (#692 C-a): the run happens outside any session, so
     *    there is no `sessionId` to route a user row to — the receipt's `withheldFromCleanArm` /
     *    `withheldNeverSent` are the reader-facing half. TWO counts, deliberately separate sentences and separate keys —
     *    `withheld` rows were kept out of the clean arm's prompts, and `neverSent` of those were not
     *    sent to the consolidation model AT ALL (a lone marked row has no group to fold, so it is
     *    withheld from every call). "Withheld from the clean arm" and "never left this machine" are
     *    different facts about a person's own data and must not be merged into one comforting number,
     *    which is why `sentToProvider` is carried as its own member rather than left to subtraction.
     *    `detail: { scope, runId, withheld, neverSent, sentToProvider }` — `scope` is
     *    neutralized/length-bounded. Audience `"user"`: the ONE consolidation row that is a reader's
     *    fact rather than an operator's (the other five report what a host-driven maintenance run DID;
     *    this one reports what the products a person will READ were written without). Minted once per
     *    run, beside the receipt — the dedup unit.
     *  - `"memory.consolidation_driver_superseded"` is an ERROR code, not a notice (named here only
     *    to keep the family's spellings in one place): a concurrent driver invocation took over this
     *    scope's run row (attempt fencing) and the losing worker's call THROWS with this `code` —
     *    it never crosses {@link deliverEngineNotice}, is deliberately absent from
     *    {@link ENGINE_NOTICE_CODES}/{@link NOTICE_AUDIENCE}, and sits in the non-governance
     *    disposition table with the error dispositions. The loser MUST NOT retry into the winner's
     *    account. A consumer diffing its own table against the catalog must not add a row for it.
     *
     *  - `"config.peer_admission_out_of_range"` (#551) — a `RunnerDeps.peerLane` field the
     *    resolver could not use: outside its legal range, or not a finite number at all (a string off
     *    an untyped host's wiring, `NaN`, `Infinity`). THAT FIELD falls back to its own default (never
     *    a clamp to the nearest edge, never a whole-config reject) and every other field is unaffected.
     *    One notice PER FIELD — two bad fields in one config are two notices — deduped per SINK on
     *    `(field, given)` rather than per call (the resolver runs on every SendMessage and every drain
     *    round; the fact is about the wiring, not about the call). Per SINK, not per process: two
     *    Runners in one process each own an `onNotice`, and a process-global ledger would deliver the
     *    fact to whichever resolved first and leave the other silently without its own disclosure. The
     *    unwired arm (no `onNotice`: the shared `console.warn` throat) latches per process instead —
     *    the console IS one process-wide channel. `detail: { field, given, default,
     *    range: [lo, hi], reason }`, where `reason` is `"out_of_range" | "not_a_finite_number"`.
     *    Audience `"operator"` (a configuration fact; the fix is the deployment's).
     *  - `"config.peer_lane_unmounted"` (design/385 §1.2⑥) — `RunnerDeps.peerDirectory` is wired but
     *    the cross-session lane could not mount on this leg: no `mailboxStore`, or one that does not
     *    declare `crossProcessSafe: true` (several terminals would share one session box on luck). The
     *    seat is inert for the run (no ListAgents, peer addresses refuse with the same reason). Once per
     *    prepared leg; `detail: { reason, code, mailboxWired, sessionId, runId }`. Audience `"operator"`
     *    (a wiring fact; the fix is the deployment's).
     *  - `"peer.inbound_disposition"` (design/385 §1.2④ / §4.6 / §4.4) — a message parked in THIS
     *    session's own box was settled at the drain WITHOUT reaching the model. `detail.disposition` is
     *    the arm (closed set `PeerInboundDisposition`): `"refused"` (this session's `crossSessionInbound`
     *    setting, or a record whose typed fields cannot be rendered canonically), `"admission_refused"`
     *    (the drain-stage admission re-check: duplicate / hop loop / runaway — `cause` names it),
     *    `"notice_unrouted"` (a notice-kind record the notice face cannot render — no typed `notice`, or
     *    a state that does not fit its kind — settled without delivery, never ridden through the
     *    peer-message envelope), `"notice_user_only"` (a delivery receipt / idle notice whose sender this
     *    session's parity would HOLD: surfaced here for the user, not read by the model — CC's
     *    `modelVisible:false` arm; `cause` carries the rendered notice text), or `"held"` — the parity
     *    judgment held it: the message ENTERED this session's process-level held queue and was acked
     *    from the box (later messages are not blocked behind it), where it is re-judged at every
     *    boundary, released or refused with a receipt, and expires WITH a receipt under `dialogExpiry`
     *    (see `peer.held_settled`); `cause` is a `PeerInboundHoldCause` — the parity causes, or the
     *    drain gate's TRANSIENT `rate_limited` (that one does NOT enter the queue: the message stays in
     *    the box and the drain stops at its seq until the bucket refills). Disclosed once per seq per leg,
     *    and again when a re-judgment moves a held entry's cause. `detail: { disposition, cause, seq, box,
     *    fromSession?, sessionId, runId }`. Audience `"user"`: the session's user is the one who did
     *    not receive it.
     *  - `"peer.held_settled"` (design/385 §4.4, slice 4) — a message left THIS session's held queue.
     *    `detail.settlement` names how: `"delivered"` (a re-judgment or an approval released it into
     *    this session's model context), `"refused"` (the setting moved to refuse, or an approval met a
     *    refusing policy), `"expired"` (the `dialogExpiry` deadline, an eviction when the 100-entry queue
     *    was full, a cancelled review, a graceful shutdown, or a record another lease holder already settled),
     *    `"denied"` (the review face), `"dropped"` (the injection lane refused the released frame).
     *    `detail.reason` is the trigger (`PeerHeldSettleReason`: `deadline | evicted | shutdown | rejudge |
     *    approved | denied | cancelled | inject_failed | superseded`); the sender was sent the matching receipt
     *    whenever the record carried a reply address — except on `superseded`, where the lease holder that
     *    settled the record sent it.
     *    `detail: { settlement, reason, seq, box, fromSession?, heldCount, sessionId, runId }`. Audience
     *    `"user"`: it is this session's user who held (or was holding) the message.
     *  - `"peer.idle_subscription"` (design/385 §5.2, slice 4) — a peer session asked to be told when
     *    THIS session next goes idle (`SendMessage … notify_when_idle`). `detail.outcome`: `"recorded"`
     *    / `"refreshed"` (a same-requester re-ask; the subscription is one-shot and expires unfired after
     *    12 h), `"full"` (the 32-entry table is full) or `"refused"` (this session's `crossSessionInbound`
     *    is `refuse`) — the last two answered the requester with an `unavailable` notice. `detail:
     *    { outcome, fromSession, live, seq, box, sessionId, runId }`. Audience `"user"`: the person whose
     *    session is being watched is the one entitled to know (CC announces it in the session UI).
     *
     *  - `"delegation.transcript_integrity"` (subagent transcript persistence) — a durable agent row
     *    with a BOUND transcript sessionId met a session store that attests `not_found` for it: the
     *    deployment's declared transcript durability is being contradicted by reality. Announced at
     *    most once per (scope, handle, process) — `detail: { handle, scope? }`, scope = the resolved
     *    access scope of the read that found the gap — from the continuation read faces (SendMessage preflight /
     *    AgentTranscript's durable leg); the per-call honest refusals are unchanged, and the declared
     *    tier is NOT auto-downgraded (declaration-制 — observation reports, it never re-adjudicates).
     *  - `"delegation.ask_unresolvable"` — an `ask` reached its FINAL deny with nobody having judged it:
     *    the call's approval could not be RESOLVED. `detail.cause` (closed set `ASK_UNRESOLVABLE_CAUSES`):
     *    `approver_unavailable` — the approver consulted (the run's own seat, or an ancestor's frozen seat
     *    on a delegated child) answered `unavailable` and no park took the call (`tool_end.gate.settlement
     *    .kind:"approver_unavailable"`); `no_approver_wired` — no approver / question face is wired, or the
     *    seat is the deny posture (the headless auto-deny, `settlement.kind:"no_approver"`);
     *    `mandate_unreconstructible` — an inherited durable mandate binds the ask and this leg has no park
     *    facility (an in-fold deny, no settlement on the frame). The deny itself is unchanged (the
     *    fail-closed sentence as the tool result); this is its person-facing half, minted at the gate's ONE
     *    deny exit, once per tool call, by `ask-unresolvable-notice.ts`. `detail: { sessionId, toolName,
     *    toolCallId, cause, parkLaneExisted, remedy? }` — `parkLaneExisted` says whether a park lane was armed and
     *    declined/failed (`true`) or none existed (`false`, the usual shape); `remedy` (the two causes that say nobody
     *    could be asked — `approver_unavailable` and `no_approver_wired`; never `mandate_unreconstructible`)
     *    is the one sentence naming the caller-side change that would have let the call run, as the tool text carries it. Audience `"user"`: the refused
     *    work is the session user's own; the operator hears it through the sink like every code.
     *
     *  - `"memory.consolidation_recommended"` (design/339 §2.2/§6.2) — the engine-minted per-scope
     *    session count crossed the consolidation thresholds (time gate open ∧ enough distinct
     *    sessions); minted at most once per crossing (a completed run re-arms the edge), NEVER when
     *    the deployment leaves consolidation off. ADVISORY: the host owns the verbs, nothing runs
     *    automatically; `detail: { scope, sessionsSince, reportId?, sessionId? }` — `reportId` (#479)
     *    is the minting harvest report's occurrence identity, shared with the harvest/hold family's
     *    notices of the same report (dedup on `(code, detail.reportId)`).
     *  - `"memory.consolidation_committed"` (design/339 §6.2) — a consolidation plan reached
     *    `completed`: products landed, superseded targets left the default read face (retained as
     *    evidence), intents settled; `detail: { planId, scope, products, superseded, intents }` —
     *    counts and engine-minted ids only, zero content.
     *  - `"memory.consolidation_conflict"` (design/339 §6.2) — a plan parked CONFLICT: at least one
     *    target did not reach its planned state (a concurrent write, a patch the store declined, or a
     *    target the re-judgment could no longer act on — the plan's audit rows carry the per-target
     *    reason). The REFUSED targets were not overwritten; patches the plan had already applied
     *    stand (discard rolls nothing back — the receipt's applied-vs-conflict counts say how many).
     *    Affected intents stay pending and wait for the host valve
     *    (`resolveConsolidationPlan` retry/discard); `detail: { planId, scope }`.
     *  - `"memory.consolidation_refused"` (design/339 §6.2) — the notice dialect of a consolidation
     *    verb's coded structured refusal (the verb itself throws
     *    {@link import("../core/memory-engine/consolidation.js").ConsolidationRefusedError});
     *    `detail: { refusalCode, scope?, occurrenceId? }`. `occurrenceId` (#495) is the THROWN ERROR's
     *    own id, minted once where the refusal happens and read off it by the notice factory — one
     *    throw rendered twice keeps one id, two refusals sharing a code/scope/message get two. Omitted
     *    (never fabricated) when the caller composes the line with no error in hand.
     *
     *  The five SERVER-minted rows ({@link NOTICE_MINTER} `"server"`) — catalogued so their audience and detail shape
     *  are read here like every other row; this engine never mints them. A server forwards them through the same sink.
     *  - `"permission.entry_mode_clamped"` — the permission mode the caller requested was clamped by the deployment's
     *    entry floor BEFORE the run reached this engine (the server's own clamp, through this package's
     *    `clampPermissionMode`), so the run starts under another mode. The disclosure is minted where the clamp happens:
     *    a fold this engine makes itself (a parent's ceiling, the deployment's deny bits, a missing classifier face) is
     *    `config.permission_mode_unavailable`, never this row. Audience `"user"`; `detail: { requested, effective,
     *    ceiling, sessionId }` — `requested` the caller's word, `effective` the word the run starts under, `ceiling`
     *    the floor that clamped it; no run id — the clamp happens at intake, before any run exists. Once per run.
     *  - `"instructions.source_changed"` — the project instruction file this run injects is not the one the same
     *    session's previous run injected (a file added, removed or renamed). Audience `"user"`; `detail: { previous,
     *    current, sessionId }` — `previous` / `current` are chain-member relative paths, `null` = that run had none.
     *    No `runId`. Once per run.
     *  - `"session.records_skipped"` — the session's store skipped records it could not read when it opened the
     *    session; the run continues on the history that remains. Audience `"user"`; `detail: { dropped, entriesKept,
     *    sessionId }` — two observed counts, not to be combined. No `runId`. Once per open that skipped.
     *  - `"memory.layer_locked_legacy"` — a memory layer an older engine left without its owner-write bit was found at
     *    boot; deletes in it will fail until the operator restores the bit. Audience `"operator"`; `detail: { layers,
     *    remedy }` — layer directory names and the one command. No session. Once per boot.
     *  - `"server.model_unreachable"` — a model call of this leg ended in error and its last request failed to
     *    connect. Audience `"user"`; `detail: { code, targetHost, remedy, sessionId, taskId?, proxyInUse? }` — `code`
     *    one of `UND_ERR_CONNECT_TIMEOUT` / `ECONNREFUSED` / `ENOTFOUND` / `EAI_AGAIN` / `EHOSTUNREACH` / `ENETUNREACH`;
     *    `targetHost` the model endpoint's `host:port`; `remedy` the constant `"check VPN / proxy; restart if the
     *    proxy changed since boot"`; `taskId` (server 7.121.0+) the run resource id of the leg this notice belongs to —
     *    absent on a streaming leg that has no run store, and the SAME value on a resumed leg and its original (a
     *    per-leg dedup folds the two into one line); `proxyInUse` present only when that request went through a proxy,
     *    its value the variable's name (`HTTP_PROXY` or `HTTPS_PROXY`), never the proxy address. No `runId`. Once per
     *    leg per target.
     *
     *  Deliberately NOT a notice family: brain retry/reconnect liveness (a rate limit, a 5xx, a
     *  transient network failure being retried). Those are per-attempt liveness frames with their own
     *  frequency semantics and ride the wire `status` channel ({@link BrainStatus}), whose sink the
     *  Runner establishes unconditionally around every brain-driving call — no host seat to wire, no
     *  console fallback to flood. A reader looking for retry disclosure should look there, not here. */
    code: string;
    /** The exact human-readable line the unwired build prints via `console.warn` — same words, one text. */
    message: string;
    /** Machine-readable facts of the notice (knob names, arriving values, values in force). */
    detail?: Record<string, unknown>;
    /** The owning session, when the notice HAS one — an ATTRIBUTION/correlation key, never a
     *  projection permission: whether a notice may be surfaced onto that session's user-facing
     *  stream is decided by the code's {@link NOTICE_AUDIENCE} row alone ("audience is entitlement,
     *  not routability" — several `"operator"` rows carry a sessionId purely for correlation, and a
     *  projector keying on presence would push deployment facts at end users). Presence only makes
     *  routing structurally POSSIBLE; a session-less code structurally cannot be routed at all.
     *  Mint sites do not set this: {@link deliverEngineNotice} — the ONE delivery throat — lifts a
     *  string `detail.sessionId` here, so the typed key and the detail carriage can never disagree.
     *  Optional and absent for process/config-scoped codes. */
    sessionId?: string;
}
/** Test seam (mirrors `__resetBashTimeoutAnnouncements`): never called by production code. */
export declare function __resetMalformedNoticeSeatAnnouncement(): void;
/**
 * The ONE delivery form behind every {@link RunnerDeps.onNotice} emission point (#170: the form was
 * triplicated across the emission stations, and every copy judged the seat with `!== undefined` — so a
 * PRESENT non-function seat (null, a config typo, an untyped host's JSON wiring) entered the wired arm,
 * threw `TypeError` on the call, and the swallow guard silenced BOTH channels at once). Contract:
 *  · a FUNCTION seat REPLACES the console line (a host forwarding notices to its own surface must not
 *    show every fact twice), swallow-guarded against both failure shapes the void-typed seat admits —
 *    a synchronous throw and an async sink's rejected promise (unhandled, that rejection is a
 *    process-level fault): a notice sink must never turn an announcement into a failure. Deliberately
 *    NO console fallback on sink failure: loudness ownership transfers with the wiring, and a console
 *    echo of a transient sink failure would double-send the fact. Corollary kept as-is: stations that
 *    de-duplicate ledger the line BEFORE this call, so a true-function sink that throws can in
 *    principle lose a ledgered line for the process lifetime — un-ledgering after the fact would race
 *    an async sink's late rejection and re-announce (double-send) on transient failures.
 *  · a PRESENT NON-function seat is a bad deployment value, and #123 (loud-bad-value law) forbids
 *    folding it to silence: the notice itself falls back to `console.warn` (no line is lost), and the
 *    seat defect — the one fact every fallback would otherwise repeat — is announced once per process.
 *  · an ABSENT seat prints the historic `console.warn` line verbatim (byte-compat loudness).
 */
export declare function deliverEngineNotice(onNotice: ((notice: EngineNotice) => void) | undefined, notice: EngineNotice): void;
/**
 * #259 loud half, one notice PER FAMILY (`task.user_steer_undrained` / `task.user_followup_undrained`):
 * a consumer routing on `code` alone must never mistake a stranded follow-up for a stranded steer, so
 * the code carries exactly the semantics its name claims — the same two-key split the settled frame
 * uses. Pure (the terminal sweep race window is not constructible deterministically; this seam is).
 *
 * `sessionId` (#433, additive and optional): these two codes are the `"user"` audience rows whose
 * subject is the END USER's own lost input, and a disclosure that cannot say WHICH session lost it
 * has nowhere to be delivered — the audience registry and the routing key are the two halves of one
 * answer. Carried in `detail` like every other session-attributed code, so {@link deliverEngineNotice}
 * — the one throat — lifts it to the typed top-level key and the two spellings cannot disagree.
 * Omitted when the caller has none (never fabricated: the message is what the run lost, and a made-up
 * routing key would deliver it to the wrong stream).
 *
 * `runId` (#499, additive and optional): THE key for this account. The two ids above name the task and
 * the conversation, and both answer the same value for two runs of one session whenever the host
 * supplies no task id — so two runs each stranding one steer produced two byte-identical rows, read as
 * one repeated disclosure about one run. The account is per-RUN by its own contract sentence ("at most
 * once per family, per run"), so it must carry the run's own id. Same omit-never-fabricate rule.
 */
/** What a settled leg still owes a person BEFORE the compensation lane has had its say: the facts the
 *  terminal-adoption lane holds, carried to the settle lane that closes the account
 *  ({@link settleOwedUserInput}). */
export interface OwedUserInputAccount {
    /** The harness's at-agent_end snapshot, held because the tail still had something to say about it. */
    readonly held: {
        steer: number;
        followUp: number;
    } | undefined;
    /** What a durable park's migration carried onto the new checkpoint, by family. */
    readonly migrated: {
        steer: number;
        followUp: number;
    };
    /** The resumed leg's undelivered continuation frames, and how many of them the migration carried. */
    readonly continuation: {
        frames: ReadonlyArray<{
            seq?: number;
        }>;
        carried: number;
    };
}
/**
 * What this leg still OWES a person, one count per family — the input to {@link undrainedUserInputNotices}.
 * TWO lanes accept caller input and either can be holding some when a run ends: the harness's live queue
 * (`held`, the at-agent_end snapshot, minus what a durable park migrated onto the new checkpoint) and a resumed
 * leg's undelivered continuation frames (`owedContinuation` — the checkpoint's parked steers and the caller's
 * wake message, minus what the same migration carried). They are ONE book: a frame is owed for the same reason
 * on either lane — accepted, and never in the transcript — and counting the live queue alone is what let a leg
 * that committed no pause drop a person's instruction in silence (the original row is already spent by then).
 */
export declare function owedUserInputCounts(held: {
    steer: number;
    followUp: number;
} | undefined, migrated: {
    steer: number;
    followUp: number;
}, continuation: {
    frames: ReadonlyArray<{
        seq?: number;
    }>;
    carried: number;
    rowReturned: boolean;
}): {
    steer: number;
    followUp: number;
};
/** Close the account — the ONE settlement point, called AFTER the reopen compensation with what that
 *  compensation actually did. Ordering is the whole rule: the counts are arithmetic, and the only fact they
 *  cannot be computed without is one the lane before this one does not have yet. EXACTLY ONCE per account,
 *  which is what lets the teardown call it again as a backstop for a settle lane that threw on its way here
 *  (an account nobody closes is the silence this seat exists to end; a second row would be the same loss
 *  counted twice). Keyed on the account OBJECT — one per run, minted by the lane that assembles it. NEVER
 *  THROWS: a station calls it from a teardown path where the run is already failing, and a disclosure that
 *  takes the cleanup down with it is worse than the silence it was fixing (the absent-sink arm of
 *  {@link deliverEngineNotice} ends at a bare `console.warn`, which a host-replaced console can throw from). */
export declare function settleOwedUserInput(account: OwedUserInputAccount, rowReturned: boolean, ids: {
    taskId?: string;
    sessionId?: string;
    runId?: string;
    parent?: ParentLegIds | undefined;
}, onNotice: ((notice: EngineNotice) => void) | undefined): void;
/**
 * Why a person's input is reported as not consumed — the `detail.cause` of BOTH undrained families
 * (`task.user_steer_undrained` / `task.user_followup_undrained`; the family is the lane the input was queued on). Closed:
 *  · `leg_end` — the leg ended with the input still owed (this engine mints it, at the leg's settlement);
 *  · `crashed` — the engine process ended before the leg settled, and an input the serving layer had accepted for it was
 *    not confirmed consumed before the process died (a live leg gives the serving layer no evidence that an input reached
 *    the model, so such an input MAY have been consumed). Only the serving layer can count these (the queue was its own), so the serving layer
 *    mints this arm, choosing the family by the lane the input was queued on, before the next run of the session starts —
 *    the one member-level exception to {@link import("./governance-codes.js").NOTICE_MINTER}'s rows for the two codes, which stay `"engine"`.
 */
export declare const USER_INPUT_UNDRAINED_CAUSES: readonly ["leg_end", "crashed"];
export type UserInputUndrainedCause = (typeof USER_INPUT_UNDRAINED_CAUSES)[number];
/**
 * The two families of the stranded-input account, keyed by the lane the input was queued on (`injectionLaneOf` maps a steer's
 * tier to it): the notice `code` the family is announced under, and the `detail` key that carries its count
 * (`detail[countKey]`, beside `cause`). {@link undrainedUserInputNotices} writes its rows with exactly these two spellings, so a
 * serving layer that mints the `crashed` arm picks its family by lane and spells code and count key the way the engine does.
 */
export declare const USER_INPUT_UNDRAINED_FAMILIES: Readonly<{
    readonly steer: Readonly<{
        readonly code: "task.user_steer_undrained";
        readonly countKey: "steer";
    }>;
    readonly followUp: Readonly<{
        readonly code: "task.user_followup_undrained";
        readonly countKey: "followUp";
    }>;
}>;
/** Where a child leg's account is ALSO announced: its spawner's session, and that run's task id when the spawner carried one
 *  (it never carries the run id, and the row never invents one). */
interface ParentLegIds {
    readonly sessionId: string;
    readonly taskId?: string;
}
/**
 * The parent a leg reports to, read off the trusted run-scoped channel (`RunInternals.parentSessionId` / `parentTaskId`,
 * threaded by the delegation lanes from the spawner's own ids). `undefined` for a leg with no parent SESSION — a row
 * with no session to address cannot be projected anywhere, so a bare `parentTaskId` names no one.
 */
export declare function parentLegIds(internals: {
    readonly parentSessionId?: string;
    readonly parentTaskId?: string;
} | undefined): ParentLegIds | undefined;
/**
 * The rows for what a leg still owes a person. One session's view is the leg's own rows (`taskId` / `sessionId` /
 * `runId` — each omitted when the caller has none, never fabricated). A leg that has a PARENT is announced to it as well,
 * the way a child's folded permission mode is: after the leg's own rows come one copy per row, `detail.sessionId` the
 * parent's (and `taskId` the parent run's, when carried), `childSessionId` the leg's own session — the key a
 * consumer reads "a child reported this" from — and the message tail `(a child leg, <childSessionId>)`. The copy carries
 * no `runId`: the account is per run and nothing carries the parent's. Only the DIRECT parent hears: a grandchild's
 * row reaches its parent's session (the middle leg), not the root's. A parent that is no addressee — null, no or an empty session, or the leg's own session (which already
 * hears the row) — gets no copy, and the call never throws on it.
 */
export declare function undrainedUserInputNotices(counts: {
    steer: number;
    followUp: number;
}, taskId?: string, sessionId?: string, runId?: string, parent?: ParentLegIds): EngineNotice[];
export {};
