| … | … | @@ -419,6 +419,64 @@ design does not pretend otherwise. |
| 419 | 419 | tracked relationship: one rkey, two collections, cascade on delete and edit. |
| 420 | 420 | - **It is confined to public posts.** Fediverse-only and local-only posts write one record. |
| 421 | 421 | |
| 422 | ### 7.1 ★ The criterion — and it is not N× |
| 423 | |
| 424 | **Decision (user, 2026-09-05).** The question was never "two records or one", it is **which |
| 425 | projections are stored at all**: |
| 426 | |
| 427 | > **Store a projection when the target network commits to its BYTES. Derive it when the network |
| 428 | > merely receives them.** |
| 429 | |
| 430 | | network | what it does with the projection | stored? | |
| 431 | |---|---|---| |
| 432 | | **atproto** | its CID goes in the **signed MST** — the repo *is* the published artifact | **yes** | |
| 433 | | **ActivityPub** | an AS2 object **delivered** to inboxes; no signature over the body, no content address | no — derived | |
| 434 | | IRC / Discord / Matrix / email (`hyper-social/bridge-*`) | a message **delivery** | no — derived | |
| 435 | |
| 436 | So the cost is **1 canonical + 1 stored projection**, not one per bridged network. Adding a bridge |
| 437 | adds **zero** files. Measured on wj62's live store: 188 posts today → 376 files after the pivot, not |
| 438 | eight times that. |
| 439 | |
| 440 | The criterion generalises rather than special-casing atproto: a future network that content-addresses |
| 441 | what it publishes would store too. |
| 442 | |
| 443 | ### 7.2 Why STORED and not derived on every sign |
| 444 | |
| 445 | Derivation is **not** a pure function of the record. Truncation at 300 graphemes (§4.3) needs |
| 446 | grapheme segmentation, which `markdown-strip.md` §9 leaves unpinned and which depends on **the |
| 447 | Unicode tables of whatever runtime is executing**. Derive on every sign and: |
| 448 | |
| 449 | - two peers on different Node/Bare versions derive different bytes → different CIDs → **a fork**; |
| 450 | - the same peer after a runtime upgrade re-derives different bytes → **the signed history silently |
| 451 | rewrites**. Nobody decided that; it arrives with a package upgrade. |
| 452 | |
| 453 | ⇒ **A stored projection is a RECEIPT, not a cache.** Once bytes are signed and published they are a |
| 454 | fact, not a derivation. Re-deriving them later is not reconstruction, it is re-guessing — §8's *"never |
| 455 | re-encode a record you did not author"*, applied across **time** rather than across authors. |
| 456 | |
| 457 | **One choice closes three mechanisms**, all of which existed only to make *repeated derivation* agree: |
| 458 | |
| 459 | | closed | it existed because | |
| 460 | |---|---| |
| 461 | | per-record `dialect` versioning (hyper-social #43) | a dialect bump re-stripped every record | |
| 462 | | pinned grapheme segmentation as a **consensus** hazard (`library.md` §10.4) | two runtimes truncate differently | |
| 463 | | `STRIP_VERSION` as an in-band **signer gate** (`library.md` §10.3) | *"the lease-holder must strip before it encodes"* | |
| 464 | |
| 465 | The strip now runs **once, at authoring, on the author's device**, and leaves the signing path |
| 466 | entirely. A signer materialises the projection from a file and strips nothing. |
| 467 | |
| 468 | Re-derivation still happens in exactly three places, and all three are **authored acts**: an edit, a |
| 469 | deliberate migration of legacy records, and a new bridge building its own projection. |
| 470 | |
| 471 | ⚠ **Residual, and it is minor.** A user editing from a second device with a different Unicode version |
| 472 | may truncate differently than the original. That is an authored act, not a fork — surprising, not |
| 473 | corrupting. |
| 474 | |
| 475 | ⚠ **The weaker AP variant is accepted, not solved.** Bump the dialect and a server re-fetching |
| 476 | `/ap/objects/<rkey>` gets different text than it cached. No signature, no content address, and |
| 477 | Mastodon largely will not refetch without an `Update` carrying `updated`. If it ever matters the fix |
| 478 | is the same one applied selectively; it is not worth paying for now. |
| 479 | |
| 422 | 480 | **What the implementation MUST guarantee:** |
| 423 | 481 | |
| 424 | 482 | 1. Delete of a canonical record deletes its projection in the same batch. A tombstone for one and |