Skip to content

fix: protect installs and artifact routing#164

Merged
benym merged 5 commits into
masterfrom
codex/fix-install-artifact-routing
Jul 7, 2026
Merged

fix: protect installs and artifact routing#164
benym merged 5 commits into
masterfrom
codex/fix-install-artifact-routing

Conversation

@benym

@benym benym commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Tests

  • npx vitest run test/domains/comet-classic/comet-scripts.test.ts test/domains/comet-classic/classic-hook-guard.test.ts test/domains/skill/symlink-install.test.ts test/domains/skill/skills.test.ts
  • git diff --check
  • git -C website diff --check

Summary by Sourcery

Protect symlink-based skill installs and adjust Classic superpowers artifact routing while preparing the 0.4.0-beta.2 release.

Bug Fixes:

  • Prevent symlink-mode installs from replacing an existing platform skills directory when it contains unmanaged files, preserving local or third-party content.
  • Route docs/superpowers writes to the appropriate design/build/verify governing change instead of being blocked by an unrelated active change.

Enhancements:

  • Add test coverage for symlink install safety and superpowers artifact routing behavior.

Build:

  • Bump package version to 0.4.0-beta.2 and update the changelog and website submodule accordingly.

Summary by CodeRabbit

  • Bug Fixes

    • Improved handling of docs/superpowers writes so the correct active change is used, and unmatched files are now blocked more clearly.
    • Fixed phase-guard installs to include only the language-specific rule file needed for the selected setup.
    • Prevented symlink-based installs from replacing existing skill folders when they contain files that aren’t managed.
  • Tests

    • Added coverage for Superpowers write routing and symlink-install protection behavior.
  • Documentation

    • Updated the changelog with the latest beta release notes.

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2de5e2e7-5add-46f2-b808-6527811b04b1

📥 Commits

Reviewing files that changed from the base of the PR and between 2ca0a7b and e63c81e.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • domains/skill/platform-install.ts
  • website
✅ Files skipped from review due to trivial changes (1)
  • CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • domains/skill/platform-install.ts

📝 Walkthrough

Walkthrough

This PR adds unmanaged-directory protection before replacing a skills directory with a symlink, refines classic hook guard routing for docs/superpowers artifact writes to match the governing change (with a runtime script mirror), updates related tests, and revises the changelog and website subproject pointer.

Changes

Symlink Install Safety

Layer / File(s) Summary
Managed entries validation and createSymlink update
domains/skill/platform-install.ts
Adds recursive directory-entry enumeration, a manifest-derived managed allowlist, and updated createSymlink logic that validates existing directories before removal to avoid deleting unmanaged files.
Symlink safety test coverage
test/domains/skill/symlink-install.test.ts
Adds a test verifying an existing skills directory with unmanaged files is not replaced by a symlink during overwrite installs.

Superpowers Artifact Routing Fix

Layer / File(s) Summary
Classic hook guard superpowers resolution
domains/comet-classic/classic-hook-guard.ts
Adds Superpowers path detection, phase eligibility, exact/inferred artifact-to-change matching, and updated governing-change routing and blocked-write responses.
Generated runtime script parity update
assets/skills/comet/scripts/comet-runtime.mjs
Mirrors the same Superpowers detection, resolution, and allow/block logic in the generated runtime script.
Concurrent-change hook guard test
test/domains/comet-classic/comet-scripts.test.ts
Adds/updates tests covering matched design-phase docs writes, longest-name boundary matching, and blocking of unmatched Superpowers artifact writes.

Release Metadata

Layer / File(s) Summary
Changelog and website pointer update
CHANGELOG.md, website
Adds 0.4.0-beta.2 fixed notes and updates the website subproject commit pointer.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
  participant classic-hook-guard
  participant SuperpowersResolver
  participant ClassicState

  classic-hook-guard->>classic-hook-guard: isSuperpowersArtifactPath(relativePath)
  alt is superpowers artifact
    classic-hook-guard->>SuperpowersResolver: resolve governing change
    SuperpowersResolver->>ClassicState: match recorded design/plan/verification paths
    alt exact match found
      ClassicState-->>SuperpowersResolver: matching change
    else no exact match
      SuperpowersResolver->>SuperpowersResolver: infer change from filename stem
    end
    SuperpowersResolver-->>classic-hook-guard: governing change (matched/unmatched)
  else not superpowers artifact
    classic-hook-guard->>classic-hook-guard: repoSourceGoverningChange(projectRoot)
  end
  classic-hook-guard->>classic-hook-guard: allow if phase design/build/verify and matched, else block
Loading
sequenceDiagram
  participant Installer as copyCometSkillsForPlatform
  participant CreateSymlink as createSymlink
  participant FS as Filesystem

  Installer->>CreateSymlink: createSymlink(target, linkPath, managedEntries)
  CreateSymlink->>FS: attempt unlink existing path
  FS-->>CreateSymlink: unlink fails (directory)
  CreateSymlink->>FS: collect directory entry paths
  FS-->>CreateSymlink: entry paths
  CreateSymlink->>CreateSymlink: check entries against managedEntries
  alt unmanaged entries found
    CreateSymlink-->>Installer: throw error, directory preserved
  else all entries managed
    CreateSymlink->>FS: remove directory and create symlink
  end
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the two main behavioral fixes: safer installs and corrected artifact routing.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage 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.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/fix-install-artifact-routing

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@sourcery-ai

sourcery-ai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Reviewer's Guide

Protects symlink-mode platform skill installs from deleting unmanaged skills directories, and adjusts Classic hook governance so docs/superpowers artifacts are associated with the appropriate design/build/verify change, along with version/changelog updates.

Sequence diagram for routing docs/superpowers artifacts to the correct governing change

sequenceDiagram
  participant GitHook as classicHookGuardCommand
  participant Guard as governingChange
  participant Super as superpowersArtifactGoverningChange
  participant Active as activeChanges

  GitHook->>Guard: governingChange(relativePath, projectRoot)
  Guard->>Guard: isSuperpowersArtifactPath(relativePath)
  alt isSuperpowersArtifactPath
    Guard->>Super: superpowersArtifactGoverningChange(relativePath, projectRoot)
    Super->>Active: activeChanges(projectRoot)
    Active-->>Super: active
    Super->>Super: matchesRecordedSuperpowersArtifact(relativePath, governing)
    alt recorded match
      Super-->>Guard: recorded governing
    else no recorded match
      Super->>Super: allowsSuperpowersArtifacts(governing)
      Super->>Super: governingChangeName(governing)
      alt name in relativePath
        Super-->>Guard: named governing
      else no named governing
        Super-->>Guard: first eligible governing or null
      end
    end
    Guard->>Guard: repoSourceGoverningChange(projectRoot) (fallback)
  else not Superpowers path
    Guard->>Guard: repoSourceGoverningChange(projectRoot)
  end
  Guard-->>GitHook: governing
  GitHook->>GitHook: isSuperpowersArtifactPath(relativePath)
  GitHook->>GitHook: allowsSuperpowersArtifacts(governing)
  alt allowed
    GitHook-->>GitHook: allowed(relativePath (superpowers))
  else blocked
    GitHook-->>GitHook: existing phase guards apply
  end
Loading

File-Level Changes

Change Details Files
Guard symlink-mode platform installs from replacing skills directories that contain unmanaged files.
  • Add helpers to compute managed skill replacement paths from the manifest and to recursively collect directory entries.
  • Introduce an assertion that a skills directory contains only manifest-managed entries before allowing its removal.
  • Extend createSymlink to inspect existing paths, and for non-symlink directories only delete them after verifying they contain no unmanaged entries.
  • Wire managed path computation into installSkillsAsSymlink so platform skills symlinks are only created when safe.
domains/skill/platform-install.ts
Route docs/superpowers writes to the correct governing change in Classic so unrelated active changes no longer block Superpowers artifacts.
  • Introduce helpers to recognize Superpowers paths, determine eligible phases, and derive names from governing changes.
  • Implement a superpowersArtifactGoverningChange selector that prefers recorded artifact paths, then matching-named changes, then any eligible design/build/verify change.
  • Update governingChange to special-case Superpowers artifacts and reuse the new resolver, and reuse isSuperpowersArtifactPath in the hook guard allow-path check.
  • Regenerate the runtime bundle to include the new Superpowers artifact routing logic.
domains/comet-classic/classic-hook-guard.ts
assets/skills/comet/scripts/comet-runtime.mjs
Expand tests and documentation to cover symlink safety and Superpowers artifact routing and bump the package version.
  • Add a symlink install test that ensures existing skills directories containing unmanaged files are preserved and not converted to symlinks.
  • Add a Classic hook guard test ensuring docs/superpowers writes succeed for the matching design change when another active change is open.
  • Document the fixes in the changelog, updating the version to 0.4.0-beta.2 and syncing the website submodule/lockfile metadata.
test/domains/skill/symlink-install.test.ts
test/domains/comet-classic/comet-scripts.test.ts
CHANGELOG.md
package.json
package-lock.json
website

Assessment against linked issues

Issue Objective Addressed Explanation
#159 Prevent comet init symlink install mode from deleting or overwriting existing non-Comet (unmanaged) skills in the platform skills/ directory (e.g., ~/.claude/skills/).
#160 Update the Comet hook (classic phase guard) so that writes to docs/superpowers/ are governed by the correct active change (the one actually associated with the Design Doc / artifact), rather than always using the alphabetically first active change.
#160 Ensure that when multiple changes are active, a design/build/verify-phase change can successfully write its docs/superpowers/ artifacts without being blocked by another active change that is still in open phase.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@greptile-apps

greptile-apps Bot commented Jul 7, 2026

Copy link
Copy Markdown

Greptile Summary

This PR protects skill installs and fixes Classic Superpowers artifact routing.

  • Adds a guard before symlink-mode installs replace an existing platform skills/ directory.
  • Routes docs/superpowers/ writes to the matching active Classic change.
  • Blocks unmatched Superpowers artifact writes instead of using an unrelated active change.
  • Syncs the generated Classic runtime, tests, changelog, and website pointer.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.

Important Files Changed

Filename Overview
domains/comet-classic/classic-hook-guard.ts Adds explicit matching and blocking behavior for Classic docs/superpowers/ artifact writes.
assets/skills/comet/scripts/comet-runtime.mjs Syncs the generated runtime with the Classic hook guard changes.
domains/skill/platform-install.ts Adds a managed-entry check before replacing an existing platform skills/ directory in symlink install mode.
test/domains/comet-classic/comet-scripts.test.ts Adds coverage for matching, longest-name, and unmatched Superpowers artifact routing.
test/domains/skill/symlink-install.test.ts Adds coverage for preserving unmanaged files during symlink-mode skill installs.

Reviews (5): Last reviewed commit: "chore: merge master into artifact routin..." | Re-trigger Greptile

Comment thread domains/comet-classic/classic-hook-guard.ts Outdated

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 4 issues, and left some high level feedback:

  • In superpowersArtifactGoverningChange and repoSourceGoverningChange, activeChanges(projectRoot) is invoked independently; consider threading a shared active-changes list through these call sites to avoid redundant filesystem calls when resolving the governing change for superpowers artifacts.
  • The unmanaged entries error in assertDirectoryContainsOnlyManagedEntries currently only shows the first few offending paths; it may be useful to include the manifest-derived managedEntries root (e.g., the skills directory) in the message to make it clearer to users what directory tree is considered managed.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `superpowersArtifactGoverningChange` and `repoSourceGoverningChange`, `activeChanges(projectRoot)` is invoked independently; consider threading a shared active-changes list through these call sites to avoid redundant filesystem calls when resolving the governing change for superpowers artifacts.
- The unmanaged entries error in `assertDirectoryContainsOnlyManagedEntries` currently only shows the first few offending paths; it may be useful to include the manifest-derived `managedEntries` root (e.g., the skills directory) in the message to make it clearer to users what directory tree is considered managed.

## Individual Comments

### Comment 1
<location path="domains/skill/platform-install.ts" line_range="97" />
<code_context>
+  managedEntries: Set<string>,
+): Promise<void> {
+  const entries = await collectDirectoryEntryPaths(dirPath);
+  const unmanagedEntries = entries.filter((entry) => !managedEntries.has(entry));
+  if (unmanagedEntries.length === 0) return;
+
</code_context>
<issue_to_address>
**issue (bug_risk):** Managed entries check treats all nested files/dirs under a managed skill as unmanaged.

Because `collectDirectoryEntryPaths` returns full relative paths (e.g. `a/b/c.txt`) while `managedEntries` only contains prefix paths (e.g. `a`, `a/b`), the `managedEntries.has(entry)` check treats all nested files/dirs under a managed skill as unmanaged. As a result, directories that only contain manifest-managed skills can still be rejected.

To treat entries as managed when they are under a managed prefix, you could instead check whether any prefix of the path is in `managedEntries`:

```ts
const unmanagedEntries = entries.filter((entry) => {
  const parts = entry.split('/');
  for (let depth = 1; depth <= parts.length; depth++) {
    if (managedEntries.has(parts.slice(0, depth).join('/'))) return false;
  }
  return true;
});
```

This preserves the guard against unrelated top-level entries while not flagging contents of managed skill paths as unmanaged.
</issue_to_address>

### Comment 2
<location path="domains/comet-classic/classic-hook-guard.ts" line_range="223-226" />
<code_context>
       return { changeDir, phase: 'open', classic: null, archived: false };
     }
   }
+  if (isSuperpowersArtifactPath(relativePath)) {
+    return (
+      (await superpowersArtifactGoverningChange(relativePath, projectRoot)) ??
+      repoSourceGoverningChange(projectRoot)
+    );
+  }
</code_context>
<issue_to_address>
**issue:** Mixed awaited and non-awaited operands in `??` expression can lead to type/clarity issues.

Here the left side resolves to `GoverningChange | null`, while the right side is `Promise<GoverningChange | null>`, so the full expression’s type becomes `GoverningChange | null | Promise<GoverningChange | null>`. That can cause awkward unions under strict typing.

To keep the return type consistent, consider awaiting both branches, e.g.:

```ts
const superpowers = await superpowersArtifactGoverningChange(relativePath, projectRoot);
if (superpowers) return superpowers;
return await repoSourceGoverningChange(projectRoot);
```

or:

```ts
return (
  (await superpowersArtifactGoverningChange(relativePath, projectRoot)) ??
  (await repoSourceGoverningChange(projectRoot))
);
```
</issue_to_address>

### Comment 3
<location path="domains/comet-classic/classic-hook-guard.ts" line_range="155-156" />
<code_context>
+  return relativePath.startsWith('docs/superpowers/');
+}
+
+function allowsSuperpowersArtifacts(governing: GoverningChange): boolean {
+  return (
+    governing.phase === 'design' || governing.phase === 'build' || governing.phase === 'verify'
+  );
</code_context>
<issue_to_address>
**suggestion:** Superpowers phase logic is duplicated instead of reusing the new helper.

In `classicHookGuardCommand`, the superpowers phase check is still duplicated:

```ts
if (
  isSuperpowersArtifactPath(relativePath) &&
  (phase === 'design' || phase === 'build' || phase === 'verify')
) {
  …
}
```

Please reuse the new helper instead:

```ts
if (isSuperpowersArtifactPath(relativePath) && allowsSuperpowersArtifacts(governing)) {
  …
}
```

This keeps phase rules in one place and avoids future divergence between command logic and governing-change logic.

Suggested implementation:

```typescript
  );
}

function isSuperpowersArtifactPath(relativePath: string): boolean {
  return relativePath.startsWith('docs/superpowers/');
}

function allowsSuperpowersArtifacts(governing: GoverningChange): boolean {
  return (
    governing.phase === 'design' || governing.phase === 'build' || governing.phase === 'verify'
  );
}

```

You should also update the superpowers phase logic inside `classicHookGuardCommand` to reuse the new helper. Look for the condition that currently looks like:

```ts
if (
  isSuperpowersArtifactPath(relativePath) &&
  (phase === 'design' || phase === 'build' || phase === 'verify')
) {
  …
}
```

and change it to:

```ts
if (isSuperpowersArtifactPath(relativePath) && allowsSuperpowersArtifacts(governing)) {
  …
}
```

This assumes `governing` is already available in that scope (as suggested by your comment). If it is not, you will need to ensure `governing` is passed into or computed within `classicHookGuardCommand` so that the helper can be used.
</issue_to_address>

### Comment 4
<location path="test/domains/comet-classic/comet-scripts.test.ts" line_range="4987-5007" />
<code_context>
       expect(result.status).toBe(0);
     }, 20_000);

+    it('allows docs/superpowers writes for the matching design change when another active change is open', async () => {
+      await createChange(
+        tmpDir,
+        'cert-signature-auth',
+        ['workflow: full', 'phase: open', 'archived: false', ''].join('\n'),
+      );
+      await createChange(
+        tmpDir,
+        'env-issue-ledger',
+        ['workflow: full', 'phase: design', 'archived: false', ''].join('\n'),
+      );
+
+      const docsDir = path.join(tmpDir, 'docs', 'superpowers', 'specs');
+      await fs.mkdir(docsDir, { recursive: true });
+      const targetFile = path.join(docsDir, 'env-issue-ledger-design.md');
+
+      const result = runHookGuard(tmpDir, hookGuardScript, hookStdin(targetFile));
+
+      expect(result.status).toBe(0);
+      expect(result.stderr).toContain('phase: design, superpowers');
+    }, 20_000);
+
</code_context>
<issue_to_address>
**suggestion (testing):** Consider adding a test that covers matching via recorded Superpowers artifacts (designDoc/plan/verificationReport), not just filename-based matching.

To exercise `superpowersArtifactGoverningChange` more fully, please add a test where the governing change is selected via recorded artifacts (e.g. `classic.designDoc` or `classic.plan`), not just by inferring from the filename.

For instance:
- Create two active changes and set `classic.designDoc` (or `plan`) on one to `docs/superpowers/specs/foo-design.md`,
- Write to that path, and
- Assert that the hook guard allows the write and that stderr reports the governing change/phase for the change with the recorded artifact.

This would explicitly cover the `matchesRecordedSuperpowersArtifact` path and help prevent regressions in artifact-based routing.

```suggestion
    it('allows docs/superpowers writes for the matching design change when another active change is open', async () => {
      await createChange(
        tmpDir,
        'cert-signature-auth',
        ['workflow: full', 'phase: open', 'archived: false', ''].join('\n'),
      );
      await createChange(
        tmpDir,
        'env-issue-ledger',
        ['workflow: full', 'phase: design', 'archived: false', ''].join('\n'),
      );

      const docsDir = path.join(tmpDir, 'docs', 'superpowers', 'specs');
      await fs.mkdir(docsDir, { recursive: true });
      const targetFile = path.join(docsDir, 'env-issue-ledger-design.md');

      const result = runHookGuard(tmpDir, hookGuardScript, hookStdin(targetFile));

      expect(result.status).toBe(0);
      expect(result.stderr).toContain('phase: design, superpowers');
    }, 20_000);

    it('allows docs/superpowers writes when governing change is selected via recorded designDoc artifact', async () => {
      // Create an open change without any recorded superpowers artifacts.
      await createChange(
        tmpDir,
        'cert-signature-auth',
        ['workflow: full', 'phase: open', 'archived: false', ''].join('\n'),
      );

      // Create a design-phase change that records a classic.designDoc superpowers artifact.
      await createChange(
        tmpDir,
        'env-issue-ledger',
        [
          'workflow: full',
          'phase: design',
          'archived: false',
          'classic.designDoc: docs/superpowers/specs/env-issue-ledger-design.md',
          '',
        ].join('\n'),
      );

      const docsDir = path.join(tmpDir, 'docs', 'superpowers', 'specs');
      await fs.mkdir(docsDir, { recursive: true });
      const targetFile = path.join(docsDir, 'env-issue-ledger-design.md');

      const result = runHookGuard(tmpDir, hookGuardScript, hookStdin(targetFile));

      expect(result.status).toBe(0);
      expect(result.stderr).toContain('phase: design, superpowers');
    }, 20_000);
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread domains/skill/platform-install.ts
Comment thread domains/comet-classic/classic-hook-guard.ts Outdated
Comment thread domains/comet-classic/classic-hook-guard.ts
Comment thread test/domains/comet-classic/comet-scripts.test.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
assets/skills/comet/scripts/comet-runtime.mjs (1)

11300-11313: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Same substring name-matching risk as the TS source.

This mirrors superpowersArtifactGoverningChange in domains/comet-classic/classic-hook-guard.ts, including the boundary-unsafe relativePath2.includes(name) fallback at Lines 11307-11310. Since this is a generated/compiled artifact, the fix belongs upstream in the TS source and should propagate here via regeneration rather than being patched directly.

🤖 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 `@assets/skills/comet/scripts/comet-runtime.mjs` around lines 11300 - 11313,
The fallback name matching in superpowersArtifactGoverningChange uses an unsafe
substring check that can incorrectly match unrelated paths. Fix this upstream in
the TypeScript source version of superpowersArtifactGoverningChange in
domains/comet-classic/classic-hook-guard.ts by replacing the boundary-unsafe
relativePath2.includes(name) logic with a safer path-boundary check, then
regenerate the compiled comet-runtime.mjs artifact so the change propagates here
rather than editing this generated file directly.
🧹 Nitpick comments (2)
CHANGELOG.md (1)

5-11: 📐 Maintainability & Code Quality | 🔵 Trivial

Align the changelog block with the documented release-note format.

The entry header is fine, but the section markup (### Fixed) does not match the repository rule for CHANGELOG.md release notes. Please use the prescribed section structure so the changelog stays machine- and human-consistent. As per coding guidelines: CHANGELOG.md entries must use ## What's Changed [x.y.z] - YYYY-MM-DD with sections for Added/Changed/Fixed/Tests/Removed/Security, each entry starting with - **keyword**: description.

🤖 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 `@CHANGELOG.md` around lines 5 - 11, The changelog entry for the 0.4.0-beta.2
release needs to follow the repository’s required release-note structure. Update
the `CHANGELOG.md` section formatting so it uses the prescribed section headings
(Added/Changed/Fixed/Tests/Removed/Security) and keep each bullet in the `-
**keyword**: description` style, adjusting the existing `Fixed` items to fit
that format without changing the release header.

Source: Coding guidelines

test/domains/comet-classic/comet-scripts.test.ts (1)

4987-5008: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Good coverage for the primary concurrent-change scenario. Consider also adding a case with overlapping/substring change names (e.g. auth vs auth-v2) to cover the name-inclusion fallback's boundary behavior, tied to the matching concern raised in classic-hook-guard.ts.

🤖 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 `@test/domains/comet-classic/comet-scripts.test.ts` around lines 4987 - 5008,
The current test covers the main concurrent-change path, but it should also
exercise the name-matching fallback in classic-hook-guard.ts for overlapping
change names. Add a test in comet-scripts.test.ts that uses two active changes
with substring/overlapping names like auth and auth-v2, then verify the
docs/superpowers write is allowed only for the correctly matching design change.
Make sure the assertions prove the boundary behavior of the matching logic in
the hook guard.
🤖 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 `@domains/comet-classic/classic-hook-guard.ts`:
- Around line 165-197: The fallback matching in
superpowersArtifactGoverningChange is too permissive because
relativePath.includes(name) can select the wrong GoverningChange for overlapping
or substring-only names. Tighten the name-based fallback in
superpowersArtifactGoverningChange (and, if needed, governingChangeName usage)
to use a boundary-safe match such as checking path segments or preferring the
most specific/longest eligible name, while keeping
matchesRecordedSuperpowersArtifact as the exact-path fast path.

---

Duplicate comments:
In `@assets/skills/comet/scripts/comet-runtime.mjs`:
- Around line 11300-11313: The fallback name matching in
superpowersArtifactGoverningChange uses an unsafe substring check that can
incorrectly match unrelated paths. Fix this upstream in the TypeScript source
version of superpowersArtifactGoverningChange in
domains/comet-classic/classic-hook-guard.ts by replacing the boundary-unsafe
relativePath2.includes(name) logic with a safer path-boundary check, then
regenerate the compiled comet-runtime.mjs artifact so the change propagates here
rather than editing this generated file directly.

---

Nitpick comments:
In `@CHANGELOG.md`:
- Around line 5-11: The changelog entry for the 0.4.0-beta.2 release needs to
follow the repository’s required release-note structure. Update the
`CHANGELOG.md` section formatting so it uses the prescribed section headings
(Added/Changed/Fixed/Tests/Removed/Security) and keep each bullet in the `-
**keyword**: description` style, adjusting the existing `Fixed` items to fit
that format without changing the release header.

In `@test/domains/comet-classic/comet-scripts.test.ts`:
- Around line 4987-5008: The current test covers the main concurrent-change
path, but it should also exercise the name-matching fallback in
classic-hook-guard.ts for overlapping change names. Add a test in
comet-scripts.test.ts that uses two active changes with substring/overlapping
names like auth and auth-v2, then verify the docs/superpowers write is allowed
only for the correctly matching design change. Make sure the assertions prove
the boundary behavior of the matching logic in the hook guard.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: b0da2dc6-da1a-4129-a454-935d48e154b3

📥 Commits

Reviewing files that changed from the base of the PR and between 07b76bf and 4d69d63.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (8)
  • CHANGELOG.md
  • assets/skills/comet/scripts/comet-runtime.mjs
  • domains/comet-classic/classic-hook-guard.ts
  • domains/skill/platform-install.ts
  • package.json
  • test/domains/comet-classic/comet-scripts.test.ts
  • test/domains/skill/symlink-install.test.ts
  • website

Comment thread domains/comet-classic/classic-hook-guard.ts
Comment thread domains/comet-classic/classic-hook-guard.ts Outdated
Comment thread domains/comet-classic/classic-hook-guard.ts
@benym
benym merged commit 9d517aa into master Jul 7, 2026
16 checks passed
@benym
benym deleted the codex/fix-install-artifact-routing branch July 7, 2026 15:18
This was referenced Jul 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant