Repository navigation
feat(skill): object-store names for skill inputs and CLI flags; drop two S3-form tombstones - #100
Conversation
…ags; drop two S3-form tombstones Skill.object_url replaces Skill.s3_url. Stored add_skills steps carry s3_url, so from_dict reads it permanently (legacy key first, as the artifact twins do), and to_dict writes both while workers on older versions read only s3_url. The s3_url keyword still constructs a Skill, warning through warn_deprecated, and skill.s3_url still reads as object_url. deploy_agent's skills read object_url, keeping skill_s3_url and s3_uri as read aliases. The CLI takes --skill-object-url (a2a-agent add-skill) and --prefix-url (artifact skill put, file-artifact-universe put-bundled); --skill-s3-url and --s3-url stay as hidden flags that note their replacement on stderr. Output labels say skill_object_url=, bundle_object_url=, cli_object_url=. parse_skill_md / fetch_skill_md are public; the private names stay as aliases. The validator's object-store skill fixture is renamed validator-test-object. Two refusals left by the S3 transfer forms' removal go: no stored task carries agent_changelog_s3_prefix any more, which a deploy step now reads without, and an agent answering a trajectory get with only trajectory_s3_prefix gets no trajectory rather than a named error. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
| if s3_url is not OMITTED and s3_url == self.object_url: | ||
| s3_url = OMITTED # how dataclasses.replace copies a Skill: it reads s3_url back off the original | ||
| # Five frames up: through the dataclass's generated __init__ to its caller. | ||
| self.object_url = renamed_keyword("Skill", "object_url", self.object_url, "s3_url", s3_url, stacklevel=5) |
There was a problem hiding this comment.
Copied skill URL cannot change
For a Skill with an existing object_url, dataclasses.replace(skill, object_url=new_url) also passes the old URL through s3_url. This check only handles an unchanged copy, so renamed_keyword raises TypeError instead of creating the updated skill. It also treats an explicit s3_url=None as omitted, so that use is not counted. Separate copies from explicit uses of the old keyword.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agent_env/task_step/task_steps/add_skills.py
Line: 47-50
Comment:
**Copied skill URL cannot change**
For a `Skill` with an existing `object_url`, `dataclasses.replace(skill, object_url=new_url)` also passes the old URL through `s3_url`. This check only handles an unchanged copy, so `renamed_keyword` raises `TypeError` instead of creating the updated skill. It also treats an explicit `s3_url=None` as omitted, so that use is not counted. Separate copies from explicit uses of the old keyword.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| "object_url": ( | ||
| skill.get("skill_s3_url", skill.get("s3_uri")) if skill_md is None else None | ||
| skill.get("object_url", skill.get("skill_s3_url", skill.get("s3_uri"))) if skill_md is None else None |
There was a problem hiding this comment.
If a deploy skill has object_url: None alongside a valid skill_s3_url or s3_uri, dict.get picks the null value. register_skill then sends only the name and description, not the skill bundle. Fall back to the older URL when object_url is null.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agent_env/task_step/task_steps/deploy_agent.py
Line: 79-80
Comment:
**Legacy skill URL gets dropped**
If a deploy skill has `object_url: None` alongside a valid `skill_s3_url` or `s3_uri`, `dict.get` picks the null value. `register_skill` then sends only the name and description, not the skill bundle. Fall back to the older URL when `object_url` is null.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| skill_md = fetch_skill_md(object_url) | ||
| frontmatter, _body = parse_skill_md(skill_md) |
There was a problem hiding this comment.
Old helper patches stop working
The private names _parse_skill_md and _fetch_skill_md still import, but skill validation and the CLI now call parse_skill_md and fetch_skill_md directly. A caller that patches either old name gets the real helper instead. Keep those patches working, or make the change clear and test it.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agent_env/artifact/artifacts/skill.py
Line: 75-76
Comment:
**Old helper patches stop working**
The private names `_parse_skill_md` and `_fetch_skill_md` still import, but skill validation and the CLI now call `parse_skill_md` and `fetch_skill_md` directly. A caller that patches either old name gets the real helper instead. Keep those patches working, or make the change clear and test it.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
Summary
Skill.object_urlreplacesSkill.s3_url. It's an object-store prefix like any other.add_skillssteps carrys3_url, sofrom_dictreads it permanently. It reads the legacy key first, as the artifact twins do.to_dictwrites both keys, because a worker still on an older version reads onlys3_url.s3_url=keyword still constructs aSkill, warning throughwarn_deprecated, so its callers are counted.skill.s3_urlstill reads asobject_url. ASkillcopied withdataclasses.replacegets the sames3_urlvalue it already has, which isn't counted.deploy_agent'sskillsentries: readobject_url, keepingskill_s3_urlands3_urias read aliases.CLI.
a2a-agent add-skill --skill-object-url,artifact skill put --prefix-urlandartifact file-artifact-universe put-bundled --prefix-url.--skill-s3-urland--s3-urlremain as hidden flags that print their replacement on stderr. Giving both spellings is a usage error.skill_object_url=,bundle_object_url=andcli_object_url=.Skill helpers.
parse_skill_mdandfetch_skill_mdare public. Their private names stay as aliases, since callers outside agent-env import and patch them.Validator. The object-store skill fixture is renamed to
validator-test-object/VALIDATOR-OBJ-99, with criterionsecret_code_object. Onlysecret_code_inlineis read back.Two refusals left over from removing the S3 transfer forms are dropped:
agent_changelog_s3_prefixnow loads without it, like the other S3-only fields. No stored task carries one.trajectory_s3_prefixnow yields no trajectory instead of a named error. That refusal hasn't fired in 14 days.Testing
object_urlcarries both spellings and doesn't warn.s3_urlkeyword warns, is counted and still works.TypeError; a copy doesn't.s3_urlasNone.deploy_agent's skills dict negotiates a bundle fromobject_url,skill_s3_urlors3_uri.put-bundledandskill putwork under either flag, with a stderr notice for the old one; giving both is exit 2.to_dictwriting one key;from_dictreading neutral-first, or legacy-only;s3_urlproperty;deploy_agentnot readingobject_url;add_skillsstep names a skill byobject_urlis stored with both keys, equal.s3_url.s3_url.Labelled
bump-version.🤖 Generated with Claude Code
The PR is not ready to merge until copied skills can change URLs and mixed deploy skill entries keep their valid URL.
Fix with agent prompt
Summary
Skills and CLI commands now use
object_urlnames for object-store prefixes while keeping older names usable. Stored skills keep both key spellings for compatibility, and two refusals for old S3-form data are removed.object_urlwhile old saved names still work.Diagram
%%{init: {'theme': 'neutral'}}%% flowchart LR A["Skill object_url"] --> B["to_dict: object_url + s3_url"] B --> C["Stored add_skills step"] C --> D["from_dict: legacy key first"] D --> E["A2AAgent.add_skill"] F["Deploy skills dict"] --> G["_skill_fields"] G --> H["A2AAgent.register_skill"]Reviews (1) · Last reviewed commit: "feat(skill): object-store names for skil..." · Reviewed by Greptile