feat(skills): add skill package schema and agent-side skill loading support #94
Open
CoreRasurae
wants to merge 1 commits from
feature/skill-package-support into master
pull from: feature/skill-package-support
merge into: cleveragents:master
cleveragents:master
cleveragents:task/m2-quick-benchmark-regression-ci
cleveragents:feature/76-budget-pre-flight
cleveragents:feature/17-public-api
cleveragents:feature/m1-registry-http-client
cleveragents:master-backup
Labels
Clear labels
auto/blocked-by-deps
auto/ci-timeout
auto/claimed-implementer
auto/claimed-merge
auto/claimed-reviewer
auto/driver-down
auto/invariant-violation
auto/last-attempt-tier-0
auto/last-attempt-tier-1
auto/last-attempt-tier-2
auto/last-attempt-tier-min
Automation Tracking
auto/needs-conflict-resolution
auto/needs-implementer
auto/postmortem
auto/ready-to-merge
auto/restart-throttled
auto/revert
auto/sentinel
auto/stale-inactivity
auto/unstable
Blocked
Needs Feedback
Signed-off: Owner
Signed-off: Scrum Master
Signed-off: Tech Lead
Spike
PR blocked by an open issue dependency. Operator must close the dep (or remove the dependency link) before the merge driver can act. Auto-cleared by merge_drive when no open deps remain.
Most recent merge cycle hit CI timeout. Driver excludes this PR while last merge_cycle row is < 30 min old; label persists thereafter as visible history.
Currently being processed by an implementer worker.
Currently being processed by the merge driver.
Currently being processed by a reviewer worker.
Merge driver heartbeat stale; pipeline halted. Closed automatically on next clean tick.
Detected master commit violating the strict merge invariant. Tracked as an issue (not a PR label); kept here for label completeness.
In-cycle escalation: most recent attempt ran at the Tier 0 slot (`tier-0`). Slot's model defined in .opencode/models/tiers.yaml.
In-cycle escalation: most recent attempt ran at the Tier 1 slot (`tier-1`). Slot's model defined in .opencode/models/tiers.yaml.
In-cycle escalation: most recent attempt ran at the Tier 2 slot (`tier-2`). Slot's model defined in .opencode/models/tiers.yaml. Gated behind IMPLEMENTER_ESCALATION_TIER2_ENABLED.
In-cycle escalation: most recent attempt ran at the Tier -1 slot (`tier-min`). Slot's model defined in .opencode/models/tiers.yaml. Suffix is ``-min`` (not ``--1``) so the Forgejo UI reads naturally.
Tracking issues used by the AI Automation system for agents to communicate and report.
Rebase conflict needs LLM conflict-resolver.
Failing CI needs implementer attention.
Documenting a driver incident or rollback.
Reviewer has APPROVED this PR and no later REQUEST_CHANGES is outstanding. The merge driver requires this label to even consider a PR for merging. Set by the reviewer worker on APPROVE; cleared on REQUEST_CHANGES.
Train repeatedly lost master-tempo races. Driver excludes via merge_cycle until cooldown elapses; label persists as visible history.
Revert PR backing out an invariant violation. Fast-tracked through the merge driver.
Sentinel PR duplicated from upstream into a personal fork by tools/duplicate_prs_to_fork.py for pipeline testing. Lives only in the fork; the canonical pipeline never sees it.
No implementer activity for N days. Flagged for human review. Auto-cleared on next push to head branch.
Repeatedly fails on current master (>= 3 ci-fail-on-rebased-sha releases in 12 h). Excluded from driver until human triage.
A ticket in a blocked state and unable to complete until some other task is completed first.
Bounty
$100
A bounty of $100 for any open-source contributor who provides a MR that solves this issue
Bounty
$1000
A bounty of $1000 for any open-source contributor who provides a MR that solves this issue
Bounty
$10000
A bounty of $10000 for any open-source contributor who provides a MR that solves this issue
Bounty
$20
A bounty of $20 for any open-source contributor who provides a MR that solves this issue
Bounty
$2000
A bounty of $2000 for any open-source contributor who provides a MR that solves this issue
Bounty
$250
A bounty of $250 for any open-source contributor who provides a MR that solves this issue
Bounty
$50
A bounty of $50 for any open-source contributor who provides a MR that solves this issue
Bounty
$500
A bounty of $500 for any open-source contributor who provides a MR that solves this issue
Bounty
$5000
A bounty of $5000 for any open-source contributor who provides a MR that solves this issue
Bounty
$750
A bounty of $750 for any open-source contributor who provides a MR that solves this issue
MoSCoW
Could have
Could have feature in order to satisfy the epic/legendary.
MoSCoW
Must have
Must have feature in order to satisfy the epic/legendary.
MoSCoW
Should have
Should have feature in order to satisfy the epic/legendary.
There are questions in the ticket that can not be completed until the project owner provides clarity.
Points
1
1 man-hours worth of work for an expert with no learning curve.
Points
13
13 man-hours worth of work for an expert with no learning curve.
Points
2
2 man-hours worth of work for an expert with no learning curve.
Points
21
21 man-hours worth of work for an expert with no learning curve.
Points
3
3 man-hours worth of work for an expert with no learning curve.
Points
34
34 man-hours worth of work for an expert with no learning curve.
Points
5
5 man-hours worth of work for an expert with no learning curve.
Points
55
55 man-hours worth of work for an expert with no learning curve.
Points
8
8 man-hours worth of work for an expert with no learning curve.
Points
88
88 man-hours worth of work for an expert with no learning curve.
Priority
Backlog
This ticket has backlogged priority and is not to be worked on yet
Priority
CI Blocker
Critical priority issue that blocks CI/CD pipeline and prevents PR merges
Priority
Critical
The priority is critical
Priority
High
The priority is high
Priority
Low
The priority is low
Priority
Medium
The priority is medium
When an epic or legendary is in review it must be signed off by owner, tech lead, and scrum master before being marked as completed.
When an epic or legendary is in review it must be signed off by owner, tech lead, and scrum master before being marked as completed.
When an epic or legendary is in review it must be signed off by owner, tech lead, and scrum master before being marked as completed.
A ticket for learning a tool or technology that is needed to be able to do future planning and design.
State
Completed
The ticket has been fully implemented, completed, and merged with the source code. This label should only be applied once a ticket is closed.
State
Duplicate
A ticket that represents the same content as an existing ticket.
State
In Progress
A ticket that is actively being developed.
State
In Review
A ticket that has had some code completed to implement but is waiting to pass peer review and is not yet merged in.
State
Paused
This ticket's work started but wasn't finished. It's on hold (likely in a feature branch) and will be resumed later, either due to a blocker or a delay.
State
Unverified
All new tickets start in this state. A developer may set it to show the ticket is unverified. This means we haven't agreed to work on it. It will either move to a verified state or be closed as wontdo.
State
Verified
The issue has been verified by a developer as legitimate. It will be worked on and verified tickets are now considered part of the backlog.
State
Wont Do
This ticket has been decided it wont be done. This may mean the bug has been determined to not be real (cant verify) or the feature is one we have decided we dont want to adopt.
Type
Automation
Any edits or discussion about the AI automated coding system.
Type
Bug
Something that doesnt work as intended.
Type
Discussion
Anytime a ticket represents a discussion about a subject and doesnt fall into one of the other categories.
Type
Documentation
An error or improvement needed in the documentation.
Type
Epic
Any first tier epic. That is, an epic which contains only issues as children and will not have sub-epics.
Type
Feature
Some new functionality not present.
Type
Legendary
A type of Epic which will contain other Epics.
Type
Refactor
A code change that restructures existing code without changing its external behavior.
Type
Support
Someone needs help using the project.
Type
Task
A generic task that doesnt fit into the other type categories.
Type
Testing
Work exclusively focusing on fixing or expanding testing.
No Label
Type
Feature
Projects
Clear projects
No project
Assignees
aditya (Aditya Chhabra)
aleenaumair (Aleena Umair)
brent.edwards (Brent Edwards)
CoreRasurae (Luis Mendes)
drew (Drew Morris)
eugen.thaci (Eugen Thaci)
freemo (Jeffrey Phillips Freeman)
HAL9000 (HAL 9000)
HAL9001 (HAL9001)
hamza.khyari (Hamza Khyari)
hurui200320 (Rui Hu)
justin.morris
khird (Kyle Hird)
org.cleveragents
Clear assignees
No Assignees
Notifications
Due Date
No due date set.
Blocks
#88 feat(skills): add Skill package schema and agent-side skill loading support
cleveragents/cleveractors-core
Reference: cleveragents/cleveractors-core#94
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Delete Branch "feature/skill-package-support"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Implements #88: a normative Skill package schema (Package Registry Standard §16) aligned with the open agentskills.io Agent Skills format, plus agent-side skill loading for
type: llmagents via a new optionalskillsconfig field, per the approved ADR-2034.SkillLoader,SkillValidator,SkillReferenceResolver(new): resolveskillsreferences through the existingcleveractors.registryclient (registry:/ID:/local:schemes), mirroring the established template package-reference resolution pattern rather than a parallel path.skilltool call returns instructions), execution (resource reads, reusingfile_read'soffset/max_charspagination from ADR-2033).AgentFactoryinvokesSkillLoaderbeforeLLMAgentconstruction;LLMAgent/ToolAgentgain no registry awareness — all resolution stays inSkillLoader.docs/actor-registry-standard.mdgains a new §16 (appended after the original body, version bumped 1.0.0 → 1.1.0) documenting the schema. Theskillsagent-config field itself is documented only in ADR-2034 — not mirrored intodocs/index.md— matching how ADR-2031/ADR-2032 handled their own new LLM agent config fields.docs/registry/updated with pointers to the new schema.Test plan
features/agent_skills.feature): schema validation (including all agentskills.io name/description constraints), all three reference schemes, loader orchestration, factory/agent integration, ToolAgent skill tool (activation, resource reads incl. base64, pagination, error paths).robot/agent_skills.robot, 6 cases): realLocalPackageStore/PackageContentResolver/SkillLoaderresolution against a real on-disk skill file, end to end through a realToolAgentdispatch — only the LLM API call is mocked.nox -s coverage_report: 96.9% (threshold 96.5%).noxsuite green (lint, format, typecheck, security_scan, dead_code, complexity, unit_tests, integration_tests, e2e_tests, coverage_report, docs, build).nox -s benchmark_regression: BENCHMARKS NOT SIGNIFICANTLY CHANGED.Closes #88
Note: bundled
scripts/resources are readable, not directly runnableA question came up after this PR landed: if a skill's
instructionssay something like "runscripts/extract.py <file.pdf>", how does thetype: llmagent actually execute that script? Documenting the answer here since it's not obvious from the code alone.What the
skilltool actually gives the model is text, not a runnable file. Callingskill(skill_name="pdf-processing", resource="scripts/extract.py")returns the script's source as a string (cleveractors.agents.tool.ToolAgent._skill_tool, commitbf1f138) — formatted as[SKILL_RESOURCE_READ]...[FILE_CONTENT_START]...[FILE_CONTENT_END]. Nothing is ever written to a real filesystem path; resources live only in the in-memory_loaded_skillsmapping threaded throughcontext["_skills"]. This is intentional, per ADR-2034 D-5: resources are exposed for execution-stage reads "without touching the host filesystem," not for by-path execution.How a bundled Python script can actually run today — no code changes needed, but it requires the model to inline the source rather than reference it by path:
skill(skill_name="pdf-processing", resource="scripts/extract.py")→ returns the source textpython_exec(code="<that source text>")→ executes it viacleveractors.agents.tool.ToolAgent._execute_python_codeThis only works when the agent config sets
exec_python: true. Worth flagging: that sandbox isn't airtight — its restricted-builtins dict still includes an unrestricted__import__, so executed code canimport os/import subprocessand go beyond the tool's documented builtin list. Pre-existing behavior, not something this PR changed, but relevant to anyone relying on it as a security boundary for skill-provided code.What does not work today: a bundled
.shscript, or any Python script that assumes it's a real file on disk (relative imports, sibling files, real argv, real cwd). There's no materialized file forshellor a real interpreter invocation to point at. A model could try to smuggle the whole script intobash -c '<script>'via theshelltool (needsallow_shell: true), but that's fragile and wasn't a designed path — it just happens to be technically possible givenshell's existing arbitrary-commandargument.If genuine file-backed execution is wanted (skill resources materialized to a real temp directory so
shell/python_exec/file_readcan operate on real paths), that's a new capability this issue didn't build. It would need its own small design decision — materialization lifecycle, cleanup, interaction withsafe_mode/unsafe_mode— similar in shape to thepack_skill_directoryfollow-up already flagged in ADR-2034's "Follow-up Required" section. Happy to scope that as a separate issue if it's wanted.PR Review: !94 (Ticket #88)
Verdict: Request Changes
The implementation is solid overall and closely follows ADR-2034, reusing the existing registry client and preserving progressive disclosure. However, two functional issues require fixes before approval: skill tool context is lost in the stuck-model synthesis path, and duplicate skill names are silently overwritten. Additionally, there are a few minor documentation/coverage observations.
Critical Issues
None
Major Issues
src/cleveractors/agents/llm.pylines 1429-1431 — The stuck-model synthesis follow-up constructstool_ctx_sas{"_unsafe_mode": True} if parent_unsafe else Noneinstead of callingself._build_tool_context(parent_unsafe). Consequently,_loaded_skillsis not forwarded to the ephemeralToolAgent. If the model emits askilltool call during this follow-up round,ToolAgent._skill_toolfails with "Skill '...' is not loaded for this agent." The main loop and the budget-exhaustion synthesis round both correctly use_build_tool_context; this path should too.src/cleveractors/agents/skills.pylines 98-109 —SkillLoader.loadstores resolved skills inloaded[skill.name] = skill, silently overwriting any earlier skill with the same name. Twoskillsreferences that resolve to identically-named skills (or two refs to the same skill) will yield a catalogue with one entry and the other's instructions will be lost. Validate uniqueness and raiseAgentCreationErrorwith a clear message.Minor Issues
Coverage threshold discrepancy — The PR test plan reports 96.9% coverage and cites a 96.5% threshold, but
CONTRIBUTING.md,.gitea/workflows/ci.yml, and thecoverage_reportsession docstring all state the gate is 97%. Thenoxfile.pyconstant is 96.5, which creates an inconsistency. Before merge, confirm the project's enforced gate and ensure the reported coverage meets it. If the gate is 97%, additional tests are needed.docs/actor-registry-standard.md§16.1 — The last paragraph references "Actor Configuration Standard §22" for the agent-sideskillsfield, butdocs/index.mdhas no §22. This appears to be a stale/future reference; it should point to the actual location (currently ADR-2034, or §4.4 once the deferred spec update lands).src/cleveractors/agents/skill_schema.pySkill.to_context_dict— The context representation exposed to theskilltool andLLMAgent.get_metadata()dropsmetadata,allowed_tools,license, andcompatibility. Sinceallowed_toolsis documented as a pre-approved tool list, making it invisible to the model limits its usefulness. Consider surfacing at leastallowed_toolsandmetadatain activation output or catalogue metadata.Nits
skillslist; emptydescription/instructionsstrings; and the stuck-model synthesis path invoking theskilltool.robot/SkillLoadingTestLib.py—tempfile.mkdtemp()directories are not cleaned up after tests.Summary
The PR delivers the normative skill schema, reference resolution, loader orchestration, and tool-agent integration described in ADR-2034. The architecture is clean and the test suite is extensive. The two major issues above are localized but real regressions in functionality; once fixed and the coverage/doc observations addressed, this should be good to merge.
bf1f13846fto05e5e52310c520a6d211tof9fea9e208PR Review: !94 (Ticket #88)
Verdict: Request Changes
The implementation still has the two functional regressions identified in the previous review: the stuck-model synthesis path drops the skill catalogue, and duplicate skill names are silently overwritten. In addition, the agent-side
skillsfield is not documented indocs/index.mdas required by the ticket acceptance criteria, and a new error-handling bug inSkillReferenceResolvercan mask exceptions withUnboundLocalError. Once these are addressed, the PR should be in good shape.Critical Issues
None
Major Issues
src/cleveractors/agents/llm.pylines 1429-1431 — The stuck-model synthesis follow-up constructstool_ctx_sas{"_unsafe_mode": True} if parent_unsafe else Noneinstead of callingself._build_tool_context(parent_unsafe). Consequently,_loaded_skillsis not forwarded to the ephemeralToolAgent. If the model emits askilltool call during this follow-up round,ToolAgent._skill_toolfails with "Skill '...' is not loaded for this agent." The main loop and budget-exhaustion synthesis round both correctly use_build_tool_context; this path should too.src/cleveractors/agents/skills.pylines 98-109 —SkillLoader.loadstores resolved skills inloaded[skill.name] = skill, silently overwriting any earlier skill with the same name. Twoskillsreferences that resolve to identically-named skills (or two refs to the same skill) will yield a catalogue with one entry and the other's instructions will be lost. Validate uniqueness and raiseAgentCreationErrorwith a clear message.docs/index.md§4.4 — The new optionalskillsconfiguration field fortype: llmagents is not documented in the Actor Configuration Standard, even though ticket #88 acceptance criteria explicitly require it and ADR-2034 D-5 states the field is added todocs/index.md§4.4. Add the field documentation (optional list of package references usingregistry:,ID:, andlocal:schemes).src/cleveractors/agents/skill_resolution.pylines 84-124 —resolvedis only assigned inside thetryblock. Ifresolver.resolve()raises an exception other thanFileNotFoundError/ValueError(e.g.,RuntimeErrorfor a registry reference called inside a running event loop), thefinallyruns and thenif resolved is None:raisesUnboundLocalError, masking the original failure. Initializeresolved = Nonebefore thetryor add a broaderexceptpath that re-raises asRegistryError.Minor Issues
Coverage threshold mismatch / below declared gate —
CONTRIBUTING.md,.gitea/workflows/ci.yml, and thecoverage_reportsession docstring state the gate is 97%, butnoxfile.pysetsCOVERAGE_THRESHOLD = 96.5. The PR test plan reports 96.9% and cites 96.5%. Alignnoxfile.pywith the documented 97% gate and add tests to reach it.docs/actor-registry-standard.md§16.1 line 576 — The last paragraph references "Actor Configuration Standard §22" for the agent-sideskillsfield, butdocs/index.mdhas no §22. This appears to be a stale/future reference; it should point to the actual location (currently §4.4 or ADR-2034).src/cleveractors/agents/skill_schema.pySkill.to_context_dictlines 72-88 — The context representation exposed to theskilltool andLLMAgent.get_metadata()dropsmetadata,allowed_tools,license, andcompatibility. Sinceallowed_toolsis documented as a pre-approved tool list, making it invisible to the model limits its usefulness. Consider surfacing at leastallowed_toolsandmetadatain activation output or catalogue metadata.src/cleveractors/agents/tool.py_decode_skill_resourceline 1099 andsrc/cleveractors/agents/skill_schema.pySkillResource.decoded_textline 53 — Base64 resources are decoded with.decode("utf-8"), which will crash for genuine binary assets (images, PDFs, etc.) even though the spec requires supporting non-UTF-8 files viaencoding: base64. Handle binary resources safely (e.g., return base64 text or skip UTF-8 decoding).robot/SkillLoadingTestLib.pyline 56 —tempfile.mkdtemp()directories are not cleaned up after tests. Add teardown cleanup to avoid leaking temp directories in CI.Nits
Test gaps — Consider adding Behave/Robot scenarios for: duplicate skill names in the
skillslist; emptydescription/instructionsstrings; and the stuck-model synthesis path invoking theskilltool.features/steps/agent_skills_steps.py—tempfile.mkdtemp()directories created for local-store tests are not cleaned up.src/cleveractors/agents/skill_resolution.pylines 94-112 — When called inside a running event loop, resolver cleanup is scheduled withasyncio.ensure_future()and not awaited, so it may not complete before loop shutdown. Prefer the async resolution path where possible.Summary
The PR delivers a clean, registry-agnostic skill-loading architecture that aligns well with ADR-2034 and reuses the existing package-resolution machinery. The test suite is extensive and the Robot integration test gives good end-to-end confidence. However, the two previously reported functional issues remain unfixed, the new
SkillReferenceResolvererror-handling path has a latentUnboundLocalError, and theskillsfield is missing from the Actor Configuration Standard. Addressing the major items above will make this ready to merge.View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.