Skip to content

fix(update): don't hijack the agents target on legacy Codex upgrade - #1522

Open
clay-good wants to merge 5 commits into
mainfrom
fix/legacy-upgrade-agents-ownership
Open

fix(update): don't hijack the agents target on legacy Codex upgrade#1522
clay-good wants to merge 5 commits into
mainfrom
fix/legacy-upgrade-agents-ownership

Conversation

@clay-good

@clay-good clay-good commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

Status

LGTM — surgical fix for an init/update interaction found by an adversarial swarm on post-v1.8.0 main, now hardened by a second adversarial-review pass. Not a release blocker for the published v1.8.0 artifact (latent, opt-in path); good fast-follow. No separate tracking issue — the interaction is between two merged PRs, #1511 (Codex → .agents) and #1303 (vendor-neutral agents target); both are referenced below.

What was wrong

Codex (#1511) and the vendor-neutral agents target (#1303) both write to .agents/skills. The main generation path reconciles which tool owns a shared root before writing, but upgradeLegacyTools in update.ts did not.

So for a project that owns the agents target (.agents/skills/.openspec-target == "agents", generic /openspec- skills) whose user also has leftover global ~/.codex/prompts/* from an old Codex install: on openspec update --force (or any non-interactive run), Codex is auto-selected as an unconfigured legacy tool, and the loop wrote Codex $openspec- skills into .agents/skills and called writeSharedSkillTarget(projectPath, 'codex') — silently rewriting the generic tree with Codex-specific syntax and flipping the ownership marker agents → codex. Future updates then suppress the agents target entirely.

Second defect (found in adversarial review of the fix): even once skill generation was skipped, the caller's immediate legacy cleanup still deleted the skipped tool's repo-local .codex/prompts/openspec-*.md. That violates the cleanup contract — files are removed because a replacement was written — leaving the tool with no OpenSpec integration and destroying the legacy signal the skip relies on to re-offer later.

How it was fixed

  1. New sharedSkillRootOwner(projectPath, toolId) / sharedSkillRootOwnedByOther(...) in shared-skill-target.ts return the established owner only when the shared root already carries an ownership signal (a marker or an existing generated tree) and reconciliation resolves it to a different tool. upgradeLegacyTools consults it and skips skill generation for that tool, leaving the established owner in place — mirroring the one-writer rule init applies. An empty/unclaimed root returns undefined, so a genuine first-time Codex upgrade still creates its skills.
  2. upgradeLegacyTools now reports skippedSharedSkillTools, and performImmediateLegacyCleanup exempts those tools' repo-local artifacts via a new omitToolLegacyArtifacts helper — so a skipped tool's .codex/prompts is preserved, not deleted with nothing put in its place.

Replication / proof (live CLI on this branch)

Bug scenario — agents target + global ~/.codex/prompts, then update --force:

ℹ Skipped Codex: .agents/skills is already managed by another tool (Shared .agents skills).
marker: agents        # unchanged
generic-syntax — PRESERVED
codex — NOT in configured tools

Counter-check — Codex-only user, no .agents yet, then update --force:

✔ Setup complete for Codex
marker: codex
codex-syntax present — codex configured (not over-skipped)

Repo-local preservation — agents target + repo-local .codex/prompts/openspec-*.md, then update --force: the agents marker is unchanged and the .codex/prompts file survives (previously deleted).

Tests

  • test/core/shared-skill-target.test.ts — the predicate (marker-owned, inferred-owned, codex-owned, empty root, non-shared tool, ambiguous-tree owner resolution).
  • test/core/update.test.ts — three UpdateCommand end-to-end cases: (a) hijack blocked + Codex absent from the persisted configured-tool set + the skip names the owner; (b) inverse no-root first-time Codex upgrade still writes the codex marker; (c) a skipped tool's repo-local .codex/prompts is preserved.
  • test/core/legacy-cleanup.test.ts — unit coverage for omitToolLegacyArtifacts.

3860 tests pass locally (the 2 failures are pre-existing, environment-only, and pass on CI).

Review responses

  • alfred (add an UpdateCommand test for the actual legacy-upgrade path + the inverse no-root case): both added — see (a) and (b) above; the boundary is now exercised through the real command, not just the helper.
  • CodeRabbit (assert the skipped tool is not persisted; assert sharedSkillRootOwner resolves the owner): both added.

Notes

Companion PR (scenario-loss parity) fixes the other, unrelated defect from the same swarm.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Prevented legacy tool upgrades from overwriting shared skills managed by another tool.
    • Preserved vendor-neutral .agents skill trees and ownership markers during Codex upgrades.
    • Allowed first-time Codex upgrades without an existing .agents tree to proceed normally.
    • Skipped conflicting upgrades without generating files or marking tools as configured.
    • Preserved repository-local legacy prompts when an upgrade was skipped.
  • Documentation

    • Documented shared .agents ownership handling and explicit Codex initialization requirements.

Codex and the vendor-neutral `agents` target share `.agents/skills`. In
upgradeLegacyTools, a Codex install inferred only from global ~/.codex/prompts
wrote Codex skills into `.agents` and flipped the ownership marker
agents -> codex, silently rewriting an existing agents-owned tree. The main
generation path reconciles shared-target ownership first; this legacy-upgrade
path did not. Add sharedSkillRootOwnedByOther() and skip generation when a
different tool already owns the shared root (marker or existing tree), while
still allowing a genuine first-time Codex upgrade with no `.agents` yet.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@clay-good
clay-good requested a review from a team as a code owner August 5, 2026 21:49
@clay-good
clay-good requested review from TabishB and removed request for a team August 5, 2026 21:49
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 6a4d63b4-1976-4096-9961-9b994b048f5c

📥 Commits

Reviewing files that changed from the base of the PR and between f8ea958 and 3bc0cc1.

📒 Files selected for processing (2)
  • test/core/legacy-cleanup.test.ts
  • test/core/update.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/core/legacy-cleanup.test.ts

📝 Walkthrough

Walkthrough

The change adds shared skill-root ownership detection. Legacy upgrades skip conflicting tools and preserve their legacy artifacts. Tests and documentation cover ownership, Codex upgrades, and cleanup behavior.

Changes

Shared skill ownership protection

Layer / File(s) Summary
Shared-root ownership detection
src/core/shared-skill-target.ts, test/core/shared-skill-target.test.ts
Adds ownership reporting for shared skill roots. Tests cover markers, inferred ownership, ambiguous trees, Codex, and non-sharing tools.
Legacy upgrade ownership guard
src/core/update.ts, test/core/update.test.ts, .changeset/..., docs/supported-tools.md
Skips generation when another tool owns the shared root. The update reports skipped tools and preserves the existing owner.
Legacy artifact preservation
src/core/legacy-cleanup.ts, src/core/update.ts, test/core/legacy-cleanup.test.ts
Filters skipped tools from cleanup while retaining other detected artifacts. Tests verify selective removal and detection-state updates.

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

Sequence Diagram(s)

sequenceDiagram
  participant LegacyUpgrade
  participant SharedSkillRootOwner
  participant SkillGenerator
  participant LegacyCleanup
  LegacyUpgrade->>SharedSkillRootOwner: Check shared-root ownership
  SharedSkillRootOwner-->>LegacyUpgrade: Return owner or undefined
  LegacyUpgrade->>SkillGenerator: Generate skills when no other owner exists
  LegacyUpgrade->>LegacyCleanup: Pass skippedSharedSkillTools
  LegacyCleanup-->>LegacyUpgrade: Preserve skipped tools' legacy artifacts
Loading

Possibly related PRs

Suggested reviewers: tabishb

🚥 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 and concisely describes the main fix: preventing legacy Codex upgrades from taking ownership of an existing agents target.
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 fix/legacy-upgrade-agents-ownership

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.

@alfred-openspec alfred-openspec left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The ownership predicate looks right and the focused suite passes, but the regression only tests the helper. Please add an UpdateCommand test for the actual legacy-upgrade path: existing agents-owned .agents/skills plus global legacy Codex prompts, then non-interactive update --force, asserting the generic files and agents marker remain unchanged. Add the inverse no-root case proving first-time Codex generation still writes the codex marker. This control-flow boundary is the bug.

Harden the agents-target ownership fix after a multi-agent review:
- Add an integration test that runs the real update flow for the bug
  scenario (agents-owned .agents + a legacy global Codex prompt) and asserts
  the marker stays `agents` and skills keep generic `/openspec-` syntax. A
  unit test of the predicate can't catch a future refactor that stops calling
  it; this can.
- Name the owning tool in the skip message ("...managed by another tool
  (Shared .agents skills)") via a new sharedSkillRootOwner() helper that
  sharedSkillRootOwnedByOther now delegates to.
- Add a unit case for the ambiguous-tree branch (existing skills, no marker,
  no inferable syntax) and one asserting sharedSkillRootOwner names agents.
- Document the known, harmless re-offer tradeoff (a skipped tool isn't
  recorded as configured, so a persistent legacy prompt re-offers it).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
test/core/update.test.ts (1)

616-639: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Assert that the skipped Codex tool is not persisted.

The test proves that the .agents/skills tree is not rewritten. It does not prove that codex is absent from the persisted configured-tool set. Add an assertion against the persisted configuration or the existing getConfiguredToolsForProfileSync(testDir) helper. Otherwise, a regression can preserve the marker and syntax while recording Codex for later updates. The PR objective requires both generation and configuration to be skipped.

🤖 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/core/update.test.ts` around lines 616 - 639, Extend the test around
UpdateCommand.execute so it also verifies Codex is absent from the persisted
configured-tool set, using getConfiguredToolsForProfileSync(testDir) or the
existing persisted configuration access. Keep the current marker and syntax
assertions, and assert the configured tools do not include codex.
🤖 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 `@test/core/shared-skill-target.test.ts`:
- Around line 78-79: Extend the test for the ambiguous shared-skill tree to
assert the inferred owner directly by checking that
sharedSkillRootOwner(projectPath, 'codex') returns 'agents', while retaining the
existing sharedSkillRootOwnedByOther boolean assertion.

---

Nitpick comments:
In `@test/core/update.test.ts`:
- Around line 616-639: Extend the test around UpdateCommand.execute so it also
verifies Codex is absent from the persisted configured-tool set, using
getConfiguredToolsForProfileSync(testDir) or the existing persisted
configuration access. Keep the current marker and syntax assertions, and assert
the configured tools do not include codex.
🪄 Autofix

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 73b189f0-86f4-46d0-ab3f-4ca601d57059

📥 Commits

Reviewing files that changed from the base of the PR and between 133f585 and 2bf0eaf.

📒 Files selected for processing (4)
  • src/core/shared-skill-target.ts
  • src/core/update.ts
  • test/core/shared-skill-target.test.ts
  • test/core/update.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/core/update.ts

Comment thread test/core/shared-skill-target.test.ts
…ests

Adversarial review of the shared-root ownership guard surfaced one real
integration defect and the review asks from alfred/CodeRabbit.

Defect: when the guard skips a legacy Codex upgrade because the `.agents`
root is owned by another tool, the caller's immediate legacy cleanup still
deleted Codex's repo-local `.codex/prompts/openspec-*.md`. That violates the
cleanup contract (remove X only because replacement Y was written): no
replacement is written for a skipped tool, so its legacy files must stay.

`upgradeLegacyTools` now reports `skippedSharedSkillTools`, and
`performImmediateLegacyCleanup` exempts those tools' repo-local artifacts via
a new `omitToolLegacyArtifacts` helper. Refactored the per-artifact tool
matching out of `getToolsFromLegacyArtifacts` so both share one matcher.

Tests (addressing the review + the defect):
- update.test.ts: hijack test now asserts Codex is absent from the persisted
  configured-tool set and that the skip names the established owner.
- update.test.ts: inverse no-root case proves a first-time Codex upgrade still
  writes the `codex` marker via the real UpdateCommand path.
- update.test.ts: a skipped tool's repo-local `.codex/prompts` is preserved.
- legacy-cleanup.test.ts: unit coverage for omitToolLegacyArtifacts.
- shared-skill-target.test.ts: assert sharedSkillRootOwner resolves 'agents'.

Docs + changeset updated to describe the preserve-on-skip behavior.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 6, 2026

Copy link
Copy Markdown

Deploying openspec-docs with  Cloudflare Pages  Cloudflare Pages

Latest commit: f8ea958
Status: ✅  Deploy successful!
Preview URL: https://3003a301.openspec-docs.pages.dev
Branch Preview URL: https://fix-legacy-upgrade-agents-ow.openspec-docs.pages.dev

View logs

@clay-good

Copy link
Copy Markdown
Collaborator Author

Pushed f8ea958 addressing the review + a second defect found while adversarially re-reviewing the fix.

@alfred-openspec — added the two UpdateCommand tests you asked for. The control-flow boundary is now exercised through the real command:

  • hijack path: existing agents-owned .agents + global legacy Codex prompts → update --force asserts the generic files and agents marker are unchanged;
  • inverse no-root: a first-time Codex upgrade (no .agents yet) → update --force still writes the codex marker.

@coderabbitai — the hijack test now also asserts Codex is absent from the persisted configured-tool set (getConfiguredToolsForProfileSync), and the ambiguous-tree test asserts sharedSkillRootOwner resolves to agents.

New defect fixed: when the guard skips a legacy upgrade, the caller's immediate cleanup was still deleting that tool's repo-local .codex/prompts/openspec-*.md — removing the legacy file without writing a replacement. upgradeLegacyTools now reports skippedSharedSkillTools and performImmediateLegacyCleanup exempts those tools' repo-local artifacts (omitToolLegacyArtifacts). Covered by a new update.ts end-to-end test and a legacy-cleanup.ts unit test.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

git show --stat --oneline f8ea958
git show --format=fuller --find-renames --find-copies -- src/core/update.ts src/core/legacy-cleanup.ts src/core/shared-skill-target.ts test/core/update.test.ts test/core/legacy-cleanup.test.ts test/core/shared-skill-target.test.ts f8ea958

echo '--- source outlines ---'
for f in src/core/update.ts src/core/legacy-cleanup.ts src/core/shared-skill-target.ts; do
  if [ -f "$f" ]; then
    ast-grep outline "$f" --items all
  fi
done

echo '--- relevant definitions and call sites ---'
rg -n -C 8 \
  'upgradeLegacyTools|performImmediateLegacyCleanup|skippedSharedSkillTools|omitToolLegacyArtifacts|getConfiguredToolsForProfileSync|sharedSkillRootOwner' \
  src test

Length of output: 63614


@clay-good, I verified f8ea958.

UpdateCommand.upgradeLegacyTools records each shared-root skip in skippedSharedSkillTools. Both immediate cleanup paths pass that set to performImmediateLegacyCleanup.

omitToolLegacyArtifacts removes only skipped tools' repo-local legacy paths from the cleanup input. Therefore, a skipped Codex upgrade preserves .codex/prompts/openspec-*.md when no replacement skill was written.

The added tests cover:

  • the agents-owned shared-root path;
  • Codex exclusion from getConfiguredToolsForProfileSync;
  • owner-specific skip output;
  • first-time Codex ownership of an unclaimed root;
  • preservation of skipped Codex repo-local prompts; and
  • selective cleanup filtering.

The static inspection found no remaining issue in the reported flow. I did not execute the test suite in this environment.


✏️ Learnings added
Learnt from: clay-good
URL: https://github.com/Fission-AI/OpenSpec/pull/1522

Timestamp: 2026-08-06T18:46:03.341Z
Learning: In `src/core/update.ts`, when `UpdateCommand.upgradeLegacyTools` skips a legacy tool because another tool owns its shared skills root, the skipped tool must not be recorded as configured and its repo-local legacy slash-command artifacts must be excluded from immediate cleanup. No replacement is generated for a skipped tool, so deleting its legacy artifacts would remove its only OpenSpec integration.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

You are interacting with an AI system.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 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/core/update.ts`:
- Around line 896-900: Preserve skipped-tool ownership through deferred global
cleanup: update the deferred cleanup calls in src/core/update.ts lines 896-900
and 933-937 to pass skippedSharedSkillTools, and update
deferredGlobalCleanup/getLegacyGlobalPromptMatches so matching removable prompts
are excluded for skipped tool IDs. Add coverage for a skipped Codex upgrade
owned by an agents shared root with legacy global prompts; both sites require
the same change.

In `@test/core/legacy-cleanup.test.ts`:
- Around line 1497-1518: Extend the legacy cleanup tests around
omitToolLegacyArtifacts with a backslash-delimited
'.codex\prompts\openspec-explore.md' path and verify it is omitted when skipping
'codex'. Ensure the assertion also confirms hasLegacyArtifacts is recomputed
correctly after removing the Windows-style artifact.

In `@test/core/update.test.ts`:
- Around line 635-639: Wrap the UpdateCommand execution in the test around await
new UpdateCommand({ force: true }).execute(testDir) with a try/finally block,
and move both stdoutSpy.mockRestore() and stderrSpy.mockRestore() into finally
so the process stream spies are always restored when the command throws.
- Around line 699-704: Update the preservation assertion in the test around the
existing FileSystemUtils.fileExists check to read
legacyPrompts/openspec-explore.md and compare its contents with the expected
legacy repo-local prompt, rather than asserting existence alone. Keep the
existing .openspec-target content assertion unchanged.
🪄 Autofix

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: ae37773a-59b4-4b98-bf84-b8fdcf1fc522

📥 Commits

Reviewing files that changed from the base of the PR and between 2bf0eaf and f8ea958.

📒 Files selected for processing (7)
  • .changeset/fix-legacy-upgrade-agents-ownership.md
  • docs/supported-tools.md
  • src/core/legacy-cleanup.ts
  • src/core/update.ts
  • test/core/legacy-cleanup.test.ts
  • test/core/shared-skill-target.test.ts
  • test/core/update.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • test/core/shared-skill-target.test.ts
  • .changeset/fix-legacy-upgrade-agents-ownership.md

Comment thread src/core/update.ts
Comment on lines +896 to +900
await this.performImmediateLegacyCleanup(
projectPath,
detection,
legacyUpgrade.skippedSharedSkillTools
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Preserve deferred global prompts for skipped tools.

Both paths pass skippedSharedSkillTools to immediate cleanup only. They retain the unfiltered detection for deferred global cleanup. A skipped Codex upgrade can then remove matching global Codex prompts even though the existing .agents skills belong to agents and no Codex replacement was generated.

Pass the skipped tool IDs to deferred cleanup. Filter getLegacyGlobalPromptMatches(detection) before it selects removable prompts. Add coverage for a Codex upgrade skipped by an agents-owned shared root with legacy global prompts.

  • src/core/update.ts#L896-L900: retain skipped-tool ownership state with deferredGlobalCleanup.
  • src/core/update.ts#L933-L937: apply the same deferred-cleanup exclusion in the interactive path.
📍 Affects 1 file
  • src/core/update.ts#L896-L900 (this comment)
  • src/core/update.ts#L933-L937
🤖 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/core/update.ts` around lines 896 - 900, Preserve skipped-tool ownership
through deferred global cleanup: update the deferred cleanup calls in
src/core/update.ts lines 896-900 and 933-937 to pass skippedSharedSkillTools,
and update deferredGlobalCleanup/getLegacyGlobalPromptMatches so matching
removable prompts are excluded for skipped tool IDs. Add coverage for a skipped
Codex upgrade owned by an agents shared root with legacy global prompts; both
sites require the same change.

Comment thread test/core/legacy-cleanup.test.ts
Comment thread test/core/update.test.ts Outdated
Comment thread test/core/update.test.ts Outdated
alfred-openspec
alfred-openspec previously approved these changes Aug 6, 2026

@alfred-openspec alfred-openspec left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The follow-up covers the real update --force path and the inverse first-time Codex case, and skipped-tool cleanup now preserves legacy prompts when no replacement was written. Fresh install, build, lint, focused and full test suites, and CodeQL pass. Approved.

@clay-good

Copy link
Copy Markdown
Collaborator Author

Re-triggering CI/Security: their workflow dispatches were dropped during the GitHub Actions major outage on 2026-08-06, so they never ran for f8ea958. Reopening to re-fire the pull_request checks on the same commit (no code change).

@clay-good clay-good closed this Aug 7, 2026
@clay-good clay-good reopened this Aug 7, 2026
clay-good and others added 2 commits August 7, 2026 08:28
…rade

Address the outstanding CodeRabbit review notes on #1522. The fix itself
is confirmed correct by three independent adversarial reviews — these are
test-only hardening that locks in the guarantees the fix promises:

- Assert the global ~/.codex/prompts survives (byte-for-byte) in the
  hijack scenario. Previously the test set the prompt up but never
  checked it was preserved; on unfixed code Codex would be generated,
  its 'explore' workflow would read as installed, and the deferred
  global cleanup would delete the prompt — so this assertion fails
  without the fix.
- Assert the repo-local .codex/prompts is preserved by content, not
  mere existence (distinguishes 'left untouched' from 'deleted+rewritten').
- Restore the stdout/stderr spies in a finally so a throw can't swallow
  output for the rest of the suite.
- Cover backslash-delimited (Windows) paths in omitToolLegacyArtifacts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@clay-good

Copy link
Copy Markdown
Collaborator Author

Follow-up: test hardening for the remaining CodeRabbit notes (commit 3bc0cc1)

Ran three independent adversarial reviews (data-loss, correctness/edge, tests+docs) over the diff. Consensus: the fix is correct — no source change needed. CodeRabbit's data-loss hypothesis (deferred global cleanup deleting ~/.codex/prompts for a skipped Codex) was refuted and verified empirically against built dist: the ownership condition that triggers the skip is exactly what makes isSharedSkillTargetActive('codex') false, so scanInstalledWorkflows(['codex']) returns [], the global prompt lands in blockedMatches, and is preserved.

This push is test-only and locks in the guarantees the fix already delivers:

  • Global-prompt preservation now asserted in the hijack test (update.test.ts) — byte-for-byte. This is the gap CodeRabbit circled; it fails on unfixed code (Codex would be generated, its explore workflow would read as installed, and the deferred cleanup would delete the prompt), so it's a real regression lock, not a tautology.
  • Repo-local .codex/prompts preservation asserted by content, not mere existence.
  • stdout/stderr spies restored in finally so a throw can't swallow output for the rest of the suite.
  • Backslash (Windows) path coverage for omitToolLegacyArtifacts.

All checks green across linux/macOS/windows; CodeRabbit review completed. The 2 long-standing environment-only local failures (config-profile, artifact-workflow Cursor) are unrelated and pass on CI.

@clay-good clay-good closed this Aug 7, 2026
@clay-good clay-good reopened this Aug 7, 2026

@alfred-openspec alfred-openspec left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The follow-up is test-only hardening that locks in byte-for-byte global and repo-local prompt preservation, restores stream spies safely, and covers Windows-style paths. Fresh frozen install, build, lint, all 239 focused tests, and the exact-head hosted matrix pass. Approved.

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.

2 participants