Skip to content

feat: add ownership transfer for packages and extend skill transfers with org support#1388

Open
TommYDeeee wants to merge 25 commits intoopenclaw:mainfrom
TommYDeeee:feat/ownership-transfer
Open

feat: add ownership transfer for packages and extend skill transfers with org support#1388
TommYDeeee wants to merge 25 commits intoopenclaw:mainfrom
TommYDeeee:feat/ownership-transfer

Conversation

@TommYDeeee
Copy link
Copy Markdown

@TommYDeeee TommYDeeee commented Mar 29, 2026

Summary

Adds the ability to transfer ownership of packages between users and organizations, and extends the existing skill transfer system with the same org support. This enables publishers to move packages/skills they own to another user or to an organization they admin — a flow that was previously impossible after initial publishing.

Motivation

Once a package was published under a personal account, there was no way to reassign it to an organization. Users who published packages under their personal handle and later wanted to move them under their org were stuck. This PR solves that by introducing a transfer system for packages (mirroring the existing skill transfer pattern) and extending skill transfers to support org targets.

What's new

Package transfers (new)

  • Full transfer lifecycle: request → accept/reject/cancel
  • 7-day auto-expiry on pending transfers
  • Audit logging for all transfer actions
  • Only one pending transfer per package at a time
  • Ownership re-validation at accept time (prevents stale transfers)

Org-targeted transfers (new for both skills and packages)

  • User → Org: Owner transfers their package/skill to an org they admin
  • Org → User: Org admin transfers an org-owned item to another user
  • Org → Org: Admin of source org transfers to a target org (recipient must be admin of target)
  • Permission checks: only admin or owner role members can request/accept org transfers

Extended skill transfers

  • Existing user→user skill transfers continue to work unchanged
  • Added org support using the same shared helpers as packages
  • Skill slug aliases are updated along with the skill on transfer (existing behavior preserved)

CLI updates

  • --type skill|package flag — explicit type selection, auto-detects when omitted
  • --publisher @org-handle flag — target an org on request, or assign to org on accept
  • Auto-detection: if both a skill and package share the same name, CLI asks user to specify --type
  • transfer list now shows both skill and package transfers with a TYPE column, sorted by date

API endpoints

Method Endpoint Description
POST /api/v1/packages/{name}/transfer Request transfer (body: toUserHandle, optional toPublisherHandle, message)
POST /api/v1/packages/{name}/transfer/accept Accept (optional body: publisherHandle to assign to org)
POST /api/v1/packages/{name}/transfer/reject Reject pending transfer
POST /api/v1/packages/{name}/transfer/cancel Cancel outgoing transfer
GET /api/v1/transfers/incoming List incoming transfers (now includes both skills + packages)
GET /api/v1/transfers/outgoing List outgoing transfers (now includes both skills + packages)

Existing skill transfer endpoints unchanged — toPublisherHandle and publisherHandle are optional additions.

Documentation updates

  • docs/http-api.md — added package transfer endpoints, org-targeted transfer params (toPublisherHandle, publisherHandle), updated transfer listing response shape with type discriminator
  • docs/cli.md — updated transfer command reference with --type, --publisher flags and package support
  • docs/api.md — added package transfer endpoints to the API endpoint listing
  • docs/orgs.md — updated transfer model section to reflect implemented publisher-based flows

Architecture

  • Parallel tables: packageOwnershipTransfers mirrors skillOwnershipTransfers — same shape, same indexes, separate tables. Lower risk than a unified table, no refactoring of working skill code.
  • Shared helpers: convex/lib/transfers.ts extracts common logic (expiry, handle normalization, ownership validation, accept permission checks) used by both systems.
  • Schema changes: Added fromPublisherId/toPublisherId optional fields to skillOwnershipTransfers. New packageOwnershipTransfers table with same structure.

Files changed

File Change
convex/lib/transfers.ts New — shared helpers (expiry, validation, permissions)
convex/lib/transfers.test.ts New — 18 tests for shared helpers
convex/packageTransfers.ts New — package transfer mutations and queries
convex/packageTransfers.test.ts New — 5 tests for package transfers
convex/schema.ts New packageOwnershipTransfers table, extended skillOwnershipTransfers
convex/skillTransfers.ts Refactored to use shared helpers, added org support
convex/skillTransfers.test.ts Added org admin transfer test
convex/httpApiV1/packagesV1.ts New POST router with transfer handlers
convex/httpApiV1/skillsV1.ts Added publisher handle resolution for org transfers
convex/httpApiV1/transfersV1.ts Unified listing: merges skill + package transfers, sorted
convex/httpApiV1.ts Registered new package POST router
convex/http.ts Registered POST route for /api/v1/packages/ prefix
packages/clawdhub/src/cli/commands/transfer.ts Added --type, --publisher, auto-detection, package support
packages/clawdhub/src/cli.ts Updated command descriptions and options
packages/clawdhub/src/schema/schemas.ts Extended response schemas for package transfers
convex/httpApiV1.handlers.test.ts Updated transfer listing test for dual-source query
docs/http-api.md Updated transfer ownership endpoint docs
docs/cli.md Updated transfer CLI command reference
docs/api.md Added package transfer endpoints to listing
docs/orgs.md Updated transfer model section

Linked issue

This would close #1431

Test plan

  • 122 tests pass (28 transfer-specific + 94 HTTP handler tests)
  • TypeScript compiles with zero errors
  • All 18 scenarios verified end-to-end:
    • User→User package and skill transfers
    • User→Org, Org→User, Org→Org transfers (both packages and skills)
    • Accept with --publisher flag to assign to org
    • Reject and cancel flows
    • 7-day expiry handling
    • Ownership-changed-after-request detection
    • Non-owner / non-admin permission denied
    • Transfer-to-self rejected
    • Duplicate pending transfer rejected
    • Auto-detection and --type fallback
    • CLI field names match HTTP handler expectations
    • Backwards compatibility with existing skill transfers

…ansfers for org support

- Make toUserId optional in skillOwnershipTransfers (for org-targeted transfers)
- Add fromPublisherId and toPublisherId optional fields to skillOwnershipTransfers
- Add new packageOwnershipTransfers table with matching shape
- Register packageOwnershipTransfers in the schema export
Add convex/lib/transfers.ts with reusable helpers for both skill and
package transfer systems: expiry check, handle normalization, and
ownership/accept permission validation with org support.
Use shared helpers from convex/lib/transfers.ts (validateTransferOwnership,
validateTransferAcceptPermission, isTransferExpired, normalizeTransferHandle)
instead of inline functions. Add toPublisherId/publisherId args for org-targeted
transfers, and type: "skill" to listing query results.
Implements convex/packageTransfers.ts mirroring the skill transfer
system, with org-targeted transfer support via fromPublisherId/toPublisherId
fields and shared validation helpers from convex/lib/transfers.ts.
Add auto-detection logic that probes both skill and package APIs when
--type is not specified. Update transfer request, accept, reject, cancel,
and list commands with --type and --publisher options. Update response
schemas to handle package transfers alongside skill transfers.
Add POST routes for package ownership transfers (request, accept, reject,
cancel) mirroring the existing skill transfer pattern. Update the transfers
listing endpoint to query both skill and package transfers in parallel.
…errors

- Add validateTransferAcceptPermission to package accept/reject handlers
- Fix recipient check in validatePendingTransferForActor for org transfers
- Make toUserId optional in validateTransferAcceptPermission signature
- Remove unused imports in test file
The transfer listing now queries both skill and package transfers,
so the mock needs to return data for the first call (skills) and
an empty array for the second (packages).
- Forward toPublisherId in HTTP handlers by resolving publisher handle
  to ID via getByHandleInternal before calling transfer mutations
- Forward publisherId on transfer accept in both package and skill
  HTTP handlers
- Fix validatePendingTransferForActor in skillTransfers to allow
  org-targeted transfers where toUserId is undefined
- Remove unsafe `as Id<"users">` cast on transfer.toUserId in
  acceptTransferInternal and rejectTransferInternal
- Fix listOutgoingInternal for skills to include org-targeted transfers
  instead of skipping them with `if (!transfer.toUserId) continue`
- Sort unified transfer listing by requestedAt descending
CLI was sending toPublisherId (handle string) but HTTP handlers read
toPublisherHandle and publisherHandle. Fixed CLI to match.
Org-targeted skill transfers were setting toUserId to undefined,
making them invisible in incoming transfer lists. Now always set
toUserId to the resolved recipient, matching package transfer behavior.
@vercel
Copy link
Copy Markdown
Contributor

vercel bot commented Mar 29, 2026

@TommYDeeee is attempting to deploy a commit to the 0xBuns Team on Vercel.

A member of the Team first needs to authorize it.

- http-api.md: add package transfer endpoints, org-targeted transfer
  params (toPublisherHandle, publisherHandle), updated listing response
- cli.md: update transfer commands with --type, --publisher flags
- api.md: add package transfer endpoint listing
- orgs.md: mark transfer model as implemented
- CHANGELOG.md: add unreleased entry for transfer features
…port

- http-api.md: add package transfer endpoints, org-targeted transfer
  params (toPublisherHandle, publisherHandle), updated listing response
- cli.md: update transfer commands with --type, --publisher flags
- api.md: add package transfer endpoint listing
- orgs.md: mark transfer model as implemented
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 29, 2026

Greptile Summary

This PR adds a complete package ownership transfer system (mirroring the existing skill transfer pattern) and extends skill transfers with org support — allowing users, org admins, and orgs to transfer packages and skills between each other. The architecture is sound: shared helpers in convex/lib/transfers.ts, parallel tables, backward-compatible schema changes, a unified listing endpoint, and an updated CLI with auto-detection and --type/--publisher flags.

Critical issue found:

  • Stale-ownership check breaks org-initiated transfers (convex/packageTransfers.ts and convex/skillTransfers.ts): transfer.fromUserId is set to the requesting actor (e.g. an org admin with user ID users:2), but the stale check compares pkg.ownerUserId (the package's linked user, e.g. users:1) against it. For every org-owned item where the admin ≠ the linked owner user, this comparison is always true, causing the transfer to be cancelled as "no longer valid" the moment the recipient tries to accept. The fix is to gate on fromPublisherId: if it is set, compare publisher IDs instead of user IDs.

Everything else looks good:

  • Shared helper tests cover expiry, handle normalisation, ownership validation, and accept-permission logic thoroughly.
  • HTTP routing segment logic in packagesPostRouterV1Handler is correct.
  • The unified transfer listing (transfersV1.ts) uses Promise.all correctly and sorts by requestedAt.
  • CLI auto-detection, --publisher wiring, and the updated table output are all correct.

Confidence Score: 4/5

Not safe to merge as-is: the stale-ownership check will silently cancel all org-initiated transfers at accept time, making the org transfer feature completely non-functional.

One P1 defect affects both new files (packageTransfers.ts and skillTransfers.ts): the stale-ownership comparison uses fromUserId (the requesting actor/admin) against ownerUserId (the package/skill's linked user), which differ for every org-owned item. This breaks the primary new feature — org transfers — end-to-end. All other logic (routing, schema, helpers, CLI, listing) looks correct and well-tested. A targeted two-line fix resolves the issue.

convex/packageTransfers.ts (acceptTransferInternal stale check) and convex/skillTransfers.ts (same stale check, carried over from pre-org code)

Important Files Changed

Filename Overview
convex/packageTransfers.ts New file implementing the full package transfer lifecycle. Well-structured and mirrors the skill transfer pattern, but acceptTransferInternal has a critical stale-ownership check bug that will break all org-initiated transfers.
convex/skillTransfers.ts Refactored to use shared transfer helpers and extended with org support. The stale-ownership check in acceptTransferInternal carries over the same bug — org-admin-initiated transfers will always be incorrectly cancelled at accept time.
convex/lib/transfers.ts New shared helper library extracting expiry, handle normalisation, and permission validation logic. Clean and well-tested.
convex/schema.ts Adds packageOwnershipTransfers table mirroring skillOwnershipTransfers, and makes toUserId optional on both to support org targets. Indexes look correct.
convex/httpApiV1/packagesV1.ts New POST router for package transfer endpoints. Segment routing logic is correct. Error mapping and auth flows are consistent with the existing skill transfer handlers.
convex/httpApiV1/transfersV1.ts Unified listing merges skill and package transfers with Promise.all and sorts by requestedAt. Clean and correct.
packages/clawdhub/src/cli/commands/transfer.ts CLI extended with --type and --publisher flags and auto-detection via parallel API probes. Logic is sound; the updated transfer list display adds a TYPE column correctly.

Comments Outside Diff (1)

  1. convex/packageTransfers.ts, line 1418-1424 (link)

    P1 Stale-ownership check breaks org-initiated transfers

    transfer.fromUserId is set to args.actorUserId in requestTransferInternal — i.e. the admin who initiated the transfer. For org-owned packages, pkg.ownerUserId is the linked user of the org (not the admin), so pkg.ownerUserId !== transfer.fromUserId is almost always true for org-initiated transfers, even when ownership hasn't changed. Every org-initiated package transfer will be incorrectly cancelled with "Transfer is no longer valid" at accept time.

    The check needs to discriminate between personal and org ownership. When transfer.fromPublisherId is set the transfer was from an org — compare publisher IDs instead:

    const ownerChanged = transfer.fromPublisherId
      ? pkg.ownerPublisherId !== transfer.fromPublisherId
      : pkg.ownerUserId !== transfer.fromUserId;
    if (ownerChanged) {
      await ctx.db.patch(transfer._id, { status: "cancelled", respondedAt: now });
      throw new Error("Transfer is no longer valid");
    }

    The same bug exists in convex/skillTransfers.ts in the acceptTransferInternal handler — that stale check was also written before org support existed and has the same issue.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: convex/packageTransfers.ts
    Line: 1418-1424
    
    Comment:
    **Stale-ownership check breaks org-initiated transfers**
    
    `transfer.fromUserId` is set to `args.actorUserId` in `requestTransferInternal` — i.e. the *admin* who initiated the transfer. For org-owned packages, `pkg.ownerUserId` is the linked user of the org (not the admin), so `pkg.ownerUserId !== transfer.fromUserId` is almost always `true` for org-initiated transfers, even when ownership hasn't changed. Every org-initiated package transfer will be incorrectly cancelled with "Transfer is no longer valid" at accept time.
    
    The check needs to discriminate between personal and org ownership. When `transfer.fromPublisherId` is set the transfer was from an org — compare publisher IDs instead:
    
    ```typescript
    const ownerChanged = transfer.fromPublisherId
      ? pkg.ownerPublisherId !== transfer.fromPublisherId
      : pkg.ownerUserId !== transfer.fromUserId;
    if (ownerChanged) {
      await ctx.db.patch(transfer._id, { status: "cancelled", respondedAt: now });
      throw new Error("Transfer is no longer valid");
    }
    ```
    
    The same bug exists in `convex/skillTransfers.ts` in the `acceptTransferInternal` handler — that stale check was also written before org support existed and has the same issue.
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: convex/packageTransfers.ts
Line: 1418-1424

Comment:
**Stale-ownership check breaks org-initiated transfers**

`transfer.fromUserId` is set to `args.actorUserId` in `requestTransferInternal` — i.e. the *admin* who initiated the transfer. For org-owned packages, `pkg.ownerUserId` is the linked user of the org (not the admin), so `pkg.ownerUserId !== transfer.fromUserId` is almost always `true` for org-initiated transfers, even when ownership hasn't changed. Every org-initiated package transfer will be incorrectly cancelled with "Transfer is no longer valid" at accept time.

The check needs to discriminate between personal and org ownership. When `transfer.fromPublisherId` is set the transfer was from an org — compare publisher IDs instead:

```typescript
const ownerChanged = transfer.fromPublisherId
  ? pkg.ownerPublisherId !== transfer.fromPublisherId
  : pkg.ownerUserId !== transfer.fromUserId;
if (ownerChanged) {
  await ctx.db.patch(transfer._id, { status: "cancelled", respondedAt: now });
  throw new Error("Transfer is no longer valid");
}
```

The same bug exists in `convex/skillTransfers.ts` in the `acceptTransferInternal` handler — that stale check was also written before org support existed and has the same issue.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: convex/skillTransfers.ts
Line: 188-191

Comment:
**Same stale-ownership bug as in `packageTransfers.ts`**

`transfer.fromUserId` stores the requesting actor (which is the org admin for org-owned skills), not `skill.ownerUserId`. The comparison `skill.ownerUserId !== transfer.fromUserId` is therefore always `true` when an org admin initiates the transfer, causing every org-initiated skill transfer to be cancelled as stale at accept time.

Apply the same fix described in `convex/packageTransfers.ts`:

```typescript
const ownerChanged = transfer.fromPublisherId
  ? skill.ownerPublisherId !== transfer.fromPublisherId
  : skill.ownerUserId !== transfer.fromUserId;
if (ownerChanged) {
  await ctx.db.patch(transfer._id, { status: "cancelled", respondedAt: now });
  throw new Error("Transfer is no longer valid");
}
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix: always set toUserId in skill transf..." | Re-trigger Greptile

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a14c3cfd6c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Remove strikethrough and implementation notes, write as production
documentation describing current transfer capabilities.
…alidation

- Use fromPublisherId for ownership-changed detection on org transfers.
  Previously compared ownerUserId against fromUserId (the requesting
  actor), which always mismatched when an org admin initiated the
  transfer, incorrectly cancelling valid org transfers at accept time.

- Validate actor has admin/owner role on explicit publisherId override
  in accept flow. Previously any user could assign ownership to an
  arbitrary publisher by passing publisherId on accept.
HTTP handlers previously looked up pending transfers by toUserId only,
blocking org admins (other than the original toUserId) from accepting
or rejecting. Now falls back to an unfiltered pending transfer lookup
for the item, letting the mutation's validateTransferAcceptPermission
handle authorization.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7d19162956

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

…-detection error

- Skip "Cannot transfer to yourself" check when toPublisherId is set,
  enabling single-admin org owners to transfer items to their own org.
- Improve auto-detection error message to suggest --type flag for
  private items where detail endpoints return 404.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ed2f665560

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

The recipient gate in validatePendingTransferForActor blocked org
admins whose userId didn't match toUserId, even when toPublisherId
indicated an org-targeted transfer. Now skips the toUserId check
when toPublisherId is set, deferring authorization to
validateTransferAcceptPermission which checks org membership.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4022b0bc7e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

…ccept body

- validateTransferAcceptPermission now checks isPublisherActive before
  allowing org-targeted acceptance, preventing ownership assignment to
  deactivated/deleted publishers.
- Accept HTTP handlers now return a parse error when a JSON body is
  sent but malformed, instead of silently falling through to personal
  publisher.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3d5939fd96

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Cancel was gated on fromUserId === actorUserId, blocking other org
admins from cancelling transfers initiated by a different admin.
Now skips the direct sender check when fromPublisherId is set and
validates org membership via validateTransferOwnership instead.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 27dfd873bf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Cancel HTTP handlers only looked up transfers by fromUserId, blocking
other org admins. Now falls back to unfiltered pending transfer lookup,
matching the accept/reject pattern. Authorization is enforced by
validateTransferOwnership in the mutation layer.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 19e7951eb3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

The packages detail endpoint falls back to returning skills when no
package exists, causing auto-detection to incorrectly report both
a skill and package for skill-only items. Now parses the response
body to confirm a package field is present before treating it as
a package match.
@pushmatrix
Copy link
Copy Markdown

This would solve #1431

Resolve conflicts in http.ts and httpApiV1.ts. Merge the new
trusted-publisher and packages DELETE routes from main with our
transfer POST routes into a unified packagesPostRouterV1Handler.
Remove duplicate runMutationRef and getPackageByNameInternal entries.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 517778d634

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

apiRequest already calls JSON.stringify on the body. Passing a
pre-stringified body caused double-encoding, so the server never
saw the publisherHandle/toPublisherHandle fields and silently fell
back to the default publisher.
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.

Cannot change an existing skill's publisher/owner to an organization

2 participants