feat(skills): gaia skill migrate — OpenClaw/Hermes skills to GAIA format - #2693
feat(skills): gaia skill migrate — OpenClaw/Hermes skills to GAIA format#2693kovtcharov wants to merge 6 commits into
Conversation
The base branch was changed.
Skill authors on OpenClaw/ClawHub and Hermes had no path onto GAIA short of rewriting each SKILL.md by hand. `gaia skill migrate --from openclaw|hermes|auto` reads the foreign metadata.<vendor> namespace and writes a metadata.gaia block, so an existing library moves over largely untouched. Fields GAIA models fully are consumed into metadata.gaia; anything partially or un-modeled stays under metadata.<vendor> and is named in the migration report, so nothing is dropped silently. Every migrated skill lands at the experimental tier via the same trust-reset `gaia skill import` uses — now a single shared reset_security_tier() rather than two security paths. v1 bridges instruction-only and connector-backed skills. A source that shells out (requires.bins) or reads config files (requires.config) maps onto shell / filesystem, which have no enforcement until the Phase 2 sandbox, so it is refused with that phase's own error rather than quietly stripped of the permission to make the migration look successful. Output is round-tripped through parse_skill() before install, so a migration can never emit a SKILL.md the parser rejects. Fixtures are 26 real published OpenClaw skills, each fetched from a commit-pinned URL and recorded in PROVENANCE.md with its license and digest.
Validating against 26 published ClawHub skills found two bugs that only appear off the documented happy path. An OpenClaw skill that puts its fields directly under `metadata`, or at the top level with no namespace key, migrated "successfully" with zero permissions — so `1password` shelling out to `op` was refused when namespaced and silently accepted when not. Same upstream skill, opposite verdicts, with the unsafe one passing. Vendor fields are now located wherever the source actually put them (metadata.<ns>, top-level <ns>, or inlined), and leftovers are written back to that same place so a round-trip never relocates them. About 5% of published skills ship no frontmatter at all; that raised out of the batch loop and killed the whole run. It is now a per-skill blocker. Also: aliases are first-wins with a note rather than merged (several skills duplicate one payload under two aliases, and merging double-counts every requirement), and envVars entries keyed `key` rather than `name` are read.
61 tests over the migrator and the shipped CLI verb. Beyond the unit cases (round-trip, vendor preservation, trust reset, local-capability refusal, the Hermes path), every one of the 26 real published skills is asserted to reach a defined verdict: migrated-and-validating, or refused-with-a-reason, never a crash and never a 'success' that quietly dropped a declared capability. Two tests guard the corpus itself — that at least 10 real skills are present with provenance, and that it still exercises both verdicts, so the refusal path cannot rot untested if fixtures are re-pinned.
… migrate Self-review caught a second silent-loss path: `metadata.openclaw: "a string"` has no fields to map, and the value was dropped while the report claimed the namespace was "absent or empty". It is now carried through verbatim with an accurate note. Pointing migrate at ./OTHER.md also resolved to a sibling SKILL.md instead of the named file. Docs: `gaia skill migrate` is documented in the CLI reference, and the plan doc's migration section moves from PROPOSED to shipped — with the field-by-field OpenClaw/Hermes mapping tables, the divergences real published skills show against OpenClaw's own documented schema, and the honest 13-migrate/13-refused split of the 26-skill corpus. Phase 3 now scopes to the Agent UI panel alone (#701) and records that its visual import should reuse this migrator.
An install collision on the fifth skill of a batch aborted the run and printed nothing about the four that had already installed. Install failures are now tracked per skill and reported separately from `blockers` — a skill that migrated cleanly but could not be written is a different thing to tell a user than one that was unmigratable, and the report prints either way.
…time #888 landing rewrote the shipped-vs-proposed claims in skill-format.mdx and agent-skills.mdx. Rebasing on top merged textually clean but left both files contradicting themselves about what exists. skill-format.mdx: the audit table's migrate row was column-misaligned (path in the Symbol column, "SHIPPED" in Location), so it rendered as garbage and sat below the NOT FOUND block; it now sits with the other shipped rows in the right columns, alongside a row for the shared trust reset. The Retractions warning read as if `gaia skill migrate` were itself retracted — it is the runtime adapter that is deferred, not the migration path — and the "no longer a retraction" list now names migrate. The acceptance-traceability CLI row enumerated the shipped verbs without it. agent-skills.mdx: its own intro promises the spec treats Hermes and OpenClaw as compatible formats, but the compatibility section documented only two of the three directions; third-party -> GAIA is now covered, and the header's shipped list names the verb. Also unified the #cross-format-compatibility--migration anchor across all three references. The repo's link checker strips fragments before validating, so a wrong anchor fails silently rather than in CI.
a7bac64 to
5b64e99
Compare
Verdict: Request changesThis PR adds The one blocking issue: the documented flagship command aborts the whole batch on real input. Running Real-world evidenceThe evidence bundle ( Forcing 🔍 Technical details🔴
|
Review of #2806 caught three artifacts the plan cited as existing that are not on main — they live in open PRs. An implementer grepping for them would come up empty. src/gaia/skills/sets.py -> PR #2695 workers/agent-hub/src/skill-manifest.ts -> PR #2668 tests/fixtures/openclaw_skills/ -> PR #2693 Adds a Forward dependencies table naming each PR, what needs it, and what to do if it has not merged. The material consequence: P3.4 skill sets is gated on #2695 landing, not just on P3.1-P3.3, because porting a module still in review yields two implementations that diverge on the first round of review feedback. P3.1 and P5.2 are not gated — they work from the 6-skill corpus on main and widen to 32 when #2693 lands, so the conformance gate discovers its corpus instead of hardcoding a path that does not yet resolve. Also corrects "FAISS in exactly three places" to name all five construction sites across four subsystems. The load-bearing claim is unchanged and in fact stronger: every one is IndexFlatL2/IndexFlatIP, and a grep for IndexIVF/IndexHNSW/IndexPQ/index_factory returns nothing.
Skill authors on OpenClaw/ClawHub and Hermes had no path onto GAIA short of rewriting each
SKILL.mdby hand, which meant GAIA's catalog started empty while a large body of compatible skills sat one format away.gaia skill migrate --from openclaw|hermes|autoreads the foreignmetadata.<vendor>namespace and writes ametadata.gaiablock, so an existing library moves over largely untouched — point it at one skill or a whole ClawHub checkout. Every migrated skill lands at theexperimentaltier via the same trust resetgaia skill importuses, and a skill that needs a local capability GAIA cannot yet enforce is refused with the reason rather than quietly stripped of the permission to make the migration look like it worked.Validated against 26 real published ClawHub skills (commit-pinned, licenses and digests recorded in
tests/fixtures/openclaw_skills/PROVENANCE.md): 13 migrate cleanly, 13 are correctly refused — 10 because they shell out, 3 because they ship no frontmatter at all.The dashboard half of #692 is deferred to #701, deliberately. There is no skills surface in the Agent UI today — no
/api/skillsrouter, no panel, not even the word "skill" anywhere insrc/gaia/ui/— and no in-flight branch adding one. Building visual import here would mean building Phase 3's surface to hang it off, and a user would import a skill into a UI that cannot list it. The migrator is UI-ready instead:MigrationOutcome.to_dict()is JSON-serializable and--jsonemits the full report, so the panel wires togaia.skills.migraterather than reimplementing the mapping. The plan doc's Phase 3 section now records that.Two bugs surfaced only by the real corpus, both silent-loss paths that a docs-driven implementation would have shipped:
metadata(or at the top level, with no namespace key) migrated with zero permissions — so1passwordshelling out toopwas refused when namespaced and silently accepted when not. Same upstream skill, opposite verdicts, the unsafe one passing.metadata.openclaw: "a string"had its value dropped while the report claimed the namespace was "absent or empty".Stacked on #2669 (Phase 1) — base retargets to
mainwhen that merges.Closes #692
Test plan
python -m pytest tests/unit/test_skills_migrate.py -q— 64 tests: round-trip throughparse_skill_file, vendor fields surviving undermetadata.<vendor>,experimentalstamped over a claimedverified/community, local-capability sources reported unmigratable rather than downgraded, and the Hermes path through the same command.python -m pytest tests/unit/test_skills_format.py tests/unit/test_skills_cli.py -q— Phase 1 unaffected by the sharedreset_security_tier()/split_frontmatter()extraction.~/.gaia/skills:python util/lint.py --all