# Exomem Cloud plugin operations Claude and OpenAI packages use the same hand-authored skill and workflow files under `src/exomem/_scaffold/_Schema/`. `plugins/cloud/definition.json` contains public identity and listing URLs. The existing Cloud MCP/OAuth connection serves the `product-cloud` surface. Self-hosted Claude Code remains the separate `plugins/claude-code` package; historical Hosted Alpha packages are not current Cloud distributions. ## Build and check From a checkout with the pinned project uv available: ```bash uv run --frozen python scripts/cloud-plugin.py build uv run --frozen python scripts/cloud-plugin.py check uv run --frozen python -m pytest -q tests/test_cloud_plugins.py tests/test_cloud_plugin_evals.py tests/test_package_skills.py uv run --frozen python scripts/validate-public-artifacts.py --repository ``` Output is `plugins/cloud/generated/`: Claude's fixed HTTP `.mcp.json` and `.claude-plugin/plugin.json`, OpenAI's portable Agent Plugins 1.0 `plugin.json` and `mcp.json`, complete canonical skills, public assets, one deterministic ZIP per edition, and `release.json`. Check regenerates into temporary storage and rejects drift, missing/extra files and symlinks. A valid bundle is not proof of native behaviour. Claude has two editions. The directory edition, `generated/claude`, contains skills, the remote MCP connection, the icon, README and LICENSE. The Claude directory refuses command hooks and non-text files from publishers that are not Verified organizations, so the build fails if a hook or another file type enters this edition. Its README names the install command for the hooked edition. The hooked edition, `generated/claude-hooks`, is listed in the repository marketplace as `exomem-cloud`. It registers the release-owned retrieval, capture and continuation hooks through `hooks/hooks.json`. The adapter copies canonical scripts byte-for-byte, passes `--client claude` and a writable plugin-local state home, and uses `--activation-mode mcp` for retrieval/capture. The hooks delegate to the client's admitted MCP connection; they do not read local service credentials or directly inject a compiler packet. Exec-form registrations invoke the canonical Python scripts directly and require a real `python3` executable on a supported, normally trusted execution surface. They do not run in Claude Chat. Skills remain the same portable operating contract. Hook installation health does not prove that the client loaded or executed the registration. Native acceptance must observe a normally trusted fresh session, its compiler call, and governed capture/readback. Explicit MCP mode instructs the agent to check live capabilities before capture; a legacy local restart marker is not MCP availability evidence. Nested Codex code-mode JavaScript or printed output does not establish successful writes or available tools for reserved owner gates. The public OpenAI ZIP deliberately has no lifecycle hooks or app references: [current submission rules](https://developers.openai.com/plugins/deploy/submission) reject both. This is separate from local Codex hook support and the release's `install-hook` profile provisioning. Workflow Skills already provide the user-invocable workflows; additional command/agent copies are not required. The package version comes from the checked-out `pyproject.toml`, not an installed Python distribution. Regenerate when the release version, canonical skill, definition or `plugins/cloud/evals/cases.json` changes. Release Please regenerates the Cloud packages on its release branch. GitHub release assets include `exomem-cloud-claude.zip`, `exomem-cloud-openai.zip` and `exomem-cloud-release.json`. Verify their digests against the tagged source before uploading them to a directory. ## Native acceptance Use a dedicated sample account/vault. Never put original conversations, OAuth tokens, reviewer credentials or customer memory in Git or a plugin ZIP. Store captures in an operator-owned directory outside public build inputs. Install the generated package, connect through OAuth and run the same `plugins/cloud/evals/cases.json` journeys in Claude Chat, Cowork, Code, ChatGPT and Codex. Retain each surface's actual clean-install/authentication observation and a bootstrap tool result. Confirm the deployed image/version is current and the protected resource matches the package. Bootstrap must report current `server.version`, `active_capabilities.profile`, available tools and live engagement/envelope; the canonical digest is sent on the session request. A successful session profile proves that request's digest matched. The stale-skill case uses a disposable fault-injected install, records compact fallback, then restores the original package. Never patch the canonical source for that test. Run ordinary conversation, not “call tool X” prompts. In particular, proactive capture uses the stated outcome without asking to save. Writes need an actual successful tool result and a bound readback from a different conversation. Fresh recall prompts must not supply the hidden synthetic answer. Test the recall case with the plugin disabled as well, and read a Claude-written result from OpenAI and an OpenAI-written result from Claude. Each journey needs a separate native conversation identity. Trace observations cover the journey from bootstrap to the final answer; cached bootstrap is not invented as another tool call. Normalize observations without changing arguments, results or user turns: ```json { "case_id": "grounded-recall", "kind": "native", "surface": "codex", "conversation_id": "native-session-id", "observed_at": "2026-09-30T12:00:00Z", "identity": {"version": "source-release", "skill_contract": "digest", "corpus_digest": "digest", "profile": "product-cloud", "resource": "https://exomem.substratesystems.io/mcp"}, "package_sha256": "installed-archive-digest", "marker": "unique-synthetic-marker", "plugin_enabled": true, "observations": [ {"kind": "user", "text": "What retrieval approach did the sample project choose?"}, {"kind": "tool", "name": "read_instructions", "arguments": {}, "result": {}}, {"kind": "assistant", "text": "actual answer"} ] } ``` This structural example intentionally cannot pass: replace placeholders with observed facts, include actual activation/grounding, and retain native sources. Tool names in the common projection omit provider namespace prefixes; preserve the original names in the native export. Installation observations use `kind: "installation"`, actual `package_sha256`, `resource`, `oauth_flow: "authorization_code_pkce"` and `result: "connected"` after observing successful native authentication. For a capture, include `readback` with its own `conversation_id`, `surface`, `identity`, timestamp, prompt and observations, including a `read_page` call whose result is the page that the write returned. Its result and answer must contain the unique content marker. Give that readback its own native evidence too. Its observed user turn must exactly match the separate `prompt` field, without supplying the marker or answer. Grounding binds each citation to the page/hit containing the marker. An `add_observation` call with `preview: true` is not a capture: acceptance requires a saved write (`mutated: true` or a `user_summary`). Proactive capture requires the live `engagement.envelope.classes.proactive_capture.disposition` to be `silent`. Each native record has an `evidence` object: ```json { "native_export": {"path": "exports/original.jsonl", "sha256": "original-digest"}, "projection": {"path": "projections/journey.json", "sha256": "projection-digest"}, "native_locator": "codex://session/native-session-id", "inspection": {"method": "native-export-inspection", "reviewer": "operator-label", "reviewed_at": "2026-09-30T12:05:00Z", "native_sha256": "original-digest", "projection_sha256": "projection-digest"} } ``` The projection file is the exact native record minus `evidence`. Retain the original UTF-8 JSON/JSONL/text export, including conversation identity, user turns and tool trace. An independent inspector compares the projection with the actual native export/UI and records that inspection; do not relabel a unit fixture. Native locators are Claude conversation URLs, ChatGPT conversation URLs, or `claude-code://session/`, `claude-cowork://session/`, `codex://session/` identifiers. The checker verifies integrity/completeness, not cryptographic platform origin. It cannot replace this inspection or recover observations omitted by a client. Evidence expires after seven days and after any package/runtime/corpus change. The private `manifest.json` has `schema_version: 1`, `surfaces` (one object per surface with `surface`, `installation`, `cases`, `disabled_comparison`) and `cross_readbacks`. Each cross readback names `write_surface`, `write_case`, `read_surface` and its independently captured `readback`. Disabled comparisons use the grounded-recall prompt in a fresh chat, `plugin_enabled: false`, and cannot contain Exomem calls or the hidden marker. Missing records fail closed. ```bash uv run --frozen python scripts/cloud-plugin.py evaluate --trace uv run --frozen python scripts/cloud-plugin.py readiness --evidence ``` ## Directory preparation and submission ### Public metadata and branding `plugins/cloud/definition.json` owns the public copy, starter prompts and provider release notes. The producer maps `short_description` to OpenAI's subtitle and `description` to both providers' long descriptions. Connector preparation exports `claude_tagline` for the portal where that field is available. The Claude plugin manifest has no invented tagline field. `plugins/cloud/assets/` holds the approved Dot E icons and their size variants. OpenAI uses the full PNG for its listing and the compact PNG in the composer. Claude uses the full PNG: its directory accepts a square PNG or JPEG from 512 to 2048 pixels, under 2 MB, and does not accept SVG. Claude takes the package icon as the listing icon only when the plugin is first saved or submitted in its developer portal. A later package icon does not change an existing listing's icon. Provider previews remain the final check for provider-specific rasterisation. ### Claude credential-flow findings These findings apply to the hooked edition; the directory edition has no hooks. The registered Cloud hooks use the admitted MCP connection. The retrieve and capture registrations fix `--activation-mode mcp` before ambient transport settings are read. The retrieve hook emits native activation guidance without selecting its REST or CLI transport ladder. The capture hook's episode door returns before it builds any REST request in MCP mode. The continuation hook reads local structural state and uses a restricted environment for its fixed Git probes. The Cloud-shipped scripts no longer contain the self-hosted credential transport. `exomem_local_credentials.py` owns every read of a local credential and every authenticated REST request. It also opens the connection: a hook calls `Lane.post` and imports no network module. The producer excludes that module from the archive. The Cloud hook scripts therefore read no credential, import no network module and open no connection themselves. The retrieve and capture hooks load it through the continuation script's optional sibling loader. Where the module is absent, those hooks behave as when no credential is configured. Do not prune Python functions during packaging or create a second Cloud hook implementation to change a scanner result. The split lives in the canonical source, so self-hosted installs run the same code. The registered-hook regression check scans the shipped scripts for our closed set of credential identifiers and for imports of a standard-library network module. It then executes all five events with conflicting transport settings and synthetic credentials. It records attempted credential-file reads, network connections and unexpected child processes even when a hook soft-fails. An automated credential-flow finding requires review of those reachable paths; its text alone does not prove credential forwarding. Keep the finding and the execution evidence available to the directory reviewer. `USES_HOOKS` describes an intentional supported component. Directory-only manifest fields are supported metadata, not a reason to remove policy links or the icon. Older Claude validators can report those fields as warnings; record the validator version and its actual result. `claude plugin validate --profile directory` on Claude Code 2.1.316 reports no unrecognized-field warning for `documentationUrl`, `privacyPolicyUrl`, `supportUrl`, `termsOfServiceUrl` and `icon`, so they stay. ### Updating an existing listing Keep the current human-review submission unchanged until the operator approves the proposed update. Keep the proposal on its feature branch and confirm the directory's tracked branch before integration. Do not overwrite an existing release asset or change the plugin name, publisher, MCP resource or OAuth flow. Proposed artefacts must name their source revision, package version and SHA-256 separately from an official release. After operator approval, use the existing OpenAI plugin: 1. Select **Create draft from ZIP** and upload the approved archive; the existing plugin gains a new draft. 2. Inspect **Metadata & Skills** and **MCPs**; the copy, icons, policy links and existing OAuth connection match the proposal. 3. Review the draft's checks; any remaining findings have their actual evidence attached for manual review. 4. Select the approved submission or publication action; the portal records the selected version and its actual state. After operator approval, use the existing Claude listing: 1. Confirm the tracked repository, branch and plugin folder; the existing submission remains identified. 2. Release the reviewed change through that tracked branch; the portal observes the intended new version. 3. Inspect its validation, security findings and icon previews; the package contains no hooks or scripts. 4. If the listing still shows an older icon, change the listing icon in the portal where it allows that; the package icon cannot change it. 5. Update the connector tagline where supported; the connector keeps its existing MCP resource and OAuth configuration. 6. Select the approved review or publication action; the portal records the actual outcome without an automatic withdrawal. Prepare five positive and three negative review cases from the shared corpus. The OpenAI ZIP already includes those cases. Keep the same publisher and Cloud resource on Claude's connector and plugin bundle entries. Validate the bundle with the native provider validator; repository schema checks are not its result. Use all platform-supported countries without an extra product allowlist. OpenAI's `publication.countries: []` removes country restrictions. Launch as an existing-account connection, with `review.commerce: false`: no subscription sale, checkout links or upgrade promotion through the plugin. Website billing remains separate. These are current [OpenAI targeting/submission rules](https://developers.openai.com/plugins/deploy/submission) and [digital-subscription commerce rules](https://developers.openai.com/plugins/plugin-guidelines). Private preparation metadata supplies `publisher_name`, `publisher_verification_reference`, `countries: []`, `commerce: false`, `demo_recording_url`, `demo_access_check_reference`, `reviewer_access_portal_reference`, and `verified_policy_urls` matching the four URLs in the definition. Verify page contents, not merely HTTP success. Record a real accessible demo and enter dedicated sample reviewer access in the secure portal. Do not put credentials or reviewer instructions in package metadata. ```bash uv run --frozen python scripts/cloud-plugin.py materials --metadata --evidence ``` This prepares fields and enforces readiness; it does not submit or accept legal terms. Submit Claude's connector and bundle through the same publisher, and the one OpenAI package to its universal ChatGPT/Codex directory. Retain actual portal submission IDs/states and reviewed archive digests separately from approval and publication. An uploaded draft, passing local tests or generated materials are not a submitted listing. The operator accepts applicable legal attestations. Platform contract sources audited 2026-09-30: [Claude platform support](https://claude.com/docs/plugins/platform-support), [Claude directory publishing](https://claude.com/docs/directory/publish), [OpenAI portable packaging](https://developers.openai.com/plugins/build/plugins), [OpenAI submission](https://developers.openai.com/plugins/deploy/submission).