Skip to content

feat: Finalize OCX v2 clean break#119

Open
kdcokenny wants to merge 101 commits intomainfrom
v2-clean-break
Open

feat: Finalize OCX v2 clean break#119
kdcokenny wants to merge 101 commits intomainfrom
v2-clean-break

Conversation

@kdcokenny
Copy link
Owner

@kdcokenny kdcokenny commented Jan 31, 2026

Summary

This PR finalizes the OCX v2 architecture with several breaking improvements:

  • V2 registry protocol: Root-relative component targets (no more /src prefix guessing)
  • Receipt-based install tracking: Replaced ocx.lock with .opencode/receipts/ for granular component metadata
  • Profile layering improvements: Local-first writes with proper config isolation
  • Ghost CLI removal: Removed deprecated ghost migration tooling
  • New commands: Added remove and verify commands for component management

Test Results

All tests passing:

  • 734 tests pass
  • ⏭️ 11 tests skipped
  • 0 tests failed
  • Lint check passed (Biome)
  • Type check passed (TypeScript strict mode)

Breaking Changes

  • Registry manifests must now use v2 format with targets array containing root-relative paths
  • ocx.lock is replaced by .opencode/receipts/*.json for installed components
  • Ghost CLI commands removed (ocx ghost migrate)

Documentation

  • Added V2_RECEIPT_IMPLEMENTATION.md with full receipt system design
  • Updated all docs to reflect v2 changes
  • Added receipt JSON schema

Summary by cubic

Finalizes OCX v2 with alias‑first registries, receipt‑based installs (.ocx/receipt.jsonc v1), global‑only layered profiles, a unified dry‑run/JSON/exit‑code contract, hardened path validation, and a Mintlify docs site at /docs. Adds ocx migrate for v1.4.6 → v2 with --global fan‑out across root and all profiles, including preview_with_errors and partial_failure statuses.

  • New Features

    • Registries & installs: require --name; reference as alias/component; ephemeral installs via --from; hash‑based canonical IDs; receipt v1 at .ocx/receipt.jsonc.
    • Protocol & CLI: root‑relative targets; normalized types (skill/plugin/agent/tool/command/bundle/profile); plural component dirs; minimal short‑flag policy; removed diff; removed registry version pinning.
    • Profiles & ops: global‑only commands with layered merge (objects deep‑merge; arrays replace; plugins/instructions concat+dedupe); instruction discovery is “first type wins” with local AGENTS.md priority; standardized --dry-run JSON and exit codes (INTEGRITY=65, NOT_FOUND=66); ocx migrate provides per‑target summaries; docs served via Mintlify.
  • Bug Fixes

    • Path safety: blocklist‑based target validation with strict containment; restore local .opencode/ installs while flattening profile/global installs.
    • Resolution & output: flexible aliasing and URL installs; profile add uses registries declared in the profile; correct NOT_FOUND (66); stricter JSON contracts with clean JSON‑mode output (spinners off); clearer path/integrity errors.
    • CI/docs/schemas: bun cache keys use bun.lock; schema/endpoint normalization; docs aligned to v2 types/paths and short‑flag policy.

Written for commit 7e8acb6. Summary will update on new commits.

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Jan 31, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
kdco-registry 7e8acb6 Commit Preview URL

Branch Preview URL
Feb 15 2026, 08:09 PM

@codecov
Copy link

codecov bot commented Jan 31, 2026

Codecov Report

❌ Patch coverage is 12.61950% with 914 lines in your changes missing coverage. Please review.
✅ Project coverage is 48.86%. Comparing base (ed74da6) to head (7e8acb6).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
packages/cli/src/commands/add.ts 2.20% 400 Missing ⚠️
packages/cli/src/commands/profile/add.ts 5.64% 234 Missing ⚠️
packages/cli/src/commands/self/uninstall.ts 24.88% 169 Missing ⚠️
.../cli/src/commands/profile/install-from-registry.ts 9.52% 57 Missing ⚠️
packages/cli/src/commands/self/update.ts 54.71% 24 Missing ⚠️
packages/cli/src/commands/init.ts 5.55% 17 Missing ⚠️
packages/cli/src/commands/opencode.ts 56.66% 13 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #119      +/-   ##
==========================================
+ Coverage   48.34%   48.86%   +0.52%     
==========================================
  Files          47       55       +8     
  Lines        4944     7204    +2260     
==========================================
+ Hits         2390     3520    +1130     
- Misses       2554     3684    +1130     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@kdcokenny kdcokenny changed the title feat: finalize OCX v2 clean break feat: Finalize OCX v2 clean break Jan 31, 2026
- Add getLayered() method to ProfileManager for merging global and local profiles
- Update ConfigResolver to use layered profile loading
- Implement local profile AGENTS.md discovery (highest priority)
- Add 19 unit tests for profile layering behavior

Merge behavior (matching OpenCode):
- Objects (registries): deep merge, local adds to global
- Arrays (exclude/include): local replaces global
- Special arrays (plugin/instructions): concatenate + dedupe
- Scalars: local wins

Documentation updates:
- Clarify --global flag usage in all profile examples
- Add 'Local vs Global Profiles' section to PROFILES.md
- Update configuration merging documentation
- Add testing philosophy section to MANUAL_TESTING.md
- Add profile layering test cases
- Remove @Version parsing from update command (never worked)
- Remove dead version field from registry config schema
- Remove version pinning sections from all documentation
- Fix component names to use real registry components:
  - kdco/agents → kdco/researcher
  - kdco/skills → kdco/code-philosophy
  - kdco/plugins → kdco/notify
- Profile install now uses only registries declared in profile's ocx.jsonc
- Add -g/--global to profile remove/move (local is default)
- Remove --force from profile add docs (use remove + add)
- Remove "marks active profile" claim from profile list docs
- Use full profile registries for transitive deps
- Only check last-profile guard for global removals
- Remove unused registries variable in profile add
- Update docs to match CLI output ("Global profiles:")
- Accept both ocx:bundle/ocx:profile and bundle/profile types
- Support direct URL profile installation (--from http://...)
- build: use path.resolve() for absolute path handling
- resolver: registry not found returns NOT_FOUND (66) instead of CONFIG (78)
- update: specific error when component not installed with empty receipt
- docs: correct test 17.7 exit code expectation (1, not 78)
Update all documentation to use correct receipt path:
- Old: .opencode/ocx-receipt.json
- New: .ocx/receipt.jsonc
- Remove --force from init calls (not a valid option)
- Add --global to profile move/remove tests (tests use global profiles)

Fixes ~50 test failures caused by tests using invalid/missing flags.
The init command does not accept a --force flag. These test calls
were causing unnecessary errors in the test output.

Changes:
- Remove --force from all init command calls in tests
- Tests now use correct init syntax without flags
OPENCODE_CONFIG_DIR always points to global config directory.
Profile config is passed via OPENCODE_CONFIG_CONTENT.
- Pass cwd to ProfileManager.create() in ConfigResolver
- Add kdco registry to test-profile-with-deps mock
@kdcokenny kdcokenny marked this pull request as ready for review February 4, 2026 00:00
Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

17 issues found across 136 files

Note: This PR contains a large number of files. cubic only reviews up to 75 files per PR, so some files may not have been reviewed.

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="packages/cli/src/commands/verify.ts">

<violation number="1" location="packages/cli/src/commands/verify.ts:56">
P2: Spinner output corrupts JSON when `--json` flag is used. The spinner should be disabled when `options.json` is true to ensure clean, parseable JSON output.</violation>
</file>

<file name="packages/cli/src/commands/add.ts.bak5">

<violation number="1" location="packages/cli/src/commands/add.ts.bak5:911">
P1: Regular npm dependencies are incorrectly placed in `devDependencies`. The function combines both `npmDeps` and `npmDevDeps` into one array, then `mergeDevDependencies` writes all of them to `devDependencies`. Regular dependencies should go to `dependencies` field instead.</violation>
</file>

<file name="packages/cli/src/utils/instruction-paths.ts">

<violation number="1" location="packages/cli/src/utils/instruction-paths.ts:147">
P2: Variable `path` shadows the imported `path` module from `node:path`. Rename the loop variable to avoid confusion and potential bugs when maintaining this code.</violation>
</file>

<file name="docs/OPENCODE_REFERENCE.md">

<violation number="1" location="docs/OPENCODE_REFERENCE.md:1046">
P3: Instruction discovery is described as a project-tree walk, but the implementation only walks up from the working directory to the git root. This doc line should reflect the upward search to avoid misleading users about which instruction files are discovered.</violation>
</file>

<file name="packages/cli/src/commands/profile/add.ts">

<violation number="1" location="packages/cli/src/commands/profile/add.ts:311">
P2: Catch-all error handler masks the real error. `manager.get()` can throw `OcxConfigError` or schema validation errors, not just `ProfileNotFoundError`. Catching and re-throwing as `ProfileNotFoundError` loses critical diagnostic information and misleads users.</violation>
</file>

<file name="docs/REGISTRY_PROTOCOL.md">

<violation number="1" location="docs/REGISTRY_PROTOCOL.md:135">
P2: The install locations for plugin/agent/command/tool are listed as singular directories, but the CLI only accepts plural prefixes (plugins/, agents/, commands/, tools/). Update the table to use the plural paths so registry authors generate valid targets.</violation>
</file>

<file name="packages/cli/src/commands/diff.ts">

<violation number="1" location="packages/cli/src/commands/diff.ts:127">
P2: The `fileStatus` variable is computed from `integrity.details` but its `status` property is never used. This appears to be incomplete implementation - the integrity check result is fetched but ignored. The subsequent code duplicates file existence checks. Either use `fileStatus.status` to optimize the logic (e.g., skip comparison for 'intact' files, handle 'missing' directly) or remove the unused integrity lookup.</violation>
</file>

<file name="packages/cli/src/schemas/registry.ts">

<violation number="1" location="packages/cli/src/schemas/registry.ts:193">
P2: The prefix validation logic incorrectly allows files like `AGENTS.md.backup` or `ocx.jsonc.old` to pass validation because `startsWith` is used for exact file name matches. For non-directory prefixes (no trailing `/`), only exact matches should be allowed.</violation>
</file>

<file name="packages/cli/src/commands/remove.ts">

<violation number="1" location="packages/cli/src/commands/remove.ts:80">
P2: Error message only shows modified files but not missing files. When `integrity.intact` is false due to missing files (but no modifications), the error says "has been modified" with an empty list, which is misleading. Include `integrity.missing` in the error message.</violation>

<violation number="2" location="packages/cli/src/commands/remove.ts:100">
P1: Success message shown before receipt is persisted. If `writeReceipt()` fails after `spin?.succeed()`, the user sees success but files are deleted without updating the receipt. Move the success message after the write operation completes.</violation>
</file>

<file name="packages/cli/src/profile/manager.ts">

<violation number="1" location="packages/cli/src/profile/manager.ts:208">
P2: Inconsistent `cwd` usage: `exists(name, false)` uses `this.cwd` from constructor, but `loadFromLocal(name, cwd)` uses the `cwd` parameter. If these differ, the existence check and load will target different directories. Consider removing the `cwd` parameter and using `this.cwd` consistently, or passing `cwd` to the existence check.</violation>
</file>

<file name="docs/MANUAL_TESTING.md">

<violation number="1" location="docs/MANUAL_TESTING.md:71">
P2: Hard-coded absolute paths make the manual test steps non-portable. Use a placeholder (e.g., `$OCX_REPO` or `~/workspace/ocx`) so other developers can follow the guide without editing every command.</violation>

<violation number="2" location="docs/MANUAL_TESTING.md:698">
P3: The update test headings (7.6/7.7) are misplaced inside the diff section, which breaks numbering and mixes unrelated commands. These update tests should live under Section 7 (ocx update), leaving Section 8 exclusively for diff cases.</violation>
</file>

<file name="packages/cli/src/commands/registry.ts">

<violation number="1" location="packages/cli/src/commands/registry.ts:100">
P2: Incorrect string escaping: `\\n` produces literal `\n` characters instead of a newline. Use `\n` (single backslash) for an actual line break in the error message.</violation>
</file>

<file name="packages/cli/src/commands/profile/install-from-registry.ts">

<violation number="1" location="packages/cli/src/commands/profile/install-from-registry.ts:314">
P2: The `typeof regsRaw === "object"` check doesn't exclude arrays since `typeof [] === "object"` in JavaScript. This could allow malformed config (e.g., `"registries": []`) to pass validation and cause runtime errors downstream.</violation>
</file>

<file name="docs/CREATING_REGISTRIES.md">

<violation number="1" location="docs/CREATING_REGISTRIES.md:134">
P2: Inconsistent directory name: uses `plugins/` here but the Component Types table shows `plugin/` (singular). These should match to avoid confusion.</violation>

<violation number="2" location="docs/CREATING_REGISTRIES.md:360">
P2: Duplicate `## Dependencies` section header. The first one should likely be renamed to something like `## Instruction Files` or the instruction content should be moved under the existing Dependencies section with appropriate subsection headers.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

7 issues found across 60 files (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="docs/integrations/workspace.mdx">

<violation number="1" location="docs/integrations/workspace.mdx:41">
P3: The specialist agent is labeled `review` in the diagram, but the rest of the doc (agent list) calls it `reviewer`. This inconsistency makes it unclear which agent name is correct. Rename the diagram label to `reviewer` to match the rest of the doc.</violation>
</file>

<file name="docs/cli/add.mdx">

<violation number="1" location="docs/cli/add.mdx:79">
P3: Label `.ocx/receipt.jsonc` as the V1 receipt file to match the repository convention.

(Based on your team's feedback about labeling `.ocx/receipt.jsonc` as V1.) [FEEDBACK_USED]</violation>
</file>

<file name="docs/cli/search.mdx">

<violation number="1" location="docs/cli/search.mdx:81">
P2: The error cause still references the deprecated lock file. Update it to reflect the receipt-based install tracking so the docs match the v2 behavior.</violation>
</file>

<file name="docs/maintainers/migration-v1-4-0.mdx">

<violation number="1" location="docs/maintainers/migration-v1-4-0.mdx:28">
P2: The migration guide instructs running `ocx ghost migrate`, but ghost CLI commands were removed in this clean-break release. This command will fail and mislead users; update the guide to remove the automatic migration step or replace it with the supported flow.</violation>
</file>

<file name="docs/registries/create.mdx">

<violation number="1" location="docs/registries/create.mdx:304">
P3: The conflict example uses `agent/researcher.md`, but the documented directory for agents is `agents/`. This mismatch can mislead users about the correct path.</violation>
</file>

<file name="docs/enterprise/overview.mdx">

<violation number="1" location="docs/enterprise/overview.mdx:31">
P3: Label `.ocx/receipt.jsonc` as the V1 receipt to match the required naming convention.

(Based on your team's feedback about labeling .ocx/receipt.jsonc as V1.) [FEEDBACK_USED]</violation>
</file>

<file name="docs/reference/agents.mdx">

<violation number="1" location="docs/reference/agents.mdx:54">
P2: The agent config directory is documented as `.opencode/agent/`, but other docs/tests use `.opencode/agents/`. This inconsistency will send users to the wrong folder. Update to the plural form.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

2 issues found across 38 files (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="docs/PROFILES.md">

<violation number="1" location="docs/PROFILES.md:199">
P3: The alias commands in this table omit `--global`, but the doc now says `--global` is required for all profile commands. Update the alias column so it doesn’t contradict the requirement.</violation>
</file>

<file name="docs/MANUAL_TESTING.md">

<violation number="1" location="docs/MANUAL_TESTING.md:1757">
P2: The new global-only section mixes two behaviors (requiring `--global` vs defaulting to global). This contradicts the subsequent tests that expect commands to fail without `--global`, so the docs should state one clear behavior.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

4 issues found across 7 files (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="docs/CLI.md">

<violation number="1" location="docs/CLI.md:651">
P2: Label the `.ocx/receipt.jsonc` receipt format as V1, not V2, to match repository conventions.

(Based on your team's feedback about labeling .ocx/receipt.jsonc as V1.) [FEEDBACK_USED]</violation>
</file>

<file name="docs/MANUAL_TESTING.md">

<violation number="1" location="docs/MANUAL_TESTING.md:1970">
P3: Update the receipt naming to V1 when describing the .ocx/receipt.jsonc migration; calling it V2 conflicts with the documented convention.

(Based on your team's feedback about labeling .ocx/receipt.jsonc as V1.) [FEEDBACK_USED]</violation>

<violation number="2" location="docs/MANUAL_TESTING.md:2001">
P3: Adjust the expected message to refer to the V1 receipt format to match the .ocx/receipt.jsonc naming convention.

(Based on your team's feedback about labeling .ocx/receipt.jsonc as V1.) [FEEDBACK_USED]</violation>
</file>

<file name="README.md">

<violation number="1" location="README.md:98">
P3: The README now calls the receipt target “v2,” but other docs label `.ocx/receipt.jsonc` as the V1 receipt file. Align the README wording with the existing V1 terminology (or omit the version) to avoid confusing users about which receipt format they’ll end up with.

(Based on your team's feedback about labeling .ocx/receipt.jsonc as V1.) [FEEDBACK_USED]</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

When --global is used, ocx migrate now discovers and processes the
global root plus every directory under profiles/*, sorted alphabetically.
Each target is analyzed/applied independently with per-target summaries.
Apply mode continues after individual target failures and exits non-zero
with a partial_failure status when any target errors.
Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 6 files (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/cli/src/commands/migrate/index.ts">

<violation number="1" location="packages/cli/src/commands/migrate/index.ts:97">
P2: Preview mode for global migration lacks per-target error handling, unlike apply mode. If any target throws unexpectedly (e.g., permission error), the entire preview crashes rather than continuing and reporting the failure per-target. Consider wrapping the `analyzeTarget` call in a try-catch consistent with the apply path.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@kdcokenny
Copy link
Owner Author

r2m

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