Skip to content

docs: improve agent guidance#2959

Open
opieter-aws wants to merge 1 commit into
strands-agents:mainfrom
opieter-aws:opieter-aws/agents-md-convention-audit
Open

docs: improve agent guidance#2959
opieter-aws wants to merge 1 commit into
strands-agents:mainfrom
opieter-aws:opieter-aws/agents-md-convention-audit

Conversation

@opieter-aws

@opieter-aws opieter-aws commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Description

AGENTS.md is supposed to steer coding agents toward house conventions, but an audit of both SDKs against their own guidance turned up three structural problems: the highest-traffic patterns (adding a model provider, the streaming contract) weren't documented at all; several judgment rules lived only in side docs an agent reading AGENTS.md never opens; and the two sub-SDK guides had drifted into different shapes with duplicated, occasionally-contradictory rules. This PR restructures all three AGENTS.md files around a shared skeleton, moves genuinely cross-SDK rules into the root so they can't diverge, documents the missing high-traffic patterns, and trims prose that merely restates what the linters already enforce.

It is documentation-only — no source or behavior changes. The companion Python docs/TESTING.md (and the test/integ/ path fix) landed separately in #2961, which this builds on.

Single source of truth for shared rules. Plugin naming, cross-SDK literal parity, public/internal API marking, the logging format, evergreen comments, and directory-naming parity now live once in the root AGENTS.md; both sub-guides point to it instead of carrying their own (previously diverging) copies.

Shared skeleton. Both sub-guides now follow the same section order (Overview → Directory Structure → Workflow → Coding Patterns → Adding a Model Provider → Async & Streaming → Testing → Quick Rules → …), so they're mechanically diffable and a rule present in one but missing in the other is visible at a glance. The Things to Do / Things NOT to Do lists collapse into a single Quick Rules block of only the un-lintable rules, each tagged "enforced by review."

Signal over linter noise. Each guide now states up front that ruff/mypy/eslint/prettier already enforce formatting, import order, line length, and type-annotation coverage, and spends its words on what those tools can't check, rather than restating them.

New guidance

  • Root — new "Cross-SDK Conventions" section: plugin/construct naming (behavior, not …Plugin); cross-SDK parity (identifiers re-cased; single-word literals byte-identical; multi-word literals snake_casecamelCase; wire field names kept verbatim; hook-event names shared); public-vs-internal marking; structured-logging format; evergreen comments (incl. tests + the deprecated/legacy nuance); directory & file naming parity.
  • Both — "Adding a Model Provider": the canonical provider skeleton (config shape, validate_config_keys/resolveConfigMetadata, the base contract to implement, flat-file-vs-subdirectory threshold).
  • Both — "Async & Streaming": the async-generator stream contract; Python's keyword-only * before tool_choice, async-first + sync-facade duality, 3.10 floor, cooperative threading.Event cancellation; TS's async-only model and AbortSignal cancellation.
  • Both — error-handling convention: providers translate vendor errors to typed SDK exceptions and preserve the cause; no bare raise Exception(...).
  • Both — descriptive variable names: name every variable for its content, including loop/catch bindings (event/index/error, never e/i/x).
  • Both — Quick Rules block: a compact, review-enforced do/don't list.
  • Python — Data Structures: TypedDict for wire/config shapes, @dataclass for runtime objects, pydantic only for model-read/write schemas, no NamedTuple.
  • Python — type rigor: X | None over Optional[X]; scoped # type: ignore[code] over bare ignores.
  • Python — Public API Surface: explicit __all__; lazy provider loading via __getattr__.
  • Python — read-first gates: "before adding a public interface, read docs/STYLE_GUIDE.md" (Protocol-over-Callable, tool_name over string literals); "before adding a hook event, read docs/HOOKS.md" (event-naming contract).
  • Python — Raises: docstring required when a public function raises as part of its contract.
  • TypeScript — interface vs type rule (object shapes as interface + extends; type for unions/intersections/etc.).
  • TypeScript — additions: prop?: T optional-property form under exactOptionalPropertyTypes; named-exports-only / no export default / kebab-case filenames / @internal; @throws TSDoc tag; a %s/%d printf-in-logs ban; plus a Product Overview and Directory Structure section (the TS guide had neither).
  • Both — directory map honesty: the structure trees end with "key subsystems shown; run ls … for the full list" so an agent doesn't treat the abbreviated tree as a complete inventory.

Removed / trimmed guidance

  • Duplicated cross-SDK rules removed from the sub-guides (now single-sourced in the root) — no rule lost, just de-duplicated.
  • Linter-restating prose trimmed: the verbose Import Organization / Docstring-format / formatting restatements shrink to a one-line "the linter enforces this" pointer.
  • Things to Do / Things NOT to Do removed as standalone lists, folded into the per-guide Quick Rules block.

Changed guidance

  • TS literal-parity rule corrected: was "use exactly the same literal as the Python SDK" — false for multi-word values (tool_usetoolUse). Now: single-word identical, multi-word camelCased in TS, wire names verbatim.
  • TS return-type contradiction resolved: "always explicit return types" vs "leverage type inference" → scoped to "explicit on signatures, infer locals."

Related Issues

None — maintenance change from a convention audit of both SDKs. Builds on #2961.

Documentation PR

Not applicable — these are contributor-facing AGENTS.md files, not user-facing docs under site/.

Type of Change

Documentation update

Testing

Documentation-only. Verified every path and symbol cited in the new guidance exists in the tree (provider skeletons, validate_config_keys/resolveConfigMetadata, STOP_REASON_MAP/snakeToCamel, the referenced docs/ links and directory names), that both sub-guides share the same section skeleton, and that an adversarial pre/post comparison found no convention dropped in the restructure (four directives initially lost were restored).

  • I ran hatch run prepare

Checklist

  • I have read the CONTRIBUTING document
  • I have reviewed and understand every line of code in this PR, including any generated by AI tools, and I can explain why it works
  • My change is focused and reasonably small; I have split unrelated work into separate PRs
  • I have added any necessary tests that prove my fix is effective or my feature works
  • I have updated the documentation accordingly
  • I have added an appropriate example to the documentation to outline the feature, or no new docs are needed
  • My changes generate no new warnings
  • Any dependent changes have been merged and published

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@github-actions github-actions Bot added size/m documentation Documentation changes, improvements, additions, content updates, site improvements, examples, guides chore Maintenance tasks, dependency updates, CI changes, refactoring with no user-facing impact labels Jun 25, 2026
@codecov

codecov Bot commented Jun 25, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@opieter-aws opieter-aws force-pushed the opieter-aws/agents-md-convention-audit branch from 94bf130 to 6be27c3 Compare June 25, 2026 01:34
@github-actions github-actions Bot added size/l and removed size/m labels Jun 25, 2026
@opieter-aws opieter-aws mentioned this pull request Jun 25, 2026
9 tasks
@opieter-aws opieter-aws force-pushed the opieter-aws/agents-md-convention-audit branch from 6be27c3 to 5df8b71 Compare June 25, 2026 16:24
@opieter-aws opieter-aws force-pushed the opieter-aws/agents-md-convention-audit branch from 5df8b71 to 93a4e00 Compare June 25, 2026 18:00
@opieter-aws opieter-aws marked this pull request as ready for review June 25, 2026 18:01
Comment thread strands-py/AGENTS.md
Public/internal marking is a cross-SDK rule — see the [root AGENTS.md](../AGENTS.md). The Python-idiomatic form:

### Error Handling
- **Every public package declares its surface with an explicit `__all__`** in its `__init__.py` (entries listed alphabetically). Internal (`_`-prefixed) and namespace-only packages may omit it. Don't lean on the redundant `import X as X` re-export alias in place of `__all__` for a public package.

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.

Issue: The parenthetical "(entries listed alphabetically)" doesn't match the codebase — and notably not even the file this section points agents to as the reference (models/__init__.py), which lists submodules then classes:

__all__ = ["bedrock", "model", "BaseModelConfig", "BedrockModel", "CacheConfig", "CacheToolsConfig", "Model"]

Same for strands/__init__.py, agent/__init__.py, and tools/__init__.py — none are alphabetized.

Suggestion: Since this PR's whole purpose is to make the guidance match what the code actually does, either drop the "(entries listed alphabetically)" claim or restate the real convention (e.g. modules first, then public symbols). As written, an agent following this would "fix" every __all__ in the repo and produce noisy, wrong diffs.

@github-actions

Copy link
Copy Markdown
Contributor

Issue: The PR description leads with two changes that aren't in this diff. The "New strands-py/docs/TESTING.md" paragraph and the "strands-ts/docs/TESTING.md also referenced a tests_integ/ directory… corrected to test/integ/" fix describe files that are not touched by this PR — both strands-py/docs/TESTING.md and strands-ts/docs/TESTING.md already exist in main identically, and the diff contains only the three AGENTS.md files.

Suggestion: Either include those TESTING.md changes (if they were meant to ship here) or trim the description to match the actual scope. As-is, a reviewer expecting a new file and a path fix will go looking for changes that aren't here. This matters because the description is the contract reviewers check against.

@github-actions

Copy link
Copy Markdown
Contributor

Assessment: Comment (approve-leaning)

Documentation-only restructure of the three AGENTS.md files. I spot-checked the concrete claims against the source and the vast majority hold up well — the Python/TS provider skeletons (validate_config_keys, resolve_config_metadata/resolveConfigMetadata, BaseModelConfig, the keyword-only * before tool_choice, @override + # type: ignore[override]), the X | None ≈99% claim, lazy __getattr__ providers, snakeToCamel/STOP_REASON_MAP, zero export default, exactOptionalPropertyTypes, the interface-vs-type examples, and every referenced docs/ link all resolve correctly. Pulling the genuinely shared rules into the root and gating the un-lintable rules at point-of-use is a solid improvement.

Two items to address
  • Accuracy: The __all__ "(entries listed alphabetically)" claim contradicts the codebase, including the file the section itself cites as the reference — see inline comment. For a PR whose stated goal is making guidance match the code, this one should be corrected or dropped.
  • Description/scope mismatch: The description leads with TESTING.md changes that aren't in this diff — see inline comment.

Neither is blocking; nice cleanup overall.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

chore Maintenance tasks, dependency updates, CI changes, refactoring with no user-facing impact documentation Documentation changes, improvements, additions, content updates, site improvements, examples, guides size/l

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant