{"id":19314,"plugin_id":"plugins_6a9669d9e57c8191a04a3c8951e44401","kind":"skill","collection_source":null,"comparison_source":null,"observed_at":"2026-09-30T23:15:29.515Z","digest":"4afc8093865735402d34bf9408e8c9322c71a619f77734950bdd3423b0213751","against":null,"payload":{"description":"Design, implement locally, or evaluate YCloud Contacts lifecycle, attribute, and note operations with strict contact/note ID separation and defensive pagination. Use for Contacts API work; exclude Custom Events, Unsubscribers, message sending, and real API mutations.","included_files":[{"relative_path":"agents/openai.yaml","size_in_bytes":297},{"relative_path":"references/openapi.md","size_in_bytes":39333},{"relative_path":"references/runtime.md","size_in_bytes":14227},{"relative_path":"references/shared/integration-boundaries.md","size_in_bytes":8401},{"relative_path":"references/shared/pagination-contract.md","size_in_bytes":3494}],"name":"ycloud-contacts","skill_md_contents":"---\nname: ycloud-contacts\ndescription: Design, implement locally, or evaluate YCloud Contacts lifecycle, attribute, and note operations with strict contact/note ID separation and defensive pagination. Use for Contacts API work; exclude Custom Events, Unsubscribers, message sending, and real API mutations.\n---\n\n# YCloud Contacts\n\nDesign or implement contract-aware contact, contact-attribute, and contact-note\nworkflows against mocks only. Never call YCloud, read credentials or customer\ndata, perform a real mutation, or treat a contact change as a downstream event,\nsubscription, or message action.\n\n## Execution and authority boundary\n\nThese restrictions govern Skill execution: do not call a YCloud Provider API, access real credentials, or read real business data. The Skill may generate server-side adapter code for an application's runtime, but must not start it or make a live request. Reading public official documentation as contract evidence is allowed and is not a Provider API call or business-data access. A live smoke test is outside the default workflow and requires separate, explicit authorization naming the target/environment, allowed operations, credential boundary, and required result evidence.\n\nHonor an Architect handoff for `scope`, `deliverable`, `mutation`, capability\nIDs, project seams, and evidence. Without one, default to focused, read-only\nguidance unless the user explicitly requests local implementation. Local-write\nauthorization permits request models/builders, adapters, handlers, service\nbindings, mocks, fixtures, and no-network tests inside the scoped project. Every\ncreate, update, or delete remains mock-only; local-write authorization does not\nauthorize a real contact or note mutation.\n\nKeep claims in three authority layers:\n\n1. **Provider contract** — [references/openapi.md](references/openapi.md) and\n   [references/runtime.md](references/runtime.md). State these as YCloud behavior.\n2. **Developer Kit policy** — `references/shared/integration-boundaries.md` when\n   retry, idempotency, queueing, error translation, or webhook reliability is in\n   scope. Label its recommendations as local policy.\n3. **Project decisions** — only facts confirmed in the user's scoped project.\n\nDo not promote an `operationId`, generated model name, `x-*` extension, example,\nplatform recommendation, or project convention into provider behavior. If the\ngenerated references are absent, stale, internally inconsistent, or do not list\nall ten operations below, stop and report the drift.\n\n## Exact operation allowlist\n\nLoad both generated references after this Skill is selected. Match only these\noperations and preserve every source-defined parameter, request/response schema,\nstatus code, and description constraint.\nFor contact list work, also read\n[`references/shared/pagination-contract.md`](references/shared/pagination-contract.md).\n\n| Intent | Method and path | operationId |\n| --- | --- | --- |\n| List attributes | `GET /contact/contacts/attributes` | `contact-attributes-list` |\n| Create contact | `POST /contact/contacts` | `contact-create` |\n| Delete contact | `DELETE /contact/contacts/{id}` | `contact-delete` |\n| List contacts | `GET /contact/contacts` | `contact-list` |\n| Create note | `POST /contact/contacts/{contactIdentifier}/notes` | `contact-note-create` |\n| Delete note | `DELETE /contact/notes/{noteId}` | `contact-note-delete` |\n| Update note | `PATCH /contact/notes/{noteId}` | `contact-note-update` |\n| List notes | `GET /contact/contacts/{contactIdentifier}/notes` | `contact-notes-list` |\n| Retrieve contact | `GET /contact/contacts/{id}` | `contact-retrieve` |\n| Update contact | `PATCH /contact/contacts/{id}` | `contact-update` |\n\nNo search operation is in this allowlist. Contact attributes are configurations,\nnot contact values. Contact retrieve/list responses do not contain notes; use\n`contact-notes-list` when note contents or note IDs are needed.\n\n## Identifier separation\n\nTreat every identifier as opaque and case-sensitive except where the contract\ndefines syntax. Never substitute one identifier class for another.\n\n- Contact `{id}` and note-owner `{contactIdentifier}` accept a contact ID, an\n  E.164 phone number beginning with `+`, or a Meta username without `@`, up to\n  255 characters. Ambiguous numeric input resolves as a username first and as a\n  contact ID only when no matching username exists; do not pre-resolve it with\n  local numeric heuristics.\n- Note `{noteId}` is a separate 24-character hexadecimal ObjectId. It is valid\n  only for `contact-note-update` and `contact-note-delete`. Obtain note IDs from\n  note create/list responses, never from the owning contact ID.\n- A `ContactNote.contactId` identifies the owner and is not the note's `id`.\n  For note mutations embedded in `contact-update`, an item without `id` creates\n  a note; an item with a note ID updates an owned note. Omitted notes remain\n  unchanged, an empty array changes nothing, and deletion uses the dedicated\n  note-delete operation.\n\n## Contract-first workflow\n\n1. Select one allowlisted operation and a confirmed server-side project seam.\n   Preserve unknown response properties and unknown attribute/source values;\n   do not fail exhaustive decoding when YCloud adds compatible fields or enums.\n2. Preserve request constraints. Contact create requires `phoneNumber`; notes\n   trim surrounding whitespace and require 1–500 characters after trimming;\n   contact create/update accepts at most 50 notes; tags accept at most 50 values\n   of at most 50 characters. `nickname` is a deprecated input alias for\n   `remarkName`, while response `nickname` is read-only WhatsApp data.\n3. Keep contact updates patch-like. A non-null `customAttributes` array replaces\n   all previous custom attributes. A no-op persisted-field update emits no\n   `contact.attributes_changed` event, although note mutations in the request\n   still apply. Do not infer note success from the returned `Contact`, because\n   that schema excludes notes.\n4. Use only synthetic contacts, phone numbers, usernames, note IDs, and payloads\n   in examples and tests. Keep `X-API-Key` injection server-side through the\n   Authentication handoff without reading a real key. An `operationId` is not an\n   SDK method name.\n5. Treat a timeout or lost response for create, update, delete, or note mutation\n   as ambiguous. Never replay it automatically. Note create has no client\n   idempotency key and replay creates another note; repeated note delete returns\n   `404`; repeated note update may emit another update event even when content is\n   unchanged. Any reconciliation or deduplication design is project policy.\n\n## Defensive pagination\n\nContact list supports two modes; choose one and do not silently switch modes.\n\n- Page mode omits `pageAfter`; `page` is 1–100 with default 1, `limit` is 1–100\n  with default 10, and `includeTotal` defaults false.\n- Its successful wire response is the merged `ContactPage allOf Page` object:\n  required `offset`, `limit`, and `length`; optional `total`; resource `items`;\n  and optional `cursor`. `offset` is response metadata, not a request parameter.\n  Preserve each Contact field, including `phoneNumber`, `countryCode`,\n  `countryName`, `sourceType`, `lastSeen`, and `lastMessageToPhoneNumber`, plus\n  unknown properties. Do not unwrap a nonexistent `data` property.\n- Forward-cursor mode starts with the string `pageAfter=0`, then passes the exact\n  returned `cursor.after` value unchanged. Never parse, increment, synthesize,\n  or persist assumptions about cursor contents.\n- Do not combine `pageAfter` with `page`, `pageBefore`, `offset`, or `sort`. Keep\n  filters unchanged for the traversal. A missing `cursor` means there is no next\n  page; do not assume an empty page, `length < limit`, or `total` is authoritative\n  evidence of continuation.\n- Cursor traversal is ordered by contact ID ascending and weakly consistent\n  under concurrent inserts, deletes, and filter-field updates. Restart with\n  `pageAfter=0` when a fresh traversal is required. `total`, when requested, is\n  the complete filtered count and is not reduced by cursor position.\n\nThe notes-list endpoint is not cursor-paginated: it returns all notes, newest\nfirst, with a maximum of 50. Do not add page parameters to it.\n\n## Mutation safeguards and tests\n\nImplement mutations only against mocks and verify them with no-network tests.\nFor any future external workflow, stop before the call, identify the exact\ncontact identifier or note ID and impact, require explicit operation-specific\nconfirmation, and leave an ambiguous result unresolved.\n\nTests should cover all ten routes and methods, required and optional body shape,\ncontact/note ID non-interchangeability, username-first ambiguity, note limits and\ntrimmed content, incremental note semantics, custom-attribute replacement, the\ncomplete provider-shaped ContactPage envelope and item mapping, pagination mode\nconflicts and terminal cursor absence, unknown fields/enums,\nprovider error/request-ID mapping, and no automatic mutation replay.\n\n## Outcome requirements\n\nReturn matched operation IDs and exact method/paths, authority-labeled contract\nfacts, project-local artifacts or proposed seams, no-network test evidence,\nidentifier handling, pagination mode, explicit unknowns, and handoffs. Do not\nclaim implementation from only a route label, sample JSON, or mock response;\nlink each implemented row to its adapter/handler and behavioral tests.\n\n### CANNOT\n\nList unsupported operations, missing project facts, source conflicts,\nunconfirmed SDK behavior, live credentials/customer data, real API calls,\nexternal mutations, automatic mutation replay, inferred note ownership, or\ndownstream event/subscription/message claims. Do not move documented contact\nidentifier resolution, note-ID syntax, or cursor behavior into `CANNOT`.\n\n### Handoff\n\nContacts is a producer for downstream capabilities, not their executor. Hand a\nconfirmed contact ID and the project-approved event payload to Custom Events;\nhand a confirmed contact identity plus channel intent to Unsubscribers; hand a\nconfirmed E.164 recipient or supported recipient identity to Messages. Keep\ncontact IDs, note IDs, event IDs, unsubscriber records, and message IDs separate,\nand do not infer that a contact mutation performed any downstream action.\n\nSend authentication storage to `ycloud-api-authentication`. Return to Architect\nwith capability-row status, changed or proposed artifacts, tests/results,\npagination and identifier evidence, unknowns, and outgoing handoffs.\n"},"changes":[],"summary":"First saved snapshot. No earlier version is available for comparison.","summary_kind":"deterministic","summary_metadata":{}}