Repository navigation
[TRTLLM-16460][feat] agent-flow: unified trtllm_modeling_v2 workflow for gated ModelingV2 onboarding and perf tuning - #20907
Draft
Wanli-Jiang wants to merge 12 commits into
Conversation
Wanli-Jiang
force-pushed
the
user/williamj/trtllm-modeling-v2-unified
branch
2 times, most recently
from
October 24, 2026 07:56
989fa12 to
352b030
Compare
1 task done
WeiHaocheng
reviewed
Oct 26, 2026
| # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| """What onboarding a checkpoint knows: the survey, the vocabulary, the runtime map, delivery.""" |
Collaborator
There was a problem hiding this comment.
The scripts under onboard/, run/, spec/, and tune/ look like agent-generated tools from bringup. It might be hard for agents to actually reuse them later under this layout. I suggest moving them into a unified tools/ directory with a README documenting their capabilities. Some also seem quite bespoke to specific edge cases—we should review them to see what's genuinely worth keeping.
Wanli-Jiang
force-pushed
the
user/williamj/trtllm-modeling-v2-unified
branch
2 times, most recently
from
October 27, 2026 03:19
a1468b5 to
9c94391
Compare
…dbox a site allows What the trtllm_modeling_v2 workflow needs from the agent backends: - A session can start as a fork of an earlier one (SessionConfig.fork_from): the Claude Code backend resumes it with fork_session, the Codex backend with the app server's thread/fork. A turn's result carries the backend's session id, and the session layer keeps the last one. - The Claude Code backend launches the CLI $AGENT_FLOW_CLAUDE_CLI names, else the claude on PATH when it is newer than the one the SDK bundles: an organization can refuse an older CLI at start-up. - The Codex backend launches $CODEX_BIN, else the codex on PATH when it is newer than the pinned runtime; a session given no model or reasoning effort runs what Codex is configured with. - A Codex session asks for the most open sandbox and approval policy the site's requirements allow (configRequirements/read); under workspace-write, BackendConfig.writable_roots are writable besides its cwd. The session reports what it runs under (SessionInitEvent.sandbox, approval_policy). - The Codex app server is started again when it exits as it starts: several starting at once collide on Codex's shared state database. Signed-off-by: Wanli Jiang <35160485+Wanli-Jiang@users.noreply.github.com>
…job's process group - Node commits and merges in git worktrees are signed off (commit -s, merge --signoff), for repositories that require DCO. - Cancelling a local job sends SIGTERM to its whole process group, so the commands it started stop with it; a pid that leads no group gets the signal alone. Signed-off-by: Wanli Jiang <35160485+Wanli-Jiang@users.noreply.github.com>
…d spec The spec a run starts from, validated before any agent is constructed: - spec/task_schema.py: task.yaml with closed keys and every error reported at once: the checkpoint, units and features (FEATURE_FAMILIES), parallel members, accuracy anchors, workloads and floors, runtime blocks, the launch environment and sessions, the commit policy, the source embargo, agents, a budget and the perf block. `profile` (smoke, bringup, standard or certified), for the run or per unit, sets how much each unit owes; `coverage.accept` names requested cells the run may leave uncovered; `accuracy.anchor: pending` makes the logit reference the oracle until an anchor is written. A disaggregated unit's witness, its only proof, is owed and its leg B required whatever the witness switch says. - spec/variants.py: configuration variants, a unit's target served under another configuration, with checks of their own. - spec/derive.py: the fast path. --checkpoint, --site and --goal derive task.derived.yaml from config.json, the cluster's site.yaml and a one-line goal (the module docstring lists its words), beside derive.report.md, which says where each unstated value came from. A goal word sets a feature's depth (`multimodal:smoke`), and a disaggregated unit goes on members whose two legs fit one node. What cannot be derived is an open question, answered in that file. - task.example.yaml documents every key and its default. Signed-off-by: Wanli Jiang <35160485+Wanli-Jiang@users.noreply.github.com>
… sessions and spools Where commands run, and how commits are judged: - infra/environment.py: the launch environment, resolved once into the command prefix every role's prompt carries. - infra/git.py: questions to git, asked with an argv and no optional locks, and the commit rules: a trial `git commit -s` proves the run's identity, and audit_commits checks a branch's sign-offs, identity, co-authors, symlinks, sandbox placeholders and paths. - infra/gpu/: a compute session is a one-node Slurm allocation whose main process is the spool scheduler (scheduler.py; its docstring is the protocol): device slots with wall limits, priority classes, a scrubbed MPI environment, devices quarantined until their memory is released, a job lost with its allocation queued again, and a hold while the native libraries are rebuilt (rebuild.py, incremental_build.sh). spoolctl.py submits, launches, waits on and cancels jobs, also by path from a shell; executor.py and placement.py run a command here or send it to the spool that finishes it soonest; sessions.py renders the sessions' entry scripts and their hand-over; provenance.py and the resolve hook record which checkout each process imported, so a job that mixed checkouts is refused. - infra/driver/: the operator loop. `driver wait` returns when the orchestrator exits or a session needs a hand, and `driver status` shows both, read from the orchestrator's heartbeat and exit marker. Signed-off-by: Wanli Jiang <35160485+Wanli-Jiang@users.noreply.github.com>
…n and delivery checks What onboarding knows before and after the agents run: - onboard/survey.py: what this build supplies, read off disk without importing the engine (surface.md): catalog entries certified here, not yet run here, kept by another target, or whose implementation does not resolve; trtllm ops no entry wraps; the installed FlashInfer's public API. - onboard/vocabulary.py: vocabulary.yaml, the surveyor's product: each computation step mapped to a catalog entry, an entry to certify at new values, or a gap to onboard from the cheapest source. - onboard/promotion.py: kernel tasks on one shared entry land as target-local variants, so they run side by side; one promote task merges them into the catalog. - onboard/delivery.py: the agents' claims checked against disk: the target, its routing, entries and tests, the accuracy reference and test-list entries, all committed, and commit hygiene. - onboard/logit_reference.py: the HuggingFace reference's per-position predictions, the logit gate's oracle. Signed-off-by: Wanli Jiang <35160485+Wanli-Jiang@users.noreply.github.com>
…arness Checks are commands with exit codes, never an agent's judgement. Every gate measures the target under TRTLLM_MODELING_V2=require and attests the engine it ran: - checks/gates.py: boot (the declared workload judged on the generated text and, with a logit reference, its first greedy tokens, decoded as one batch), accuracy (trtllm-eval against the anchor, above an anchor - k*SE floor, with a finding when every member shifts) and acceptance (a speculator's acceptance length against the stock path and the last passing tree). - checks/witness.py: what boot and accuracy cannot see (disaggregated equivalence, a quantized KV cache, host offload, bounded replay), as two legs that differ in one variable; the equivalence witness's second leg is a launcher the spec supplies, held to an ablation leg and rank pairs. - envelope_boot.py, logit_agreement.py, serve_gate.py: the target at its declared limits, log-probs position by position against the HuggingFace reference, and trtllm-serve answering. These and the witnesses are owed only when the spec turns them on, as the certified profile does. - checks/observables.py: per feature, what a check must observe and the oracle it is judged by. - checks/harness/: the programs gate jobs run under the measured tree's interpreter; the rank hook makes every rank's worker print its engine record and the modules it imported. - gate_cli.py: `trtllm-modeling-v2 gate <name>`, one gate run exactly as the orchestrator runs it. testcache.py: per-test results for an exact clean commit and environment, with one pytest process per file. Signed-off-by: Wanli Jiang <35160485+Wanli-Jiang@users.noreply.github.com>
…d the entry skill - agents/roles.py: five roles (planner, surveyor, kernel_writer, assembler, tuner), each on the agent that started the run (Codex inside a Codex session, else Claude Code) unless the spec's agents block routes it. - agents/decisions.py: every role's decisions, when to write each and what follows; each prompt's ending, the enum decision files are validated against and the orchestrator's finishing set are read from it. - agents/prompts/: one worker prompt every role shares, ending with its role's decisions; a task's tags (its kind, then its `tags`) add their sections (tags.py). Prompts carry the launch prefix, the embargo and the spec's notes, and point at the code rather than restate it. The planner's prompt holds its contract and scheduling rules (a goal cites evidence and operator messages by path), the surveyor's its survey. - agents/turns.py: one in-process agent turn and its records; a turn an earlier process answered is adopted for the same task attempt. agents/slots.py: one cap on the agents working at once, slots granted by priority (planner, critical path, required, side work, a campaign). - agents/skills/trtllm-modeling-v2-entry: the procedure for writing or changing a ModelingV2 catalog entry, copied into each run's workspace. Signed-off-by: Wanli Jiang <35160485+Wanli-Jiang@users.noreply.github.com>
…tegration and records - run/contract.py: every check a run owes, compiled from the spec in three tiers (inner, proof, delivery), as each unit's profile asks: a smoke unit boots, a disaggregated unit owes its equivalence witness alone, and with a logit reference a unit's benchmark runs at one member (attention DP first) while the logit gate and the boot's greedy tokens prove the others. A verdict is recorded under a fingerprint of what its check read, narrowed to the modules its processes imported, and holds while that is unchanged; checks that run one command against one criterion share a pass, and a verdict on other inputs is stale. - run/requirements.py: the request as a feature x member matrix (coverage.yaml); a cell nothing covers stops preflight unless the spec, or its profile, accepts it. - run/graph.py: the task graph, the planner's only output: kinds, hard and soft dependencies, the files a task touches, exclusive and nice_to_have tasks, attempts, check tasks, and which ready tasks start. A goal the planner writes is at most 2,000 characters. - run/integration.py: one sparse worktree per task, audited, rebased onto the checkout and fast-forwarded in, so the checkout moves only by whole tasks; a conflict or a commit that breaks the rules goes back to its worker. - run/snapshots.py: read-only snapshots of the checkout's commits, the trees checks measure. - run/progress.py, receipts.py, runtime_facts.py, journal.py, state.py, metrics.py, inbox.py: decision files (the only channel an agent's verdict reaches the orchestrator through), what a finished task hands its dependents, what passing gates' engines ran, the journal, the checkpoint, the run's metrics and the operator's messages, a burst of which reaches one planner turn. Signed-off-by: Wanli Jiang <35160485+Wanli-Jiang@users.noreply.github.com>
…loop and the workflow - run/preflight.py, run/stages/preflight.py, run/agent_probe.py: what must hold before an agent is worth starting: the checkpoint against the spec, the host and launch settings, the coverage matrix, an agent probe (each agent writes through Bash and does not run read-only), the survey and the contract. The substrate gate runs before the planned loop; a failure ends preflight before workers start. - run/orchestrator.py: the graph-driven loop. Tasks start as agent slots, devices and their files allow; the planner runs on its own thread, woken by events, a green finish within 15 minutes; a finished task's checks run on a shared pool, at most 32 in flight, queued by the sessions' schedulers, and a stale benchmark runs again only at a milestone. The loop adds the survey's kernel tasks, promotes and regression checks (run/regression.py: the full declared test list at HEAD, failing tests at the last cleared commit, with no agent turn). Engine-defect reports reach the planner through the failed-receipt event. - run/workflow.py, run/stages/: the run's lifecycle and the services the loop calls: turn dispatch, primers (run/primers.py: a briefing session each later turn of its role forks), checks on snapshots, the milestones (<branch>-bringup, -correct, -best, cells.json) and delivery, which runs the delivered tests at the integrated HEAD. - A run resumes from its workspace: the tasks that were running start again first, an answered turn is adopted, and the checks and rebases a stop cut short run again. An interrupt waits for none of the work in flight. Signed-off-by: Wanli Jiang <35160485+Wanli-Jiang@users.noreply.github.com>
Makes a correct target faster without making it less correct: the tuner proposes, Python decides. A `tune` task of the planned run (perf.enabled, the goal word `tune`) or `trtllm-modeling-v2 tune` on a delivered target; it never gates delivery and is never merged into it: - tune/stage.py: the checks (a keep gate with an anchor, a short serve per world size), ground truth (the baseline, its repeats as the noise floor, the stock reference, optional speed-of-light ceilings), the campaign and its wrap-up, bounded by perf.campaign.max_hours. - tune/campaign.py, worktree.py: one hypothesis per tuner turn and worktree, measured against a same-host control; winners stack on the best tree. A blocked candidate counts as a campaign miss. - tune/records.py, keep_gate.py, inert.py, split.py: a win clears the tree's own noise on a headline metric, loses nothing beyond it, keeps the first token within perf.campaign.ttft_tolerance, and passes the contract's accuracy gate and the target's tests; a member the change cannot touch is not judged, and a loss confined to one parallel mode is split into a member-gated variant. - bench.py, measure.py, sol.py, serving_config.py: the serving sweep on idle nodes, its jobs, speed-of-light ceilings, and the serving config derived from the workload. - UNMERGED_OPTIMIZATIONS.md lists what the campaign kept, for a human to merge. Signed-off-by: Wanli Jiang <35160485+Wanli-Jiang@users.noreply.github.com>
`trtllm-modeling-v2 --task task.yaml --workspace W` runs and resumes a run; --checkpoint, --site and --goal derive its spec first. The subcommands are tune, gate, tests, spool, session, driver, feedback and metrics. Before the run the CLI proves the commit identity with a trial commit and exports it to every agent, takes the workspace lock, starts the heartbeat and turns Claude Code's auto-memory off (every worktree of a repository shares it); on Ctrl-C it leaves at once, its exit marker written. pyproject.toml adds the entry point and the package data a run reads at runtime: the example spec, the entry skill, TROUBLESHOOTING.md, the session scripts and the two import hooks. Signed-off-by: Wanli Jiang <35160485+Wanli-Jiang@users.noreply.github.com>
…d troubleshooting - README.md: the quick start, the documents, how a run works, and every flag and subcommand; test_package_hygiene keeps its flags table in step with the CLI. - docs/user-guide.md: operating a run, from the prerequisites and the spec to compute sessions, watching and steering a run, tuning, and one gate run standalone. - docs/design.md: how the harness works and where to change it. - TROUBLESHOOTING.md: infrastructure failures that read as model failures, keyed on the verbatim error text. The repository's AGENTS.md lists the commonest and points at it; agent-flow/README.md names the workflow. Signed-off-by: Wanli Jiang <35160485+Wanli-Jiang@users.noreply.github.com>
Wanli-Jiang
force-pushed
the
user/williamj/trtllm-modeling-v2-unified
branch
from
October 27, 2026 05:55
9c94391 to
b3bf8b4
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
@coderabbitai summary
Description
trtllm-modeling-v2is an agent-flow workflow that takes a HuggingFace checkpoint, a clustersite.yamland a one-line goal, builds the model as a ModelingV2 target in TensorRT-LLM, proves it correct at every requested parallel configuration, and can then tune its serving performance. Agents write the code; whether something is done is decided only by Python checks (boot, accuracy, acceptance, and opt-in witnesses) on the measured tree.This PR unifies #20145 (the gated onboarding workflow, used as the base) with the parts of #20792 (the modeling-v2-onboard planner/worker workflow) that the merge plan kept.
What a run does
--task task.yaml, or--checkpoint,--siteand--goal, which derivetask.derived.yamlin one step; an open question stops the command before the run starts and is answered in that file.<branch>-correctonce every required check holds on one commit,<branch>-best,cells.json,metrics.json.UNMERGED_OPTIMIZATIONS.md, never merged into the delivered target.Running it from Claude Code or Codex
Started from a Codex session (an interactive one or a
/goal), the orchestrator runs every role on Codex, on the model that Codex is set to and with the newer of itscodexand the pinned runtime; the run copies its skills where Codex finds them (<workspace>/.agents/skills). In-process Codex roles now fork primers through native app-serverthread/fork, with a saved-summary fallback when a fork fails. Unspecified Codex reasoning effort and context-window settings retain native defaults. Each role session asks for the most open sandbox and approval policy the site's Codex requirements allow (full access where allowed, elseworkspace-writewith the checkout and spools writable, andon-requestapprovals the orchestrator grants), records what it actually ran under, and the agent probe stops the run if Codex would run the roles read-only. The Codex session starts the orchestrator detached, outside its sandbox (user guide, Starting a run). Verified on a ChatGPT-login Codex: the workflow's agent probe passes from inside a Codex session with noagentsblock, and a Codex role loads the entry skill.Taken from #20792
The operator inbox (
trtllm-modeling-v2 feedback), job priority classes (accuracy, then feature, then perf), primer sessions (on by default,--no-primers), the unmerged-optimizations list, and agent-flow fixes (session fork, DCO sign-off on node commits and merges, cancelling a local job's whole process group).Changes since #20145 worth a reviewer's attention
contrast_commandandcontrast_env), the manualperfsubcommand,nsys_summary.pyandmicrobench.py, near-miss stacking, the spool's memory-pressure kill and the external driver-heartbeat check, and the external agent transport (--agent-transport externaland the driver's turn hand-out): every role turn runs in process, and the agent probe proves each agent can take a turn there. A kernel writer'sengine_defectreport reaches the planner in the failed-receipt event; the planner plans its repair.progress.yaml. A missing answer cannot fall back to an earlier attempt's decision; kernel writers use the same dispatch/adoption path as other tasks.Backend recovery and task policy
depends_ongraph; unresolved blockers with no runnable remedy use the existing escalation path.AgentLayer; changed agent routing invalidates primers. Terminal records retain reported model/effort, elapsed time and outcome.Codex roles under site policies, and runs that keep going
From a real Codex-driven onboarding run (25 h to bring-up, stopped by the operator), the general fixes:
danger-full-access/neverunconditionally. On a site whose/etc/codex/requirements.tomlforbids them, Codex silently ran every role read-only (105Read-only file systemfailures in 91 of 483 sessions). Sessions now ask for what the requirements allow, record the effective sandbox and approval policy in each turn's.done, and the agent probe fails a session that would run read-only.MAX_ATTEMPTS.ESCALATEno longer ends the run while other work can move: the work that does not wait on the answer goes on, and the run endsescalatedonce nothing runs and nothing new came in.EDQUOT,ENOSPC) no longer kills the loop: the state stays in memory and is written again each pass, and nothing new starts until it lands.Run-specific fixes from that run (sparse worktrees for an inode quota, per-unit check sessions for a two-node topology, fixed-length perf validation) are not included.
A leaner harness
The aim is the smallest harness that still decides everything a run needs. A third pass removes duplicated state, redundant dispatch paths and optional scheduler controls while keeping tune and the two agent-flow core changes in this PR. The third pass reduces the workflow package (including docs) from 54,255 to 49,346 lines (−4,909), and its tests from 10,974 to 10,551 (−423), relative to the preceding PR head.
git commit -sin their own worktree; the lockingcommit.shhelper and--no-task-worktreesare gone. The CLI exports the run's identity for every agent, and before a task is integrated its commits are audited (sign-off, identity, no co-author, symlink, sandbox placeholder or path outsidecommit_policy.allowed_paths); a commit that breaks a rule goes back to its worker like a rebase conflict.--manage-sessions), multi-node sessions andsession status/session watchdogare gone;driver statusanddriver waitcover the last two.scheduler.watcher_records) replaces six. Checks are not held in the orchestrator until a session reads as free: at most 32 are in flight on sessions, and each session's scheduler queues the rest by fit, class and age. Overflow to other sessions, lending tune dev sessions and moving queued jobs between spools are gone.environment.py,git.py(git questions and the commit rules),gpu/(sessions, spools, the scheduler and who runs a command) anddriver/(the operator loop and the orchestrator heartbeat); the turn executor and agent slots are inagents/.runtime_index.yaml,kernel_sources.yamlandruntime_map.pyare gone. Workers read the code (the repository'sAGENTS.md, grep), and the survey measures only what the build supplies (catalog entries and trtllm ops no entry wraps); the surveyor reads the checkpoint's demands itself.spec/derive.pyturns the checkpoint,site.yamland the goal intotask.derived.yamlbesidederive.report.md, which says where every unstated value came from; a goal's<feature>@<members>writes the members it leaves out intocoverage.accept. The intake package (compile, freeze and confirm,--confirm, theintakesubcommand,INTAKE.md) is gone. What the checkpoint cannot meet (heads or experts that do not split, fp4 below sm_100, missing draft layers, a multimodal unit without a tower) is a preflight check,check_checkpoint_fit, for derived and written specs alike.examples/and thepackage:embargo entries are gone; which units owe acceptance or a witness is read fromtask_schema.SPECULATORSandwitness.WITNESS_KINDS.task.example.yaml, each checked against the code.Fewer, cheaper checks
A Codex-driven onboarding run owed 43 checks and ran 59 verdicts for them, 31 of them re-runs after a landing changed the code; its 19 full GSM8K runs (up to two hours each on a target not yet tuned) took about 21 h, three quarters of all check time. Its spec could not say what its notes asked (a multimodal smoke test; TP4/TEP4 text-only), so 15 h went to vision numerics. Now:
profilemay be set per unit, with a newsmokelevel (the unit's boot alone); the goal says it asmultimodal:smoke(or:full, thecertifiedprofile).disaggunit and its variants owe the equivalence witness alone, always, through the spec's leg B: its boot and accuracy had run an aggregated engine. A derived spec puts it on members whose two legs fit one node (device.gpus_per_node); the units before it prove the wider members. A benchmark through the hop needs a framework leg ontrtllm-serve's disaggregated serving, which is not done yet.--event-window900 s); failures, reports and an empty graph still wake it at once. A burst of operator messages reaches one turn. A goal the planner writes or changes is at most 2,000 characters: evidence and operator messages are cited by path.A spec with that run's stated scope now compiles to 20 checks, 3 of them benchmarks (43 and 13 before).
Third-pass behavior changes
record_atcall first runs one lightweight environment-probe job, including cache hits; probe failure is incomplete, never a cached pass. No GPU timing claim is made for the full list.delivery-failed; restarting lets the planner read that failure and plan a repair. Tests without recorded results do not block the earlier correctness milestone.SESSION_ENDING, not a separate request-file protocol. Removed host-memory admission, per-job memory limits, configurable rerun counts and manual--pick; automatic gate placement remains.BLOCKEDand count as campaign misses. The tune session-staleness threshold now shares the 180-second watcher threshold.Commits
The change is split into 12 commits, one per layer, each holding its files in their final state with their tests, so each layer can be reviewed on its own. The layers import one another, so only the last commit is meant to build and pass the tests.
Known gaps
Test Coverage
agent-flow/testsCPU suite at the head: 1,982 passed, 3 skipped, in 91.58 seconds; live backend checks are skipped. Both pre-commit configs are clean on the changed files. The tests keep only what guards a workflow decision; besides the layers' own, they cover the integration-time commit audit, the trial commit, the derived spec, the checkpoint-fit check and stopping on Ctrl-C. The spool scheduler and client run by path from a shell.workspace-writerequest with the writable roots andon-requestapprovals, and the session records them; the site-policy read is covered by CPU tests, not yet exercised on the cluster whose policy motivated it.pre-commit -awith the agent-flow configuration also traverses unrelated repository files and fails on existing spelling/lint issues; its unrelated automatic edits were reverted. Full repository lint/CI success is not claimed.PR Checklist
Please review the following before submitting your PR:
PR description clearly explains what and why. If using CodeRabbit's summary, please make sure it makes sense.
PR Follows TRT-LLM CODING GUIDELINES to the best of your knowledge.
Test cases are provided for new code paths (see test instructions)
If PR introduces API changes, an appropriate PR label is added - either
api-compatibleorapi-breaking. Forapi-breaking, includeBREAKINGin the PR title.Any new dependencies have been scanned for license and vulnerabilities
CODEOWNERS updated if ownership changes
Documentation updated as needed
Update tava architecture diagram if there is a significant design change in PR.
The reviewers assigned automatically/manually are appropriate for the PR.
Please check this after reviewing the above items as appropriate for this PR.
GitHub Bot Help
To see a list of available CI bot commands, please comment
/bot help.🤖 Generated with Claude Code