Skip to content

fix(skills): reconcile managed skill publication form on sync#153

Merged
BlackHole1 merged 3 commits into
mainfrom
fix-register-skills
May 7, 2026
Merged

fix(skills): reconcile managed skill publication form on sync#153
BlackHole1 merged 3 commits into
mainfrom
fix-register-skills

Conversation

@BlackHole1
Copy link
Copy Markdown
Member

Registry skill startup synchronization and update currency checks previously compared only metadata, so a symlinked target on a copy-only host (e.g., CodeBuddy) was treated as up-to-date even though it violated the host's required publication mode.

Add isManagedSkillPublicationCurrent and consult it from both the startup sync and isRegistrySkillCurrentInAllHosts so a stale symlink triggers republication as a copy when the host requires it.

Registry skill startup synchronization and update currency checks
previously compared only metadata, so a symlinked target on a
copy-only host (e.g., CodeBuddy) was treated as up-to-date even
though it violated the host's required publication mode.

Add `isManagedSkillPublicationCurrent` and consult it from both the
startup sync and `isRegistrySkillCurrentInAllHosts` so a stale
symlink triggers republication as a copy when the host requires it.

Signed-off-by: Kevin Cui <bh@bugs.cc>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 96657443-ac33-4ac9-8ef0-6f1384d36150

📥 Commits

Reviewing files that changed from the base of the PR and between f1bb718 and 2b73b6f.

📒 Files selected for processing (1)
  • src/application/commands/skills/managed-skill-publication.test.ts

Summary by CodeRabbit

  • New Features

    • Added per-installation publication mode handling and publication-state checks for managed skills.
  • Bug Fixes

    • Registry synchronization now skips redundant publishes when installations are already current and honors host-specific publication modes.
    • “Current” status now requires both metadata match and confirmed publication state.
  • Tests

    • Added CLI startup sync test and a test ensuring missing copy-mode targets are treated as not current.

Walkthrough

This PR adds publication mode-aware synchronization logic for managed skills. A new isManagedSkillPublicationCurrent() helper validates whether a managed skill's filesystem state (symlink vs. copy) matches its intended publication mode. The auto-sync workflow now resolves publication mode per installation, checks metadata consistency, and skips republishing if the skill is already current. The update module's currentness detection now combines metadata equality with publication state validation, ensuring accurate detection across hosts. A new test verifies that symlinked registry skills are properly converted to copies for CodeBuddy during CLI startup.

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant AutoSync
  participant Filesystem
  participant Publisher
  CLI->>AutoSync: startup triggers registry sync
  AutoSync->>Filesystem: readManagedSkillTargetState
  AutoSync->>AutoSync: resolveManagedSkillPublicationMode(agentName)
  AutoSync->>Filesystem: compare canonical vs installed metadata
  alt metadata differs
    AutoSync->>Publisher: publishBundledSkillInstallation(publicationMode)
    Publisher->>Filesystem: write files (copy or symlink)
  else metadata matches
    AutoSync->>Filesystem: isManagedSkillPublicationCurrent(path, publicationMode)
    alt publication not current
      AutoSync->>Publisher: publishBundledSkillInstallation(publicationMode)
      Publisher->>Filesystem: write files (copy or symlink)
    else publication current
      AutoSync->>CLI: skip publishing
    end
  end
  AutoSync->>CLI: log "Registry skill synchronized during CLI startup."
Loading

Possibly related PRs

  • oomol-lab/oo-cli#35: Introduces the bundled-skill publication flow (canonical directory + symlink-or-copy publishing) that this PR extends with per-installation publication-mode resolution and currentness checks.
  • oomol-lab/oo-cli#111: Refactored the auto-sync and managed-skill publication code paths that this PR modifies to add per-installation publication-mode resolution and publication-current validation.
  • oomol-lab/oo-cli#123: Makes concurrent changes to per-agent publication-mode resolution and publication-state checking logic in the same skill synchronization and publication flows.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title follows the required format and clearly describes the main change: fixing managed skill publication form reconciliation during sync.
Description check ✅ Passed The description is directly related to the changeset, explaining the problem (symlinks on copy-only hosts treated as current) and the solution (adding isManagedSkillPublicationCurrent check).
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch fix-register-skills

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/application/commands/skills/update.ts (1)

623-655: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Add direct skills update coverage for the new publication-currentness check.

This path now changes when skills update decides a managed registry skill is already current, but the added test only exercises startup sync. A stale symlink on a copy-only host can still regress here without failing the suite, so this needs a focused update-path test as well.

As per coding guidelines, "Any modification must include sufficient tests".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/application/commands/skills/update.ts` around lines 623 - 655, Add a
focused test for the skills update path that exercises
isRegistrySkillCurrentInAllHosts' new publication-currentness check: create a
test that sets up a managed registry skill across hosts (including a copy-only
host with a stale symlink), stub/spy resolveManagedSkillHostInstallations to
return those hosts and stub
directoryExists/readManagedSkillMetadata/isManagedSkillPublicationCurrent (and
resolveManagedSkillPublicationMode) to simulate both publicationCurrent = true
and publicationCurrent = false cases, then call the code path that runs "skills
update" and assert that when publicationCurrent is true no update/install is
attempted and when false the update/install flow runs; reference the function
names isRegistrySkillCurrentInAllHosts, resolveManagedSkillHostInstallations,
readManagedSkillMetadata, isManagedSkillPublicationCurrent, and
resolveManagedSkillPublicationMode when locating code to stub and where to
assert behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/application/commands/skills/auto-sync.ts`:
- Around line 294-321: The current code returns early when a managed target's
packageName or version differs from skill.metadata, preventing stale managed
installs from being reconciled; instead, remove the return in the
metadata-mismatch branch so the republish path runs, and only short-circuit when
metadata matches AND isManagedSkillPublicationCurrent(...) returns true.
Concretely: in the targetState.kind === "managed" block, keep the
context.logger.warn(...) when targetState.metadata differs from skill.metadata
but do not return there; afterwards change the existing short-circuit to: if
(metadata matches && await
isManagedSkillPublicationCurrent(installation.installedSkillDirectoryPath,
publicationMode)) return; ensuring variables referenced (targetState,
skill.metadata.packageName/version, installation, publicationMode,
isManagedSkillPublicationCurrent, context.logger.warn) are used to implement
this flow.

In `@src/application/commands/skills/managed-skill-publication.ts`:
- Around line 24-26: The switch branch for publicationMode "copy" currently
awaits lstat(skillDirectoryPath) which can throw ENOENT; modify this logic to
EAFP-style: wrap the lstat(...) call in a try/catch, return false when the
caught error.code === "ENOENT" (treat missing target as not current), and
rethrow or propagate other errors; keep the existing return for the non-symlink
case when lstat succeeds so callers can republish instead of aborting.

---

Outside diff comments:
In `@src/application/commands/skills/update.ts`:
- Around line 623-655: Add a focused test for the skills update path that
exercises isRegistrySkillCurrentInAllHosts' new publication-currentness check:
create a test that sets up a managed registry skill across hosts (including a
copy-only host with a stale symlink), stub/spy
resolveManagedSkillHostInstallations to return those hosts and stub
directoryExists/readManagedSkillMetadata/isManagedSkillPublicationCurrent (and
resolveManagedSkillPublicationMode) to simulate both publicationCurrent = true
and publicationCurrent = false cases, then call the code path that runs "skills
update" and assert that when publicationCurrent is true no update/install is
attempted and when false the update/install flow runs; reference the function
names isRegistrySkillCurrentInAllHosts, resolveManagedSkillHostInstallations,
readManagedSkillMetadata, isManagedSkillPublicationCurrent, and
resolveManagedSkillPublicationMode when locating code to stub and where to
assert behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 84fcd209-3ac2-4620-a4fd-9dd61f4340c6

📥 Commits

Reviewing files that changed from the base of the PR and between e174b61 and 6d0284c.

📒 Files selected for processing (4)
  • src/application/commands/skills/auto-sync.ts
  • src/application/commands/skills/index.cli.test.ts
  • src/application/commands/skills/managed-skill-publication.ts
  • src/application/commands/skills/update.ts

Comment on lines 294 to +321
if (targetState.kind === "managed") {
if (
targetState.metadata?.packageName === skill.metadata.packageName
&& targetState.metadata.version === skill.metadata.version
targetState.metadata?.packageName !== skill.metadata.packageName
|| targetState.metadata.version !== skill.metadata.version
) {
context.logger.warn(
{
agentName: installation.agentName,
canonicalPackageName: skill.metadata.packageName,
canonicalVersion: skill.metadata.version,
path: installation.installedSkillDirectoryPath,
skillName: skill.name,
targetPackageName: targetState.metadata?.packageName,
targetVersion: targetState.metadata?.version,
},
"Registry skill startup synchronization skipped because the target metadata does not match canonical metadata.",
);
return;
}

context.logger.warn(
{
agentName: installation.agentName,
canonicalPackageName: skill.metadata.packageName,
canonicalVersion: skill.metadata.version,
path: installation.installedSkillDirectoryPath,
skillName: skill.name,
targetPackageName: targetState.metadata?.packageName,
targetVersion: targetState.metadata?.version,
},
"Registry skill startup synchronization skipped because the target metadata does not match canonical metadata.",
);
return;
if (
await isManagedSkillPublicationCurrent(
installation.installedSkillDirectoryPath,
publicationMode,
)
) {
return;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Metadata mismatch should trigger republishing, not an early skip.

This branch now returns when a managed target’s packageName or version differs from canonical metadata. That prevents startup sync from reconciling stale managed installations at all; only the “metadata matches and publication is current” case should short-circuit.

💡 Suggested fix
         if (targetState.kind === "managed") {
-            if (
-                targetState.metadata?.packageName !== skill.metadata.packageName
-                || targetState.metadata.version !== skill.metadata.version
-            ) {
-                context.logger.warn(
-                    {
-                        agentName: installation.agentName,
-                        canonicalPackageName: skill.metadata.packageName,
-                        canonicalVersion: skill.metadata.version,
-                        path: installation.installedSkillDirectoryPath,
-                        skillName: skill.name,
-                        targetPackageName: targetState.metadata?.packageName,
-                        targetVersion: targetState.metadata?.version,
-                    },
-                    "Registry skill startup synchronization skipped because the target metadata does not match canonical metadata.",
-                );
-                return;
-            }
+            const metadataCurrent =
+                targetState.metadata?.packageName === skill.metadata.packageName
+                && targetState.metadata.version === skill.metadata.version;
 
             if (
+                metadataCurrent
+                &&
                 await isManagedSkillPublicationCurrent(
                     installation.installedSkillDirectoryPath,
                     publicationMode,
                 )
             ) {
                 return;
             }
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/application/commands/skills/auto-sync.ts` around lines 294 - 321, The
current code returns early when a managed target's packageName or version
differs from skill.metadata, preventing stale managed installs from being
reconciled; instead, remove the return in the metadata-mismatch branch so the
republish path runs, and only short-circuit when metadata matches AND
isManagedSkillPublicationCurrent(...) returns true. Concretely: in the
targetState.kind === "managed" block, keep the context.logger.warn(...) when
targetState.metadata differs from skill.metadata but do not return there;
afterwards change the existing short-circuit to: if (metadata matches && await
isManagedSkillPublicationCurrent(installation.installedSkillDirectoryPath,
publicationMode)) return; ensuring variables referenced (targetState,
skill.metadata.packageName/version, installation, publicationMode,
isManagedSkillPublicationCurrent, context.logger.warn) are used to implement
this flow.

Comment thread src/application/commands/skills/managed-skill-publication.ts Outdated
Treat disappearing copy-mode skill targets as not current so sync and update paths can republish instead of aborting on ENOENT. This keeps the publication-form reconciliation robust against filesystem races.

Signed-off-by: Kevin Cui <bh@bugs.cc>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/application/commands/skills/managed-skill-publication.test.ts`:
- Around line 31-37: Replace the hardcoded POSIX "/tmp/..." with a platform-safe
temp path: import os and path (node:os, node:path) in the test and construct the
target using path.join(os.tmpdir(),
"oo-missing-managed-skill-publication-target") (or use fs.mkdtemp for a unique
dir) and pass that value to isManagedSkillPublicationCurrent so the test no
longer assumes POSIX separators.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e714f0ed-96c0-46ca-a88d-5d7eebebf5fa

📥 Commits

Reviewing files that changed from the base of the PR and between 6d0284c and f1bb718.

📒 Files selected for processing (2)
  • src/application/commands/skills/managed-skill-publication.test.ts
  • src/application/commands/skills/managed-skill-publication.ts

Comment thread src/application/commands/skills/managed-skill-publication.test.ts
Build the missing publication target path from the platform temp directory and a v7 UUID so the test stays portable across operating systems.

Signed-off-by: Kevin Cui <bh@bugs.cc>
@BlackHole1 BlackHole1 merged commit 684ef4b into main May 7, 2026
4 of 5 checks passed
@BlackHole1 BlackHole1 deleted the fix-register-skills branch May 7, 2026 15:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant