Skip to content

docs(skills): fix broken refs in adk-workflow skill#6044

Closed
freddypatota wants to merge 603 commits into
google:mainfrom
freddypatota:fix/adk-workflow-skill-doc-bugs
Closed

docs(skills): fix broken refs in adk-workflow skill#6044
freddypatota wants to merge 603 commits into
google:mainfrom
freddypatota:fix/adk-workflow-skill-doc-bugs

Conversation

@freddypatota

@freddypatota freddypatota commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes 5 documentation bugs in the adk-workflow skill on v2. Each is empirically verified against google-adk==2.2.0 and v2 source. Full verification transcript inline below.

Fixes in this PR

  1. advanced-patterns.md — Remove the hard-coded local-filesystem URL file:///Users/deanchen/Desktop/... left in a doc link; replace with the relative path dynamic-nodes.md.

  2. testing.md — All examples imported from tests.unittests.*, which is not in the published google-adk wheel. Rewrite to use the public from google.adk.runners import InMemoryRunner plus a small inline run() helper. Three rewritten snippets (basic workflow, state, parallel worker) were executed end-to-end against google-adk==2.2.0 and pass. The MockModel section is replaced with a FakeLlm(BaseLlm) pattern that uses only public symbols.

  3. llm-agent-nodes.md — The doc claims "LlmAgentWrapper outputs types.Content, NOT str." The source (_llm_agent_wrapper.process_llm_agent_output) sets event.output = text (a str) when output_schema is unset, and the validated dict when set. Rewrite the section, the table, and drop the "use Any and extract text" workaround that depended on the wrong claim.

  4. parallel-and-fanout.md + import-paths.md — Drop from google.adk.workflow._parallel_worker import ParallelWorker. The class doesn't exist under that name; only the private _ParallelWorker does. The same files already document the public API (parallel_worker=True flag on @node or LlmAgent) — rewrite samples to use it consistently.

  5. state-and-events.md (+ one cross-reference in advanced-patterns.md) — Drop triggered_by, in_nodes, execution_id, retry_count from the Context property tables and code samples. None of them exist on Context in v2 source (verified by grep in src/google/adk/agents/context.py). Rename retry_countattempt_count (the live name). Also drop get_next_child_execution_id from the methods table for the same reason.

Scope

All five fixes target the same file tree (.agents/skills/adk-workflow/references/) with the same concern: "skill docs reference symbols/imports/paths that don't exist." Per CONTRIBUTING.md "small, focused PRs", they're bundled because each is a surgical edit and they share verification setup. Happy to split if reviewers prefer.

Testing plan

Doc-only changes. No source code or behavior modified.

  1. Pip-install reproduction of every bug claimed in a clean venv with google-adk==2.2.0. See "Verification details" below for the verbatim ImportError, hasattr == False, and source quotes that prove each claim.

  2. Rewritten testing.md snippets executed end-to-end against google-adk==2.2.0:

    • test_simple_workflow — PASSED
    • test_state_management — PASSED
    • test_parallel_worker — PASSED
  3. Pre-commit hooks ran clean on the changed files. mdformat is excluded for .agents/ by the repo's .pre-commit-config.yaml, and the other hooks (isort, pyink, addlicense) target Python/shell files only.

Notes for reviewers

  • All claims are pinned to 2.2.0 + v2 HEAD as of the date of this PR.
  • The testing.md rewrite is the largest delta (~500 lines), but almost every line either drops a tests.unittests.* import or replaces a testing_utils.X call with public-API equivalents.
  • A previously-considered "bug" (parallel-worker naming as {name}__{index}) was dropped from this PR after confirming it had already been fixed on v2 to use {name}@{run_id} with run_id starting at "1".

Verification details

Setup:

uv venv --python 3.13 .venv && source .venv/bin/activate
uv pip install google-adk
python -c "import google.adk; print(google.adk.__version__)"
# -> 2.2.0

Bug 1 — Hard-coded file:/// URL

$ grep -n 'file:///' .agents/skills/adk-workflow/references/advanced-patterns.md
38:See the dedicated [Dynamic Node Scheduling Reference](file:///Users/deanchen/Desktop/adk-workflow/.agents/skills/adk-workflow/references/dynamic-nodes.md) for detailed rules, examples, and best practices.

A developer's local filesystem path leaked into the published skill.

Bug 2 — tests.unittests... imports unreachable from pip install

>>> from tests.unittests.workflow import testing_utils
ModuleNotFoundError: No module named 'tests'

>>> from tests.unittests.testing_utils import InMemoryRunner, MockModel
ModuleNotFoundError: No module named 'tests'

The tests/ directory ships only in the source repo, not in the installed google-adk wheel. Any user copying these snippets gets ModuleNotFoundError. The PR rewrites samples to use the public from google.adk.runners import InMemoryRunner and demonstrates a publicly-importable mock pattern (subclass BaseLlm).

The three rewritten snippets (basic, state, parallel) were run end-to-end against google-adk==2.2.0 and all three passed.

Bug 3 — LlmAgentWrapper output type doc is wrong

The skill claims: "LlmAgentWrapper outputs types.Content, NOT str."

The source in both 2.2.0 and v2 says otherwise. From src/google/adk/workflow/_llm_agent_wrapper.py:

def process_llm_agent_output(agent: Any, ctx: Context, event: Event) -> None:
    ...
    text = (
        ''.join(p.text for p in event.content.parts if p.text and not p.thought)
        if event.content.parts else ''
    )
    if agent.output_schema:
        if text.strip():
            output = validate_schema(agent.output_schema, text)
        else:
            output = None
    else:
        output = text          # <-- str, not types.Content
    ...
    event.output = output

When output_schema is unset, event.output is the concatenated string of the model's text parts. When output_schema=MyModel is set, it's the validated model_dump() dict. The PR rewrites the section, table, and the "use Any and extract text" workaround that depended on the wrong claim.

Bug 4 — _parallel_worker.ParallelWorker is not importable

The class doesn't exist under that name — only the underscore-prefixed _ParallelWorker does, and that path is private:

>>> from google.adk.workflow._parallel_worker import ParallelWorker
ImportError: cannot import name 'ParallelWorker' from 'google.adk.workflow._parallel_worker'

>>> from google.adk.workflow._parallel_worker import _ParallelWorker
>>> _ParallelWorker
<class 'google.adk.workflow._parallel_worker._ParallelWorker'>

The recommended public API is the parallel_worker=True flag — already documented as preferred in the same files. Verified end-to-end:

from google.adk.workflow import node, Workflow

@node(parallel_worker=True)
def double(node_input: int) -> int:
    return node_input * 2

# Workflow constructs OK; `double` is an internal _ParallelWorker
# under the hood — no user-visible private-API surface needed.

PR drops the private import from parallel-and-fanout.md and the import-paths.md table, and rewrites samples to use the flag.

Bug 5 — Removed Context properties documented as live

>>> from google.adk.agents.context import Context
>>> for name in ['triggered_by', 'in_nodes', 'execution_id', 'retry_count', 'attempt_count']:
...     print(name, hasattr(Context, name))
triggered_by False
in_nodes False
execution_id False
retry_count False
attempt_count True

The same is true on v2 source — grep for those names in src/google/adk/agents/context.py returns nothing, while attempt_count has 4 hits.

Code samples using ctx.retry_count / ctx.triggered_by raise AttributeError. PR removes the four absent properties from the docs and renames retry_countattempt_count everywhere it's mentioned. get_next_child_execution_id is also gone from Context on v2; the PR removes it from the methods table for the same reason.

boyangsvl and others added 30 commits April 8, 2026 00:08
… same run

Change-Id: I223793ddabb6f491fb6a47ebfa3ddb5403f66021
Change-Id: I376f98b7d631704d8d20627e2e593681526ad0f9
…mAgent is root agent

Change-Id: I9434c874539b4abe3405a7b4f9df3f6e15aa4659
Change-Id: If0db12a0d9799ee147878a59cce5ec5e51600811
Change-Id: Ie08e42fe7137eb223ac2f00663420e0d5fb3aae2
Change-Id: I777f76b2709abfa383b508caaae9b677ed090273
Change-Id: I88f49f59d26ba85022836af6f6a351e96a6a6964
Change-Id: Ieb69c4890ed199cc4adb5eba55f03f626a6a6964
Co-authored-by: Liang Wu <wuliang@google.com>
PiperOrigin-RevId: 891960308
Change-Id: I373db38c38c69825c811a82355017d13aeaa8c00
Set mock_process.pid to None in the resource limits test. Without this, the `finally` block in `run_async` attempts to call `os.killpg` with a fallback `Mock` object (which evaluates to True). In some environments like GitHub Actions, this can inadvertently kill the test runner's process group rather than raising a TypeException, causing the CI job to hang indefinitely.

Co-authored-by: Liang Wu <wuliang@google.com>
PiperOrigin-RevId: 895992429
Change-Id: I926e353228875a8a608de3a266ce6532a95cadbb
Merge google#4966

Co-authored-by: Xuan Yang <xygoogle@google.com>
COPYBARA_INTEGRATE_REVIEW=google#4966 from llalitkumarrr:main ff7a846
PiperOrigin-RevId: 896062781

Change-Id: Ifcb6579557e283962a291e518721a85dabc786fb
PiperOrigin-RevId: 896063584
Change-Id: I9acb4f8286f6cae610642437b20e2078705ad9f3
Remove extra "would you like to proceed" from ToS message since it is asked later. For checking express eligibility, if the API response is "ELIGIBLE" then it is also considered eligible.

Co-authored-by: Kathy Wu <wukathy@google.com>
PiperOrigin-RevId: 896066249
Change-Id: Ib21227234fc5624ef05fc045ee2d2eb3bbb28441
…nsport, protocol version, and auth headers

Co-authored-by: Kathy Wu <wukathy@google.com>
PiperOrigin-RevId: 896068855
Change-Id: Ib1a6843cec757b43e8887aec3cd1c617983c5668
Close google#4993

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 896076625
Change-Id: Ic175bb1f65e04cf7b8c15779beb7f10e5a641eda
We recently overhauled our MCP Toolbox documentation and moved it to a new domain. This PR propagates that URL change here.

PiperOrigin-RevId: 896118946
Change-Id: I77d474b8d120263f7d025b3cad900ae8b84cf0d8
PiperOrigin-RevId: 896133436
Change-Id: Ifc277e9ab9770a4646850ef01ea5bfa0163a54b1
PiperOrigin-RevId: 896153946
Change-Id: Ic7744bac8b49c0271d8f0f107943a193a90a5302
PiperOrigin-RevId: 896191354
Change-Id: I14cdb2357316d6a4d1931ae8583bc3e6f4d92201
When an MCP server (or any toolset) is unavailable or raises an
exception during get_tools(), the error previously propagated uncaught
through _convert_tool_union_to_tools() and canonical_tools(), crashing
the entire agent silently with no log output.

This change wraps the tool resolution call in a try-except that catches
exceptions, logs a warning with the toolset class name and error details,
and returns an empty tool list.

Cherry-picked from 5df03f1.
Manually applied to v2 path (agents/llm/_single_llm_agent.py).

PiperOrigin-RevId: 890512146
Change-Id: Ic26b0cf3bf79a471e161b18d3da03a0779573340
V4 still uses deprecated Node.js 20.

Co-authored-by: Liang Wu <wuliang@google.com>
PiperOrigin-RevId: 882275971
Change-Id: Ie45f89984786b8a1d94026bf4cdeb1699e51092d
The workflow now accepts a `release_branch` input, checks out the specified release branch, and updates the version in `src/google/adk/version.py`

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 890134954
Change-Id: Id6a9c8aef5bd5227e59279f1e4b52f689b627086
- Add missing `import json` in vertex_ai_session_service.py
- Fix `contents` → `_contents` reference in test_compaction.py

Change-Id: Ia9b6a3ed6f902189e67e42bd5dbc9e8c8b33b866
… real Context

This aligns with the ADK style guide (Rule 5) by using real components instead of heavy mocks for core domain objects like Context and InvocationContext, making the tests more robust and realistic.

Change-Id: Ifa7e196125a6437c9562da0ebad5935055a397b7
Change-Id: I372f2ed93ffdb7c4352bcfef97d86d3caf9479bf
Add safety_acknowledgement='true' to the function response in ComputerUseTool after user confirmation, satisfying the Gemini API requirement.

Change-Id: I1e8e1c0b8eb71d203c6744f56439c73bbd9263e1
Change-Id: I94a186a7d793d2215d40730b964655d151d7cce8
Stabilize V2 workflow engine HITL tests in non-resumable mode and improve test quality.

1. Fixed Newly Passed Tests:
   - test_workflow_request_input_resume: Updated assertion to ignore redundant output events triggered by non-resumable optimization.
   - test_rerun_on_resume_waits_for_all_interrupts: Updated path-matching logic to use startswith instead of exact match to handle invocation suffixes in V2.
   - test_workflow_yield_output_and_request_input_raises: Renamed to test_workflow_allows_mixing_output_and_request_input. Removed xfail and exception expectation since V2 allows mixing outputs and interrupts.

2. Accounted for XFAIL and XPASS:
   - Maintained xfail for tests requiring complex cross-agent state restoration (e.g., test_workflow_pause_and_resume). These are known V2 limitations.
   - Maintained xfail for tests expecting strict V1-style state checkpoint events in resumable=True mode.
   - Acknowledged xpass results for some functional tests, which pass because they do not aggressively assert on the specific structure of checkpoint events.

3. Code Quality:
   - Moved custom node definitions inside test functions inline (ADK Style Guide Rule 7).
   - Removed unused imports and clean-up variables.

Change-Id: Ie6428c6e0f82da2e0518edee813bba9f5afbd813
…le turns

This change is mainly to fix tests/unittests/flows/llm_flows/test_agent_transfer.py.

1. src/google/adk/workflow/_v1_llm_agent_wrapper.py
- Use build_node instead of hardcoding wrapper: Previously, the code hardcoded _V1LlmAgentWrapper for the target agent during transfer. If the target was a non-LlmAgent (like LoopAgent), it failed with an AttributeError because it lacked the mode attribute. Using build_node allows the factory to choose the correct node type (e.g., AgentNode for LoopAgent).
- Adjust event order: Moved await ctx.run_node(...) before yielding the end_of_agent state event. This ensures that events from the child node are emitted before the parent node's completion event, matching the order expected by the tests.

2. src/google/adk/workflow/_agent_node.py
- Preserve event author via context: When AgentNode wraps an agent with sub-agents, the sub-agent events carry their own names as authors. However, NodeRunner would blindly override them with the node's name. By dynamically setting ctx.event_author = event.author during yielding, we preserve the original author information.

3. src/google/adk/runners.py
- Add is_resumable check in routing: In _find_agent_to_run, the system originally routed to the agent that called a function if the last event was a function response. However, in non-resumable scenarios with a fresh text message, this caused incorrect routing. Adding the is_resumable check ensures we only use this branch for active resume operations, falling back to the root agent otherwise.

Change-Id: Idffbd8fe6a823978ddf5186fa2311ae2464fe902
Change-Id: I3a5988950b1d5c1825bdf0068b68f71230d94667
boyangsvl and others added 12 commits April 20, 2026 17:59
Add a new sample 'auth_oauth' demonstrating how to request a GitHub OAuth token in a workflow and use it to list repositories. Also rename the 'auth_config' sample to 'auth_api_key' for consistency.

Change-Id: I014b2f46f74b3017be89efc092380087179cf533
Two ADK source files import third-party packages that are not declared
in [project] dependencies, leaving bare `pip install google-adk` one
transitive resolver change away from breaking on `from google.adk import
Runner`:

* utils/model_name_utils.py and cli/cli_deploy.py do
  `from packaging.version import …` at module top level. Reachable from
  the canonical import chain (runners.py -> telemetry.tracing ->
  utils.model_name_utils) and from `adk --help`. Reproduced today by
  uninstalling packaging from a fresh venv.
* tools/environment_simulation/environment_simulation_config.py does
  `from pydantic_core import ValidationError`. pydantic-core ships with
  pydantic, so this works at runtime, but the dep is undeclared. pydantic
  re-exports ValidationError, so swap the import.

Adds packaging>=21.0 to main deps and switches the env_simulation import
to pydantic. Includes guard tests in tests/unittests/test_release_
dependencies.py that pin the contract.

Change-Id: Idab2bffe8ae1d66aa049801fbfff9569af7b7687
Co-authored-by: Sasha Sobran <asobran@google.com>
PiperOrigin-RevId: 902757902
Change-Id: I93ede8bf16feffb5fe72bea88a79029300975e83
Co-authored-by: Sasha Sobran <asobran@google.com>
PiperOrigin-RevId: 902898232
Change-Id: I98ccedb70a7e3ce1a7f90d1f236acb0fb551e464
PiperOrigin-RevId: 901509317
Change-Id: I6e29638b71e3f762d279309c95047a539b0402ab
Co-authored-by: Sasha Sobran <asobran@google.com>
Updated references from 'Alpha' to 'Beta' and updated the installation example version to 2.0.0b1.

Change-Id: I01a28b7ff7be5cc3281e94e86e19bd083e151de8
Five doc bugs in the adk-workflow skill produce import errors,
attribute errors, or misleading guidance against the published
google-adk wheel and v2 source. Each verified empirically against
google-adk==2.2.0 and the v2 dev branch:

- advanced-patterns.md: remove hard-coded file:///Users/deanchen/...
  link to dynamic-nodes.md; use a relative path.
- testing.md: rewrite examples to use the public InMemoryRunner from
  google.adk.runners instead of `tests.unittests.workflow.testing_utils`
  (which raises ModuleNotFoundError under pip install). Replace
  MockModel with a FakeLlm(BaseLlm) pattern. Three rewritten snippets
  executed end-to-end against google-adk==2.2.0 and pass.
- llm-agent-nodes.md: correct the claim that LlmAgentWrapper outputs
  types.Content. process_llm_agent_output (in _llm_agent_wrapper.py)
  sets event.output = text (a str) when output_schema is unset, and
  the validated model_dump dict when set. Drop the
  workaround section and update the summary table.
- parallel-and-fanout.md, import-paths.md: stop importing the
  non-existent `ParallelWorker` from the private `_parallel_worker`
  module (only `_ParallelWorker` exists and the path is private).
  Rewrite samples to use the documented `parallel_worker=True` flag.
- state-and-events.md, advanced-patterns.md: drop `triggered_by`,
  `in_nodes`, `execution_id`, `retry_count`, and
  `get_next_child_execution_id` from the Context property/method
  tables and code samples — none of them exist on Context in v2.
  Rename `retry_count` → `attempt_count` (the live name).
@adk-bot adk-bot added the documentation [Component] This issue is related to documentation, it will be transferred to adk-docs label Jun 10, 2026
@freddypatota

Copy link
Copy Markdown
Contributor Author

CI failures here are pre-existing on v2 and unrelated to this PR. tests/unittests/test_release_dependencies.py:31 does an unconditional import tomllib, which fails on Python 3.10 with ModuleNotFoundError. The 3.11–3.14 jobs are then cancelled by the matrix's fail-fast policy mid-run, not failing on their own merits.

Same failure is reproducible on the most recent unrelated v2 PR (#5938), which never touched any Python code.

This PR only modifies markdown files under .agents/skills/adk-workflow/references/, i.e. no source code, no test code.

@freddypatota freddypatota marked this pull request as ready for review June 10, 2026 00:34
process_llm_agent_output writes event.output internally, but the
framework clears it before the event surfaces to user code in
runner.run_async(...). The validated value still flows forward to the
next node's node_input and to session.state[output_key] — the user-
facing surface that matters.

Spell out the caveat and direct readers to alternative assertion
targets so tests do not false-fail when checking event.output on an
LLM agent's own event.
@freddypatota

Copy link
Copy Markdown
Contributor Author

Added one follow-up commit (4a61d38): an observability caveat on the LLM Agent Output Types section.

While doing a fuller verification pass of the skill, I traced process_llm_agent_output end-to-end against google-adk==2.2.0: the function does set event.output (matching the description in this PR), but the framework clears that field before the event reaches user code in runner.run_async(...). The validated value still flows to the next node's node_input and to session.state[output_key] — the user-facing surfaces — so the summary table in this PR remains correct. The new commit just spells out that event.output itself isn't the right thing to assert on for an LLM agent's event, and points to the alternatives.

@rohityan rohityan self-assigned this Jun 11, 2026
@rohityan rohityan added the needs review [Status] The PR/issue is awaiting review from the maintainer label Jun 11, 2026
@rohityan rohityan requested a review from GWeale June 11, 2026 22:21
@rohityan

Copy link
Copy Markdown
Collaborator

Hi @GWeale , can you please review this.

@DeanChensj DeanChensj assigned DeanChensj and unassigned rohityan Jun 15, 2026
@DeanChensj

Copy link
Copy Markdown
Collaborator

Hi @freddypatota , thanks for the great PR, note that you are sending PR against the v2 branch, and we are now fully migrating the main branch

@DeanChensj DeanChensj changed the base branch from v2 to main June 15, 2026 22:17
copybara-service Bot pushed a commit that referenced this pull request Jun 16, 2026
Merge #6044

## Summary

Fixes 5 documentation bugs in the `adk-workflow` skill on `v2`. Each is empirically verified against `google-adk==2.2.0` and `v2` source. Full verification transcript inline below.

### Fixes in this PR

1. **`advanced-patterns.md`** — Remove the hard-coded local-filesystem URL `file:///Users/deanchen/Desktop/...` left in a doc link; replace with the relative path `dynamic-nodes.md`.

2. **`testing.md`** — All examples imported from `tests.unittests.*`, which is not in the published `google-adk` wheel. Rewrite to use the public `from google.adk.runners import InMemoryRunner` plus a small inline `run()` helper. Three rewritten snippets (basic workflow, state, parallel worker) were executed end-to-end against `google-adk==2.2.0` and pass. The `MockModel` section is replaced with a `FakeLlm(BaseLlm)` pattern that uses only public symbols.

3. **`llm-agent-nodes.md`** — The doc claims "LlmAgentWrapper outputs `types.Content`, NOT `str`." The source (`_llm_agent_wrapper.process_llm_agent_output`) sets `event.output = text` (a `str`) when `output_schema` is unset, and the validated dict when set. Rewrite the section, the table, and drop the "use `Any` and extract text" workaround that depended on the wrong claim.

4. **`parallel-and-fanout.md` + `import-paths.md`** — Drop `from google.adk.workflow._parallel_worker import ParallelWorker`. The class doesn't exist under that name; only the private `_ParallelWorker` does. The same files already document the public API (`parallel_worker=True` flag on `@node` or `LlmAgent`) — rewrite samples to use it consistently.

5. **`state-and-events.md` (+ one cross-reference in `advanced-patterns.md`)** — Drop `triggered_by`, `in_nodes`, `execution_id`, `retry_count` from the `Context` property tables and code samples. None of them exist on `Context` in v2 source (verified by `grep` in `src/google/adk/agents/context.py`). Rename `retry_count` → `attempt_count` (the live name). Also drop `get_next_child_execution_id` from the methods table for the same reason.

### Scope

All five fixes target the same file tree (`.agents/skills/adk-workflow/references/`) with the same concern: "skill docs reference symbols/imports/paths that don't exist." Per CONTRIBUTING.md "small, focused PRs", they're bundled because each is a surgical edit and they share verification setup. Happy to split if reviewers prefer.

## Testing plan

Doc-only changes. No source code or behavior modified.

1. **Pip-install reproduction** of every bug claimed in a clean venv with `google-adk==2.2.0`. See "Verification details" below for the verbatim `ImportError`, `hasattr == False`, and source quotes that prove each claim.

2. **Rewritten `testing.md` snippets executed end-to-end** against `google-adk==2.2.0`:
   - `test_simple_workflow` — PASSED
   - `test_state_management` — PASSED
   - `test_parallel_worker` — PASSED

3. **Pre-commit hooks** ran clean on the changed files. `mdformat` is excluded for `.agents/` by the repo's `.pre-commit-config.yaml`, and the other hooks (`isort`, `pyink`, `addlicense`) target Python/shell files only.

## Notes for reviewers

- All claims are pinned to `2.2.0` + `v2` HEAD as of the date of this PR.
- The `testing.md` rewrite is the largest delta (~500 lines), but almost every line either drops a `tests.unittests.*` import or replaces a `testing_utils.X` call with public-API equivalents.
- A previously-considered "bug" (parallel-worker naming as `{name}__{index}`) was dropped from this PR after confirming it had already been fixed on v2 to use `{name}@{run_id}` with `run_id` starting at `"1"`.

---

## Verification details

Setup:

```bash
uv venv --python 3.13 .venv && source .venv/bin/activate
uv pip install google-adk
python -c "import google.adk; print(google.adk.__version__)"
# -> 2.2.0
```

### Bug 1 — Hard-coded `file:///` URL

```
$ grep -n 'file:///' .agents/skills/adk-workflow/references/advanced-patterns.md
38:See the dedicated [Dynamic Node Scheduling Reference](file:///Users/deanchen/Desktop/adk-workflow/.agents/skills/adk-workflow/references/dynamic-nodes.md) for detailed rules, examples, and best practices.
```

A developer's local filesystem path leaked into the published skill.

### Bug 2 — `tests.unittests...` imports unreachable from `pip install`

```python
>>> from tests.unittests.workflow import testing_utils
ModuleNotFoundError: No module named 'tests'

>>> from tests.unittests.testing_utils import InMemoryRunner, MockModel
ModuleNotFoundError: No module named 'tests'
```

The `tests/` directory ships only in the source repo, not in the installed `google-adk` wheel. Any user copying these snippets gets `ModuleNotFoundError`. The PR rewrites samples to use the public `from google.adk.runners import InMemoryRunner` and demonstrates a publicly-importable mock pattern (subclass `BaseLlm`).

The three rewritten snippets (basic, state, parallel) were run end-to-end against `google-adk==2.2.0` and all three passed.

### Bug 3 — `LlmAgentWrapper` output type doc is wrong

The skill claims: *"LlmAgentWrapper outputs `types.Content`, NOT `str`."*

The source in both `2.2.0` and `v2` says otherwise. From `src/google/adk/workflow/_llm_agent_wrapper.py`:

```python
def process_llm_agent_output(agent: Any, ctx: Context, event: Event) -> None:
    ...
    text = (
        ''.join(p.text for p in event.content.parts if p.text and not p.thought)
        if event.content.parts else ''
    )
    if agent.output_schema:
        if text.strip():
            output = validate_schema(agent.output_schema, text)
        else:
            output = None
    else:
        output = text          # <-- str, not types.Content
    ...
    event.output = output
```

When `output_schema` is unset, `event.output` is the concatenated string of the model's text parts. When `output_schema=MyModel` is set, it's the validated `model_dump()` dict. The PR rewrites the section, table, and the "use `Any` and extract text" workaround that depended on the wrong claim.

### Bug 4 — `_parallel_worker.ParallelWorker` is not importable

The class doesn't exist under that name — only the underscore-prefixed `_ParallelWorker` does, and that path is private:

```python
>>> from google.adk.workflow._parallel_worker import ParallelWorker
ImportError: cannot import name 'ParallelWorker' from 'google.adk.workflow._parallel_worker'

>>> from google.adk.workflow._parallel_worker import _ParallelWorker
>>> _ParallelWorker
<class 'google.adk.workflow._parallel_worker._ParallelWorker'>
```

The recommended public API is the `parallel_worker=True` flag — already documented as preferred in the same files. Verified end-to-end:

```python
from google.adk.workflow import node, Workflow

@node(parallel_worker=True)
def double(node_input: int) -> int:
    return node_input * 2

# Workflow constructs OK; `double` is an internal _ParallelWorker
# under the hood — no user-visible private-API surface needed.
```

PR drops the private import from `parallel-and-fanout.md` and the `import-paths.md` table, and rewrites samples to use the flag.

### Bug 5 — Removed `Context` properties documented as live

```python
>>> from google.adk.agents.context import Context
>>> for name in ['triggered_by', 'in_nodes', 'execution_id', 'retry_count', 'attempt_count']:
...     print(name, hasattr(Context, name))
triggered_by False
in_nodes False
execution_id False
retry_count False
attempt_count True
```

The same is true on `v2` source — `grep` for those names in `src/google/adk/agents/context.py` returns nothing, while `attempt_count` has 4 hits.

Code samples using `ctx.retry_count` / `ctx.triggered_by` raise `AttributeError`. PR removes the four absent properties from the docs and renames `retry_count` → `attempt_count` everywhere it's mentioned. `get_next_child_execution_id` is also gone from `Context` on v2; the PR removes it from the methods table for the same reason.

Co-authored-by: Shangjie Chen <deanchen@google.com>
COPYBARA_INTEGRATE_REVIEW=#6044 from freddypatota:fix/adk-workflow-skill-doc-bugs 4a61d38
PiperOrigin-RevId: 932770088
@adk-bot

adk-bot commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

Thank you @freddypatota for your contribution! 🎉

Your changes have been successfully imported and merged via Copybara in commit 24a1b26.

Closing this PR as the changes are now in the main branch.

@adk-bot adk-bot added the merged [Status] This PR is merged label Jun 16, 2026
@adk-bot adk-bot closed this Jun 16, 2026
@freddypatota freddypatota deleted the fix/adk-workflow-skill-doc-bugs branch June 22, 2026 09:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation [Component] This issue is related to documentation, it will be transferred to adk-docs merged [Status] This PR is merged needs review [Status] The PR/issue is awaiting review from the maintainer

Projects

None yet

Development

Successfully merging this pull request may close these issues.