Repository navigation
fix(integrate_patch): end a patch set the tree already holds as already_present - #1845
okorzh-amd wants to merge 11 commits into
Conversation
9e8e41c to
fcbcb4a
Compare
chaojhou
left a comment
There was a problem hiding this comment.
Thanks @okorzh-amd. No blockers:
- A set ends
already_presentonly when every patch is fully present and nothing errored. - Both channels' reverse checks fail closed.
- The extracted
apply_patch_setmatches the old loop.
Two must-fix items:
-
The merged-PR-head exit skips the "round changes nothing else" check.
integrate_patch.py:2042-2043ends the attempt asalready_presentin_stage_resolve. That is before_stage_applyreadsconfig_changes,extra_server_args,extra_envs,artifactsor the enablement setup.changes_beyond_patches(:2419,:2704) guards only the patch-set exit.- Example: an
upstream_prtask for a merged PR with nodiff_url, which also carriesextra_server_argsto turn on the PR's opt-in switch. It endsalready_presentand writes the KB row, and the arg change is never measured. The same candidate with adiff_urlis benchmarked. - That contradicts SKILL.md ("ends the same way") and the description.
- Please take this exit only when the params carry no other change, and otherwise return the checkout-head failure as before.
-
The description predates the rebase onto #1858.
- Main no longer has a
git apply -3retry; #1858 removed it. So three statements describe nothing this PR changes: "runs the reverse check before the-3retry", "reports a committed patch present instead of three-way merging it" and "no longer goes throughgit apply -3". - The complexity numbers are pre-rebase. At head:
_stage_applyis cyclomatic 53→45, cognitive 131→110, and 513→470 lines;integrate_patch.pyis 4804→4703 lines;- the baseline edits lower 9 entries and remove 1.
- Please drop the
-3statements and refresh the numbers.
- Main no longer has a
CI has not run yet; the fork workflows are awaiting approval. In a local run, ruff, import-linter and the code-metrics checks pass, and pr-hygiene will warn at 825 production lines.
|
Gate job conclusion: success (run) on The report below was uploaded by that run; the conclusion above is the result. Code metrics gate: PASSEDJudged: the 22 files this change touches. Files it does not touch never fail it; anything found there is listed under Outside this change for information.
Module length warning: over 800 lines, within the 1200-line failure limit (not failing): 1
Already over at the base (backlog, not this change's): 3
50 findings in files this change does not touchOutside this change (informational): 50
...and 25 more; the job log lists every row.
Thresholds and sources: |
| _is_within, | ||
| ) | ||
| from ...enablement.lane import bind_integrate_to_lane | ||
| from ._patch_set_apply import _run_git_apply, apply_patch_set |
chaojhou
left a comment
There was a problem hiding this comment.
PR #1845 -- fix(integrate_patch): end a patch set the tree already holds as already_present
What it does: integrate_patch used to restart the server and benchmark candidates whose every patch the framework tree already held, because both apply channels reported "already present" as an ordinary success. The apply helpers now return a named PatchApplyResult carrying which reverse check found the patch present, the apply loop moves into _patch_set_apply.apply_patch_set, and an attempt whose patches are all present (or whose merged PR head matches the tree on its changed files) and that changes nothing else ends already_present with a KB row and no benchmark.
Blocking issues: 1
- [X2] The PR description still describes a
git apply -3path that exists at neither base nor head, and its complexity numbers are stale [verified]
Problem: The body says "The git channel runs the reverse check before the-3retry", that it "reports a committed patch present instead of three-way merging it", and that "a fully present patch no longer goes throughgit apply -3". The git channel,_git_apply_collect_feedback, has no-3path at base 1a444f6 or at head. The body also gives_stage_applycyclomatic 55 to 47 and the largest new function at 9; c901.py measures 53 to 41 and 7 at head. src/hyperloom/orchestrator/actions/executors/_patch_set_apply.py:95
Impact: Reviewers read an index-safety change and complexity figures that this PR does not have.
Action: Drop the-3statements, including the one in the opening paragraph, and refresh every figure in the size and complexity list against the current head.
Checked: integrate_patch.py _stage_apply, _materialize_upstream_pr, _RoundProposal.changes_beyond_patches, _end_already_present; _patch_set_apply.py apply_patch_set, _git_apply_collect_feedback; _nogit_patch.py _reverse_probe_levels; _patch_source_pr.py _empty_head_diff, _tree_has_head_files; patch_safety.py path_resolves_at; baseline.py _apply_warm_patches; consumers of already_present in framework/artifacts.py, phases/framework.py, loop/writeback.py; ci.txt | Ran: python ..\pr_tools\hl_fetch.py 1845; git grep for -3/3way at base and head; python ..\pr_tools\c901.py 1a444f6 510fddd on the changed src files; git log/diff --stat fcbcb4a..510fddd; gh run list --commit 510fddd after approving the fork runs (9 workflows success, the 2 label-gated E2E skipped) | Base: 1a444f6 | Head: 510fddd
chaojhou
left a comment
There was a problem hiding this comment.
PR #1845 -- fix(integrate_patch): end a patch set the tree already holds as already_present
What it does: integrate_patch restarted the server and benchmarked candidates whose every patch the framework tree already held, because both apply channels reported "already present" as an ordinary success. The apply helpers now return a named PatchApplyResult with present_by, the apply loop moves into _patch_set_apply.apply_patch_set, and an attempt whose patches are all present (or whose merged PR head matches the tree on its changed files) and that changes nothing else ends already_present with a KB row and no benchmark.
Blocking issues: 2
-
[S4] A drifted deletion-only patch in mail format still reads as present, so the round ends
already_presentwhile the file is untouched [verified]
Problem:_reverse_probe_levelsfalls back to every-plevel whenparse_patch_manifestraisesValueError. Agit format-patchdeletion (mail header before the diff) raises there, while the preflight parser reads it (patch_file_targetsgives[('a/mod/alpha.py', '/dev/null')], nothing missing). At a level where the stripped path is absent, the reverse check of the deletion passes. src/hyperloom/orchestrator/actions/executors/_nogit_patch.py:112
Impact: on a scratch repo whosemod/alpha.pydrifted from the patch's pre-image,_git_apply_collect_feedbackreturnsok=True, present_by=GIT_REVERSE_APPLYand the file is left as it was, soapply_patch_setreportschanged_nothingand the round writes analready_presentKB row for a change the tree does not have. This is the false positive the anchoring in this PR closes, reopened for mail-format input; the description says such a patch "now fails to apply".
Action: take the probe's paths from the parser the preflight already uses (patch_file_targets) and delete theValueErrorbranch; add a mail-format case to the drifted-deletion test. -
[X2] The description still describes code this PR does not contain, and its figures are stale [verified]
Problem: unchanged since the last round at this head. It still says "agit apply -3of a patch the tree already holds ...", "The git channel runs the reverse check before the-3retry", "reports a committed patch present instead of three-way merging it" and "no longer goes throughgit apply -3", though neither base nor head has a-3path; and it still gives_stage_apply"55 to 47, cognitive 140 to 119, 538 to 495", "4915 to 4780" and a largest new function of 9. src/hyperloom/orchestrator/actions/executors/_patch_set_apply.py:111
Impact: reviewers and the release notes read an index-safety change and figures this PR does not have.
Action: remove every-3statement, refresh the figures from the head, and drop "the preflight and the probe resolve paths the same way" (they use different parsers, item 1).
Design review (requested by the maintainer; beyond the blocking bar)
The aim of this section is a smaller PR, not more guards: each point says what to delete or merge.
Purpose, route, files. Purpose and route as above. Production: _apply_feedback.py (result types), _nogit_patch.py (anchored probe), the new _patch_set_apply.py (git channel and set loop moved out of integrate_patch.py), integrate_patch.py (_RoundProposal, two exits, _end_already_present), _patch_source_pr.py (head-diff result type, _tree_has_head_files), patch_safety.py (path_resolves_at), baseline.py (warm replay reads present_by), status consumers in artifacts.py and framework.py. Tests: test_patch_already_applied_unit.py, test_patch_source_pr.py, tuple-to-dataclass migrations. Docs: SKILL.md, the integrate_patch playbook, optimization-loop.md.
1. Completeness. The core exit agrees with its consumers: writeback journals already_present as no_promote and the enablement lane counts it as a stall; the description states neither. Apart from item 1, a set whose deletion upstream already made never reaches the probe, because the preflight ends it apply_failed/patch_target_missing; that is fine, say so.
2. Simpler / smaller. The merged-PR-head exit buys nothing at runtime. At base an empty merge-base..head diff without a diff_url already ended fetch_failed with no boot (base _patch_source_pr.py:288, :301); with a diff_url, base and head both fall back to the diff and apply_patch_set now decides. Its only effect is relabelling fetch_failed as already_present, and for that the PR adds PresenceCheck.PR_HEAD_FILES, _tree_has_head_files (which trusts discovery's changed_files and fails once any of them is touched after the merge), _empty_head_diff, PrMaterialization.already_present riding on failure, _materialize_upstream_pr, _RoundProposal, an async _stage_resolve_upstream_pr, and a second "changes beyond patches" decision. My first-round must-fix asked for that guard; dropping the exit is the better fix. Ride-alongs that belong in their own PR: the semantic_status cleanup and its test edits, the comment and codespell fixes.
3. Redundancy and silent failure. "Changes beyond patches" is decided twice from different inputs: integrate_patch.py:2122-2123 uses params["enablement"] as a stand-in for setup and runtime, :2462-2465 uses the measured setup_commands_applied and the attempt runtime. PrMaterialization encodes three states in two fields. _end_already_present writes patches_reverted: [] and also clears attempt.applied; one is enough. The ValueError fallback is item 1; git errors otherwise fail closed.
4. Docs and comments. Besides item 2: _patch_source_pr.py:211-212 says the caller returns the failure as is, but integrate_patch.py:2118-2124 may return already_present instead; SKILL.md, optimization-loop.md and _apply_feedback.py:26 say "every file the PR changes", while the code checks the candidate's changed_files.
5. Tests. Keep: the all-present exit (test_patch_already_applied_unit.py:426), all-present plus server args benchmarks (:609), a no-op riding with a real change (:625), merged PR via diff_url (:559), reverted PR (:593), the drifted deletion (:290), the present_by assertions and the mechanical migrations. Redundant or freezing the current shape: :213 (the -3 story, repeats :178), :230 (duplicates :625 one layer down), :273 (fold into :426 as a non-git parameter), :574 (same decision as :609), :328 (pins the fail-open fallback of item 1), and the PR-head-exit tests :500, :531 and test_patch_source_pr.py:139-200.
Starting from zero. The mechanism is worth having in its core form: the channels already prove presence with a reverse check; surface it as a field, end an all-present round that changes nothing else, write the KB row. That is PatchApplyResult, apply_patch_set (needed for the metrics gate and the import cycle), a probe anchored on the preflight parser, one exit in _stage_apply and _end_already_present -- roughly half of today's production diff, with _patch_source_pr.py and _stage_resolve_upstream_pr left as at base.
Change list, in order of impact:
- Fix the probe (item 1) and add the mail-format test; delete
:328. - Delete the merged-PR-head exit: revert
_patch_source_pr.pyand_stage_resolve_upstream_prto base, dropPR_HEAD_FILES,_materialize_upstream_prand_RoundProposal, and name the existing predicate once inside_stage_apply; delete its tests and doc sentences. - Trim the tests listed in point 5.
- Keep one encoding of "nothing applied" in
_end_already_present. - Move the
semantic_statuscleanup to its own PR. - Rewrite the description (item 2), and state the
no_promotejournalling, the enablement stall, and that a deletion already made upstream endspatch_target_missing.
Checked: _nogit_patch._reverse_probe_levels, _patch_set_apply._git_apply_collect_feedback / apply_patch_set, patch_safety.patch_file_targets / path_resolves_at, integrate_patch._materialize_upstream_pr / _stage_apply / _end_already_present and both changes_beyond_patches calls, _patch_source_pr at base and head, the tests named above, the description against the head | Ran: _git_apply_collect_feedback on a scratch git repo with a drifted mod/alpha.py and a git format-patch deletion (present_by=GIT_REVERSE_APPLY, file unchanged); no test suite | Base: 1a444f6 | Head: 510fddd
510fddd to
c81fdbe
Compare
…dy_present A patch set whose every patch reverse-applies cleanly was benchmarked as a new candidate. The apply helpers now return a named PatchApplyResult that says when a patch was already present, the apply loop moves into apply_patch_set with a PatchSetOutcome, and the executor ends such a set as already_present without a benchmark, writing OUTCOME_ALREADY_PRESENT through the framework-KB writeback. A reverse check counts only at a strip level where a file the patch names resolves, so a deletion whose target drifted is not taken as present. An empty checkout-head diff is not proof on its own, because a reverted PR keeps its head in the history: with a diff_url the reverse check decides, and without one the tree must match the head on every changed file. Critic priors and the phase summary count the new status, an upstream-PR result keeps its task id so the enablement lane treats it like every other upstream-PR result, the dead semantic_status mapping is removed, and the operator docs describe the outcome. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ort cycle apply_patch_set took the whole IntegrateAttempt, so _patch_set_apply imported _integrate_attempt. That module already reaches back to integrate_patch on main (through shared_state, gaps, collaborator, the coordinator and writeback), so the new module joined that cycle and CodeQL flagged both ends of it. apply_patch_set reads only the attempt's framework root and appends to its applied list, so it now takes those two. The list is still filled as each patch lands, so an interrupted stage still reports what reached the tree. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…en the round changes nothing else The upstream-PR lane ended a candidate whose head the tree already holds in _stage_resolve, before the round's server args, env, artifacts, setup and runtime were read. A task carrying any of them therefore ended already_present and wrote the KB row without measuring that change, while the patch-set exit in _stage_apply benchmarks the same round. The round's proposal (config changes, server args, safe env with the switch env, artifact specs) is now read by one function, and the "changes beyond patches" rule is one method on its result. _stage_apply and the PR-head exit both use it. The PR-head exit runs before setup replay and runtime provisioning, which happen only in an enablement round, so such a round counts as changing both. When the round changes more, the exit is not taken and the checkout-head failure (fetch_failed / checkout_head_failed) is returned, as on main: the evidence now rides on that failure instead of replacing it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…parser The probe read its paths with parse_patch_manifest and probed every -p level when that parser raised. A git format-patch file raises there (the mail header is a section with no target), so a drifted deletion in mail format passed the reverse check at a level where the stripped path is absent and was reported present with the file untouched. Read the paths from patch_file_targets, the parser the target preflight uses, and drop the ValueError fallback. The drifted-deletion test gains a format-patch case; the test that pinned the fallback goes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
An empty merge-base..head diff already ended fetch_failed with no boot when the candidate had no diff_url, and with one the round falls back to the diff, whose reverse check in apply_patch_set decides. The exit only relabelled fetch_failed as already_present, at the cost of a third PresenceCheck member, a head-files probe over discovery's changed_files, a second result field on PrMaterialization, an async upstream-PR stage and a second "changes beyond patches" decision read from params. Revert _patch_source_pr.py and _stage_resolve_upstream_pr to main, drop PresenceCheck.PR_HEAD_FILES, _materialize_upstream_pr, _RoundProposal and _read_round_proposal, and name the existing no-patches predicate once in _stage_apply as changes_beyond_patches, where both its uses read it. The exit's tests and doc sentences go with it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…s already present _end_already_present cleared attempt.applied and also wrote patches_reverted: [] into its result. _finish_attempt overwrites patches_reverted from attempt.applied for every non-KEEP status, so the literal was dead; the cleared list is the one encoding left. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…s they pin Drop the tests that repeat another one or freeze the current shape: the committed blob-id patch (the git superset/subset test already shows the reverse check), the one-no-op-in-a-set channel test (the executor's no-op-riding-with-a-real-change test covers it end to end), and the merged-PR diff_url test with server args (same decision as the all-noop set with server args). The channel-level all-present set test becomes a non-git parameter of the executor's already_present exit test. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The semantic_status mapping in _record_framework_agent_authoring_empty_outcome, its fixtures and tests, and the "unparseable" spelling fix in the same module are independent of ending a present patch set early, and move to a separate change. With them gone, adding already_present to the Critic priors set would be this change's only line in phases/framework.py, and the code-metrics gate refuses any growth of that module: main is at 3204 lines against a 3139-line baseline entry. The priors are review context only (candidate re-selection rides on the processed-candidate keys, which cover any keyed row), and they already leave out the other apply-stage ends, apply_failed and fetch_failed, so already_present stays out with them. The tested-outcome summary in framework/artifacts.py keeps counting it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…y exits Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…tity The framework KB writer records a candidate only when it carries an upstream PR identity (an upstream-PR URL or head SHA, or a framework proposal's fa_pr_url / fa_pr_sha). A specialist patch without one, or an enablement round, still ends already_present but writes no row, so the three places that describe the outcome now say when the row is written. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…rinks Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
c81fdbe to
945b0cb
Compare
Description: what and why
integrate_patchbenchmarked candidates the framework tree already held. Both apply channels can already tell that a patch is fully present (a cleangit apply -R --check, orpatch -R --fuzz=0 --dry-runwith no offset), but they reported that as an ordinary success. A patch set in which every patch was already present therefore went on to restart the server and run the benchmark, and an upstream PR that was already merged into the tree was graded as if it were new. On the git channel the check was also reached too late: agit apply -3of a patch the tree already holds succeeds without changing anything, so a diff that carries blob ids never reached the reverse check at all.OUTCOME_ALREADY_PRESENTexisted in the KB writeback, but nothing wrote it.Once "present" ends a candidate without a benchmark, a false "present" costs more than it did when it only benchmarked an unchanged tree. Two such cases are closed here:
a/mod/x.pyat-p0).merge-base..head) shows only that the PR head is in the live history. A PR that was merged and then reverted keeps its head in the history, while its change is gone from the tree.What changes:
PatchApplyResult(ok,error,backups,feedback,present_by) in place of their 3- and 4-tuples.present_bynames the check that found the patch present, as aPresenceCheckmember. Every caller is migrated, including warm-patch replay, which now readspresent_byinstead of inferring "already present" from an empty backup list.-3retry.path_resolves_at) is extracted frompatch_targets_missing, which now uses it too, so the preflight and the probe resolve paths the same way._stage_applyintoapply_patch_set, which returns aPatchSetOutcome. It lives in the new_patch_set_apply.py, together with the git-channel apply helpers it calls, which moved out ofintegrate_patch.py. A satisfied no-op inside a set that changes the tree still lands with the set, as before.status: already_presentwith the per-patch evidence and does not benchmark. It writes analready_presentrow through the existing framework-KB writeback.diff_url, the candidate falls back to it as before, and the apply's reverse check decides: a merged PR endsalready_present, a reverted one is applied and benchmarked.already_presentonly when the working tree matches the PR head on every path in itschanged_files, each of which must exist at the head. Otherwise it reportsfetch_failedas before.already_present.semantic_statusmapping in the empty-authoring recorder read a field that nothing has written since the per-candidate audit was retired. It is deleted, so that path stampsauthor_empty, which is the status it already produced in practice.SKILL.md,docs/conceptual/optimization-loop.mdand theintegrate_patchaction playbook.Linked issue(s): none
Tests: added and updated. (what to test; if a test was replaced, where its assertions live now)
orchestrator/actions/executors/tests/test_patch_already_applied_unit.py, each running the executor against a temporary git repo:upstream_prandspecialist_authoredlanes, endsalready_presentwith per-patch evidence. The benchmark is never invoked, HEAD and the working tree are untouched, and exactly one KB row with outcomealready_presentis written under a temporaryINFERENCE_OPTIMIZER_FA_KB_PATH.extra_server_argsreaches the benchmark and writes noalready_presentrow.diff_url, it endsalready_present(pr_head_files_in_tree) without a fetch and writes the KB row.diff_url, it falls back to the diff and endsalready_presentthrough the reverse check.git revert -m 1reaches the benchmark and writes noalready_presentrow.patchchannels:apply_patch_seton a tree that holds every patch reportschanged_nothing.apply_patch_setkeeps one subset no-op in the set.present_by.test_patch_source_pr.pypins the checkout-head mapping throughmaterialize_candidate_patches, on a repo whose PR head is merged with a merge commit:changed_filesmatch endsalready_present.fetch_failed, notalready_present.changed_filespath that does not exist at the head proves nothing.diff_url, an empty head diff falls back to the diff.test_framework_agent_selection_audit.pykeeps its base assertion: an empty diff with nothing to confirm it is a failure.test_nogit_patch_unit.py,test_integrate_patch_executor.py,test_delivery_channels.pyandtest_integrate_patch_helpers_unit.pyare migrated to the named result. The 4-tuple shape assertions are replaced by field assertions.semantic_statustests become one test that carries the idempotency assertion over, withauthor_empty. Fixtures that only setsemantic_statusare dropped.ruff checkandruff format --checkon the changed files: pass.lint-imports: 7 contracts kept.python scripts/code_metrics.py --base-ref origin/main: PASSED (11 baseline entries lowered and 2 removed in this change).pre-commit run --files <changed files>: codespell, reuse, gitleaks, import-linter and the CLI-reference hooks pass. Theruff (legacy alias)hook fails on any file on main (its config passes--force-excludetwice), andbanditreports the same Low findings per file as main.pyteston the touched test modules (orchestrator/actions/executors/tests,orchestrator/delivery/tests,orchestrator/enablement/testsand the touchedinference_optimizer/testsmodules): 3 failures, which fail the same way on main (two need TraceLens, one is intest_setup_script.py).pytest src/hyperloom/orchestrator src/hyperloom/inference_optimizer: the failures need TraceLens, rocprof-compute or multi-node tooling that is not installed locally, and they match main. The exception istest_aiperf_client_sh.py, a timing-sensitive suite that loads none of the changed modules. Its failing cases differ between runs on both trees: serially 3 on the branch and 1 on main, and a different set on each under-n 16.Size/complexity triggers crossed: split, or why the shape is right:
IntegratePatchExecutor._stage_applygoes down from cyclomatic 55 to 47, cognitive 140 to 119, and 538 to 495 lines.patch_targets_missinggoes down from cyclomatic 9 to 3.integrate_patch.pygoes from 4915 to 4780 lines.materialize_candidate_patchesgoes from 96 to 82 lines._stage_resolve_upstream_prand_git_apply_collect_feedbackdrop under 80 lines.patch_safety.pygrows by 6 lines to 1091, over the 800-line warning and under the 1200-line limit, as it already was.If this simplifies or refactors: which mechanism was removed, which contract was preserved, and the tests that show it:
_apply_patch_no_git,_git_apply_collect_feedbackand_materialize_pr_diff_from_head.semantic_statusbranch.integrate_patchre-export of_apply_patch_no_git, which no longer has a consumer there.patch_targets_missing, nowpath_resolves_at.test_patch_already_applied_unit.py,test_nogit_patch_unit.py,test_integrate_patch_executor.pyandtest_specialist_patch_safety_unit.py:retry_feedback.Observable effect: what an operator will now see, and what the old behaviour cost them (if none, why the change is unobservable):
integrate_patchwithstatus: already_present. The result carries analready_presentlist of{patch, check}rows, or one{diff_range, check}row for a merged PR head confirmed against its changed files. The FRAMEWORK progress row has statusalready_present, and the framework KBlessons.jsonlgets a row withoutcome: already_present.keptorrevertedverdict.diff_url, and still reportsfetch_failedwithout one, as before. It is never recorded asalready_present.git apply -3, so the index is no longer touched in that case.already_presentstatus now comes from the reverse check. Before, it came from an empty backup list; the two differ only for a patch that touches no file.Breaking changes: no. The changed signatures are private helpers whose in-repo callers are all migrated.
already_presentis a new status value and was already an allowed KB outcome.PR addresses single concern: yes. Three things ride with it:
integrate_patch.pyshrinks; it is over the module-length limit and may only go down._nogit_patch.py, the existingerrors="replace"literal is named as a module constant, because the new read made it a repeated literal under the metrics gate.Root cause is upstream (Magpie/TraceLens/GEAK/IntelliKit/AgentKernelArena), ticket filed: no, the cause is in this repo.