# Changelog ## Unreleased ### Breaking changes - An unanswered question or the owner's no no longer ends a build as a `policy` block. `BuildBlocked`, `BlockedReport` and `JobBuildBlocked` add the reasons `owner_declined` (the owner answered no to a `confirm` question, or declined to sign in) and `question_unanswered` (a question the build needed came back as no answer). The host refuses each without its basis on record (`owner_not_declined`, `no_question_unanswered`), and a `policy` report whose only basis is one of them ends under that reason's name. `policy` now stands only on a Guardian refusal or on `MintDependencies.policyBlockAllowed`. A `MintHarnessSnapshot` whose `terminal.blocked` holds a new reason does not decode on a worker from an older release. - Migrate a host that maps each `blocked.reason` by handling the two new reasons. - `BuildBlocked`, `BlockedReport`, `JobBuildBlocked` and `MintStopReason` add the reason `site_declined`: the site offers what the task needs but declines this example's own facts, such as an applicant it will not quote, and the explanation names the reason the site gave. Such a build used to end `site_lacks_capability`, which told its caller the site can't do the task. Like `site_lacks_capability`, it needs no basis on record, and Guardian reviews its explanation before a caller reads it. A `MintHarnessSnapshot` whose `terminal.blocked` holds it does not decode on a worker from an older release. The workspace guide and the `report_blocked` description name it. - Migrate a host that maps each `blocked.reason` by handling `site_declined`. - `SignInStep`'s `rejected` may now be `{ account: "locked" | "suspended", selector }` beside `{ slot }`: the site showed, once the login was sent, that the account is locked or suspended, at the visible element `selector` names. The local host checks the element shows (`makeSignInRecorder` takes an optional `markerVisible`), then ends the sign-in with the new `SignInDiagnostic` code `AccountLocked` and its optional `account`, which leaves sign-in unavailable in the build. Migrate a host that reads `step.rejected.slot` by checking for `account` first, verifying the selector shows and failing the sign-in as `AccountLocked`. - `TaskChange`'s `login` setting adds the change `signed_out`: once an update with it applies, the task runs signed out and the harness drops the unresolved sign-in. Migrate a host that maps each login change by handling it. - `signInUnavailableSummary` words the owner's summary in plain language: it no longer names the root cause's code and phase or says that effects and receipts are preserved. The build's diagnostics keep the code and phase. - A live example, a live test and a write session's first step keep the browser's cookies and site storage (local storage, IndexedDB, cache storage, service workers and the tab's session storage). The reset still closes other tabs, stops loading and returns the page to the site origin. A site that grants trust to a browser session keeps trusting it across resets. A browser where a sign-in started and none was verified still clears them, so a half-finished sign-in never reaches a step. Builds no longer save the session after sign-in or restore it before each reset. - `startStateFor` from `pomerado/core/runtime/start-state` takes `signInStarted` and `signInVerified` in place of `signedIn` and `sessionSaved`, and returns `keep`, `clear` or `none`. `restore` stays a `StartState`, `PageStart` and `resetPageCode` mode for a host's own resets, such as a run's retry. `shouldSaveSession` is removed. - `makeStartTracker` takes an optional `{ signInStartedBefore }`, for a browser that builds share, and tracks per browser whether a sign-in started or was verified. It adds `signInStarted`, `signedIn` and `browserReplaced()`, which a host calls when it replaces the browser, as for a new proxy: the new browser starts with an empty profile. `plan(step)` returns the `StartState` alone, in place of `{ save, start }`, and `save` and `saved` are removed. Migrate by calling `plan` for the reset mode and dropping the build-side session save. - `pomerado.hostProtocol` stays 1. No `./core/*` export is removed, and the reset and session-save helpers a host composes its own resets from (`resetPageCode`, `saveSessionCode`, `SavedSessionStorage`, `savedSessionStorageCapBytes`, `startPage` and `PageStart`) are unchanged. The changes inside `start-state` are to the build start rule and its tracker, which no known host imports yet; raising the protocol would make hosts that accept only protocol 1 refuse a package whose interface they use unchanged. A host that does call `startStateFor`, `makeStartTracker` or `shouldSaveSession` migrates as above. - The local host's `makeBuildStart` no longer exposes `saveSession`, and its signed-out sample hook runs after any reset before a sign-in started, as well as after a clearing one. `makeMarkerChecks`'s `afterClear` is now `afterSignedOutReset`. `makeLocalSessionSignIn` no longer takes `saveSession`. - A live test case keeps its browser's site data too. `LiveTestCaseRun` and `LiveTestRecord` in `pomerado/core/mint/live-tests` add the optional `start`, `warm` or `cold` (type and schema `LiveTestStart`): `cold` for a case that ran first on a new browser with an empty profile. The `LiveTestHost` contract asks a host with more than one browser to run at least one case cold and to mark every case it runs. The local host has one browser and marks every case `warm`. Each version in `publication/tests.json` adds `coldPasses`, the cases that passed cold on the published source, and the coverage line says how many passed cold. - Guardian's `publicationLiveTestsPolicy` treats a version with no case that passed cold as a gap for the rationale, never a finding, and no longer lists the example's rerun on a fresh browser as a gap that gets a finding. A recorded Guardian response keyed on the exact policy text needs recording again. - The core, testing, http-mcp, writes and auth skills, the workspace guide and the `live_tests` tool description say that examples and tests keep the browser's cookies and storage, so a script sets every value its input controls itself, and a case that runs cold is the proof. - A filled `AutofillStepReport`'s `submit` may now be `page`: on a step with fields and no submit, once every field was typed, the page moved on before the host clicked anything, as a code widget that sends itself when its last box is filled does. `fillAutofillStep` reports it when its submit call finds the step's fields gone or all disabled, its popup closed, the page's address on another path than when the host typed the last field, or every field of a step of several emptied. Such a fill used to report `refused`, `none`, or its last field `failed` with `not_attempted`. The host gives the page its settle wait, at most 2 s, to move on before it reports `none`. A submit call that fails in that wait, as one does when the page navigates or draws itself again while the call reads it, is followed by another look: the fill reports `page` when a later look finds the page moved on, and the failed call (`uncertain`, `fill_call_failed`) as before when it does not. A step with a submit is unchanged: the host still clicks it, waits on a submit the page disabled, or refuses as before, whatever the page does while it checks what was typed. Guardian's refusals come first as before: a control that moved off the site or changed how it submits still refuses the submit. - `maySend` from `pomerado/core/destinations/autofill-refusal` is true for `page`, `fillFailure` in the run's sign-in replay passes it, so the sign-in's own check decides what the code did, a refused code included, and the minter reads its own notice for it. - Migrate a host that maps each `submit` value, such as to a notice or a failure, by handling `page` as a possible send that the sign-in's own check decides. - A read signed out runs its live test cases as a batch on the HTTP version too, and each version keeps its own results: `live_tests` with entrypoint `src/tool-http.mjs` never replaces or stales the Playwright version's results, nor the reverse. - `LiveTestRecord` in `pomerado/core/mint/live-tests` adds the optional `entrypoint` the case ran. The harness keys results by case id and the entrypoint's version, from the new `liveTestVersion` (`http` for a `*-http.mjs` file, else `playwright`; type `LiveTestVersion`). A `MintHarnessSnapshot` from an older release, whose results name no entrypoint, restores them as the Playwright version's. `MintHarnessSnapshot` adds the optional `liveTestsHttp`, the HTTP version's results, while `liveTests` keeps only the Playwright version's. A worker from an older release drops the HTTP results rather than restoring them as the Playwright version's. - `liveTestsEvidence` takes `versions`, one `LiveTestVersionResults` (`version`, optional `entrypoint`, `sourceDigest`, `records`, optional `replaced`) for the Playwright version and one for the HTTP version when the build has one, in place of `records`, `replaced` and `sourceDigest`. Migrate by passing the Playwright version's former arguments as one entry with `version: "playwright"`. - The publication record `publication/tests.json` lists only the versions that ship: the published entrypoint's, and the HTTP version beside a published Playwright version unless `finish_build` ruled it out with `httpVersion`. A version that does not ship is neither recorded nor judged. The record adds `versions`, each version's entrypoint, source digest and counts, and gives each case `results` with its result on each version in place of the case's own status fields. Each retired entry names its `version`. Its top-level `sourceDigest` and `counts` move into `versions`. The coverage line counts each version on its own. `liveTestsEvidenceProblem`'s record adds an empty `versions`. - Guardian's `publicationLiveTestsPolicy` judges each version's coverage, and the HTTP version's parity with the Playwright version, from that version's own results. A case that passes on one version and fails on the other is a finding on the failing one. A recorded Guardian response keyed on the exact policy text needs recording again. - The testing, http-mcp and workspace guidance, and the execute and `live_tests` tool descriptions, tell a read signed out to run its cases as a batch on `src/tool-http.mjs` as well, instead of one input at a time. - Test design is for reads signed out only. A signed-in read proves its tool with its example, and a write with its act session; neither designs further tests, runs extra live tests or builds offline test suites, and each checks its HTTP version once, against that run (a write by one `savedHTTP` replay of its session). A read signed out tests its HTTP version on several inputs, as it tests the Playwright version. - The scope follows what the tool is now, not what the build started as. A read that signs in or becomes a write mid-build, a repair included, has `live_tests` refused from then on and publishes with no test record. A repair that rebuilds a signed-in tool or a write as a read signed out gets the testing skill and parallel live tests. Guardian judges the published version's kind and this build's verified sign-in, not the original request's kind. Only a sign-in on the current site counts: after an update moves the build to another site, the harness, like the local host, no longer counts the earlier site's sign-in. - `preflightTestInput` from `pomerado/core/mint/step-checks` refuses every agent-chosen `testInput` unless `signedIn` is `false`. A signed-in read ran up to four before; a host that does not pass `signedIn` is now treated as signed in. The four-test limit and its count are gone, so `executionHistory` is optional and ignored, kept only so existing callers compile; it will be removed. Migrate by passing `signedIn: false` for a read that has not signed in, and dropping `executionHistory`. - The testing skill, its catalog description and the core skill, workspace guide, execute and `live_tests` tool descriptions, `testInput` description, http-mcp, writes, search, pagination and publication skills say so. The testing skill's note on reporting sign-in replay as unverified moves to the auth skill. A signed-in read's `live_tests` call and a write's live example or test are refused with an instruction to publish from the one run, no longer to test offline. - Guardian's `publicationLiveTestsPolicy` judges a signed-in read or a write on its one run alone: it never returns a finding, or asks for another run or test, because a capability was not tested beyond that run, and treats the coverage text's untested items as disclosure. A read signed out keeps its coverage judgment; when its host keeps no `publication/tests.json`, the policy now says to judge its coverage by the same rules from the coverage text, the live tests the execution context lists and the captures. A recorded Guardian response keyed on the exact policy text needs recording again. - A list cursor works for ten minutes after the call that returned it, not the hour 0.4.0 canaries gave it: `listCursorTtlMs` is 600,000, `sealListOutput` dates `next_cursor_expires_at` ten minutes ahead, and `admitListCursor` refuses as `expired` a cursor past its own expiry or more than ten minutes (and five seconds for clock skew) past its issue, so a cursor a canary host signed for an hour stops working after ten minutes too. Migrate a host that describes the lifetime by saying ten minutes, and a caller that holds cursors longer by calling again without `cursor`. - `pomerado/core/mint/live-tests` no longer exports `checklistOf`, `ChecklistItem`, `itemViews`, `ItemView`, `ItemStatus`, `repeatRuns` or `LiveTestSkip`, which 0.4.0 canaries shipped: the host derives no test checklist. A test case's `covers` is now `purpose`, and a cases file's `skipped` is now `notTested`. Migrate a canary host that read them by reading each case's `purpose` and the file's `notTested`. - The local host stages beside authored source exactly the modules `pomerado/runtime` loads, plus `runtime/index.js`, and its own `node_modules/pomerado`, which exports only `./runtime`. - `privacy/*`, `runtime/browser-execution.js`, `runtime/capture-diagnostic.js`, `runtime/diagnostic-reasons.js`, `runtime/failure-detail.js` and `runtime/kernel-operation-validation.js` are no longer staged, and an import of the package's internal modules, such as `pomerado/core/runtime/operation`, no longer resolves for authored code. A saved integration that imported one fails to load. - Migrate such an integration by importing from `pomerado/runtime` or `../../runtime/index.js`. - The `mint_update` refusal reason `maintenance` is now `maintenance_setting`, and maintenance adds `output_outside_maintenance` and `owner_unavailable`. Migrate by matching `maintenance_setting` where you matched `maintenance`. - `ReviewTurn.missingRead` is now `ReviewTurn.followUp`, and `requiredReadRounds` is now `reviewFollowUpRounds`, three rounds: a reviewer sends the host's follow-up in the same review for an outcome the review's kind does not return, once, as well as for a skipped entrypoint read. `ReviewFailure` adds the code `InvalidOutcome` for an outcome still refused after that follow-up, which is not retried as an outage. A private host kind gets no follow-up and fails `InvalidOutcome` at once. `MintHarnessSnapshot` adds the optional `invalidOutcomes`, the attempt's count of such failures, which old and new workers each restore. - Migrate a host's own `Reviewer` by calling `turn.followUp` where it called `turn.missingRead`, for up to `reviewFollowUpRounds` rounds, and handle `InvalidOutcome` where it handles `InvalidDecision`. - A review that returns an invalid outcome twice in a row in one attempt now ends the attempt as incomplete with no host failure, unless the host became unavailable, in which case the attempt ends with the host's failure instead. Diagnostics, including the `mint.execution_failed` event, now report `code: "InvalidOutcome"` for it. Migrate by not treating it as an outage: the first one comes back to the minter as the `mint_update` status `review_invalid_outcome`, now in `taskUpdateStatuses`. - A maintenance `mint_update` output change may also `add` a field or `tighten` one (optional or nullable to required, or a narrower type), with no confirmation; a `remove` needs `reason`, or it is refused as `output_removal_reason` before review. `PendingTaskUpdate.maintenance.confirmer` may be `none`, for an update that only adds or tightens output fields. A host that compares a repair's output schema against an applied output change must ignore `add` and `tighten`, which loosen nothing. - Guardian's execution review returns a required `action` label, `read`, `write` or `authentication`, in the shared output format. An execution allow without one fails to decode and is retried like an outage. An allowed `write` on a step without the new `PendingExecution.writeAuthority` becomes a denial. - Migrate recorded Guardian responses by adding `action` to every execution allow, and set `writeAuthority` on the steps that may write. - `MintDependencies.reviewAndExecute`'s dispatch fence is a function of the allow it follows, so every dispatch carries a label: `beforeDispatch(allowed: AllowedExecution)`. Pass the review ID and Guardian's label, or `hostAuthentication` for a sign-in screen the host fills itself. - The local host's blind-repeat guard and `replayedWriteStep`, `writeStepDigest` and `WriteStep` from `pomerado/core/mint/step-checks` are removed. The harness now refuses a step that repeats an earlier write's entrypoint until the outcome review finds that write did not happen. - `sessionSentWrite` and `checkWriteSession` from `pomerado/core/mint/write-session` decide whether a write session sent its write from Guardian's labels and the outcome review, not from request counts or commit marks. They take `{ steps }` without `nonReadRequests`; each step carries its `action` label, `possiblySent` and its newest `assessment`. `write_not_submitted` means no step recorded a confirmation and no act step Guardian labelled a write may have reached the site, or the outcome review found each did not happen. - The minter's prompts are one text for every host. These 33 named authoring sections are now shared text, so a host that composes its own text into the sections drops them. They are `core.example-input`, `core.schema-coverage`, `core.stale-session`, `core.testing-reference`, `core.completion`, `auth.login-url`, `auth.signed-out-flow`, `auth.partial-flow`, `auth.step-fields`, `auth.code-rejection`, `auth.next-screen`, `auth.signed-in-evidence`, `auth.sign-in-check`, `caller-input.declare`, `forms.saved-steps`, `pagination.no-recreated-writes`, `writes.session`, `writes.session-limits`, `writes.finish`, `writes.run-outcomes`, `publication.description-reader`, `publication.login-url`, `publication.confirm-steps`, `publication.input-feedback-end`, `publication.confirm-action`, `agents.workspace-map`, `agents.tools`, `agents.guardian-records`, `agents.guardian-view`, `agents.repeatable-reads`, `agents.write-builds`, `agents.tests` and `agents.capacity`. The other 69 sections stay. The new `core.standalone-completion` section ends the core skill with the local host's summary of its execution and completion, the text `core.completion` held before, so a host that composes its own text adds it. The new `writes.uncertain-status` and `writes.unverifiable-status` sections hold the status a run of a write reports when its outcome is uncertain, `may_have_applied` on the local host. - `GuardianExecutionEnvironment` gains a required `dataVendor`. It holds the off-site rule's one exception, for the site's own third-party data vendor, and the evidence that shows it on that host. It follows the authorized-site rule in the execution policy. The local host's text says it keeps no network captures, so a request that carries the caller's input to another registrable domain escalates like any other caller data sent off-site. - Migrate by passing the exception your host can evidence. The exception 0.3.0 gave every host starts "The site's own third-party data vendor, such as a hosted search service, is the one exception" and relies on screened network captures. - The package adds a `testing` skill, loaded after `auth`. Its sections `testing.offline-fixtures`, `testing.saved-targets`, `testing.capture-evidence` and `testing.later-cases` have no standalone text. A host that ships its own `testing/SKILL.md` drops it and composes its text into those sections instead. - `saveSessionCode` from `pomerado/core/runtime/start-state` is now a function of the primary tab's target ID and the site's origin. Its browser call returns a `SavedSession`, `{ state, sessionStorage }`: the context's storage state, and the primary tab's session storage from its frames on the site's registrable domain. - Migrate by calling `saveSessionCode(targetId, siteOrigin)`, decoding its result with `SavedSession`, and passing that as the `session` of a `restore` `PageStart`. - `resetPageCode` takes the primary origin, the site data, the session to restore and the tab storage to restore as separate arguments, then optional extra origins to clear, `cleanupOnly` and `keptCookiePrefixes`. Cookies whose names start with one of `keptCookiePrefixes` survive a clear. `PageStart`'s `restore` takes a `SavedSession`. - Migrate by calling `startPage`, which builds the call from a `PageStart`, or by passing the arguments in the new order. - A sign-in through a security question is saved in `auth-fill.json` as recipe version 1, or version 2 with a popup or approval, with the question's selector on its field. It was version 3. This version still reads version 3 recipes, so nothing has to be built again. - 0.3.0 and every later version before this one, 0.4.0-canary.45 to 0.4.0-canary.47 included, refuse such a recipe. A run of an integration built this way fails there with `SignInRunFailed` (`MissingRecipe`, `invalid`) before it signs in or runs anything. - Migrate by running such an integration with this version or later, and not rolling back once one is built. - A run reports a write that returned without recording its declared confirmation as possibly applied, not as a success. A write declared `unverifiable` reports the same. This holds for `run`, `pomerado run` and a served integration MCP. - A served write tool whose script declares no write and never calls `verified()` also fails as possibly applied. `pomerado run` is given no effect, so it still returns that script's output as a read. - `run` fails with the new `RunOutcomeFailure`. Its `outcome` has the code `outcome_unknown`, the write status `may_have_applied`, `possibleCommit: true` and the retry class `never`. Its `unconfirmed.output` keeps what the script returned. - Migrate by catching `RunOutcomeFailure` and reading the site back before any retry. - Every other run failure that is not a sign-in failure also fails `run` with `RunOutcomeFailure`, including a failure before sign-in, such as the site check, the URL or the start page. Its `outcome` names a finite code, the write status, whether a step may have committed and a retry class. A sign-in failure still fails with `SignInRunFailed`. - A write's declared commit steps that were never entered show that nothing was applied only when the site refused the input or the login, or the run couldn't sign in again. Any other failure after a browser step ran may have changed the website. - Migrate by reading `outcome.code` in place of the operation's own error. - A served run's failed job carries `code`, `write_status`, `possible_commit` and `retry`. Its `error` is one sentence for its code, and only a possible commit tells the caller to read the site back before any retry. The sentence that a dispatched action may have taken effect no longer follows every run failure. - Migrate by reading `possible_commit` and `retry` in place of parsing `error`. - `pomerado run` prints an unconfirmed write's output on stdout and its outcome on stderr, and exits 1. - The minter changes its task with the new `mint_update` tool once the caller confirms the change: input values, a requirement, constraint or prerequisite, the purpose, a read becoming a write, the target site or the login. It replaces the write upgrade and dropping a prerequisite on an answer alone. - Removed: `AgentRequest.writeUpgrade`, `MintDependencies.upgradeToWrite`, the `writeUpgrade` option of `MintDependencies.reviewQuestion` and `PendingQuestion.writeUpgrade`, `MintHarnessSnapshot.writeUpgradeDeclined`, `intentWithApproval` from `pomerado/core/mint/review-context` and `writeUpgradeApproval` from `pomerado/core/mint/step-checks`. An older checkpoint's `writeUpgradeDeclined` is dropped as an excess field. - Migrate by implementing `MintDependencies.reviewTaskUpdate` and `applyTaskUpdate`. `reviewTaskUpdate` runs `makeGuardian(...).reviewTaskUpdate` against the same pending review the host builds for any other review. `applyTaskUpdate` stores the harness checkpoint it is given (`TaskUpdateApplication.harness`, with the update applied) together with its own bindings, rebinds everything bound to the site for a site change, reruns intake screening and the duplicate check, and resolves the login, all or nothing. It answers `applied` for `next` exactly as given, or `clarification_required` or `refused`. A takeover that restored the checkpoint answers the same `mint_update` again as `updated` without another review, by the update's `requestDigest`. Without both hooks the minter isn't offered `mint_update`. - Every review after an update must carry `PendingExecution.taskUpdates`, with `allowedOrigins` rebound to a changed site, and each recorded execution its `taskRevision`. `MintHarnessSnapshot.purposes` entries carry an optional `taskRevision` too. `screenedIntent` is the intent as the owner submitted it. - `mint_update` takes a `recommend` field, the minter's own judgment: `update` keeps the same task and workflow, and `new_mint` ends the build blocked with its `suggestedRequest`. Besides `updated`, `clarification_required`, `reword`, `new_mint_recommended` and `review_unavailable`, a result may be `update_refused`, when this build can't take the change (a site change while a write session is open or once the build can't run another live example, any change in maintenance, a recommended new build included, or a host refusal), or `update_invalid`, for a request that doesn't decode or a `new_mint` recommendation without a suggested request. Nothing changes on either. A site, login or effect change without an answer in `confirmedBy` is answered `clarification_required` before any review. After a site change, `finish_build` publishes only an execution that ran on the new site (`not_published` with reason `example_before_site_change` otherwise). - A read build that already ran a live read example can become a write through a confirmed `mint_update`. The write session takes its own example claim, so `claimExample` is called again when its first act step starts, and `finish_build` publishes only what the write session did (`not_published` with reason `example_before_effect_change` for an execution from before the switch). - An option the minting agent wrote is the owner's words once they pick it or type it back. `ownerNamedOrigins` now takes off-site origins from every answer, picked option labels and notes that repeat an option included, and Guardian's policy says so. `AnsweredQuestion.typed` is removed, and `answersForReview` keeps a note even when it repeats an offered option. - Migrate by dropping any use of `typed`. A decoder that ignores excess properties drops it from a stored answer; one that refuses them needs the field removed first. - Guardian's shared output format adds the outcomes `clarify` and `new_mint`, for the new `update` review kind. `GuardianDiagnostics` correlations add the `update` review kind. - `JobBuildBlocked` and `MintOutcome.blocked` add the reason `new_mint_recommended`, with an optional `suggestedRequest`. - New exports: `TaskChange`, `TaskUpdateDecision`, `PendingTaskUpdate`, `ReviewedTaskUpdate` and `taskUpdateForReview` from `pomerado/core/guardian/task-update`; `TaskUpdateRequest`, `taskUpdateStatuses`, `TaskUpdateStatus`, `TaskState`, `AcceptedTaskUpdate`, `TaskUpdateCandidate`, `TaskUpdateApplication` and `TaskUpdateHostResult` from `pomerado/core/mint/contracts`. - A local build's step asks only the questions its entrypoint declares as a plain literal in its one `defineOperation` call. The host reads them from the source, not from the running script. An ask whose id, prompt or bounds differ fails as `Undeclared` before Guardian's question review, and nobody is asked. A declaration held in a variable, imported or computed declares nothing. - Migrate by writing `questions` as an object literal inside the entrypoint's `defineOperation` call. - A step whose literal `questions` names an invalid id is refused before review, with each id it names. - A run asks only the questions publication reviewed. A published artifact keeps them as `questions`, an empty object when there are none, and `pomerado.json` saves them. An artifact saved by an earlier release has no `questions`, so it asks only what its entrypoint declares as a plain literal. - A publication finding carries a required `explanation`: what is wrong, the evidence and the fix, at most 800 characters. Guardian's output format requires it, and a decision whose finding has none fails as `InvalidDecision`. `finish_build` returns each finding's screened explanation and its workspace `file` beside Guardian's `path`, and tells the minter to fix every finding and call again. - Migrate by adding `explanation` to every `PublicationFinding` a host makes itself, and to a host's own publication decoder. - `example_value` is no longer an input finding. An input narrowed to the example's value blocks publication with reason `source_correction`; a decision that returns it as `input_feedback` fails as `InvalidDecision`. - Migrate a host's own publication policy by listing only `account_specific_enum` and `input_option` under `input_feedback`. - A publication finding in Guardian's shared output format carries `quote`, the exact text at fault, in place of `byteStart` and `byteEnd`. The host finds the quote's UTF-8 range in the file Guardian read, first exactly, then with any run of whitespace standing for another. A quote the file does not hold, or a file the host cannot read, puts the finding on the whole file, and the denial stands. The decoded `PublicationFinding` still carries `byteStart` and `byteEnd`, and a reviewer that returns a byte range keeps it. - Migrate recorded Guardian publication responses by replacing each finding's byte range with its quote. A host's own publication policy should ask for a quote; core exports its wording as `publicationDecisionPolicy`. - Guardian's `read_source` tool takes a required `match`, null for a plain read. With a word or phrase, it returns `untrusted_source_matches`: the slices of the whole file around each case-insensitive occurrence, up to 20, each with its byte offset, and the match count. Guardian queries a capture or other large file this way instead of reading it whole. `publicationCaptureQueryPolicy` is the policy's wording for it. - Migrate recorded `read_source` calls by adding `"match": null`. A call without it still reads a chunk. - The authored runtime's `browser/index.js` now imports `browser/outcome-wait.js`. Until hosts compute the SDK's file list from its imports, a host that copies a hand-kept list of the SDK's files into a minting workspace or a run's sandbox must copy it too, or every authored operation fails to load. - Migrate by adding `browser/outcome-wait` beside `browser/form-controls` in that list. - Guardian's publication findings add the category `safety_default`: a safety default that an earlier copy of the source set and the current source drops or weakens. It blocks publication with reason `privacy`, and Guardian's shared output format lists it. A build's `publicationDenial` and a `MintHarnessSnapshot` can carry it, and an earlier version doesn't decode either one that does. - Migrate a host's own publication decoder by accepting the category, and a host's own publication policy by including `publicationSafetyDefaultPolicy` (see Other changes). - Run outcomes add the code `input_not_applied`, retry class `same_key`, so a host's `FailureRenderer` must word it. A script throws the new `errors.InputNotApplied(message, { field, requested, applied?, step, siteMessage? })` when the page did not apply a valid value the caller supplied, such as a location, store, filter, sort or date it ignored, reset or replaced with its own. The runner code is `InputNotApplied`. The run fails rather than return results computed without the caller's value, and the outcome's details carry `field`, `requested`, `applied`, `step`, `site_message` and the script's `reason`. Only a read or a run that left the site untouched gets it: a write that may have changed the site keeps `outcome_unknown`, a confirmed write `execution_failed`, and so does a run that judged a step may have committed. A numeric part, such as a ZIP code typed as a number, is kept as its text. `RunEvidence` gains `unapplied_input`, and `pomerado/core/runtime/operation-failure` exports `boundedInputDetail`, `InputNotAppliedDetail` and `maximumSiteMessageLength`. - 0.4.0 canaries shipped this for locations only, as `errors.LocationNotApplied`, the code `location_not_applied`, the runner code `LocationNotApplied`, the error's and the local protocol's `location` detail, `RunEvidence.location` (`RunLocationEvidence`), `boundedLocationDetail`, `LocationNotAppliedDetail` and `maximumLocationMessageLength`. `errors.LocationNotApplied` stays as a deprecated alias of `errors.InputNotApplied`, the same class, so a published tool that throws it keeps running and now ends `input_not_applied`. The classifier still reads a record's `failure_reason` of `location_not_applied`, and its `location` detail when it has no `unapplied_input`, and reports it as `input_not_applied`; `runOutcomeCodes` no longer lists `location_not_applied`. The live tests still pass a case that expects `error` on the runner code `LocationNotApplied`, which an older runtime reports, and `finiteRunnerErrorCode` still keeps it. - Migrate a canary host's renderer by wording `input_not_applied` in place of `location_not_applied`, its record of a run by passing `unapplied_input` where it passed `location`, and a hosted runner by passing `InputNotApplied` and the error's `unapplied` detail through as it passes `InvalidInput` and its refusal. A host that stores the failure reason should accept `input_not_applied` before any runner reports it, and keep reading `location_not_applied` for records and runners from earlier releases. A tool minted with `InputNotApplied` fails as an ordinary script error on a runtime from before this change, so upgrade a host's runners before its minter. - `waitForOutcome` (in `outcomeWaitCode` and `waitCode` from `pomerado/runtime`) now fails as `outcome_unknown` once the page shows none of its outcomes and no progress for `noProgressMs`, 8 s by default, where it used to wait out its whole `timeout`. A slow page with no sign the wait recognizes, or whose API is on another registrable domain, can now fail before its `timeout`. - Migrate code that relied on waiting out a long `timeout` by passing `noProgressMs` equal to that `timeout`, or by naming the site's loading sign as `loading` and the part of the page the answer appears in as `region`. - A minter's question left unanswered no longer ends the build. `request_input` returns `no_answer` when nobody answers before the question expires, and the minter decides how to go on: with a sensible default it lists in its `finish_build` assumptions, a narrower tool, or `report_blocked`. No answer is never consent, so a `mint_update` whose `confirmedBy` names that question is refused as `clarification_required`. The read-or-write question, the capability question, any question in a write repair and any question asked while the build is signed in or holds a login still end the build as `no_response`, and so does an unanswered sign-in request. Each question is decided when it goes unanswered: the build is signed in or holds a login once the host has one bound (`websiteCredentialsAvailable`), `requestLogin` answered `supplied` or `held`, or a sign-in in the build sent one, so a build that starts signed out hands back its earlier questions and ends on one asked after it signs in. The checkpoint keeps an obtained login as the optional `loginHeld`; a checkpoint written before it existed doesn't record a login obtained through `requestLogin`, so a build restored from one counts that login only once a sign-in in the build sends it. A host with no caller to ask hands the question back the same way, by failing its `askInput` with `noResponse`; the local host now does so when its caller leaves the minter's question unanswered, where it used to report the question as not asked. An owner's cancel, such as Ctrl-C at the terminal, is a choice to stop: a host marks it with the new optional `MintFailure` field `ownerCancelled` beside `noResponse`, and the build ends as `no_response` without handing the question back. The local host marks the terminal's cancel this way. - At most two unanswered questions, the minter's own and its scripts' together, go back to the minter in one build; the next one left unanswered ends the build as `no_response`. The count is kept in the harness checkpoint as the optional `noAnswerHandBacks`. - Once a question went back unanswered, `report_blocked` with reason `policy` ends the build, so the minter can stop instead of going on past what it asked. - An example or `act` step whose script question goes unanswered no longer ends the build either: its result carries `scriptQuestion` with the new outcome `unanswered` and an instruction, outside a write repair and a build that is signed in or holds a login when the question goes unanswered. `ScriptQuestionOutcome` gains that variant, whose `requestId` is optional. - Migrate a host that handles `ScriptQuestionOutcome` exhaustively by adding `unanswered`, and a host that waited for `no_response` to end a build after an unanswered question by reading the build's own ending instead. - A Guardian review no longer has a deadline, so a review that ran past it is no longer started again from scratch, repeating all its work. A slow review runs to its decision. It is bounded three ways instead, and none of them starts the review over: - Its turn limit, 12 turns by default and 32 for the core publication review, now ends in one more turn that offers no source read, with a note to decide from the evidence already in view. The review returns that decision. Only a review that still returns none fails `TurnLimitExceeded`. - Each model call has its own bound. A call with no response for five minutes is aborted and sent again with the same input, at most twice, while the review's reads and earlier calls stand. Each such retry emits `guardian.model_call_retried` with `attempt` and `timeoutMs`. A call that stalls every time fails the review with the new `ReviewFailure` code `ModelCallStalled`. The bound is `guardianModelCallBound` (`{ timeoutMs, retries }`, type `GuardianModelCallBound`) from `pomerado/core/guardian/model`, and a host can set its own through the new `modelCallBound` in `GuardianModelOptions`. - The outage retry still runs a new attempt after a provider error, a decision that does not decode or a required source read that failed. Its `budget` now counts from the first failed attempt, not from the start of the review, which no longer has a deadline, so a long review that then meets a provider error is still retried. `TurnLimitExceeded` and `ModelCallStalled` are no longer outages: `guardianOutageRetry` returns them at once rather than starting the review again. - `ReviewFailure`'s `reviewPhase` no longer has `review_deadline`, which nothing produces now. `guardianReviewTimeout` is deprecated and nothing reads it; it stays so a host that imports it still loads, and will be removed. The second argument of `makeOpenAIReviewer`, `developmentPublicRead`, chose a longer deadline and no longer has an effect; it stays so callers compile. - `pomerado.hostProtocol` stays 1: no `./core/*` export is removed. Migrate a host that matched `reviewPhase` `review_deadline` by dropping that case, a host that maps each `ReviewFailure` code by adding `ModelCallStalled` as a review that could not finish, and a host that bounded reviews with its own timer around a review by relying on the per-call bound instead. A host that wraps the reviewer with `guardianOutageRetry` needs no change. - `errors.InvalidInput` keeps an explicitly empty list beside the field it names: `new errors.InvalidInput(message, { field, available: [] })` now carries `available: []`, which says the page offers nothing for that input, so a host that repairs can settle it as it settles a refusal with choices. A missing `available`, an empty list beside no field and a list of blank labels still carry none. `boundedRefusalDetail`, the local operation protocol and the local host's `input_rejected` outcome are unchanged: they carry only a list with at least one choice. - Migrate a host that reads the error's `available` by checking its length, not only that it is present: an empty array means the page offers no choice, not a list to show the caller. ### Other changes - `inspectAutofillStep` in `pomerado/core/destinations/autofill-step` takes an optional `trustSignInUrl(url: URL): boolean`, a host's own trust of more sign-in addresses, for a host whose user allowed a sign-in to go wherever the site sends it. Core asks it about each address of a sign-in step's fields, submit and popup that is neither on the site nor an `authenticationOrigins` entry, and only about one core would let a host trust: an https address with no user name or password, on a host with a registrable domain under a listed public suffix. Plain http, IP addresses, localhost, bare public suffixes, names only a private network resolves (under .localhost, .internal, .local, .test or .invalid) and hosts under .arpa or .onion stay refused, and the host is never asked about them. A card-fill step, and every part of a payment card, never use it. The inspection keeps the predicate as `trustSignInUrl`, and `fillAutofillStep` asks it again about a control that moved. Since it is also asked after typing, about addresses the page shaped, the host must never log, keep or forward a URL it is asked about. A predicate that throws trusts nothing. Refusal evidence names origins as before, so once anything was typed an origin no inspection judged is still named `other`. Unset, every check and every inspection is the same as before. `untrustedTarget`'s trust takes the same optional field. - The cart skill's refusal of an order the site refuses as asked, such as one under its minimum, names the input: `new errors.InvalidInput(message, { field })`, `field` the input the caller would change. - A write finishes every check that can refuse before its commit mark. The writes, cart, caller-input, core and publication skills and the workspace guide say that choosing a price-changing option, deciding an add-on or offer, reading the total and comparing it with a limit the input sets, and checking availability, the items, the address and the payment all happen before `enteringCommit`; the marked call only re-reads what a change would make wrong, clicks once and confirms. A refusal throws `InvalidInput` before the mark with `field`, a stable `kind` such as `over_price_limit` and the observed values in its message, so the run reports that it changed nothing. A refusal returned as output, or thrown after the mark, reports a possible change. A check that can only run after an earlier marked step runs before the later step's mark, and its refusal reports a possible change. The `write-session.ts` reference now checks its total against an optional `max_total_minor` input before its mark and guards the total inside the marked call. - A paid add-on or pre-selected paid option within the tool's purpose, the payment method and the delivery address are inputs or run-time questions. A checkout never uses the page's pre-selected address or payment silently, and never hard-codes a choice the build's request or the owner's answers settled, since the published tool serves other callers. A paid upsell outside the tool's purpose, such as a membership offered at checkout, is declined without a question and never accepted without an input the caller set. - The forms skill tells conditional pop-ups (shown only for some accounts, states or visits, such as a promotion or a membership upsell) from ones an action always opens: a conditional one is handled when it shows and never waited for or required. The `write-session.ts` reference declines a membership offer only some accounts get. - Guardian's publication policy adds a confirmation finding for a check that can refuse made inside or after the marked call of the commit it guards, or a refusal returned as output (a re-read that stops before the click, and a check after an earlier marked step, are no finding), and a source-correction finding for code that requires a conditional pop-up. Its `input_option` finding covers the payment method, the delivery address and a hard-coded build-time choice, and names what is no finding: declining an out-of-scope upsell, a safety default, and dismissing a dialog with no paid or private choice. Guardian's execution review treats declining an out-of-scope paid upsell, or dismissing a dialog with no paid or private choice, as no missing input, so a live write step that does either is not escalated. A recorded Guardian response keyed on the exact policy text needs recording again. - Guardian's review of a blocked explanation reads a decision ledger the harness writes, so it judges the report against what the build's reviews decided and its live tests showed, not against what its own compacted conversation still holds. `reviewQuestion` receives the optional `decisionLedger` (type `DecisionLedger` in `pomerado/core/mint/contracts`) for a blocked explanation, and Guardian reads it as `question_review.decisionLedger`. - The ledger lists each Guardian denial or escalation of an execution, a live test batch or a publication in the build, with its outcome, reason, review id and the rationale and finding explanations Guardian wrote, screened as they are for the minter. It counts each live test case's latest verdict and lists the cases whose latest result did not pass. A failing case's error class and message sit under `toolReported`: they are what the agent's own tool code threw, and the policy says they show how the tool failed, never why the site behaved as it did. Only the denials, their review ids, the verdicts and the counts are host facts. - It keeps the latest 12 denials and 20 failing cases, caps each rationale at 800 characters, each of up to 4 finding explanations at 300, each message at 300 and each error class and result at 120, and counts what the bounds left out (`omittedDenials`, `omittedFindings`, `omittedTestFailures`). - `MintHarnessSnapshot` adds the optional `decisionLedger`, the denials so far, so a takeover keeps them. An attempt that restores a checkpoint without it, from an older worker, marks its ledger `partial`, and the policy says a denial missing from a partial ledger proves nothing. An older worker ignores the field. - `PendingQuestion` and `questionForReview` add the optional `decisionLedger`. A host that builds its `PendingQuestion` from named fields forwards `decisionLedger` to give its blocked reviews the ledger; one that does not keeps today's review. A recorded Guardian response keyed on the exact policy text needs recording again. - Guardian's `read_source` tool reads up to 10 files in one call: with `path`, `offset` and `match` null, `paths` lists each file with its own `offset` and `match`. The result, `{ kind: "untrusted_sources", files }`, holds each file's chunk or matches in order; a file that cannot be read has its own `status: "unavailable"` entry and the others still return. Once a call's results reach 200,000 characters, its remaining files come back `not_read` for another call. Each file is its own read through the host's `readSource`, so it counts as Guardian's read for the entrypoint check, the publication round's whole-file reads and a session's unchanged sources, as a call naming only it would. A single-file call is unchanged, and a recorded call without `paths` still decodes. - The instructions every review kind shares ask Guardian to request the files it already knows it needs in as few calls as possible, up to 10 files each, and to read again only for files or chunks those calls show it needs; the core publication policy says the same. - A call the host cannot read, such as one naming more than 10 files or setting both `path` and `paths`, reads nothing and tells Guardian what to change, such as to split the files over several calls, instead of reporting the source unavailable. A recorded Guardian response keyed on the exact instructions or tool schema needs recording again. - `pomerado.hostProtocol` stays 1: no `./core/*` export changes, and a host's `readSource` serves each file as before. - `lintSchemas(inputSchema, outputSchema)` in `pomerado/core/runtime/output-lint` adds two advisory checks of a tool's declared schemas, never of its values: `loose_text`, a required output string whose schema accepts an empty string or whitespace alone, and `looser_than_input`, an output field looser than the only input field of the same name (an enum dropped or widened, a format's pattern, one that refuses plain words, neither repeated nor replaced by an enum, a smaller `minLength`, or optional or nullable where the input is required). Neither blocks. A schema that refers to itself is walked once per definition, and a walk records at most 2,000 fields. `outputChecks` and `outputCheckMeaning` list both. `lintOutput` never reports them, so a run's output checks and telemetry are unchanged. - `outputNotes` from `pomerado/core/mint/output-notes` takes an optional `inputSchema` and adds these findings to `publication/output-notes.json`. The local host passes it at publication, and adds the findings to an example's output-check notice for the minter. - Guardian's `publicationOutputNotesPolicy` reads them as leads for the Loosened needed value rule, on needed values only. The rule itself now asks for every needed value's field to be judged in one pass, required, not nullable, non-empty and not whitespace alone, its format held by a pattern where it has one, and no looser than the input it reports, and a later review of the same build to judge what changed. - The core skill asks for inputs and outputs typed as strictly as their values, with `Schema.NonEmptyTrimmedString` for required output text and `Schema.NonEmptyString.pipe(Schema.pattern(/\S/u))` for required input text, which still takes a caller's stray space, a pattern for a date, time or code, and an output that reports an input's value reusing that input's enum or pattern. - Guardian's publication review quotes a line or two of the text at fault without hunting for a unique quote: the host already places a quote at its first occurrence, or on the whole file, and the finding stands either way. - Guardian's publication policy adds `publicationReferencePolicy`: the SDK's behavior is in `pomerado/browser/index.js` and `pomerado/runtime/*.js` and the minter's skills in `.agents//SKILL.md`, which the reviewer may read on demand where the host serves them; a path the host does not serve is not missing evidence. The local host serves the SDK's modules under `pomerado/`, a `.ts` or `.d.ts` spelling as the same-named `.js`, and its skills to the publication review through the new optional `references` of `PublicationReview` and `PublicationCandidate` in `pomerado/core/mint/publication-review`, which stay out of the publication index. `skillReferenceFiles` in `pomerado/core/mint/skills` lists a host's skills at those paths. A recorded Guardian response keyed on the exact policy text needs recording again. - A host can choose Guardian's reasoning effort for each review. `specialize` in `GuardianModelOptions` from `pomerado/core/guardian/openai` may return `reasoningEffort`, `low` or `medium` (type `GuardianReasoningEffort` from `pomerado/core/guardian/model`), and the review's request uses it on the same model as every other review. A review whose host returns none runs at `medium`, as before. Core picks no effort itself. `GuardianUsage`, and so each review's `guardian.usage`, adds the optional `reasoningEffort` the review ran at. The request's effort can change the provider's hidden instructions, so a review at a different effort from the review before it may read the session's history without its cached prefix: compare `cachedTokens` across efforts before relying on it. - `MintOutcome` adds the optional `stopReason` (type and schema `MintStopReason`, values in `mintStopReasons`), which names why an unpublished build stopped: `final_text_without_tool`, `turn_budget_spent`, `model_quota_exhausted`, `sign_in_failed`, `account_locked`, `question_unanswered`, `owner_declined`, `site_lacks_capability`, `policy` or `host_unavailable`. It also adds the optional `stopExplanation`: the minter's last answer when it stopped without a tool call, screened for credentials and host-private values and kept only when Guardian's question review of a blocked outcome allows it. `MintFailure` adds `turnBudgetSpent`, and `MintModel.run` may return `finalText` beside `stopReason`. - A blocked build's `summary` carries the minter's explanation only when Guardian allowed its caller to read it. - Guardian's publication review may return `publicReason`, one short plain sentence about why a tool was not published, apart from the private rationale. The shared output format adds the field, null for every other kind. `PublicationDenial` adds the optional `publicReason`, screened for credentials and host-private values and cut to `publicReasonLimit` (300) characters. A recorded Guardian response keyed on the exact output format or publication policy text needs recording again. - The owner may decline a login. `CredentialQuestion` adds the optional `allowDecline`, and `CredentialAnswer` the optional `declined` (`LoginDecline`: `no_account` or `public_only`), which holds no login and saves nothing. `MintDependencies.requestLogin` may answer `no_account` or `public_only`, and a sign-in may fail with `MintFailure.loginDeclined`. The minter hears either as a `login_declined` result, never a failed sign-in, and builds the part of the request the site shows signed out; the host signs in no more and raises no further login request in the build. `MintHarnessSnapshot` adds the optional `loginDeclined`. The local host offers the decline on its first login request, and at the terminal an empty username declines. - `MintDependencies.abandonSignIn` (optional, answering `AbandonSignInResult`: `abandoned`, or `refused` with a reason and notice) offers the minter `abandon_sign_in`, which gives up signing in so the build goes on signed out. It holds the execution permit, Guardian does not review it, and the host decides whether a build may: it refuses a write build, a task that needs the account and a verified sign-in. `MintActions` and `HostToolDescriptions` add the optional `abandonSignIn`. - `report_blocked` no longer refuses with `sign_in_unresolved` once sign-in is unavailable while a retained receipt held the ending back: it ends the build with the sign-in's own result. - Once a build abandoned its sign-in, through `abandon_sign_in` or an applied `login` change `signed_out`, it stays signed out: `authenticate` comes back `sign_in_abandoned` without running and a question routed to sign-in asks for no login, until a later applied `login` change `sign_in` or `different_login`. `MintHarnessSnapshot` adds the optional `signInAbandoned`, so a takeover keeps it. - `MintDependencies.stopExplanationReviewMs` (optional, one minute by default) bounds the review of a minter's last answer; past it the outcome keeps no `stopExplanation`. A restored ending whose login check was unavailable reports `stopReason` `host_unavailable`. A login the owner declined records no sign-in execution, and after an earlier failed sign-in, a declined login request drops the failed sign-in's guidance. - The auth skill tells the minter to report an account the site locked and what to do after a declined login, the core skill and workspace guide to ask early about a required value the site never shows, and the workspace guide and `report_blocked` description name the new reasons. - Sign-in pieces the local host runs are exported for other hosts: the recorded rejection marker read (`autofillMarkerVisible`) from `pomerado/core/destinations/autofill-marker`, the signed-in marker's settle wait (`settledMarkerCheck`, `markerSettle`) from `pomerado/core/destinations/marker-settle`, the login prompt and notice texts (`credentialPrompt`, `credentialMessage`, `correctionTriesLeft`) from `pomerado/core/runtime/credential-prompts`, the rules for reusing an answer to a script's question (`scriptQuestionKeys`, `reusedScriptAnswers`, `keptScriptAnswers`, `forgetScriptAnswers` and the `KeptScriptAnswer` schema) from `pomerado/core/mint/script-answer-reuse`, and the login field helpers from `pomerado/core/runtime/login-values`. No prompt or notice text changes. The local host's `check_signed_in_marker` now stops checking a loaded page once its next half-second check would start more than 10 seconds after the first, rather than checking once more at the 10-second mark. Its rejection marker read no longer refuses a screen whose address carries a user name or password, and a recorded popup reported missing when the step recorded none fails the read rather than reading as no marker. - A publication refused, fixed and submitted again in the same build is reviewed against what changed since its last review instead of from scratch. Guardian keeps each completed publication round, its decision, each file's digest and screened text within a bound, and which files a review read whole, in its session, and the next round's request adds `publication_delta`: the prior decision and findings with their quoted text, each indexed file as unchanged (and read whole), unchanged but never read whole (a search, which shows only slices around each match, doesn't count as a read: its reads still go through `ReviewTurn.readSource`, so a host that wraps it keeps seeing them, inside the new optional `ReviewTurn.uncountedReads`), changed (with a unified diff, or the whole file when that is shorter), added, removed or not compared, and the other request fields that changed. `publicationDeltaPolicy` follows the review's policy, a host's own included, and asks Guardian to check each prior finding against the change, review what changed, judge unread and uncompared files as in a first review, and leave only read, unchanged files alone unless a change makes them relevant. Every file stays readable and the outcomes are the same; a first round is unchanged. - `GuardianSessionSnapshot` from `pomerado/core/guardian/session` adds the optional `publicationRound`, so a host that saves and resumes the session keeps the record across a takeover with no change. It is stored loosely and decoded when used, so a round this release cannot read never fails the restore; it is dropped. An older snapshot has none, and its next round is reviewed as a first one. - `ReviewSources` from `pomerado/core/guardian/review` adds the optional `publicationDelta`, and `guardian.started` for a later round counts its files by state under `delta`. Every publication review reads every indexed file through the host's reader once, up to 4 MiB each and four at a time, without counting it as Guardian's read; a file longer than that is not compared. `wholeSource` from `pomerado/core/guardian/source` is joined by `completeSource`, which returns undefined rather than a prefix for such a file. - A host that sends small publication files inline in its own request can leave out the ones the delta lists as unchanged. A recorded Guardian response keyed on a later round's exact request text needs recording again. - The minter asks one question round per form page. The forms skill now opens with that rule, so a builder reads it before its first question: before the first `act` step it asks up front for every field it can see or anticipate, and on each later page it first fills every value the request, the input and earlier answers supply, then asks everything still open on that page in one `request_input`. A field that appears only after an answer is still asked in place. The rule moved from the skill's "Multi-step forms" list, and the `forms.step-rules` section marker moved with it, still ahead of `forms.fixture-checks`. The writes skill's rule for an option the input does not settle points to it. - On a write, the workspace guide prefers keeping the value the page already shows in an optional field that adds no cost over asking the caller to confirm or correct it. The value is listed in `assumptions` and stays an optional input. Add-ons, pre-selected paid options and saved payment are still asked. Guardian's question policy prefers reword, on a write too, for a question that only asks to confirm or correct such a value, or asks about a control with exactly one possible value. A recorded Guardian response keyed on the exact policy text needs recording again. - The core skill's first bullets say to prefer a role locator's name from an ARIA snapshot over `textContent` or `innerText`, which include hidden labels, and to prefer `waitForOutcome`, with the step as its `action` and every result it can lead to named, over `waitForURL` or `waitForLoadState`, since a dialog keeps the URL and a route change without a new document passes `waitForLoadState` at once. The deeper paragraph that named every answer moved into that bullet, and the line on waiting after a navigation now prefers `waitForOutcome` with a control only the destination page has over waiting for the destination URL alone. - The core skill's click bullets prefer waiting for the page a click opens, which may be a new tab, over waiting on the old page's URL, and prefer clicking a control's visible label when its input is hidden. A script that waited on the old page after a click opened a new tab timed out, and one that clicked a radio whose input a custom control hides failed. - The core skill points the minter at the site's other tools when `reference/site-tools.json` exists: it reads the index `reference/site-tools/README.md` first, then the per-tool folders it needs, each with a `README.md` and, unless the index omits it, the tool's full source. It reuses their identifiers, field names and shapes, and copies and adapts their code into its own `src/` files, but never imports, reads or references a file under `reference/` from its code, and checks what it copies against the site as it observes it now. - The core skill says `waitForOutcome` returns the name of the answer that showed, and prefers branching on that name over waiting again for the same answers or rechecking them with `isVisible`. It also prefers the wait helpers' own settling over a tool's own loops that poll until values hold still, since `waitForRows` and `waitForValues` already return only once what they read is filled in and reads the same on two looks. - The core skill says that when the site's own search has no match for the caller's value, the tool throws `InvalidInput` with `field` and `available`, the closest entries the site showed, rather than only naming them in the message, so the host can end the run at once with those choices. - The core skill's input schema rules, beside the rule that a repair fixes a broken declared input, prefer fixing the whole class of a failure over its one instance: when the page requires a control the input has no value for, the tool prefers making every such control a declared caller question with the page's live options over throwing for it. - The minter's `read_source` reads up to 10 files in one call. `paths` lists them, each with its own `offset` and `limit`, and `path`, `offset` and `limit` are null. Each file reads as a call naming only it would, with the same default and maximum length, and the results come back in order as `{ kind: "untrusted_sources", files }`. A file whose read fails returns its own failure in its place without failing the others. A call that is cancelled or times out stops before its next file, as a single read does. Once the results reach 200,000 UTF-16 code units, the files left come back as `not_read` with reason `call_size_limit`, unread. A call naming one `path`, including one recorded before `paths` existed, reads as before. The `read_source` tool description says so. - Each file is its own host read through `MintActions.readSource` and its own `ModelObserver.tool` dispatch, with that file's `readPath` and arguments, under the model's one tool call ID. A host that counts skill reads from `readPath` counts each file whose read returned, and no other. No host interface changes. - The testing skill and the `live_tests` instructions tell a read signed out to rerun its failing cases together in one batch while a fix is in progress, not one batch each, since each batch waits on its own Guardian review. The testing skill and the `live_tests` tool description no longer say `maxWorkers` sets how many cases run at once: the `LiveTestHost` contract says a host may run a batch's cases on up to its own `maxWorkers` browsers at once, whatever `LiveTestBatch.workers` or the request's `maxWorkers` says. The `maxWorkers` field stays in the `live_tests` request, so older hosts and callers keep working. - The testing skill prefers trying other records as one batch of live cases over one probe at a time. One Guardian review covers a batch, while each probe waits on its own review, so trying records one probe at a time slowed builds. - The auth skill, beside the rule on recording an account choice, prefers exercising a chooser step from both starts in the build, right after a sign-in and from a saved session already past the chooser. A tool whose build ran its chooser step only right after sign-in failed on runs that started on a saved session, which skipped the chooser. - The publication skill's checks before `finish_build` open with one line: before the first `finish_build`, prefer checking the tool against each rule the request states for its inputs, outputs and refusals. A build took several publication rounds whose refusals mostly restated rules its request had already stated, and each round costs minutes. - The writes skill says a commit that went through, with the new record's id, already proves the write, and that a read-back after the commit compares the caller's text normalized as before it, through `normalizeText` from `pomerado/runtime`, since sites reformat what they save. It compares a chosen option's value only where the view shows that value, and its label otherwise, and prefers finding each value in the record by a stable id, its column header or a `data-` attribute over its cell's position. - Guardian's "Unapplied supplied input" rule no longer flags a read's tailoring input whose description says the site picks the value when the caller gives none, or may apply another, which the output labels. A recorded Guardian response keyed on the exact policy text needs recording again. - The core skill says an input that tailors the answer on a read is the exception to keeping required inputs required: it is optional unless the task can't run without it. The writes skill still keeps the inputs a write states required, while the core skill's guidance for tailoring inputs, optional unless the task can't run without them, still applies to any tool that offers one, a cart tool included. It also says `_note` is an optional output field, set only when the site won't apply the caller's value. - `LiveTestInconclusiveReason` in `pomerado/core/mint/live-tests` adds `site_block`: the site blocked the browser's network egress, such as an edge refusal of the page's own data call or a bot vendor's block page, so the case tested nothing about the tool. `judgeCase` judges it inconclusive and tells the minter it is an egress problem, not a script failure, so it does not change a working script for it. A host that maps every reason, such as to its own text, adds this one. - A caller's answer to a design question about how an input is used is respected. An input that tailors the answer, such as a location or store, may have a limited use, such as a postal code used only to look up nearby locations without changing the selected one, when the caller chose that use. The minter asks it as a `request_input` choice question with id `input_use_`, `` being the input's name in lowercase snake_case, so a camelCase input such as `postalCode` is `input_use_postal_code`, offering the full use beside the limited ones, and records the chosen use in the input's description, starting "By design:". - Guardian's `publicationOutputPolicy` counts such a documented use as the input applied, not an unapplied supplied input, when `trusted_authority.answeredQuestions` holds the caller's answer under that id. A "By design:" description that misses or contradicts the answer, or that no answer backs, is still a finding, and so is an answer to another question. A filter, sort, date or other value that restricts the answer stays strict whatever an answer says. The "Unapplied input" rule points to the exception. A recorded Guardian response keyed on the exact policy text needs recording again. - `AnsweredQuestion` gains an optional `id`, the question's id in its request. `answersForReview` sets it, and Guardian's review input passes it in `trusted_authority.answeredQuestions`, so an answer binds to what it decides by id, not by prompt text. A host that builds its own answers may add it. - `PublicationScope` gains an optional `publishedInputs`, each `{ name, description? }`: the inputs the tool's published revision declares. A host that repairs a published tool may supply it from its own record of that revision, never from anything the minter wrote, and Guardian then lets a repair keep a "By design:" description that revision declared for the same input word for word, as the caller's earlier decision. Without it a repair needs the caller's answer in its own job, like a first build. The core review passes it in `trusted_publication`; the local host keeps no published revisions and supplies none. - The core skill replaces its rule that no answer makes a supplied value lookup-only with that exception, teaches a first build to ask the question when an input's use is genuinely ambiguous, and tells a repair to keep a "By design:" input's use and description word for word, changing the use only after asking the caller again. The workspace guide lists the question among the reasons to ask. - The testing skill, the workspace guide and the `live_tests` instructions tell a read signed out to prefer rerunning only the failing cases while a fix is in progress, then to run every case once more after its last edit, before the example. A batch of every case waits on its slowest case, so rerunning the cases that already passed after each fix slowed builds without adding evidence. The publication record is unchanged: a case that ran before the last edit is still stale. - The core skill says `readRows` and `waitForRows` read every returned row of a list in one call, and the search skill asks for the facts each card shows without a click. - The testing skill tells the minter to stop reworking a click once a case fails at the same control after two fixes, usually a custom widget, and to apply that input through the results URL the site produced or the page's own data request in the HTTP version, reading it back from the site's committed state or the answer. The search skill allows either route when a control is unreliable. The search and testing skills also say that when setting one control reloads the result list, such as a location or store, it goes before the filters and sorts its reload would drop, and that after each reload the tool waits for the row identifiers or results header to change and settle, never for every card's value to hold. - The http-mcp, testing and workspace guidance treat publishing a read's HTTP version alone as a normal outcome, not a fallback: once the HTTP version passes its tests and the example ran on it, the minter publishes `http` alone, even while the Playwright version is incomplete or fails a case, and stops fixing a Playwright version it will not ship. Inputs are still read back from the answer, and a version that does not ship is neither recorded nor judged. A repair of a read may finish the same way, including a tool that shipped only its Playwright version, while keeping the tool's inputs and outputs. - `MintFailure` gains the publication reason `input_contract_changed` with `brokenInputs`, each `{ field, change }` (type `BrokenInput`) with `change` one of `removed`, `required` or `changed`. A host that compares a publication's input schema with the published tool's refuses one that changes a declared input by failing its `publish` with it. `finish_build` hands the minter exactly its fixed instruction with `brokenInputs`, neither a review nor an outage, and records the refusal with recovery `new_observation`: the minter restores each input, keeps every new input optional, runs the example again with the edited code and publishes that execution. Core ships no comparison, and the local host refuses none. - Guardian's publication output policy adds "Unapplied supplied input": code or an output field that lets a run return results when a valid value the caller supplied did not apply, such as a filter, sort or date the page ignored or reset, is a `schema_mismatch` finding with reason `source_correction`. That covers an output flag such as `sort_applied: false`, results for the page's own default, or a "best-effort" or "lookup-only" description. Facts the page shows for the applied value, such as availability at the applied location, stay data. Neither the description nor an answer to a question excuses it. Returning the site's own default when the caller supplied no value, and saying so, stays fine. Inputs that tailor the answer, such as a location, store, delivery or pickup mode, vehicle, or membership or rate type, are the exception in a read: once the code has tried each way the captures show the site offering to set that value, returning the site's results with the value it actually applied, read back from the page and shown beside the caller's value with the site's message, is right, while skipping a way the captures show, hiding that the applied value differs or presenting the results as the caller's is the finding, fixed by trying those ways and labeling the result. So is code in a read that submits caller details in a step the captures show creating an account or a lasting record on the site. A write throws `InputNotApplied` for such an input before its first commit step. For any other input the fix stays `InputNotApplied` or `InvalidInput`. The live-tests policy counts a tailoring input applied, and one the site won't apply returned with the applied value, as the capabilities to test. 0.4.0 canaries named it "Unapplied supplied location" and covered locations only, refusing them. A recorded Guardian response keyed on the exact policy text needs recording again. - The authoring skills treat inputs that tailor the answer, facts about the visitor such as where they are (a ZIP, address, store, or delivery or pickup), what they have (a vehicle, a device) or who they are (a membership or rate type, a plan, a business account), as inputs the tool offers when the site lets a visitor set one, optional unless the task cannot run without it. - Left out, the site's default applies and the tool returns the value the site used. Given, the tool tries each way the site offers, preferring one that needs nothing more from the caller, reads the applied value back and returns it as `applied_`, such as `applied_store`. - When the site still won't apply the caller's value, or says it does not serve a location, a read returns what the site did show, with the applied value beside the requested one (`applied_`, null only when the page shows none) and the site's message in `_note`, never presented as the answer for the caller's value. A read no longer throws `InputNotApplied` for such an input; a write still does, before its first commit step. Filters, sort and dates stay strict and still throw `InputNotApplied`. - More detail the site needs to apply the input, such as a street address or a vehicle's trim, is its own optional input, described as sent to the site and submitted only when the caller gave it. A name, email or phone is offered only when the site won't apply the input without it. A step that creates an account or a lasting record on the site is a write, so a read stops before it. While building, a live step, the example's or a test case's, that enters a name, email or phone still asks the owner first, as the execution review requires, and the owner's answer keeps the build a read. The forms skill says a quote, contact or inquiry form that sends a name, email or phone creates a lead or other lasting record, so it is a write, while a step that only sets a tailoring input with details the caller gave, a name, email or phone the site requires for it included, stays part of the read unless it creates an account or a lasting record. - The search skill's location section lists the ways to try: the store picker, the ZIP box, the delivery or pickup choice, the manual entry beside "use current location", and declining the browser's location permission. A store missing from the picker, and a store and a ZIP that exclude each other, throw `InvalidInput`. - The core, search, caller-input, testing, publication, writes, forms and http-mcp skills and the workspace guide say so. A live test of a location the site won't apply expects a labeled result, not an error. - The caller-input skill now owns validating a tool's input and choosing its error, and the minter reads it before settling any tool's inputs and errors. The input schema holds every rule it can express: types, enums, bounds, formats and required fields. Rules it cannot express, such as an end date on or after its start date, a count within the site's limits, inputs that exclude each other or at least one of a set, are checked before any site action and refused with `InvalidInput`. `InvalidInput` is for a value that is wrong or that the site does not offer, with `available`. `InputNotApplied` is for a valid, offered value the page did not apply. A greyed-out option on a read is unavailable data, not an error, and a write that may have changed the site is never retried. - The authoring skills settle how a tool treats inputs the page does not take: - An option the page lists but greys out, such as a booked date or a sold-out size, is data on a read: the requested value unavailable, with the page's reason and alternatives, and the run succeeds. A write throws `InvalidInput` with `available`. A value the page does not list at all stays `InvalidInput` with `available`. Neither picks another value. - `available` for an input that depends on another, such as a check-out after a check-in, is read after the input it depends on applied. Rules the input alone breaks are checked before any site action. - A removed or not-found record page is a named outcome that ends in `InvalidInput` with the page's words, never a readiness timeout. - A number on a stepped scale, such as a price slider, snaps to the nearest step inside the caller's bound (down for a maximum, up for a minimum), and the tool returns the applied bound. The workspace guide's read-back rule names this and a disabled filter group as the two cases that return results with what applied. The "value the page does not offer" rule covers choice lists only, and a filter group the page disables for a result set is reported, never waited on. - Read-back text compares normalized for case and whitespace, locators never use generated CSS class names, and checks that values stopped changing ignore text that changes on its own, such as countdowns. - Search results keep related items, recommendations and duplicates out of the matches, and mark a card that stands for a group of variants. - `report_blocked` takes the reason `host_unavailable` (`BlockedReport`) when the host has ended live execution and what the build still needs cannot run without it, such as the fresh example a source correction needs. It ends the attempt with the host's failure (`hostFailure: "host_unavailable"`), never as blocked, and runs no explanation review; while the host still offers live execution it is refused as `host_available`. `BuildBlocked` and the recorded `blocked` outcome are unchanged. The workspace guidance no longer says a retained receipt may take a source correction once live execution ended: only unchanged source publishes against it. The HTTP skill says to try an HTTP version while building, before the final example, so `finish_build`'s ask is only a backstop. - One output check for every place that looks at a tool's output: `lintOutput` in `pomerado/core/runtime/output-lint`. It is deterministic and site-independent, and names each finding by field path (array positions folded to `[]`), check and count. It flags script code, style rules, markup and template leftovers, which block publication, and JSON held as text, invisible characters, untrimmed or very long strings, text that may have been read collapsed (a value ending in an expand control's words such as "Read more", the label of an expand control from `controlLabelsFromAriaSnapshot`, a button or on-page link such as "Show more" right after text, or an ending ellipsis on a page that offered one), one field restating its siblings (a whole card's text), repeated entries or records, and list fields that never vary or are always empty, which are flags for the publication review. The blocking checks need strong signals, such as a function or arrow body, a call or assignment on a DOM global, a statement-ending declaration, embedded JSON-LD, a selector block with style declarations, an HTML element's tag or an unreplaced placeholder, so specs, prices, policies and citations that run long with parentheses and semicolons are not flagged. Every pattern's work per character is bounded by a constant, and the leak checks read at most the first 16 KB of a value; a value that is JSON as a whole is never read as script. A field whose schema declares a code or markup `contentMediaType` (`text/javascript`, `text/css`, `text/html` and a few other named code types) keeps its findings, reported as not blocking with that type as their `override`, so the review sees them. The whole-card and repeated-words checks stop at a work budget on very large outputs and mark their findings `partial`. - The local host adds `outputChecks` to an execution's observations when its output has findings, with samples, so the minter fixes the read before publishing. - `finish_build` refuses a read example whose output still holds a blocking finding, with the new reason `output_checks_blocked` and `MintFailure.outputFindings`, before the contract review. `PublicationRequest.outputOverrides` lets the minter keep any flagged value that is correct as returned, such as code on a tool that returns code or page text that only resembles code, with a reason the publication review checks against the captures. - `outputNotes` and `outputNotesPath` in `pomerado/core/mint/output-notes` give hosts the same check at publication: the blocking findings, and `publication/output-notes.json`, every finding with each override and its reason, as host evidence for the publication review. Guardian's publication policy adds `publicationOutputNotesPolicy`, which treats every finding as a lead to verify, never a verdict. Its Lost information rule counts text that ends in or holds an expand control's label, or ends in an ellipsis, as trimmed only when the captures show a fuller copy on the page the tool read, never a snippet the site cuts short itself or an `include` section an example did not ask for. - `pomerado/runtime` adds `visibleTextCode`, page code that defines `visibleText`, `visibleTexts` and `readRows`. They read only the text a person sees on the rendered page, never a hidden copy, a script, a style or a template, normalize whitespace and remove characters that never show (keeping the zero-width non-joiner and joiner, which spell words and join emoji), read a `display: contents` row through its rendered children, and fail with a `VisibleTextFailure` naming its reason and counts rather than fall back to hidden text. `as: "accessible"` reads what a screen reader reads instead. `readRows` reads a window of rows in one call, and fails as `hidden_only` when rows matched and none is rendered while one still has a box, such as rows fading in from `opacity: 0`; rows with no box at all, such as a no-results page's `hidden` or `display: none` template row, read as `[]`. `normalizeText` applies the same normalization to text outside the page. - The authoring skills read values with `visibleText` or `readRows`, expand collapsed text before reading it, return each fact once, keep related records out of a list's results, mark a card that stands for a group as a group, and offer costly or bulky parts of a page through one optional `include` input, such as the site's filters, each record's whole text or related records. - `waitCode` from `pomerado/runtime` declares the page waits `waitForOutcome`, `waitForRows`, `waitForValues` and `waitForChange` and `waitReport()`, over one set of budgets in `timeoutDefaults` (navigation 30 s, action 5 s, `answer` 8 s without progress, `answerCap` 30 s, `settle` 0.5 s, `unchanged` 2 s, `lateFillCap` 15 s). A wait keeps going while the page shows progress (a loading sign, DOM changes in a named `region`, the site's own document or API request in flight, a URL change, or its own reads filling) and fails with a readable reason once it shows none. `outcomeWaitCode` still declares only the names it declared before, `waitForOutcome` and its four `outcomeWait…` helpers, with the rest kept inside, so code that pastes it beside helpers of its own still parses; paste one of the two, never both. A generic loading sign (a busy region, an indeterminate progress bar, a skeleton, shimmer, spinner or loader class) counts only while it is new, and determinate bars and form controls never do, so a page's decoration holds a wait for at most the no-progress window; the site's own sign passed as `loading` counts for as long as it shows. `waitForRows` also waits while a visible sign shows beside its rows, never inside one: one there before the action, in the same number, is the page's own, and any other counts until it and the rows have held for `noProgressMs` with no request the action started in flight, so a sign that never leaves costs one window, never the cap. A request the wait's `action` started counts until it ends, up to `lateFillCap`, and the page then gets `unchangedMs` to render; any other request keeps the page busy for at most 4 s, since long-polls and streams stay open. A request repeated with the same method, URL and body at a steady interval is a poll and is ignored, and requests to one API path with different bodies are not. Without `siteDomain`, the site is the page host's last two labels, or three under a country's shared second level such as `co.uk`. `waitForRows` reads each field from its first visible match and reports a needed field, or a row's key, whose matches are all hidden as `hidden`. `waitForOutcome` adds `noProgressMs`, `loading`, `region` and `siteDomain` and the reason `outcome_unknown`, and its `timeout` defaults to the 30 s cap while the page keeps progressing; its `outcome_timeout` message now also names the progress it saw. - Guardian's execution policy and the publication finding "Built URL" allow a tool to open a URL built from the site's own pattern, as the authoring skill now describes: a URL the site's controls produced, with only the parts that carry each input filled from the caller's values, through `URLSearchParams` for a query part or `encodeURIComponent` for a path part, every input read back from the page, and the page's controls run once instead when the landing is not an expected answer or a read-back differs. A guessed parameter or route, iterating on URL variants, an opaque filter code the page did not produce in that run, and a built URL without a read-back stay denied. A recorded Guardian response keyed on the exact policy text needs recording again. - A tool's call that ends with `outcome_unknown`, `values_loading`, `values_timeout` or `change_unknown` now fails as a `BrowserActionTimeout`, as `outcome_timeout` does. - `probeCallCode` from `pomerado/core/runtime/host-execute` wraps an exploration probe's call so its actions default to the 5 s action budget, and the local host applies it to every call of an `explore` step. - A read that has not signed in tests itself: the minter designs live test cases from what it saw on the site, runs them in batches and reads every result. Only a read signed out tests beyond its example; a signed-in read and a write prove the tool with one run (see the breaking change on test scope). - The new minter tool `live_tests` has two actions. `plan` returns the agent's cases in `test/cases.json` and each case's latest status, result and failure, with the authored source line that threw. `run` runs the named cases, or all of them, as one batch after one Guardian review, each case on a page reset like the example's, and returns every result. A case may also run the next page through the tool's own cursor. The first passing example's receipt reminds the agent to design and run its cases once the schemas are settled. - Each case says in `purpose` what it establishes, and the file's optional `notTested` lists what the agent chose not to test, each with `what` and `reason`. The host derives no checklist: the testing skill guides the agent's design, with every input alone and together, smart combinations of controls, different real values unlike the example's that the site lists (another retailer, store or record with another layout), a value the site does not offer, no results, the next page, a location applied and one the site cannot apply, and one rerun of the example's input on a fresh browser. A read is a list read by its shape, an output that is an array or a results list, and `primaryListOf` names that list; a details record with image or variant arrays stays a details read, and an empty array inside it is not an empty result. - `MintDependencies.liveTests`, a `LiveTestHost`, runs the batches; `pomerado/core/mint/live-tests` holds the cases file schema, `runLiveTestCase` and the verdicts. The local host offers it and runs a batch one case at a time on its one browser. A host may run a batch on several fresh browsers at once. `LiveTestHost.caseTimeoutMs` names how long one case may run on that host, `defaultLiveTestCaseMs` (60 s, the local host's) when absent, and the harness refuses a batch as `no_time`, running nothing, when less than that is left before the batch's deadline. - Guardian reviews a batch once, as `currentExecution.input` `agent_chosen_batch` with every case's id and input listed, and the cursor input of each case that also runs its next page (`batchReviewInput`), judging each case under the agent-chosen input rules. The new mark is also a history entry's `input`. - Results never refuse publication by themselves. At `finish_build` the harness gives `publish` its record of the cases on the source being published, as the new optional third argument `hostEvidence.liveTests`, and adds a line to coverage. The local host's publication review reads it as the host-owned file `publication/tests.json`, and Guardian judges the cases there against the tool's claims and the captures: a failing case, or a claimed capability no passing case tests while time remained, is a finding. The coverage line counts the cases as pass, fail and inconclusive. Each case's output gets the same output check as an example's: the agent reads its findings, with their meaning and a screened sample, in the case's `outputChecks`, and the record keeps each finding's path, check, count and whether it blocks. A case's findings never refuse publication; the example's still do at `finish_build`. A case that expects `error` passes only on a declared refusal, `InputNotApplied`, or `LocationNotApplied` from an older runtime; any other throw fails it. A failure stays in the record when the case later passes unchanged on the same source, marked `flaky`, and a retired failure carries `excusedBy` only when a case passes the same input with the same expectation on the published source after the source changed. The record keeps retired cases, ones the agent deleted or changed after they failed, with their last result and input. It says `outOfTime` when a batch was refused for time or a case was stopped by a batch deadline the attempt's own end set, never by a batch's ten-minute cap; then, and for gaps whose cases were only inconclusive, Guardian notes the gaps without refusing, and a repair is never refused for not repeating the example. A record the host could not build says why instead of being left out. A host with its own publication policy can include the new export `publicationLiveTestsPolicy` from `./core/guardian/publication`. `MintHarnessSnapshot` adds the optional `liveTests`, each case's latest result and the replaced failures before them, and `liveTestsOutOfTime`, which old and new workers each restore. A case's digest covers its input, expectation and next page, not its purpose, so rewording a purpose keeps its result. - `preflightTestInput` takes an optional `signedIn`. Only false, a read signed out, runs agent-chosen live tests, with no limit. The execute tool's description promises batches only when the host offers `live_tests`. - A choice the request, the caller or the owner declined is never an input of the tool. The skills say so, and the publication policy makes such an input a source correction. The rules that make every control an input, in the search and publication skills and Guardian's "Unapplied input" finding, make the same exception, and Guardian's question review never allows asking about a declined choice again. - A local operation's failure names the authored source line that threw, such as `src/tool.mjs:12`, as `LocalOperationFailure.frame`. - List tools page. `pomerado/runtime` adds `listInputFields`, `listOutputFields`, `startList`, `readList`, `selectRows` and `finishList`, with `ListMechanism`, `ListPosition`, `ListSegment`, `ListReader`, `CursorRefusal` and the limits `listLimitDefault` (20), `listLimitMax` (50), `listCursorTtlMs` (ten minutes), `listCallBounds` and `listDepth`. The new `pomerado/core/runtime/list-cursor` gives hosts `admitListCursor`, `sealListOutput`, `listCursorKeys` and `randomListCursorKeys`; a host signs each run's next cursor and checks a returned one before the run starts, so a script never makes or reads a cursor. The local host does both for `run`, `pomerado run`, served integrations, the minter's own steps and each case of a live test batch, so a `next_page` case's page two runs from the cursor its page one returned, and `PomeradoOptions` adds `listCursors` for its keys and clock. - A host that adopts this release's pagination skill must check cursors with `admitListCursor` before a run and sign outputs with `sealListOutput` after it. The checked position reaches the script on a host channel beside its input, never in the input: `executeKernelOperation`'s browser binding and `runLocalOperation` take `list`, `executeOperation` takes `{ list }`, and `withListHost` (`pomerado/core/runtime/list-page`) attaches it. A run without it refuses any cursor that would continue a runtime list, and its `finishList` returns no next cursor (`next_cursor_unavailable: "unsigned_host"`). - A cursor is bound to the tool, the inputs other than `limit`, `cursor` and `include`, the caller the host names as `ListCursorScope.subject` (refused as `other_account` for any other) and ten minutes after the call that returned it. Its body is signed, not encrypted: it shows the site's own link, token and scope for the next page. The tool, inputs and caller appear only as digests keyed by the signing key. - A cursor in another form, such as a tool's own `page=2`, passes through admission and sealing unchanged, so tools that page their own way keep paging. A runtime draft sent in as a cursor is refused. - `readList(list, reader)` reads one call's page from site actions a tool gives it: `open` reaches where the call starts, the first segment or the one the position names, `more` reads more of the open segment without navigating, and `next` moves to the next site page, API response or "Load more" step or scroll. It asks for exactly the rows the window needs, reads on while rows are lacking within `listCallBounds` and the run's time, and writes the next position: the segment that holds the last returned row (its site link and page number, the API token, or the fewest steps that load it) and the offset after that row. A call's `limit` and the site's page size need not match: with `limit` 20 over 30 rows per site page, page two returns site page one's rows 21 to 30 and site page two's rows 1 to 10. Rows inserted above the last returned row move the window down its segment, and the call still returns `limit` rows when its reads allow. Every count `readList` asks a reader for is at most 1,000, or the last segment's rows read plus `limit` when that is more; `open` asks for the position's offset plus `limit`. - When the segment a later page opens shows neither the last returned row nor any row the cursor remembers, `readList` looks for them within `listCallBounds`. For `pages`, `next_link` and `api` it reads up to two segments on, since rows inserted above push them on, keeping a read to start over. It also reads the previous segment by its page number, which finds them after removals above of up to about a segment's rows, and resumes there. A reader reaches a page by its number by following Next from the first page, so that read costs one read per page: when the previous segment is the list's first it is read before looking on, since a start over needs it too; an earlier one is read after looking on, only when it fits within the call's reads with one left to start over, never deep in a list. When neither direction shows them, it starts over from the list's first segment in the same call: after the last returned row or a remembered row when that segment shows one, else from the list's first row, repeating rows rather than skipping any. A step back or a start over sets `list_changed`. A start over, or a page continued from a segment's first row with neither the last returned row nor any remembered row in sight, can return rows returned before the 16 the cursor remembers, so the cursor then carries a flag and `list_changed` is true on that page and on every later page from it; callers should de-duplicate by key once `list_changed` is true. `finishList` takes `mayRepeat` for it. A call returns no rows while the list has more only when the run's time or the call's reads stopped it; reads that ran out before the start over hand back the list's first segment. `finishList` takes `resumeAfter`, the key of the row a call that returned no rows resumes after when that is not the previous page's last row, such as a row returned earlier that it read past, so the next cursor's anchor matches its position. `ListReader.open` may get a position with only a `page`: for `api` without a token, fetch the responses up to that page. A cursor remembers the last 16 rows returned across calls, not only the last call's. - When the run's time stops a call before it reads past its position, its next cursor holds that same position with `has_more` true; `finishList` takes `bounded` for it. A second such call in a row at that position ends the list with `no_progress`, so a caller paging until `has_more` is false still ends when every run's time runs out before it reads. Otherwise only a call that read and stalled ends with `no_progress`. - `list_changed` is true when anything changed at or above the last returned row: it moved from the offset the cursor saw it at (rows inserted above it, which the caller never gets, or returned rows removed), `readList` found it in the previous segment or started over, it is gone, or a row the cursor remembers came back, and on every page after a start over. Rows that change below it are returned without it. A position whose site `token` starts right after that row (offset 0) does not expect it. - A site link that carries a session, a credential or a signature is left out of the next position, which then reaches that page by its number: user info, a `;jsessionid=` path parameter, an ASP.NET cookieless `(S(...))` path segment, or a `sid`, `sessid`, `sessionid`, `session_id`, `PHPSESSID`, `CFID`, `CFTOKEN`, `access_token`, `id_token`, `auth_token`, `authenticity_token`, `csrf_token`, `token`, `api_key`, `password`, `sig`, `signature`, `X-Amz-Signature`, `X-Amz-Credential`, `X-Amz-Security-Token` or `X-Goog-Signature` parameter. - Every position a draft can hold fits in a cursor of at most 2,048 characters: a draft keeps the last 16 row-key digests and at most 1,360 bytes of JSON. When a list has more but no cursor, `next_cursor_unavailable` says why: `depth_cap`, `no_progress` (a later page that returned no rows and stayed at the position it started from, such as a list that still stalls, so a caller paging until `has_more` is false still ends), `position_too_long`, `unsigned_host`, `off_site`, `not_a_draft` or `inputs_not_json`. - `InputRefusalDetail` and `errors.InvalidInput`'s detail add `kind`, a short token such as `expired`, which a run's `input_rejected` outcome carries as `details.kind`, and `RunEvidence` adds `refusal_kind`. - The pagination skill is rewritten around this contract and its mechanisms: numbered pages and Next links by the site's own link, load more and infinite scroll by a capped replay, the page's own list API, offsets within a page, changed lists and when to stop. The search, core, testing and publication skills point to it, and `references/pagination.ts` is a paged search tool and a "Show more" list on the same contract (`readRooms`), both reading through `readList`; `readRooms` waits for each step's rows with `waitForRows` and keeps a list that stops growing while its control is still offered open, with `has_more` true and a position that continues. - Guardian treats a signed-out session's own state as part of a read. Choosing a store or location, a delivery or pickup mode, a filter, sort, option, currency or language, or dismissing a dialog is never a write there, even through a Save button, and choosing among the page's own options for it is not an invented value. Saving a guest address signed out is part of the read too when it only sets the delivery or store location. A signed-out write includes a step that submits or sends something to a person or an organization, such as a contact form, an application, a message or review, a sign-up or newsletter subscription, or an order, booking or payment. Adding to a cart, a hold on inventory, a seat or a time slot, an upload and a draft stay writes. A signed-out quote or price form that only fetches is a read, but any signed-out step that enters a name, email, phone number or similar personal detail, an address dialog that also takes them included, is a write that needs the owner's confirmation, so Guardian escalates it without one. When Guardian can't establish that a session is signed out, it applies the signed-in rules. Signed-in sessions keep the rules they had. The authoring skills say the same. - A script that refuses a caller's value can name the input and list the choices the page offers for it: `new errors.InvalidInput(message, { field, available })`. The run's `input_rejected` outcome carries them as `details.field` and `details.available`, and the local host's failure text lists them. A host that repairs may close such a refusal without a repair, since the caller has the page's choices, and never asks the caller for a replacement. Only a refusal without choices, of a free-form value, may still be checked and confirmed with the caller. The detail is optional, and a refusal without it is unchanged. `RunEvidence` gains `refusal_field` and `refusal_available`, and `pomerado/core/runtime/operation-failure` exports `boundedRefusalDetail`, which keeps at most 100 non-empty choices of at most 200 characters each. - `ExecutionEvidence` gains the trusted host marker `refusal`, `{ field?, available }`. In maintenance of a read, an example whose script refused the caller's value with the page's choices is a publication receipt: maintenance cannot change the caller's input, so that refusal shows the repaired tool behaves correctly. A first build, a write repair, or a refusal without choices still needs a completed example. - `package.json` declares the host protocol, `"pomerado": { "hostProtocol": 1 }`. It goes up by one whenever a release removes a `./core/*` export hosts use or changes one incompatibly. A package without it is protocol 1. `pnpm test` first runs `tools/check-host-protocol.ts`, which fails when a `./core/*` export of the `canary` or `latest` version on npm is gone without a higher protocol. - `getRuntimeSources` and `runtimeSourceEntry` from `pomerado/assets` give the modules `pomerado/runtime` loads, computed from its imports, for a host's Guardian source reads and minter workspace. The build writes the same set as data to `dist/runtime-sources.json`, exported as `pomerado/runtime-sources.json`, for a host that reads the package without running it. - `pomerado/runtime` holds the HTTP version API and only ever adds exports. It adds `SiteHttp` (its service key stays `pomerado/SiteHttp`), `SiteHttpRequest`, `HttpFailure`, `defineHttpOperation`, `readText`, `readJson`, `requestPastChallenge`, `isBotChallenge`, `parseSavedHttp`, `SavedHttpFixtures`, `SavedCaptureEvidence` and `OfflineFixtureUnavailable`, with their types. Authored tools may import it by that package path; the local host resolves it to the modules it runs. - `makeSiteHttp` from the new `pomerado/core/runtime/site-http-host` builds `SiteHttp` over a host's `HttpTransport`. Its optional `isChallenge` is the host's test for a bot-protection challenge page, which marks a result `challenge: true`. `isBotChallenge` reads that mark, so without a test no answer is a challenge. The contract modules are `pomerado/core/runtime/site-http`, `pomerado/core/runtime/http-operation` and `pomerado/core/runtime/saved-http`. - `runLocalOperation` takes an optional `http` transport and runs an HTTP version over it. Without one, an HTTP version fails before it sends anything. - `pomerado/runtime` adds HTML reading for HTTP versions, which import no other package. `parseHtml(text)` parses an answer into an inert tree, where nothing runs and nothing is fetched, with `select(css)`, `selectOne(css)` and `title()`, and each node's `text()` (entities decoded, whitespace collapsed), `attr(name)` (only attributes the element has), `html()` and its own `select` and `selectOne`. `embeddedJson(text, selector)` returns, as an `Either`, the JSON a page embeds in a `