Repository navigation
feat(managed-agent): H4e-a team record contract - #13811
Conversation
The record-contract half of slice H4e of #12827, tracked by #13745. H4e is split like H4d: the team domains land as records first, and the runtime follows as H4e-b, together with the close cascade under teams (#13745 E3) and the rest of what the H4a map gives H4e. Detach (#13745 E2) stays the separate Detach follow-up of the H4a map. The team domains are the durable form of the Legacy team model, not a parallel one. All four stay disabled for submission, and the Java store validates them ahead of any writer. - managed-team_state v1 (chain teamId): the roster and the lifecycle. Members are append-only, join one per revision and only while the team is active; membershipRevision is one more than the member count; the lifecycle runs active -> closing -> deleted one step at a time, with a purely logical run that is admitted until the team is deleted. - managed-team_task v1 (chain taskId): one board item with the Legacy fields and a team-unique number. in_progress needs an owner, deleted is terminal, and dependencies are stored once, as blockedBy, and only grow; whether one still blocks is read from the blocker's status. - managed-team_message v1 (chain messageId): one mailbox message to one recipient, shaped like the H4d-a outbox entry, with plain text or one of the six Legacy mailbox types, each with a fixed direction between the leader and a member. - managed-team_plan v1 (chain requestId): the approval state of one plan request, keyed by the D6 action that asks the leader; it waits, then settles with a decision or is cancelled when withdrawn or superseded. - Commit-time rules in the TS authority and the Java store, also checked when the authority rebuilds a log: the team is led by this Session; a member joins as a live child Session run of this Session that no other team lists; a task, message or plan request opens only in an active team, so a closing team drains; task numbers are unique per team, owners are the leader or members, and new dependencies stay in the team and close no cycle; messages travel between the leader and members and target the lead Session or the Session the member's run attached; plan requests come from members that require plan mode. Content references join the resource closure on both sides. - Shared fixtures managed-team-record/1 (125 cases, 57 successor pairs) replay in both languages; no team record projects a task. No migration and no public contract change. Design: docs/design/2026-10-10-managed-agent-teams.md (en + zh-CN); the H4, H4a and H4d-a designs point to it, and the H4 design's detach-owner question now points to the Detach follow-up instead of H4c.
|
|
wenshao
left a comment
There was a problem hiding this comment.
Reviewed commit 12be226486ae50183be37c1e068167fcc389eff6.
One P2 finding: a malformed team-message successor dereferences a missing recipient before the immutable-field check can reject it. The same defect was reproduced through the TypeScript authority and the Java store; the inline comment covers both implementations.
Validation: 410 existing TypeScript tests and 17 existing Java tests passed, and the full repository build and typechecking passed. Targeted regression witnesses fail because the actual exceptions are TypeError / NullPointerException instead of the expected record-conflict exceptions. In both witnesses, the rejected attempt leaves the committed revision unchanged and the corrected successor succeeds.
This review covers the record contract, resource closure, ownership rules, replay, and matching English/Chinese designs. Runtime enablement and the explicitly deferred H4e-b work remain outside this PR's scope. Reproduction used the authority and H2 store; no live CLI or MySQL/MariaDB E2E result is claimed.
| JsonNode child = recordBody(scopeKey, sessionId, | ||
| "child_run", memberWith(team, "name", to) | ||
| .get("childRunId").textValue(), |
There was a problem hiding this comment.
[P2] Validate the successor's recipient before dereferencing it
After committing a planned message from leader to alice, submit the same messageId with to: "carol" (absent from the roster), targetSessionId: "session-run-1", and delivery accepting. Because previous != null skips the participant check and isSuccessor runs only after requireTeamRecord, memberWith(...) returns null and this dereference throws NullPointerException instead of the normal 409 managed_session_extension_record_rejected. The API's generic exception handler maps it to 500. The equivalent TypeScript path in managed-session-authority.ts:2480â2484 throws TypeError: Cannot read properties of undefined (reading 'childRunId').
Both failures were reproduced on this commit; corrected successors still commit successfully. Validate the immutable fields or recipient membership before this lookup in both implementations. Add authority/store regression cases that change an existing message's recipient to an unknown member and assert the normal conflict exception and an unchanged committed revision.
There was a problem hiding this comment.
Fixed in 4851c23.
The party check now runs on every revision in both implementations, not only when the message opens. The roster only grows and from/to are fixed, so a lawful successor always passes, while one that readdresses the message to a name outside the roster is refused with the normal record conflict ("Team message must travel between the leader and members of its team.") before anything looks its recipient up.
Regression witnesses, both confirmed red before the fix (TypeError in the authority, NullPointerException in the store) and green after:
- Authority: after committing message-1
planned, anacceptingsuccessor addressed tocarolis refused withManagedSessionConflictErrorand the rule's message, and the committed record stays at revision 1 with its original body; the corrected successor then commits. - Store: the same successor is refused with 409
managed_session_extension_record_rejectedand the rule's message, and the Session's committed revisions are unchanged.
I also rechecked the other lookups the team rules make ahead of the chain rules (the joining member's child run, new blockers and the cycle walk, the team itself); each either handles a missing record as a refusal or only follows ids that an earlier commit already verified.
äļæčŊīæ
å·ēåĻ 4851c23 äŋŪåĪã
äļĪį§åŪį°į°åĻé―åĻæŊäļŠäŋŪčŪĒäļæĢæĨæķæŊįåäļč
,čäļåŠæŊåĻæķæŊåžåŊæķæĢæĨãåååŠåĒäļå,from/to æŊåšåŪåæŪĩ,å æĪåæģįåįŧ§æŧč―éčŋ;čææķæŊæđæįŧåååĪååįåįŧ§,äžåĻæĨæūæĨæķæđäđåäŧĨæĢåļļįčŪ°å―åēįŠ("Team message must travel between the leader and members of its team.")čĒŦæįŧã
åå―č§čŊåĻäŋŪåĪååäļšįšĒ(authority äļäļš TypeError,store äļäļš NullPointerException),äŋŪåĪåäļšįŧŋ:
- Authority:æäšĪ message-1 į
plannedå,åįŧcarolįacceptingåįŧ§čĒŦäŧĨManagedSessionConflictErroråčŊĨč§åįæķæŊæįŧ,å·ēæäšĪčŪ°å―äŧååĻäŋŪčŪĒ 1ãå åŪđäļå;éåæĢįĄŪįåįŧ§åŊäŧĨæäšĪã - Store:åäļäļŠåįŧ§äŧĨ 409
managed_session_extension_record_rejectedåčŊĨč§åįæķæŊčĒŦæįŧ,čŊĨ Session å·ēæäšĪįäŋŪčŪĒæ°äļåã
æäđåĪæĨäšåĒéč§ååĻéūč§åäđååįå ķäŧæĨæū(å å Ĩæåį child runãæ°åĒįéŧåĄæđäļįŊæĢæĩéåãåĒéæŽčšŦ):åŪäŧŽčĶäđæčŪ°å―įžšåĪąå―ä―æįŧåĪį,čĶäđåŠæēŋįäđåįæäšĪå·ēįŧæ ļéŠčŋį id æĨæūã
|
Thanks for the PR! Template looks good â â every required heading is present, the Evidence section correctly says N/A for a contract that ships disabled, and the design doc is linked in both languages. Problem: Real, and I checked it against the base tree rather than taking the description's word. Direction: Aligned. Landing the record contract inert, ahead of the runtime that produces it, is the convention this codebase already documents for itself â the base Size: Core paths are touched (
At 1,642 production lines this is over both the 500-line maintainer-awareness mark and the 1,000-line large-PR advisory, so I am recording it plainly. It does not change the outcome here: the two-tier core gate in AGENTS.md applies to external PRs and exempts maintainer-authored ones, and this PR is authored by a repo admin who is also a listed owner in Approach: The scope feels right, and notably this PR is the split rather than something that needs splitting: H4e was cut into H4e-a (records, this PR) and H4e-b (runtime), the same way H4d was cut. Everything expensive and user-visible is deferred â the seven team tools, plan resolution, member shutdown, the mailbox relay, legacy import, enablement, detach, the close cascade. What remains is the minimum that can express the rules at all. I considered whether Reuse is good: no parallel validator utility is introduced. The TS side builds on Risk: No elevated risk signals. The Stage 1e revert-correlated path scan matched nothing (no content generator, streaming parser, shell execution, MCP, LSP, ACP, relaunch or sandbox paths). The change is also structurally inert: the four domains stay out of Moving on to code review. ð äļæčŊīææč°ĒčīĄįŪïž æĻĄæŋåŪæī â ââ ææåŋ éæ éĒé―åĻïžEvidence äļæ åŊđäļäļŠ"įĶįĻæäšĪ"įåĨįšĶæĢįĄŪå°åäš N/AïžčŪūčŪĄææĄĢåčŊéūæĨé―å Ļã éŪéĒïž įåŪååĻïžčäļææŊåŊđį
§ base äŧĢį æ æ ļåŪįïžæēĄæåŠéäŋĄæčŋ°ã æđåïž åŊđé―ãčŪĐčŪ°å―åĨįšĶå
äŧĨ"įĶįĻ"įķæč―å°ãåįąäš§įåŪįčŋčĄæķč·äļïžæĢæŊčŋäļŠäŧĢį åščŠå·ąå·ēįŧåæįįšĶåŪ ââ base į č§æĻĄïž æŽ PR č§Ķåæ ļåŋč·Ŋåūïž 1,642 čĄįäš§éŧčūåæķčķ
čŋäš 500 čĄįįŧīæĪč
å
ģæģĻįšŋå 1,000 čĄįåΧ PR æįĪšįšŋïžæäŧĨæåĶåŪčŪ°å―ãä―čŋäļæđåįŧčŪšïžAGENTS.md įæ ļåŋäļĪįš§éĻįĶéåŊđįæŊåĪéĻ PRïžįŧīæĪč
æŽäššæäšĪį PR åąäščąå
čåīïžčæŽ PR įä―č
æĒæŊäŧåš adminãäđæŊ æđæĄïž čåīåįïžčäļåžåūæģĻæįæŊïžčŋäļŠ PR æŽčšŦå°ąæŊæåįįŧæïžčäļæŊäļäļŠéčĶåæį PR ââ H4e čĒŦåæ H4e-aïžčŪ°å―ïžåģæŽ PRïžäļ H4e-bïžčŋčĄæķïžïžäļ H4d įåæģäļčīãæææčīĩäļįĻæ·åŊč§įéĻåé―čĒŦæĻåäšïžäļäļŠåĒéå·Ĩå
·ãčŪĄåčĢåģãæåå
ģåãmailbox äļįŧ§ãæ§åĒéåŊžå
ĨãåŊįĻåžå
ģãdetachãå
ģéįš§čãåĐäļįæĢæŊ"č―æč§åčĄĻčūūåšæĨ"įæå°éåãæäđčččŋč―åĶå
åŠč―å° åĪįĻååūåĨ―ïžæēĄæåžå
Ĩåđķåįæ ĄéŠå·Ĩå
·ãTS äū§åŧšįŦåĻ éĢéĐïž æ åįš§éĢéĐäŋĄå·ãStage 1e į revert įļå
ģč·ŊåūæŦææēĄæå―äļäŧŧä―äļéĄđïžäļæķåå
åŪđįæåĻãæĩåžč§ĢæåĻãshell æ§čĄãMCPãLSPãACPãrelaunch æ sandbox č·ŊåūïžãæđåĻåĻįŧæäļäđæŊæ°æ§įïžåäļŠ domain äŧįķäļåĻ čŋå ĨäŧĢį åŪĄæĨ ð â Qwen Code · qwen3.8-max-2026-09-02 Reviewed at |
Code reviewI wrote down what I would do before opening the diff: follow the existing The cross-language parity holds. That is the main thing that can silently rot in a two-validator contract, so I walked all four domains clause by clause rather than spot-checking. Every rule matches, including the rejection strings: lead binding, append-one-member-per-revision, the lifecycle stepping one state at a time, Non-blocking, one thing for H4e-b to inherit carefully. The message-target path assumes a recipient named when the message opened is still on the roster â I also checked the No correctness bugs, no security concerns, no regressions found. No AGENTS.md violations: ESM, no Design docs are complete in both languages. EN and zh-CN have identical 15-heading structure (Problem and scope â Current state â Decisions â Records â the four record subsections â Non-goals â Files affected â Validation â Acceptance criteria â Open questions â Follow-up work), and all six pointer updates into the H4, H4a and H4d-a docs are mirrored in the Chinese versions. No translation gap. Files changed (20 of 20 shown)
Testing evidenceThis is an unattended CI run, so per the triage rules I did not build or execute anything from this PR â the evidence below is the PR's own CI, read through the API for the reviewed commit. No check has failed. Final CI results for
One row per check name (latest run); skipped checks omitted; failures sort first. / æŊäļŠæĢæĨåäļčĄïžåææ°äļæŽĄčŋčĄïžïžįįĨ skippedïžåĪąčīĨéĄđæåĻæåã Two workflow runs with What CI does substantiate is the PR's central claim. The cross-language agreement is not an assertion in the description â it is two suites, Sandboxed verification would settle the one claim CI cannot: Not verified, with reasons: the mutation-testing numbers (author's claim, no CI lane reproduces them); the Java äļæčŊīæäŧĢį åŪĄæĨæåĻæåž diff äđåå
åäļäščŠå·ąäžæäđåïžæēŋįĻ base æ éå·ēæåå äļŠåŪäūį č·ĻčŊčĻäļčīæ§æįŦã čŋæŊåæ ĄéŠåĻåĨįšĶéæåŪđæææč
åįéĻåïžæäŧĨæéäļŠ domain éæĄåŊđį
§ïžæēĄæåŠæ―æĨãææč§åé―äļčīïžå
æŽæįŧäŋĄæŊåįŽĶäļēïžlead įŧåŪãæŊäļŠäŋŪčŪĒåŠčŋ―å äļåæåãįå―åĻææŊæŽĄåŠčĩ°äļæĨã ééŧåĄïžæäļåĪéčĶ H4e-b å°åŋįŧ§æŋã æķæŊįŪæ č§Ģæč·ŊåūåčŪū"æķæŊåžåŊæķčĒŦæåįæĨæķæđäŧåĻååäļ" ââ TS authority éį æäđæĢæĨäšæ°åĒ authority äŧĢį åéį æŠåį°æĢįĄŪæ§įžšé·ãåŪå
ĻéŪéĒæåå―ãæŠåį°čŋå AGENTS.md äđåĪïžESMãæ čŪūčŪĄææĄĢåčŊåŪæīã čąæäļäļæįæåŪå Ļįļåį 15 äļŠæ éĒįŧæïžéŪéĒäļčåī â į°įķ â åģį â čŪ°å― â åäļŠčŪ°å―å°č â éįŪæ â åå―ąåįæäŧķ â éŠčŊ â éŠæķæ å â åžæūéŪéĒ â åįŧå·Ĩä―ïžïžåđķäļåŊđ H4ãH4aãH4d-a ææĄĢįå ĻéĻå åĪæåæīæ°é―åĻäļæįåæĨäšãæ įŋŧčŊįžšåĢã æĩčŊčŊæŪčŋæŊäļæŽĄæ äššåžåŪį CI čŋčĄïžå æĪæį § triage č§åææēĄææåŧšææ§čĄæŽ PR įäŧŧä―äŧĢį ââ äļéĒįčŊæŪæŊéčŋ API čŊŧå°įãčŊĨ PR čŠčšŦåĻčĒŦåŪĄæĨ commit äļį CI įŧæãæēĄæäŧŧä―æĢæĨåĪąčīĨã åŪĄæĨæķčŋæäļĪäļŠ CI č―åĪčŊåŪįæĢæŊæŽ PR įæ ļåŋäļŧåž ãč·ĻčŊčĻäļčīäļæŊæčŋ°éįäļåĨæčĻ ââ åŪæŊäļĪäļŠåĨäŧķïžvitest äļį æēįŪąåéŠčŊč―åĪč§Ģåģ CI č§ĢåģäļäšįéĢäļæĄäļŧåž ïž æŠéŠčŊéĄđååå ïžååžæĩčŊæ°åïžä―č
äļŧåž ïžæ CI ééåĪį°ïžïžJava â Qwen Code · qwen3.8-max-2026-09-02 Reviewed at |
|
Confidence: 4/5 â the parity work is genuinely careful and the change is structurally inert; the one point I am holding back is a fragility this PR hands to H4e-b, not a defect in it. Stepping back: my independent proposal was to follow the existing sibling record pattern, put the invariants in the one authority, mirror them in Java, replay one shared corpus in both languages, and ship disabled. The PR matches that and beats it in one respect I did not think of â it aligns the order of validation clauses across the two languages, which is what turns "refused on the clause the fixture names" from a lucky substring match into a real assertion. I did not find a simpler path it missed. On the questions that usually decide these: the problem is verified in the base tree, not asserted (four registered domains with no body and no enablement). The direction follows a server-first ordering this codebase already documents for itself. The scope is already the reduced one â H4e was split into records and runtime, and everything user-visible is on the other half. Nothing in the diff is unrelated: six doc edits, each a consequence of the re-scope, each mirrored in Chinese. If I were maintaining this in six months I would thank the author for the fixture corpus and the clause-ordered validators rather than curse them; the thing that usually rots in a two-language contract is silent divergence, and 125 cases plus 57 successor pairs replayed from a single file is the right defence against it. What keeps this at 4 rather than 5 is the roster-shrink fragility I named in Stage 2. It is not a bug today â I traced the invariant that makes it safe (parties are fixed keys, the roster is append-only, I am not treating the size as a reservation. At 1,642 production lines this is over the advisory mark, but the two-tier core gate in AGENTS.md governs external PRs and exempts maintainer-authored ones, and this author is a repo admin and a listed owner in Verdict: approve, with the approval deferred until CI lands green on The one thing I would suggest, if you want it before merge rather than after: an äļæčŊīæäŋĄåŋåšĶïž4/5 ââ äļčīæ§å·Ĩä―ååūįĄŪåŪįŧčīïžæđåĻåĻįŧæäļæŊæ°æ§įïžæäŋįįéĢäļåæŊæŽ PR äšĪįŧ H4e-b įäļåĪčåžąæ§ïžčäļæŊåŪčŠčšŦįįžšé·ã éäļæĨįæīä―ïžæįŽįŦæåšįæđæĄæŊæēŋįϿǿįåįš§čŪ°å―æĻĄåžãæäļåéæūčŋåŊäļį authorityãåĻ Java äū§éåãįĻäļäŧ―å ąäšŦčŊæåĻäļĪį§čŊčĻäļåæūïžåđķäŧĨįĶįĻįķæäšĪäŧãčŋäļŠ PR äļäđäļčīïžåđķäļåĻäļäļŠææēĄæģå°įæđéĒååūæīåĨ― ââ åŪåŊđé―äšäļĪį§čŊčĻäļæ ĄéŠååĨį饚åšïžččŋæĢæŊæ"æ fixture æåįååĨčĒŦæįŧ"äŧįǰ巧įåäļēå―äļåæįæĢæčĻįå ģéŪãææēĄææūå°åŪéæžįæīįŪč·Ŋåūã å ģäšéåļļåģåŪčŋįąŧåĪæįå äļŠéŪéĒïžéŪéĒæŊåĻ base äŧĢį æ éæ ļåŪčŋįïžäļæŊæčĻåšæĨįïžåäļŠå·ēįŧčŪ°į domainïžæĒæ čŪ°å―ä―äđæŠåŊįĻïžãæđåéĩåūŠįæŊčŋäļŠäŧĢį åščŠå·ąå·ēįŧåæį server å čĄéĄšåšãčåīå·ēįŧæŊčĢåŠåįéĢäļå ââ H4e čĒŦåæčŪ°å―äļčŋčĄæķïžææįĻæ·åŊč§įéĻåé―åĻåĶäļåãdiff éæēĄææ å ģå åŪđïžå åĪææĄĢäŋŪæđé―æŊéæ°ååŪčåīįåŋ įķįŧæïžäļé―åĻäļæįåæĨäšãåĶæå äļŠæåįąææĨįŧīæĪïžæäžæč°Ēä―č įäļčŋäŧ― fixture čŊæåæååĨæåšįæ ĄéŠåĻïžčäļæŊåæĻäŧïžåčŊčĻåĨįšĶééåļļč åįäļčĨŋæŊææ å̰æŊįåæ§ïžč"125 äļŠįĻäūå 57 äļŠåįŧ§åŊđãäŧåäļæäŧķåæū"æĢæŊåŊđæĪįæĢįĄŪéēåūĄã čŪĐåŪååĻ 4 åčäļæŊ 5 åįïžæŊæåĻ Stage 2 įđåįåååŊįžĐåčåžąæ§ãčŋåĻäŧåĪĐäļæŊ bug ââ æčŋ―æĨäšä―ŋåŪåŪå
ĻįäļåéïžåäļæđåąäšåšåŪéŪãåååŠčŋ―å ãåžåŊæķåžšåķæ ĄéŠ ææēĄææč§æĻĄå―ä―äŋįæč§ãäŧĨ 1,642 čĄįäš§éŧčūčŪĄïžåŪčķ
čŋäšæįĪšįšŋïžä― AGENTS.md įæ ļåŋäļĪįš§éĻįĶéåŊđįæŊåĪéĻ PRãčąå
įŧīæĪč
æŽäššæäšĪį PRïžčæŽä―č
æĒæŊäŧåš adminãäđæŊ įŧčŪšïžæđåïžä―ææđååŧķčŋå° CI åĻ åĶææģåĻååđķåčäļæŊååđķåčĄĨäļäŧķäšïžæįåŧščŪŪæŊïžč·äļæŽĄ â Qwen Code · qwen3.8-max-2026-09-02 Reviewed at |
Address the review on #13811. The authority and the store checked that a team message travels between the leader and members of its team only when the message opened, and the cross-record rules run before the chain rules. A successor that readdressed an existing message to a name outside the roster therefore reached the recipient lookup with no member: the TypeScript authority threw a TypeError and the Java store a NullPointerException (a 500) instead of the record conflict. Check the parties on every revision in both languages. The roster only grows and from/to are fixed, so a lawful successor always passes, and a readdressed one is now refused as the conflict it is, before anything looks its recipient up. Authority and store witnesses change an existing message's recipient to a stranger and assert the conflict, the rule's message and an unchanged committed revision.
wenshao
left a comment
There was a problem hiding this comment.
Re-reviewed at 4851c2364eddb5ab55a15611c2b01e7dc55fad22, including the delta from 12be226486ae50183be37c1e068167fcc389eff6 and the previous unresolved finding.
No new actionable findings. The previous P2 recipient-lookup issue is fixed in both implementations. Participant validation now precedes the recipient lookup on every revision. I checked the append-only roster and immutable message parties against the current code, including the authority's journal-rebuild path; valid successors remain admissible.
Independent reruns of the original recipient-change witness now produce ManagedSessionConflictError in TypeScript and ApiException / HTTP 409 / managed_session_extension_record_rejected in Java. Both preserve the committed revision after rejection and accept the corrected successor.
Validation on this exact head:
npm run buildandnpm run typecheck: passed.- Four affected TypeScript suites: 410 tests passed.
- Three affected Java suites: 17 tests passed.
- Independent reproduction checks: 2 TypeScript and 2 Java tests passed.
These are record-level checks (Java Spring/H2 and the existing TypeScript team-domain test enablement), not full HTTP/CLI E2E. Some hosted CI jobs were still pending when checked.
Local maintainer verification â PR #13811
|
| # | Round-1 measurement | Status at 4851c236 |
|---|---|---|
| 1 | Corpus replay + cross-language identity (199 lines) | Re-measured â re-executed at the new head, identical again (the input closure â managed-team-record.ts, managed-extension-projection.ts, ManagedTeamRecords.java, ManagedExtensionProjection.java, the fixtures â is byte-unchanged between heads, confirmed by git diff --stat over the closure AND by re-execution) |
| 2 | Domain gate | Re-measured â identical |
| 3 | TS mutation matrix | Re-measured â 14/14 killed (13 + the reverted-fix mutant) |
| 4 | Java mutation matrix | Re-measured â 7/7 killed (6 + the reverted-fix mutant) |
| 5 | Full suites | Re-measured â TS 3160/3160 green, Java 1625 tests as before |
The delta commit itself was verified as load-bearing: reverting its every-revision parties check to the round-1 open-only form (mutants TS-M14 / JV-M7) turns its own new regression tests red on both sides.
äļææčĶ
įŧčŪš:åŊäŧĨååđķ(å―å head 4851c236)ã äļĪč―Ūå
ą 17,916 æĄčæŽæčĻ,æēĄæäŧŧä―åĪąčīĨåŊå―å äšæŽ PRã
- éŠčŊæéī head į§ŧåĻčŋäļæŽĄ(
12be2264â4851c236,æ°åĒäļäļŠ fix æäšĪ:æåĒéæķæŊįæķåæđæĢæĨäŧ"äŧ åžéūäŋŪčŪĒ"æđäļš"æŊäļŠäŋŪčŪĒ")ãæææŋéæĩéå·ēåĻæ° head éč·;čŊĨ fix æŽčšŦäđéčŋååžéŠčŊäļšæŋéââæåŪåéææ§å―Ēåž(TS-M14 / JV-M7),å ķæ°åĒåå―æĩčŊåĻäļĪäū§įŦåģåįšĒã - æ ļåŋå̰ææįŦ:åäļŠåĒéčŪ°å―ä―åĻ TS äļ Java äļåæūåäļäŧ―čŊčĻäļįŦčŊæ(125 įĻäū + 57 åįŧ§åŊđ),199/199 čĄåĪåģéåčäļčī;æŊäļŠæ æįĻäūé―æ fixture æåæĄæŽūčĒŦæįŧ(100 clause å
Ļå―äļ);åĶææįŽįŦæé į 17 äļŠčūđįæĒé(2^53 čūđįį
number/planRevisionãčķ éæåãastral ååãcancelled æšåļĶ input į),äļĪčŊčĻåĪåģåŪå ĻäļčīãåšįšŋåŊđį §:base æ 199/199 å ĻéĻNO-BODYã - ååžįĐéĩ:TS 14/14ãJava 7/7 å
ĻéĻčĒŦæ,æŊäļŠååžé―æŧåĻäŧĨå
ķå―åįåŪåŦæĩčŊäļ;äļĪäū§ååŦæīŧæ§åŊđį
§ååž,åįŦåģåįšĒ;įĐéĩįŧæåäļĪæĢĩæ
git statusåđēåã - åå―:æ° head å
Ļé TS åĨäŧķ 3160/3160(58 æäŧķ,äļ PR å̰æäļčī);Java
clean verify:1625 æĩčŊã0 åĪąčīĨã2 äļŠ error äļšToolPublicationStoreTestį§įšĶčķ æķââåĻ base äļäŧĨįļåįūåéį°,äļæŽ PR æ å ģ;Checkstyle 0 čŋč§ãSpotBugs 0 åį°ã - éĻįĶ:åäļŠ domain å
registered=1 enabled=0 REFUSED-EXACT,taskKindOfå Ļäļš null(äļæå―ą task),scheduleæĢåŊđį §éčŋã - æŠčĶį:H4e-b čŋčĄæķ(æŽ PR æįĄŪįĶįĻ);įåŪ MySQL/MariaDB éæ(store äŧĨ H2 MySQL æĻĄåžéĐąåĻ);ä―č čŠæĨį 22+22 å Ļéååžæ°æŠéäļåĪč·(æŽæĨåäŧĨįŽįŦ 14+7 įĐéĩčĶįå ĻéĻæŋéåŪåŦ);å ķäŧ OS į CI flake éĒįã
- éĒå ååĻįįšĒæĩčŊ(TS flake äļ Java į§įšĶčķ æķ)čŊĶ觿̿ "Pre-existing observations"ã
Central claim and A/B proof
Claim: the four team domains (team_state, team_task, team_message, team_plan) land as schema-v1 record bodies whose validators and commit-time rules behave identically in TypeScript and Java, replaying one language-neutral corpus (125 cases, 57 successor pairs) â all four domains registered but disabled for submission, projecting no task.
| Cell | Base (ad91df5) |
Head (4851c236) |
Oracle |
|---|---|---|---|
| Corpus replay (TS validators) | 199/199 NO-BODY (no team bodies exist) |
199/199 lines: expected verdict reproduced | per-line exp/got/rid/start/clause |
| Corpus replay (Java store) | 199/199 NO-BODY |
199/199 lines | same canonical format |
| Cross-language identity | â | diff of the two 199-line verdict files: empty â at both heads |
byte-identical lines |
| Invalid-case clause attribution | â | 100/100 clause=HIT on both sides, 0 MISS/WRONGTHROW |
fixture's named clause must appear in the refusal |
| Domain gate | all 4 REFUSED-EXACT, body ABSENT |
all 4 REFUSED-EXACT, body PRESENT |
exact error string domain <d> is registered but not enabled for submission. |
| Task projection | â | taskKindOf â null for all 5 templates |
no team record projects a task |
Full src/managed-runtime/ vitest |
2963 tests (3 pre-existing flakes under load) | 3160/3160 green at both heads (58 files â matches the PR's claim exactly) | vitest |
Java managed-agent-server verify |
same 2 errors (see below) | 1625 tests, 0 failures, 2 pre-existing errors, 1 skipped (macOS-disabled) â identical at both heads | surefire |
checkstyle:check / spotbugs:check |
â | 0 violations / 0 findings (BUILD SUCCESS) | maven |
Witness: 01-cross-language-fixture-identity.png, 04-domain-gate-ab.png, 03-suite-ab-and-gates.png below.
Independent drift probes (beyond the PR's own corpus)
17 extra probes I authored (harness/drift-probes.json) â the PR's fixtures are the author's, so these provide the independent input: number/planRevision at exactly 9_007_199_254_740_990 (accepted) and ...991 (refused) in both languages, an 11-member roster refused identically, astral-plane name refused, from == to == "leader" refused, a cancelled delivery carrying inputId refused, case-sensitivity of decision/status, string-typed planModeRequired, and an eleventh-member join successor refused. TS and Java agree on every probe, including the design-confirming positives that a team may be named leader (the reserved name binds participants only) and that a task can be in_progress owned by leader.
The follow-up fix (4851c236) is load-bearing
The delta commit widens the team-message parties check from the opening revision to every revision (both languages), closing the window where a successor readdressed to a non-member raced the fixed-key chain rule and hit the recipient lookup first. Reverting just that hunk (TS-M14 / JV-M7) turns the commit's own new regression tests red on both sides (routes a message only within the teamâĶ / routesMessagesAndPlansWithinTheTeam), so the fix is pinned by its tests, not decorative.
Mutation matrix (vacuity check on the guards)
Every guard was disabled one at a time and the PR's team tests re-run; each mutant died on the fixture case or authority/store test named for that guard, and both worktrees were git status-clean afterwards. A liveness-control mutant per language (perturbing an asserted error string) died immediately, proving both the suites and the harness can fail. Counts at 4851c236:
| Mutant | Result | Killing witness |
|---|---|---|
| TS: membershipRevision = members+1 | KILLED | state-revision-behind/state-revision-ahead |
TS: member named leader refused |
KILLED | state-member-leader |
| TS: message digest == content digest | KILLED | message-digest-mismatch |
| TS: message kind direction rules | KILLED | message-plan-request-from-leader et al. (4 tests) |
| TS: plan feedback only with decision | KILLED | plan-feedback-without-decision |
| TS: team led by this Session | KILLED | refuses a team that another Session leads |
| TS: one team per child run | KILLED | keeps each child run in one teamâĶ |
| TS: records open only in active team | KILLED | opens team records only in an active teamâĶ |
| TS: task number unique per team | KILLED | keeps task numbers uniqueâĶ |
| TS: dependency cycle refused | KILLED | refuses a dependency outside the team or one that closes a cycle |
| TS: message targets recipient's Session | KILLED | routes a message only within the teamâĶ |
| TS: plan from plan-mode member | KILLED | takes a plan request only from a member that must plan |
| TS: liveness control (error string) | KILLED | state-members-null + state-members-not-array |
| TS: revert the follow-up fix to open-only parties check | KILLED | routes a message only within the teamâĶ (new regression assertions) |
| Java: team led by this Session | KILLED | bindsATeamToItsLeadAndItsMembersToLiveChildRuns |
| Java: one team per child run | KILLED | same |
| Java: task number unique | KILLED | keepsTheBoardInsideItsTeam |
| Java: dependency cycle | KILLED | same |
Java: MAX_MEMBERS 10â11 |
KILLED | limits pinned by contract test (2 tests) |
| Java: liveness control (error string) | KILLED | validatesTheSharedRecordsAndStarts |
| Java: revert the follow-up fix | KILLED | routesMessagesAndPlansWithinTheTeam (new regression assertion) |
Witness: 02-mutation-matrix.png. The PR's own larger claim (22 authority + 22 Java mutants) was not re-run exhaustively; this independent matrix covers every guard the PR introduces â see Not covered.
Static gates (liveness-proven)
ESLint and Prettier were first proven live (a planted unused variable and a planted format break were both caught), then run clean over the five changed TS files plus the two fixtures files; tsc --noEmit for @qwen-code/qwen-code-core is clean. Design docs exist in both languages with reciprocal links and equal length (190 + 190 lines).
Findings
None blocking. One design-level confirmation worth recording: the validators deliberately allow a team to be named leader (only participants reserve the name); both languages agree, and the design doc's wording ("the reserved leader", in the participant sense) matches the code. No action needed.
Pre-existing observations (not attributable to this PR)
- TS load-correlated flakes. Under a heavily loaded first pass (two installs + two suites + Maven running concurrently), the round-1 head showed 4 red tests and base 3, with byte-identical signatures for the shared three (
managed-session-authority.hook-scale.test.tsÃ2,managed-session-record-sink.test.tsENOTEMPTY temp-dir race Ã1). The three affected files re-run in isolation: 54/54 green on both arms; quiet full-suite reruns at both heads: 3160/3160 green. The single head-only extra (managed-child-run-supervisor.test.ts âš settles a terminated process only after the unit is empty, a file this PR does not touch) also passes in isolation on both arms. - Java
ToolPublicationStoreTestà 2 errors (renewsTheOriginalClaimWhileScanningSlowObjectBytes,streamsLargeOutputAndReadsItsTailAfterStoreReplacementâ lease/expiry timing): fail with the byte-identical signature at base and at both heads, in isolation, on this machine, in a file this PR does not touch. Pre-existing environmental failure (macOS; the PR's own evidence was gathered on Linux). - Skipped-count delta: PR reports 2 skipped on Linux; 1 skipped here (
RuntimeBrokerDefaultOnTestisDisabled on operating system: Mac OS X). Environmental, not a discrepancy in the change.
These 13 red executions are disclosed here rather than counted as failures of this verification: each was either re-driven to green in isolation on both arms or shown byte-identical at base, which is the A/B expectation for pre-existing failures.
Not covered
- H4e-b runtime (team tools, teammate routing, mailbox relay, plan resolution, member shutdown, legacy import, enablement) â explicitly out of scope for this PR; the four domains stay disabled, which the gate harness proves.
- Real MySQL/MariaDB integration lanes â the store was exercised on H2 in MySQL mode (the module's own test setup), as the PR's test plan specifies; no migration ships here.
- The author's full 22+22+6 mutation run was not reproduced exhaustively; the independent 14+7 matrix above covers every guard the PR introduces.
- Per-commit attribution of
12be2264's internals: it is a single commit; the aggregate diff was verified. The follow-up4851c236was verified separately (above). - Other OS/CI lanes (Windows, Linux CI flake rates);
npm run bundlebeyond the per-workspace build the suites consume.
Methodology
Two isolated worktrees (tmp/pr13811-head at the current head, tmp/pr13811-base @ ad91df5), each corepack pnpm install --frozen-lockfile (prepare-built); internal-link integrity asserted via readlink -f node_modules/@qwen-code/qwen-code-core resolving inside each tree. Harnesses are mock-free against the real modules: TS replay via tsx importing managed-extension-projection.ts directly; Java replay via a scratch FixtureReplay.java compiled against the module's target/classes and its Maven dependency classpath, driving ManagedExtensionProjection.RECORD_BODIES. The corpus and my probe file were treated as pure input (base trees read them from the head tree). Mutation drivers apply anchored single replacements, assert exactly one occurrence, run the new team test files/classes, and restore byte-identically (verified by git status). Java env: Homebrew JDK 21 + Maven 3.9, -Dgpg.skip=true. Round-1 measurements whose input closures are byte-unchanged between heads were carried forward only after re-execution confirmed identical output (replay, gate); everything else was re-measured at the new head. All harnesses and raw logs are under tmp/pr13811-verify-20261010-112810/ (harness/, logs-*, evidence/).
Evidence captures
01 â Cross-language fixture identity (the central claim): 199/199 verdict lines byte-identical between TS and Java; base trees show 199/199 NO-BODY. (Captured at 12be2264; re-executed byte-identically at 4851c236.)
02 â Mutation matrix at 4851c236: 14/14 TS guards and 7/7 Java guards killed by their named witnesses, including the reverted follow-up fix; liveness controls killed; worktrees clean afterwards.
03 â Suites and gates at 4851c236: head suite 3160/3160 green on a quiet machine; loaded-pass flakes identical on base; Java 1625 with the 2 pre-existing errors attributed; checkstyle/spotbugs clean.
04 â Domain gate A/B: all four team domains registered but refused for submission on both arms; bodies present only at head; schedule positive control passes.
Local maintainer verification by @wenshao's request. Artifacts: tmp/pr13811-verify-20261010-112810/ (report.md, verdict.txt, assertions.json, harnesses, raw logs, evidence).
qqqys
left a comment
There was a problem hiding this comment.
Reviewed at head 4851c2364eddb5ab55a15611c2b01e7dc55fad22 (Critical-only scan).
Prior blocking issue â confirmed fixed. The P2 (a successor that readdressed a committed team message to a name outside the roster reached the recipient lookup before any participant check, so TypeScript threw TypeError and the Java store threw NullPointerException, which the generic handler maps to 500 instead of the normal 409) no longer holds on either side:
managed-session-authority.tsâ thetakesPart(message.from) && takesPart(message.to)refusal now sits in theteam_messagebranch unconditionally, not underprevious === undefined, so it runs on every revision and precedes theteam.members.find(...)!/extensionRecord('child_run', ...)!dereferences in thetargetSessionIdblock.ManagedExtensionRecordStore.requireTeamRecordârequire(takesPart(team, from) && takesPart(team, to), ...)likewise runs on every revision, andapplyRevisioncalls it for anyteam_domain regardless ofprevious, so it precedesmemberWith(team, "name", to).get("childRunId").
The non-null dereferences that remain are backed by the record validator, which refuses leader as a member name and requires distinct member names and distinct child run ids, so a participant check that passes on a non-leader name matches exactly one member in both languages.
Current scan â no Critical. Checked against the head code:
- The cycle walk can only dereference tasks that exist: every newly added blocker is verified present and in the same team before the walk starts, and a committed task's own
blockedByentries were verified when it committed, with records append-only â sothis.extensionRecord('team_task', next)!and Java'srecordBody(...).get("blockedBy")cannot miss. - The team-task owner rule, the task-number uniqueness rule, the blocker-scoping rule and the plan-mode member rule agree across the two implementations, including the "unchanged from the previous revision" allowance on the owner.
- Team membership rules match: a member joins as a non-terminal child Session run of the lead, and no child run may appear in a second team; the roster is append-only (
prefixon the member list) withmembershipRevision == members.length + 1. - All four domains are registered identically in
MANAGED_EXTENSION_RECORD_BODIESandManagedExtensionProjection, each projecting no task, and the resource-closure list coversdescriptionRef,metadataRef,contentRef,planRefandfeedbackRefon both sides â the closed key sets make the Java superset scan equivalent to the per-domain TypeScript lists. - Nothing here enables submission for the team domains, so the contract stays inert at merge.
CI: Qwen Autofix and the label/self-report jobs succeeded; Qwen Code CI was queued and SDK Java still running at review time. Nothing fails attributable to this diff, and no failure evidence contradicts the record-level checks above.
yiliang114
left a comment
There was a problem hiding this comment.
LGTM. Reviewed at 4851c2364e, including the delta from 12be226486.
The record contract holds up. The four team domains are registered bodies (all taskKindOf: () => null, so none of them projects a task), stay out of MANAGED_SESSION_ENABLED_DOMAINS, and the generic path refuses them at the extension funnel before the enablement check â no writer mints a team_state: / team_task: / team_message: / team_plan: event id. The commit-time rules match decision 8 rule-for-rule in both languages: lifecycle one step at a time (terminal deleted freezes the run), member join (a live child Session run of this Session, in no other team, active only), task (number is a fixed key, so uniqueness only needs to be checked at the open), owner (leader/member, or unchanged), blockedBy grows-only + in-team + acyclic, message parties checked on every revision with the target binding (leaderâlead Session, memberâits run's attached Session, otherwise it stays planned), plan from a plan-mode member keyed by the D6 requestId. The journal-rebuild path reuses the same assertExtensionRevision, so rebuild and live commit agree.
Your P2 from the earlier review is fixed: the parties predicate is evaluated unconditionally, before the recipient lookup, in both managed-session-authority.ts and ManagedExtensionRecordStore.java, and a rejected successor leaves the committed revision untouched. Shared fixtures are real (125 cases / 57 successor pairs, both suites load the same JSON), and the projection + ref-closure additions are equivalent across the two languages. The EN/ZH designs line up.
One non-blocking observation: managed-session-authority.extension.test.ts:1131 still describes team_state as "neither in the plain enabled list nor in the extension-record body index". After this PR it is in the body index, so in the real module that event takes the extension-funnel refusal ("records commit only through commitExtensionRecord") rather than "not enabled for submission". The test still passes only because that suite mocks the index down to {monitor_run}, so the probe's rationale is now stale â worth refreshing the comment (or dropping the mock later) so it stays honest. Not a behavior issue.
CI on this head: the lanes that have run are green (Test ubuntu Node 22.x, Lint & Static, Java matrix incl. Windows/macOS/11/17/21, Real daemon, Integration, Flyway uniqueness); Hosted process fault gates / MySQL 8.4, Runtime Broker and Managed Agent MariaDB, web-shell E2E and Desktop Shell are still pending, none failing. mergeStateStatus stays BLOCKED until the required approvals land.
Scope note: this is a source-level review of the frozen head plus fixture/doc checks; I did not rerun the MySQL/MariaDB lanes locally.
|
Released in v0.25.1. |




What this PR does
This is the record-contract half of slice H4e of the managed child-agent work (#12827, Stage H), tracked by #13745. The issue asked for three items: the team record domains (E1), detach to an independent durable owner (E2), and the close cascade under teams (E3). The issue's triage left three questions for a human, and the design settles them. H4e is split like H4d: this PR lands E1 as records, and the runtime follows as H4e-b, together with E3 and everything else the H4a delivery map gives to H4e (the seven team tools, plan resolution, member shutdown, the mailbox relay that H4d-a moved here, legacy team import, and the managed Agent tool's
namefor teammate routing). Detach stays the separate "Detach" follow-up that the H4a map already lists, and the stale pointer in the H4 design that sent its owner question to H4c now points there. The team domains are the durable form of the existing Legacy team model, not a parallel one: they keep its sanitized names, the reservedleader, the board statuses, the mailbox message types and plan approval.Four record bodies are added, all held in the lead Session's journal and all disabled for submission. The team state is the roster and the lifecycle: members are append-only, join one per revision and only while the team is active, and the lifecycle runs active, closing, deleted, one step at a time. A team task is one board item with the Legacy fields;
in_progressneeds an owner, deletion is terminal, and dependencies are stored once, asblockedBy, and only grow, so a write never has to update two tasks to keep one edge. Whether a dependency still blocks is read from the blocker's status. A team message is one mailbox message to one recipient, shaped like the H4d-a outbox entry: settled from its first revision, with a session delivery line, a target fixed at handover, and the target's input named once accepted. Its kind is plain text or one of the six Legacy mailbox types, each with a fixed direction between the leader and a member. A team plan is the approval state of one plan request, keyed by the D6 action that asks the leader: it waits, then settles with a decision, or is cancelled when the plan is withdrawn or superseded.Commit-time rules in both the TypeScript authority and the Java store bind every team record to its lead. The team must be led by this Session. A joining member must be a live child Session run of this Session that no other team lists. A task, message or plan request opens only in an active team of this Session, so a closing team drains without taking new work. A task number is unique in its team, an owner is the leader or a member, and a new dependency stays in the team and closes no cycle. A message travels between the leader and members and targets the lead Session or the Session its member's run attached, so a message to a member not attached yet stays planned. A plan request comes from a member that requires plan mode. The authority also checks these when it rebuilds a reopened log, and the bodies' content references join the resource closure on both sides.
Shared fixtures (125 cases, 57 successor pairs) replay identically in both languages. No team record projects a task. There is no migration and no public contract change. The design is recorded in both languages, and the H4, H4a and H4d-a designs point to it.
Why it's needed
Teams are what let several child agents work together, and on the Managed path they do not exist: the four team domains are registered names with no body, so nothing about a team can be committed, rebuilt or checked. The Legacy team model works, but it lives in the lead's process and its local files. On the Managed path the Runtime can be reclaimed and the Harness replaced, so every team fact the runtime will rely on has to be a committed record first. This PR pins those facts and the rules that keep them consistent, ahead of the runtime that will produce them, in the same server-first order as the earlier slices.
Reviewer Test Plan
How to verify
Evidence (Before & After)
N/A: a record contract that stays disabled for submission, so nothing is user-visible. Verification output:
managed-agent-server: the fullclean verify checkstyle:check: 1625 surefire tests, 0 failures, 2 skipped; 0 Checkstyle violations; 0 SpotBugs findings.npm run typecheck, ESLint and Prettier on the changed files: clean.Tested on
Environment (optional)
Unit and contract suites only: vitest for core, Maven surefire on H2 (MySQL mode) for
managed-agent-server, JDK 25. No MySQL/MariaDB integration lane was run locally, and this change has no migration.Risk & Scope
team_state:<n>,team_task:<n>,team_message:<n>andteam_plan:<n>event ids are now reserved for Stage H records, as every registered body's are; no writer produces those id shapes.Design: English · įŪä―äļæ
Linked Issues
Part of #13745 (E1; E2 moves to the Detach follow-up and E3 to H4e-b, so the issue stays open). Part of #12827.
äļæčŊīæ
čŋäļŠ PR åäšäŧäđ
čŋæŊ managed child-agent å·Ĩä―(#12827,Stage H)äļ H4e åįįčŪ°å―åĨįšĶéĢäļå,įą #13745 č·čļŠãčŊĨ issue æäšäļéĄđ:åĒéčŪ°å― domain(E1)ãdetach å°įŽįŦįæäđ ææč (E2),äŧĨååĒéäļįå ģéįš§č(E3)ãissue įåčŊįäļäšäļäļŠéčĶäššæĨåŪįéŪéĒ,æŽčŪūčŪĄįŧåšäšįæĄãH4e å H4d äļæ ·äļåäļšäš:æŽ PR äŧĨčŪ°å―įå―Ēåžč―å° E1,čŋčĄæķéåä―äļš H4e-b,čŋå E3 äŧĨå H4a äšĪäŧå°åūåįŧ H4e įå ķä―å åŪđ(äļäļŠåĒéå·Ĩå ·ãčŪĄåčĢåģãæåå ģåãH4d-a į§ŧå°čŋéį mailbox äļįŧ§ãæ§åĒéåŊžå Ĩ,äŧĨåįĻäš teammate č·Ŋįąį managed Agent å·Ĩå ·
name)ãdetach äŧæŊ H4a å°åūäļå·ēååį "Detach" åįŧå·Ĩä―,H4 čŪūčŪĄäļæå ķææč éŪéĒäšĪįŧ H4c įčŋæķæåį°å·ēæđäļšæåéĢéãåĒé domain æŊį°æ Legacy åĒéæĻĄåįæäđ å―Ēæ,čäļæŊåđķåįåĶäļåĨ:åŪæēŋįĻįŧčŋæļ æīįååãäŋįåleaderãäŧŧåĄæŋįķæãmailbox æķæŊįąŧåäļčŪĄååŪĄæđãæ°åĒåäļŠčŪ°å―ä―,å ĻéĻäŋååĻ lead Session į journal äļ,äļå ĻéĻäļåžæūæäšĪãåĒéįķææŊååäļįå―åĻæ:æååŠčŋ―å ,æŊäļŠäŋŪčŪĒå å ĨäļäļŠ,äļåŠč―åĻåĒéäļš active æķå å Ĩ;įå―åĻæäļš activeãclosingãdeleted,æŊæŽĄäļæĨãåĒéäŧŧåĄæŊäļäļŠåļĶ Legacy åæŪĩįäŧŧåĄæŋæĄįŪ;
in_progresséčĶ owner,å éĪæŊįŧæ,äūčĩåŠæblockedByäŋåäļæŽĄäļåŠåĒäļå,å æĪäļæŽĄåå Ĩæ°ļčŋäļéčĶäļšįŧīæäļæĄčūđčæīæ°äļĪäļŠäŧŧåĄãäūčĩæŊåĶäŧåĻéŧåĄ,įąéŧåĄæđįįķæčŊŧåšãåĒéæķæŊæŊåįŧäļäļŠæĨæķæđįäļæĄ mailbox æķæŊ,å―Ēįķäļ H4d-a į outbox æĄįŪäļčī:äŧįŽŽäļäļŠäŋŪčŪĒčĩ·å°ąæŊ settled,åļĶäļæĄ session æéįšŋ,įŪæ åĻäšĪæĨæķåšåŪ,åđķåĻ accepted ååæįŪæ į inputãå ķ kind æŊįšŊææŽ,æå į§ Legacy mailbox įąŧåäđäļ,æŊį§é―æ leader äļæåäđéīįåšåŪæđåãåĒéčŪĄåæŊäļäļŠčŪĄåčŊ·æąįåŪĄæđįķæ,äŧĨčŊ· leader ååģåŪį D6 action äļšéŪ:åŪå įåū ,įķååļĶįåģåŪ settled,æåĻčŪĄåčĒŦæĪåæčĒŦåäŧĢæķ cancelledãTypeScript authority äļ Java store äļįæäšĪæķč§åææŊæĄåĒéčŪ°å―įŧåŪå°å ķ leadãåĒéåŋ éĄŧįąæŽ Session éĒåŊžãå å Ĩįæååŋ éĄŧæŊæŽ Session äļäļŠåæīŧįãæēĄæå ķäŧåĒéååšį child Session runãäŧŧåĄãæķæŊæčŪĄåčŊ·æąåŠč―åĻæŽ Session äļäļŠ active įåĒéäļåžåŊ,å æĪ closing äļįåĒéåĻæķå°ūæķäļåæĨæķæ°å·Ĩä―ãäŧŧåĄįžå·åĻåĒéå åŊäļ,owner æŊ leader ææå,æ°äūčĩįåĻåĒéå äļäļå―ĒæįŊãæķæŊåĻ leader äļæåäđéīäž é,įŪæ æŊ lead Session æčŊĨæå run æ attach į Session,å æĪåįŧ尿Рattach įæåįæķæŊåįåĻ plannedãčŪĄåčŊ·æąæĨčŠčĶæą plan mode įæåãauthority åĻéåŧšéæ°æåžįæĨåŋæķäđäžæĢæĨčŋäšč§å,čŪ°å―ä―äļįå åŪđåžįĻåĻäļĪäū§é―įšģå Ĩčĩæšéå ã
å ąäšŦ fixture(125 äļŠįĻäūã57 äļŠåįŧ§åŊđ)åĻäļĪį§čŊčĻäļįŧåšåŪå ĻįļåįįŧæãåĒéčŪ°å―äļæå―ą taskãæēĄæčŋį§ŧ,äđæēĄæå Žå ąåĨįšĶåæīãčŪūčŪĄäŧĨäļĪį§čŊčĻčŪ°å―,H4ãH4a äļ H4d-a įčŪūčŪĄåå·ēæååŪã
äļšäŧäđéčĶ
åĒéčŪĐåĪäļŠ child agent č―åĪååå·Ĩä―,č Managed č·ŊåūäļåđķäļååĻåĒé:åäļŠåĒé domain åŠæŊæēĄæčŪ°å―ä―įå·ēįŧčŪ°åå,å æĪåĒéįäŧŧä―äšåŪé―æ æģæäšĪãéåŧšææĢæĨãLegacy åĒéæĻĄååŊäŧĨå·Ĩä―,ä―åŪåŠååĻäš lead įčŋįĻåå ķæŽå°æäŧķäļãåĻ Managed č·Ŋåūäļ,Runtime åŊč―čĒŦåæķãHarness åŊč―čĒŦæŋæĒ,å æĪčŋčĄæķå°äūčĩįæŊäļŠåĒéäšåŪé―åŋ éĄŧå æäļšå·ēæäšĪįčŪ°å―ãæŽ PR åĻäš§įčŋäšäšåŪįčŋčĄæķäđå,å æčŋäšäšåŪåįŧīæå ķäļčīįč§ååšåŪäļæĨ,éĩåūŠäļäđååįįļåį server å čĄéĄšåšã
čŊåŪĄæĩčŊčŪĄå
åĶä―éŠčŊ
čŊæŪ(åååŊđæŊ)
äļéįĻ:čŋæŊäŋæįĶįĻįčŪ°å―åĨįšĶ,æēĄæįĻæ·åŊč§įååãéŠčŊčūåš:
managed-agent-server:åŪæīįclean verify checkstyle:check:surefire 1625 äļŠæĩčŊ,0 åĪąčīĨã2 č·ģčŋ;Checkstyle 0 čŋč§;SpotBugs 0 éŪéĒãnpm run typecheckãESLint äļ Prettier:æ éŪéĒãæĩčŊåđģå°
įŊåĒ(åŊé)
åŠč·äšåå äļåĨįšĶæĩčŊ:core įĻ vitest,
managed-agent-serverįĻ H2(MySQL æĻĄåž)äļį Maven surefire,JDK 25ãæŽå°æēĄæč· MySQL/MariaDB éææĩčŊ,æŽåæīäđæēĄæčŋį§ŧãéĢéĐäļčåī
team_state:<n>ãteam_task:<n>ãteam_message:<n>äļteam_plan:<n>å―Ēåžįäšäŧķ id į°åĻäŋįįŧ Stage H čŪ°å―,äļæŊäļŠå·ēįŧčŪ°čŪ°å―ä―äļæ ·;æēĄæåå Ĩæđäš§įčŋäš id å―Ēįķãå ģč Issue
åąäš #13745(E1;E2 į§ŧå° Detach åįŧå·Ĩä―,E3 į§ŧå° H4e-b,å æĪčŊĨ issue äŋææåž)ãåąäš #12827ã