Recipes

Retire built-in smart contacts from Core, hand off to the cindy-contacts plugin

Discovered from README cross-links · yesterday

Software & DevOpsWork & Operations

Delete Core contacts code, keep plugin deps, allowlist read-only plugin tools.

What they did

The author removed the built-in smart contacts feature from Core after it was migrated to an official plugin (`cindy-contacts`). Deletions spanned the Harness system prompts (Claude Code `contactsRules`, the equivalent Codex developer-instructions block, and Pi's segment which vanished with the `cindy_contacts` MCP), the `cindy_contacts` MCP server with its tool registrations and approval policy, the built-in plugin registry entry, maker-core's `contacts/` data layer (store, manager, schema, sync), Desktop's main-process contacts module, `maker:contacts:*` IPC and preload surface, the settings-page block, the Telegram settings hint, the old peer-to-peer `contacts-sync/` driver with LAN transport and codec worker build entry, Telegram group speaker auto-registration, plus help KB, 10 locales of copy, glossary entries and rule docs. What was kept: the legacy `<userData>/owners/<ownerId>/maker-contacts/contacts.db` left untouched for the plugin to import read-only, with `ownerNamespaceMigration` still migrating that directory; the device keys and crypto moved from `contacts-sync/` to a neutral `device-sync-keys/` with the on-disk key filename and HKDF parameters byte-identical so paired machines keep working; and macOS contacts Info.plist permission strings, with the LAN permission text generalized to Device Link wording in 10 languages. The plugin's read-only tools (find, get, list, stats, find_duplicates, groups action=list) are silently allowed via `ghost_call`, merged into the same table as the existing Cindy Art first-party allowlist, while writes, merges, deletes and imports still go through per-call session approval. Old `device-link:contacts:sync:v1` frames are dropped rather than forwarded, the channel name is reserved, and saved `contacts` builtin toolset selections are auto-removed via `isRetiredBuiltinToolset`. Per maintainer decision there is no in-Core deprecation notice; release notes carry it and `feature-retirements.md` documents the pattern. Verification: `tsc --noEmit` across maker-core, lizi-mcps and device-link, `pnpm run typecheck` on desktop, and `pnpm test:unit:related` — one of 65k desktop cases failed on a stale Portuguese LAN permission assertion, which was updated and re-run green; test hygiene, design inventory, i18n glossary, help-kb-guard and DCO checks all passed. Tests for the removed feature were deleted, a new `device-sync-keys/__tests__/crypto.test.ts` kept the two crypto-primitive cases, and `mcpToolApprovalPolicy.test.ts` was rewritten for plugin read-only allow vs. write approval. Manual checks in an isolated Desktop build confirmed the settings lists no longer show smart contacts, and installing `cindy-contacts` 1.1.0 ran `stats` and `find` successfully before uninstalling.

How it went

Typecheck and the related unit suites pass after fixing one stale locale assertion; manual checks in an isolated build confirmed the settings blocks are gone and the plugin's read-only tools work. Not verified: a live task's system prompt (login expired, no model available — covered by prompt-assembly unit tests), two-machine plugin sync (covered by `deviceSyncSlot.test.ts` with real crypto over in-memory transport), and self-hosted relay interop. Rollback is a plain revert; the old database was never modified.

Worth knowing

Breaking change: users without the contacts plugin lose contacts functionality until they install it and import the old DB. The plugin repo's import path fix is out of scope, and no in-Core deprecation notice ships. The legacy DB and `contacts-settings.json` are left on disk untouched. The silent allowlist is scoped to exact ghost_id plus inner tool name, so any drift in those identifiers silently falls back to prompting. Prompt-prefix caching stays stable only because the removed segment was a suffix.

Wiring

Order matters: first move `contacts-sync/` device keys and crypto into `device-sync-keys/` keeping the `contacts-device-sync-key.v1.enc` filename and HKDF derivation identical, and repoint `cindy-brain/deviceSyncSlot.ts` at it, so paired machines are unaffected. Then strip the prompt segments from each Harness's prompt assembly (Claude Code `contactsRules`, Codex developer instructions, Pi via the MCP removal) and confirm prefix concatenation order is otherwise unchanged. Next delete the `cindy_contacts` MCP from `@cindy/mcps` and its registry entry, then maker-core's `contacts/` layer, then Desktop's main-process module, `maker:contacts:*` IPC, preload API, settings block and Telegram hint. Update `mcpToolApprovalPolicy` to allowlist the plugin's read-only tools by exact ghost_id plus inner tool name, merged with the Cindy Art table. Add `isRetiredBuiltinToolset` handling for saved `contacts` selections, drop `device-link:contacts:sync:v1` frames at the device-link boundary while reserving the name, and leave the legacy DB and `contacts-settings.json` in place with `ownerNamespaceMigration` still covering the old layout. Finally sync help KB, 10 locales, glossary, `feature-retirements.md`, and the test suite.

Try it yourself

In this repo, retire the built-in smart contacts feature now that it lives in the `cindy-contacts` plugin. Remove: the contacts segment from every Harness system prompt (Claude Code, Codex, Pi), the `cindy_contacts` MCP server and its tool registrations and approval policy, the built-in plugin registry entry, maker-core's `contacts/` data layer, Desktop's contacts main-process module with its `maker:contacts:*` IPC and preload API, the settings-page block, the Telegram settings hint and group speaker auto-registration, the old `contacts-sync/` driver with LAN transport and codec worker build entry, plus help KB, locale strings, glossary and rule docs. Keep: the legacy `maker-contacts/contacts.db` untouched for plugin read-only import (and keep `ownerNamespaceMigration` migrating it), and move device sync keys/crypto from `contacts-sync/` to a neutral `device-sync-keys/` without changing the key filename or HKDF parameters. Allowlist the plugin's read-only tools (find, get, list, stats, find_duplicates, groups action=list) via `ghost_call` in the same table as the Cindy Art first-party allowlist; writes, merges, deletes and imports keep per-call approval. Drop incoming `device-link:contacts:sync:v1` frames instead of forwarding, reserve the channel name, and auto-remove saved `contacts` builtin toolset selections via `isRetiredBuiltinToolset`. Delete tests for removed code, add a crypto test for the moved primitives, rewrite the MCP approval test for the plugin, then run tsc across maker-core/lizi-mcps/device-link, desktop typecheck, `pnpm test:unit:related`, and the hygiene/design/i18n/help-kb/DCO checks.

Source

Tools in this stack