mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-11-07 06:19:43 -10:00
* feat(managed-agent): H4a child agent and child acceptance record contract Define the H4 delivery map as six slices and land the first: the managed-child_run body for kind child_agent and the managed-child_acceptance body, both dispatched inside the managed-child_run/child_acceptance schema version 1 records, with commit-time cross-record consistency and resource closure rules and dual-language shared fixtures. The record-body registry's static task kind becomes a function of the record so child_agent projects the existing child_agent task kind while child_acceptance projects no task. No domain is enabled for submission and no runtime path changes; enabling child_run and child_acceptance is H4b's explicit, server-first-deployed step. Refs #12380, #12827 (slice H4a) * fix(managed-agent): Tighten the child agent record contract from review Answer the three review findings on the H4a contract: the result and its terminal receipt now change only together and only in the revision that settles the run, so a half-result can never wedge the chain; the child Session identity is required once the execution proves creation (running_attached or settled), not merely forbidden before it; and a terminal child agent record freezes everything but its delivery line, so a stop request can never be added to terminal evidence. Fixes are mirrored in TypeScript and Java with new shared fixture witnesses and eight killed targeted mutants across both validators. Refs #12380, #12827 (slice H4a) * fix(managed-agent): Close the H4a real-stack verified Java parity gap The real-stack verification on the PR head found the Java child_agent validator skipped the identity checks the TypeScript one makes on childRunId, ownerScopeId and rootSessionId: bodies the writer would refuse were stored (HTTP 200), and a Session holding one could not be reopened - exactly the disagreement the server-first enablement order depends on the store rejecting. Apply the verified candidate intact: three id() calls at the end of requireChildAgent plus three shared fixture cases, closing the differential to zero in both languages. Fold in the regression-protection gap found with it: the store-level acceptance binding test (run kind, completion call, receipt digest and the launch input's resource closure) and the two authority witnesses for the receipt binding and the closure, each proven red by its matching mutant in both languages. The H4b delivery-direction observation (delivery can outrun or contradict an acceptance) is recorded as such in the design's follow-up section, in both languages; it is not blocking for this slice. Refs #12380, #12827 (slice H4a) * fix(managed-agent): Gate shell capture admission on the shell-only parser Round-2 real-stack verification found the remaining shell-only child_run consumer missed in the parser widening: LocalShellStreamResultSession proves a background Shell's start before admitting an output capture by looking up the record by the capture's executionCallId, and since H4a the widened parser would also parse a child_agent record as that proof. A child agent with a matching call id would be admitted as a proven background Shell start. It now parses with parseChildShellRun like the cli callers, and a new witness refuses a child_agent record at admission; the same-domain monitor confusion suite passes unchanged. Fold in the remaining regression-protection gaps the same review round named, one authority case and one store chain closing the settling revision's result and receipt resources, mutants J7b/T7b killed by each. the three F1 id removals JF1-JF3 and the reverse-parser witness are proven red as well. Refs #12380, #12827 (slice H4a) * fix(managed-agent): Close the H4a child-agent contract's enforcement gaps Real-stack review of #13505 (bot R1 round plus human P1/P2 round) found the child_agent validator certifying records its own design forbids, with the terminal freeze then locking every omission in permanently: - require the dispatched definition pin and the Runtime binding that hosts an attached child Session, in both languages (the successor rules make both unaddable once dispatched); - refuse Windows drive specs in workingDirectory in both languages; - read resultVersion with a finiteness guard in Java, so 1e400 refuses instead of throwing NumberFormatException; - bind a first-level child's rootSessionId to the journal's Session at commit time on both sides, and drop two successor clauses the terminal freeze made unreachable; - teach verifyWorkspaceRestore to skip child_agent records instead of misparsing them as shells, give the shell-only parser its own refusal clause, dedupe the shell stop-reason logic onto the shared helpers with the same clause order in both languages, and derive ChildRunKind from the union instead of a dead parallel spelling. The shared fixture corpora pin every new rule in both languages and the 74 pre-existing entries keep their merge-base formatting; the replay harnesses now fail loudly on an unresolvable template; six successor fixtures that never reached their named clause are restated or dropped; eleven unwitnessed validator terms gain cases; the taskKindOf probes run on real records; and the design docs correct the nine stale claims their review round measured (citations, the unknown-delivery wording, the depth-cap relationship, V48+). * feat(managed-agent): H4b child Session runtime Slice H4b of #12827 (Stage H of proposal #12380), stacked on H4a's child/acceptance record contract: the child Session runtime — the four producers of the H4a bodies, the first cross-Session dispatcher, the close cascade, and kind-selective enablement, on the server-first order. Parent side: the Agent tool joins the Hosted Workspace Shell-lane declaration set behind a per-kind gate; startChildRun commits the launch intent with the definition pin and the derived record key before any side effect; the foreground arm waits on the committed chain alone and answers from evidence, the background arm returns a task handle and takes its result through a durable notification input plus wake, with accepted delivered after the answer and consumed after the consuming turn settles — two independent, separately observable facts. Control plane: V51 lineage columns, the delivery-pending index and the relay ledger qwen_managed_child_result_relay (the reserved task-event name stays reserved); createChildSession under the parent's exact binding stamps parent/root lineage idempotently from the launch record key; the relay drives creation, attaches, copies the terminal result and receipt digest-bound, submits the acceptance, marks orphaned at a closing parent and unknown past bounded retries, never re-executing. The close cascade in SessionLifecycleCoordinator cancels non-terminal children through the ordinary close pipeline before the Harness close. Gates: child_acceptance joins the plain enabled list; child_run gains MANAGED_SESSION_ENABLED_CHILD_RUN_KINDS=['child_agent'] so H3's shell stays disabled — and the owed delivery-direction decision landing as the reverse commit-time check in both languages (delivery reaches accepted/consumed only with its acceptance record, and never returns to unknown/rejected after one). Public OpenAPI, Flyway-adjacent schema and WebShell surface do not move. Validated: core 2310/2310 and cli serve sweeps green (timing-sensitive flakes documented in .qwen/e2e-tests/managed-child-session-runtime.md, including one pre-existing failure reproduced on the base tree); managed-agent-server surefire green except one pre-existing integration failure reproduced on the base tree; 12 targeted mutations (7 TS + 5 Java) all killed; dual-language contract replays green. * fix(managed-agent): Close the H4b review round's relay, cascade and wake-consumption gaps The review round found five child-runtime gaps this commit closes: - findChildLineage returned a null row for a root Session through Stream.findFirst() and threw, breaking every Hosted Session create. - The relay binding probe read the tool-session Runtime record, which a text-only child never acquires; it now reads the physical Runtime binding that warm() creates when the Hosted tool turn is constructed, and the tool-session read path is retired with it. - The close cascade treated a child created but not yet attached as unstarted; it now resolves the child through the relay ledger before judging, and closes it through its own Session lifecycle (admitted idempotent close, owed until terminal Session status) instead of a bare daemon DELETE — child creation inherits the parent creator's workspace grant so that admission passes the creator checks. - Journal debt (stop or terminal revisions faltering) re-arms the whole close instead of being dropped, with the acceptance-answered early-out restricted to arms past delivering so the relay still owes its own mark_accepted. - The wake-turn pump treated its blocked sentinel branches as settle proof and consumed the delivered child result without a Turn; the consumption wrapper now requires a genuine settle. Terminal-classified relay rows also leave the discovery set so a grown ledger cannot starve the fleet scan, the funnel routes conflict and record refusals as typed errors instead of message matching, the turn-settle flush keeps the unconsumed remainder owed when it dies mid-commit, and the design documents carry the corrected semantics in both languages. * fix(managed-agent): pass the MariaDB-lane Java checkstyle gates The pushed H4b review fixes left a trailing-whitespace javadoc line, two imports eroded by the relay binding probe change, and an empty switch default written in a shape both checkstyle rules reject; the lane runs checkstyle:check that the local mvn test path does not. * fix(managed-agent): reconcile the H4b create→attach window, lost delivering advance, and attach-less settle The second review round proved three gaps in the first: - A child whose create answered (Session and lineage committed) while its ledger advance died was judged unstarted by the close cascade: the cascade now consults the relay ledger and then the committed lineage row, so a missing ledger id never proves not_started. - Settling such a ledger-only child committed close_scope(started=true) over a body whose childSessionId is null, which the record parsers must reject: the cascade first replays the dispatch_started/attach chain from the proven evidence — the bodys own committed dispatch facts, or the physical Runtime binding the child warms with — so the settling revision always commits over a chain the parsers accept. - An accept committed while the delivering advance was lost left the ledger watching with the answered acceptance short-circuiting every scan: answered acceptance short-circuits only arms the relay never owed, and a watching row reconciles through the same idempotent walk (replay the result, the acceptance, then the advance). * fix(managed-agent): tolerate repair journal failures and replay committed child results The review round on the create-attach reconciliation found two more: - The repair ops' refusal/transport-ambiguous throws are plain RuntimeExceptions (DaemonHttpException, MutationOutcomeUnknownException), which escaped the IllegalStateException guard and skipped the child's own physical close — the reconstruction's journal debt no longer preempts the physical effect. - A commit_result whose acceptance reply was lost minted a second terminal revision over frozen result references: settleCompleted now answers the committed copies after byte-equality with the published resources, so a replay returns the original result instead of a revision the terminal rule must refuse. * fix(managed-agent): expect the hook-listed Agent tool in the hosted tool-turn E2E driver H4b advertises the Agent tool on depth-0 Shell-lane Sessions behind the child kind gate, so a shell-profile fake-model assertion pinning exactly [edit, monitor, read_file, run_shell_command, write_file] legitimately now reads agent first; the other lanes keep their exact vocabulary as designed (files-driver unchanged). * fix(managed-agent): commit tool-arm consumption only after durable Turn settlement The tool-arm consumption flush fired before the turn_result commit: a crash between them, or a turn-result write that keeps failing through the outer retry, left child records durably consumed while the consuming Turn never settled. The flush now runs only after the Turn settlement commits — the wake turn's requirement applied to the tool arm too; and a mid-flush death still keeps the remainder owed in the queue, so the recovery drive can finish each committed side. * fix(managed-agent): wire the real H4b stack end to end A real-stack verification rig of head proved three blockers and three follow-ups, every one confined to seams mocks could not reach: - The relay got a null Runtime Broker in the real Spring app: the owned service never became a bean, so scan never walked a single pending row. Its owner now exposes service() and the configuration registers it with destroyMethod = "" (lifecycle stays with the Broker), and the relay tolerates missing wiring as a retry instead of a null drive. - terminalResultText filtered part_type = 'text' while the only writer stores assistant text as 'output_text'; the query and its fixture now agree with the written vocabulary. - The sent-arm notification escaped its full text into the bundled input, so an XML-hostile 20 KiB result rejected accept at the inline bound: the notification now carries the design's bounded summary — escape-then-bounded below the inline cap with a marker — and full bytes stay on the acceptance record. Its <task-id> is the task_ id the launch itself returned. - A foreground child + any tool deadlocked on the shared Workspace mount with its waiting parent: the mount is taken only for a batch with a non-agent tool, never held across an agent-only wait. - A done child kept an ACTIVE Session and its worker forever: the relay now admits the child idempotent lifecycle close (shared with the close cascade through a new ChildLifecycleAdmissions component). - A child still RUNNING no longer eats the attempt budget — deferred without attempt++, scheduling is not failure — and the discovery set resurfaces delivering ledger rows whose record raced past accepting, so their owed mark_accepted/classify-done step always completes. * fix(managed-agent): close the round-3 seams the second rig and bot proved The re-verified real stack and the bot's CHANGES_REQUESTED round found the rest of the same family: - The bundled notification still wedged: the bound measured the XML text, but publication JSON-serializes the envelope — and the wake turn's own message envelope would blow next. Bound now: packaged JSON bytes against a 48 KiB budget (the design's summary, never the whole answer), computed over the multiline-friendly stripping that keeps newlines instead of gluing lines. - Refuse a foreground agent call when the parent turn already holds the Workspace mount or the batch has a non-agent sibling — the deadlock cannot start rather than burn to the deadline. - A redriven (recovery-continuation) turn consuming child results now drains the same per-id consumption flush; previously its declare-only path would silently pass the queue to the next unrelated turn. - Discovery resurfaces also the watching arm behind consumed/accepted deliveries, and deferral/leased rows no longer occupy the bounded fleet window ahead of real due work. - classify can't be rewritten by a stale claimant (owner guarded in the WHERE), and the Runtime binding probe answers only live READY rows — newest READY, not the lexical max across inert generations. - A done child's close admission runs before classify(done): the row stays owed and retried until admitted, so a falter keeps the close recoverable rather than an invisible ACTIVE orphan. - Gradle-proof witnesses everywhere the round was blind: RuntimeBrokerConfigurationTest pins the bean's existence, and RuntimeBrokerServiceBindingReadTest pins the READY-only filtering. * fix(managed-agent): answer the bot's full round review, first batch of fills Review round answered inline by thread: - Replay halves of the funnel locked into the one serialization: settleCompleted (guard + publications + commit) and accept (check, notification, commit) can no longer mint on a racing re-drive. - Notification text: multiline strip with code-point slices (no glued lines, no unpaired surrogates), bounded by packaged JSON bytes under a 48 KiB budget per the design's summary shape. - Foreground agent already refused while the mount is held or the batch shares with a non-agent tool — deadlock never enters v1. - Discovery pulls the watching arm when consumed ran ahead; deferred and leased rows no longer squat the bounded window; classify is owner- guarded; pending discovery also serves the watching arm behind a committed acceptance. - Binding probe answers only READY (newest READY), never the lexical-max across inert generations. - Tool-arm completion witness pins the no-notification shape; coordinator cascade test now asserts ordered sequences and the derived dispatchId. - New witnesses: discovery columns, consumed pre/post-acceptance halved, child_agent wake intake, config bean pins, binding-read, authority gate commit, multiline/JSON size bounds. - RuntimeBrokerService delete-safe javadoc row and creation-key delegation; docs: six ops belong to managed-child-operations, decision 5 sent-arm pinned as the bounded summary + complete acceptance, and the plan says the reverse gate is pinned at authority/store level in both languages. * fix(managed-agent): refuse a malformed accept-notification as 400 Present-but-wrong-shape notification values were silently dropped by `object()`, so an accept op arrived with nothing. A non-object or a missing-description notification is now a typed refusal instead of a silent no-op. * fix(managed-agent): expect V51 in the workspace migration golden list Co-authored-by: Qwen-Coder <[email protected]> * fix(managed-agent): answer the round-4 review and the R1-5/R1-9 recovery gaps Co-authored-by: Qwen-Coder <[email protected]> * fix(managed-agent): pin the child terminal-result query's two decisions (R1-90) Co-authored-by: Qwen-Coder <[email protected]> * fix(managed-agent): recheck mount ownership after hook side effects and settle the fixture wake before close Co-authored-by: Qwen-Coder <[email protected]> * fix(managed-agent): translate the oversized launch envelope into its byte_limit refusal (R1-8) Co-authored-by: Qwen-Coder <[email protected]> * fix(managed-agent): close the R1-round's deferred Criticals and pin their regressions Co-authored-by: Qwen-Coder <[email protected]> * fix(managed-agent): fire PostToolUse for admitted agent launches (R1-62) * fix(managed-agent): keep the give-up's close-and-settle debt alive and read durable dispatch evidence on resume Co-authored-by: Qwen-Coder <[email protected]> * fix(managed-agent): renumber the H4b migration to V52 past main's V51 lifecycle Co-authored-by: Qwen-Coder <[email protected]> * fix(sdk-java): give the unconfirmed lifecycle-detach heartbeat room on busy runners Co-authored-by: Qwen-Coder <[email protected]> * fix(managed-agent): reconcile attach from the parent record at give-up and admit claimed child cleanup past the lifecycle fence Co-authored-by: Qwen-Coder <[email protected]> * fix(managed-agent): give the fence its claim kind and keep stale authority out of the client Co-authored-by: Qwen-Coder <[email protected]> * fix(managed-agent): authorize claimed child cleanup through the lifecycle commit gate Co-authored-by: Qwen-Coder <[email protected]> * fix(managed-agent): settle Shell-lane parents and children without Workspace close SessionLifecycleService admits protocol 1 only for the Files profile; the Shell lane keeps the protocol-0 close whose child cascade passes ordinary authorization, so a H4b parent close/delete no longer ends RECOVERY_BLOCKED workspace_lifecycle_profile_unavailable. ChildResultRelay no longer holds a parent's settlement hostage to a close the host cannot dispatch: without Workspace-close support the relay still settles failed and over-bound child records and retires the ledger row instead of retrying forever. The harness-session route test now pins the lifecycle authority stamped on the Store client at verb-commit time. * fix(managed-agent): retain the child-close obligation as a discoverable close debt On a host that cannot close Workspace Sessions at all, retiring the relay row alongside the parent settlement discarded the only durable record that still owed the child Session its close: the child stayed ACTIVE with no ledger scan and no close cascade able to find it again, even after close capability returned. The settlement now still commits on time, but the row parks as close_debt — claimed, named by the child id, due on the heartbeat, and surfaced by the discovery page's cancelled arm — until a capable scan admits the close and only then retires. A parked debt outlives the orphaned early-out and never re-enters the give-up chain, whose settle-and-classify already happened for this row. * fix(managed-agent): renumber the child lineage relay migration to V53 #13163 took V52 for the mutation-attempt sequence while the branch was in flight, so the merged tree carried two V52 migrations and Flyway refused every context. The golden-list IT now expects 48-53 and passes against a live MySQL on the merged migrations; the module's 1439 tests pass with the renumbered layout. * fix(managed-agent): reconcile the close obligation before orphaned classification The settlement of a child run can be committed while its close_debt ledger write never lands — a crash or a lost reply between them. If the parent then closes or deletes before the next scan, the settled task is already invisible to the close cascade's live-scope walk, and the orphaned classification erased the ledger row that was the only durable holder of the owed close: child ACTIVE, zero CLOSE operations, zero future discovery. The non-ACTIVE-parent branch now reconciles before it classifies: a settled delivery whose child Session still stands (row id or committed lineage, short of CLOSED/DELETED) parks as close_debt instead and discharges through the ordinary admission. Unsettled rows classify orphaned exactly as before — the cascade owned their stop. * fix(managed-agent): read the close capability once in the give-up chain The admission order and the retention flag each re-read closeSupported(); a TOCTOU flip between the two could mark the debt settled without ever admitting the close. One cached read now feeds both. * test(managed-agent): pin the give-up chain to a single capability read Sequencing closeSupported() true-then-false must still admit the close and call hte accessor exactly once — the TOCTOU window between a double read is what lost the debt silently in the first place. * docs(managed-agent): renumber the design's migration cites to V53 and record the close debt The renumbers (V51 took main's lifecycle, V52 #13163's mutation attempt) left both language versions citing a migration id that no longer exists, and the close-retention machinery added since the doc landed had no ledger description. Both versions stay synchronized: V53 everywhere plus the close_debt / orphan-reconciliation paragraph in decision 8. * fix(managed-agent): retain the close obligation at task settlement, from every intermediate ledger state Task settlement leads delivery settlement: the run record settles and projects task completed while delivery still says accepting, so the close cascade's live-scope walk never sees it — the retention predicate must read task-settled evidence, not terminal delivery alone. The discovery page's settled arm now admits every non-terminal ledger state too: a give-up whose settle committed but whose close_debt write failed sat in binding, outside the old arm-2 list, permanently undiscoverable even before a parent close. Arm 2 now mirrors arm 1's terminal-only exclusion; the in-flight work sets stay disjoint by delivery, and the due/lease predicates still own eligibility. * fix(managed-agent): never re-enter the walk for a settled-cancelled record Widening arm 2 to every non-terminal ledger state made the settled arm discover a creating/binding row whose create-or-fail reply or terminal classification write was lost. On the next scan work() replayed the record into create() and actually launched a child already recorded as creation_failed / not_started_proven. The walk now reconciles such rows instead: a standing child owes exactly its close (parked close_debt, ordinary discharge), and with none the row classifies unknown. The settled arm keeps reaching them; only the creation/execution arms close. * fix(managed-agent): keep retained close debts from starving new child work On a host that cannot close Workspace Sessions, every finished child parks a close_debt row due again every five seconds, and the discovery page ordered by created_at always served those oldest rows first — about 150-200 delegations in, the freshest launch is never reached and no new child Session is ever created (real-stack evidence on the macOS rig). Breaking the starvation breaks nothing of the retention: due debts now sort after every other owed row on the page, and a host that cannot discharge a debt at all revisits it every 300 seconds instead of every heartbeat — nothing could land before a restart returns the capability, and a capable scan still discharges on its very next visit. * fix(managed-agent): fence relay creation recovery against settled evidence at every seam The same lost-write interruption can hide behind three seams, and each authorized the wrong outcome on its own snapshot: - The claimed walk read the discovery page's captured delivery. A record settled between the page and the claim retired or relaunched on the stale snapshot; retirement authorization now re-reads the committed projection (falling back to the page only when the row is gone). - A stalled creator whose relay lease lapsed minted the Session anyway after another worker's give-up committed creation_failed and retired: the creation admission never read the run's terminal verdict. It now fences that mint inside its own transaction with FOR UPDATE on the run's task line, so a settled record refuses and unanswered replay still answers. - A give-up with a lost relay session id resolved no attach and read no lineage: it claimed creation_failed while a real child ran. The child now resolves from the committed lineage before the proof, and a vetoed attach replay consults the child's own committed Turn before started=false may be claimed. * fix(managed-agent): never admit children queued behind a cancelled wait A batch whose foreground wait was abandoned by cancellation returned control to the batch loop, which then admitted every agent call queued behind it — work the cancelled turn never started, including a successful 'started in the background' answer. The admission gate now checks the abort signal before describing, bounding or stamping anything; keeping an already-launched child running stays documented. * fix(managed-agent): close the launch-admission edges codex flagged behind the quota line Three adjacent admission edges all authorized against the wrong party: - Quotas counted the replayed child: a re-driven batch whose siblings filled the active run quota got count_limit for the launch it was already running. The record lookup now precedes the quota verdict, and children.admit's replay identity answers as before. - The background workaround was recommended where it deadlocks: a Session-owned mount (Hook catalog or MCP owner) survives the Turn and releases only at Session close, so a background child launched under it parks on workspace_busy. The background arm refuses there, and only turn-sourced holds keep the foreground hint. - A wake turn's id embeds its commissioning run id, so chained background launches grew one suffix per hop into the 128-char lineage bound by the third hop. The launch key from a wake turn is now a sha256-truncated digest of the same stable name — replay deterministic, hop-bounded, and lineage-parser-legal at every hop. * fix(managed-agent): never let a verdict race past a still-committing settlement Two seams in the same family both read pre-collapse evidence and then acted durably on it: - Between the walk's read of the projection and the orphaned classification commit, another worker's settlement can still land — and the classification would orphan the child that settlement just made owable. The verdict now locks the record row FOR UPDATE inside one transaction for the re-read: losing that race only says the truth settled, and exactly one fresh evaluation assigns the close debt instead of retirement. - A give-up's verdict committed on the no-child proof while the stalled creator resumed and minted between the proof and the failure commit. At the commit seam, the lineage is re-read: new child evidence arrives after the proof owes one more bounded wait, never the confidently wrong pairing. * fix(runtime-broker): read every page for the newest READY binding findLatestBindingByHarnessSession returned from inside the first page that held any READY row (and advanced the keyset cursor only when it held none), so a greater-id READY binding on a later page was never considered — the live Runtime identity for a child's first controlled binding could pin a stale generation on generation-lite bindings. The whole bucket now traverses, keeping the greatest READY id seen, and returns after the traversal ends. * fix(managed-agent): retention defaults on the standing child, never the racing verdict A classification now orphaned only children that no longer stand — whichever side wins the settle-vs-verdict race, a child that still owns its Session keeps its one discoverable holder, ending the orphan-burial shape in both directions at once (forward settle-then- verdict and inverse verdict-then-settle). The atomic classify-when-unsettled from R20 folded away in favour of the simpler default; orphaned remains for the child who genuinely stands no more. * fix(managed-agent): stamp out three launch-side certification gaps codex pinned - A non-plain-object lineage field on session create now knows it: arrays (which object() maps to null) no longer stand in for accepted ancestry; only absent/null lineage is root. Anything else is the debuggable 400 invalid_hosted_lineage, never a 200 root that carries a malformed ancestry silently. - The restarted-wake-turn recovery probe asks the durable record about child dispatch under the same collapsed key the launcher wrote — acceptChildAgent's wake-turn key derivation is now shared with the PostToolUse re-query (sha256-truncated digest of identities), so resume finds it instead of losing ded though no test pinned its wake shape before. - A lifecycle claim stamped by the children/operations middleware now restores on EVERY exit of the route — including the early-refusal arms (invalid operation, unavailable profile, blocked session) that previously ran before the try and leaked the claim onto the shared store client for the rest of the process where a future claim's generations match no longer comparison. * fix(managed-agent): prove close debt by fact and address the fence by key Two avoidance-class truths respected: - A close debt whose child no longer stands is discharged by fact, not retried by suspicion — dischargeCloseDebt now re-reads the child's status before it admits, so an already-closed/deleted child never faces a permanently-refused admission, and an unproven-null read does not discharge against an unconfirmed child that we know exists. Default mock fixtures now answer ACTIVE for a child whose recovery would need it. - The child-creation terminal-truth fence addresses the parent's real launch row by primary key (session scope + record key, the same key the idempotent admission derives), so its FOR UPDATE never becomes a cross-tenant table scan against MySQL's default REPEATABLE READ. * fix(managed-agent): settle close-road blockers by evidence, not retry or wire-code faith Three segments, each adjudicated prior to code: - On a host that cannot close Workspaces, the close cascade's child close admission is fake-owed, and the delivery taxonomy gets a plain IllegalStateException it cannot file — the parent retries forever under an unnameable retry instead of the typed blocked code it already uses when the cascade had nothing to do. On such a host the admission is simply skipped, and the parent close proceeds into that typed gate. - A lineage-named child with no body facts, no ledger creation key and no READY binding has no replay source: that evidence settles as creation-never-attached with started: false once, not an eternally re-armed debt on empty proof. - A give-up's creation_failed verdict is now proved off the record's own transition field: intent (legally holding no attach or past dispatch successors) is answered never-started were nothing physical contradicts; dispatch_started or past is answered started; a physically-ran child with an intent record whose binding cannot name its dispatch answer gets deferred instead of invented verdicts. * fix(managed-agent): let a never-started verdict name its minted Session The create→attach window leaves exactly one durable fact — the minted child Session's lineage — and the terminal revision that retires such a run could never record it: the validators required the Session to stay null until a dispatch admitted it, to have a Runtime binding, and for creation_failed in particular to be anonymous. A retired run then left its mint behind with nothing able to discover it again. The contract now lets a not_started_proven settlement carry childSessionId with no binding — bounded to that one terminal, the only state with no dispatch to host from. The fail/close_scope operations accept an optional childSessionId, the builders restate the same id replay-safely and refuse a rename with child_operation_record, and the shared fixtures pin the newly-lawful shapes in both languages. Pins: core record/operation suites (190) replay the amended fixtures and the naming/rename rules; the harness-session route pin drives the wire contract end to end (named 202, replay 202, rename 409, malformed 400). Prettier, ESLint and tsc clean in cli and core. * fix(managed-agent): arbitrate terminal verdicts against the mint at commit R22's give-up wedge: executionState read the runtime_state projection (unbound/provisioning/ready) while the caller compared execution enum values, so intent and dispatch-started rows never matched — every row looked started, the refused started verdicts repeated forever, and the owed work never retired. The proof now reads the committed record body through its inline resource; an unreadable body owes a bounded retry, never a guessed verdict, and a self-proven not_started_proven execution pairs the unstarted verdict directly. R21's inverse race (a give-up verdict evaluated over an empty lineage, then the mint lands) closes at commit time: ingestion of any not_started_proven terminal revision locks the run's own row FOR UPDATE — the same row the creation fence reads — and requires the verdict to name exactly the Session the lineage proves minted, or refuses the commit. The refused verdict defers the relay, whose retry re-reads the lineage and names the child; every give-up verdict now names its child whenever the child is known. Mutation witnesses, all reverted byte-identical afterwards: a runtime_state read fails the execution-body pin (an intent fixture answers unbound); a no-arbitration gate fails the two refusal pins and passes the two admission pins; dropping the verdict's naming fails the relay's intent-family naming pin. Suites: relay 36/36, relay store 13/13 (new execution-body pin and the never-started pairing), verdict-reconcile decision table 4/4, creation fence 3/3, and the full managed-agent-server verify green (checkstyle, SpotBugs, 117 suites). * fix(managed-agent): bind the close cascade's started call to any-state evidence A creation key is an attempt id, never a start, and a READY binding's absence says nothing — R22 reproduced both halves at this head: the cascade wedged a parent in CLOSING behind a ledger-known child whose binding had retired (the repair demanded warmth the retired row could never offer again), and it committed not_started_proven over a child whose own Turn had durably completed. A start is now proven by the body's committed dispatch facts, by a binding row in ANY state (the new findLatestBindingByHarnessSessionAnyState — a retired RELEASED/LOST row still proves its dispatch and names the repair's identity, while the relay's repair-for-warmth read stays READY-only), or by the child Session's own durable Turn. With none of the three, the never-started pairing settles started:false and names the Session the lineage minted; with any, the repair rebuilds dispatch and attach from that evidence and the settlement commits started:true. The READY-read Javadoc is also reworded to name the rule the method implements (the greatest READY binding id across the full traversal; generation is not consulted), closing the R1-16 follow-up note that the old comment argued against the rule. Mutation witness, reverted byte-identical afterwards: dropping the binding/Turn evidence from the started call fails both new cascade pins with the exact R22 shape (started=false over proven execution). Suites: coordinator 11/11 (two new reproductions plus the named unstarted baseline), broker binding-read 2/2, and the managed-agent-server verify green with the broker chain reinstalled. Design amended in both languages. * fix(managed-agent): close the arbitration on the production topology, the retired binding, and the pre-admission Turn R23 reran the preserved schedules on the arbitration head and broke each side of it in one class apiece: - The verdict/mint seam reopened on MySQL 8.0.46 REPEATABLE READ: the committing transaction's snapshot was established by its ordinary reads long before the verdict's row lock, so a plain lineage SELECT missed a mint committed after that snapshot — the unnamed never-started verdict still committed, exactly the interleave the seam exists for. The lineage read now runs FOR UPDATE as well: a locking read always sees the latest committed data on either InnoDB default, and takes the same extension-then-session lock order the mint uses. Witness on live MariaDB REPEATABLE READ with two connections: a plain read goes RED ("Expecting code to raise a throwable" — the gate accepted the mint- after-snapshot verdict), the locking read stays green; the H2 decision table is unchanged. The temporary MariaDB used for the witness is torn down after the run. - The give-up's physical-child repair required READY, so a child whose binding retired (RELEASED) before the record recovered wedged the attempt budget behind an answer that could never come — no failure, no close admission, no settlement. Terminal reconciliation needs the historical dispatch identity, not warmth, so the repair replays from the any-state read too. The relay-side pin drives dispatch/attach/fail off a retired row whose READY answer is empty; its RED mutation witness (READY-only lookup) leaves the same row parked in binding, exactly the repro's shape. - A Turn no longer proves a start by existing: only its G3 dispatch pair (submission mark or harness epoch) does, mirroring the cancellation store's own predicate. A Turn the coordinator failed before warm-up or submission is a pre-admission failure and settles the named never-started pairing instead of sending the repair after a binding that never existed — R23's parent-CLOSING wedge, pinned in the relay (creation_failed settles named, no repair attempt) and the cascade (close_scope started:false named, parent completes). The RED mutation witness for both qualifications (dropping the dispatch pair) wedges all three pins exactly as reproduced. All mutation witnesses were reverted byte-identical (sha256/cmp checked). Suites: relay 38/38, relay store 13/13, coordinator 12/12, gate 4/4, and the full managed-agent-server verify green. Design amended in both languages. * fix(managed-agent): fence the never-started verdict against runnable Turns, and keep its refusal rollbackable R24's two probes held against the arbitration head, both in fresh code from this branch: - A live ACCEPTED child Turn waiting for its coordinator shares the undispatched shape with the terminal pre-admission failure, so the give-up's new dispatch-pair qualification minted creation_failed over a Turn the coordinator then claimed, submitted and completed (the close-incapable host has no admission guard to catch it). The relay now defers bounded on any Turn that neither dispatched nor terminally failed pre-admission; only the terminal undispatched half settles the named never-started pairing. Pin: an enqueued ACCEPTED Turn sends nothing and parks at binding until the admission settles FAILED, when the named verdict commits; its mutation witness (deducting the qualification) fires the verdict at once, exactly the reproduction. The close cascade needed no sibling branch — the active-Turn close admission already continues past the same window there, and the terminal truth settles on the next walk (verified at coordinator 12/12 without touching its flow). - The gate's 409 reached the Hosted writer as a generic record rejection, which the HTTP client's refusal classification did not recognize: ManagedSessionAuthority latched writeFailure and the relay's corrected named retry could never commit on the resident parent. The gate now answers its own child_run_lineage_minted code, and the client maps it to ManagedSessionCommitRejectedError — the rollbackable non-commit class the authority rethrows without the latch. Pinned at all three levels: H2 decision table and the MySQL IT assert the exact wire code, and the TS client suite drives a one-shot 409 into a typed rejection (writesStopped false) followed by the corrected retry committing (two attempts, one landed); its mutation witness (mapping removed) rethrows a plain HttpError. Witnesses were reverted byte-identical (sha256/cmp). Suites: relay 39/39, store 13/13, coordinator 12/12, gate 4/4, TS http store 65/65, full managed-agent-server verify green. Design amended in both languages. * fix(managed-agent): treat the withdrawn submission mark as evidence-neutral, never as non-admission proof R25's probe held against the intent reconciliation: G3's withdrawSubmissionAttempted after a lost admission reply leaves the Turn CANCELLED with neither dispatch mark nor epoch — the identical field shape as a genuine pre-admission failure — while the historical binding (READY → retired) remains. The terminal-shape check certified that as never-started, converting an unanswered admission into a proved non-admission: the relay then committed creation_failed over a child whose admission outcome nobody had disproved. The rule is now ordered by what can actually prove the verdict. An outstanding Turn (live, neither dispatched nor terminal pre-admission) always owes the bounded defer; the proven pair or the historical binding row takes the dispatch replay with the same identity — a terminal status never upgrades G3's reset to a proof. Only their joint absence plus a terminal pre-admission failure settles the named never-started pairing. The legacy space closes the same way it always did: no Turn, no binding, no pair. Pin: a CANCELLED Turn with no marks drives the dispatch/attach replay from the binding's identity and settles child_failed named, never creation_failed. Mutation witness (dropping the binding from the dispatch-evidence disjunction) fires the never-started verdict in the exact repro shape and is reverted byte-identical afterwards. Suites: relay 40/40, store 13/13, coordinator 12/12, gate 4/4, full managed-agent-server verify green. Design amended in both languages. * fix(managed-agent): renumber the lineage relay migration to V54 after main's V52/V53 The uniqueness gate flags the duplicate version: main landed V53__managed_workspace_roles.sql after the last incorporation, so the relay's V53__managed_child_lineage_relay.sql collides with it. Same remedy as the V52 collision: the ledger moves to V54, its two code comments and the migration-rank golden list (WorkspaceMigrationMySqlIT, now "48".."54") move with it, and both design languages name V54 where they named V53. Verified green at full depth: runtime-broker + qwencode reinstalled chain-first (the shared ~/.m2 alpha jar had been overwritten by a sibling worktree's main build, which lacked the any-state read), the stale target/classes copy of the old V53 file removed (Maven's incremental resources copy otherwise ships it alongside the rename and re-applies the whole ledger as a duplicate version), and the full managed-agent-server verify passes with 117 suites, no failures. --------- Co-authored-by: Qwen-Coder <[email protected]>