> For the complete documentation index, see llms.txt.
Skip to main content

Check out Port for yourself ➜ 

Avoid duplicate skills in your org registry

Implement with AI

Send this guide to your coding agent.

Prerequisite: Install Port MCP

A registry that grows without limits eventually grows the same skill twice.

The root cause is almost always discovery: someone builds a skill similar to one that already exists because they never knew it was there. Enable discovery and distribution from the first guide in this series is the main defense against that, and it's worth getting right before layering on anything else. But discovery will always have room to slip. This guide adds two safeguards.

We'll build this in three parts:

  1. Identify similar skills in the Publish Skill workflow - catch an obvious duplicate at submission time, before a pull request even opens.
  2. Assess skill uniqueness in the Skill Certification Review workflow - a deeper, asynchronous check that scores overlap against every other cataloged skill and links the skills involved.
  3. Visualize in your dashboards - so reviewers see a duplicate flag before approving a request, and platform teams see how much duplication is accumulating registry-wide.

Common use cases

  • Rework instead of reuse. One team already built a skill to calculate blast radius, five more teams shouldn't have to spend the time building their own.
  • No clear golden path. Once several versions of the same process exist, nothing tells a developer which one is current, which one is actually maintained, or which one is cheapest to run.
  • Knowledge chaos. Skills covering the same ground tend to end up with similar descriptions. When several of them load into the same agent's context, the agent has to guess which one to call, and contradicting instructions from the near-duplicates make each of them perform worse individually.

Prerequisites

This guide assumes you have all three previous guides implemented:

Step 1: Identify similar skills pre-publish

This extends the Publish Skill workflow from Ship new skills through one golden path with an early gate: before a pull request ever opens, an AI node compares the new submission against every cataloged skill. If it finds one that covers substantially the same purpose, the publisher has to explicitly acknowledge that and confirm they still want to publish, or cancel.

The workflow now has seven nodes:

  1. Trigger - unchanged from the previous guide: raw SKILL.md content and an optional target path.
  2. Check for Similar Skill - a new AI node that calls list_entities on the skill blueprint and compares the new submission's description against every existing skill for overlapping purpose, not just matching text.
  3. Similar Skill Found? - a condition node that branches on whether the AI node found a match.
  4. Confirm Publish Despite Similar Skill - a new input node that only runs when a similar skill was found. It pauses the run and asks the publisher, specifically the same person who triggered it, to either confirm they still want to publish or cancel outright.
  5. Extract Skill Metadata - unchanged.
  6. Dispatch Skill Publish PR - unchanged.
  7. Create Skill Entity - unchanged.

Check for Similar Skill also uses the node's variables block to turn the AI's raw JSON response into two directly-referenceable outputs: similar_skill_identifier, and a ready-to-click similar_skill_link pointing at the existing skill's entity page. The condition node and the confirmation prompt both read these instead of re-parsing the AI's response themselves.

To update the workflow:

  1. Go to the Workflows page in Port.

  2. Click Edit on the Publish Skill workflow you created in the previous guide.

  3. Click the {...} button to open the JSON editor.

  4. Copy and paste the workflow JSON below to replace it:

    Publish Skill workflow JSON, with similarity check (click to expand)
    {
    "identifier": "publish_skill",
    "title": "Publish Skill",
    "icon": "Rocket",
    "description": "Publish a new SKILL.md to the skills registry: checks for a similar existing skill, opens a pull request, and creates the corresponding skill entity as pending.",
    "allowAnyoneToViewRuns": true,
    "nodes": [
    {
    "identifier": "trigger",
    "config": {
    "type": "SELF_SERVE_TRIGGER",
    "userInputs": {
    "properties": {
    "skill_content": {
    "title": "SKILL.md content",
    "description": "Full raw content of the SKILL.md file to publish.",
    "type": "string",
    "format": "multi-line"
    },
    "target_path": {
    "title": "Target path in skills repo (optional)",
    "description": "Repo-relative path, e.g. \"skills/my-new-skill/SKILL.md\". Leave empty to let the workflow choose one.",
    "type": "string"
    }
    },
    "required": ["skill_content"],
    "order": ["skill_content", "target_path"]
    }
    }
    },
    {
    "identifier": "check_similar_skill",
    "title": "Check for Similar Skill",
    "config": {
    "type": "AI",
    "systemPrompt": "You check whether a new skill submission duplicates an existing skill in the registry. You MUST call list_entities on the \"skill\" blueprint (include the \"description\" property) before answering - do not answer without first calling this tool, even if you think you already know the answer. Once you have the results, compare the new SKILL.md content (its frontmatter description) against each existing skill for semantic similarity - not just exact text matches, but overlapping purpose or duplicate functionality. If you find an existing skill that covers substantially the same purpose, set has_similar_skill to true and similar_skill_identifier to that skill's identifier. Otherwise set has_similar_skill to false and similar_skill_identifier to an empty string.",
    "userPrompt": "New SKILL.md content:\n{{ .outputs.trigger.skill_content }}",
    "tools": ["list_entities", "list_blueprints"],
    "outputSchema": {
    "type": "object",
    "properties": {
    "has_similar_skill": { "type": "boolean" },
    "similar_skill_identifier": { "type": "string" }
    },
    "required": ["has_similar_skill", "similar_skill_identifier"]
    }
    },
    "variables": {
    "similar_skill_identifier": "{{ .result.response | fromjson | .similar_skill_identifier }}",
    "similar_skill_link": "{{ if (.result.response | fromjson | .has_similar_skill) then (\"https://app.port.io/skillEntity?identifier=\" + (.result.response | fromjson | .similar_skill_identifier | gsub(\"/\"; \"%2F\"))) else \"\" end }}"
    }
    },
    {
    "identifier": "similarity_check",
    "title": "Similar Skill Found?",
    "config": {
    "type": "CONDITION",
    "outlets": [
    {
    "identifier": "similar_skill_found",
    "title": "Similar skill found",
    "expression": "(.outputs.check_similar_skill.similar_skill_identifier // \"\") != \"\""
    }
    ]
    },
    "links": ["{{ .outputs.check_similar_skill.similar_skill_link }}"]
    },
    {
    "identifier": "similar_skill_approval",
    "title": "Confirm Publish Despite Similar Skill",
    "config": {
    "type": "INPUT",
    "description": "A similar skill may already exist in the registry: {{ .outputs.check_similar_skill.similar_skill_identifier }}\nView it here: {{ .outputs.check_similar_skill.similar_skill_link }}\n\nDo you still want to publish this new skill?",
    "userInputs": {
    "properties": {},
    "buttons": [
    { "identifier": "publish_anyway", "label": "Publish Anyway", "variant": "PRIMARY" },
    { "identifier": "cancel", "label": "Cancel Publish", "variant": "DANGER" }
    ]
    },
    "outlets": [
    { "evaluationMethod": "button", "identifier": "publish_anyway", "title": "Publish Anyway", "numOfResponders": 1 },
    {
    "evaluationMethod": "button",
    "identifier": "cancel",
    "title": "Cancel Publish",
    "numOfResponders": 1,
    "statusLabel": { "text": "Skipped - publish cancelled by requester", "variant": "alert" },
    "workflowStatusLabel": { "text": "Skipped - publish cancelled by requester", "variant": "alert" }
    }
    ],
    "responders": {
    "users": ["{{ .workflowRun.trigger.by.email }}"]
    }
    }
    },
    {
    "identifier": "extract_skill_metadata",
    "title": "Extract Skill Metadata",
    "config": {
    "type": "AI",
    "systemPrompt": "You extract metadata from SKILL.md files. Read the frontmatter (between --- lines) for the skill's name and description. If target_path is empty, call list_entities on the \"skill\" blueprint (including the \"path\" property) to see where existing skills already live, then propose a path that fits the same directory conventions instead of inventing a flat one. Always return the target_path to use, either the user's or your proposed one.",
    "userPrompt": "SKILL.md content:\n{{ .outputs.trigger.skill_content }}\n\nUser-provided target_path (may be empty): {{ .outputs.trigger.target_path }}",
    "tools": ["list_entities", "list_blueprints"],
    "outputSchema": {
    "type": "object",
    "properties": {
    "skill_name": { "type": "string" },
    "description": { "type": "string" },
    "target_path": { "type": "string" }
    },
    "required": ["skill_name", "description", "target_path"]
    }
    }
    },
    {
    "identifier": "dispatch_publish_pr",
    "title": "Dispatch Skill Publish PR",
    "config": {
    "type": "INTEGRATION_ACTION",
    "installationId": "<your-github-integration-installation-id>",
    "integrationProvider": "github-ocean",
    "integrationInvocationType": "dispatch_workflow",
    "integrationActionExecutionProperties": {
    "org": "your-org",
    "repo": "skills-registry",
    "workflow": "publish-skill.yml",
    "workflowInputs": {
    "skill_name": "{{ .outputs.extract_skill_metadata.response | fromjson | .skill_name }}",
    "target_path": "{{ .outputs.extract_skill_metadata.response | fromjson | .target_path }}",
    "skill_content": "{{ .outputs.trigger.skill_content }}",
    "node_run_id": "{{ .workflowNodeRun.identifier }}"
    },
    "reportWorkflowStatus": true
    }
    }
    },
    {
    "identifier": "create_skill_entity",
    "title": "Create Skill Entity",
    "config": {
    "type": "UPSERT_ENTITY",
    "blueprintIdentifier": "skill",
    "mapping": {
    "identifier": "{{ \"your-org/skills-registry/\" + (.outputs.extract_skill_metadata.response | fromjson | .target_path) }}",
    "title": "{{ .outputs.extract_skill_metadata.response | fromjson | .skill_name }}",
    "properties": {
    "description": "{{ .outputs.extract_skill_metadata.response | fromjson | .description }}",
    "path": "{{ .outputs.extract_skill_metadata.response | fromjson | .target_path }}",
    "author": "{{ .workflowRun.trigger.by.email }}",
    "pr_url": "{{ .outputs.dispatch_publish_pr.pr_url }}",
    "publish_status": "pending"
    }
    }
    }
    }
    ],
    "connections": [
    { "sourceIdentifier": "trigger", "targetIdentifier": "check_similar_skill" },
    { "sourceIdentifier": "check_similar_skill", "targetIdentifier": "similarity_check" },
    { "sourceIdentifier": "similarity_check", "targetIdentifier": "extract_skill_metadata", "fallback": true },
    { "sourceIdentifier": "similarity_check", "targetIdentifier": "similar_skill_approval", "sourceOutletIdentifier": "similar_skill_found" },
    { "sourceIdentifier": "similar_skill_approval", "targetIdentifier": "extract_skill_metadata", "sourceOutletIdentifier": "publish_anyway" },
    { "sourceIdentifier": "extract_skill_metadata", "targetIdentifier": "dispatch_publish_pr" },
    { "sourceIdentifier": "dispatch_publish_pr", "targetIdentifier": "create_skill_entity" }
    ]
    }
  5. The placeholders in dispatch_publish_pr and create_skill_entity are unchanged from the previous guide; replace them the same way if you haven't already.

  6. Click Save.

If the publisher clicks Cancel Publish, the run stops there: no pull request opens, and no entity gets created. If they click Publish Anyway, the run continues exactly as it did before this guide.

Step 2: Assess skill uniqueness

The check in Step 1 only catches a duplicate the publisher happens to trigger against what's cataloged at that exact moment. Certification runs the same comparison again, asynchronously, and records the result on the entity so it shows up on a dashboard regardless of what the publisher saw.

Add uniqueness to the skill blueprint

  1. Go to the Data model page in Port.

  2. Expand the skill blueprint, click the ... button, and select Edit blueprint.

  3. Click on the {...} Edit JSON button.

  4. Add the following properties under schema.properties:

    New skill blueprint properties (click to expand)
    {
    "uniqueness": {
    "title": "Uniqueness",
    "type": "string",
    "description": "How much this skill's purpose and instructions overlap with another cataloged skill",
    "enum": ["unique", "partial_duplicate", "mostly_duplicate", "complete_duplicate"],
    "enumColors": {
    "unique": "green",
    "partial_duplicate": "yellow",
    "mostly_duplicate": "orange",
    "complete_duplicate": "red"
    }
    },
    "uniqueness_reasoning": {
    "title": "Uniqueness Reasoning",
    "type": "string",
    "format": "markdown",
    "description": "Why the certification review scored this skill's uniqueness the way it did"
    }
    }
  5. Add the following relation under relations:

    New skill blueprint relation (click to expand)
    {
    "similar_skills": {
    "title": "Similar Skills",
    "target": "skill",
    "required": false,
    "many": true
    }
    }
  6. Click Save.

similar_skills is a relation from a skill back to itself: each skill the certification review flags as overlapping gets linked here directly, so a reviewer doesn't have to re-run the comparison by hand to see what a flagged skill actually overlaps with.

Add a uniqueness scorecard

  1. Still on the skill blueprint, click on the Scorecards tab.

  2. Click + Scorecard.

  3. Click on the {...} Edit JSON button in the top right corner.

  4. Paste the following JSON configuration:

    Skill uniqueness scorecard (click to expand)
    {
    "identifier": "skill_uniqueness",
    "title": "Skill Uniqueness",
    "blueprint": "skill",
    "levels": [
    { "title": "Basic", "color": "lightGray" },
    { "title": "Bronze", "color": "bronze" },
    { "title": "Silver", "color": "silver" },
    { "title": "Gold", "color": "gold" }
    ],
    "rules": [
    {
    "identifier": "uniqueness_not_complete_duplicate",
    "title": "Not a complete duplicate",
    "level": "Bronze",
    "query": {
    "combinator": "and",
    "conditions": [{ "operator": "!=", "property": "uniqueness", "value": "complete_duplicate" }]
    }
    },
    {
    "identifier": "uniqueness_unique_or_partial",
    "title": "Unique or only partially overlapping",
    "level": "Silver",
    "query": {
    "combinator": "and",
    "conditions": [{ "operator": "!=", "property": "uniqueness", "value": "mostly_duplicate" }]
    }
    },
    {
    "identifier": "uniqueness_is_unique",
    "title": "Fully unique",
    "level": "Gold",
    "query": {
    "combinator": "and",
    "conditions": [{ "operator": "!=", "property": "uniqueness", "value": "partial_duplicate" }]
    }
    }
    ]
    }
  5. Click Save.

Each level rules out a worse overlap than the one before it: Bronze only requires that the skill isn't a complete duplicate, Silver additionally rules out a mostly-duplicate, and Gold requires no overlap being flagged at all, not even a partial one.

Extend the Skill Certification Review workflow

The Skill Certification Review workflow now has one more processing node, inserted right after the entity resolves and before the description-quality check:

  1. On Skill Created and Run Certification Review - unchanged.
  2. Resolve Skill Entity - unchanged.
  3. Check Uniqueness - a new AI node that reads the resolved skill's description and full content, calls list_entities on the skill blueprint, and compares this skill against every other cataloged skill. It returns a uniqueness verdict and the identifiers of any skills it substantially overlaps with.
  4. Check Description Quality - unchanged.
  5. Synthesize Recommendation - now weighs both check results. A skill that's a complete duplicate is disqualifying on its own, the same way a missing description already was.
  6. Update Skill Entity - now also writes uniqueness and uniqueness_reasoning, and maps the AI's list of overlapping skill identifiers onto the similar_skills relation added above.
  7. Notify Admin Slack - unchanged.

To update the workflow:

  1. Go to the Workflows page in Port.

  2. Click Edit on the Skill Certification Review workflow you created in the previous guide.

  3. Click the {...} button to open the JSON editor.

  4. Copy and paste the workflow JSON below to replace it:

    Skill Certification Review workflow JSON, with uniqueness check (click to expand)
    {
    "identifier": "skill_certification_review",
    "title": "Skill Certification Review",
    "icon": "Award",
    "description": "Review a skill's uniqueness and description quality, and record a certification recommendation, automatically after it's created or on demand.",
    "allowAnyoneToViewRuns": true,
    "nodes": [
    {
    "identifier": "trigger_event",
    "title": "On Skill Created",
    "config": {
    "type": "EVENT_TRIGGER",
    "event": {
    "type": "ENTITY_CREATED",
    "blueprintIdentifier": "skill"
    }
    }
    },
    {
    "identifier": "trigger_manual",
    "title": "Run Certification Review",
    "config": {
    "type": "SELF_SERVE_TRIGGER",
    "userInputs": {
    "properties": {
    "skill": {
    "title": "Skill",
    "type": "string",
    "format": "entity",
    "blueprint": "skill"
    }
    },
    "required": ["skill"]
    },
    "contexts": [{ "on": "ENTITY", "userInput": "skill" }]
    }
    },
    {
    "identifier": "resolve_skill",
    "title": "Resolve Skill Entity",
    "config": {
    "type": "WEBHOOK",
    "method": "GET",
    "url": "https://api.port.io/v1/blueprints/skill/entities/{{ (.outputs.trigger_event.diff.after.identifier // .outputs.trigger_manual.skill) | @uri }}",
    "onFailure": "terminate"
    }
    },
    {
    "identifier": "check_uniqueness",
    "title": "Check Uniqueness",
    "config": {
    "type": "AI",
    "systemPrompt": "You are reviewing a proposed skill for a skills registry certification pipeline. Judge only uniqueness relative to other cataloged skills. Call list_entities on the skill blueprint at most once, then return the structured output immediately. Do not loop. When referencing another skill, you MUST use the exact `identifier` field returned by list_entities, never the `title` field - identifiers in this catalog are full repo-relative paths (e.g. \"org/repo/path/to/SKILL.md\") and are NOT the same as the display title.",
    "userPrompt": "Skill identifier: {{ .outputs.resolve_skill.response.entity.identifier }}\n\nDescription:\n{{ .outputs.resolve_skill.response.entity.properties.description }}\n\nFull SKILL.md content, for context:\n{{ .outputs.resolve_skill.response.entity.properties.instructions }}\n\nCompare this skill's purpose and instructions against every other cataloged skill and determine how much it overlaps with an existing one.",
    "tools": ["list_entities", "list_blueprints"],
    "outputSchema": {
    "type": "object",
    "properties": {
    "uniqueness": { "type": "string", "enum": ["unique", "partial_duplicate", "mostly_duplicate", "complete_duplicate"] },
    "duplicate_of": { "type": "array", "items": { "type": "string" } },
    "uniqueness_reasoning": { "type": "string" }
    },
    "required": ["uniqueness", "duplicate_of", "uniqueness_reasoning"]
    }
    }
    },
    {
    "identifier": "check_description_quality",
    "title": "Check Description Quality",
    "config": {
    "type": "AI",
    "systemPrompt": "You review a skill's description for a registry where only the skill's name and description are preloaded into an agent's context. The description alone determines whether an agent notices the skill and calls it at the right moments. Judge the description on that standard: \"missing\" if there's no description or it says nothing about when to use the skill; \"too_vague\" if it doesn't say clearly enough when an agent should reach for this skill; \"too_verbose\" if it buries the trigger conditions in more detail than an agent needs preloaded into context; \"good\" if it's concise and makes clear both what the skill does and when to use it. Read the full SKILL.md content for context on whether the description accurately represents the skill, but judge quality by the description alone.",
    "userPrompt": "Skill name: {{ .outputs.resolve_skill.response.entity.title }}\n\nDescription:\n{{ .outputs.resolve_skill.response.entity.properties.description }}\n\nFull SKILL.md content, for context:\n{{ .outputs.resolve_skill.response.entity.properties.instructions }}",
    "tools": [],
    "outputSchema": {
    "type": "object",
    "properties": {
    "description_quality": { "type": "string", "enum": ["good", "too_verbose", "too_vague", "missing"] },
    "description_quality_reasoning": { "type": "string" }
    },
    "required": ["description_quality", "description_quality_reasoning"]
    }
    }
    },
    {
    "identifier": "synthesize_recommendation",
    "title": "Synthesize Recommendation",
    "config": {
    "type": "AI",
    "systemPrompt": "You turn certification checks into one recommendation for a human reviewer deciding whether to approve a pending skill. Today there are two checks, uniqueness and description quality, but this node is designed to combine more as your registry adds them. Recommend \"approve\" when every check passed, \"iterate\" when a check found something the author should fix before merging (such as a vague or verbose description, or a partial overlap with another skill), and \"reject\" only when a check found something disqualifying (such as a missing description, or a complete duplicate of an existing skill). Explain your reasoning in one or two sentences a reviewer can read at a glance.",
    "userPrompt": "Uniqueness check result:\n{{ .outputs.check_uniqueness.response }}\n\nDescription quality check result:\n{{ .outputs.check_description_quality.response }}",
    "tools": [],
    "outputSchema": {
    "type": "object",
    "properties": {
    "certification_recommendation": { "type": "string", "enum": ["approve", "reject", "iterate"] },
    "certification_recommendation_reasoning": { "type": "string" }
    },
    "required": ["certification_recommendation", "certification_recommendation_reasoning"]
    }
    }
    },
    {
    "identifier": "update_skill_entity",
    "title": "Update Skill Entity",
    "config": {
    "type": "UPSERT_ENTITY",
    "blueprintIdentifier": "skill",
    "mapping": {
    "identifier": "{{ .outputs.trigger_event.diff.after.identifier // .outputs.trigger_manual.skill }}",
    "properties": {
    "uniqueness": "{{ .outputs.check_uniqueness.response | fromjson | .uniqueness }}",
    "uniqueness_reasoning": "{{ .outputs.check_uniqueness.response | fromjson | .uniqueness_reasoning }}",
    "description_quality": "{{ .outputs.check_description_quality.response | fromjson | .description_quality }}",
    "description_quality_reasoning": "{{ .outputs.check_description_quality.response | fromjson | .description_quality_reasoning }}",
    "last_certification_check_at": "{{ now | todateiso8601 }}",
    "certification_recommendation": "{{ .outputs.synthesize_recommendation.response | fromjson | .certification_recommendation }}",
    "certification_recommendation_reasoning": "{{ .outputs.synthesize_recommendation.response | fromjson | .certification_recommendation_reasoning }}"
    },
    "relations": {
    "similar_skills": "{{ .outputs.check_uniqueness.response | fromjson | .duplicate_of }}"
    }
    },
    "onFailure": "terminate"
    }
    },
    {
    "identifier": "notify_admin_slack",
    "title": "Notify Admin Slack",
    "config": {
    "type": "WEBHOOK",
    "url": "https://slack.com/api/chat.postMessage",
    "method": "POST",
    "headers": {
    "Content-Type": "application/json; charset=utf-8",
    "Authorization": "Bearer {{ .secrets[\"__SLACK_APP_BOT_TOKEN_<team_id>\"] }}"
    },
    "body": {
    "channel": "<your-reviewers-channel-id>",
    "blocks": [
    {
    "type": "section",
    "text": {
    "type": "mrkdwn",
    "text": "*{{ .outputs.resolve_skill.response.entity.title }}* certification review: *{{ .outputs.synthesize_recommendation.response | fromjson | .certification_recommendation }}*\n{{ .outputs.synthesize_recommendation.response | fromjson | .certification_recommendation_reasoning }}"
    }
    }
    ]
    },
    "onTimeout": "continue",
    "onFailure": "continue"
    }
    }
    ],
    "connections": [
    { "sourceIdentifier": "trigger_event", "targetIdentifier": "resolve_skill" },
    { "sourceIdentifier": "trigger_manual", "targetIdentifier": "resolve_skill" },
    { "sourceIdentifier": "resolve_skill", "targetIdentifier": "check_uniqueness" },
    { "sourceIdentifier": "check_uniqueness", "targetIdentifier": "check_description_quality" },
    { "sourceIdentifier": "check_description_quality", "targetIdentifier": "synthesize_recommendation" },
    { "sourceIdentifier": "synthesize_recommendation", "targetIdentifier": "update_skill_entity" },
    { "sourceIdentifier": "update_skill_entity", "targetIdentifier": "notify_admin_slack" }
    ]
    }
  5. Replace <your-reviewers-channel-id> and __SLACK_APP_BOT_TOKEN_<team_id> the same way you did in the previous guide, if you haven't already.

  6. Click Save.

Step 3: Visualize in your dashboards

Add uniqueness to the Skills Lifecycle Control dashboard

If you already have the Pending Publish Requests table from the previous two guides, extend it:

  • Open the table widget and edit it.
  • Add the Skill uniqueness scorecard as a column, alongside Production readiness and Skill discoverability.
  • Click Save.

A reviewer now sees a duplicate flag right in the same table as the request itself, before they approve or reject it.

Add uniqueness to the Skills Registry Health dashboard

Add one more widget to the Skills Registry Health dashboard, alongside Skills Production Readiness, Published Skills, Not Certified, and Discoverability:

  • Uniqueness:
    • Click + Widget and select Pie Chart.

    • Title: Uniqueness.

    • Description: Checks whether a skill duplicates or overlaps with another.

    • Choose the skill blueprint.

    • Under Breakdown by property, select Uniqueness.

    • Add this filter to the Initial filters editor, so the chart only counts skills that have actually been reviewed:

      {
      "combinator": "and",
      "rules": [{ "operator": "isNotEmpty", "property": "last_certification_check_at" }]
      }
    • Click Save.

Between the extended table and this pie chart, a reviewer sees a duplicate flag before approving a request, and a platform team sees how much duplication is accumulating across the whole registry, the same way they already track description quality and production readiness.

Possible enhancements

Suggest deduplication automatically

Flagging a duplicate still leaves someone to decide what to do about it: delete one skill, or merge two into one. That decision needs the same context a human reviewer would use, plus someone to actually act on it. A dedicated workflow can propose that action instead of leaving it to whoever happens to notice a low uniqueness score.

At a high level:

  • Add a skills_dedup_suggestion blueprint. Conceptually, it needs: a status (pending, approved, rejected), the assessment and reasoning behind it, a recommended action (for example merge or remove), a link to the pull request once one exists, and relations back to the skill entities involved, the same skill and its similar_skills from Step 2.
  • Add a workflow triggered when a skill's uniqueness drops. An event trigger watching for uniqueness becoming mostly_duplicate or complete_duplicate fits the same pattern as On Skill Created above. It fetches the skill and every entity in its similar_skills relation, has an AI node assess whether one should simply be removed or whether several should be merged into one, and delegates the actual file changes to a coding agent that opens a pull request to the skills repo with the deduplicated result. The workflow records its assessment and the pull request link on a new skills_dedup_suggestion entity.
  • Extend the Skills Lifecycle Control dashboard. A table widget showing open skills_dedup_suggestion entities gives reviewers the same kind of queue as Pending Publish Requests. Its pending/approved/rejected statuses can be automated the same way, through dedicated approve and reject workflows like the ones in the previous guide's enhancements.