Enhance report-driven adapter workflows and evidence handling - #2960
Merged
Conversation
Stop chasing per-site completeness folklore: exhaustive AX reads must report depth truncation, skipped rows cannot prove success, and checkbox/Next actions stale the snapshot until a fresh root read. Co-authored-by: Cursor <cursoragent@cursor.com>
Compact now gets a brief execution contract and a shorter progress_update.workflowReconciliation schema. The evidence kernel matches AX-truncated metadata, keeps valid fields when one name is unknown, emits live required=false, and fail-closes only on form-relevant depth or iframe omission. Co-authored-by: Emre Sokullu <esokullu@gmail.com>
Co-authored-by: Emre Sokullu <esokullu@gmail.com>
Truncated AX values now bind value_len and value_fp so a shared 60-character prefix is not exact saved-state evidence. Failed same-site or urlFilter-targeted frames stay in inventory documents. Discarded classifier metadata fields keep verification incomplete without wiping the rest of the list. Co-authored-by: Emre Sokullu <esokullu@gmail.com>
Resolve agent.js conflicts by keeping the PR's unified planner follow-up path and combining siteWorkflow continuation binding with main's task-authority keys.
Emit required= only for native required or an explicit aria-required value, so React/app validation cannot be skipped as optional. Hash AX metadata values after the same NFKC normalization used by verification. Omit empty third-party iframe noise only when another frame already inventoried form controls; a lone failed cross-origin application frame stays incomplete. Co-authored-by: Emre Sokullu <esokullu@gmail.com>
…8d73 Shrink Compact workflow prompts and fix evidence false positives
CodeQL flagged incomplete sanitization because input/textarea values escaped quotes without first escaping backslashes. Always escape \\ then " in value= tokens, and parse those escapes back so metadata readback still sees the app-owned string. Co-authored-by: Emre Sokullu <esokullu@gmail.com>
Escape AX inventory values to clear CodeQL alerts on #320
Re-resolve the planner site-workflow against the live tab after a reviewed-text edit so a wording fix cannot drop transaction_fulfilled. Include ARIA searchboxes in form inventories and text-action evidence. Co-authored-by: Emre Sokullu <esokullu@gmail.com>
…ntory-8d73 Keep live workflow contracts after plan wording edits
The inner planner gate returned draft_recipients but the outer gate reconstructed its result field by field and copied only messaging, so _startPlanExecutionGuard received null and draft verification fell back to accepting any nonempty To line. The last commit's binding never reached the guard. The outer gate now normalizes and passes it through, and a test drives _maybeRunPlannerGate end to end so the handoff cannot be dropped again. Cumulative form inventories kept every prior item on a fresh root read, which is right for a wizard section that scrolls away after being answered but wrong for a question a branching answer hid before anyone touched it. That row stayed required with no possible action evidence and no way to skip it, so a form that is complete on screen could never reconcile. A complete exhaustive root read now rebuilds the current document's item set and carries over only rows the run has accounted for, by per-control action evidence or a terminal ledger row. Handled sections and resolved threads survive; an untouched question the page dropped does not. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G6FtgcHXYSp9Av6pFZGCYb
Gmail's floating compose never renders the sent message inline, so the compose fallback accepts the body captured at dispatch without an outgoing match. That was fine as a delivery signal but it never compared the text to the classified body requirement, so a stale composer sent to the right person passed as the reviewed message. The subject check now covers body too, over a composerBody the binding records whether or not a recipient binding exists. A classified field the composer cannot prove fails closed. upload-release-assets is a publish job with a ledger, which _workflowJobBindsPublicationPayload excludes, so it skipped payload matching entirely. Filenames say which assets were uploaded and nothing about where they landed, and the same names saved on another release satisfied the terminal check. The saved release URL is now bound to the repository the dispatch came from and, when the request named one, to that exact tag. The job collects payload fields for that, and the ledger classifier is told to return the tag when the user names it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G6FtgcHXYSp9Av6pFZGCYb
Waiting for rootReadComplete meant the rebuild only ever fired on a root read that finished in one page. A paginated re-read has continuationPending on page 1 and never starts a root read on its continuations, so a control the branch hid stayed in the inventory and reconciliation stayed impossible. The rebuild now runs on the first page of any fresh exhaustive root read and the continuations accumulate onto it. That first page also drops the document's earlier completeness, since a half-rebuilt item set is not complete coverage; the terminal page restores it. Message body normalization collapsed every whitespace run to a single space, so two paragraphs accidentally joined into one line compared equal to the requested body and the new exact-body check could not see the difference. Bodies now normalize per line: horizontal whitespace and blank-line counts still collapse, because editors rewrite those on their own, but line structure survives. The content probe's copy has to agree, since it compares page text against a body the agent already normalized, and a test pins the two implementations together. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G6FtgcHXYSp9Av6pFZGCYb
A submit binding carried adapter and job identity but nothing about which form was submitted, so another Microsoft Form or Greenhouse application on the same host produced the same generic confirmation and satisfied a run that had reconciled a different form entirely. Inventory documents now record the form they were read from, host plus path plus the app-owned form id, and the confirmation requires the dispatch to have come from that same complete document. Two forms on one host no longer look alike. Reconciliation let any aria-required=false row be skipped, which is right for a referral code nobody asked about and wrong for the cover letter the user asked to attach. Nothing carried that distinction, because form workflows reconcile against the AX inventory rather than classifier rows. The intent classifier now reports which fields the request named, in the user's own words, and an optional control whose label matches one of them has to be processed like a required one. The match is label to label, and either side may contain the other, since the request and the page rarely word it the same way. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G6FtgcHXYSp9Av6pFZGCYb
…fields Keeping only named targets in draft_recipients discarded the one kind a "save a reply in this thread" request produces. Both messaging and draft_recipients came out null, the draft bound to nobody, and any recipient in the composer passed. The conversation kind is kept now and pinned at the gate exactly as messaging is, so the draft binds to the verified recipients of the thread that was open when the run was authorized. It still grants no send authorization: messaging stays null whenever the plan submits nothing. The unresolved-thread matcher only knew English. GitHub ships a localized UI, so a page full of unresolved conversations read as zero items, and this job treats a complete empty inventory as a successful no-op. It now matches the resolve control across the languages GitHub ships. filter="all" deliberately keeps aria-hidden and invisible nodes, which pulled conditional and honeypot inputs into form inventories. They are rarely marked optional, so reconciliation demanded rows that can never earn a verified user action. The tree marks those controls hidden and the inventory leaves them out, rather than deadlocking the form or pushing the agent to fill something the user was never shown. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G6FtgcHXYSp9Av6pFZGCYb
…honest A structural action marked every inventory document incomplete, including steps the run had already finished and navigated away from. On a multi-page Workday or Forms flow the read on the next route completes only its own document, so the earlier step stayed incomplete forever and the cumulative inventory could never be trusted again. Invalidation is now scoped to the document the action ran in, and a step that was complete when a Next click left it is handed back as finished history once a different document root is read. The review-thread inventory kept only Resolve buttons, so a request to post a specific reply and resolve each thread could reconcile every button row while the reply went missing. Each thread's reply box now joins the inventory, paired to the resolve control that follows it, as an optional row. A resolve-only request still skips it; once the request names a reply, the same requested-label rule that covers any optional control makes it mandatory. The conversation-level comment box has no resolve control after it and stays out, so a pull request with nothing left to resolve is still an empty inventory. The hidden-control marker only reached AX lines. iframe_read now reports which matches the page hides and the iframe inventory drops them, so a display:none conditional field or honeypot in an embedded application cannot demand a ledger row that no verified interaction can ever fill. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G6FtgcHXYSp9Av6pFZGCYb
The completion probe collapsed the whole page into one line, but published payload verification only accepts a requested title, notes, or body as a whole line or as the entire page. So a real GitHub release, LinkedIn post, or Douyin publication could never verify its payload, while the unit tests fed the matcher newline-preserving strings and passed. The probe now collapses only horizontal whitespace and keeps line boundaries, and a test runs the probe's own expression into the real matcher so the two cannot drift apart. Binding the form confirmation to the inventoried document assumed an AX document key. A Workday or Greenhouse form that lives entirely in an iframe is inventoried under iframe:<frameId>:<frameUrl> keys, so the exact lookup missed and a valid confirmation could never be accepted. When the dispatch scope is absent from the inventory, the bound form identity now selects the frames of that same form, all of which must be complete. A different application on the same host still has a different identity and still fails. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G6FtgcHXYSp9Av6pFZGCYb
Excluding hidden controls swept up the file input behind every styled upload widget. Greenhouse and Workday hide the real input under a visible label, and upload_file finds and drives it on purpose, so dropping it left a requested resume with no inventory obligation at all and reconciliation could close without the attachment. File inputs are exempt from the exclusion now; ordinary hidden controls still stay out. The requested-field matcher compared whole normalized labels, which fails the example in its own comment: "attach my cover letter" and "Cover letter (optional)" do not contain each other, so the row stayed skippable. It now compares content words, dropping the optionality decoration a page adds and the verbs and pronouns a request wraps around the field it names. A match is one side's identity words being fully covered by the other's. Whole-string containment stays as the fallback for scripts that do not separate words, and the reconciliation test now uses the wording from the report rather than the already-contained phrase that let the old matcher pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G6FtgcHXYSp9Av6pFZGCYb
Preserving the form inventory across a submit is correct where the submit is the job, but prepare-form and prepare-application exist to leave the form unsubmitted. An accidental Submit took the same early return, kept the inventory valid, and since those jobs need no submission evidence the run could report success from the resulting confirmation page. A submit in a prepare-only job is now a recorded contract violation: it invalidates the inventory, blocks a success outcome, and gets its own recovery text telling the model to report plainly what happened rather than submit again. Job evidence was satisfied automatically for every non-submit job except Gmail drafts, so count-results and read-complete-thread could finish on a single accessibility-tree read. Both declare a specific contract, and both now have to meet it: an exact count needs a verified result from the deterministic count tool, and a complete conversation needs read coverage through its terminal page. Selecting the thread job arms that coverage tracking, and the requirement is only imposed where the run can actually track it, so a job with no contract of its own is unaffected. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G6FtgcHXYSp9Av6pFZGCYb
Passing the outer AX document token to the invalidation meant an iframe write matched no inventory key, so every frame stayed complete and the workflow could reconcile without rereading the frame it had just changed. The changed frame is now resolved from the action's own frame id, and an action whose frame cannot be resolved invalidates all frames rather than none. The evidence allowlist covered only two Gmail jobs, so read-transcript, collect-ranked-products, collect-comments and review-pull-request still finished on any single successful tool call. Each declared contract now has a condition: a deterministic tool where one exists, reconciled rows for a collection, and for a reading job a successful read of the resource the job was selected on. That last one is the weakest of the four and does not prove full coverage; it rules out answering from a listing, a title, or another tab, which is what an adapter-wide host match let through. It also needs the URL the job was selected on, so that is threaded through the gates, and where it is missing the requirement is not imposed at all rather than pretended to. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G6FtgcHXYSp9Av6pFZGCYb
…mpty sets The count contract required result.countVerified, which _countGmailResults returns only when it fails to restore the route. Its success path reports verified and exact, so a correct deterministic count could never satisfy the job it was written for. The gate reads the real fields now, and a test derives the producer's success shape from source and fails if the gate ever asks for a field the tool does not emit. The old test passed only because its fixture carried the field I assumed rather than the one the tool returns. Calling the transcript tool counted as transcript evidence regardless of what came back, so an empty window or a first page with more text pending could close a complete-summary question. Evidence is now the content: text or segments present, and no further window pending from either the provider or the result trimmer. It is re-evaluated per call, so paging to the end satisfies it and stopping halfway does not. Requiring at least one collected row made a genuinely empty result impossible, and a Douyin video with no comments is a real answer. That job may now finish with no rows, but only when the run actually observed the video the job was selected on. A collection that cannot legitimately be empty, such as ranked products, still needs its rows. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G6FtgcHXYSp9Av6pFZGCYb
The scope identity ignored the hash and the video parameter, so every Gmail view collapsed to one path and every YouTube video to /watch. A job scope now keeps both, and three contracts hang off it. A transcript counts only for the video the job selected, so a complete transcript of another video no longer answers for this one. A Gmail count counts only for a result set this run actually read: the tool reports an exact number for whatever route it ran on and warns that it does not validate the query, so it now names that route and the guard matches it against the sets the run observed. Any successful observation used to ground a reading job, including a screenshot, a scroll, or a window probe. Only content reads ground a contract now. review-pull-request also got its own condition: the changed files of that exact pull request have to be read, not the overview and not a screenshot. That still does not prove every file was inspected, and it is not claimed to. Collection jobs treated the model's own ledger as proof of its own completeness. Where the planner seeded an expected set, the existing ordered check now has to pass. Where it did not, each row must carry the fields the adapter's job contract declares, with no duplicates, so a bare skipped row cannot stand in for a collected item. Those fields are part of the app-owned job record, validated with the rest of the contract. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G6FtgcHXYSp9Av6pFZGCYb
A transcript window can end the video while starting anywhere, so checking only whether another window follows accepted the final fragment of a video nobody read the start of. Coverage is now a chain: it has to begin at offset zero and each window has to continue where the last one ended. A gap resets it. The arguments live on the completion path, so the chain is recorded there and the guard reads the result. An empty comment collection rested on having read the video page, which says nothing about comments. The page itself has to say there are none now, the same way a form confirmation has to say the response was submitted. Douyin is Chinese-first so the signal covers its wording along with a few others; where the page says nothing recognizable the run reports partial rather than claiming zero. An unbounded collection stayed the weakest link: one well-formed row passed for a whole ranking, because row shape says nothing about coverage. Nothing app-owned enumerates that list, so a ledger the model wrote cannot prove it collected all of it. Completeness is now claimable only against the expected set the app seeded from the request, with every ordered row processed and carrying the fields the job declares. An unbounded request still collects and still reports its rows; it reports them as partial, which is what they are. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G6FtgcHXYSp9Av6pFZGCYb
…ds stable Gmail defers pinning "reply here" until the composer opens. Nothing carried the thread across that wait, so a tab that moved to another conversation had its composer pinned instead and the message could go to a thread the user never chose. The thread that was open when the run was authorized now keeps its identity on the guard, and both the deferred setup click and the eventual pin require the page to still be on it. A single content word was enough to claim any label containing it, so "attach my resume" marked a "consent to automated resume screening" checkbox as requested and reconciliation would not let it be skipped. That pressures the agent toward granting consent nobody asked for. One token now matches only a label it actually heads, and only a short one; two or more still match by subset, which is what the request wording usually gives. A React-style form that replaces a control on every change mints a new ref for the same field, so the inventory kept the old id for its action evidence and added the new one, and the same question had to be answered twice. The tree now emits the app-owned id and name for every inventory control, not just file inputs, and identity prefers those. A radio group shares one name, so its options separate by label. A control with neither still falls back to the ref, which the test states rather than hides. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G6FtgcHXYSp9Av6pFZGCYb
Marking every invisible control hidden swept up the standard custom-control pattern: a checkbox, radio, or select made transparent or clipped and driven through a visible label or wrapper. set_checked and label activation still operate those, and when the wrapper exposes no ARIA control of its own the field simply vanished from the trusted inventory. A prepare-only workflow, or a submitted one with an optional requested field, could then reconcile without ever processing it. Hidden now means what it says: aria-hidden, or removed from layout by display or visibility. A control that is merely transparent or zero-sized stays only if nothing can drive it; a checkbox, radio, select, or file input with a visible label or wrapper is a custom control and remains in the inventory. A text input is not label-driven, so a transparent text honeypot is still excluded, and a display:none conditional field still is too. Both the accessibility tree and the iframe probe apply the same rule. The test harness stubbed the visibility helper, which is why this passed before. It now slices the real helpers out of the module and runs them, so the decision is exercised rather than assumed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G6FtgcHXYSp9Av6pFZGCYb
…frame steps Read coverage completes against whatever conversation root the tab is on, and nothing checked which one. A complete read of a thread the user never asked about could authorize the answer for the one they did. The job's conversation scope is now compared before the evidence is set, and the requirement is only imposed where that scope exists. The tree left optionality unstated for ordinary native controls, because HTML reports required === false without any attribute. Reconciliation only lets explicitly optional rows be skipped, so every optional field had to be touched and a required radio group was impossible: each unchosen option needed action evidence of its own. Native inputs, textareas, and selects now state required=false, custom ARIA controls stay unknown, and a radio group counts as answered when one of its options is processed, so the alternatives that answer rejects may be skipped. A group nobody answered still cannot reconcile. Archiving a finished wizard step existed only for accessibility-tree reads. An iframe wizard advances by replacing the frame, so its completed step stayed incomplete forever and the aggregate inventory could never close. The iframe merge now archives it the same way. A deferred draft reply carries no messaging at all, so the thread identity that survives the wait has to come from the draft target too. Without it, a draft saved after navigating away was accepted in whatever conversation was open. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G6FtgcHXYSp9Av6pFZGCYb
The continuation snapshot covered the evidence kinds that existed when it was written and nothing since. Two kinds fell through it, both in a way that only shows up on a long task. A paged transcript stores where the last window ended so the next one can continue from it. That chain was reset on every new run, so a video that ran out of turns partway could only be finished by rereading it from offset zero, which the contiguity check demands and the model has no reason to expect. A release-asset proof records that a file was uploaded and read back on the saved release. Those proofs were dropped while the ledger rows that depend on them survived, so reconciliation asked for evidence that could only be recreated by uploading files GitHub already has. Pending proofs waiting on their readback went too. Both now travel with the rest of the workflow evidence, under the same task and conversation binding, and the snapshot fires when either is the only evidence a turn produced. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G6FtgcHXYSp9Av6pFZGCYb
…ole diff The classifier is told to return workflowFields=[] when the request named no field. A response that omits the key entirely was read as that same answer, so an empty requirement list passed vacuously and a Gmail send could be reported successful with a subject or body the user never asked for. A missing key is now inconclusive, and a job whose contract binds requested fields cannot verify until the classification has actually run. An open progress session used to skip that classification, so it now resolves the fields it needs before returning, and the resolution travels across Continue with the rest. Landing on the changed-files view is where a review starts. The gate accepted it as the whole review, so a truncated or paginated first fragment of a large diff could stand for the entire thing. Diff coverage now runs as a chain, the same way the transcript does: from the first page, without a gap, until the result reports nothing pending. The test guards had to say that the classifier ran, because they set the requirements by hand and the real path sets both. That is the honest shape, and it is why the vacuous case was invisible until now. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G6FtgcHXYSp9Av6pFZGCYb
The coverage chain accepted every content-read tool, so get_selection or extract_data on the changed-files route closed it: a highlighted line or a targeted query carries no continuation metadata, and the tracker read that absence as complete coverage. Only read_page and the accessibility tree walk a whole document, so only those can close a diff now. Those two also paginate differently, and the tracker knew one of them. It looked for a page number, which read_page never sends, so a window opened directly at a nonzero offset counted as the first one and a mid-diff fragment stood for the whole review. Each reader is now tracked in its own coordinates, read_page by character offset and the tree by page, and a chain belongs to the reader that started it, since the two numbers do not mean the same thing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G6FtgcHXYSp9Av6pFZGCYb
…rols An accessibility read closed the diff whatever it covered, so a subtree read of one changed file or a filter that keeps only interactive nodes counted as the first window, and a small result with nothing pending finished the review. An AX window now has to meet the same document-root, filter, and depth conditions the form inventory already demands of an exhaustive read. The iframe serializer still answered optionality only from ARIA, so an ordinary optional input, textarea, or select in an embedded form came back with unknown requiredness and could not be skipped. It now reads the native property, as the accessibility tree does. A form can leave required on a control it has disabled, which is how a conditional field says it does not apply. Such a control constrains no submission and cannot take the verified action a processed row needs, so an otherwise finished form could never reconcile. Disabled controls are skippable now in both inventories. A request that names one still promotes it, and the run then has to report what blocked it rather than pass silently. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G6FtgcHXYSp9Av6pFZGCYb
read_page sets textTruncated on any window past the start, because earlier text is missing from that window. Reading it as pending coverage meant the final continuation of a multi-window diff, which reports hasMore false and no next offset, never closed the chain, so a fully read pull request could not complete. Only hasMore and a usable next offset say something still lies ahead. The test now builds its windows with the real read_page windowing instead of hand-written shapes, so the two cannot disagree again. An embedded wizard can also advance without navigating its frame. The next exhaustive read then restarts the same scope, and the rebuild dropped every row it held, including controls already answered whose ledger entries live on. The exact inventory-to-ledger match then failed for good. Accounted rows survive a restart here now, as they already do in the accessibility-tree rebuild; the archival branch added earlier only covered a frame that changed its URL. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G6FtgcHXYSp9Av6pFZGCYb
…ls first The transcript chain tracked offsets and nothing else, so a window opened on one video at zero and continued on the selected one at the matching offset looked contiguous, and the final scope check saw only the last window. Every window now has to belong to the video the job selected, the chain carries that scope so a Continue cannot splice two videos together, and a provider that names the video in its own result is held to the same binding. The requested-label set had the same gap the requested-field set had before it: a classifier response that omits the key normalized to an empty list, indistinguishable from an answer that the request named no control, so an explicitly requested optional field could be skipped and the form still submit. Resolution is tracked now and an omission is inconclusive. It only bites where a row was actually left out, since a ledger that skipped nothing never needed to know which controls were named. An open progress session resolves the labels it needs the same way it already resolves fields. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G6FtgcHXYSp9Av6pFZGCYb
An upload is a structural action. An application that parses the resume it was given, or that reveals questions depending on the attachment, is a different form afterwards, yet the pre-upload inventory stayed marked complete. The upload's own proof and its ledger row could then reconcile while the newly mounted controls were never inventoried, so a prepare-only run could report completion with required fields missing. The transcript identity check read only ?v=, but the adapter also accepts youtu.be/<id> and the /shorts, /embed, and /live routes, which carry the video in the path. On those the lookup found nothing and accepted whatever id the provider reported, so a contiguous transcript of another video could satisfy the job whenever the result carried no URL of its own and scope resolution fell back to the open page. The id is now read from every route the adapter matches. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G6FtgcHXYSp9Av6pFZGCYb
…vator A form widget that consumes the file it was given leaves the upload recorded only as pending until a later read finds its filename. That record was missing from the continuation snapshot while the analogous release-asset one was there, so a run that reached Continue first could no longer promote the upload, and the input it was attached to may already be gone. Exact reconciliation then needed the same file attached twice. The activator fallback accepted any visible ancestor, which is what a honeypot sits in. A transparent required checkbox inside an ordinary container lost its hidden marker, entered the trusted inventory, and either blocked reconciliation or pushed the agent at an invisible field. A custom control wraps its input in a label or in something carrying the control's own role, so only those count now; the label paths are unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G6FtgcHXYSp9Av6pFZGCYb
The execution guard runs before the completion recorder, so a contract whose evidence is the window that closes it was being judged against the previous window. The terminal transcript or diff window arrived, coverage became complete inside the recorder, and nothing re-evaluated the contract, leaving done blocked until an extra read that should not have been needed. The empty comment collection had the same shape: the page's own "no comments" line is read from that same result. Those three contracts are settled now once the window they rest on exists, and the guard no longer judges them from stale state. The coverage tests called the two helpers in the reverse of production order, which is why none of them saw this. They follow the real order now, and each of the three fails without the fix. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G6FtgcHXYSp9Av6pFZGCYb
A custom radio group carries no HTML control name, so grouping by name alone dropped every ARIA option. Once one was chosen the alternatives could not be skipped as members of an answered group, and the form either blocked or pushed the agent at every mutually exclusive option. The tree now names the enclosing radiogroup for an option that has no control name, and the ledger groups by whichever of the two holds the alternatives together. A readonly field takes no mutation and browsers leave it out of required-field validation, but only disabled controls were skippable. A form with a fixed account or identifier field, marked required and readonly as those often are, could never reconcile. Readonly is stated now and counts as no obligation, exactly as disabled does, in both the tree and the iframe serializer. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G6FtgcHXYSp9Av6pFZGCYb
The 12306 contract bound a newly observed order identifier and a paid or ticket-issued status, which proves a purchase happened, not that it is the purchase the user authorized. A wrong train, date, or passenger paid for successfully read as success. The booking details the request named are classified now and have to read back from that order's own block on the page, not from the page at large, so another order on the same screen cannot stand in for it. Form workflows kept only the labels the request named, so reconciliation proved each control had received some verified mutation and nothing about what it received. A typo, a truncation, or a value the page filled in all passed. The classifier now returns the values the user supplied alongside their labels, and a control carrying one has to read that value back exactly, through the same truncation-aware comparison the metadata readback uses. A field given a value also counts as a field the request named, so it cannot be skipped either. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G6FtgcHXYSp9Av6pFZGCYb
…kflows Add report-driven adapter workflow contracts
|
@esokullu is attempting to deploy a commit to the esokullu's projects Team on Vercel. A member of the Team first needs to authorize it. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.