Skip to content

Commit 5fd8720

Browse files
authored
fix: align RunState schema policy with release boundaries (#2632)
1 parent 4f40c01 commit 5fd8720

File tree

4 files changed

+29
-19
lines changed

4 files changed

+29
-19
lines changed

.agents/skills/implementation-strategy/SKILL.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,17 @@ Use this skill before editing code when the task changes runtime behavior or any
1919
```
2020
3. Judge breaking-change risk against that latest release tag, not against unreleased branch churn or post-tag changes already on `main`.
2121
4. Prefer the simplest implementation that satisfies the current task. Update callers, tests, docs, and examples directly instead of preserving superseded unreleased interfaces.
22-
5. Add a compatibility layer only when there is a concrete released consumer or durable external state that requires it, or when the user explicitly asks for a migration path.
22+
5. Add a compatibility layer only when there is a concrete released consumer or an explicitly supported durable external state boundary that requires it, or when the user explicitly asks for a migration path.
2323

2424
## Compatibility boundary rules
2525

2626
- Released public API or documented external behavior: preserve compatibility or provide an explicit migration path.
27-
- Persisted schema, serialized state, wire protocol, CLI flags, environment variables, and externally consumed config: treat as compatibility-sensitive even if the implementation is local.
28-
- Python-specific durable surfaces such as `RunState`, session persistence, exported dataclass constructor order, and documented model/provider configuration should be treated as compatibility-sensitive when they were part of the latest release tag.
27+
- Persisted schema, serialized state, wire protocol, CLI flags, environment variables, and externally consumed config: treat as compatibility-sensitive when they are part of the latest release or when the repo explicitly intends to preserve them across commits, processes, or machines.
28+
- Python-specific durable surfaces such as `RunState`, session persistence, exported dataclass constructor order, and documented model/provider configuration should be treated as compatibility-sensitive when they were part of the latest release tag or are explicitly supported as a shared durability boundary.
2929
- Interface changes introduced only on the current branch: not a compatibility target. Rewrite them directly.
30-
- Interface changes present on `main` but added after the latest release tag: not a semver breaking change by themselves. Rewrite them directly unless they already define durable external state.
30+
- Interface changes present on `main` but added after the latest release tag: not a semver breaking change by themselves. Rewrite them directly unless they already define a released or explicitly supported durable external state boundary.
3131
- Internal helpers, private types, same-branch tests, fixtures, and examples: update them directly instead of adding adapters.
32+
- Unreleased persisted schema versions on `main` may be renumbered or squashed before release when intermediate snapshots are intentionally unsupported. When you do that, update the support set and tests together so the boundary is explicit.
3233

3334
## Default implementation stance
3435

AGENTS.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,13 @@ When working on OpenAI API or OpenAI platform integrations in this repo (Respons
3232

3333
#### `$implementation-strategy`
3434

35-
Before changing runtime code, exported APIs, external configuration, persisted schemas, wire protocols, or other user-facing behavior, use `$implementation-strategy` to decide the compatibility boundary and implementation shape. Judge breaking changes against the latest release tag, not unreleased branch-local churn. Interfaces introduced or changed after the latest release tag may be rewritten without compatibility shims unless they define durable external state or the user explicitly asks for a migration path.
35+
Before changing runtime code, exported APIs, external configuration, persisted schemas, wire protocols, or other user-facing behavior, use `$implementation-strategy` to decide the compatibility boundary and implementation shape. Judge breaking changes against the latest release tag, not unreleased branch-local churn. Interfaces introduced or changed after the latest release tag may be rewritten without compatibility shims unless they define a released or explicitly supported durable external state boundary, or the user explicitly asks for a migration path. Unreleased persisted formats on `main` may be renumbered or squashed before release when intermediate snapshots are intentionally unsupported.
3636

3737
### ExecPlans
3838

39-
Call out compatibility risk early in your plan only when the change affects behavior shipped in the latest release tag or durable external state, and confirm the approach before implementing changes that could impact users.
39+
Call out compatibility risk early in your plan only when the change affects behavior shipped in the latest release tag or a released or explicitly supported durable external state boundary, and confirm the approach before implementing changes that could impact users.
4040

41-
Use an ExecPlan when work is multi-step, spans several files, involves new features or refactors, or is likely to take more than about an hour. Start with the template and rules in `PLANS.md`, keep milestones and living sections (Progress, Surprises & Discoveries, Decision Log, Outcomes & Retrospective) up to date as you execute, and rewrite the plan if scope shifts. Call out compatibility risk only when the plan changes behavior shipped in the latest release tag or durable external state. Do not treat branch-local interface churn or unreleased post-tag changes on `main` as breaking by default; prefer direct replacement over compatibility layers in those cases. If you intentionally skip an ExecPlan for a complex task, note why in your response so reviewers understand the choice.
41+
Use an ExecPlan when work is multi-step, spans several files, involves new features or refactors, or is likely to take more than about an hour. Start with the template and rules in `PLANS.md`, keep milestones and living sections (Progress, Surprises & Discoveries, Decision Log, Outcomes & Retrospective) up to date as you execute, and rewrite the plan if scope shifts. Call out compatibility risk only when the plan changes behavior shipped in the latest release tag or a released or explicitly supported durable external state boundary. Do not treat branch-local interface churn or unreleased post-tag changes on `main` as breaking by default; prefer direct replacement over compatibility layers in those cases, and renumber or squash unreleased persisted schemas before release when the intermediate snapshots are intentionally unsupported. If you intentionally skip an ExecPlan for a complex task, note why in your response so reviewers understand the choice.
4242

4343
### Public API Positional Compatibility
4444

@@ -84,7 +84,7 @@ The OpenAI Agents Python repository provides the Python Agents SDK, examples, an
8484
- `src/agents/stream_events.py` (stream event names)
8585
- `src/agents/run_state.py` (RunState serialization/deserialization)
8686
- `src/agents/run_internal/session_persistence.py` (session save/rewind)
87-
- If the serialized RunState shape changes, bump `CURRENT_SCHEMA_VERSION` in `src/agents/run_state.py` and update serialization/deserialization accordingly.
87+
- If the serialized RunState shape changes, update `CURRENT_SCHEMA_VERSION` in `src/agents/run_state.py` and the related serialization/deserialization logic. Keep released schema versions readable, and feel free to renumber or squash unreleased schema versions before release when those intermediate snapshots are intentionally unsupported.
8888

8989
## Operation Guide
9090

@@ -102,7 +102,7 @@ The OpenAI Agents Python repository provides the Python Agents SDK, examples, an
102102
```
103103
2. If dependencies changed or you are setting up the repo, run `make sync`.
104104
3. Implement changes and add or update tests alongside code updates.
105-
4. Highlight compatibility or API risks in your plan before implementing changes that alter the latest released behavior or durable external state.
105+
4. Highlight compatibility or API risks in your plan before implementing changes that alter the latest released behavior or a released or explicitly supported durable external state boundary.
106106
5. Build docs when you touch documentation:
107107
```bash
108108
make build-docs

src/agents/run_state.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -112,14 +112,14 @@
112112

113113

114114
# RunState schema policy.
115-
# 1. Bump CURRENT_SCHEMA_VERSION when serialized shape/semantics change.
116-
# 2. Keep older readable versions in SUPPORTED_SCHEMA_VERSIONS for backward reads.
115+
# 1. Keep schema versions shipped in releases readable.
116+
# 2. Unreleased schema versions may be renumbered or squashed before release when their
117+
# intermediate snapshots are intentionally unsupported.
117118
# 3. to_json() always emits CURRENT_SCHEMA_VERSION.
118-
# 4. Forward compatibility is intentionally fail-fast (older SDKs reject newer versions).
119-
CURRENT_SCHEMA_VERSION = "1.7"
120-
SUPPORTED_SCHEMA_VERSIONS = frozenset(
121-
{"1.0", "1.1", "1.2", "1.3", "1.4", "1.5", "1.6", CURRENT_SCHEMA_VERSION}
122-
)
119+
# 4. Forward compatibility is intentionally fail-fast (older SDKs reject newer or unsupported
120+
# versions).
121+
CURRENT_SCHEMA_VERSION = "1.5"
122+
SUPPORTED_SCHEMA_VERSIONS = frozenset({"1.0", "1.1", "1.2", "1.3", "1.4", CURRENT_SCHEMA_VERSION})
123123

124124
_FUNCTION_OUTPUT_ADAPTER: TypeAdapter[FunctionCallOutput] = TypeAdapter(FunctionCallOutput)
125125
_COMPUTER_OUTPUT_ADAPTER: TypeAdapter[ComputerCallOutput] = TypeAdapter(ComputerCallOutput)

tests/test_run_state.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3849,11 +3849,12 @@ async def test_from_json_missing_schema_version(self):
38493849
await RunState.from_json(agent, state_json)
38503850

38513851
@pytest.mark.asyncio
3852-
async def test_from_json_unsupported_schema_version(self):
3852+
@pytest.mark.parametrize("schema_version", ["1.6", "1.7", "2.0"])
3853+
async def test_from_json_unsupported_schema_version(self, schema_version: str):
38533854
"""Test that from_json raises error when schema version is unsupported."""
38543855
agent = Agent(name="TestAgent")
38553856
state_json = {
3856-
"$schemaVersion": "2.0",
3857+
"$schemaVersion": schema_version,
38573858
"original_input": "test",
38583859
"current_agent": {"name": "TestAgent"},
38593860
"context": {
@@ -3867,7 +3868,9 @@ async def test_from_json_unsupported_schema_version(self):
38673868
"generated_items": [],
38683869
}
38693870

3870-
with pytest.raises(UserError, match="Run state schema version 2.0 is not supported"):
3871+
with pytest.raises(
3872+
UserError, match=f"Run state schema version {schema_version} is not supported"
3873+
):
38713874
await RunState.from_json(agent, state_json)
38723875

38733876
@pytest.mark.asyncio
@@ -3895,6 +3898,12 @@ async def test_from_json_accepts_previous_schema_version(self):
38953898
assert restored._context is not None
38963899
assert restored._context.context == {"foo": "bar"}
38973900

3901+
def test_supported_schema_versions_match_released_boundary(self):
3902+
"""The support set should include released versions plus the current unreleased writer."""
3903+
assert SUPPORTED_SCHEMA_VERSIONS == frozenset(
3904+
{"1.0", "1.1", "1.2", "1.3", "1.4", CURRENT_SCHEMA_VERSION}
3905+
)
3906+
38983907
@pytest.mark.asyncio
38993908
async def test_from_json_agent_not_found(self):
39003909
"""Test that from_json raises error when agent is not found in agent map."""

0 commit comments

Comments
 (0)