Skip to content

feat(skill): object-store names for skill inputs and CLI flags; drop two S3-form tombstones - #100

Merged
earakely-scale merged 1 commit into
mainfrom
edgararakelyan/skill-object-url
Oct 7, 2026
Merged

earakely-scale merged 1 commit into
mainfrom
edgararakelyan/skill-object-url

Conversation

@earakely-scale

@earakely-scale earakely-scale commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Skill.object_url replaces Skill.s3_url. It's an object-store prefix like any other.

  • Reading stored tasks: stored add_skills steps carry s3_url, so from_dict reads it permanently. It reads the legacy key first, as the artifact twins do.
  • Writing: to_dict writes both keys, because a worker still on an older version reads only s3_url.
  • Old spelling: the s3_url= keyword still constructs a Skill, warning through warn_deprecated, so its callers are counted. skill.s3_url still reads as object_url. A Skill copied with dataclasses.replace gets the same s3_url value it already has, which isn't counted.
  • deploy_agent's skills entries: read object_url, keeping skill_s3_url and s3_uri as read aliases.

CLI.

  • New flags: a2a-agent add-skill --skill-object-url, artifact skill put --prefix-url and artifact file-artifact-universe put-bundled --prefix-url.
  • --skill-s3-url and --s3-url remain as hidden flags that print their replacement on stderr. Giving both spellings is a usage error.
  • Output labels now read skill_object_url=, bundle_object_url= and cli_object_url=.

Skill helpers. parse_skill_md and fetch_skill_md are 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 criterion secret_code_object. Only secret_code_inline is read back.

Two refusals left over from removing the S3 transfer forms are dropped:

  • A stored deploy step with agent_changelog_s3_prefix now loads without it, like the other S3-only fields. No stored task carries one.
  • A trajectory get answered with only trajectory_s3_prefix now yields no trajectory instead of a named error. That refusal hasn't fired in 14 days.

Testing

  • Unit:
    • A skill named by object_url carries both spellings and doesn't warn.
    • The s3_url keyword warns, is counted and still works.
    • Conflicting spellings raise TypeError; a copy doesn't.
    • Stored skills load keyed legacy, neutral, or both (legacy wins).
    • An inline skill reads s3_url as None.
    • deploy_agent's skills dict negotiates a bundle from object_url, skill_s3_url or s3_uri.
    • put-bundled and skill put work under either flag, with a stderr notice for the old one; giving both is exit 2.
    • The helper aliases are the public functions.
    • The tests that pinned the two refusals now pin the new behaviour.
    • Suite: 6175 passed, 13 skipped.
  • Mutants (out-of-tree): 12 of 12 caught. Each of these fails a test:
    • to_dict writing one key;
    • from_dict reading neutral-first, or legacy-only;
    • the keyword not warning;
    • copies conflicting;
    • dropping the s3_url property;
    • deploy_agent not reading object_url;
    • the old flag not noting its replacement;
    • both flags accepted;
    • dropping a private alias;
    • restoring either refusal.
  • Dev stage (Mongo):
    • A task whose add_skills step names a skill by object_url is stored with both keys, equal.
    • 0.9.1282 loads it through s3_url.
    • This branch loads it, and also a stored dev task whose skill has only s3_url.

Labelled bump-version.

🤖 Generated with Claude Code

RetriggerConfidence Score: 3/5

The PR is not ready to merge until copied skills can change URLs and mixed deploy skill entries keep their valid URL.

Fix All in CursorFindings

  1. P1 Copied skill URL cannot change ▶
  2. P1 Legacy skill URL gets dropped ▶
  3. P2 Old helper patches stop working ▶
Fix with agent prompt
### Issue 1
src/agent_env/task_step/task_steps/add_skills.py:47-50
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.

### Issue 2
src/agent_env/task_step/task_steps/deploy_agent.py:79-80
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.

### Issue 3
src/agent_env/artifact/artifacts/skill.py:75-76
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

Skills and CLI commands now use object_url names 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.

  • Skills accept object_url while old saved names still work.
  • Commands use object-store names for skill and bundle prefixes.
  • A trajectory reply with only an S3 prefix no longer stops the fetch.
  • Stored deploy steps load past the old changelog prefix field.
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"]
Loading

Reviews (1) · Last reviewed commit: "feat(skill): object-store names for skil..." · Reviewed by Greptile

…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>
@earakely-scale
earakely-scale requested a review from a team as a code owner October 7, 2026 18:20
Comment on lines +47 to +50
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)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 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.

Fix in Cursor Fix in Claude Code Fix in Codex

Comment on lines 79 to +80
"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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 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.

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.

Fix in Cursor Fix in Claude Code Fix in Codex

Comment on lines +75 to +76
skill_md = fetch_skill_md(object_url)
frontmatter, _body = parse_skill_md(skill_md)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 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.

Fix in Cursor Fix in Claude Code Fix in Codex

@earakely-scale
earakely-scale merged commit 4886834 into main Oct 7, 2026
13 checks passed
@earakely-scale
earakely-scale deleted the edgararakelyan/skill-object-url branch October 7, 2026 18:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant