@hraness/message-like-me 0.8.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.8.1
4
+
5
+ - Verify the Beeper bundle boundary at Wrench v0.16.5 and adapter
6
+ `beeper-local@2.3.0`: 32 reviewed operations comprise 27 through the pinned
7
+ CLI 0.6.2 executable and five fixed Desktop reads. The tagged source package
8
+ declaration of 0.6.1 is provenance only; export remains internal and bounded,
9
+ and Message Like Me owns no Beeper operation, credential, or live session.
10
+ - Make the exact public `@hraness/message-like-me@0.8.1` npm package the
11
+ supported default install, with the same reviewed bytes mirrored in the
12
+ immutable GitHub Release.
13
+ - Document that the Wrench v0.16.5 and official Wacli v0.15.0 WhatsApp
14
+ producer omits reaction-shaped rows and reports `reaction-state-unproven`
15
+ when current active or removed state cannot be established.
16
+ - Keep the v2 reaction artifact and strict parser for proven records while
17
+ treating an empty artifact from this producer as unobservable reaction
18
+ behavior, not evidence that no reactions occurred. Native WhatsApp overlap
19
+ therefore cannot reconcile reaction state from this producer.
20
+ - Give each supported source a distinct decorative SVG mark, preserve those
21
+ marks in forced-colors mode, and point the Beeper MCP note to Beeper's
22
+ official built-in server documentation.
23
+
3
24
  ## 0.8.0
4
25
 
5
26
  - Add `ensoul prepare` as an offline, bounded source adapter for the owner or
package/README.md CHANGED
@@ -1,46 +1,43 @@
1
1
  # Message Like Me
2
2
 
3
+ [![skills.sh](https://skills.sh/b/hraness/message-like-me)](https://skills.sh/hraness/message-like-me)
4
+
3
5
  **A local-first CLI and Agent Skill for studying private messaging history and
4
6
  drafting messages that sound like you.**
5
7
 
6
- Message Like Me turns private local messaging history into deterministic
7
- conversation metrics, bounded study packets, reusable style profiles, and
8
- privacy-bounded evidence packets for the Ensoul person-model workflow. It
9
- reads native iMessage history, caller-owned X data archives, and strict local
10
- source bundles, including multi-account Beeper and native WhatsApp exports
11
- produced through Wrench. Its copied Message Like Me and Ensoul Agent Skills
12
- teach Codex, Claude, and other coding agents how to interpret those local
13
- artifacts, build revisable evidence-led person models, and draft unsent replies
14
- in your voice.
15
-
16
- The CLI has no model integration. It does not authenticate with a product
17
- account, invoke Wrench, Beeper, Wacli, or WhatsApp, send messages, or operate
18
- Messages. An agent you already run may use the installed skill for semantic
19
- analysis and drafting; opening a study or Ensoul packet exposes its bounded
20
- excerpts to that agent environment.
21
-
22
- This is an evidence layer for relationship-aware drafting. It does not train a
23
- model, represent your identity, infer your beliefs, or claim that a draft is
24
- what you would have written. Your current meaning, facts, and intent outrank
8
+ Message Like Me turns caller-owned messaging history into deterministic local
9
+ metrics, bounded evidence packets, reusable style profiles, and unsent drafts.
10
+ The CLI owns ingestion, storage, measurement, and export. Its copied Message
11
+ Like Me and Ensoul Agent Skills teach Codex, Claude, and other coding agents how
12
+ to interpret those artifacts through the agent environment you already use.
13
+
14
+ The result is an inspectable evidence layer for relationship-aware drafting,
15
+ not a model of your identity. Your current meaning, facts, and intent outrank
25
16
  historical style.
26
17
 
27
- ## Supported sources
18
+ ## Why Message Like Me
28
19
 
29
- | Source | What Message Like Me reads | Boundary |
30
- | --- | --- | --- |
31
- | Apple Messages | The current macOS user's native `chat.db` history | Read-only ingestion from an ownership-checked stable local copy; Messages is never operated or changed. |
32
- | X data archive | Direct-message history in a caller-owned archive ZIP | X Chat is not included; the importer does not contact X, extract the archive, or download media. |
33
- | Beeper via Wrench | A bounded local bundle produced by verified Wrench v0.16.1 with Beeper CLI 0.6.2 | Message Like Me reads the finished bundle; it receives no Beeper credential, invokes no Wrench or Beeper operation, and sends nothing. |
34
- | WhatsApp via Wrench | A one-account native bundle produced by Wrench v0.16.3 with official Wacli 0.15.0 | Message Like Me verifies the finished bundle; Wrench alone owns Wacli, linked-device authentication, synchronization, and provider operations. |
35
- | macOS Contacts | Optional names and exact email or phone handles from AddressBook | Label enrichment only; Contacts is not a messaging-history source and is never changed. |
20
+ - **Separate evidence from interpretation.** The CLI produces deterministic
21
+ metrics and versioned packets without a model integration. Semantic analysis
22
+ happens only when you deliberately open an artifact in your own agent
23
+ environment.
24
+ - **Study each relationship in context.** Outgoing messages supply style
25
+ evidence; incoming messages supply the response context. Tempo, sessions,
26
+ bubble shape, replies, and prose remain attributable to their source.
27
+ - **Keep private history bounded.** Read-only import uses stable local copies,
28
+ ordinary views omit bodies and private labels, and body-bearing exports go
29
+ only to explicit owner-selected paths.
30
+ - **Stop at an inspectable draft.** The installed skill can help draft text,
31
+ but Message Like Me never authenticates with a messaging provider, operates a
32
+ messaging app, or sends, reacts to, or schedules a message.
36
33
 
37
- ## Install
34
+ ## Install and first run
38
35
 
39
- Message Like Me requires Bun 1.3.14 or newer. Install the immutable public
40
- release from GitHub, then install both bundled Agent Skills:
36
+ Message Like Me requires Bun 1.3.14 or newer. Install the exact public npm
37
+ package, then install both bundled Agent Skills:
41
38
 
42
39
  ```sh
43
- bun add --global github:hraness/message-like-me#v0.8.0
40
+ bun add --global @hraness/message-like-me@0.8.1
44
41
  messagelikeme skill install
45
42
  ```
46
43
 
@@ -56,18 +53,60 @@ messagelikeme skill install --target agents --scope project
56
53
  messagelikeme skill path
57
54
  ```
58
55
 
59
- Message Like Me is distributed directly through GitHub and is not published to
60
- npm.
56
+ The npm tarball and the immutable GitHub Release artifact are the same reviewed
57
+ bytes. Neither distribution includes private messages, contacts, profiles,
58
+ packets, drafts, or a network-backed runtime.
61
59
 
62
- ## Start with private local history
63
-
64
- Initialize the private data store and inspect its location:
60
+ Initialize an empty private store, then inspect it without reading any messaging
61
+ source:
65
62
 
66
63
  ```sh
67
64
  messagelikeme init
68
65
  messagelikeme doctor --json
69
66
  ```
70
67
 
68
+ The JSON response reports the initialized state, exact local paths, and store
69
+ integrity checks. This is the shortest complete product check; no message body,
70
+ contact, account, or provider credential is involved.
71
+
72
+ The first proof is intentionally data-free. It shows where Message Like Me will
73
+ work and whether its private store is healthy before you choose a source or
74
+ open evidence in an agent environment.
75
+
76
+ ## Three deliberate interfaces
77
+
78
+ | Surface | What it owns | First useful action |
79
+ | --- | --- | --- |
80
+ | CLI | Read-only ingestion, deterministic measurement, private artifacts, validation, and redacted machine-readable output | `messagelikeme doctor --json` |
81
+ | Agent Skill | Evidence-calibrated interpretation and unsent drafting inside the agent environment you already chose | Invoke `$message-like-me` after preparing an explicit bounded packet |
82
+ | TypeScript library | Versioned public types, strict bundle parsers, canonical JSON, digest helpers, and pure packet builders | Import from `@hraness/message-like-me` or a versioned subpath |
83
+
84
+ The artifact is the seam between these surfaces. Deterministic code produces
85
+ and validates it; an authorized agent may interpret it; typed consumers can
86
+ verify it. Importing the library does not inspect a source, open a private
87
+ packet, access a network, or start the CLI.
88
+
89
+ ## What becomes observable
90
+
91
+ After you add a source, ordinary commands expose pseudonymous contacts, source
92
+ health, conversation counts, sessions, response tempo, bubble sequences,
93
+ reactions, and explicit-reply coverage. Study and Ensoul commands can then write
94
+ bounded, body-bearing packets to paths you name. Profiles retain the exact
95
+ corpus revision and packet digest they came from, so stale evidence fails
96
+ visibly instead of being treated as current.
97
+
98
+ ## Supported sources
99
+
100
+ | Source | What Message Like Me reads | Boundary |
101
+ | --- | --- | --- |
102
+ | Apple Messages | The current macOS user's native `chat.db` history | Read-only ingestion from an ownership-checked stable local copy; Messages is never operated or changed. |
103
+ | X data archive | Direct-message history in a caller-owned archive ZIP | X Chat is not included; the importer does not contact X, extract the archive, or download media. |
104
+ | Beeper via Wrench | A bounded local bundle produced by verified Wrench v0.16.5 with Beeper adapter 2.3.0 and executable runtime 0.6.2 | Message Like Me owns zero Beeper operations, credentials, or live sessions; it reads the finished bundle, never sends, and does not claim complete history. |
105
+ | WhatsApp via Wrench | A one-account native bundle produced by Wrench v0.16.5 with official Wacli 0.15.0 | Reaction-shaped rows are omitted with `reaction-state-unproven` when current state cannot be proved. Message Like Me verifies the finished bundle and never operates WhatsApp. |
106
+ | macOS Contacts | Optional names and exact email or phone handles from AddressBook | Label enrichment only; Contacts is not a messaging-history source and is never changed. |
107
+
108
+ ## Add private local history
109
+
71
110
  On macOS, the default store is:
72
111
 
73
112
  ```text
@@ -142,12 +181,12 @@ same or a later archive preserves proven deduplication; archive absence does not
142
181
  delete retained history.
143
182
 
144
183
  To study accounts connected through Beeper, install the currently verified
145
- [`@hraness/wrench@0.16.1`](https://www.npmjs.com/package/@hraness/wrench/v/0.16.1)
184
+ [`@hraness/wrench@0.16.5`](https://www.npmjs.com/package/@hraness/wrench/v/0.16.5)
146
185
  package from npm, then use Wrench to create a new private Message Like Me
147
186
  bundle:
148
187
 
149
188
  ```sh
150
- bun add --global @hraness/wrench@0.16.1
189
+ bun add --global @hraness/wrench@0.16.5
151
190
  wrench beeper export-message-like-me \
152
191
  --auth <beeper-auth-id> \
153
192
  --output /absolute/private/path/beeper-bundle \
@@ -156,22 +195,32 @@ wrench beeper export-message-like-me \
156
195
 
157
196
  The optional `--limit-chats`, `--limit-messages`, and `--max-participants`
158
197
  flags lower the export bounds. The output path must be a normalized absolute
159
- path to a directory that does not already exist. Wrench v0.16.1 calls the
160
- pinned [official Beeper CLI 0.6.2 release](https://github.com/beeper/cli/releases/tag/v0%2E6%2E2)
161
- directly. It enumerates
198
+ path to a directory that does not already exist. Wrench v0.16.5 adapter
199
+ `beeper-local@2.3.0` exposes 32 reviewed Beeper operations: 27 use the pinned
200
+ CLI and 5 use fixed Desktop loopback reads. Message Like Me owns none of
201
+ those operations. The bundle command enters Wrench's separate internal bounded
202
+ export, which fixes the raw export arguments, excludes attachments, and
203
+ preserves explicit incomplete-coverage evidence instead of claiming full
204
+ history.
205
+
206
+ Wrench calls the pinned
207
+ [official Beeper CLI 0.6.2 release](https://github.com/beeper/cli/releases/tag/v0%2E6%2E2)
208
+ directly. The exact executable reports version `0.6.2`; the tagged source file
209
+ `packages/cli/package.json` declares `0.6.1`. That source value is provenance
210
+ only and never overrides the executable runtime identity. Wrench enumerates
162
211
  the connected account realm, invokes `export --no-attachments` once per
163
212
  account in deterministic order, and reports the account ordinal, elapsed-time
164
213
  heartbeats, and cumulative validated chat and message counts on stderr. It
165
214
  retains each private raw shard until it can atomically publish the complete
166
215
  mode-`0700` seven-file bundle with mode-`0600` files.
167
216
 
168
- The export does not use the separate
169
- [Beeper Desktop API MCP project](https://github.com/beeper/desktop-api-mcp).
170
- The CLI path supplies the bounded account snapshots and local files needed for
171
- hash validation, deterministic conversion, crash recovery, and atomic
172
- publication. Provider URLs and credentials are excluded. Message Like Me does
173
- not receive the Beeper credential, start Wrench, invoke a Beeper operation, or
174
- send a message.
217
+ Beeper Desktop also includes a
218
+ [built-in MCP server](https://developers.beeper.com/desktop-api/mcp/), but this
219
+ export path does not use it. The pinned CLI path supplies the bounded account
220
+ snapshots and local files needed for hash validation, deterministic conversion,
221
+ crash recovery, and atomic publication. Provider URLs and credentials are
222
+ excluded. Message Like Me does not receive the Beeper credential or live
223
+ session, start Wrench, invoke a Beeper operation, or send a message.
175
224
 
176
225
  Ingest the finished directory, then inspect its redacted source health:
177
226
 
@@ -190,7 +239,7 @@ iMessage and prior bundle sources remain alongside it.
190
239
  The interchange, integrity, identity, and reimport laws are in the
191
240
  [version-one local message bundle contract](docs/local-message-bundle-v1.md).
192
241
  Message Like Me accepts bundle schema `1` with source ID `beeper-local` and
193
- source-transform version `1.1.0`. Wrench v0.16.1 is the currently verified
242
+ source-transform version `1.1.0`. Wrench v0.16.5 is the currently verified
194
243
  producer. Compatibility is determined by those exact manifest coordinates,
195
244
  not by an open-ended Wrench package range.
196
245
 
@@ -201,11 +250,11 @@ reappearance restores it. Older snapshots cannot overwrite newer state. Use
201
250
  `sources show <source-id> --private --json` only when you deliberately need the
202
251
  private provider account and source metadata.
203
252
 
204
- For native WhatsApp evidence, install Wrench v0.16.3 and let its official
253
+ For native WhatsApp evidence, install Wrench v0.16.5 and let its official
205
254
  Wacli 0.15.0 adapter create the one-account v2 bundle:
206
255
 
207
256
  ```sh
208
- bun add --global @hraness/wrench@0.16.3
257
+ bun add --global @hraness/wrench@0.16.5
209
258
  wrench whatsapp export-message-like-me \
210
259
  --auth <whatsapp-auth-id> \
211
260
  --output /absolute/private/path/whatsapp-bundle \
@@ -224,6 +273,14 @@ broadcast, newsletter, credential, session-database, provider-URL, and media-byt
224
273
  surfaces are excluded. The complete contract is in
225
274
  [local message bundle v2](docs/local-message-bundle-v2.md).
226
275
 
276
+ Wacli v0.15.0 may retain an earlier emoji after a reaction is removed, so its
277
+ local rows cannot prove current active reaction state. Wrench v0.16.5 omits
278
+ every reaction-shaped row and adds `reaction-state-unproven` when it observes
279
+ one. An empty `reactions.ndjson` from this producer means reaction behavior was
280
+ unobservable, not that the account had no reactions. The v2 wire contract keeps
281
+ its fixed reaction artifact and strict parser for proven records; Message Like
282
+ Me imports only the records the producer can establish.
283
+
227
284
  If a Beeper WhatsApp source already represents the same exact account, inspect
228
285
  the redacted source inventory and name it explicitly:
229
286
 
@@ -239,7 +296,8 @@ unambiguous shared text-message fingerprint. Groups, bodyless messages, names,
239
296
  phone suffixes, and approximate timestamps cannot prove equivalence. Both
240
297
  provenances and source-unique history remain. Native Wacli evidence becomes the
241
298
  preferred `whatsappJid` route; its proven Beeper duplicate remains
242
- `evidence-only` with reason `superseded-route`.
299
+ `evidence-only` with reason `superseded-route`. This producer supplies no native
300
+ reaction records, so overlap cannot reconcile reaction state.
243
301
 
244
302
  Optionally enrich and join direct conversations with private identities from
245
303
  macOS Contacts:
@@ -548,6 +606,22 @@ context support them.
548
606
  Drafting ends with text in the agent task. Message Like Me has no send, react,
549
607
  schedule, or messaging-application command.
550
608
 
609
+ ## Find the right documentation
610
+
611
+ - **Install and prove the local boundary:** use [Install and first run](#install-and-first-run).
612
+ - **Add evidence:** choose a path under [Supported sources](#supported-sources),
613
+ then follow [Add private local history](#add-private-local-history).
614
+ - **Study or draft:** inspect behavior, prepare a study packet, build a profile,
615
+ audit it against later conversations, then draft an unsent reply using the
616
+ task sections above.
617
+ - **Review evidence and safety claims:** read the
618
+ [methodology](docs/methodology.md), [research review](docs/research.md), and
619
+ [security policy](SECURITY.md).
620
+ - **Integrate a producer:** use the versioned
621
+ [Beeper bundle](docs/local-message-bundle-v1.md),
622
+ [WhatsApp bundle](docs/local-message-bundle-v2.md), and public JSON Schemas
623
+ instead of inferring a wire format from examples.
624
+
551
625
  ## Command reference
552
626
 
553
627
  Run `messagelikeme --help` for the checked grammar. The public surfaces are:
@@ -594,7 +668,7 @@ messagelikeme doctor [--json]
594
668
 
595
669
  Place global `--data-dir PATH` before the command.
596
670
 
597
- ## Privacy model
671
+ ## Privacy, security, and limitations
598
672
 
599
673
  - The original `chat.db` and AddressBook databases remain authoritative.
600
674
  SQLite opens only stable private copies, never the source files or sidecars.
@@ -617,6 +691,9 @@ Place global `--data-dir PATH` before the command.
617
691
  data handling you accept; the CLI cannot make a hosted agent local.
618
692
  - Public fixtures are synthetic. Private corpora, profiles, packets, and drafts
619
693
  do not belong in Git, issues, logs, packages, or examples.
694
+ - Message Like Me does not train a model, represent your identity, infer your
695
+ beliefs, or claim that a draft is what you would have written. Current facts,
696
+ meaning, and intent remain the user's responsibility.
620
697
  - A draft is never sent.
621
698
 
622
699
  Read [SECURITY.md](SECURITY.md) before integrating the library into another
@@ -675,18 +752,26 @@ import type {
675
752
  The library does not start the CLI, inspect Messages, Contacts, or an X archive,
676
753
  connect to a network, or send a draft merely because it is imported.
677
754
 
678
- ## Development
755
+ ## Verify a checkout
679
756
 
680
757
  ```sh
681
758
  bun install --frozen-lockfile --ignore-scripts
682
759
  bun run check
683
760
  ```
684
761
 
762
+ The complete gate type-checks the project, runs the synthetic test suite,
763
+ validates both bundled Agent Skills and the standalone public boundary,
764
+ rebuilds `dist/`, checks the committed build, and exercises a packed consumer.
765
+
766
+ ## Develop and contribute
767
+
685
768
  Tests use synthetic Messages and AddressBook databases, synthetic X archive
686
769
  ZIPs, and synthetic source bundles and conversations. Never add a real message,
687
770
  handle, group title, attachment, contact record, private path, or derived
688
771
  profile to a fixture.
689
772
 
773
+ Read [CONTRIBUTING.md](CONTRIBUTING.md) before opening a change.
774
+
690
775
  The canonical repository is
691
776
  [`hraness/message-like-me`](https://github.com/hraness/message-like-me).
692
777
  The informational project page is
package/dist/cli.js CHANGED
@@ -6214,7 +6214,7 @@ class LocalStore {
6214
6214
  }
6215
6215
 
6216
6216
  // src/version.ts
6217
- var MESSAGE_LIKE_ME_VERSION = "0.8.0";
6217
+ var MESSAGE_LIKE_ME_VERSION = "0.8.1";
6218
6218
 
6219
6219
  // src/x-archive.ts
6220
6220
  import { createHash as createHash4 } from "crypto";
@@ -8,11 +8,11 @@ importer never receives provider credentials, starts Wrench, invokes a Beeper
8
8
  operation, or sends a message.
9
9
 
10
10
  The currently verified producer is the local Beeper export in the
11
- [`@hraness/wrench@0.16.1`](https://www.npmjs.com/package/@hraness/wrench/v/0.16.1)
11
+ [`@hraness/wrench@0.16.5`](https://www.npmjs.com/package/@hraness/wrench/v/0.16.5)
12
12
  npm package:
13
13
 
14
14
  ```sh
15
- bun add --global @hraness/wrench@0.16.1
15
+ bun add --global @hraness/wrench@0.16.5
16
16
  wrench beeper export-message-like-me \
17
17
  --auth <beeper-auth-id> \
18
18
  --output <normalized-absolute-new-directory> \
@@ -31,13 +31,26 @@ express.
31
31
  ## Compatibility coordinates
32
32
 
33
33
  Message Like Me accepts schema version `1` with source ID `beeper-local` and
34
- source-transform version `1.1.0`. Wrench v0.16.1 emits those coordinates with
35
- the pinned Beeper CLI version `0.6.2`. A later Wrench package release remains
36
- compatible only while its manifest still declares that schema, source ID, and
37
- `source.version: "1.1.0"`; package age or a permissive version range never
38
- overrides the manifest coordinates. The provider version records the pinned
39
- Beeper CLI used for capture and may change without changing the bundle
40
- contract.
34
+ source-transform version `1.1.0`. Wrench v0.16.5 emits those coordinates through
35
+ adapter `beeper-local@2.3.0`. That adapter has 32 reviewed Beeper operations:
36
+ 27 use the pinned CLI and 5 use fixed Desktop loopback reads. The Message
37
+ Like Me bundle is made by Wrench's separate internal bounded export, not by a
38
+ Message Like Me provider operation. It fixes the raw export arguments, excludes
39
+ attachments, and preserves incomplete-coverage evidence. It does not claim a
40
+ complete Beeper history.
41
+
42
+ The pinned Beeper CLI executable reports version `0.6.2`. At the corresponding
43
+ source tag, `packages/cli/package.json` declares `0.6.1`; that source value is
44
+ provenance only and never overrides executable runtime identity. A later Wrench
45
+ package release remains compatible only while its manifest still declares the
46
+ same bundle schema, source ID, and `source.version: "1.1.0"`. Package age,
47
+ adapter version, or a permissive package range never overrides those manifest
48
+ coordinates. The provider version records the pinned Beeper CLI used for
49
+ capture and may change without changing the bundle contract.
50
+
51
+ Message Like Me owns zero Beeper operations, credentials, or live sessions. It
52
+ does not start Wrench, call the provider, or support sending. Its authority
53
+ begins at strict verification of the already finished private directory.
41
54
 
42
55
  The dependency-free package subpath is the executable contract authority for
43
56
  producers and consumers:
@@ -10,7 +10,7 @@ network, or sends a message.
10
10
  The intended producer flow is:
11
11
 
12
12
  ```sh
13
- bun add --global @hraness/wrench@0.16.3
13
+ bun add --global @hraness/wrench@0.16.5
14
14
  wrench whatsapp export-message-like-me \
15
15
  --auth <whatsapp-auth-id> \
16
16
  --output /absolute/private/path/whatsapp-bundle
@@ -20,10 +20,16 @@ messagelikeme ingest bundle \
20
20
  --json
21
21
  ```
22
22
 
23
- The checked compatibility coordinates are Wrench v0.16.3 and official Wacli
23
+ The checked compatibility coordinates are Wrench v0.16.5 and official Wacli
24
24
  v0.15.0. Wrench owns that executable dependency and its authentication state;
25
25
  neither enters Message Like Me.
26
26
 
27
+ That exact producer excludes every reaction-shaped Wacli row. Wacli v0.15.0
28
+ may retain an earlier emoji after a removal, so the local projection cannot
29
+ durably establish current active reaction state. A bundle that encountered any
30
+ such row carries the categorical `reaction-state-unproven` warning. This is an
31
+ observability limit, not evidence that no reactions occurred.
32
+
27
33
  The normative object schema is
28
34
  [`schema/local-message-bundle-v2.schema.json`](../schema/local-message-bundle-v2.schema.json).
29
35
  The runtime parser is stricter than JSON Schema where byte length, canonical
@@ -82,6 +88,11 @@ changes, invalid UTF-8, blank or oversized records, noncanonical JSON, count or
82
88
  byte disagreement, and SHA-256 disagreement. The same public bounds as v1
83
89
  apply, except v2 admits exactly one account.
84
90
 
91
+ The v2 wire contract retains the fixed `reactions.ndjson` artifact and strict
92
+ reaction parser for proven records. The checked Wrench v0.16.5/Wacli v0.15.0
93
+ producer leaves that artifact empty because it cannot prove current reaction
94
+ state.
95
+
85
96
  `manifest.json` is canonical JSON followed by one newline. Every NDJSON record
86
97
  is one canonical JSON object followed by one newline. The manifest integrity
87
98
  digest covers its canonical projection without the `integrity` member;
@@ -112,8 +123,10 @@ matches.
112
123
  Every message has a proven incoming or outgoing direction. Direct-message
113
124
  senders are proven. A group or system row may retain a null sender when the
114
125
  bounded local observation cannot prove one, and it cannot establish overlap.
115
- Reply, edit, deletion, reaction, attachment, and tombstone fields retain the
116
- same strict meanings as v1. Attachment bytes, provider URLs, credentials, Wacli
126
+ Reply, edit, deletion, attachment, and tombstone fields retain the same strict
127
+ meanings as v1. The contract also defines reaction records, but the checked
128
+ producer emits none and reports `reaction-state-unproven` when it encounters a
129
+ reaction-shaped row. Attachment bytes, provider URLs, credentials, Wacli
117
130
  session state, database paths, and unmodeled provider payloads are excluded.
118
131
  Unsupported status, broadcast, and newsletter records are not represented as
119
132
  ordinary conversations.
@@ -148,11 +161,13 @@ messages, groups, names, phone suffixes, approximate timestamps, and ambiguous
148
161
  duplicates cannot prove equivalence.
149
162
 
150
163
  Both source provenances and all source-unique history remain stored. Proven
151
- message and reaction duplicates contribute once. The native Wacli conversation
152
- is the preferred action route and carries the exact private `whatsappJid`
153
- coordinate. Its proven Beeper duplicate remains evidence with reason
154
- `superseded-route`. Reimport rechecks the named proof atomically and fails
155
- closed on disagreement.
164
+ message duplicates contribute once. A reaction can deduplicate only when a
165
+ conforming producer supplies a proven reaction record; Wrench v0.16.5 supplies
166
+ none, so this overlap path does not reconcile reaction state. The native Wacli
167
+ conversation is the preferred action route and carries the exact private
168
+ `whatsappJid` coordinate. Its proven Beeper duplicate remains evidence with
169
+ reason `superseded-route`. Reimport rechecks the named proof atomically and
170
+ fails closed on disagreement.
156
171
 
157
172
  ## Privacy and action boundary
158
173