Skip to content

Comments

feat: Finalize OCX v2 clean break#119

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

feat: Finalize OCX v2 clean break#119
kdcokenny wants to merge 118 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, v2 protocol, receipt-based installs (.ocx/receipt.jsonc v1), global-only layered profiles, a unified dry-run/JSON/exit-code contract, hardened path safety, and a Mintlify docs site at /docs. Adds ocx migrate for v1.4.6 → v2 with --global fan-out (root + all profiles), per-target summaries, and preview_with_errors/partial_failure statuses.

  • New Features

    • Registries & installs: require --name; alias/component refs; ephemeral --from; hash-based canonical IDs; receipt v1 at .ocx/receipt.jsonc.
    • Profiles & CLI: global-only layered profiles (objects deep-merge; arrays replace; plugins/instructions concat+dedupe); local AGENTS.md priority; minimal short-flag policy; standardized --dry-run JSON output; exit codes INTEGRITY=65, NOT_FOUND=66; removed diff.
    • Protocol & docs: registry protocol v2 with root-relative targets, normalized types/paths (skill/plugin/agent/tool/command/bundle/profile; plural dirs), and target path blocklist; profile add supports direct URL installs; Mintlify docs proxied at /docs with redirects and schemas under /schemas/*.
    • Migration: ocx migrate v1.4.6 → v2 with --global fan-out across root + profiles/*, per-target summaries, apply/preview modes, and partial_failure/preview_with_errors statuses.
  • Bug Fixes

    • Path safety: strict blocklist validation + realpath containment; restore local .opencode install paths; keep profile/global installs flattened; clearer integrity/path errors.
    • Resolution & output: profile install uses only registries declared in the profile; deterministic JSON-mode output (spinners off); correct NOT_FOUND (66) and INTEGRITY (65); await integrity checks in remove; improved error handling and path resolution.
    • CI: cache keys use bun.lock.

Written for commit 5e39452. 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 5e39452 Commit Preview URL

Branch Preview URL
Feb 24 2026, 12:41 PM

@codecov
Copy link

codecov bot commented Jan 31, 2026

Codecov Report

❌ Patch coverage is 33.02611% with 872 lines in your changes missing coverage. Please review.
✅ Project coverage is 54.70%. Comparing base (ed74da6) to head (5e39452).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
packages/cli/src/commands/add.ts 26.68% 250 Missing ⚠️
packages/cli/src/commands/profile/add.ts 5.64% 234 Missing ⚠️
packages/cli/src/commands/self/uninstall.ts 24.88% 169 Missing ⚠️
packages/cli/src/lib/build-registry.ts 0.00% 81 Missing ⚠️
.../cli/src/commands/profile/install-from-registry.ts 9.23% 59 Missing ⚠️
packages/cli/src/commands/init.ts 6.66% 28 Missing ⚠️
packages/cli/src/commands/self/update.ts 54.71% 24 Missing ⚠️
packages/cli/src/profile/atomic.ts 6.66% 14 Missing ⚠️
packages/cli/src/commands/opencode.ts 56.66% 13 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #119      +/-   ##
==========================================
+ Coverage   48.34%   54.70%   +6.36%     
==========================================
  Files          47       56       +9     
  Lines        4944     7451    +2507     
==========================================
+ Hits         2390     4076    +1686     
- Misses       2554     3375     +821     

☔ 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.

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

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 8 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/tests/registry/fetcher.test.ts">

<violation number="1" location="packages/cli/tests/registry/fetcher.test.ts:225">
P3: Test name contradicts its assertions; it says "returns null" but expects a non-null ancient-format result. Rename the test to reflect the actual behavior.</violation>
</file>

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

…ly in worker

- Enforce canonical versioned registry schema URLs in CLI checks.

- Emit diagnostics for legacy, invalid, and unsupported schema declarations.

- Serve bundled schemas in the worker, restore registry v1, and add versioned routes.

- Update templates, manifests, and tests for v2 schema URL behavior.
- enforce schema URL major-version validation in the CLI registry flow
- serve local registry schemas from worker-backed v1/v2 routes
- align docs/templates with required `$schema` guidance and versioned
  URLs
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