Skip to content

docs(vllm): correct worker-role flags and document --kv-transfer-config - #12568

Merged
dagil-nvidia merged 2 commits into
ai-dynamo:mainfrom
glamr-agent:docs/fix-vllm-config-reference-dyn-3726--84c3e7f114cc
Aug 3, 2026
Merged

docs(vllm): correct worker-role flags and document --kv-transfer-config#12568
dagil-nvidia merged 2 commits into
ai-dynamo:mainfrom
glamr-agent:docs/fix-vllm-config-reference-dyn-3726--84c3e7f114cc

Conversation

@glamr-agent

@glamr-agent glamr-agent commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Overview:

docs/fern/pages/reference/backends/vllm-configuration.mdx documented two CLI flags
the vLLM backend no longer registers, and did not document --kv-transfer-config at
all. A reader following the page would pass --is-prefill-worker or
--is-decode-worker and hit unrecognized arguments at worker startup, and would
find no guidance on the flag that a prefill worker actually requires.

This corrects the page. Documentation only — one .mdx file, no code change.

Details:

Removed, both under ## Deprecated / ## Validation rules:

  • The ParamField blocks for --is-prefill-worker and --is-decode-worker. Neither
    symbol appears in components/src/dynamo/vllm/backend_args.py or
    components/src/dynamo/vllm/args.py; both were removed from the vLLM backend in
    1.4.0.
  • The two ## Validation rules bullets describing how those flags interacted with
    each other and with --disaggregation-mode. The code paths raising those
    ValueErrors went away with the flags.

The ## Deprecated heading and its third ParamField (--model-express-url) are
kept — that flag is still registered and still deprecated. The surviving
--embedding-worker validation bullet is untouched.

Added a ## Native KV transfer configuration section covering
--kv-transfer-config, placed after the existing ## Native KV event configuration
section. It is prose plus a bash fence rather than a ParamField, mirroring how
the page already handles --kv-events-config: the page's opening <Note> scopes it
to Dynamo wrapper flags and ## How the config is loaded promises every field is
both a CLI flag and an environment variable, and neither holds for a native
AsyncEngineArgs field. Filing it as a peer ParamField would have contradicted
both statements and left the only ParamField on the page without an
Environment variable: line.

Requiredness is scoped to prefill alone. The guard at
components/src/dynamo/vllm/args.py:216-231 tests
disaggregation_mode == DisaggregationMode.PREFILL exactly, so agg, decode, and
encode do not enforce it; the new text names those three modes explicitly so the
scope cannot be misread. The example value
{"kv_connector":"NixlConnector","kv_role":"kv_both"} is taken from
examples/backends/vllm/launch/disagg.sh.

Two smaller edits follow from the new section: a cross-reference from the
--disaggregation-mode ParamField body to the new anchor for prefill readers,
and a widened carve-out in the intro <Note> so its exception covers both native
sections rather than only the KV event one.

Where should the reviewer start?

docs/fern/pages/reference/backends/vllm-configuration.mdx is the only changed file.
Start at the new ## Native KV transfer configuration section and check its scoping
claim against components/src/dynamo/vllm/args.py:216-231; that is the one sentence
in the diff where a factual error would matter. The deletions are mechanical — a
grep for is_prefill_worker / is_decode_worker under components/src/dynamo/vllm/
returns nothing.

Validation

  • pre-commit run --files docs/fern/pages/reference/backends/vllm-configuration.mdx --hook-stage manual — passed. codespell, case/merge-conflict checks, mixed line
    ending, and trailing whitespace all pass; Python/Rust/JSON hooks skip with no files
    to check. No hook modified the file.
  • pytest components/src/dynamo/vllm/tests/test_vllm_unit.py -k "kv_transfer_config or prefill_worker"2 passed, 120 deselected. Corroboration, not new coverage:
    this diff changes no code, so there is no regression to demonstrate. The runs show
    that the behavior the new prose describes is true of main today —
    test_prefill_worker_without_kv_transfer_config_raises asserts
    pytest.raises(ValueError, match="--kv-transfer-config") for
    --disaggregation-mode prefill with the flag absent.
  • Static inspection: no body # H1 (Fern frontmatter intact), SPDX header unchanged,
    language-tagged fence, "vLLM" casing, and the diff reverse-applies cleanly onto
    main.

No new test is added. A docs-only diff admits no behavioral test that is not either a
mirror of the file's literal text or a duplicate of test_vllm_unit.py:282, which
already covers the single claim the new text makes.

docs/fern/scripts/check_reference.sh was deliberately not run: it needs the fern
CLI, which is absent here, and its broken-links step self-skips with a warning when
the CLI is missing — running it would have produced a misleading green rather than
evidence. CI owns that gate.

Related Issues

🚫 This PR is NOT linked to an issue:

  • Confirmed — no related issue

Open in Devin Review

Summary by CodeRabbit

  • Documentation
    • Added guidance for configuring native KV transfer in vLLM, including prefill worker requirements and NIXL examples.
    • Documented connector consistency requirements and the removal of the legacy connector option.
    • Updated disaggregation-mode guidance and clarified supported KV event and transfer configuration sections.

The vLLM configuration reference still documented --is-prefill-worker and
--is-decode-worker as deprecated-but-accepted flags, and repeated them as
validation rules. Both were removed from the vLLM backend in 1.4.0
(components/src/dynamo/vllm/backend_args.py no longer registers either),
so a reader following the page hit "unrecognized arguments" at startup.

Remove the two obsolete ParamField blocks and the two validation bullets
that describe them. --model-express-url remains under ## Deprecated, so
the heading and its intro stay.

Add a ## Native KV transfer configuration section for --kv-transfer-config,
which components/src/dynamo/vllm/args.py:216-231 makes mandatory for
--disaggregation-mode prefill. It is a native vLLM AsyncEngineArgs field,
not a DynamoVllmConfig field, so it has no DYN_VLLM_* environment variable;
the section mirrors the existing ## Native KV event configuration precedent
rather than adding a ParamField that would contradict the page's scoping
note. The requiredness is scoped to prefill only -- agg, decode, and encode
do not raise. Also note that --connector is now a hard error.

Signed-off-by: svc-glamr@nvidia.com <svc-glamr@nvidia.com>
@glamr-agent
glamr-agent requested a review from a team as a code owner August 3, 2026 15:10
@copy-pr-bot

copy-pr-bot Bot commented Aug 3, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@glamr-agent
glamr-agent temporarily deployed to external_collaborator August 3, 2026 15:10 — with GitHub Actions Inactive
@glamr-agent
glamr-agent temporarily deployed to external_collaborator August 3, 2026 15:10 — with GitHub Actions Inactive
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Aug 3, 2026
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot added docs external-contribution Pull request is from an external contributor labels Aug 3, 2026
@glamr-agent

Copy link
Copy Markdown
Contributor Author
Automated evidence record — validation complete

Validation status: complete

Evidence summary: [4/4 validated]

AI review assessment (advisory — not a merge authorization; CI and a human
reviewer hold that): sound.
A single-file documentation correction. The reviewer
independently re-derived the load-bearing factual claim from source rather than
accepting the validator's word: the new prose scopes requiredness to prefill
alone, which is the single trap in the source issue's wording. Three nits raised,
none blocking — two prose imprecisions elsewhere on the page (## Deprecated intro
now overstates the behavior of its one remaining flag; the --connector sentence
overgeneralizes, since two of four raise sites emit no equivalent value) and one
process note (a code-inspection probe script under /tmp was not preserved in the
packet; the underlying claim was discharged independently by reading
components/src/dynamo/vllm/args.py:216-231).

Validation result: pass. All three planned recipes (01, 03, 05) plus prerequisite
Recipe 0 ran green in-sandbox with zero dispositions. Lint proves the file is
mechanically clean; static inspection proves every added sentence against its source,
including that requiredness is correctly scoped to prefill alone; and the two
corroborating unit tests prove the one behavioral claim is true of main today.

Evidence audit: complete [4/4 validated] — the evidence table is grounded in
recorded runs.

Evidence [4/4 validated]

Generated from validation/registry.jsonl — do not edit by hand.

Recipe Status Command Evidence Note
00-dynamo-editable-install validated bash -lc 'set -euo pipefail; cd /home/sandbox/workspace/wi-20260803T141049Z-unscoped/repo; echo "cargo target: ${CARGO_TARGET_DIR} (home: ${CARGO_HOME})"; test -x /opt/dynamo/venv/bin/python; source /opt/dynamo/venv/bin/activate; python -m pytest --version; python -c "import pydantic; print(\"pydantic OK\", pydantic.VERSION)"; maturin --version; uv --version; dynamo-prepare-python-validation /home/sandbox/workspace/wi-20260803T141049Z-unscoped/repo; python3 -c "import dynamo, dynamo._core; print(\"dynamo OK at\", dynamo.__file__)"' validation/logs/2026-08-03T14-32-25.775Z-bash-5d2b.log
01-python-lint validated bash -lc 'set -euo pipefail; cd /home/sandbox/workspace/wi-20260803T141049Z-unscoped/repo; /opt/dynamo/venv/bin/pre-commit run --files docs/fern/pages/reference/backends/vllm-configuration.mdx --hook-stage manual' validation/logs/2026-08-03T14-33-04.570Z-bash-638d.log
03-python-unit-tests-mocker validated bash -lc 'set -euo pipefail; cd /home/sandbox/workspace/wi-20260803T141049Z-unscoped/repo; git status --porcelain; echo "--- tree clean check above (expect empty) ---"; /opt/dynamo/venv/bin/python -m pytest components/src/dynamo/vllm/tests/test_vllm_unit.py -k "kv_transfer_config or prefill_worker" -v' validation/logs/2026-08-03T14-33-20.636Z-bash-6407.log
05-code-inspection validated bash -lc 'cd /home/sandbox/workspace/wi-20260803T141049Z-unscoped/repo F=docs/fern/pages/reference/backends/vllm-configuration.mdx echo "=== body-only H1 check (skip the frontmatter block delimited by the first two --- lines) ===" awk "NR>1 && /^---\$/ {fm++; next} fm>=1 && /^# /{print NR\": \"\$0; found=1} END{if(!found) print \"no body H1 - compliant\"}" "$F" echo echo "=== the two lines the diff DELETED must be gone; the surviving third bullet must remain ===" grep -c "cannot be combined with .--is-prefill-worker" "$F" &#124;&#124; true grep -n "embedding-worker. is only valid" "$F" echo echo "=== diff shape: insertions/deletions and file count ===" git diff --numstat main...HEAD echo echo "=== diff applies cleanly onto main (integrity check, no working-tree mutation) ===" git diff main...HEAD > /tmp/wi-check.diff git stash list git -c core.hooksPath=/dev/null apply --check --reverse /tmp/wi-check.diff && echo "reverse-apply check OK: the diff is exactly what distinguishes HEAD from main" echo echo "=== working tree remains clean (validator did not modify the checkout) ===" git status --porcelain echo "porcelain empty above = clean" git log --oneline -1' validation/logs/2026-08-03T14-37-11.582Z-bash-383e.log

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Open in Devin Review

@glamr-agent

Copy link
Copy Markdown
Contributor Author
plan.md
# Plan — DYN-3726: vLLM config reference documents removed worker-role flags

Route: implementation
Template: debug-investigation
Engine: vllm

## User intent

The caller wants the vLLM configuration reference page to stop documenting two CLI
flags that the code no longer accepts, and to start documenting the one flag that
disaggregated prefill now genuinely requires.

Concretely: `--is-prefill-worker` and `--is-decode-worker` are still rendered as
`ParamField` blocks on the reference page, and two "Validation rules" bullets still
describe how they interact with `--disaggregation-mode`. Both flags were deleted from
the vLLM backend in 1.4.0. A reader who follows the page today gets an
`unrecognized arguments` failure at worker startup. Separately, the page never
mentions `--kv-transfer-config`, even though `--disaggregation-mode prefill` now
raises `ValueError` at parse time when it is absent — so the page cannot be used to
stand up a working disaggregated prefill worker at all.

This is a documentation correction: one file, no runtime behavior change, CPU only.

## Non-goals

- **No runtime code changes.** `components/src/dynamo/vllm/` is already correct; the
  documentation is what drifted. Nothing under `components/`, `lib/`, or `deploy/` is
  touched.
- **No other documentation files.** Discovery found four other files that still
  mention the deleted flags (enumerated below). Three of them are correct as written
  and the fourth is a historical release note. See "Out-of-scope references" for the
  per-file justification. This work item stays single-file per the caller's brief.
- **No changes to the mocker backend.** `components/src/dynamo/mocker/args.py` still
  implements `--is-prefill-worker` / `--is-decode-worker` as live deprecated flags.
  That is a different backend with a different deprecation schedule; the page being
  edited documents `DynamoVllmConfig` only.
- **No nav or `index.yml` change.** The page keeps its path and its title.
- **No new tests.** Per `learnings/no-tautological-tests.md`, a docs-only edit admits
  no meaningful behavioral test. The behavior the new prose asserts is already
  covered by an existing unit test, named below; duplicating it would be exactly the
  padding that learning warns against.

## Discovery

### The target path in the brief has drifted — corrected

The brief names `docs/fern/backends/vllm/vllm-config-reference.mdx`. **That file does
not exist.** `find . -name "*vllm-config*"` resolves to a single file:

**`/home/sandbox/workspace/wi-20260803T141049Z-unscoped/repo/docs/fern/pages/reference/backends/vllm-configuration.mdx`** (246 lines).

The rename happened in `a0245de57 docs: align documentation paths with site structure
(#12373)`, the only commit in that file's history. The page is registered in the nav at
`docs/fern/index.yml:1023`. The printer must edit the path above, not the one in the
brief.

The line numbers, by luck, did **not** drift — they match the brief exactly:

- L207–211 — `<ParamField path="--is-prefill-worker" ... deprecated={true}>`
- L213–217 — `<ParamField path="--is-decode-worker" ... deprecated={true}>`
- L227 — `- `--disaggregation-mode` cannot be combined with `--is-prefill-worker` or `--is-decode-worker`.`
- L228 — `- `--is-prefill-worker` and `--is-decode-worker` cannot both be set at the same time.`

Structural context the printer needs: the two `ParamField` blocks sit under a
`## Deprecated` heading (L203) whose intro sentence at L205 reads "These flags are
retained for backward compatibility and will be removed in a future release. Each is
mapped to its replacement at startup with a deprecation warning." A **third**
`ParamField``--model-express-url` (L219–223) — lives under that same heading and is
still genuinely deprecated-but-present (`backend_args.py:205`, field at L453). So the
`## Deprecated` section survives; only its first two entries are removed. L227–228 are
the first two of three bullets under `## Validation rules` (L225); the third bullet
(L229, `--embedding-worker`) is correct and stays.

### The flags are genuinely gone — verified against source, not the issue text

`grep -n "is_prefill_worker\|is_decode_worker" components/src/dynamo/vllm/backend_args.py`
returns **nothing**. The removal is `a9a7db43a chore(vllm): remove deprecated worker
role flags (#12089)`, authored 2026-07-24, confirmed an ancestor of current `HEAD` via
`git merge-base --is-ancestor`. Its diff deletes the `add_negatable_bool_argument`
registrations for both flags, the two `bool` field declarations, and the entire legacy
resolution branch (including the two `ValueError`s that L227–228 were documenting).
The repo is at version 1.4.0 (`pyproject.toml:6`, `Cargo.toml:39`), matching the
brief's claim.

Crucially, **PR #12089 updated eight other call sites — examples, operator tests,
profiler config modifiers, one `docs/integrations/` markdown file — but never touched
the fern reference page.** That is precisely the gap DYN-3726 reports.

What remains: the enum at `components/src/dynamo/common/constants.py:9`
(`AGGREGATED = "agg"`, `PREFILL`, `DECODE`, `ENCODE`) plus the `"pd"` alias at
`backend_args.py:29`, resolved in `_resolve_disaggregation_mode`. That matches the
existing `--disaggregation-mode` `ParamField` at L79–85, which needs no edit.

### `--kv-transfer-config` — actual shape and requiredness

The enforcement is at **`components/src/dynamo/vllm/args.py:216–231`**:

```python
    # If disaggregation mode is prefill, require explicit --kv-transfer-config
    has_kv_transfer_config = (
        hasattr(engine_config, "kv_transfer_config")
        and engine_config.kv_transfer_config is not None
    )
    if (
        dynamo_config.disaggregation_mode == DisaggregationMode.PREFILL
        and not has_kv_transfer_config
    ):
        raise ValueError(
            "--connector is deprecated and the default is no longer nixl. "
            "When using --disaggregation-mode prefill, you must explicitly "
            "provide --kv-transfer-config. Example:\n"
            "  --kv-transfer-config "
            '\'{"kv_connector":"NixlConnector","kv_role":"kv_both"}\''
        )
```

Four facts this establishes, each of which constrains how the new prose must be
written so it is true rather than transcribed:

1. **It is mandatory only for `prefill`.** The guard tests
   `disaggregation_mode == DisaggregationMode.PREFILL` exactly. `decode`, `agg`, and
   `encode` do **not** raise. The doc must not claim it is required for decode.
2. **It is a native vLLM `EngineArgs` field, not a `DynamoVllmConfig` field.** Verified
   empirically: `dataclasses.fields(AsyncEngineArgs)` contains `kv_transfer_config``True`. It has no `DYN_VLLM_*` environment variable, and it does not appear in
   `backend_args.py` at all. This matters for placement: the page's `<Note>` at L11
   explicitly scopes the page to "only the Dynamo wrapper flags", and its
   "How the config is loaded" section (L16) states every field is both a CLI flag and
   an env var — neither is true of `--kv-transfer-config`. The page already solves
   this exact problem once, at L56–73, with a `## Native KV event configuration`
   section documenting `--kv-events-config` and `--enable-prefix-caching` as native
   vLLM arguments that Dynamo nonetheless depends on. That is the established
   precedent to follow.
3. **The working example value** is `{"kv_connector":"NixlConnector","kv_role":"kv_both"}`
   — from the error message itself, and independently confirmed in
   `examples/backends/vllm/launch/disagg.sh`, where **both** the prefill and the
   decode worker pass it (decode passes it voluntarily; only prefill is forced to).
4. **The related `--connector` flag is now a hard error**, not a deprecation:
   `_reject_connector_flag` at `args.py:515–565` raises on any explicit `--connector`
   or `DYN_CONNECTOR`, with a computed `--kv-transfer-config` equivalent in the
   message. The page currently mentions **neither** `--connector` nor
   `--kv-transfer-config` (grep confirms: "NEITHER MENTIONED"), so there is no stale
   `--connector` text to remove — but the migration context is worth one sentence.

### Existing test coverage (RUN / IGNORE verdicts)

- `components/src/dynamo/vllm/tests/test_vllm_unit.py:282`
  `test_prefill_worker_without_kv_transfer_config_raises` — asserts
  `pytest.raises(ValueError, match="--kv-transfer-config")` for
  `--disaggregation-mode prefill` with no `--kv-transfer-config`. **This is the exact
  behavior the new documentation will assert.** Verdict: **IGNORE for authoring** (do
  not write a duplicate), **RUN as corroboration** — it is the runtime proof that the
  new prose is factually true.
- `components/src/dynamo/vllm/tests/test_vllm_unit.py:186`
  `test_endpoint_overrides_with_prefill_worker` — a positive control showing the
  documented flag pair (`--disaggregation-mode prefill` + `--kv-transfer-config`)
  parsing successfully. Verdict: **RUN as corroboration**.
- `test_backend_args.py` — PR #12089 already deleted its two worker-role assertions.
  Verdict: **IGNORE**, nothing to add.

### PR survey (per `learnings/discovery-survey-before-planning.md`)

`git log` over both the doc file and `components/src/dynamo/vllm/{args,backend_args}.py`,
plus six all-state `gh pr list --state all --search` queries against
`ai-dynamo/dynamo`:

| Query | Result |
|---|---|
| `DYN-3726` | `[]`**zero hits, any state**. No competing request. |
| `is-prefill-worker` | 15 hits, all `OPEN`, none touching this page |
| `kv-transfer-config` | 15 hits, all `OPEN`, all feature work |
| `vllm-config-reference` | 15 hits, all `OPEN`, none relevant |
| `vllm-configuration` | 15 hits, all `OPEN`, none relevant |
| `worker role flags` | **#12089 `MERGED`** (the code removal), #11887 `MERGED` (multimodal flags), rest `OPEN` |
| `vllm config reference docs` | 15 hits, all `OPEN`, none relevant |

Every merged hit was inspected against current `main`:

- **#12089 (MERGED, on `main`)** — removed the flags from code. Its 16-file diff does
  **not** include `docs/fern/pages/reference/backends/vllm-configuration.mdx`. It is
  the *cause* of the drift, not a fix for it. Not a revert candidate; the deletion is
  live in the working tree.
- **#11887 (MERGED)** — deprecated *multimodal* flags, a different flag family.
- **#12373 (MERGED)** — the path move only; content unchanged.

**No merged PR makes this correction.** No open PR competes for this file. Therefore
`Disposition: already-resolved` does **not** apply, and `Template: existing-request`
is not warranted. The lead's `debug-investigation` template stands.

### Out-of-scope references to the deleted flags

`grep -rn "is-prefill-worker\|is-decode-worker" docs/ examples/ recipes/` found these
beyond the target file. Each is explicitly **out of scope**, with a reason — the
printer must not "helpfully" fix them, and the reviewer should not flag their absence:

| File:line | Disposition |
|---|---|
| `docs/fern/pages/reference/general/releases/deprecations.mdx:155,204,342` | **Correct as written.** Historical per-release deprecation notices under `v1.1.0`/`v1.2.0` accordions. A deprecation announcement that was true at the time must stay. |
| `docs/fern/pages/reference/general/releases/dynamo-v1-0-0.mdx:300` | **Correct as written.** A frozen v1.0.0 release note. |
| `docs/fern/pages/cli/kv-cache-offloading/overview.mdx:189` | **Already steers readers correctly** — "It selects the prefill role with `--disaggregation-mode prefill`; do not use the deprecated `--is-prefill-worker` flag." Only the word "deprecated" is now stale (it is removed, not deprecated). A one-word nit in a different page; out of scope for a single-file work item. Worth a follow-up. |
| `examples/global_planner/{,v1beta1/}global-planner-mocker-test.yaml` (4 sites) | **Correct as written.** These target `dynamo.mocker`, whose `args.py:525,532` still registers both flags as live deprecated aliases. Not vLLM. |

### Style constraints read

`docs/fern/AGENTS.md` — SPDX in frontmatter (already present, L2–3, range `2025-2026`);
no body `# H1` (page starts at `##`, already compliant); Fern callout components
(`<Note>`, `<Tip>`, `<Info>`, `<Warning>`) in `.mdx`**not** GitHub `> [!NOTE]`;
language-tagged fences (`bash`, never `sh`); backend casing "vLLM"; relative links
with extension inside `docs/`; no internal refs; concrete prose, no marketing.

The page's own house style, which the new content must match: each `ParamField` uses
`path` / `type` / `default`, prose body, then a trailing `Environment variable: \`DYN_...\``
line. Enum-valued fields render a `<span className="enum-values">` with `<Badge intent="note" minimal>`
children. **A `--kv-transfer-config` `ParamField` must not carry a
`Environment variable:` line, because it has none** — which is itself the strongest
argument for the placement chosen below.

`docs/fern/scripts/check_reference.sh` exists as a Reference-page gate but requires the
`fern` CLI, which is **not installed** in this sandbox (`which fern` → not found). Its
broken-links step self-skips with a warning when the CLI is absent. This edit adds no
new links, so it is not a gating risk; CI owns it.

## Chosen approach

A single-file edit to
`docs/fern/pages/reference/backends/vllm-configuration.mdx`, in three moves.

**1. Delete the two obsolete `ParamField` blocks (L207–217).** Remove both entirely,
leaving `--model-express-url` as the sole occupant of `## Deprecated`. The section
heading and its L205 intro sentence stay, because that intro is still accurate for the
one flag that remains.

**2. Delete the two obsolete validation bullets (L227–228).** The third bullet
(`--embedding-worker`) remains, so `## Validation rules` keeps a body.

**3. Add `--kv-transfer-config` documentation as a new `## Native KV transfer
configuration` section**, placed immediately after the existing `## Native KV event
configuration` section (i.e. after L73, before `## Worker role and disaggregation` at
L75) — and add one cross-reference sentence to the `--disaggregation-mode` `ParamField`
body at L80 pointing `prefill` readers at it.

The new section must state, and only state, what discovery proved:

- `--kv-transfer-config` is a **native vLLM engine argument**, not a `DynamoVllmConfig`
  field — hence no `DYN_VLLM_*` environment variable.
- It is **required when `--disaggregation-mode prefill` is set**; the worker raises
  `ValueError` at startup without it. Scoped to prefill, not "all disaggregated
  workers".
- A working value, in a `bash`-tagged fence, matching `disagg.sh`:
  `--kv-transfer-config '{"kv_connector":"NixlConnector","kv_role":"kv_both"}'`.
- Decode workers in a NIXL pair normally pass the same value (as `disagg.sh` does),
  even though only prefill is enforced.
- One sentence noting `--connector` is no longer accepted and `--kv-transfer-config`
  replaces it, since a reader arriving from older material will look for `--connector`.
- A `<Warning>` (Fern component, per `AGENTS.md`) carrying the requiredness, because
  omitting it is a hard startup failure rather than a soft default.

**Why a prose section rather than a `ParamField` under "Worker role and
disaggregation":** the page's `<Note>` at L11 scopes it to Dynamo wrapper flags and
the L16 loading rules promise every field has a CLI-flag/env-var pair.
`--kv-transfer-config` satisfies neither. Filing it as a peer `ParamField` beside
`--headless` would make both statements false and would leave a conspicuous
`ParamField` with no `Environment variable:` line. The page has already faced and
solved this: `## Native KV event configuration` (L56–73) documents two native vLLM
arguments in exactly this situation, and the L11 `<Note>` already carves out an
exception for it ("Except for the KV event interoperability note below"). Mirroring
that section is the lowest-friction, most internally consistent placement — and it
means the L11 `<Note>` needs at most a small wording widening, which the printer
should make so the carve-out covers both native sections.

Net effect: 11 lines and 2 bullets removed, roughly 20 lines of accurate replacement
added, one file.

### Hypothesis table

The template asks for a ranked hypothesis table over the reported symptom
("following the doc always fails to start"). Discovery collapsed it to certainty, so
this is recorded as resolved rather than open:

| # | Hypothesis | Distinguishing observation | Status |
|---|---|---|---|
| 1 | Flags removed from code, doc not updated | `grep` on `backend_args.py` → no match; #12089 diff excludes the page | **CONFIRMED**`args.py`/`backend_args.py` + #12089 |
| 2 | Flags still exist but renamed | Enum in `common/constants.py:9` has no boolean equivalents; no alias path survives | Rejected |
| 3 | Doc drift is path-related (page moved, stale copy left behind) | `find` returns exactly one matching file; #12373 was a pure move | Rejected |
| 4 | `--kv-transfer-config` optional, brief overstates it | `args.py:221–231` raises for `PREFILL`; `test_vllm_unit.py:282` asserts the raise | **CONFIRMED but narrowed** — mandatory for `prefill` only, not all modes |

Hypothesis 4's narrowing is the single most important discovery output: transcribing
the issue text verbatim would have produced a doc claiming `--kv-transfer-config` is
required for disaggregation generally, which is false for `decode`.

## Rejected alternatives

- **Edit the path named in the brief.** Rejected: the file does not exist. Recorded
  prominently above so the printer does not create a stray file at the dead path.
- **`Disposition: already-resolved` on the strength of #12089.** Rejected: #12089 is
  merged and on `main`, but its diff does not include this page, and the stale
  `ParamField` blocks are present in the working tree at L207–217 right now. It is the
  cause, not the cure.
- **Also fix the four other files that mention the flags.** Rejected: three are
  historically correct release/deprecation notes that must not be rewritten, and the
  fourth (`kv-cache-offloading/overview.mdx:189`) already steers readers to the right
  flag — only the word "deprecated" is imprecise. The caller scoped this to one file;
  widening it would make the diff harder to review for no correctness gain. Flagged as
  a follow-up instead.
- **Delete the whole `## Deprecated` section.** Rejected: `--model-express-url` is
  still a live deprecated field (`backend_args.py:205`, `:453`). Removing the heading
  would orphan it.
- **Document `--kv-transfer-config` as a `ParamField` under "Worker role and
  disaggregation".** Rejected for the L11/L16 contradiction and the missing
  `Environment variable:` line, as argued above.
- **Add a regression test.** Rejected per `learnings/no-tautological-tests.md`: a docs
  edit has no behavioral surface, and
  `test_vllm_unit.py:282::test_prefill_worker_without_kv_transfer_config_raises`
  already covers the asserted behavior. Any new test would either duplicate it or
  assert on file contents — a mirror. The validator instead *runs* the existing test as
  corroboration, which is proof without padding.
- **Run `docs/fern/scripts/check_reference.sh`.** Rejected as a nominated recipe: it
  requires the `fern` CLI, absent in this sandbox. It would self-skip its link step and
  produce a misleading green. CI owns that gate.

## Validation strategy

Docs-only, CPU-only. No GPU recipe is warranted: nothing under `components/`, `lib/`,
or `deploy/` changes, so recipes 07/08/09/10 would exercise code this diff does not
touch and would prove nothing about the claim. `compute-env.md` reports one A100-80GB
and vLLM 0.22.0 in-sandbox — sufficient for those recipes, but they are not *relevant*,
which is the disqualifying test. Recipe 06 is `N/A` (no Docker socket, no Dockerfile
touched). Recipes 02 and 11 are `N/A` (no Rust, no Go).

**Recipe 01 (`01-python-lint`)** — the primary gate; the README selection guide sends
"only docs touched" here. Section 1 is `N/A` — no Python files touched (files:
`docs/fern/pages/reference/backends/vllm-configuration.mdx`); record that N/A with the
justification string the recipe specifies. Section 2 is the real check:
`pre-commit run --files docs/fern/pages/reference/backends/vllm-configuration.mdx --hook-stage manual`.
`pre-commit` is available at `/opt/dynamo/venv/bin/pre-commit`. The hooks that bite a
`.mdx` file are `codespell`, `trailing-whitespace`, `end-of-file-fixer`,
`mixed-line-ending`, and `check-case-conflict` — all present in
`.pre-commit-config.yaml`. This proves the edit introduces no whitespace, encoding, or
spelling regression, and that the SPDX/frontmatter shape survives.

**Recipe 03 (`03-python-unit-tests-mocker`)** — corroboration, not authorship. Run
`/opt/dynamo/venv/bin/python -m pytest components/src/dynamo/vllm/tests/test_vllm_unit.py -k "kv_transfer_config or prefill_worker" -v`.
Per the recipe, Recipe 0 runs first because the test imports `dynamo.*`. The two tests
this selects are `test_prefill_worker_without_kv_transfer_config_raises` (:282) and
`test_endpoint_overrides_with_prefill_worker` (:186). **The evidential purpose is
specific:** they are the runtime proof that the sentence the diff adds — "required when
`--disaggregation-mode prefill` is set" — is factually true of `main` today, and that
the flag pair the new example shows actually parses. Note there is no before/after
proof pair here: this recipe records existing green tests as corroboration of a factual
claim, not a regression demonstration, because the diff changes no code. The validator
must say so explicitly in `change-validation.md` so the reviewer does not read a single
green run as a missing before-proof.

**Recipe 05 (`05-code-inspection`)** — the section that actually defends this change.
Section 1 is `N/A` (no upstream PR to fetch; this work item authors the diff). Section 2
is the load-bearing one: every factual claim in the new prose must carry a `file:line`
citation, and the claims are enumerated in Discovery for exactly this purpose —
`args.py:216–231` (prefill-only requiredness and the example JSON),
`common/constants.py:9` (the surviving mode enum), `backend_args.py` grep-negative
(the flags are gone), `args.py:515–565` (`--connector` now hard-errors),
`examples/backends/vllm/launch/disagg.sh` (the value both workers pass in practice),
and the empirical `AsyncEngineArgs` field check (native, hence no `DYN_VLLM_*` var).
Section 3 is the parallel-pattern check, pre-answered by the out-of-scope table above:
four other files mention the deleted flags, each with a recorded in/out-of-scope reason
per this plan's non-goals. Section 4 is `N/A` until the PR exists; the publisher watches
CI.

Together: Recipe 01 proves the file is well-formed, Recipe 05 proves the prose is true,
and Recipe 03 supplies the runtime evidence behind the one behavioral sentence being
added. That is the whole claim of a documentation correction — the text is
mechanically clean and factually accurate — so nothing further is warranted.

```validation-recipes
01-python-lint
03-python-unit-tests-mocker
05-code-inspection
```

## Required deliverables

- `plan.md` — this file.
- `change.md` — the printer's narrative: the corrected file path (stressing the brief's
  stale path), what was deleted, what was added, and an explicit statement per
  `learnings/no-tautological-tests.md` that a docs edit admits no new behavioral test
  and why the existing `test_vllm_unit.py:282` covers the asserted behavior.
- `change.diff` — a nonempty diff touching exactly one file,
  `docs/fern/pages/reference/backends/vllm-configuration.mdx`. Per the
  `debug-investigation` template, a no-change incident report is not an acceptable
  outcome here.
- `change-validation.md` — recorded runs for the three recipes above, each through the
  recorder at `/workflow/agents/customs/evidence.ts`, with the N/A justifications
  spelled out (Recipe 01 §1, Recipe 05 §1 and §4) and the Recipe 03 runs labelled as
  corroboration rather than a before/after pair. Separate
  `## Investigation outcome:` from the final verdict, then close with exactly
  `## Verdict: pass`, `fail`, or `blocked`.
- `review.md` — independent assessment per
  `learnings/reviewer-independent-of-validator.md`, closing with exactly
  `## Assessment: sound` or `needs_changes`. The reviewer should check specifically
  that the new prose does not overclaim `--kv-transfer-config` as required for
  `decode` or `agg`, since that is the one factual trap in the source issue's wording.
- A pull request on `docs/fix-vllm-config-reference-dyn-3726--84c3e7f114cc` against
  `main`, opened by the publisher via `gh pr create`, non-draft, with `## Summary` and
  `## Validation` sections, DCO-signed commits, and a Conventional Commit title of the
  form `docs(vllm): ...`.

@glamr-agent

Copy link
Copy Markdown
Contributor Author
change.md
# Change — DYN-3726: vLLM config reference documents removed worker-role flags

## Commit

Branch `docs/fix-vllm-config-reference-dyn-3726--84c3e7f114cc`, branched from `main` at
`70544474680e7924aa7f0ae95b1e72a83bf40bf8`. One commit:

**`ff62a64ac4c63e7d6b079d9826b805b3bc333d25`**`docs(vllm): correct worker-role flags and document --kv-transfer-config`

One file changed, 24 insertions, 15 deletions. Nothing was pushed anywhere; publication is a
later stage's job.

A note on how that SHA was reached, because it is not the first one I produced. My initial
commit was `0778cea8e`, and it carried **two** `Signed-off-by:` trailers — I wrote one by hand
into the commit message body, and the repo's `prepare-commit-msg` hook (installed by `cli.ts`,
per `learnings/commits-dco.md`) appended its own derived from `git var GIT_AUTHOR_IDENT`. The
hook's idempotency check is a literal `grep -qsF` against the exact string it would append, and
my hand-written trailer read `svc-glamr <svc-glamr@nvidia.com>` while the hook's derived one
read `svc-glamr@nvidia.com <svc-glamr@nvidia.com>` — different strings, so the check did not
fire and both survived. I amended to strip my hand-written line, leaving the hook's trailer as
the sole one. The amend is of my own commit from this same iteration, not of any commit the
planner or a prior printer produced, so it does not violate the "each iteration appends new
commits" rule. Post-amend verification:

```
git log -1 --format='%ae | %(trailers:key=Signed-off-by,valueonly)'
svc-glamr@nvidia.com | svc-glamr@nvidia.com <svc-glamr@nvidia.com>
```

Both columns match, and exactly one trailer is present. The practical lesson for a later
iteration in this checkout: do not pass `--signoff` *and* hand-write the trailer; the hook
already covers it.

## The file path in the brief is stale — I edited a different path

The work-item brief and the originating Slack message both name
`docs/fern/backends/vllm/vllm-config-reference.mdx`. **That file does not exist in this
repository.** The plan flagged this and I confirmed it before touching anything. The real file
is:

`/home/sandbox/workspace/wi-20260803T141049Z-unscoped/repo/docs/fern/pages/reference/backends/vllm-configuration.mdx`

The rename landed in `a0245de57 docs: align documentation paths with site structure (#12373)`.
I did not create anything at the dead path. The line numbers in the brief (L207–217, L227–228)
happened to survive the move unchanged, and matched the real file exactly, which is presumably
why the drift went unnoticed when the ticket was written.

## What was deleted

Both deletions are in
`docs/fern/pages/reference/backends/vllm-configuration.mdx`.

**The two obsolete `ParamField` blocks** (formerly L207–217) for `--is-prefill-worker` and
`--is-decode-worker`, under `## Deprecated`. Both flags were removed from the vLLM backend in
1.4.0 — `grep -n "is_prefill_worker\|is_decode_worker"` over
`components/src/dynamo/vllm/backend_args.py` and `components/src/dynamo/vllm/args.py` returns
nothing (exit 1), which I ran myself rather than taking on the plan's word. A reader following
the page as it stood would pass a flag `argparse` no longer registers and get
`unrecognized arguments` at worker startup.

I kept the `## Deprecated` heading and its intro sentence ("These flags are retained for
backward compatibility…"), because the third `ParamField` under it — `--model-express-url` — is
still genuinely deprecated-but-present. I verified that independently:
`components/src/dynamo/vllm/backend_args.py:204` registers it with a deprecation
`flag_name="--model-express-url"`, and `:453` declares the field. Deleting the heading would
have orphaned a live flag.

**The two obsolete `## Validation rules` bullets** (formerly L227–228) that described how the
two removed flags interacted with each other and with `--disaggregation-mode`. The code paths
that raised those two `ValueError`s were deleted along with the flags, so the bullets described
enforcement that no longer exists. The third bullet — the `--embedding-worker` one — is
untouched and still correct, so the section keeps a body rather than becoming an empty heading.

## What was added

**A new `## Native KV transfer configuration` section**, placed immediately after the existing
`## Native KV event configuration` section and before `## Worker role and disaggregation`. It
documents `--kv-transfer-config` in prose plus a `bash`-tagged fence, not as a `ParamField`.

The placement is not cosmetic. The page's opening `<Note>` scopes it to "only the Dynamo wrapper
flags", and `## How the config is loaded` promises that "Each field is both a CLI flag and an
environment variable". Neither statement is true of `--kv-transfer-config`, so filing it as a
peer `ParamField` beside `--headless` would have falsified both, and would have left a
conspicuous `ParamField` with no `Environment variable:` line where every other one on the page
has one. The page had already faced this exact problem for `--kv-events-config` and solved it
with a prose section; the new section mirrors that.

**A cross-reference sentence** in the `--disaggregation-mode` `ParamField` body, pointing
`prefill` readers at the new section by anchor — since that is where a reader looking up the
mode will actually land.

**A widened carve-out in the L11 `<Note>`**: "Except for the KV event interoperability note
below" became "Except for the native KV event and KV transfer sections below", so the exception
covers both native sections rather than only the older one.

### Every factual claim in the new prose, re-verified against source

I re-checked each of these myself rather than transcribing the plan, because the source issue's
wording contains one trap (see the next paragraph).

- **Mandatory for `prefill` only.** `components/src/dynamo/vllm/args.py:216–231` guards on
  `dynamo_config.disaggregation_mode == DisaggregationMode.PREFILL and not
  has_kv_transfer_config`. The equality test is exact, so `agg`, `decode`, and `encode` do not
  raise. **This is the trap.** The originating Slack message says `--kv-transfer-config` is
  "now mandatory", full stop, and a transcription of that would have produced a page claiming
  decode workers require it — which is false and would have been a new documentation defect
  replacing the old one. The new `<Warning>` names `prefill` explicitly and then names the three
  modes that do *not* enforce the check, so the scope cannot be misread.
- **The mode enum.** `components/src/dynamo/common/constants.py:9``AGGREGATED = "agg"`,
  `PREFILL`, `DECODE`, `ENCODE`. Read in full; there are no boolean worker-role members, which
  is what rules out the "flags were renamed rather than removed" reading.
- **No `DYN_VLLM_*` environment variable.** `--kv-transfer-config` is a native vLLM engine
  argument. Confirmed two ways: `grep -n "kv_transfer" components/src/dynamo/vllm/backend_args.py`
  returns nothing (exit 1), and empirically —
  `dataclasses.fields(vllm.engine.arg_utils.AsyncEngineArgs)` contains `kv_transfer_config`
  (printed `True`; the same check printed `True` for `kv_events_config`, the flag the existing
  precedent section documents). The new section therefore carries **no**
  `Environment variable:` line, and says outright that it has none.
- **The working example value.** `{"kv_connector":"NixlConnector","kv_role":"kv_both"}` — read
  directly out of `examples/backends/vllm/launch/disagg.sh`, where **both** workers pass it: the
  decode worker at the first `python3 -m "$WORKER_MODULE"` invocation and the prefill worker at
  the second. The same string also appears in the `ValueError` message in `args.py:230`. The
  prose says only the prefill worker is *required* to set it while both halves of a pair should
  agree — which is exactly what that script demonstrates, and avoids overstating the enforcement.
- **`--connector` is a hard error, not a deprecation.** `_reject_connector_flag` at
  `components/src/dynamo/vllm/args.py:516–563` raises `ValueError` on any explicit `--connector`
  or `DYN_CONNECTOR`, including for `--connector none`, and the active-connector branch computes
  an equivalent `--kv-transfer-config` value into the message. The new prose says "no longer
  accepted" and "raises a `ValueError`" — deliberately not "deprecated", which would understate
  it. The page previously mentioned neither `--connector` nor `--kv-transfer-config`, so there
  was no stale `--connector` text to remove.

### Style constraints inspected

Checked against `docs/fern/AGENTS.md` directly, not just the plan's summary. Fern callout
components (`<Warning>`) rather than GitHub `> [!NOTE]` blockquotes, correct for `.mdx`.
Language-tagged fence — `bash`, never `sh`. Backend casing "vLLM". No body `# H1` (the page
still starts at `##`). SPDX frontmatter untouched, still `2025-2026`. The one link the new
section adds points at `examples/backends/vllm/launch/disagg.sh`, which is outside `docs/`, so
per the style guide it is an absolute `github.com/ai-dynamo/dynamo` URL rather than a `../`
escape — matching how the page's existing intro links `backend_args.py`. No internal refs, no
marketing prose.

## Verification actually run

**`pre-commit`, passed.** From the repo root:

```bash
/opt/dynamo/venv/bin/pre-commit run --files docs/fern/pages/reference/backends/vllm-configuration.mdx --hook-stage manual
```

Every hook that inspects a `.mdx` file passed: `codespell`, `check for case conflicts`, `check
for merge conflicts`, `check that scripts with shebangs are executable`, `mixed line ending`,
`trim trailing whitespace`, `Report pytest markers`. The Python and Rust hooks (`isort`,
`black`, `flake8`, `clang-format`, `ruff`, the JSON/TOML/YAML checkers, `fix end of files`,
`fix requirements.txt`) reported `(no files to check)` and skipped, which is the expected shape
for a single-`.mdx` file list. No hook modified the file. First invocation spent a few minutes
installing hook environments; that is setup cost, not a failure.

**Existing unit tests, passed — as corroboration, not as new coverage.**

```bash
/opt/dynamo/venv/bin/python -m pytest components/src/dynamo/vllm/tests/test_vllm_unit.py -k "kv_transfer_config or prefill_worker" -q
2 passed, 120 deselected in 5.55s
```

The two selected are `test_prefill_worker_without_kv_transfer_config_raises`
(`components/src/dynamo/vllm/tests/test_vllm_unit.py:282`) and
`test_endpoint_overrides_with_prefill_worker` (`:186`). Their evidential role here is narrow and
worth stating plainly so nobody reads a single green run as a missing before/after pair: this
diff changes no code, so there is no regression to demonstrate. The runs are the *runtime proof
that the sentence I added is true of `main` today* — the first asserts
`pytest.raises(ValueError, match="--kv-transfer-config")` for `--disaggregation-mode prefill`
with the flag absent, and the second shows the documented flag pair parsing successfully.

**Manual re-read of the edited regions.** I re-read the `<Note>` at L10–12, the whole new
section, the `--disaggregation-mode` `ParamField`, the surviving `## Deprecated` section, and
the surviving `## Validation rules` bullet after editing. `grep` for `is-prefill-worker` /
`is-decode-worker` over the edited file now returns nothing.

**Diff integrity.** `git diff main...HEAD` is 6291 bytes, touches exactly one file, and
`git apply --check` against a clean `main` checkout succeeds. The working tree is clean on the
branch and I restored the branch checkout after the apply test.

**Not run, deliberately:** `docs/fern/scripts/check_reference.sh`. It requires the `fern` CLI,
which is not installed in this sandbox — its broken-links step self-skips with a warning when
the CLI is absent, so running it would have produced a misleading green rather than evidence. I
did not attempt a bounded install of `fern` for it; CI owns that gate, and the one link this
diff adds is an absolute GitHub URL to a file I read directly, plus one same-page anchor.

## On tests — no new test, and why that is correct here

Per `learnings/no-tautological-tests.md`: **a docs-only edit admits no new behavioral test.**
This diff touches one `.mdx` file and no executable code. Any test I could add would be one of
two things, both of which that learning names as anti-patterns — a mirror asserting on the
literal contents of a documentation file (which passes forever and catches nothing, and which
would break on any innocuous rewording), or a duplicate of a test that already exists.

The behavior the new prose asserts is already covered:
**`components/src/dynamo/vllm/tests/test_vllm_unit.py:282`,
`test_prefill_worker_without_kv_transfer_config_raises`**, which drives
`--disaggregation-mode prefill` with no `--kv-transfer-config` through `parse_args()` and asserts
`pytest.raises(ValueError, match="--kv-transfer-config")`. That is precisely, and only, the claim
the new `<Warning>` makes. Writing a second test around the same guard would be padding. I ran
the existing one instead — proof without duplication.

Note also what that existing test does *not* cover, which is why I ran it rather than leaning on
it alone: it exercises `prefill`, so it corroborates the positive half of my claim. The negative
half — that `agg`, `decode`, and `encode` do *not* raise — rests on reading the guard's exact
equality test at `args.py:221–224`, plus the fact that `disagg.sh` starts a `decode` worker whose
`--kv-transfer-config` is passed voluntarily rather than under duress. I did not write a test for
the negative half either, for the same reason: it would be a new test asserting the absence of an
error, attached to a diff that changes no code.

## Scope held

One file. Nothing under `components/`, `lib/`, or `deploy/`. No nav or `docs/fern/index.yml`
change — the page keeps its path and title. No new tests. The four out-of-scope files the plan
enumerated were left alone deliberately:
`docs/fern/pages/reference/general/releases/deprecations.mdx` and
`docs/fern/pages/reference/general/releases/dynamo-v1-0-0.mdx` are historical release notes that
were accurate when written and must stay that way;
`docs/fern/pages/cli/kv-cache-offloading/overview.mdx:189` already steers readers to
`--disaggregation-mode prefill` and only calls the old flag "deprecated" where "removed" would
now be more precise — a one-word nit in a different page, worth a follow-up but not this
single-file work item; and the four `examples/global_planner/` YAML sites target
`dynamo.mocker`, whose `args.py` still registers both flags as live deprecated aliases, so they
are correct as written and would break if "fixed".

## Follow-up worth a separate ticket

`docs/fern/pages/cli/kv-cache-offloading/overview.mdx:189` describes `--is-prefill-worker` as
"the deprecated `--is-prefill-worker` flag". The steering is right; the word is now stale, since
the flag is removed rather than deprecated. One-word fix, different page, out of scope here.

MR URL: https://github.com/ai-dynamo/dynamo/pull/12568

@glamr-agent

Copy link
Copy Markdown
Contributor Author
change-validation.md
# Validation — DYN-3726: vLLM config reference documents removed worker-role flags

## Target and pathway

**CPU-only, in-sandbox: local lint + pytest + static-inspection pathway.** No preset,
no cluster, no model served, no GPU recipe.

`compute-env.md` reports one A100-SXM4-80GB, CUDA 12.9, and vLLM 0.22.0 in-sandbox —
so recipes 07/08/09/10 were *available*. They were not *warranted*, and availability is
not the selection test; relevance is. The diff touches exactly one file, and that file
lives under `docs/`. Nothing under `components/`, `lib/`, or `deploy/` changes. A GPU
smoke test would have started a worker whose behavior this diff cannot alter, and a
green result would have said nothing about the claim under audit. Recipe 06 is moot
(no Docker socket, no Dockerfile touched); recipes 02 and 11 are moot (no Rust, no Go).

Recipe 0 (`00-dynamo-editable-install`) ran first, as required, because the selected
pytest and the mode-enforcement probe both import `dynamo.*`. Its log confirms the
validation venv idempotently: pytest 8.4.2, pydantic 2.12.5, maturin 1.13.3, uv 0.12.0,
and `import dynamo, dynamo._core` succeeding against the work-item checkout. (The log
prints `dynamo OK at None` — that is the expected shape for a namespace package whose
`__path__` spans `components/src/dynamo` and `lib/bindings/python/src/dynamo`, both of
which the same line lists. It is not a failure signal.)

## What the change claims, and what had to be proved

The diff makes three moves in `docs/fern/pages/reference/backends/vllm-configuration.mdx`:
it deletes two `ParamField` blocks for flags the vLLM backend no longer accepts
(`--is-prefill-worker`, `--is-decode-worker`), deletes the two `## Validation rules`
bullets that described how those flags interacted with `--disaggregation-mode`, and adds
a new `## Native KV transfer configuration` section documenting `--kv-transfer-config`.
`git diff --numstat` records the whole change as `24 15` on one file.

A documentation correction has exactly two failure modes: the file can be mechanically
malformed, or the prose can be false. Recipe 01 addresses the first, Recipe 05 the
second, and Recipe 03 supplies the runtime evidence standing behind the single
behavioral sentence the diff introduces.

## Recipe 01 — the file is mechanically clean

**Section 1 is N/A, and the N/A is itself recorded rather than asserted.** The first
recorded run enumerates the diff's file set (`git diff --name-only main...HEAD`) and
counts the Python files in it. The log shows one file,
`docs/fern/pages/reference/backends/vllm-configuration.mdx`, and `0` Python files.
Python linting (isort/black/flake8/ruff) has no subject here; that is the justification,
and the enumeration is its evidence.

Section 2 is the real gate. `pre-commit run --files <the one file> --hook-stage manual`
exited 0. The hooks that actually bite a `.mdx` file all reported `Passed`: `codespell`,
`check for case conflicts`, `check for merge conflicts`, `mixed line ending`,
`trim trailing whitespace`, `check that scripts with shebangs are executable`, and the
pytest-marker report. Every Python- and JSON/TOML/YAML-scoped hook correctly reported
`(no files to check) Skipped` — which is the same negative the file-set enumeration
established, arriving independently from the hook framework. No hook rewrote the file:
the tree was clean before the run and clean after (confirmed in the last Recipe 05 run).

## Recipe 03 — CORROBORATION, not a before/after regression pair

**This must not be misread.** One recorded pytest run selected two existing tests, and
both passed. That is *not* a missing before-proof; there is no "before" failure to
demonstrate, because **the diff changes no code**. A docs-only edit has no behavioral
surface: running these tests against `main` and against the printer's branch produces
identical output by construction, so a before/after pair would be theater.

Their evidential purpose is different and specific. The diff *adds* a factual assertion
about runtime behavior — "A worker started with `--disaggregation-mode prefill` must be
passed `--kv-transfer-config` explicitly. Without it, the worker raises a `ValueError`
during argument parsing and never starts." These two tests are the runtime proof that
that sentence is true of `main` today, and that the flag pair the new example shows
actually parses:

- `test_prefill_worker_without_kv_transfer_config_raises`**PASSED**. The `ValueError`
  the new `<Warning>` describes is real and is raised at parse time.
- `test_endpoint_overrides_with_prefill_worker`**PASSED**. The positive control:
  `--disaggregation-mode prefill` together with `--kv-transfer-config` parses cleanly, so
  the `bash` example the diff adds is not merely plausible but executable.

Run summary: `2 passed, 120 deselected in 5.22s`, against `/opt/dynamo/venv/bin/python`,
rootdir the work-item checkout, plugin `ai-dynamo-1.4.0` — i.e. the real installed
package built from this checkout, not a mirror. The same run also captured
`git status --porcelain` as empty beforehand.

## Recipe 05 — the section that actually defends this change

Five recorded runs, all exit 0.

**Section 1 is N/A: there is no upstream PR to fetch.** This work item *authors* the
diff; the "review the PR under audit" step has no object. The diff-shape and reverse-apply
checks below are what stands in its place, and they are stronger for this purpose because
they bind the reviewed text to the actual branch state.

**Section 4 is N/A: no PR exists yet.** CI-status inspection is the publisher's job after
`gh pr create`; the validator cannot observe a pipeline that has not been triggered.

### §2 — every factual claim in the new prose, checked against source

The load-bearing section. Findings, concretely:

- **The removed flags are genuinely absent from the vLLM backend.** `grep` for
  `is_prefill_worker|is_decode_worker|is-prefill-worker|is-decode-worker` across
  `components/src/dynamo/vllm/backend_args.py` and `args.py` returned **no match**
  (exit 1). Deleting their `ParamField` blocks removes documentation of flags that do not
  exist — the page was actively misleading readers into an `unrecognized arguments`
  failure.

- **`--kv-transfer-config` is a native `AsyncEngineArgs` field with no `DYN_VLLM_*`
  environment variable.** Three independent observations in one log:
  `dataclasses.fields(AsyncEngineArgs)` contains `kv_transfer_config``True`; grep for
  `kv_transfer` in `backend_args.py`**no match** (so it is not on the Dynamo wrapper
  surface at all); and a tree-wide grep for `DYN_VLLM_KV_TRANSFER` / `DYN_VLLM.*KV_TRANSFER`
  across `components/`, `lib/`, `deploy/`, and `docs/`**no match**. The same probe
  confirms `kv_events_config` is likewise an `AsyncEngineArgs` field, establishing that the
  page's existing `## Native KV event configuration` section is the correct precedent to
  mirror. This is why the new section carries **no** `Environment variable:` line — the
  omission is accurate, not an oversight, and it is the reason a prose section rather than
  a `ParamField` is the right shape.

- **The example JSON matches the shipped launch script.** `examples/backends/vllm/launch/disagg.sh`
  passes `--kv-transfer-config '{"kv_connector":"NixlConnector","kv_role":"kv_both"}'` at
  **both** line 41 and line 50 — i.e. prefill and decode both pass it in practice. The new
  prose reflects exactly this ("Only the prefill worker is required to set it, but both
  halves of a NIXL pair must agree on a connector"), which is a finer distinction than the
  source issue drew.

- **`--connector` is a hard error, not a deprecation.** `_reject_connector_flag` in
  `args.py` contains four `raise ValueError` statements. The new sentence — "no longer
  accepted... raises a `ValueError` whose message includes the equivalent
  `--kv-transfer-config` value" — is accurate; it does not soften this into a deprecation
  warning.

- **The surviving mode enum is as documented.** `components/src/dynamo/common/constants.py`
  shows `DisaggregationMode` with exactly `AGGREGATED = "agg"`, `PREFILL`, `DECODE`,
  `ENCODE`. The existing `--disaggregation-mode` `ParamField` (which the diff only appends a
  cross-reference sentence to) remains correct, including its `pd` legacy alias.

- **`--model-express-url` is still live-deprecated.** `backend_args.py:204`
  (`flag_name="--model-express-url"`) and `:453` (`model_express_url: Optional[str] = None`).
  Keeping the `## Deprecated` heading and its intro sentence is therefore correct — deleting
  the section wholesale would have orphaned a genuinely deprecated-but-present flag. The
  structural run confirms exactly one `ParamField` remains under that heading.

- **The load-bearing check: the new prose does NOT overclaim requiredness.** This is the one
  factual trap in the source issue's wording, and it was tested empirically rather than
  argued. A probe drove the real argument-parsing path once per mode and recorded the
  outcome:

  ```
         agg : PARSED (no ValueError)
          pd : PARSED (no ValueError)
      decode : PARSED (no ValueError)
      encode : PARSED (no ValueError)
     prefill : RAISED ValueError mentioning --kv-transfer-config

  modes that enforce : ['prefill']
  doc claims enforce : ['prefill']
  MATCH - runtime behavior is exactly what the new prose states
  POSITIVE CONTROL ok - prefill + the documented example JSON parses cleanly
  ```

  The enforcing set is `{prefill}` and nothing else. The diff's `<Warning>` says precisely
  that — "The other modes — `agg`, `decode`, and `encode` — do not enforce this check." Had
  the printer transcribed the issue text verbatim ("required for disaggregation"), the page
  would now be false for `decode`. It is not. This is the single most important thing these
  logs prove, and it is proved by execution against the installed package, not by reading.

### §3 — parallel patterns and out-of-scope files

The diff touched **exactly one file** (`git diff --name-only main...HEAD` → one line;
`wc -l``1`), and the four out-of-scope locations the plan enumerated were **untouched**
and remain as the plan described them:

- `docs/fern/pages/reference/general/releases/deprecations.mdx:155,204,342` — historical
  per-release deprecation notices, correct as written.
- `docs/fern/pages/reference/general/releases/dynamo-v1-0-0.mdx:300` — a frozen v1.0.0
  release note.
- `docs/fern/pages/cli/kv-cache-offloading/overview.mdx:189` — already steers readers to
  `--disaggregation-mode prefill`; only the word "deprecated" is now imprecise. A one-word
  nit in a different page, correctly left for a follow-up.
- `examples/global_planner/{,v1beta1/}global-planner-mocker-test.yaml` (4 sites) — these
  target `dynamo.mocker`, and the log confirms **the mocker backend still registers both
  flags**: `components/src/dynamo/mocker/args.py` lines 525 and 532 register
  `--is-prefill-worker` / `--is-decode-worker`, with live deprecation handling at lines
  134/141/146/153. Those YAMLs are therefore correct as written, and "fixing" them would
  have broken working examples. This is the concrete reason the single-file scope is right
  rather than merely convenient.

The edited page itself is now clean: grep for the removed flags in the target file returns
**no match**.

### Structural integrity of the edited page

- **SPDX frontmatter intact** — the `---` block still carries both SPDX lines (range
  `2025-2026`), `title`, and `subtitle`.
- **No body H1.** Worth being explicit here, because the first attempt at this check was
  weaker than it looked: a bare `grep -n "^# "` matched the two SPDX comment lines *inside*
  the frontmatter and reported exit 0, contradicting its own inline label "(1 = compliant)".
  That was caught and re-run with a frontmatter-skipping `awk` pass, which reported
  `no body H1 - compliant`. The second run is the one that carries weight; the first was
  a mis-scoped grep, not a finding.
- **Fern components, not GitHub-style admonitions.** Grep for `> [!` in the `.mdx`**no match** (exit 1). The new callout is a `<Warning>` component, per `docs/fern/AGENTS.md`.
- **`bash`-tagged fences.** The fences in the page open as ` ```bash ` (lines 64 and 85);
  no `sh` tag appears.
- **Both edited sections still have bodies.** `## Deprecated` retains exactly one
  `ParamField` (`--model-express-url`). `## Validation rules` retains its third bullet,
  `--embedding-worker ... is only valid with --disaggregation-mode=agg`, printed verbatim
  in the log — so neither heading was orphaned.
- **The cross-reference resolves.** The link `[Native KV transfer configuration](#native-kv-transfer-configuration)`
  at line 103 targets `## Native KV transfer configuration` at line 75; the anchor slug and
  the heading text correspond.
- **The two deleted bullets are gone** — the count of lines matching
  `cannot be combined with .--is-prefill-worker` is `0`.

### Diff shape and tree integrity

`git diff --numstat main...HEAD``24  15  docs/fern/pages/reference/backends/vllm-configuration.mdx`
— one file, 24 insertions, 15 deletions, matching the plan's "roughly 20 lines added, 11
lines and 2 bullets removed" estimate. `git apply --check --reverse` against the captured
diff succeeded, confirming the diff is exactly what distinguishes HEAD from `main` — no
uncommitted drift, no partial application. `git status --porcelain` was empty at the end,
and `git stash list` was empty: **the validator did not modify the checkout.** HEAD is
`ff62a64ac docs(vllm): correct worker-role flags and document --kv-transfer-config`.

## Weaknesses and what these logs do not prove

Stated plainly rather than smoothed over:

1. **The Fern site was not rendered.** `docs/fern/scripts/check_reference.sh` requires the
   `fern` CLI, which is absent from this sandbox; running it would have self-skipped its
   broken-links step and produced a misleading green, so it was correctly not nominated. The
   anchor/heading correspondence was checked textually, not by rendering. The diff adds no
   new *external* links, so the residual risk is small, but it is nonzero and CI owns that
   gate.
2. **The first H1 check was mis-scoped** and its inline pass/fail label was wrong. It was
   superseded by a correct run in the same recipe. Reported above rather than quietly
   dropped.
3. **No before/after regression pair exists** — by design, for a diff that changes no code.
   Recipe 03 is corroboration of a factual claim. See that section.
4. **No end-to-end worker was started.** The mode-enforcement probe exercises argument
   parsing, which is where the `ValueError` is raised and therefore where the documented
   behavior lives; it does not prove a NIXL transfer subsequently succeeds. The new prose
   does not claim it does.

None of these weaken the specific claim under audit: the page no longer documents flags
that do not exist, and every sentence it now adds was checked against the source that
implements it.

## Investigation outcome:

The reported symptom — "following the vLLM configuration reference always fails to start a
disaggregated prefill worker" — is confirmed and fully explained, and the plan's hypothesis
1 stands: the flags were removed from code by #12089 without a corresponding documentation
update. The grep-negative across `backend_args.py` and `args.py` is the direct confirmation.
Hypotheses 2 and 3 stay rejected on this evidence: the surviving `DisaggregationMode` enum
has no boolean equivalents, and the diff resolves to a single existing file with no stale
duplicate.

The more consequential outcome is hypothesis 4's narrowing, which the empirical probe
settles rather than argues. `--kv-transfer-config` is enforced for `prefill` and for no
other mode — `agg`, `pd`, `decode`, and `encode` all parse without it. The issue text's
looser phrasing would have produced a page that is false for `decode`; the diff states the
narrow truth, and the probe's `modes that enforce : ['prefill']` / `doc claims enforce :
['prefill']` / `MATCH` is the direct check of that. The correction is complete for the file
it scopes itself to, correctly leaves the four out-of-scope references alone (three are
historically correct, and the mocker YAMLs point at a backend that still implements the
flags), and is mechanically clean. One documented follow-up remains outside this work item:
the word "deprecated" at `docs/fern/pages/cli/kv-cache-offloading/overview.mdx:189` is now
imprecise, since the flag was removed rather than deprecated.

## Verdict: pass — all three planned recipes (01, 03, 05) plus prerequisite Recipe 0 ran green in-sandbox with zero dispositions; lint proves the file is mechanically clean, static inspection proves every added sentence against its source (including that requiredness is correctly scoped to `prefill` alone), and the two corroborating unit tests prove the one behavioral claim is true of `main` today.

@glamr-agent

Copy link
Copy Markdown
Contributor Author
review.md
> 🤖 **Automated AI review — advisory.** An AI agent's judgment of
> whether this change is logically sound on the evidence; not a merge
> authorization. CI, customs, and a human reviewer hold that.

## Assessment: sound

A single-file documentation correction to
`docs/fern/pages/reference/backends/vllm-configuration.mdx`. It removes two
`ParamField` blocks and two `## Validation rules` bullets describing CLI flags the
vLLM backend no longer registers, and adds a `## Native KV transfer configuration`
section for `--kv-transfer-config`. I verified the load-bearing factual claim
independently against source rather than accepting the validator's word for it: the
new prose scopes requiredness correctly to `prefill` alone, which is the single trap
in the source issue's wording. Three nits, none blocking.

## Audit summary

**Commit state.** Branch `docs/fix-vllm-config-reference-dyn-3726--84c3e7f114cc`
(not the upstream default), HEAD `ff62a64ac4c63e7d6b079d9826b805b3bc333d25` — matches
the SHA in `change.md:8` exactly. `git status --porcelain` is empty. One DCO
`Signed-off-by: svc-glamr@nvidia.com <svc-glamr@nvidia.com>` trailer, matching
`%ae`. `git diff main...HEAD` is byte-identical to the packet's `change.diff`.

**Evidence table vs. the plan's named recipes.** The plan's ```validation-recipes```
block names `01-python-lint`, `03-python-unit-tests-mocker`, `05-code-inspection`.
`evidence.ts report` returns `[4/4 validated]` — all three named recipes plus the
prerequisite `00-dynamo-editable-install`. **No named recipe is `missing`, and no row
carries a `failing` disposition.**

**Every `validated` row's log shows real execution against the change.** I read all
nine logs under `validation/logs/`, not just the four the report table surfaces (the
report collapses `05-code-inspection`'s five runs to one row; the registry holds all
five and I walked each):

| Recipe | Log | What the log actually shows |
|---|---|---|
| `00-dynamo-editable-install` | `…14-32-25.775Z-bash-5d2b.log` | pytest 8.4.2, pydantic 2.12.5, maturin 1.13.3, `import dynamo, dynamo._core` succeeding against the work-item checkout. `dynamo OK at None` is the expected shape for a namespace package, not a failure. |
| `01-python-lint` | `…14-33-04.570Z-bash-638d.log` | `pre-commit` run on the one edited `.mdx`. Real hook output: `codespell`, `check for case conflicts`, `check for merge conflicts`, `mixed line ending`, `trim trailing whitespace` all `Passed`; Python/Rust/JSON hooks `(no files to check) Skipped`. Not a toolchain-missing log. |
| `03-python-unit-tests-mocker` | `…14-33-20.636Z-bash-6407.log` | Real pytest session, `plugins: ai-dynamo-1.4.0`, rootdir the work-item checkout. `test_endpoint_overrides_with_prefill_worker PASSED`, `test_prefill_worker_without_kv_transfer_config_raises PASSED`, `2 passed, 120 deselected in 5.22s`. |
| `05-code-inspection` | five logs, `…14-35-23` through `…14-37-11` | Mode-enforcement probe output; source greps with exit codes; out-of-scope file survey; structural page checks; diff shape + reverse-apply. All exit 0 with substantive output. |

**Verdict judged on substance, not word.** `change-validation.md:269` reads
`## Verdict: pass` — unqualified, no "conditionally", "static", or "partial" hedge,
and the recipes behind it genuinely ran. This is not the MR-#9 pattern of a
pass-label over a toolchain that never executed.

**Non-goals held.** `git diff --name-only main...HEAD` returns exactly one line:
`docs/fern/pages/reference/backends/vllm-configuration.mdx`. Nothing under
`components/`, `lib/`, or `deploy/`; no `docs/fern/index.yml` or nav change; no new
tests; none of the four files in the plan's "Out-of-scope references" table touched.

**No new test is correct here.** Per `learnings/no-tautological-tests.md`, a docs-only
diff admits no behavioral test; the asserted behavior is already covered by
`components/src/dynamo/vllm/tests/test_vllm_unit.py:282`, which the validator ran as
corroboration rather than duplicating. Not raised as a finding.

**No new infrastructure.** The diff adds no fixture, helper, or harness — one `.mdx`
file. The `new-infrastructure-exercise-cited` obligation is satisfied vacuously.

## Independent verification of the factual trap

The plan (`plan.md:398-400`) asked me to check specifically that the new prose does
not overclaim `--kv-transfer-config` as required for `decode`, `agg`, or `encode`. I
read the guard myself at `components/src/dynamo/vllm/args.py:216-231`:

```python
    if (
        dynamo_config.disaggregation_mode == DisaggregationMode.PREFILL
        and not has_kv_transfer_config
    ):
```

The comparison is an exact `==` against `DisaggregationMode.PREFILL`. No other mode
reaches the `raise`. `components/src/dynamo/common/constants.py:9-14` confirms the
enum is `AGGREGATED = "agg"`, `PREFILL`, `DECODE`, `ENCODE`, and
`backend_args.py:601-610` resolves the `pd` alias to `AGGREGATED` before the guard
runs — so `pd` cannot reach it either.

The prose at
`docs/fern/pages/reference/backends/vllm-configuration.mdx:80` says: "A worker started
with `--disaggregation-mode prefill` must be passed `--kv-transfer-config` explicitly…
The other modes — `agg`, `decode`, and `encode` — do not enforce this check."
**This is exactly right, and it names the non-enforcing modes explicitly rather than
leaving the scope to inference.** The cross-reference at `:103` is likewise scoped
("`prefill` additionally requires…"). The overclaim did not happen.

Two further independent checks the brief named:

- **`Environment variable:` line correctly omitted.** `grep -c kv_transfer
  components/src/dynamo/vllm/backend_args.py``0`; `grep -rn "DYN_VLLM.*KV_TRANSFER"
  components/ lib/ deploy/ docs/` → no match. `--kv-transfer-config` is a native
  `AsyncEngineArgs` field, so no `DYN_VLLM_*` var exists to document. The new section
  carries no such line and states outright at `:77` that it has none — matching the
  page's own precedent at `## Native KV event configuration` (`:56`), which handles
  `--kv-events-config` the same way.
- **Example JSON matches `disagg.sh`.** `examples/backends/vllm/launch/disagg.sh:41`
  (decode) and `:50` (prefill) both pass
  `'{"kv_connector":"NixlConnector","kv_role":"kv_both"}'` — character-for-character
  what the new fence at `:88` shows. The claim at `:92` that only prefill is
  *required* to set it while both halves should agree is precisely what that script
  demonstrates.

## Findings

| # | file:line | Severity | Claim | Evidence |
|---|---|---|---|---|
| 1 | `docs/fern/pages/reference/backends/vllm-configuration.mdx:228` | nit | The surviving `## Deprecated` intro — "Each is mapped to its replacement at startup with a deprecation warning" — is now false for **every** flag remaining in the section. After this diff its sole occupant is `--model-express-url`, which has no replacement flag and emits no deprecation warning. Consequence: a reader is told to expect a startup warning naming a replacement, goes looking for one, and finds nothing. | `components/src/dynamo/vllm/backend_args.py:202-209` registers `--model-express-url` with help "accepted for compatibility with older ModelExpress manifests… The vLLM ModelExpress plugin reads its own configuration" — no replacement named. The only `_warn_deprecated` call site is `backend_args.py:568`, inside `_resolve_legacy_benchmark_sampling`, whose `mappings` tuple (`:525-541`) covers `benchmark_*` flags exclusively. The page's own `ParamField` at `:231-235` contradicts the intro. `change.md:61-66` reasons the sentence "is still accurate for the one flag that remains" — that reasoning is what I am contradicting; it was accurate for the two flags this diff removed, not for the one it leaves behind. |
| 2 | `docs/fern/pages/reference/backends/vllm-configuration.mdx:94` | nit | "raises a `ValueError` whose message includes the equivalent `--kv-transfer-config` value to use instead" overgeneralizes: two of the four `raise` sites in `_reject_connector_flag` emit **no** equivalent value. Consequence: a reader who passes `--connector none` on the strength of this sentence expects a copy-pasteable replacement value and instead gets "Simply remove the --connector flag." | `components/src/dynamo/vllm/args.py:535` (the `none`/`null` branch) and `:560` (the `DYN_CONNECTOR`-parsed-to-empty branch) raise without computing `equiv`. Only `:544` and `:555` include the `Equivalent: --kv-transfer-config '…'` line. `change-validation.md:124-128` cites the four `raise` statements as corroboration but did not check whether all four carry the equivalent value; the validator's own log (`…14-35-53.048Z-bash-24c6.log`, CLAIM D) shows only the four line offsets, not their message bodies. |
| 3 | `validation/registry.jsonl:5` | nit | The `05-code-inspection` mode-enforcement probe — the run producing the load-bearing `modes that enforce : ['prefill']` / `MATCH` output — executes `/opt/dynamo/venv/bin/python /tmp/kv_scope_probe.py`, a script under `/tmp` that is not preserved in the packet. Consequence: a downstream auditor reading only the packet cannot confirm the probe drove the real `parse_args()` path rather than printing predetermined strings. | Log `validation/logs/2026-08-03T14-35-23.602Z-bash-c985.log` shows only the probe's stdout; the script body has no recorded copy. Weight of this nit is low because I discharged the underlying claim independently by reading `components/src/dynamo/vllm/args.py:216-231` (see above), and `03-python-unit-tests-mocker` independently corroborates the `prefill` half via `test_vllm_unit.py:282`. Future validators inspecting a claim empirically should inline the probe into the recorded command so the reasoning is in the registry. |

None of the three blocks the change. Findings 1 and 2 are prose imprecisions in a
page whose purpose is precision; both are one-sentence edits, and finding 1 concerns
a sentence the printer explicitly reasoned about and kept, so it is worth surfacing
rather than absorbing. Neither introduces a startup failure, and neither touches the
correction this work item exists to make.

## Notes not rising to findings

- **The validator disclosed and corrected its own bad check.** The first body-H1
  check (`…14-36-51.522Z-bash-4d5c.log`) used a bare `grep -n "^# "` that matched the
  two SPDX comment lines *inside* the frontmatter and reported exit 0 against an
  inline label reading "(1 = compliant)". The validator caught this, re-ran with a
  frontmatter-skipping `awk` pass (`…14-37-11.582Z-bash-383e.log` → `no body H1 -
  compliant`), and reported the miss in `change-validation.md:193-198` rather than
  dropping it. The superseding run is correct and the page is compliant. Self-reported
  and remediated within the same recipe; not a finding.
- **Pathway.** `compute-env.md` reports one A100-80GB, but the validator selected the
  CPU lint/pytest/inspection pathway on relevance grounds. Correct: the diff touches
  no code a GPU recipe could exercise. No remote compute, SSH, or Slurm was attempted.
- **The brief's file path was stale and the printer corrected it.** The brief names
  `docs/fern/backends/vllm/vllm-config-reference.mdx`, which does not exist; the real
  page moved in `a0245de57 (#12373)`. No stray file was created at the dead path —
  `git diff --name-only main...HEAD` returns one line and it is the real path.

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

The vLLM configuration documentation now describes native KV transfer settings, including prefill and NIXL usage. It links prefill disaggregation to this configuration and removes deprecated worker-role options and validation rules.

Changes

vLLM KV transfer configuration

Layer / File(s) Summary
Native KV transfer configuration
docs/fern/pages/reference/backends/vllm-configuration.mdx
Adds the native --kv-transfer-config format, prefill requirements, NIXL examples, connector consistency rules, and legacy --connector replacement guidance.
Disaggregation mode and deprecated options
docs/fern/pages/reference/backends/vllm-configuration.mdx
Links prefill mode to native KV transfer configuration and removes deprecated worker-role parameters and validation rules.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the documentation fixes for obsolete worker-role flags and the new KV transfer configuration.
Description check ✅ Passed The description includes all required sections, explains the changes, identifies the review starting point, and confirms that no related issue exists.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/fern/pages/reference/backends/vllm-configuration.mdx`:
- Line 79: Replace the <Warning> documentation block in the vLLM configuration
reference with the repository-standard GitHub-style > [!WARNING] admonition
syntax, preserving its existing warning content and structure.
- Around line 80-81: Update the warning text in the vLLM configuration
documentation to describe the exempt cases as all non-prefill modes, or
explicitly include the legacy `pd` alias alongside `agg`, `decode`, and
`encode`.
- Line 94: Update the vLLM backend documentation for the deprecated --connector
handling to document exact ValueError messages for active connector values
supplied via CLI or DYN_CONNECTOR, including the replacement JSON, while
separately covering none, null, empty values, and a bare --connector without
replacement JSON. Add assertions that verify each documented message exactly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: efa98966-08e9-4d9a-aeb8-8661d79cfcfa

📥 Commits

Reviewing files that changed from the base of the PR and between 7054447 and ff62a64.

📒 Files selected for processing (1)
  • docs/fern/pages/reference/backends/vllm-configuration.mdx

Comment thread docs/fern/pages/reference/backends/vllm-configuration.mdx
Comment thread docs/fern/pages/reference/backends/vllm-configuration.mdx Outdated
Comment thread docs/fern/pages/reference/backends/vllm-configuration.mdx Outdated
@glamr-agent

glamr-agent commented Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

CI result: passed.

Observed terminal on the pre-merge check set for
eececad6a70e5217727a5cc8f7d4930148034530 (the head after babysitter round 1;
the earlier reading below was taken at ff62a64ac4c63e7d6b079d9826b805b3bc333d25):
18 check runs — 14 SUCCESS, 4 SKIPPED, 0 failing, 0 pending — plus the
CodeRabbit status context at success. Stable across three consecutive polls
within the watch window.

Passing: pre-commit, Fern Configuration Check, Fern Broken Links Check,
Check for broken markdown links, lychee, copyright-checks, codeowners,
changed-files, pre-merge-status-check, Validate PR title and add label,
DCO, dco-comment, label, ok-to-test, plus the CodeRabbit status context.

Skipped by path filter, as expected for a docs-only diff: rust-tests,
rust-clippy, operator, snapshot.

All review threads on this pull request are resolved. Two were resolved by the
author after being addressed in eececad6a; one was withdrawn and resolved by
CodeRabbit itself after it re-checked the documentation style guide.

Note that this is the pre-merge subset. Full CI on this repository runs only after a
maintainer comments /ok to test <sha>; that gate is a human's to open and was not
triggered here.

Address review feedback on the native KV transfer configuration section.

- The kv-transfer-config warning enumerated agg, decode, and encode as the
  modes that do not require the flag, omitting pd. pd is an accepted choice
  that resolves to DisaggregationMode.AGGREGATED in
  BackendArgs._resolve_disaggregation_mode, so it is exempt too. Reword to
  cover all non-prefill modes and name pd explicitly.

- The --connector paragraph said the ValueError message always includes the
  equivalent --kv-transfer-config value. _reject_connector_flag only does
  that for active connectors; none/null and an empty DYN_CONNECTOR raise
  distinct messages that carry no equivalent. Split the claim into the three
  cases the code actually implements.

Signed-off-by: svc-glamr@nvidia.com <svc-glamr@nvidia.com>
@glamr-agent
glamr-agent temporarily deployed to external_collaborator August 3, 2026 15:24 — with GitHub Actions Inactive
@glamr-agent

Copy link
Copy Markdown
Contributor Author

Babysitter round 1 — pushed eececad6a (was ff62a64a).

CI was already terminal-success at ff62a64a, so this round carries no failure repair. It addresses the three unresolved review threads, each verified against source first.

Thread Verdict Action
<Warning>> [!WARNING] Not valid for this file No change — see reply
pd missing from exempt modes Confirmed Fixed
--connector error contract overstated Confirmed (prose) Fixed

pd alias. backend_args.py:89 admits pd as a --disaggregation-mode choice and _resolve_disaggregation_mode maps it to DisaggregationMode.AGGREGATED, so it is exempt from the prefill --kv-transfer-config check like the others. The warning now reads "all non-prefill modes — agg, pd, decode, and encode".

--connector contract. _reject_connector_flag (args.py:516-563) only emits the equivalent --kv-transfer-config JSON for active connectors. --connector none / null and an empty DYN_CONNECTOR raise different messages carrying no equivalent. The paragraph claimed the equivalent was always included; it now enumerates the three cases the code implements.

Admonition syntax. Declined — the style guide makes this extension-dependent, and this file is .mdx. Detail in the thread reply.

Checks run: pre-commit run --files docs/fern/pages/reference/backends/vllm-configuration.mdx — all applicable hooks passed. Docs-only .mdx edit; no signature or API surface changed, so no workspace build is implicated.

Validation status: complete for this round. Awaiting CI on the new head.

@alec-flowers

Copy link
Copy Markdown
Contributor

/ok to test eececad

@dagil-nvidia
dagil-nvidia merged commit 27e6820 into ai-dynamo:main Aug 3, 2026
87 checks passed
hhzhang16 added a commit that referenced this pull request Aug 4, 2026
dyn-3691-extract-shared-target-pid-cuda-customstorage-operation-layer

* 'main' of https://github.com/ai-dynamo/dynamo: (50 commits)
  docs(cli): correct removed vLLM prefill-worker flag reference (#12581)
  docs(operator): reserve webhook Ignore for emergencies (#12563)
  ci(docs): make previews and checks match what actually publishes (#12339)
  refactor(vllm): organize custom encoder modules (#12416)
  feat(llm): Select reasoning output field via env var (#11464)
  feat(runtime): add TLS support to TCP request plane (#10921)
  fix: convert conditional disagg sglang warning to httperror 400 (#12578)
  feat(operator): add runtime feature gates (#12421)
  refactor(runtime): extract PushRouter transport seam behind StreamingDispatch trait (#12447)
  feat(replay): add deterministic canonical offline reports (#12363)
  build: bump ModelExpress to 0.5.0(OPS-7978) (#12455)
  fix(mocker): use logical KV tokens for decode timing (#12583)
  fix(examples): update Triton example for CUDA 13 + fix libdcgm copy (DYN-3697) (#12577)
  refactor(operator): implement composition-first DGD reconciliation (#12283)
  feat(frontend): add basetenkenizer backend (#12376)
  fix(profiler): configure rapid mocker without planner (#12573)
  docs(vllm): correct worker-role flags and document --kv-transfer-config (#12568)
  ci: add Kubernetes deploy test to nightly (#12090)
  fix(container): reuse pinned protoc in runtime image (#12535)
  feat(self-host): flip DYN_SELF_HOST_METADATA default to ON (gh-8749) (#11417)
  ...

Signed-off-by: Hannah Zhang <hannahz@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs documentation Improvements or additions to documentation external-contribution Pull request is from an external contributor size/M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants