* fix(ci): stabilize TUI attachment wait and allow full acceptance run
* fix(ci): record the tui-parity budget at its new 75-minute total
The parity job moved from 30 to 45 minutes, so the two jobs in the workflow
now declare 45 + 30 = 75. check-ci-budget.mjs treats a raised total as a
deliberate ratchet and refuses the ledger entry, so Lint & Static failed on
this PR's own change. Update the line it names; the workflow and the guard
test are unchanged.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
---------
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Both failing branches of the `Verify temp paths carry no short alias` step
emitted a bare console.error, so a real 8.3-alias regression reached the
check UI as only "Process completed with exit code 1" with no annotation
attached to the run. Prefix each message with '::error::', matching the
hosted-process-suite step in this same workflow and
configure-windows-runner/action.yml. The casing-only branch keeps its
deliberate ':⚠️:', and process.exitCode = 1 is unchanged.
Patrol-Run: qwen-issue-patrol/jmvkv59rei6
Co-authored-by: yiliang114 <yiliang114@users.noreply.github.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
The verify-sdk-package job's publish dry-run queries the live registry, so
every dispatch after a release fails with the npm 11 collision error
'You cannot publish over the previously published versions' until the next
version bump (observed on #12442's dry runs 39809916847 and 39921492569;
main's last green dispatch, 39555655699, predates the 0.26.0 publication
by three hours). Treat only that exact registry-collision message as a
skip with a notice; every other failure (auth, network, packaging) still
fails, and the real publish jobs stay untouched. Adds matcher unit tests,
including the bare-exit mutation that must not be masked.
* docs(verify-pr): add a --publish shorthand for maintainer rounds
A maintainer round that publishes its report had to restate the same five
clauses (English body, collapsed Chinese, screenshots, assets branch, merge
reference) every time to unlock the skill's opt-in publish path. Restating
drifts: a round that omitted one clause shipped a thinner report.
--publish now resolves the whole deliverable on its own and names the branch
order --assets-branch, $QWEN_VERIFY_ASSETS_BRANCH, assets/pr<n>-validation --
so a stable image host is configured once. Only coordinates default; posting
consent stays per-invocation, and the repo rule still forbids an origin
fallback. The local path keeps the guard and read-back, which the workflow
publisher does not cover.
* docs(verify-pr): keep the deliverable list in one place
Review pointed out that the new contract block restated the report format
already owned by the local publish rule, so a single list existed twice — the
drift this change set out to remove.
The block now names the four deliverables and points at the rule for their
shape; the rule states the shape once. The consent split, the branch order and
the untouched repo rule are unchanged.
The Linux lanes now route to the ARM64 self-hosted fleet, but the linter
bootstrap only mapped linux/x64 (plus both macOS arches), so `--setup`
threw "Unsupported platform/architecture: linux/arm64" and every real lint
step after it was skipped — the lane was red having checked nothing.
Pin the ARM64 builds the two linter steps already know how to unpack:
actionlint 1.7.12 linux_arm64 and shellcheck 0.11.0 linux.aarch64, with
digests recomputed from the exact release URLs the installer builds.
* fix(ci): refuse a partial jobs page list before reporting
`--paginate` prints one document per page and the step merges them with
`jq -s`, so a page 2 failure leaves a list holding only page 1. The exit
status cannot tell that apart from a complete read, and the partial file
reads exactly like a whole run to the failed-job projections and to the
infra classifier — which counts only the jobs present, turning a regression
parked on the missing page into a rerun instead of an issue.
Stage the merge in a temp file and promote it only when the merged job
count matches the payload's own total_count; otherwise report without the
job list. The receipt keeps the guard independent of the shell's pipefail
setting, which the meta-test now exercises by spawning the step without it.
* fix(ci): require the job-list receipt instead of skipping it
`$total == null or …` treated a response that carries no `total_count` as
complete, so a populated first page followed by an API error published its
partial list — the inverse of the contract the step's own comment states,
reproduced with jq: the predicate exits 0 and emits page 1 alone.
Make the receipt part of the pass condition: a page set is promoted only
when it declares a total and the merged jobs match it. No total means no
proof of completeness, so it takes the same fallback as a failed fetch —
empty list, staging file removed, warning. The new negative case feeds a
populated page without `total_count` and asserts that fallback.
* fix(ci): say when the job list could not be read in full
The reporter degraded to an empty list with only an annotation on its own
job, so the PR comment and the issue looked exactly like a run with no
identifiable failure — the readers who need the signal never saw it. Carry
a jobs_complete output onto both bodies: when it is false they say the
report may name fewer failing jobs than the run had, and the issue body
guards the sentence so a recurrence does not stack a second copy.
Also widen the meta-test pin back over the fetch's producer, so --paginate
and per_page=100 stay pinned: the earlier rewrite started the span at the
merge and left both unpinned while the comment still claimed they rode it.
* test(ci): name the receipt case for what it covers
The case now rejects both a truncated page set and a response carrying no
total_count, so a name that mentions only "stops short of total_count"
understates it. The flag's comment said "both reports" where the two
readers are the PR comment and the issue body.
* fix(ci): let a certified run clear the short-list note
The note added by the previous commit was append-only: once one degraded
run wrote it into a tracking issue, every later replan preserved it --
including runs whose job list was read in full and receipt-certified -- so
a rebuilt failure block sat under a permanent claim that the list might be
short, and the substring guard then refused a later degraded run a fresh
note of its own.
Strip the exact sentence before deciding whether to re-add it, so a
certified run clears a stale copy and a degraded run speaks for itself.
Pin the producer-to-consumer wiring (step id and both env mirrors), add the
plan path's negative control, and cover gh exiting 0 with a short list.
* fix(ci): reuse open workflow failure issues
* fix(ci): single-quote the concurrency queue value
yamllint's quoted-strings rule (quote-type: single, required: true) rejects
the bare `max` scalar, which is what failed the Lint & Static lane at
.github/workflows/main-ci-failure-issue.yml:342:14.
The value is unchanged after parsing: `queue: max` and `queue: 'max'` both
yield the string "max", so the workflow semantics and the parsed-document
assertions in scripts/tests/main-ci-failure-issue-workflow.test.js are
unaffected.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmvjvf9h5qa
* fix(ci): publish the tracking issue before the cosmetic label edit
A non-zero exit from the decorative `--remove-label` edit aborted the
writer step under the `set -euo pipefail` this branch adds, losing both
the recurrence comment and `issue_url`. report_pr is `always()`-gated and
reads `issue_url` unconditionally, so it posted the merged PR's failure
summary with the [Tracking issue] link silently omitted. Restore the
ordering the deleted comment documented: url and comment first, then the
retirement.
Also drop the `issues: read` grant the analyze job no longer uses now
that the plan step is gone, and pin the paginated lookup's page merge,
which no fixture exercised because every one fed a single JSON document.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmvk8a84dqp
* fix(ci): preserve provenance and lifecycle of failure aggregates
---------
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(managed-agent): refuse `;` comments in the Harness credentials file
systemd skips a line starting with `;` as a comment, but a shell refuses
it and stops reading the file at that line. With such a line above the
two keys, the control plane read both credentials while a Harness
started through `set -a; . harness.env` got neither. The parser now
refuses the line wherever it falls, naming the file and line, so the two
readers agree again.
The design doc, READMEs and template now say to start comments with `#`,
and the design doc names the remaining shell-only stop: a line in
another key that a shell cannot parse, such as an unquoted `(`.
* test(managed-agent): pin the `;` refusal above the keys and correct the shell-stop note
Pin the reported shape: a `;` line above both credential keys, and one
between them, are refused at their own line without echoing the token.
The bootstrap test now also checks that the generated harness.env has
no `;` line, as the template test already does.
The design doc said a shell stop at an unparseable line always fails
the harness row's live probe. bash keeps the assignments above that
line, and the probe sends only the token, so --check notices only a
lost token. The doc now says so, and names the `KEY = value` shape a
shell runs as a command.
* fix(cli): restore bounded OpenTUI dialogs and add migration gates
* fix(cli): preserve runtime boundaries during standalone updates
Preserve Node/Bun archive flavor and validate actual CLI loading, package
identity, version and prepared installation identity before activation.
Keep Node npm transport and restrict Bun registry lookup to trusted config.
Integrates chiga0's update stack through ce67edaba5,
with isolated fixtures and real Node/Bun runtime regression coverage.
Co-authored-by: gary.gq <gary.gq@alibaba-inc.com>
* fix(cli): preserve OpenTUI input and attachment ownership
* test(cli): bound cancellation test waits and cleanup
* feat(cli): ship private Bun for opt-in npm OpenTUI sessions
* feat(cli): align OpenTUI status lines with Ink
* fix(cli): align OpenTUI editor and cancellation transactions
* fix(tui): harden migration cleanup and evidence after review
* test(tui): fail acceptance on unhandled errors
* fix(cli): validate standalone launchers and registry settings
* docs(cli): clarify consolidated OpenTUI delivery status
* test(cli): isolate system settings in TUI parity fixtures
* test(cli): isolate compile cache in standalone runtime checks
* test(cli): reduce standalone runtime fixture compression cost
* fix(cli): settle OpenTUI cancellation when replay resets a session
* feat(cli): align OpenTUI Vim, history and session output
Reuse the shared editing and history contracts, preserve cancellation ownership, and connect accepted interactive turns to the existing output bridge.
* style(cli): format merged OpenTUI test callbacks
* test(core): control terminal conversion in PTY parking regression
* test(cli): preserve visible input across session rotation
* test(cli): cover shell recall provenance and streaming search selection
* test(cli): await cancellation before releasing held response
* fix(cli): preserve imported history ownership and active search
* docs(cli): synchronize consolidated OpenTUI validation status
* test(cli): await AppContainer configuration cleanup
* test(cli): mock Vim clipboard imports before module loading
* test(cli): check standalone asset roots for npm subpaths
* test(cli): share clipboard mocks across module export views
* test(cli): verify finalized MOD parts and shared JSON projection
* fix(cli): reuse Ink editing history for OpenTUI Unicode input
* test(cli): keep native Vim input callbacks void
* test(cli): preserve Escape boundaries in Unicode Vim fixtures
* test(cli): read the current composer across Vim remounts
* test(cli): align Unicode deletion cursor with Ink oracle
* fix(cli): preserve completion and escape precedence and validate status colors
* fix(cli): use boundary selection for the OpenTUI composer
* fix(cli): preserve attachment navigation with one completion
* test(cli): preserve Shift in the native redo key sequence
* fix(cli): display session titles in the OpenTUI composer border
* fix(cli): discard generated command work after cancellation
* test(cli): align Unicode unit cursor with Ink oracle
* test(cli): model native selection key delegation
* fix(cli): preserve Vim Escape ownership across listeners
* fix(cli): preserve shared Vim command messages in OpenTUI
* test(cli): observe rendered Vim mode during parity checks
* docs(cli): record integrated OpenTUI validation checkpoint
* fix(ci): account for dedicated TUI parity coverage
* fix(cli): preserve collapsed paste payloads across Vim undo
* docs(cli): refresh OpenTUI distribution acceptance status
---------
Co-authored-by: gary.gq <gary.gq@alibaba-inc.com>
* refactor(managed-agent): Simplify first-release contracts
Remove unreleased compatibility paths across the managed runtime, Java control plane and WebShell. Keep one fresh V1 baseline and document that schema versions stay at V1 until the first formal release.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* test(managed-agent): Align remaining tool fixture with current contract
Restore schema rationale comments while retaining the V1 baseline.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(managed-agent): Align CSI proof with current checkpoint encoding
Require the three current nullable checkpoint fields in the native proof, keep malformed and missing fields rejected, and synchronize retained fixture bytes and digest chains. Update tests that still expected retired pre-release state and replay compatibility.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
---------
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* feat(managed-agent): add local serve and lightweight runtime packages
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* feat(managed-agent): download local server runtime on first use
Require local Java 21, install the exact-version service JAR atomically from OSS or GitHub, and reuse verified runtime caches offline. Add protected signed Central publication and identical release distribution without bundling Java in the CLI.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(ci): register managed runtime workflow size
Record the new release workflow in the repository size baseline so the static CI gate can track it.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* feat(managed-agent): Share the local daemon and Managed entry
Serve daemon APIs and the signed Managed proxy from one loopback listener. Keep workspace ownership explicit, enable authorized local directory import and preserve the daemon trust boundary.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(cli): expose configured models in local Managed tasks
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* test(web-shell): Cover Managed and local deep-link restoration
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* test(managed-agent): Align local database expectation with main schema
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(managed): Keep local model and approval choices independent
Use native initial model selection for local model presets and persist it with the selected approval mode before admitting the first Turn. Preserve custom Agent definition policy pins and creation replay semantics.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
---------
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* test(managed-agent): replace the Runtime MCP connection mid-stream (FG12e)
FG12f's replacement case commits a new AgentDefinition revision while the
running Session stays pinned, so no Runtime MCP connection was ever replaced
under a stream gap; #14447 and #12380 still listed that boundary as open.
The new HostedMcpStreamGapIT cell holds a model-driven MCP tool call, cuts a
public and a WebShell observer, and sends the Harness-private configure
command for revision 2 under the attachment Spring holds for its own Session
(the Harness, not the Broker, mints that client id; the FG13 gates read it the
same way). It pins that the Runtime opens revision 2 beside revision 1 and
retires revision 1 only after its held call drains, that both streams resume
with no gap and no duplicate, that the held call is answered once by the
process it started on, and that the next call runs on the replacement.
awaitCompleted now stops at any terminal Turn state, so a failed Turn reddens
at once instead of after the minute-long wait.
Bundle witnesses: closing a retiring sibling with a pending call reddens at
the retiring assertion; keeping the first catalog after a replacement fails
the Turn. Measured 12.9-13.8 s in-class on JDK 21 + MySQL 8.4.6, budgeted at
28 s: healthy 10651 s, fork 11251 s, Verify stays 198 minutes.
* test(managed-agent): share the FG12e configuration read and note #15119
- Read the cell's mcp_configuration records through
HostedGateTestSupport.extensionRecords, as the four sibling MCP letters
do. The configRevision sort stays, because that helper orders by
record_id.
- Rebase the design doc's budget on the FG11d queue-one cell: fork
11264 s, with 16 s left in the 198-minute Verify step.
- Since #15119, Spring sends the Session MCP management route, which
refuses while a Turn is active. Say so instead of claiming that
HostedHarnessClient has no MCP route.
* fix(cli): restore bounded OpenTUI dialogs and add migration gates
* fix(cli): preserve OpenTUI input and attachment ownership
* feat(cli): align OpenTUI status lines with Ink
* fix(cli): align OpenTUI editor and cancellation transactions
* fix(tui): harden migration cleanup and evidence after review
* test(tui): fail acceptance on unhandled errors
* docs(cli): clarify consolidated OpenTUI delivery status
* test(cli): isolate system settings in TUI parity fixtures
* fix(cli): settle OpenTUI cancellation when replay resets a session
* feat(cli): align OpenTUI Vim, history and session output
Reuse the shared editing and history contracts, preserve cancellation ownership, and connect accepted interactive turns to the existing output bridge.
* style(cli): format merged OpenTUI test callbacks
* test(cli): preserve visible input across session rotation
* test(cli): cover shell recall provenance and streaming search selection
* test(cli): await cancellation before releasing held response
* fix(cli): preserve imported history ownership and active search
* docs(cli): synchronize consolidated OpenTUI validation status
* test(cli): await AppContainer configuration cleanup
* test(cli): mock Vim clipboard imports before module loading
* test(cli): share clipboard mocks across module export views
* test(cli): verify finalized MOD parts and shared JSON projection
* fix(cli): reuse Ink editing history for OpenTUI Unicode input
* test(cli): keep native Vim input callbacks void
* test(cli): preserve Escape boundaries in Unicode Vim fixtures
* test(cli): read the current composer across Vim remounts
* test(cli): align Unicode deletion cursor with Ink oracle
* fix(cli): preserve completion and escape precedence and validate status colors
* fix(cli): use boundary selection for the OpenTUI composer
* fix(cli): preserve attachment navigation with one completion
* test(cli): preserve Shift in the native redo key sequence
* fix(cli): display session titles in the OpenTUI composer border
* fix(cli): discard generated command work after cancellation
* test(cli): align Unicode unit cursor with Ink oracle
* test(cli): model native selection key delegation
* fix(cli): preserve Vim Escape ownership across listeners
* fix(cli): preserve shared Vim command messages in OpenTUI
* test(cli): observe rendered Vim mode during parity checks
* docs(cli): record integrated OpenTUI validation checkpoint
* fix(ci): account for dedicated TUI parity coverage
* fix(cli): preserve collapsed paste payloads across Vim undo
* test(managed-agent): pin the queue_one reachability (FG11d)
The design record claims the queued-input arm is reachable under queue_one; this
cell is the witness. A second manual fire is admitted at one active run, its run
commits, and no prompt for its occurrence ever appears. The cancel half stays
open: measured here, a task cancel on that queued run is accepted and the run
stays running, because the queue is settled by the turn.
* test(managed-agent): align the queue_one assertions and mark the cancel half ungated (FG11d)
The active-run half of the reachability cell excluded one settled state fewer
than the queued half, so a run that had failed would still read as untouched.
Both halves now exclude the same three settled states.
The design record called the un-gated cancel half "(measured)", which reads as
if a witness exists in the tree. It now says "measured locally, not gated", the
same claim the PR body makes.
* test(managed-agent): observe the queue_one dwell and scope the record to what it proves (FG11d)
The negative rested on a single `isEmpty()` sampled one poll after the run row
committed, so an asynchronous dispatch of the queued input would land after the
assertion had already returned green. It now runs as a bounded dwell through
`waiting()`, keeping the failFast and lock-exception guard.
Both records cited the cell as pinning that no prompt appears, which attributes
the absence to the overlap arm. The first turn is parked, so the wake pump
cannot start the queued input under any policy; the records now say the cell
pins admission (its own run id and a `fired` occurrence) and only observes the
absence over a dwell.
* feat(managed-agent): read the Harness credentials from one file
The Hosted Harness and the control plane share a bearer token and a
capability digest, which the reference distribution stored twice, in
harness.env and server.env, so every rotation or hand edit had to change
both files identically.
QWEN_MANAGED_AGENT_HARNESS_CREDENTIALS_FILE now names the Harness's own
environment file. The control plane reads QWEN_SERVER_TOKEN and
QWEN_HOSTED_HARNESS_CAPABILITY_DIGEST from it into its configuration,
never into its process environment, and refuses to start when the file
is combined with a direct token or digest, or lacks either key.
Preflight reports the file's permissions and points credential fixes at
it. The bootstrap, templates, systemd instructions and guide store the
credentials once.
* fix(managed-agent): refuse credential lines the Harness reads differently
The credentials parser accepted a subset of the environment-file grammar
and silently mis-read the rest. A continuation, an escape, an
unterminated quote, text after a closing quote or a `$` expansion gave
the control plane a different value than systemd or a shell gave the
Harness, while preflight passed. Such lines now fail startup, naming
the file and line but never the content.
The file is now read once and only while the Harness is enabled, and an
unreadable file reports its exception type in the validation row.
Preflight shares one secret-file permission policy between the config
and credentials rows. The credentials row also fails an unreadable file
and reports an unusable path instead of aborting, and the harness row
fails the shipped placeholder token.
Tests now pin the run order, values containing `=`, world-readable
files, the file-aware 401 fix and the shipped harness.env.example keys.
The systemd READMEs describe the warning as a warning.
* fix(managed-agent): accept only credential lines both readers take literally
Credential values in the Harness credentials file may now contain only
letters, digits and ._:/+=-, and whitespace around `=` or a carriage
return is refused: a shell runs `KEY = value` as a command and keeps a
trailing \r, so the control plane and the Harness could hold different
digests while every preflight row passed.
When startup refuses the file, the harness row now skips its probe
instead of sending whatever credentials are left bound, and the file is
applied atomically. run() delegates to checks(args) so a test can pin
the credentials row after the harness row.
* fix(ci): scope the sdk-java host lock to the cache write-back (#15117)
Run 39627609266's fast-tier job arrived at 17:38, stayed head of the
sdk-java host-lock queue, and failed at 19:15 without starting Maven.
The MariaDB integration lane held the same lock across its install and
full MySQL clean verify; under pool contention that hold ran from 15:54
to 18:57 (run 39628985638), past the fast tier's 90-minute budget.
Both MariaDB Maven runs already resolve from a job-local repository, so
they touch no host-shared state. Seed that repository before the lock,
run both Maven commands unlocked, and take the host lock only for the
cache write-back, the sole remaining host-shared mutation. Drop fd 9 on
the write-back children so a cancelled job's survivor cannot retain the
lock. Raise the lane ceiling to 300 minutes for the ~3-hour verify plus
one lock wait and setup, and pin the new order and fd closes.
Fixes#15117
* test(ci): exempt the OpenTUI parity suites from the no-AK gate (#15117)
The main merge brought three interactive suites from #14964. The no-AK gate requires every runnable integration test to be selected or explicitly exempted, so the Test job fails until they are listed.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
---------
Co-authored-by: Qwen Autofix <qwen-code-assistant@users.noreply.github.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: jinjing.zzj <jinjing.zzj@alibaba-inc.com>
Co-authored-by: 易良 <1204183885@qq.com>
Close Spring contexts before fixture database cleanup, run integration classes in isolated reusable forks, and remove redundant fixture validation and duplicate load-test execution. Document bounded local validation and preserve full CI gates.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(ci): align Java integration database and lock lifetime
* test(ci): re-pin the sdk-java lock and MySQL probe after the single-step merge
The merge commit staged the conflict resolution before these two re-pins were
written, so they land as a follow-up in the same push:
- `keeps the host lock out of every Maven child` counted three lock-bearing
steps; #14903 collapsed the MariaDB lane's two Maven steps into one, so the
count is now two (test job + mysql-integration).
- The MySQL startup probe took the step's first `mvn` line, which is now the
`-DskipTests install` staging command; select the `-Pmysql-integration`
command instead so the probe still exercises the consumer.
Verified: vitest sdk-java-workflow.test.js 96 passed; prettier --check clean.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-conflict/jmvgz448zlp
* fix(ci): skip the MySQL binlog, drop the unused database, correct the reaper bound
Three independent corrections to the mysql-integration lane this PR reworks.
1. MySQL 8 enables the binary log by default (MariaDB 10.11 did not in the
configuration this lane replaced), and the datadir is an uncapped tmpfs.
Nothing in packages/managed-agent-server/src or packages/sdk-java reads a
binlog (0 hits for binlog|log_bin|GTID|SHOW MASTER STATUS), and the
container is disposable with no replica, so `--skip-log-bin` is appended
after the image name beside --innodb-use-native-aio=0. Without it the
~67 MiB hex-encoded LONGBLOB query this lane already raises
max_allowed_packet for is serialized a second time into host RAM.
2. `--env MYSQL_DATABASE='runtime_broker_test'` had no consumer: the lane's
only connection is -Dmysql.url=.../managed_agent_test?createDatabaseIfNotExist=true.
The declaration invited a cleanup that drops createDatabaseIfNotExist and
dies at first connect with Unknown database. Dropping it lets the JDBC URL
own the schema and shrinks the diff.
3. The cleanup comment and removal-failure warning said the host reaper
reclaims a stranded container "after a 24h age gate" / "up to a day".
The real bound is ~48h: qwen-docker-cleanup.sh:31 gates on a strict
`now - created_at > 86400` (exactly 24h is SKIPPED) and
qwen-docker-cleanup.timer fires once a day, so a container created just
after a fire is skipped at the next one and reaped at the one after.
Test pins updated in the same change: the start-step assertion, the exact
docker-run trace literal, and the cleanup warning literal. All three were
mutant-verified red (drop --skip-log-bin / re-add MYSQL_DATABASE / restore
"after 24h") and restored byte-identical.
Only this PR's own copy of the reaper wording is corrected; the two
pre-existing copies (sdk-java.yml's event-transport-multi-node job and
release-managed-agent-reference.yml) are untouched to keep this diff scoped.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmvhb9cysmc
* feat(ci): restore a MariaDB leg for the non-Hosted IT family
#14936 moved the mysql-integration lane onto MySQL 8.4 and removed the last
real MariaDB server, leaving the published MariaDB-only V12 repair (RTRIM,
error 1901) and the schema catalog's MariaDB branches without a falsifier.
Both engines now run the same non-Hosted *IT family as two matrix legs of one
job: the container image, its startup arguments, its client, and its readiness
signal come from the leg, and the container name carries the engine so two
legs on one pool host cannot collide.
---------
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* feat(managed-agent): Add session lifecycle UI and reading exports
Expose durable lifecycle actions and archive navigation in Managed Web Shell, with identity-scoped browser recovery and immutable HTML/Markdown reading exports.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(managed-agent): Resolve lifecycle review and smoke regressions
Use the host React DOM server renderer, announce global errors once, and advance the OpenAPI version. Keep smoke selectors specific to session navigation and align the Workspace expectation with its existing Managed label.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* test(managed-agent): Preserve schema bindings in channel race fixture
Reuse the production schema-aware JDBC template so repeated H2 queries keep binary identity parameters. Preserve the existing ownership race and its assertions.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(managed-agent): Correct lifecycle retry and export handling
Keep failed unadmitted archive intents idle until explicit retry, scope approval capability to each session profile, and preserve public export semantics and embedded downloads.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
---------
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* test(managed-agent): keep a settled run's outcome (FG11d)
* test(managed-agent): reuse a same-key cancel (FG11d)
* test(managed-agent): refuse a second fire while the turn runs (FG11d)
* revert: move the second-fire cell onto its own branch
The cell belongs to the next PR in the chain, which bases on this branch, so
it cannot also live here: the next PR's diff would be empty. Content of this
branch is unchanged from acdab642b7.
* test(managed-agent): refuse a second fire while the turn runs (FG11d)
* test(managed-agent): keep a sibling run alive past a cancel (FG11d)
* docs(managed-agent): name the settled run's real refusal code
The cell's own comment still said task_already_settled while the assertions
and both design docs record 409 task_action_unavailable, which is the code the
measured behaviour actually answers.
* test(managed-agent): assert the replayed cancel operation's identity
The cell discarded both cancel responses, so "reuses its operation" rested on
the run state alone — which a second operation could also have produced. It now
compares the two operation ids, pins task_id to the cancelled task, and asserts
the replay flag flips false then true.
* docs(managed-agent): separate the queued-input claim from the settled one
The bullet ran the two claims together with no separator, so a reader could not
tell whether "a queued input cancelled before any attempt" was delivered too.
Both language versions now take the same form as the neighbouring bullets, and
the Chinese continuation line gets its indentation back.
* test(managed-agent): pin the occurrence ledger in the isolation cell
Review R1-1 and R1-2: the cell's comment claimed the schedule's future fires
survive, which nothing in it observes, and it was the only cancel cell that
never read ma_schedule_occurrence. The comment now says what the cell proves
(the definition stays live; a scheduled slot is outside the arm) and the cell
pins both keys fired exactly once with no re-drive. The workflow comment's
wrap figure follows the fork to 11781.
* docs(managed-agent): leave the queued-input arm open and fix its carriers
Review round 2. R1-1: the queued-input criterion is reachable, so the bullet
no longer books it delivered — under queue_one (and allow below quota) a second
manual fire is admitted while the first run is active, because hasOpenTurn reads
only public ma_turn rows. The cell pins the default-skip refusal, not that arm.
R1-10: the cell's comment now names the count arm instead of the open-Turn arm.
R2-1/R2-2: the workflow's two budget comments and both derivation ledgers record
this cell's 25 s (fork 11212, healthy 10612).
* test(managed-agent): catch a re-drive by attempts, not by row count
Review R1-2 on this PR: the count assertion cannot witness a re-drive, because
the occurrence primary key makes a re-drive an UPDATE that bumps attempts rather
than an INSERT. Both keys now pin attempts == 0, and the count assertion is
labelled for what it does catch - a spurious third key. Re-ran the cell on
JDK 21 + MySQL 8.0.46: 6.204 s, green.
---------
Co-authored-by: 易良 <1204183885@qq.com>
* feat(web-shell): allow hosts to inject disabled catalog models
Hosts can show product models that are not currently usable in the composer picker, with a framework-neutral hover slot for text or authorize actions.
Co-authored-by: Cursor <cursoragent@cursor.com>
* docs(web-shell): add unavailable-model picker screenshots
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(web-shell): polish unavailable-model tooltip card
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(web-shell): make unavailable models look disabled
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(ci): isolate script fixtures and reuse runtime archives
* test(cli): bound command budget fixture work and drain timed-out writes
---------
Co-authored-by: Ethan Ye <ethanye@U-463KXYWL-1938.local>
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(ci): bound the MariaDB lane's cache write-back and size its ceiling (#15000)
Run 39563394993's mysql-integration job was cancelled at the 200-minute ceiling: 54 minutes queued for the host lock, 75 minutes of Maven work, then a 60-minute unbounded tar of the host-shared ~/.m2. The cache is an optimization, so skip it after five minutes; the ceiling now covers the 90-minute wait, the measured Maven pass, the bounded write-back and setup headroom.
* fix(ci): write back only the MariaDB lane's Maven delta by hardlink
The bounded tar pipeline extracted straight into the host-shared ~/.m2,
so a timeout mid-member left a truncated artifact that --skip-old-files
never repaired and the next seed hardlinked into its job. Its 300 s cap
was also below the measured 26-61 minute whole-tree cost, so it would
have tripped on every run.
Publish only the files Maven fetched in this job (nlink == 1 after the
hardlink seed) with per-file cp -ln, keeping the two in-house excludes.
Each link is atomic and never replaces a host file, so the 300 s
backstop can only leave artifacts missing. Distinguish the timeout from
other failures, and fold the backstop into the derived ceiling floor
instead of a second hardcoded one.
* ci: record sdk-java.yml's real size in the workflow ratchet
This PR grows .github/workflows/sdk-java.yml from 113473 to 114180 bytes:
the MariaDB lane's timeout ceiling moves 200 -> 280 with the arithmetic
that justifies it, and the Maven write-back is rewritten to publish only
this job's own downloads (nlink == 1) under a 300 s backstop instead of
taring the whole tree. The growth is the change, so record it rather than
move the step out.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-conflict/jmvj37bcsp8
---------
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(ci): serve the fast Java tier before full-tier lock holds (#15032)
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(ci): keep a parked lock head's ticket alive under fast priority
A head blocked in `flock --wait` could not touch its ticket, so once a
hold outlasted the 1800s mtime arm another waiter reaped a live head and
the next ticket, whatever its class, parked in the kernel too. With two
classes that lets normal tickets take kernel positions ahead of fast
tickets that arrive later during a long hold. A keepalive now refreshes
the parked head's ticket at the waiters' two-second cadence; it closes
the lock fd and uses `touch -c`, so it can neither hold the lock nor
recreate a removed ticket.
The ordering and promotion cases used a stub whose `--wait` returned
every 200 ms, so they exercised a head that loops, which production never
does. They now park `--wait` until release and assert the reachable
paths: a fast ticket overtaking a normal ticket that is still waiting,
and a promoted waiting normal ticket preceding a later fast ticket.
The comments and design docs no longer claim a fast job waits at most
the hold already running (a parked head still goes first) or cite
#14962/#15032 as fast-behind-full evidence; they state what priority
removes and that a single hold longer than the budget is outside it.
---------
Co-authored-by: Qwen Autofix <qwen-code-assistant@users.noreply.github.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: wenshao <shaojin.wensj@alibaba-inc.com>
* test(managed-agent): keep a settled run's outcome (FG11d)
* test(managed-agent): reuse a same-key cancel (FG11d)
* test(managed-agent): refuse a second fire while the turn runs (FG11d)
* revert: move the second-fire cell onto its own branch
The cell belongs to the next PR in the chain, which bases on this branch, so
it cannot also live here: the next PR's diff would be empty. Content of this
branch is unchanged from acdab642b7.
* test(managed-agent): refuse a second fire while the turn runs (FG11d)
* docs(managed-agent): name the settled run's real refusal code
The cell's own comment still said task_already_settled while the assertions
and both design docs record 409 task_action_unavailable, which is the code the
measured behaviour actually answers.
* test(managed-agent): assert the replayed cancel operation's identity
The cell discarded both cancel responses, so "reuses its operation" rested on
the run state alone — which a second operation could also have produced. It now
compares the two operation ids, pins task_id to the cancelled task, and asserts
the replay flag flips false then true.
* docs(managed-agent): separate the queued-input claim from the settled one
The bullet ran the two claims together with no separator, so a reader could not
tell whether "a queued input cancelled before any attempt" was delivered too.
Both language versions now take the same form as the neighbouring bullets, and
the Chinese continuation line gets its indentation back.
* docs(managed-agent): leave the queued-input arm open and fix its carriers
Review round 2. R1-1: the queued-input criterion is reachable, so the bullet
no longer books it delivered — under queue_one (and allow below quota) a second
manual fire is admitted while the first run is active, because hasOpenTurn reads
only public ma_turn rows. The cell pins the default-skip refusal, not that arm.
R1-10: the cell's comment now names the count arm instead of the open-Turn arm.
R2-1/R2-2: the workflow's two budget comments and both derivation ledgers record
this cell's 25 s (fork 11212, healthy 10612).
---------
Co-authored-by: 易良 <1204183885@qq.com>
* test(managed-agent): keep a settled run's outcome (FG11d)
* test(managed-agent): reuse a same-key cancel (FG11d)
* test(managed-agent): refuse a second fire while the turn runs (FG11d)
* revert: move the second-fire cell onto its own branch
The cell belongs to the next PR in the chain, which bases on this branch, so
it cannot also live here: the next PR's diff would be empty. Content of this
branch is unchanged from acdab642b7.
* docs(managed-agent): name the settled run's real refusal code
The cell's own comment still said task_already_settled while the assertions
and both design docs record 409 task_action_unavailable, which is the code the
measured behaviour actually answers.
* test(managed-agent): assert the replayed cancel operation's identity
The cell discarded both cancel responses, so "reuses its operation" rested on
the run state alone — which a second operation could also have produced. It now
compares the two operation ids, pins task_id to the cancelled task, and asserts
the replay flag flips false then true.
* docs(managed-agent): separate the queued-input claim from the settled one
The bullet ran the two claims together with no separator, so a reader could not
tell whether "a queued input cancelled before any attempt" was delivered too.
Both language versions now take the same form as the neighbouring bullets, and
the Chinese continuation line gets its indentation back.
* fix(release): cover every spawned vendored binary in the tarball exec-bit gate
The gate matched only vendor/ripgrep/*/rg, but the tarball spawns a second
vendored executable: vendor/landlock-run/<arch>-linux/qwen-landlock-run. Its
own runtime heal shares the ripgrep heal's hard limit — it can only chmod an
install the running user owns, so a root-owned sudo install degrades the
same silent way. Widen the expected set with a small spec list keyed on the
leaf binary's presence (the landlock-run/src source directory falls out
structurally), parse every regular-file tar entry instead of a single
ripgrep regex, and extend the fixture tests to cover both trees with
pass/reject cases.
Also correct the stale comment above the landlock heal: it claimed npm
archives strip non-bin exec bits, which is the inverse of what #12668
established — pnpm's packer wrote 0644 for every non-bin entry from 0.24.4
to 0.25.3; npm keeps working-tree modes.
Refs #12668
* fix(release): keep missing-binary strictness in the widened exec-bit gate
Deriving the expected set from the leaf binary's presence meant a platform
directory whose binary was dropped during staging silently shrank the
expected set: a dist without vendor/ripgrep/x64-linux/rg but with the
directory would pass with five entries. Platform membership now comes from
the directory name alone — ripgrep skips '-win32', landlock-run skips the
'src' source directory — so a missing binary fails as 'missing from the
packed tarball' again, matching the pre-widening strictness. Also update the
function doc block, which still claimed the gate covered ripgrep only.
Regression test: an empty ripgrep platform directory fails with the
missing-entry diagnostic even when every other binary passes.
* fix(ci): align Java integration database and lock lifetime
* test(ci): re-pin the sdk-java lock and MySQL probe after the single-step merge
The merge commit staged the conflict resolution before these two re-pins were
written, so they land as a follow-up in the same push:
- `keeps the host lock out of every Maven child` counted three lock-bearing
steps; #14903 collapsed the MariaDB lane's two Maven steps into one, so the
count is now two (test job + mysql-integration).
- The MySQL startup probe took the step's first `mvn` line, which is now the
`-DskipTests install` staging command; select the `-Pmysql-integration`
command instead so the probe still exercises the consumer.
Verified: vitest sdk-java-workflow.test.js 96 passed; prettier --check clean.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-conflict/jmvgz448zlp
* fix(ci): skip the MySQL binlog, drop the unused database, correct the reaper bound
Three independent corrections to the mysql-integration lane this PR reworks.
1. MySQL 8 enables the binary log by default (MariaDB 10.11 did not in the
configuration this lane replaced), and the datadir is an uncapped tmpfs.
Nothing in packages/managed-agent-server/src or packages/sdk-java reads a
binlog (0 hits for binlog|log_bin|GTID|SHOW MASTER STATUS), and the
container is disposable with no replica, so `--skip-log-bin` is appended
after the image name beside --innodb-use-native-aio=0. Without it the
~67 MiB hex-encoded LONGBLOB query this lane already raises
max_allowed_packet for is serialized a second time into host RAM.
2. `--env MYSQL_DATABASE='runtime_broker_test'` had no consumer: the lane's
only connection is -Dmysql.url=.../managed_agent_test?createDatabaseIfNotExist=true.
The declaration invited a cleanup that drops createDatabaseIfNotExist and
dies at first connect with Unknown database. Dropping it lets the JDBC URL
own the schema and shrinks the diff.
3. The cleanup comment and removal-failure warning said the host reaper
reclaims a stranded container "after a 24h age gate" / "up to a day".
The real bound is ~48h: qwen-docker-cleanup.sh:31 gates on a strict
`now - created_at > 86400` (exactly 24h is SKIPPED) and
qwen-docker-cleanup.timer fires once a day, so a container created just
after a fire is skipped at the next one and reaped at the one after.
Test pins updated in the same change: the start-step assertion, the exact
docker-run trace literal, and the cleanup warning literal. All three were
mutant-verified red (drop --skip-log-bin / re-add MYSQL_DATABASE / restore
"after 24h") and restored byte-identical.
Only this PR's own copy of the reaper wording is corrected; the two
pre-existing copies (sdk-java.yml's event-transport-multi-node job and
release-managed-agent-reference.yml) are untouched to keep this diff scoped.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmvhb9cysmc
---------
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* test(managed-agent): resume the automation streams across a settle (FG11e)
Gate the FG11e row of #14446 with two cells in the FG11 automation class.
One cuts a run's task event feed and a public Session stream while the
wake Turn is held between its fire and its settle, and resumes both from
their last positions; the other pages the run list across an insert. The
Session arm asserts today's precondition that a run adds no event to the
Session stream, so it gates the resume across that window rather than
automation content.
The cells book 39 s on the shared Hosted fork (healthy 10272 s, fork
10872 s, so the Verify step moves to 192 minutes and the job ceiling to
295), and the FG11e design record moves to partly delivered in both
languages.
* test(managed-agent): decode the FG11e task cursor independently
The stream cell built each expected task cursor with the store's own
encoder, so the cursor format was never witnessed apart from the code
that writes it. The served cursor is now decoded in the test and must
name its row's sequence. The Session comparison trims the ledger with a
limit instead of subList, so a stream that served more frames than the
journal holds fails as an assertion rather than an IndexOutOfBounds.
* test(managed-agent): count each run's settle in the FG11e list cell
The run-list cell counted model Turns for the automation but never read
the settle commands, so a run settled twice without a second model call
would pass it, while both design records say each cell checks one settle
per occurrence. It now asserts the journal holds exactly one settleRun
per run, in the order the runs settled.
* test(managed-agent): fail an FG11e task cursor outside its format
The independent cursor check fell back to the decoded text when it did
not match the cursor format, so a cursor that decodes to a bare number
became a valid-looking sequence and matched the ledger. A cursor outside
the format now fails the check outright.
* test(managed-agent): rebook the FG11e cells at their slower samples
Two later runs measured the stream cell at 14.1 s and the run-list cell
at 5.6 s, above the 13.8 s and 5.2 s the ledger was booked against. At 2x
the slower samples the cells take 41 s: healthy 10424 s, fork 11024 s,
which the 194-minute Verify step still wraps with 16 s of margin.
* feat(managed-agent): derive Session Store wiring on a single host
An enabled Hosted Harness requires the Managed Session Store, yet the
store's switch, base URL and workspace ID all had to be configured by
hand, and the shipped templates set a workspace ID that the documented
local-process tool path then rejects.
The store now follows the Harness switch, takes its base URL from this
server's loopback or wildcard listener, and shares the Runtime Broker's
workspace identity, derived from the Workspace directory when no ID is
set. Explicit values keep precedence. Preflight reports the effective
store wiring, and the templates, bootstrap and deployment guide name the
Workspace directory instead of a conflicting ID.
* fix(managed-agent): derive no plaintext Store URL for a TLS listener
When the public listener serves TLS through server.ssl and no
dedicated internal listener exists, the derived http:// loopback URL
reached the TLS connector, so every Store call failed while preflight
reported PASS. Such a listener now derives nothing and the existing
refusal asks for an explicit URL. The internal listener is always
plaintext, so it still derives behind a TLS public listener.
* test(managed-agent): pin the dual-path entry to the derived Store wiring
The dual-path README contract still required the explicit store switch
that the entry no longer exports, and its ordering check would pass
vacuously once the name was gone. It now checks that the store follows
the Harness by default, that Prerequisites exports the workspace ID,
and that the Harness base-url export precedes the restart cue, with
both positions required.
* test(managed-agent): lose the store ack after the result commit (FG13a)
StoreFaultGate's existing seam rejects a commit before it applies, so the
whole commit rolls back. This layer adds the opposite failure: the commit
lands and the caller never sees its acknowledgement. The faulting store
calls afterApply only after super.apply succeeds; the new cell proves one
physical Hook call, a durable settled result, and a status read that
returns it without a second call.
Adds the bilingual design note. One cell adds 150 s to the shared fork:
healthy 10189 s, fork 10789 s, Verify 190 minutes, job 293 minutes.
* test(managed-agent): keep the durable dispatch after a lost ack (FG13a)
* test(managed-agent): reuse the pinned registration after a lost ack (FG13a)
* test(managed-agent): report the lost ack after the commit lands (FG13a)
* test(managed-agent): let the dispatch ack-loss cell call its Hook (FG13a)
* docs(managed-agent): describe the ack seam without overclaiming (FG13a)
* test(managed-agent): bound the dispatch recovery instead of guessing (FG13a)
* docs(managed-agent): carry the audit fixes into the registration slice (FG13a)
* docs(managed-agent): align the Chinese dispatch slice with its cell (FG13a)
* docs(managed-agent): drop the model-request claim from the Chinese slice (FG13a)
* docs(managed-agent): align the Chinese registration slice budget (FG13a)
* test(managed-agent): witness the replay instead of a transient phase (FG13a)
* test(managed-agent): witness the replay on the registration ack loss (FG13a)
* test(managed-agent): lose the store ack after the result commit (FG13a)
StoreFaultGate's existing seam rejects a commit before it applies, so the
whole commit rolls back. This layer adds the opposite failure: the commit
lands and the caller never sees its acknowledgement. The faulting store
calls afterApply only after super.apply succeeds; the new cell proves one
physical Hook call, a durable settled result, and a status read that
returns it without a second call.
Adds the bilingual design note. One cell adds 150 s to the shared fork:
healthy 10189 s, fork 10789 s, Verify 190 minutes, job 293 minutes.
* test(managed-agent): keep the durable dispatch after a lost ack (FG13a)
* test(managed-agent): report the lost ack after the commit lands (FG13a)
* test(managed-agent): let the dispatch ack-loss cell call its Hook (FG13a)
* docs(managed-agent): describe the ack seam without overclaiming (FG13a)
* test(managed-agent): bound the dispatch recovery instead of guessing (FG13a)
* docs(managed-agent): align the Chinese dispatch slice with its cell (FG13a)
* docs(managed-agent): drop the model-request claim from the Chinese slice (FG13a)
* test(managed-agent): witness the replay instead of a transient phase (FG13a)
* test(managed-agent): lose the store ack after the result commit (FG13a)
StoreFaultGate's existing seam rejects a commit before it applies, so the
whole commit rolls back. This layer adds the opposite failure: the commit
lands and the caller never sees its acknowledgement. The faulting store
calls afterApply only after super.apply succeeds; the new cell proves one
physical Hook call, a durable settled result, and a status read that
returns it without a second call.
Adds the bilingual design note. One cell adds 150 s to the shared fork:
healthy 10189 s, fork 10789 s, Verify 190 minutes, job 293 minutes.
* test(managed-agent): report the lost ack after the commit lands (FG13a)
* docs(managed-agent): describe the ack seam without overclaiming (FG13a)
* docs(managed-agent): drop the model-request claim from the Chinese slice (FG13a)
* test(managed-agent): keep a settled run's outcome (FG11d)
* docs(managed-agent): name the settled run's real refusal code
The cell's own comment still said task_already_settled while the assertions
and both design docs record 409 task_action_unavailable, which is the code the
measured behaviour actually answers.
* feat(core): separate app launcher and JS interpreter runtime boundary
Establishes the runtime boundary the Bun migration (MYWO-8 S1) needs
before workers, hooks, sandbox helpers and the code-mode host spawn
JavaScript children under a future compiled-app flavor: arbitrary-JS
child processes resolve their interpreter through one accessor (package
manifest first, process.execPath fallback) instead of reading
process.execPath directly, Bun detection has a single seam, and the
app-launcher contract is documented alongside the full call-site
inventory in a bilingual design doc. Adds the same-fixture Node/Bun
regression matrix (stdio framing, exit codes, SIGTERM, IPC channel,
--expose-gc, --input-type=commonjs --eval).
* fix(core): anchor interpreter resolution to the trusted install root
Arbitrary-JS child resolution no longer walks upward looking for a
manifest: the only trusted location is the standalone package root —
the parent of the caller's bundle directory, which must hold both a
manifest.json and the standalone entry marker lib/cli-entry.js — so an
unrelated ancestor manifest in npm, source or SDK layouts can never be
read. Declarations are validated before use: runtime must be exactly
'bun' or 'node', and the interpreter must be an existing executable
regular file; any invalid or missing declaration falls back to
process.execPath without throwing (non-string interpreter, directory,
non-executable file, unknown runtime, absent binary, missing marker).
Adds the regression matrix for unrelated ancestor manifests (npm
bundle and deep SDK layouts), object/number/directory interpreters,
unknown runtime, missing and non-executable interpreters, and the
trust anchor; syncs both design-doc language versions with the
validated contract.
* docs(design): complete the migration inventory handoff
Adds the three interpreter-identity call sites the re-review found
missing from the call-site inventory (hook-command-cgroup launcher,
bwrap-execution and landlock-execution allowlist staging), marks the
unwired rows as owned by the S3 migration sweep, and notes platform
applicability: the cgroup/bwrap/landlock rows are Linux-only
mechanisms validated against the Linux targets of the platform
matrix, while the mem0 and pem rows apply on every platform. Both
design-doc language versions are updated; no product or test code
changes.
* feat(core): add a real PTY backend for the Bun runtime
Under Bun, @lydell/node-pty's wrapper pumps the PTY master fd through a
tty.ReadStream, and Bun surfaces the nonblocking fd's EAGAIN as an error
event instead of "no data yet", destroying the stream and closing the
master: onData never fires, resize fails with EBADF, and the shell tool
and web terminal hang. Reproduced on Bun 1.3.14 and 1.4.0.
getPty() now loads a qwen-bun-pty backend under Bun (non-Windows) that
drives the same native addon directly with a main-thread async fs.read
pump: EAGAIN retries on a short timer instead of erroring, EIO/EBADF end
the pump and close the fd, writes queue through an async fs.write with
EAGAIN retries, and exit waits briefly for the drained fd so data is
never truncated behind onExit. The wrapper's observable contract (env
sanitizing, flow-control bytes, encoding, resize validation, IPty
lifecycle) is mirrored, and the pump starts in the constructor so an
unpumped fd can never hold the event loop open.
Packaging stages the node-pty addon for both runtime flavors, since Bun
installs now load it through this backend. The Windows-under-Bun gate
stays honest (explicit load error), and the runtime-boundary design docs
point at the new backend doc for the mechanism and evidence.
Tests: unit coverage for the backend lifecycle (output, write, resize,
kill, pause/resume, flow control, raw encoding), getPty selection under
Bun/Windows, and a cross-runtime regression that runs the same fixture
through the real getPty() seam under both Node and Bun when a bun binary
is present.
* fix(core): keep PTY exit detection alive while paused
The pinned node-pty addon fires its native exit callback only around
master-fd read activity (isolated-probe evidence, identical on Node and
Bun), so the old pause() that stopped the pump lost onExit entirely:
pause then child exit left the master fd open and the host unable to
drain — the same mechanism behind the XOFF-then-cancel FD residue under
handleFlowControl.
pause() now buffers output into a bounded hold (1 MiB) that resume()
flushes, while reads continue so exit detection rides on the pump.
On end of life the hold is discarded for wrapper parity: onExit arrives
exactly once and the fd is released, but resume-after-exit replays
nothing, matching the wrapper's destroyed paused socket. Docs are
synced to the actual main-thread async fs.read pump (the earlier
worker-threads/blocking-read sketch is documented as abandoned), and
regressions cover pause-at-exit, hold overflow, and XOFF-cancel on both
runtimes.
* fix(core): make the Bun PTY pause hold lossless and exits leak-free
Serializes master-fd reads behind an in-flight guard (a resume chaining
into a read that is still outstanding would cross-observe the shared
64 KiB buffer), holds paused output without a cap so nothing is ever
silently dropped, and routes every end-of-life path through one cleanup
that flushes the decoder, stops the retry timer, closes the master fd
and fires onExit exactly once — including the 500 ms fallback, which is
now ref'd so exit delivery cannot depend on a droppable timer. Pins all
of it with byte/line-exact regressions: no-gap pause/resume storms and
a ~1.9 MB flood resumed byte-for-byte, cross-runtime.
* fix(core): cap the Bun PTY pause hold and terminate on overflow
The lossless pause hold could grow without bound while a consumer
stays paused. The hold now copies chunks into fixed 64 KiB blocks
under a per-pty byte ledger (4 MiB default) inside a process-wide
budget (32 MiB total hold, 8 active ptys, 1 MiB write queue), so the
bound holds for any producer chunk pattern.
Reaching a cap terminates that pty (SIGTERM then SIGKILL) and emits
one structured PtyLimitError — never a success result and never a
fabricated native exit; the real signal death lands only in
stats.nativeExitRecord. Shell executions converge the error into
{error, exitCode: null, signal: null}; web terminals mark the session
failed and the /terminal route sends an error control frame followed
by close 4001. Node behavior is unchanged.
Design docs corrected per review: the drip "kernel corruption" claim
is withdrawn, and exit detection wording no longer overstates when
exits are observed.
* fix(core): harden post-termination pty edges per review
Dual-review follow-ups on the bounded hold: resize() joins write() in
treating a terminated/closed pty as a no-op instead of throwing EBADF
from the addon; a read that was in flight when the limit termination
closed the fd no longer delivers or re-charges its late bytes; and a
resume() replay stops at the first data listener that terminates the
pty, so no held bytes flow after the structured error.
* fix(core): detach pty error subscriptions by observed shape, not assumed Disposable
node-pty's Terminal.prototype.on proxies the listener to its master socket
and returns undefined, while an EventEmitter-based pty (the Bun backend)
returns the emitter itself. The registry stored that return value as a
Disposable and called dispose() on release, so every Bun release, natural
exit, or backend failure path threw TypeError from the route/registry and
could take the whole serve process down (server exit 1, client saw 1006
instead of the explicit close).
Store the subscription return as unknown and detach by observed shape: a
real { dispose } is used as-is, otherwise off/removeListener removes the
listener, and undefined (node-pty's actual return) keeps the previous
no-dispose behavior unchanged.
Covered by two registry unit tests on the mocked shapes plus a real-serve
lifecycle probe (release 4004 / exit 4000 / backend over-limit 4001 with
failed-session reconnect replay, server alive throughout) run under Bun
1.3.14, Bun 1.4.0, and Node.
* fix(core): close Bun PTY fds only after in-flight ops settle
Review round on #14046:
- bun-pty closeFd() ran closeSync() while libuv fs.read/fs.write ops could
still own the master fd, so the recycled descriptor number could take a
queued write into an unrelated session. fdClosed now flips and the
process-wide admission slot is released immediately, but the real close
is deferred until the last outstanding op settles, with witnesses for
both the read-held and write-held arms.
- A write-side EIO/EBADF (child already gone) is the expected teardown
race and is no longer reported, matching the wrapper's swallowed EIO;
other write errors keep their errno code so the entry classifier and
getPtyLimitMessage can recognize them instead of escaping as an
uncaught exception. The pump's emit dispatch runs under try/finally so
a throwing error listener can never strand the pump.
- The web-terminal registry's pty error subscription no longer re-throws:
a throw there leaves the backend's fs callback as an uncaught exception
and kills the serve daemon. EIO/EAGAIN races are swallowed and other
non-limit errors go to the diagnostic sink.
- Failed sessions no longer count against the web-terminal admission cap,
and release() no longer fabricates a 143/15 exit frame for them.
- processPtyBudget.maxActivePtys is 64 (headroom over the eight-slot
web-terminal cap so a full house cannot starve the shell tool), with
the per-pty vs process-wide cap relationship documented.
- ShellExecutionResult.executionMethod declares 'qwen-bun-pty', its
error-field contract documents resource-limit termination, and the two
casts that hid the Bun backend name are dropped.
- getPty tests that assert the Bun arm loads now pin a POSIX platform so
they pass on Windows hosts; docs and the packaging help text are
aligned (correct smoke-runtime.js path, boundary-doc PTY row now cites
the bun-pty fixture, node-pty staging applies to both runtimes).
* chore: re-verify S1 boundary test-file merge resolution with recorded evidence
Empty commit: no code change is warranted by the deterministic gate's
test-weakening rejection. The merge ddc7666b9f resolved
packages/core/src/utils/runtime-info.test.ts and
packages/core/src/utils/runtime-boundary.node-bun.test.ts to main's
reviewed versions from #14036 (2eab3cc431), replacing the older
stacked draft copies this branch carried while the prerequisite was
unmerged. Both files are byte-identical to origin/main (empty merge-
base diff: this PR no longer modifies them), and main's versions are
strictly richer (19/16 vs 16/15 expect() calls), so no assertion
coverage is lost. The per-file evidence is recorded in the round's
test-weakening.json; restoring the drafts would revert the reviewed
suites and re-introduce the stacked duplicates the merge correctly
eliminated.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(core): preserve PTY failure output and bounded settlement
* fix(core): deliver reaped PTY exits on limit trips, replay failed scrollback
Review round on the Bun PTY backend:
- terminateResourceFailure no longer signals a pid it already reaped
(pendingExit parked): the real exit is delivered and the structured
limit error reported, instead of SIGTERM/SIGKILL at a recycled pid.
- The failed-reconnect branch of the terminal WS route replays the
retained scrollback through the backpressure guard before the error
frame, matching the exited path.
- QWEN_PTY_ACTIVE_LIMIT no longer claims a termination that never
happened, and acceptedBytes stays a byte count (0 at refusal).
- sanitizeEnv also applies when options.env is omitted, matching the
wrapper's resolved-env rule.
- The arithmetically unreachable paused-hold-segments cap is deleted.
- Tests: the live-failure route test now reaches a live session; the
registry limit test is parametrized over both limit codes; the
paused-exit fixture gains a withheld-tail oracle so it cannot pass
without a real pause; the bounded-hold tests get afterEach failure
isolation; the process-wide write-queue budget is exercised; and the
stale throw-propagation assertion is reconciled with the deliberate
reportListenerError containment (un-breaks the core test lane).
* docs(core): clarify pending PTY close validation
* docs(core): fix PTY validation checklist indentation
* fix(pty): make resource failures the terminal event
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* test(cli): raise ps maxBuffer for loaded CI shards
The managed-runtime process probe lists the whole process table with execFileSync. On a loaded CI shard that output exceeds the 1 MiB default and the probe fails with ENOBUFS; raise the cap to 16 MiB.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* docs(core): name the PTY fd-lifetime tests
Point the deferred-close validation entry at the two exact fd-lifetime tests in both design languages.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(cli): release failed terminal replay sessions safely
* test(web-shell): verify failed terminal replay protocol
* test(cli): pin failure listener cleanup on terminal exit
* fix(core): preserve failure precedence across terminal exits
* fix(core): isolate terminal failure notifications and verify release
* fix(core): preserve PTY write and admission failure diagnostics
* test(core): assert PTY cleanup drains every budget
* test(core): verify production PTY refunds start with charged budgets
* fix(core): stop late PTY signals and integrate cleanup refund tests
* test(core): witness PTY subscriber delivery and document native loading
* test(core): reclaim captured PTYs when fd race assertions fail
* test(core): verify exit diagnostics with a real PTY child
* fix(core): avoid signalling reaped PTY leaders during fallback
* test(core): pin paused PTY boundaries and isolate loader probes
* fix(core): verify PTY ownership and target-specific packaging
* fix(core): refuse oversized terminal input before the pty cap
* fix(core): park paused PTY reads at the hold cap instead of killing the tree
Under the Bun backend the shell tool passes handleFlowControl: true, so a
single Ctrl+S (XOFF) byte calls pause(). The pump kept reading into the
paused hold and, once the 4 MiB per-pty (or 32 MiB process-wide) hold was
full, terminated the child process tree with QWEN_PTY_OUTPUT_LIMIT. During
a flood that killed the command about a second after one keystroke.
Size each paused read to the room left in the hold and, once there is
none, stop issuing reads until resume(). The kernel pty buffer then fills
and the child blocks on write, which is the backpressure the node-pty
wrapper's paused socket applies. Nothing is dropped or killed, and
resume() replays the hold and restarts the reads; pty.stats.readParked
exposes the state. QWEN_PTY_OUTPUT_LIMIT and the paused-hold limit kinds
are removed together with the consumer branches that handled them. The
write-queue and admission limits are unchanged.
While parked, a child that dies with output undrained may only finish
exiting on macOS once the master is read again, so the shell tool's
cancel now calls resume() before it signals the process group.
The design docs also get the live-pty cap right (64, not 8) and no longer
describe a block-count cap that the hold never had.
* test(core): run the PTY drain-window witness on Linux only
The real-child test for a write-limit trip after the native exit relies
on a HUP-ignoring grandchild keeping the pty slave open after the session
leader exits. macOS ends the master side right away (a plain-pty probe on
Darwin reads EOF about 70 ms after the leader exits), so the pump closes
the fd before the write and the case failed 3/3 there. Skip it off Linux;
bun-pty-failure-order.test.ts still pins the same guard on every platform
with a mocked native exit.
* fix(core): reap a PTY's whole session on a resource-limit termination
terminateResourceFailure signalled only the leader and the leader's
process group. An interactive shell runs each foreground job in its own
group, so a job that ignores SIGTERM and SIGHUP outlived the termination:
the failure was reported and the budgets refunded while the job kept
running, reparented to init. Reproduced on Linux and macOS.
List what the pty owns with ps while the leader is still unreaped: its
descendants, plus every process in its session (Linux sid) or on its
controlling tty (macOS tdev). Signal each of them as well as the group.
The SIGKILL pass after the grace period lists the tree again. It keeps
an earlier member only while that member's start time still matches, so
a recycled pid is never signalled. It reuses the scope only where the
scope still names the session: macOS drops the tty with the leader, and
Linux keeps the sid unless the pid now leads a new process.
The web terminal registry's killPtyTree already walked the same ps
listing; both now share collectPtyProcessTree.
* test(core): tolerate the pty's CR jitter in paused-flood oracles
macOS's pty driver occasionally inserts an extra CR before LF; a plain script(1) probe reproduces it without this project's code. The paused-flood tests compared the stream byte-for-byte and waited for pausedBytes to equal the logical byte count, so a jittered stream could never satisfy the predicate.
Wait for pausedBytes >= the logical size, derive the process-wide case's expected hold from the observed hold, and compare the resumed stream with runs of CR before LF collapsed. Every line's content, order and count is still asserted, so loss and duplication remain caught.
---------
Co-authored-by: gary.gq <gary.gq@alibaba-inc.com>
Co-authored-by: qwen-code-autofix <qwen-code-autofix@localhost>
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>
Co-authored-by: Qwen Autofix <qwen-autofix@users.noreply.github.com>
Co-authored-by: wenshao <shaojin.wensj@alibaba-inc.com>
Cover #14880 item 3. During a lifecycle v2 close the Broker proxy refuses
the owner release after every configuration is drained, then the test
replaces Spring and its embedded Broker. Detach must recover with exactly
one forwarded owner release (no acquire, no MCP control), clear the
storage holder, and leave the Runtime Session RELEASED.
The recorded blocker was the rig, not WorkspaceStorageGuard:
startSpring() registered the storage and inserted the Workspace rows on
every boot, and the guard refuses a second registration while the holder
is booked. Production never re-registers on start, so seed only on the
first boot and restart in the JVM; the Broker's close writes nothing and
the durable provisioner keeps the worker alive, which is the state R1-1
needs.
R1-1 does not reproduce on main: the replaced Broker reaches the
persisted-Session release path, and #13673's managed-context branch
releases the saved Session while the Harness drains. Disabling that
branch turns the case red 3 of 3 times with the original symptom (every
retry answers 503 and the holder stays booked).
Budget the cell at 24 s (2x its measured 11.9 s): healthy 10039 -> 10063,
fork 10639 -> 10663, Verify stays 188 minutes.
* fix(cli): preserve runtime boundaries during standalone updates
Preserve Node/Bun archive flavor and validate actual CLI loading, package
identity, version and prepared installation identity before activation.
Keep Node npm transport and restrict Bun registry lookup to trusted config.
Integrates chiga0's update stack through ce67edaba5,
with isolated fixtures and real Node/Bun runtime regression coverage.
Co-authored-by: gary.gq <gary.gq@alibaba-inc.com>
* test(cli): bound cancellation test waits and cleanup
* fix(cli): validate standalone launchers and registry settings
---------
Co-authored-by: gary.gq <gary.gq@alibaba-inc.com>
A MessageDisplay command Hook suppresses the streamed reply; the public
ledger must not carry its text, and after a Harness restart the next model
request's history must not carry it either. The fixture gains a
hook-suppress mode that records the Hook input and answers
{suppressOutput:true}.
Adds the bilingual design note. One cell adds 150 s to the shared fork:
healthy 10012 s, fork 10612 s, Verify 187 minutes, job 290 minutes.
* fix(ci): queue the sdk-java host lock first-come-first-serve (#14743)
flock(2) does not order its wakeups, so under continuous arrivals one waiter can lose every release and die at the wait budget — repeatedly burning 40, then 60, then 90 minutes per starved lane across at least three hosts. Replace the inline flock --wait at all three acquisition sites with a ticketed queue: every acquirer takes the next sequence number atomically, only the head of line retries the lock, dead owners are reaped, and the lock still releases at the step boundary. The pinned infrastructure-signature error text is preserved.
The .size-baseline records the workflow's new size (smaller than main's, but main itself already exceeds its recorded baseline).
* fix(ci): block the queue head instead of polling the lock
The first live run starved its full 5400 s budget as queue ticket 1: the head polled flock --nonblock while main and older branches still run blocking flock --wait, so every release went to a legacy waiter's kernel wakeup. The head now blocks in five-second slices, re-entering the same wake race on even terms; tickets still order every run that uses this helper.
* fix(ci): serve the fast tier before full-tier holds
The queue bounded the wait by position but a fast-tier PR job still queued behind every full-tier hold ahead of it. Give the fast tier its own class: the head is the first sorted ticket and fast tickets sort first, so a PR job waits at most the hold already running. A normal ticket ages into the fast class after 20 minutes, so continuous PR traffic cannot starve the full tier.
* refactor(ci): keep the lock queue plain FIFO.
Drops the priority/aging classes: a single first-come-first-serve line is simpler to review and reason about. The head still blocks in five-second slices so it re-enters the kernel wake race on even terms with legacy waiters, and dead owners are still reaped. The tradeoff is explicit: a fast-tier job waits its turn behind full-tier holds instead of jumping them.
* fix(ci): stop leaking the host-lock ticket and reap stale orphans
Three bounded defects in the fair-lock helper, all confirmed by executing
it rather than by reading alone:
- The EXIT/TERM/INT traps quoted `${ticket}`, a `local` that is already out
of scope once the sourcing step's shell exits, so every trap ran
`rm -f ''` and each successful acquisition left its head ticket behind.
Drop the traps and leave the queue at acquisition instead: a holder needs
no place in line once the kernel holds the lock, and SIGKILL runs no trap.
- The ticket was published after `exec 8>&-` released the counter lock, so
a later arrival could take seq+1, see an empty queue, name itself head and
win the lock out of arrival order. Publish inside the critical section.
- Orphan liveness was decided solely by `kill -0`, so a ticket whose PID had
been recycled to a live process was never aged out and, being head, made
an idle lock unreachable for every job on the host. Age a ticket out on
mtime alone above 1800s; a live waiter re-touches every ~7s.
Measured on a free lock with a live-PID, 2h-old orphan head ticket:
base burns its full 6023ms budget and exits 1 with the orphan intact;
this commit acquires in 19ms and reaps it.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmvhb9cysmc
* style(ci): keep only the load-bearing why in the lock helper comments
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmvhb9cysmc
* fix(ci): keep the lock queue head in one blocking wait
The head re-entered the kernel wait queue every five seconds, so a release
reached waiters that stayed blocked first; the head could lose every
release and starve to its budget. Run 39472038337 still burned the full
5400 s as queue ticket 2. Wait once for the remaining budget and log the
remaining time at most once a minute.
A resumed release tag can predate the helper, so both acquisition sites
fall back to the bare lock instead of dying before Maven and wedging the
release. Pin the emitted ::error:: annotation prefix rather than a file
substring a comment can satisfy, and observe the real acquisition order in
the stub.
* fix(ci): name the blocking ticket in the sdk-java lock timeout
The timeout line reported only the dying waiter's own ticket, so a deep
queue and a wedged one produced the same message and neither could be
acted on from the log. Name the head of line, which separates a real
sibling hold from an orphan ticket.
Bring both design docs back to the shipped mechanism: two acquisition
sites, the flock'd counter with no aging path, the 1800 s mtime arm and
the ticket removed at acquisition, plus the operator recovery command.
Refresh the stale .size-baseline entry for sdk-java.yml.
* test(ci): wait for the first ticket instead of a fixed sleep
The FIFO case started the second acquirer 800 ms after the first and relied
on that being enough for A to take sequence 1. On a loaded runner it is not,
and B wins sequence 1, so the case fails on timing rather than behavior.
Poll for A's published ticket instead.
* fix(ci): print the lock wait line before blocking
The wait line was emitted only after `flock` returned, so a head parked
behind a sibling hold printed nothing for its whole budget: run
39528681869's step sat in flock for 40 minutes and was cancelled with an
empty log, which is the opposite of the attribution this change exists to
add. Probe with --nonblock, print the ticket line, then block.
The observability bullet in both design docs now states what the code does.
---------
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Shaojin Wen <szujobs@gmail.com>