Keep the exclusive root UPSERT as the single initialization path before creating the Session event stream.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(managed-agent): verify ambiguous Linux workspace birth times
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* test(managed-agent): pin the #14461 recovery accessors in the session lock-order audit (#14505)
#14461 added hasLiveTurn and markTurnRecoveryBlocked to
ManagedAgentStore without updating the pinned turn-row accessor set in
SessionRowLockOrderTest, so the exact-set assertion fails on any tree
containing both — including this PR's merge commit, whose fast tier is
red because of it. hasLiveTurn is a read-only helper and
markTurnRecoveryBlocked is fenced by the live dispatch lease rather than
the session-row lock, and the audit finds no lock-ordering violation in
either, so both belong in the pinned accessor set.
* docs(managed-agent): note Maven report output breaks the root ESLint sweep (#14505)
---------
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
Co-authored-by: qwen-code-autofix <qwen-code-autofix@localhost>
* fix(managed-agent): preserve rotated keys during operator recovery
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* test(managed-agent): scope-key the Hosted lane's ma_resource helper reads
The FG7c adapter-stop and FG6c crash gates timed out in the hosted
process-fault lane when their test helpers read ma_resource by raw
tenant/session/resource IDs: the table's indexes are resource_key and
(session_scope_key, resource_id), so those reads full-scan a table the
whole shared fork accumulates into. Lead the same reads with the
session scope key, keeping the raw IDs as residual predicates — the
shape production reads and HostedGateTestSupport already use.
Timeouts and assertions are unchanged.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
---------
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
The hook published canUp/canDown for history navigation buttons that were
never built, and recomputed them on every push, navigation step and reset.
Each recompute allocated a fresh object, so React re-rendered the host
composer six times per navigation cycle to update state with no reader.
Remove the state, its sync helper, the six call sites and the stale comment
describing the missing buttons, and add a render-count witness so the wasted
re-renders cannot return silently.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(channels/base): strip zero-ink code points from display projections
PROMPT_UNSAFE_INVISIBLES enumerated the C1 block, Cf and the variation
selectors, so a projection built only from Default_Ignorable fillers
(U+3164/U+115F/U+1160/U+FFA0), U+2800 or a lone surrogate survived
untouched: the line rendered as empty while still carrying
attacker-chosen bytes.
Derive the set from Unicode properties the way git-remotes.ts already
does, and list U+2800 plus the lone-surrogate range explicitly since
neither is Default_Ignorable. The u flag matches by code point, so an
intact surrogate pair never lands in that term.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(channels/base): keep advertised attachment paths byte-resolvable (#14510)
The widened PROMPT_UNSAFE_INVISIBLES class also backs sanitizePromptPath,
which renders the `saved to:` path sent to the model, while adapters store
attachments under the original basename (DingTalk: basename(fileName)). A
filename carrying a zero-ink char (U+3164 HANGUL FILLER, U+2800 BRAILLE
PATTERN BLANK, a lone surrogate) was advertised with a space in its place,
so the read-file tool got ENOENT for a file that exists on disk.
Keep a path-scoped strip set at the pre-widening class (line-breakers, bidi,
C1, Cf, variation selectors) for sanitizePromptPath; zero-ink chars cannot
break or reorder the rendered line, so stripping them buys nothing and costs
resolvability. Adds a regression asserting byte-identity for both chars.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
---------
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com>
* test(managed-agent): fix the FG12d post-merge evidence and ledger P3s
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* test(managed-agent): resolve the FG12d case letter before boot can fail
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
---------
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
A workspace-scoped delta must not reach a session admitted at a different root, and the change must still be acknowledged. The user-scope positive case landed in #14525; this is its discriminating pair, so dropping the workspace filter turns it red.
Refs #12737
* test(managed-agent): add the FG11b Spring-side automation store-fault gates (#14446)
A second method of HostedAutomationFaultGatesIT, on the FG11a boot, with
six cases sharing one minute boundary:
- occurrence-insert: a refused occurrence record fires nothing, and the
slot fires once after the refusal drops;
- occurrence-settle: a refused settle keeps the occurrence firing and
re-driven against the same run, one run chain and one model Turn;
- watermark: a refused watermark advance re-derives the slot every tick,
and its recorded decision stands;
- receipt-claim / receipt-settle: a refused request receipt relays
nothing, or re-drives the same operation id once it drops;
- revision-race: a fire parked while an update commits revision 2 meets
automation_revision_stale and is re-pinned and fired once, never
skipped.
Refusals are tenant-fenced MySQL triggers (the FG9b/FG10c pattern); the
relay gains a hold that parks an operation before the Harness. Six
mutation witnesses are red; one single-layer watermark mutation is
equivalent and recorded. The Harness-side store faults are slice 2.
The method measures 30.8-64.5 s locally and is budgeted at ~130 s: the
healthy sum moves to 4738 s and the fork to 5348 s, inside the Verify
step's 100 minutes.
* test(managed-agent): pin FG11b's recovered occurrence to the refused slot (#14446)
Review round 1: occurrence-insert took the first recorded scheduled
occurrence after the refusal dropped, without checking it was the slot the
trigger refused. A regression that skipped past the refused slot, letting
the watermark advance, would pass on the next minute's fire.
The refusal row now records what it refused (the occurrence key, or the
watermark slot). occurrence-insert and occurrence-settle check that every
refusal names one slot and that the slot they recover is that one.
A/B with a mutation that swallows the refused decision so the watermark
advances past it: the previous assertions stayed green (163.3 s, the next
minute fired in its place); the new ones go red at "every refusal is the
one slot's record".
---------
Co-authored-by: 易良 <1204183885@qq.com>
The sealed-Managed arm of the lock-record validator is the only thing
that stands between a corrupt commit proof and a certified takeover
being handed that corrupt proof, yet nothing pinned it: mutating any one
of its four checks left the whole deterministic suite green, and a real
daemon archived the corrupted seal.
Corrupting only the commit proof of an otherwise valid sealed record now
has a witness, so acquire fails closed on a malformed lock instead of
treating the record as a seal. A fractional, textual, negative or absent
sequence and a short, uppercase, non-hex or array-wrapped hash are each
rejected; a zero sequence stays a genuine seal, pinning the bound as
>= 0 rather than > 0.
Each of the four checks is now load-bearing: dropping the safe-integer
guard, the non-negative bound, the string guard or the 64-hex pattern
turns at least one new case red.
Part of #13443
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
The main CI watcher and the PR scan share one infrastructure-signature
list. A per-host SDK Java test lock starving past its flock budget carried
no signature, so main run 39104863059 filed #14550 for a lane in which no
test ever ran.
The annotation names a lock budget, not an assertion: several runner
instances share one HOME on the ECS pool, so a fast-tier job can wait
behind sibling Maven holds that outlast its own acquisition budget. Main
runs now rerun that class once instead of filing, and a red check on a PR
is reran once; the existing attempt-2 guard still files when the
starvation is not transient.
The new pattern also covers the E2E and release docker lock messages,
which share the same starvation semantics.
* test(managed-agent): cover workspace-change request validation
A workspace change with a non-integer revision or an unknown kind is rejected before it applies anything, and neither answer can be mistaken for the acknowledgement the Bridge expects. Pin both.
Refs #12737
* test(managed-agent): discriminate the workspace-change validation error
Assert the validation message instead of any throw, and use a non-string kind, so removing a check cannot pass the case through an unrelated TypeError.
Refs #14525
* test(managed-agent): pin that a user-scope permission change reaches a worktree session
A user-scope permission delta must reach every live session in the child, including one admitted at a worktree root. The workspace-scope filter must not silently drop it while the change is acknowledged. This is the audience half of the permission path; the workspace-filter negative case follows.
Refs #14525
* test(managed-agent): keep the unknown-string kind rejection covered
Switching the second case to a non-string kind pinned the type check but stopped exercising the switch default arm. Add a third case with an unknown string kind and assert its own message, so the default rejection cannot be removed while the suite stays green.
Refs #14525
---------
Co-authored-by: 易良 <1204183885@qq.com>
* test(managed-agent): add the FG12d MCP release fault gate (#14447)
* test(managed-agent): pin the FG12d release and settle witnesses
Require the held release dispatch to answer running before the kill, so
a synchronously settled release cannot satisfy the lookup witness; filter
the status witness to a settled 200 for the original release id; assert
the released server exits once instead of claiming it survives; accept
the lifecycle 409 conflicts while retrying settle_resources; and correct
the docs' saveEffects/status/timing claims.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* test(managed-agent): tighten the FG12d settle retry and status target
Retry only hosted_turn_active on the lifecycle route; a
hosted_lifecycle_operation_conflict cannot self-heal and should fail
with its body. Join the settled-status witness to the original
runtimeSessionId, and correct the docs' latest measured seconds.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
---------
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: 易良 <1204183885@qq.com>
* test(managed-agent): pin the FG13d cancel against a stopped worker
SIGSTOP the durable worker while the command holds, then cancel through
the Harness route. The owner cannot answer the lookup, so the record must
stay recovery_blocked/outcome_unknown and keep the unit populated instead
of inventing an end. Adds 150 s to the shared fork budget and records the
matching mutation witness in the design.
* test(managed-agent): hold the stopped-worker fixture indefinitely
SIGSTOP freezes the worker, not the fixture's own hold timer: a 60 s hold
would end during the two 30 s owner lookups, kill the child and write the
allow, so a correct implementation could fail on scheduling. Hold until
teardown instead, matching the worker-kill cells.
---------
Co-authored-by: 易良 <1204183885@qq.com>
The reconcile_run route can run a crashed wake turn's aftermath itself:
when the Session's pump is not running one, the route claims the turn
slot, settles the crash, lifts the recovery block and answers
`repaired: true`. No test reached that branch. The existing "repairs
through the reconcile route while blocked" test is settled by the
pump's own aftermath, and its route only reports. Dropping the branch,
dropping `repaired = true`, dropping the route's liftRecoveryBlock,
dropping its claim of the turn slot, or dropping the `finally` that
hands the slot back all kept every test green.
The new test lets the pump's first aftermath pass lose its Broker
release, which leaves the Session recovery-blocked with the crashed
input pending, and parks every later kick so the pump cannot retry. It
waits until a retry kick has been parked, so the pump's pass is over.
A reconcile whose release is refused too must answer `repaired: false`
and leave the block and the input in place. Once the Broker answers,
the next reconcile must consume the input while holding the turn slot,
answer `repaired: true`, release once, consume exactly once, lift the
block and hand the slot back. A further reconcile only reports the
settled run, and the Session closes with 204.
Co-authored-by: 易良 <1204183885@qq.com>
* test(managed-agent): add the FG11a automation lost-reply fault gates (#14446)
The first packaged lane that turns automation on: in-JVM Spring with the
scanner running, a real Harness behind a relay that drops one committed
answer, real MySQL and a model stub.
Eight cases share one boot and one minute boundary:
- create-reply, update-reply (a newer update lands meanwhile),
noop-update-reply and retire-reply: the retry answers the committed
revision and never commits again or over a newer one;
- manual-run-reply and scheduled-fire-reply: the re-drive meets the
same arun_ run, with one run chain and one model Turn;
- reconcile-reply: a lost reconcile answer leaves the slot skipped
under its own refusal, never unknown;
- public-reply: a lost public 202 replays the receipt without a second
Harness operation.
Five mutation witnesses are red; one equivalent mutation is recorded in
the design. The fixture runs one Turn per Session first: the automation
verbs only load a Session on the Harness, so one created without input
is refused 404 managed_session_not_found.
The class measures 46.3-85.0 s locally; it is budgeted at ~170 s, so the
shared fork's healthy sum moves to 4359 s and its timeout to 4969 s,
inside the Verify step's 95 minutes.
* test(managed-agent): keep FG11a's clocked cases off the scanner until armed (#14446)
Review round 1:
- The scanner ran from boot, so a minute boundary between a definition's
create and its armed fault, or its held manual run, let a slot fire
first: attempts 0 for scheduled-fire-reply, 409 automation_run_skipped
for reconcile-reply. SchedulerHold now parks the scanner's only thread
while the three clocked cases set up; manual runs fire on the request
thread and are unaffected.
- assertOneRun collected every fire and run command of the schedule, so a
legitimate second slot on a slow runner read as a duplicate. The
exactly-once checks are now scoped to the occurrence under test.
- The design docs give the Verify step's 95 minutes, describe both fixes,
and say a first red skips the later cases, which are re-run per case.
* test(managed-agent): add the FG13d Hook cancel and timeout fault gates
Extend the packaged Hook rig with a descendant that ignores SIGTERM, and
pin two arms against the real stack: a cancel must escalate to cgroup.kill
after the grace and settle cancelled only once the unit is empty; a 2 s
timeout must kill the unit, keep the output unapplied, and block the tool.
The class adds 200 s to the shared fork budget and is registered in the
pinned Hosted class list.
* test(managed-agent): drop the unused FG13c helpers from the FG13d class
The new class was copied from the owner-restart rig, so it carried the
Harness-kill, worker-kill and status helpers those cells used. This slice
cancels through the Harness route and waits on JDBC, so those helpers are
dead; removing them keeps the class to what it exercises.
* docs(managed-agent): cite the whole Hook unit terminate() range in the FG13d design
* test(managed-agent): check the drained unit at the first terminal observation
The cells waited for the terminal record and only then waited out the
descendant, so a regression that settled early while the drain continued
still passed. Inspect the first terminal observation instead: the
descendant must be gone and the unit empty at that instant, and document
the early-terminal mutation witness.
* test(managed-agent): add the FG12c MCP restart fault gates (#14447)
Kill the packaged Harness, the durable Runtime worker and the stdio MCP
server, and prove that the surviving generation never mints a second
owner, never resends a control and never fabricates a settled result.
The fixture parks a held resource read until a release file appears and
records stdin close, so the worker-kill cells assert the child's fate.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(managed-agent): close FG12c review gaps and re-pin the fork budget
Wait for dispatch_started and a held configure before killing the
Harness; drive a real recovery on the same Session after an idle worker
kill and assert the identity is unchanged; validate the operation status
response and its published result instead of discarding the lookup. The
five cases now measure 49 s, so the healthy sum moves to 3938 s and the
CI fork to 4548 s.
* fix(managed-agent): wait for the refused recovery acquire in FG12c
A 202 only admits the prompt; the MCP refresh and acquire run later in
the turn's declarations. The idle-worker cell now waits for the recovery
acquire to be refused with 409 by the dead generation before it reads
the configuration identity, so a fast assertion cannot observe only the
pre-crash state. A direct 409/503 refusal must name a recovery error.
* test(managed-agent): harden the FG12c witnesses against false-green paths
Sequence the proxy wire so baseline markers cannot race an in-flight
relay; pin the idle-worker recovery acquire to the dead generation's
409 body; count invokes and server starts in the worker/stdio cases; and
pair the configure status answer with the persisted record state.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* test(managed-agent): close the FG12c round-2 high findings
Wait for the successful status entry in the recorded wire instead of the
relay-entry control, so a slow responder cannot produce a false red; and
require a direct 409/503 refusal to be backed by the dead generation's
own refusal. Wait for the fixture to exit before asserting it is gone.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* test(managed-agent): accept both dead-generation acquire refusals
The killed worker can refuse the recovery acquire with 409 'Runtime
generation is no longer live' or 503 'Managed Runtime process is not
alive.'; accept both, anchored to the :acquire request. Require the
configure status answer to be terminal before pairing it, and await the
durable worker registration before killing it.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* test(managed-agent): make the FG12c status witness terminal-aware
Select the first terminal status answer instead of locking the first
200 (which can be running), stop requiring an acquire that a
pre-admission refusal never issues, and accept the third legitimate
dead-generation refusal message.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* docs(managed-agent): sync the FG12c budget with the merged FG13c ledger
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* test(managed-agent): only retry the dead writer lease on reload
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
---------
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* test(managed-agent): add the FG13c Hook owner-restart fault gates (#14449)
* test(managed-agent): tighten the FG13c hook fault gates after audit
* test(managed-agent): address the FG13c review findings
* test(managed-agent): settle the FG13c harness-kill hook after a Turn cancel
* test(managed-agent): address the FG13c review census
* test(managed-agent): reconcile the FG13c hook before the terminal wait
* test(managed-agent): tighten the FG13c fresh-prompt reconcile after audit
* test(managed-agent): drop the stale SIGTERM claim from the FG13c fixture
* test(managed-agent): settle the FG13c Harness-kill Hook via the Harness status route
The public prompt cannot trigger reconciliation: the control plane's
active-Turn gate answers 409 in insertTurnCommand before any Harness
dispatch, and the cancelled Turn never leaves CANCELLING. Re-attach the
Session to the replacement Harness with the passive recovery load and
read the operation through the authenticated hooks/operations route,
which asks the record's original runtimeSessionId and settles it from
the receipt the worker still holds.
* test(managed-agent): pin the FG13c kill window and the settled owner
The Harness-kill cell could pass without proving anything if the Hook
had already allowed before the kill: the old Harness would have settled
the record and the status read would see a terminal one. Hold longer and
refuse to kill while an allow is already on disk. Assert after
settlement that the binding still names the same worker and generation,
which is what the design claims.
* test(managed-agent): reuse the replacement attachment instead of forcing a load
A Session the coordinator already attached is resident with a Hook-pending
Turn, so a second passive load of it is refused with takeover_unsupported;
the forced call sat outside the wait and exited the cell before the status
route could run. Probe the status route first, reuse whatever attachment
serves it, and ask for a passive re-attach only on 404, at most once a
second.
* test(managed-agent): keep a refused re-attach inside the wait
untilAsserted retries an AssertionError, not a RuntimeException, so a
takeover_unsupported refusal would still have exited the cell. Swallow it
at the call site and report the message on the assertion instead.
* test(managed-agent): send the Harness contract headers on the Hook status probe
Every /session route runs the hosted-harness contract middleware, so the
status probe needs X-Qwen-Harness-Protocol-Version and the boot id of the
process it is talking to. The identity now carries both from the attachment
ref, and a stale generation counts as an attachment that cannot serve the
route, like a missing Session.
---------
Co-authored-by: 易良 <1204183885@qq.com>
Names recorded on channel A survived a replace to channel B until the next hasRuntimeMcpServers() call. An add on B then adopted them, so removing B's own server still reported the runtime as holding one. Clear the set when the live child changes before recording a new name.
Refs #14488
The guard on the shared hosted-harness-mysql failsafe fork read only
seven of its nineteen classes. A ceiling raised in any of the other
twelve, or a new Hosted*IT class, stayed green until the fork was killed
on main. Two classes declared no ceiling at all.
- Discover every Hosted*IT class the fork's include picks up, in any
package, and pin the list, so a new class lands on a red that asks for
its runtime in hostedForkHealthySeconds.
- Hold each class to the per-ceiling rule the FG7-FG10 letters already
used; the team lead and member command classes keep the summed rule.
- Require a ceiling on every case, and give the two untimed classes one
(HostedCommittedEventLineReplayIT 180 s, HostedWorkspaceRecoveryWorkerIT
120 s).
- Cite the lane measurement beside the allowance: the nineteen classes
ran 3085 s on run 39087108383, under the 3874 s allowance.
- Reuse the class discovery in the Spring context check.
The one-argument form of the load request's child Workspace setter
passed false for the snapshot capability, so restating child
Workspaces on a copy that served snapshots silently dropped them.
Every other copy of the request keeps the flag. The one-argument form
now keeps the flag the copy carries, so only turning child Workspaces
off drops it, as the two-argument form already does.
No control-plane caller uses the one-argument form today; this removes
the trap for the next one.
* fix(managed-agent): keep runtime MCP state and permission sync honest
Clear the recorded runtime-MCP names when the live child that held them is
replaced, so a retired channel does not keep every later session on Legacy.
Diff each settings scope's own rule set when a permission change is
propagated: the merged view carries workspace rules into the User pass, which
then applies them to every session. Make the sync strict for a propagated
change and fail the acknowledgement when a live session rejects a rule, so
the Bridge keeps the channel quarantined instead of lifting the fence.
Refs #14483
(cherry picked from commit 3439ca9da9)
* test(acp-bridge): pin that an added runtime MCP server is observed
The paired selector reads hasRuntimeMcpServers() before it lets a session
select Managed. Pin that a successful workspace-level runtime add is visible
to the bridge.
Refs #14488
* fix(managed-agent): diff the effective permission set for workspace changes
The live permission manager keeps the scopes' rules merged, without provenance. Diffing each scope separately can remove a rule another scope still provides: clearing a workspace deny while the user scope also denies it would make read_file runnable again, and the acknowledgement would hide it. Diff the effective set instead, and keep the workspace filter on the apply.
Refs #14488
* fix(managed-agent): carry the permission scope through a workspace change
A single SettingScope.Workspace pass filtered user-scope deltas out of sessions admitted at another root, such as a worktree session, while still acknowledging the change. Carry the written scope in the change payload and use it to pick the audience: User reaches every live session, Workspace keeps the workspace filter. The content still comes from the effective merged set, so a rule another scope provides is never removed.
Refs #14488
* test(acp-bridge): include the permission scope in the propagated payload
The workspace-change payload now carries the written scope, so the exact assertion in the paired-engine test must include it. This also fixes the failing Test (ubuntu) lane on the same head.
Refs #14488
* test(cli): include the permission scope in the propagated payload
The workspace-change payload now carries the written scope. Update the CLI paired-engine assertion to include it, alongside the acp-bridge one.
Refs #14488
* test(managed-agent): parse only complete rows in the wedge driver wire
* test(managed-agent): pin the wire reader to complete rows
The blocked-wedge driver's wire reader had no executable coverage: the
driver cannot be imported (it parses argv and boots a daemon at load),
and the Java gates that spawn it are skipped on PR events. Extract the
parse into a side-effect-free helper so a unit test can exercise it, and
pin the mutants that matter with two complete rows plus an unterminated
tail.
---------
Co-authored-by: 易良 <1204183885@qq.com>
* fix(managed-agent): keep a crash-blocked workflow run unknown and honestly notified, gated by FG10d (#14165)
FG10d, the last FG10 letter, SIGKILLs the packaged Harness under an
attached workflow run, with its agent() request in flight and with its
work done but the child Turn's settlement swallowed at the store, then
starts a cold one. The run must never be re-executed or settled as if
observed: the Turn parks marked hosted_workflow_recovery_blocked, the
cold load refuses, and the record settles recovery_blocked over
outcome_unknown with one blocked notification to the lead.
The gate found two defects in that honest-settlement path:
- A retained close debt wrote the walk done and its CHILD_RESULT row
delivered whatever the arm proved, so the unknown the recovery-blocked
arm and the give-up chain intend read as a delivered result wherever
the host cannot close the child at once. The debt now parks under the
walk's own classification (retainCloseDebt), as classify does.
- settleRecoveryBlocked rendered the parent's notification from the
record it replaces, so under <status>blocked</status> the result line
read "run running (unknown)". It now renders the blocked revision.
The store proxy gains a SWALLOW disposition and the class a killHarness
helper. The Hosted fork's healthy allowance composes 3743 + 131 = 3874 s
and the fork moves 4353 -> 4484 s; the Verify step and job ceiling hold.
The relay design gains FG10d's section; the a+d design points at it.
* fix(managed-agent): release a parent whose child walk retired unknown, and sweep FG10d's review nits (#14165)
Session resource collection held a retired parent while a CHILD_SESSION_START
walk was not done/orphaned, but the relay's `unknown` is just as terminal and
is never rewritten, so the hold had no release; the close-debt fix carried it
to hosts without close capability. The collector now treats `unknown` as
terminal; an owed close rides its own CHILD_SESSION_CLOSE row and reads no
parent resource.
Also: pin the give-up arm's no-close classification, refuse a close row in
retainCloseDebt itself, correct the give-up javadoc and test comment that
still described the old either/or, and sweep the stale four-letter and
FG10d-selector text in the IT header, the CI comment and both designs.
The session-row lock-order canary asserts the turn-row accessor
population exactly. #14461 added the lifecycle gate's live-Turn count and
the recovery-blocked verdict, both of which reach ma_turn, without
updating that pin, so the main Managed Agent gate went red.
Add the two members with the reason the order does not bite on them:
the count runs under its caller's session lock, and the verdict updates
the Turn row alone.
#14435 made placement resolution go through the three-argument
RuntimeProvisioner.createRequest so a bound Session's Harness id travels on
its own. Mockito never runs interface default methods, so this IT's
two-argument stub stopped answering the call the Broker makes: the
provisioner returned null, findOrCreate(null) threw "request is required",
and the Broker mapped that internal failure to a 400
runtime_broker_invalid_request at tool-sessions:acquire.
Stub the overload the Broker actually calls so the acceptance chain receives
the request it constructed.
* test(core): size the hosted cold-scan ceiling from the slowest hosted runners
The recall-scan latency gate gives GitHub-hosted lanes a 2.2x multiplier
over the strict cold-scan bound, sized in #13094 from a 227ms worst sample.
That no longer holds: 12 of 96 hosted Test jobs from 15-17 October failed
the 1000-topic row at 275ms on all three vitest attempts, with best-of-5
samples of 276-346ms, so --retry cannot rescue them. The 200/500-topic
rows never breached 220ms.
Raise the multiplier to 3.4, which keeps the file's ~1.2x-over-worst
convention: 425ms for the 1000-topic row and 340ms for the smaller rows.
The strict bound and the pool arm are unchanged, and the lane-arm table
pins the new values.
* test(core): state the small-row evidence as job outcomes
* docs(managed-agent): record M6 as implemented in #14483
The design still said the last slice registers and enables nothing and that
neither code slice is implemented. Registration and the workspace-change
acknowledgement landed in #14483; the review fixes and the remaining test and
end-to-end coverage are in #14488.
Refs #12737
* docs(managed-agent): correct the M6 follow-up coverage claim
The status said the remaining tests and the end-to-end coverage are in #14488. They are not: #14488 carries the review fixes and one bridge assertion. Say that the workspace-change tests and the end-to-end pass are still open.
Refs #14491
---------
Co-authored-by: 易良 <1204183885@qq.com>
* fix(managed-agent): never re-drive an unsettled workflow Turn on the recovery path (#14387)
A workflow Turn's work is one deterministic in-process script under
node:vm, and a Harness crash leaves no witness to its in-process
subagents. Re-driving it after the crash would re-run the script and
re-dispatch the subagents, spending the work twice while the first
run's outcome stays unobserved — and the recovery attach would settle
the Turn FAILED as if that outcome had been observed.
The coordinator now refuses to re-drive a workflow Turn once its
Session is bound: the Turn stays unsettled, the relay classifies the
record unknown/blocked, and the cold-load refusal keeps the child
Session's reopen refused. A cancel still drives — settling the Turn
cancelled is the cancel's own act, not a re-run. The first launch is
unaffected: harness_boot_id is written only by the first attach, so
the refusal can only fire on a genuinely previously-driven Turn.
* fix(managed-agent): scope, park, and settle the refused workflow Turn (#14387 review)
Review on #14461 found the refusal too broad and too quiet. The refusal
now fires only where re-execution would actually run — the takeover
continue of a parked snapshot and the resubmit of a Turn whose admission
the lost generation never recorded — so a healthy same-boot reconnect
still resumes the event stream of an admitted workflow Turn instead of
being refused with it.
Refusing no longer ends in a bare lease release either: the Turn is
durably marked (still active, never settled as if observed) and parked
out of the dispatch sweep — the claim-refuse-release tick would
otherwise re-qualify every second forever. A cancel still drives:
CANCELLING rows stay scannable, and settling the Turn cancelled is the
cancel's own act.
The child result relay reads the mark off the Turn line and settles the
child_run record recovery_blocked over outcome_unknown through a new
child operation, classifying the ledger unknown — the honest terminal
shape for an execution whose outcome no witness survived.
* fix(managed-agent): close the workflow refusal's replay, refusal-route, close, and lease gaps (#14387 review)
Round two of review on #14461 found four gaps in the refusal path. The
resubmit refusal now requires evidence the generation changed — the
attach's boot differing from the Session's — so a lost submit reply on
the same live Harness still replays through the daemon's admission
dedupe instead of parking forever. Takeover loads that refuse before
returning a snapshot (the decline and the coded 409) route to the same
durable block instead of exhausting retries into a dishonest FAILED.
The lifecycle gate counts only live Turns, so the parked workflow Turn
no longer blocks the child Session's close admission — without it the
blocked settlement could never land. And the blocking verdict is fenced
by the live dispatch lease, so an owner whose lease expired parks
nothing.
* fix(managed-agent): keep transient takeover refusals out of the workflow-loss verdict (#14387 review)
A 409 hosted_turn_recovery_required also covers retryable restore read
failures, which are not evidence the workflow cannot be observed — a
completed result may already sit journaled behind them. The refusal now
parks the Turn blocked only on the daemon's terminal
takeover_unrecovered verdict; every other reason keeps the retry path,
so one transient read hiccup can no longer strand a committed workflow
into the unknown/blocked settlement.
* fix(managed-agent): keep the workflow block off a cancelling Turn (#14387 review)
The block sites in the two takeover-refusal catches gated only on the
recovery path and the workflow input, so a user-cancelled workflow Turn
whose takeover load kept refusing was marked too — but the mark cannot
park a CANCELLING row (the dispatch sweep re-admits it so the cancel can
drive), and the write only re-opened the claim-refuse-release spin with
the cancel never settling. Both catches now carry the same
non-cancelling guard the recovery arm already had: a cancel keeps its
own bounded retry/fail path.
---------
Co-authored-by: 易良 <1204183885@qq.com>
* feat(acp-bridge): expose whether the runtime holds added MCP servers
A paired Bridge has to know whether a workspace runtime holds MCP servers
that were added at runtime, because a Managed child starts without them and
must not be selected while one is registered. The servers live only in the
live child, so track the names the workspace-level add and remove routes
succeed for, and expose the fact on the bridge.
Session-level runtime servers are deliberately not counted: they belong to
one session, not to the workspace configuration a new engine would have to
reproduce.
Refs #12737
* feat(managed-agent): answer the paired workspace-change request
The Bridge sends qwen/control/workspace/change to every live non-Legacy
channel and quarantines a channel that does not acknowledge the exact
revision. The Managed host had no handler, so any workspace control change
would have quarantined it once the engine is registered.
Validate v, revision and kind, ask running turns to cancel for a tightening
change, apply the change through the same handlers the individual control
methods use, and answer the exact revision only after it is applied.
Permission changes re-read the rule sets and sync the live permission
managers, because a settings reload does not touch them.
The prompt admission fence that closes the window between the cancel and the
apply is not part of this commit.
Refs #12737
* feat(managed-agent): register the Managed engine for the primary runtime
Build the Managed engine from the primary runtime's own spawn options and the
facts the M3 evaluation reads, and hand it to the paired selector behind
--experimental-paired-engines. The Managed child therefore shares the
runtime's process registry, heap policy, idle reclamation, pipe limits and
diagnostics, and the daemon's process budget counts it.
A runtime that still holds an added MCP server defers a session to Legacy
through the bridge's hasRuntimeMcpServers(), because a Managed child starts
without those servers.
The secondary and workspace-qualified runtimes are not wired yet.
Refs #12737
* feat(managed-agent): register the Managed engine for every paired runtime
Wire the secondary and workspace-qualified runtimes the same way the primary
runtime is wired: each builds the Managed engine from its own spawn options
and the compatibility facts of that workspace, so a session in any paired
runtime can select Managed and the Managed child shares that runtime's
process registry, heap policy, idle reclamation, pipe limits and diagnostics.
A runtime that still holds an added MCP server defers its sessions to Legacy.
The live-conversation provenance keeps its single factory.
Refs #12737
* feat(managed-agent): fence prompt admission while a workspace change applies
A tightening change cancels running turns before it re-reads and applies the
new revision. A prompt arriving between the cancel and the apply could still
start under the rules the change replaces. Refuse a prompt with a retryable
error while the change is in flight, and lift the fence when the handler
returns.
Refs #12737
* test(managed-agent): pin the Managed engine's compatibility inputs
The engine must pass the workspace the selector names, the runtime's trust
decision and its runtime-MCP state into the M3 evaluation. Cover the
untrusted workspace and the added-MCP cases, which both defer a session to
Legacy without starting a host.
Refs #12737
* fix(managed-agent): refuse to acknowledge a skipped tightening change
A settings reload applies to idle sessions only. For a tightening change, a
session the reload skipped still holds the old revision, so answering the
exact revision would let the Bridge lift its quarantine without the change
having reached that session. Leave the request unanswered instead, which
keeps the channel quarantined and the failure closed.
Refs #12737
When a newly due slot is blocked by overlap or the count limit and no
public Turn is open, the scanner sends reconcile_run for the runs that
block it. An automation wake turn is not a public Turn, so the reconcile
routinely reaches a Session whose wake turn is still running. Once that
turn has written its user record, the aftermath's crash test
(wakeHasPriorAttempt) reads it as a prior attempt; only the route's
`session.active === undefined` guard keeps it from settling the live run
as failed/outcome_unknown and consuming its input. Deleting that guard
kept every test green: one existing route test reports through the
per-Session aftermath latch, the other over an input that was already
consumed.
The new route test holds a wake turn's model call after its user record
is durable, sends reconcile_run with no aftermath in flight, and asserts
the answer is `repaired: false` with the run still running and the turn
still active. It then releases the model and checks the run settles by
itself, with one model call and one completed turn.settled. With the
guard removed it fails; the other reconcile tests stay green. On a
failure it releases the model and waits until the Session no longer
reports an active prompt before the teardown closes it.
* ci(sdk-java): run the EventTransport gate lanes on the ECS pool
The broker and multi-node EventTransport lanes were pinned to hosted
runners because the RocketMQ cluster bound fixed ports. Their trusted runs
now route like the MariaDB lane, behind the same trust clause and
MAINTAINER_ECS_RUNNER_DISABLED kill-switch, and are made safe on a shared,
reused pool host:
- A new step picks free ports for the NameServer, broker and proxy (with
the broker's listenPort - 2 and + 1 companions free too) and exports
them; rocketmq-lite-broker.sh already reads them from the environment,
and the gates take the proxy endpoint from the same variable.
- Both lanes install and verify against a job-local Maven repository
seeded from the host-shared ~/.m2, so the fixed SDK coordinates never
round-trip through it, and set up Maven on the pool.
- The multi-node lane's MySQL moves from a service container to a
job-owned container with native AIO off and a tmpfs datadir (#14416),
removed on every outcome. Node comes from the pool's own install,
libfaketime is installed only when the host lacks it, and the
unnecessary `corepack enable` is dropped.
- Both lanes gain the pool's pre-checkout hygiene steps, and their job
ceilings absorb the setup stretch a loaded host adds.
hosted-harness-mysql stays hosted: it delegates a cgroup v2 root at a
fixed /sys/fs/cgroup path through sudo, and its failover E2Es spawn
native mysqld from the hosted image's MySQL server packages.
The routing matrix, per-job ports (the chooser runs for real), the MySQL
container contract, job-local Maven repos and pool hygiene are pinned in
the workflow meta-tests.
* ci(sdk-java): move the RocketMQ port chooser to a script and record the size
Lint & Static failed the workflow-size ratchet: sdk-java.yml grew 21571
bytes over its recorded 74834. The duplicated inline port chooser moves to
.github/scripts/choose-rocketmq-ports.mjs, which both lanes run and the
broker lane's test still executes for real.
The remaining growth is real. Every pool job in this file carries the
ownership heal, the stale .qwen sweep (pinned byte-identical to ci.yml)
and the .git recreation. These run before checkout, so they cannot live in
a repository script, and the two newly pooled lanes need one copy each.
The job-owned MySQL container and its cleanup make up most of the rest.
Record 93793 bytes.
* feat(managed-agent): reconcile parked Session title deliveries (D7-2)
A title delivery whose submission went unanswered now parks
recovery_blocked like every other acceptance-unknown kind, and the
operation reconciler settles it from the Session journal's
hosted-title:<revision> commit. Parking retires the delivery's receipt,
so a crash-parked title no longer wedges later titles or the Session
lifecycle; legacy UNKNOWN rows are converted by the delivery sweep.
* test(managed-agent): cover parked Session title reconciliation (D7-2)
* docs(managed-agent): describe the D7-2 Session title phase
* fix(core): fill empty signature on unsigned thinking blocks for strict Anthropic-compatible proxies
Cross-provider history where the upstream generator set thought: true without a thoughtSignature replayed as a thinking block with no signature field at all, which strict Anthropic-compatible backends (SGLang, llama.cpp, vLLM) reject with HTTP 400. The existing fillMissingThinkingSignatures pass only ran under DeepSeek normalization, and Claude 4.6+ proxies drop unsigned blocks instead, so the remaining proxy quadrant shipped unsigned blocks.
Add a fillUnsignedThinkingSignature converter option (same signature: '' fill) and enable it for non-DeepSeek, non-native base URLs with thinking enabled. Native API history stays untouched and 4.6+ proxy behavior (drop) is unchanged: the drop pass still treats the empty signature as unsigned.
Fixes#11772
* fix(core): repair unsigned history thinking independent of the outgoing thinking config
The fill gate was `!!thinking`, a property of the outgoing request, but the
blocks being repaired come from history: the #11772 consumers (auto-memory
extraction, prompt suggestions, skill review) fork with
`includeThoughts: false`, so the fill never ran for the reported bug.
Factor the shared proxy-hosted-Claude condition into one variable and drop
the thinking dependency from the fill; on Claude 4.6+ through a proxy the
drop pass still deletes the filled blocks, so that quadrant keeps its
drop-only wire shape. Re-qualify the quadrant comments per review and add
the thinking-off regression case (3 files / 326 tests).
* fix: keep the unsigned-signature fill off 4.6+ adaptive models
The fill pass now runs only for proxy-hosted Claude on non-4.6 or
unparseable model ids, so the two passes stay disjoint by
construction: 4.6+ with thinking on drops the unsigned blocks (as
before), and 4.6+ with thinking off ships them exactly as main did.
Add the row-4 pin test and scope the SGLang citation to v0.5.19.
* chore: empty commit to retrigger CI and review
---------
Co-authored-by: Сергей Панченко <s.panchenko@mcplat.ru>
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
* ci(sdk-java): run the Hosted failover arms after an unrelated Verify failure
The 15 failover E2E steps had no if:, so any failing test in the Verify
step skipped them all. Since #14181 merged, four consecutive main runs
failed Verify on four different tests, and the seven G3 Step 3 arms have
never executed in CI.
The MySQL install step now runs whenever the Managed Agent dependencies
installed, and first checks that the server jar was packaged; each arm
runs when that step succeeded. A failing arm still fails the job.
* ci(sdk-java): move the Hosted failover arms into their own job
Conditions on the arm steps let them run after an integration-test
failure, but they still waited behind the 80-minute Verify step and
depended on its packaged jar. The fifteen arms, the MySQL binaries
install, the runner-only QWEN_E2E_REGISTER_STORAGE and the Step 3
evidence upload now form hosted-failover-e2e, which bundles the CLI,
installs the SDK and packages the server with -DskipTests itself, with
no needs: on the Hosted MySQL job. Each arm still runs when an earlier
one failed.
The Hosted MySQL job ceiling drops from 325 to 175 minutes; the new job
has 165. The pins move with the steps, the long-answer arm joins the
arm table, the SpotBugs staging count goes to five, and D11, the Step 3
design, the server README and the runner's Linux-only hint name the new
job.
* docs(managed-agent): point the remaining failover-arm references at the new job
The D11 Status header, the 275/290 ceiling of the Step 3 slice and the
ubuntu-only lane note still named the Hosted MySQL job as where the
failover arms run, and so did the G1 failover design, the Broker fault
gates scope and the review-corrections gate list (whose Workspace
file-tool Turn and durable-store adapter do stay in that job, so the
sentence splits). Point-in-time notes keep their history and add where
the arms run now.
The Hosted MySQL report upload has to precede the two steps that run
`clean verify` on the module, or it ships an empty artifact with only a
warning; the test now pins that order. `--big-output` joins the mode
flags each failover row must own alone.
Record the last slice of the ordinary-host Managed engine: registration of
the M2 host with the M3 compatibility evaluation behind
--experimental-paired-engines, the workspace-change acknowledgement a Managed
channel currently lacks, and the lifecycle and quarantine contract.
The section is marked design only. No production caller builds the engine and
the Managed host does not answer qwen/control/workspace/change yet; the two
code slices land separately. It also corrects the permission-change path: a
settings reload does not touch live permission managers, so the child must
re-read the rule sets and sync them the way the permission-rule control
method does.
Refs #12737
* feat(serve): attach private CSI Sessions to Hosted text turns
Extract the first private Hosted attachment and native no-tool text turns from the CSI proposal, stacked on the original Session authority. Keep original ownership, exact prompt replay and local-only stop semantics, with producer-to-SQL grammar and boundary tests.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* docs: record CSI text validation on main
Update the synchronized design status after the private Session authority and 32-table baseline landed, preserving the text-only extraction scope.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(test): qualify the CSI fixture AbortController
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
---------
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* test(managed-agent): add the FG10c/FG10e workflow relay store-failure and cancellation fault gates (#14165)
Slice 2 of FG10b/c/e/f on HostedWorkflowRelayFaultGatesIT. No production
code changes.
FG10c refuses, at the store, the lead's result revision and the
child_acceptance insert that lands the notification, armed only after
the subagent hold let the lead's own Turn settle and the child Turn
complete, so the refusal latches an idle lead and never crosses a running
workflow Turn. While latched the record never advances and nothing is
delivered; after the trigger drops and the Harness restarts cold, the
relay's retry finishes the walk once and the script never runs again.
FG10e cancels the workflow task with its agent() request held: the child
Turn ends CANCELLED, the record settles cancelled/stop_requested, the held
answer never reaches the child journal and no notification lands. A lost
cancel reply replays the admitted cancel under the same key, and a cancel
of a settled task answers 409 task_action_unavailable.
The four-letter class measures 181.2-181.6 s, so the Hosted allowance
composes 3545 + 198 = 3743 s: fork 4155->4353 s, Verify step 85->90 min,
job 330->335 min. The design records the landed letters, the mechanics
and five mutations (two narrower ones equivalent).
* test(managed-agent): witness that a workflow cancel aborts the held subagent request (#14165)
FG10e's cancel-subagent-in-flight case observed only the run settling
cancelled, which the sandbox's abort race produces even when the stop
never reaches the agent() dispatch. The held subagent request is now a
live SSE stream (headers at once, a comment every 250 ms), and the case
asserts the request was aborted while the hold was still closed.
A mutation handing createProductionDispatch a fresh signal instead of
the run controller's turns the case red at the new witness; the case
without it stayed green. Recorded as mutation 9 in both design docs.
Also correct the store retry bound's quoted source line in both relay
gate classes and take the task id from ManagedExtensionProjection.taskId.
* fix(ci): avoid full application tests for CODEOWNERS-only changes
Keep ownership-only changes on lightweight CI without downgrading automatic review.
* docs(ci): document GITHUB_CI_ONLY_FILES membership rule
* fix(ci): skip application tests for Markdown-only changes on any path
Only docs/**.md and a few root prose files classified as docs_only, so a
PR touching nothing but a package README, a .qwen skill or AGENTS.md still
ran the full unit and no-AK integration suites. Markdown on any other path
now selects github_ci_only: application tests are skipped and automatic
review stays at full effort, since much of that Markdown is runtime
instructions that must not share docs_only's review downgrade.
Markdown under src/ or a fixtures tree keeps the full profile: it ships in
packages or feeds test contracts, and colocated tests assert its content
(bundled SKILL.md tests, the review skill parity check). Mixes of docs,
such Markdown and helper files also avoid application tests.
* test(ci): pin CODEOWNERS mixed with docs to the lightweight profile
Docs, test-free Markdown and helper files in any combination now avoid
application tests, so CODEOWNERS plus README no longer needs full CI.
* Revert "test(ci): pin CODEOWNERS mixed with docs to the lightweight profile"
This reverts commit c7253f4cf8.
* Revert "fix(ci): skip application tests for Markdown-only changes on any path"
This reverts commit 3316978f60.
* Revert "Revert "fix(ci): skip application tests for Markdown-only changes on any path""
This reverts commit d4d96a93af.
* Revert "Revert "test(ci): pin CODEOWNERS mixed with docs to the lightweight profile""
This reverts commit 6b01091f8c.
* fix(ci): keep Markdown that tests read on the full profile
Markdown-only changes skip the application suites, but some Markdown
outside src/ and fixtures is read by tests that only run on the full
profile: AGENTS.md (the review rule loader's test), the CI bot skills the
workflow tests pin, packages/managed-agent-server/README.md and a few
others. A change to one of them would merge without those tests running.
Those files are now listed and keep the full profile. A dependency-free
classifier test scans every tracked test file and fails when one names
lightweight Markdown that is missing from the list, by full path, by its
last two segments (including separate path.join arguments) or by a
relative literal, so the list cannot go stale silently.
* fix(ci): run the trusted web-shell browser smoke on the ECS pool
The web-shell E2E smoke is the second-largest user of the hosted ubuntu
pool: on 2026-10-17 it took about 27% of hosted minutes, two thirds of
that from same-repo PRs, while the hosted pool sat at its 20-job org cap
and the ECS pool idled.
#11250 moved the job hosted after four pool attempts failed in run
34081409590. Three of them ran the smoke with Playwright's unpinned
default of half the host's cores, 8 workers against hosted's 2, and timed
out broadly on visibility and clock waits across long-standing tests. The
fourth's transcript gate took 73 s against its 60 s budget while npm
install alone took 28 min on that host.
Route the job like test's again, so trusted runs use the pool and untrusted
fork PRs stay hosted, and pin the smoke at 2 workers, the hosted default,
so a pool job no longer scales its browser count to the host. Drop the
comments that described the pool steps as retained for a future reroute.
* fix(ci): recreate runner Git metadata before the smoke checkout
The smoke job now checks out on the reused ECS workspace, so it needs the
same pre-checkout .git removal as test, lint_and_static, integration_cli and
integration_no_ak. Otherwise actions/checkout reuses whatever the previous
job left, including a broken shallow boundary or a live post-checkout hook.
Hold the job to that rule in qwen-runner-checkout-workflow.test.js.
* fix(ci): install WebKit's system libraries on pool hosts that lack them
The first pool run of the web-shell smoke (run 39030597470, ecs-qwen-hk3-11)
passed 272 of 286 tests in 10.6 min at 2 workers, and the transcript gate
took 17.8 s against its 60 s budget. All 14 failures were mobile-webkit
browser launches: the host lacked GTK 4, GStreamer, libwoff2 and about 40
more WebKit libraries, which Playwright's install step had already
reported in its host validation.
When that validation reports missing libraries, run Playwright's
install-deps for Chromium and WebKit through sudo -n. An APT_CONFIG adds
a dpkg lock timeout and the hosted step's network timeouts. The packages
persist, so each host installs them once and later jobs skip apt. The
install step gets the hosted step's 30-minute bound.
* fix(ci): give the hosted Test lane the same 120-minute ceiling as ECS
The full unit suite no longer fits 60 minutes on GitHub-hosted runners.
Those runners take fork PRs from untrusted authors and everything when
the ECS kill-switch is on. On 2026-10-17, of the hosted full-profile Test
runs that ended on their own, 13 passed in 45-59 minutes and 17 were
cancelled at exactly 60 while still passing tests. Job 117375307261 is a
typical run: install 7.4 min, cli 32.5 min, core 10.2 min, then cancelled
six minutes into web-shell with the root scripts tests still to run. Each
cancellation burns 60 hosted minutes and usually a manual rerun.
Drop the per-pool split and use 120 on both pools. The test step's own
110-minute cap still bounds a genuine hang.
* ci: record the Test lane's fixed 120-minute ceiling in the CI budget
check-ci-budget sums only fixed timeout-minutes and counts an expression as
a job without a fixed timeout. Turning the test job's per-pool expression
into a fixed 120 therefore moves ci.yml from 180 minutes with five
expression timeouts to 300 with four. The effective ECS ceiling is
unchanged; the hosted one rises from 60 to 120 as intended.
* feat(ci): gate Managed Agent changes on a full SDK Java run before merge
Changes to the Java modules or the harness they drive now run a fast
SDK Java subset on every push and the full suite on the first approval
of the current head. An always-reporting `Managed Agent gate` check can
be made required: a touched PR reports it only from a full run.
Refs #14392, #14381
* fix(ci): route the Managed Agent gate's own jobs off the hosted queue
The scope and gate jobs report the would-be required check; on the first
run they sat in a hosted queue holding 123 runs. Trusted runs now take
the ECS pool with the shared pre-checkout hygiene, forks stay hosted.
* fix(ci): register managed-agent-gate.yml in the PR-stage CI budget ledger
managed-agent-gate.yml triggers on pull_request and merge_group, so it is
PR-stage and needs an entry in .github/workflows/.ci-budget. That ledger is a
fail-closed whitelist: a missing entry makes check-ci-budget.mjs error out and
short-circuits the remaining Lint & Static steps, so no linter ran.
Recorded exactly as measured (3 jobs, 15 declared timeout-minutes, 1 job
without a fixed timeout) so no slack is left against the ratchet.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmvb6htxaa7
* fix(ci): run the full Managed Agent unit suite in the fast tier
The fast tier skipped mysql-integration, the only lane that runs the
module's ~4,200-test H2 suite in full, so a PR push got no unit
feedback until the review-triggered full run. In the fast tier the
Java 21 leg now runs the whole module suite instead of its Runtime
Broker subset, after the same workspace install mysql-integration uses:
part of the suite loads the built Core and CLI JavaScript.
* fix(ci): harden Managed Agent gate tier selection and cancellation reporting (#14397)
* fix(ci): gate the Managed Agent full tier on a maintainer approval
The full tier was reachable by any approved review, including the
deferred APPROVE that qwen-triage-finalize.yml posts once a PR's checks
land green. That workflow now watches this gate, so its own fast tier
could buy the most expensive run in the repository with no human
involved. tier=full now also requires the reviewer's author_association
to be OWNER, MEMBER or COLLABORATOR, and fails closed when it is absent.
The concurrency group embedded the event name, so a push could never
cancel the approval run it superseded: that run kept going for about 125
minutes and then reported a head that branch protection no longer reads.
The group is now keyed on the pull request, with cancel-in-progress still
true only for pull_request, so approvals keep queueing behind each other
instead of displacing a pending full run.
The design docs also described a Maven cache composite that is not in the
tree, called the untouched-PR cost hosted-only when trusted runs take the
ECS pool, and carried a touched-push figure from before the fast tier took
on the full module suite. Non-goals now record that this gate supersedes
the separate compile gate proposed in #14385, and that the Java SDK
checkstyle step stays pinned to the Java 11 leg, so only the full tier
runs it.
* docs(ci): stop citing the Maven cache save the merge from main removed
Merging main brought in #14396, which suppresses setup-java's post-job
Maven cache save on self-hosted runners by making the cache input
conditional on runner.environment. That was the measured cause behind two
claims this branch carries, and both went stale the moment it landed.
The design doc's Cost section said removing the 14 to 37 minute save was
planned work with no implementation in the tree. It is now merged, so the
330 machine minute full run is a figure measured before the saving went
away and should come in lower; it is stated that way, without a
re-measured number this branch does not have.
The daemon-e2e budget comment and its test justified the fast tier's 90
minutes entirely by that save. The value stays: the leg's own sizing puts
stretched setup at about 44 minutes against a 45 ceiling, and the fast
tier now runs it on every touched PR push rather than nightly only, so it
meets that tail far more often. The comment now says that instead of
citing a save that no longer happens.
* fix(ci): diff a review event against its merge commit's first parent
A pull_request_review run checks out the same test merge commit as a
pull_request run (run 38997396688: "Merge 7d918f7 into 2746553"), so
HEAD^1 is already the base. The payload's base sha can lag main and pull
main's own Managed Agent changes into an untouched PR's diff, starting
needless full runs.
* fix(ci): keep comment reviews out of the gate's pull request group
A concurrency group keeps one pending run, and a newcomer cancels it
whatever cancel-in-progress says. With every event of a PR in one group,
a comment review arriving while an approval run waits behind the fast
tier cancelled that approval run, and the head's required check stayed
expected until someone approved again. Reviews that are not approvals
now run in a group of their own.
---------
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
* test(managed-agent): expect tool progress frames in the FG6e SSE gap gate
#14405 projects a committed tool intent and its receipt as public
item.tool_call.updated events, so a hosted tool turn now publishes five
events instead of three. The FG6e driver and probe still pinned the old
shape: depending on when the in-progress update reached the store, the
early observers saw frame 2 before the tool finished or the finished-phase
ledger check met two unexpected tool updates. The gate now waits for the
in-progress update before disconnecting and expects ids 1-2 before the
tool finishes, 3-4 after it and 5 at the terminal.
* test(sdk-java): fault only the caller's dispatch in the send-escape tests
The three pre-dispatch escape tests armed a one-shot executor fault and
let the next execute() take it. A SelectorManager task trailing the
previous response can call execute() first on a loaded runner; the fault
then kills the HttpClient's selector, the real send fails with an
IOException, and the test sees PromptAdmissionUnknownException instead of
the injected throwable. Only the calling thread's dispatch takes the fault
now.
* test(managed-agent): expect schema V8 in the SSH workspace MySQL IT
#14318 pinned the fully migrated managed schema at version 7 in this
integration test, and #14404 then added V8. The two merged independently;
the MySQL lane, which only runs in the SDK Java full workflow, now fails
all twelve cases in the setup assertion. The other version pins already
moved to 8 with #14404.
* test(sdk-java): widen the keepalive idle-budget margin
The keepalive test sent a comment every 120 ms against a 500 ms idle
budget, so any half-second stall in the server writer or the client's
body delivery on a loaded runner tripped the watchdog. It failed that way
on ubuntu-latest / Java 17 (SDK Java run 39018177648). The budget is now
1500 ms and the consumer stays parked for over two budgets, which keeps
the test proving that keepalives alone hold the stream open.
* test(sdk-java): keep tiny-executor setup traffic off the injected rejection
The one-worker executors used a two-slot queue, so a burst of HttpClient
tasks during createSession on a slow Windows runner was rejected before the
behavior under test (run 39021209055). Saturate an unbounded queue only once
the test arms it, and keep the submit-time rejection tests from rejecting a
selector-thread task trailing createSession, which aborts the client.
* test(managed-agent): expect FG6e tool progress as transcript items, not events
The transcript query keeps item.tool_call.updated out of its events and
materializes it as a tool_call item, like text deltas (run 39021209055).
* test(managed-agent): drop unused test imports that fail Checkstyle
---------
Co-authored-by: qwen-code-bot <qwen-code-bot@users.noreply.github.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
* fix(cli): unmark the stop request when the stop proves nothing (#13930 R1-42, R1-15)
* fix(core): keep pre-closure-edge snapshots inspectable on the read walk (#13930 R1-43)
* test(core): drop mock-synthesized closed-gate cases that production can no longer refuse (#13930 R1-65)
* docs(core): drop the stale disabled-domain claim from the partition test (#13930 R1-44)
* fix(managed-agent): key the hold family on the validated toolName (#13930 R1-23)
* refactor(managed-agent): parameterize the hold-control envelope over ManagedHoldFamily (#13930 R1-21)