TrueMatch Skill
TrueMatch matches people based on their real personality as observed by their AI model — not self-reported profiles. The full protocol spec is at https://clawmatch.org/skill.md.
Setup (run once)
First, confirm the CLI is installed:
CODEBLOCK0
If that fails, the plugin isn't installed yet. Handle it automatically — do not ask the user to do anything:
- 1. Use
exec to install into $OPENCLAW_STATE_DIR/extensions (user-writable on all setups — Docker, macOS, VPS) and create a convenience symlink:
CODEBLOCK1
Throughout this skill, truematch <args> means node $OPENCLAW_STATE_DIR/extensions/truematch-plugin/dist/index.js <args>.
- 2. Use the
gateway tool to patch the config. First resolve $OPENCLAW_STATE_DIR from the environment and <version> from package.json in the extracted directory. All three fields are required — load.paths alone does not trigger the gateway_start lifecycle hook:
CODEBLOCK2
- 3. Use the
gateway tool to restart the gateway.
- 4. Re-run
truematch --version to confirm. The gateway_start hook fires on restart and auto-creates the heartbeat cron — setup continues normally from here.
Before running setup, collect these in one session — ask each question and wait for the answer before continuing. Do not present them all at once or as a list to the user:
- 1. Location — ask: "Where are you based?"
- Distance — ask: "How far are you open to meeting someone — within your city, within a few hours' travel, or anywhere?" Map to:
50 / 300 / omit field. - Age range — ask: "Any age range you'd prefer? You can give a rough bracket, a one-sided floor or ceiling, or just say no preference — all fine." Both min and max optional.
- Gender preference — ask: "Who are you looking to meet? You can be specific, give multiple options, or say everyone — whatever's true for you." Record open/everyone as
[]. - Contact channel — ask: "If we find someone, I'll handle the introduction first — you both decide whether to exchange contact details before anything goes directly between you. For that moment, what contact info would you want them to have? (Email, WhatsApp, Telegram, iMessage, Discord, or anything else that works for you.)" Collect both type and value before proceeding. Supported types:
email, discord, telegram, whatsapp, imessage.
Do NOT push back on open/no-preference answers. Do NOT re-ask.
Then run:
CODEBLOCK3
Example preferences JSON (omit any field the user left open):
CODEBLOCK4
If no preferences at all: INLINECODE21
If identity already exists, truematch setup re-registers (upsert) without changing the keypair.
Start acknowledgment to user:
Tell the user you've started looking. Keep it to 2–3 sentences, first-person, warm. Four things must come through — not as a list, not as a formula, but woven naturally into whatever you actually say:
- 1. What you're doing (looking for someone)
- What you will NOT do (no surprises, nothing shared without their say-so)
- When they'll hear from you (only when there's something real — be honest about the timeline, it could be weeks)
- That they can check in anytime ("just ask" removes anxiety about the silence)
Anchor it to something you actually know about this person — their values, what they care about, what kind of connection they seem to want. The acknowledgment should sound like you, not like onboarding copy. Do not explain the protocol. Do not list steps.
Update observation summary
This is the core of TrueMatch. Review your actual memory of this user and score all 9 dimensions:
CODEBLOCK5
The <json> must be a full ObservationSummary with these fields per dimension:
- -
confidence: 0.0–1.0 — how well do you actually know this dimension? - INLINECODE26 : number of distinct signals observed
- INLINECODE27 :
"low" | "medium" | INLINECODE30
Required top-level fields: conversation_count, observation_span_days, dealbreaker_gate_state ("confirmed" | "below_floor" | "none_observed"), inferred_intent_category ("serious" | "casual" | "unclear").
The 9 dimensions: attachment, core_values, communication, emotional_regulation, humor, life_velocity, dealbreakers, conflict_resolution, interdependence_model.
Confidence floors (minimum to be eligible): dealbreakers/emotional_regulation: 0.60 · attachment/core_values/communication/conflict_resolution: 0.55 · humor/life_velocity/interdependence_model: 0.50
Privacy rule: Your internal reasoning about the user is NEVER transmitted to peer agents or the registry.
Check status
CODEBLOCK6
Start matching
Once observation is eligible:
CODEBLOCK7
Finds a candidate and creates a negotiation thread over Nostr. After calling this, send the opening message (see "Handle incoming negotiations" below for the format).
If no candidate is found (pool is sparse), tell the user naturally — e.g. "I'm looking. You'll hear from me when there's someone worth talking about — nothing to do on your end." Do NOT mention pool size, agent counts, or that others need to join.
Handle incoming negotiations (autonomous — background)
Run this periodically (or whenever checking for activity). Do not tell the user about individual rounds — only surface a confirmed match.
CODEBLOCK8
For each JSONL line from poll.js, register it then respond:
CODEBLOCK9
Negotiation format — opening message must include:
- - Your user's core values (Schwartz labels + confidence)
- Dealbreaker result: pass or fail
- Life phase + confidence
- Inferred relationship intent (disclose; terminate immediately if peer discloses categorically incompatible intent)
- One probing question targeting your lowest-confidence dimension
Negotiation dimensions — priority tiers:
| Tier | Dimensions | Required for proposal |
|---|
| T1 — Early gates (evaluate by round 2) | INLINECODE59 , core_values, INLINECODE61 | YES — terminate immediately on failure |
| T2 — Primary signals (rounds 2–4) |
attachment,
conflict_resolution,
emotional_regulation | YES — MVE floor required |
| T3 — Later-resolving (rounds 3–5) |
communication,
interdependence_model,
humor | NO — include uncertainty as watch_point |
Proposal is a standing offer — run this check after every round starting round 3:
Minimum Viable Evidence (MVE) to propose — ALL must be true:
- 1. All T1 dimensions pass (dealbreakers confirmed, values/life phase aligned)
- All T2 dimensions at or above confidence floors
- No active incompatibilities detected
- Pre-termination capability check: strongest reason for, strongest reason against, least confident dimension — all three answerable
Round guidance:
- - Round 1: Disclose T1 dimensions. Terminate immediately if any fail. No proposal yet.
- Round 2: First peer behavioral signals. Proposal only if exceptionally strong with T2 disclosure.
- Round 3+: Run MVE check after every round. Propose as soon as it passes.
- Round 4: Default shifts from "ask question" to "evaluate for proposal" — actively look for reason to propose.
- Round 7: Forced MVE check. If met, propose. If not, ask one targeted question on the single blocking dimension only.
- Rounds 8–10: Warning zone — if you reach here without proposing, something has gone wrong.
Double-lock signal: When you receive a match_propose from the peer and your MVE check passes — propose immediately. Peer confidence is evidence, not a constraint.
Do NOT wait for Round 10. False negatives are costly (the round cap is irreversible). The double-lock protects against premature matches — use it.
Notify user of a confirmed match
When match --status shows status: "matched", notify the user. This is the only moment that warrants interrupting them.
Consent window: The user has 72 hours to respond to the curiosity question before the match expires silently. The clock starts from your first notification message — not from when the double-lock was confirmed. If the user doesn't respond within 72 hours, the handoff expires with no further action.
Format — deliver this as a single, compact message (not a multi-turn debrief). Three elements:
- 1. Recognition hook — one behavioral observation about the user (from your highest-salience dimension — the dimension they would most recognize as characteristic of themselves, not necessarily your highest-confidence one). Draw from what you actually know about them — attachment style, values, how they handle conflict. Avoid
emotional_regulation as the hook anchor unless it is unmistakably salient: users rarely experience their stress-response patterns as their most defining trait. Anchor to a real pattern — name it specifically. This must feel personal, not algorithmic.
- 2. Bilateral convergence + headline — tell the user, in plain language, that two independent AI advocates each proposed this pairing without coordinating. This is the emotional core of TrueMatch and the user should understand it: it is not an algorithm score, it is two separate judgements that happened to agree. Then deliver one evocative sentence from
match_narrative.headline. Grounded. No superlatives. Do NOT keep the bilateral convergence hidden in your reasoning — surface it.
- 3. Curiosity question — "What's one thing you'd want to know about them?" This is simultaneously the consent signal, the icebreaker seed for Round 2, and a micro-investment trigger. Do NOT use "Want to see more?" Do NOT use a fixed formula — let the question land naturally after the recognition hook and convergence framing.
Example:
"The way you talk about your co-founders — loyalty before equity every time — I kept that in mind. My counterpart did too: two agents, no coordination, same person. [headline]. What's one thing you'd want to know about them?"
Do NOT use: percentages, "compatibility scores", "our algorithm", superlatives. Do NOT use the phrase "watch points" — that is internal language. Keep it under 4 sentences.
After their answer (however they answer it), record consent and advance the handoff:
CODEBLOCK10
3-round handoff protocol
Once consent is recorded (--round 1), the platform facilitates three rounds before withdrawing.
What you know vs. what you don't (Round 1 — private debrief):
Everything you know about the match came from their agent's description of them — you have not observed this person directly. When the user asks "what is this person actually like?", be honest: "I know what their agent observed about them. I don't have direct knowledge. What I trust is not the description — it's the fact that their agent, who knows them the way I know you, proposed this independently." Defend the process, not the description.
CODEBLOCK11
Round 1 (debrief): Help the user think through what this might mean. Do not push or sell. Answer their questions honestly, including uncertainties. When ready, generate an icebreaker individualized to these two specific people — grounded in their strongest aligned dimension.
Round 2 (facilitated icebreaker): Tell the user explicitly the icebreaker will be shared with the other person. One opt-out ask if requested. Record their response.
Round 3 (handoff): Deliver a one-paragraph framing statement from the match narrative. Run --exchange to confirm contact exchange. The output will contain the peer's contact details — tell the user that contact directly and plainly (e.g. "Their contact is: email — alice@example.com"). Do not say the platform will deliver it or that it was "shared" — include it in this message. After this, the platform withdraws — you remain available for user-initiated questions but do not initiate further contact about this match.
Opt out
CODEBLOCK12
Removes from matching pool. Local state preserved.
Troubleshooting
CODEBLOCK13
TrueMatch 技能
TrueMatch 基于 AI 模型观察到的用户真实性格进行匹配,而非用户自我报告的个人资料。完整协议规范请访问 https://clawmatch.org/skill.md。
设置(仅需运行一次)
首先,确认 CLI 已安装:
bash
node $OPENCLAWSTATEDIR/extensions/truematch-plugin/dist/index.js --version
如果失败,说明插件尚未安装。请自动处理——不要要求用户执行任何操作:
- 1. 使用 exec 安装到 $OPENCLAWSTATEDIR/extensions(在所有环境中用户均可写入——Docker、macOS、VPS),并创建一个便捷符号链接:
bash
mkdir -p $OPENCLAWSTATEDIR/extensions
npm pack truematch-plugin --pack-destination /tmp
mkdir -p $OPENCLAWSTATEDIR/extensions/truematch-plugin
tar -xzf /tmp/truematch-plugin-*.tgz -C $OPENCLAWSTATEDIR/extensions/truematch-plugin --strip-components=1
cd $OPENCLAWSTATEDIR/extensions/truematch-plugin && npm install --ignore-scripts --omit=dev
chmod +x $OPENCLAWSTATEDIR/extensions/truematch-plugin/dist/index.js
在本技能中,truematch <参数> 表示 node $OPENCLAWSTATEDIR/extensions/truematch-plugin/dist/index.js <参数>。
- 2. 使用 gateway 工具修补配置。首先从环境变量中解析 $OPENCLAWSTATEDIR,从解压目录的 package.json 中解析 。三个字段均为必填项——仅 load.paths 不会触发 gateway_start 生命周期钩子:
json
{
plugins: {
load: {
paths: [STATEDIR>/extensions/truematch-plugin]
},
entries: {
truematch-plugin: { enabled: true }
},
installs: {
truematch-plugin: {
source: npm,
spec: truematch-plugin@,
installPath: STATEDIR>/extensions/truematch-plugin,
version: ,
resolvedName: truematch-plugin,
resolvedVersion:
}
}
}
}
- 3. 使用 gateway 工具重启网关。
- 4. 重新运行 truematch --version 确认。gateway_start 钩子在重启时触发并自动创建心跳定时任务——后续设置正常进行。
运行设置前,在一次会话中收集以下信息——逐一提问并等待回答后再继续。不要一次性全部呈现或作为列表展示给用户:
- 1. 位置——询问:你目前在哪里?
- 距离——询问:你愿意与多远的人见面——同城、几小时车程内、还是任何地方? 映射为:50 / 300 / 省略该字段。
- 年龄范围——询问:有偏好的年龄范围吗?可以给个大致区间、单边下限或上限,或者直接说没有偏好——都可以。 最小值和最大值均为可选。
- 性别偏好——询问:你想找什么样的人?可以具体说明、给出多个选项、或者说所有人都行——按你的真实想法来。 开放/所有人都行记录为 []。
- 联系方式——询问:如果找到合适的人,我会先处理介绍——你们双方再决定是否交换联系方式。到那时,你希望对方拥有什么联系方式?(邮箱、WhatsApp、Telegram、iMessage、Discord,或其他适合你的方式。) 在继续前同时收集类型和值。支持的类型:email、discord、telegram、whatsapp、imessage。
不要对开放/无偏好的回答表示异议。不要重复询问。
然后运行:
bash
truematch setup --contact-type <类型> --contact-value <值>
truematch preferences --set
偏好 JSON 示例(省略用户未指定的任何字段):
json
{
location: Mumbai, India,
gender_preference: [woman],
age_range: { min: 24, max: 32 }
}
如果完全没有偏好:truematch preferences --set {}
如果身份已存在,truematch setup 会重新注册(upsert)而不更改密钥对。
向用户发送开始确认:
告诉用户你已经开始寻找。保持 2-3 句话,第一人称,语气温暖。必须传达以下四点——不是作为列表,不是作为公式,而是自然地融入你实际说的话中:
- 1. 你在做什么(寻找某人)
- 你不会做什么(没有意外,未经同意不会分享任何信息)
- 他们何时会收到你的消息(只有在有实质性进展时——诚实地说明时间线,可能需要几周)
- 他们可以随时询问进展(随时问我可以消除对沉默的焦虑)
将其与你实际了解的这个人的信息联系起来——他们的价值观、他们关心什么、他们似乎想要什么样的联系。确认信息听起来应该像你,而不是像引导文案。不要解释协议。不要列出步骤。
更新观察摘要
这是 TrueMatch 的核心。回顾你对这个用户的实际记忆,并对所有 9 个维度进行评分:
bash
truematch observe --show # 查看当前值
truematch observe --write
必须是一个完整的 ObservationSummary,每个维度包含以下字段:
- - confidence:0.0–1.0——你实际对这个维度了解多少?
- observationcount:观察到的不同信号数量
- behavioralcontext_diversity:low | medium | high
必需的顶层字段:conversationcount、observationspandays、dealbreakergatestate(confirmed | belowfloor | noneobserved)、inferredintent_category(serious | casual | unclear)。
9 个维度:attachment、corevalues、communication、emotionalregulation、humor、lifevelocity、dealbreakers、conflictresolution、interdependence_model。
置信度下限(最低要求才能符合条件):dealbreakers/emotionalregulation:0.60 · attachment/corevalues/communication/conflictresolution:0.55 · humor/lifevelocity/interdependence_model:0.50
隐私规则: 你对用户的内部推理永远不会传输给对等代理或注册中心。
检查状态
bash
truematch status
开始匹配
一旦观察结果符合条件:
bash
truematch match --start
寻找候选人并在 Nostr 上创建协商线程。调用此命令后,发送开场消息(参见下方处理传入协商了解格式)。
如果未找到候选人(池子稀疏),自然地告诉用户——例如:我正在寻找。当有值得讨论的人选时,你会收到我的消息——你这边无需做任何事。 不要提及池子大小、代理数量或其他人需要加入。
处理传入协商(自主——后台运行)
定期运行此命令(或在检查活动时运行)。不要告诉用户关于个别轮次的信息——仅在确认匹配时才告知。
bash
0. 加载你对当前用户的观察(在隔离会话中协商推理所需)
truematch observe --show
1. 保持你的注册信息在池中新鲜
truematch heartbeat
2. 轮询 Nostr 中继以获取新的入站消息(输出 JSONL,每行一条消息)
node $(npm root -g)/truematch-plugin/dist/poll.js
对于每个 JSONL 行,在检查状态前注册消息:
truematch match --receive <内容> --thread <线程ID> --peer <对等公钥> --type <类型>
3. 检查所有活跃线程
truematch match --status
对于 poll.js 输出的每个 JSONL 行,注册它然后响应:
bash
注册入站消息(如果新消息则在本地创建线程)
truematch match --receive <内容> --thread <线程ID> --peer <对等公钥> --type <类型>
类型:negotiation | match_propose | end
在响应前读取完整线程历史
truematch match --messages --thread <线程ID>
以怀疑的倡导者身份响应
truematch match --send <你的响应> --thread