astp-docs-server 0.1.0__py3-none-any.whl

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.
Files changed (42) hide show
  1. astp_docs_server/__init__.py +12 -0
  2. astp_docs_server/__main__.py +8 -0
  3. astp_docs_server/atlas_manifest.py +108 -0
  4. astp_docs_server/corpus/open/CHANGELOG.md +568 -0
  5. astp_docs_server/corpus/open/CONFORMANCE-BFM.md +430 -0
  6. astp_docs_server/corpus/open/CONFORMANCE-CONTEXT.md +332 -0
  7. astp_docs_server/corpus/open/CONFORMANCE-CROSS-EPISODE-LINKING.md +258 -0
  8. astp_docs_server/corpus/open/CONFORMANCE-LAYER3.md +272 -0
  9. astp_docs_server/corpus/open/CONFORMANCE-REPRODUCIBILITY.md +219 -0
  10. astp_docs_server/corpus/open/CONFORMANCE-TRUST.md +639 -0
  11. astp_docs_server/corpus/open/GLOSSARY.md +661 -0
  12. astp_docs_server/corpus/open/GOVERNANCE.md +43 -0
  13. astp_docs_server/corpus/open/IMPLEMENTATION-CONTEXT.md +121 -0
  14. astp_docs_server/corpus/open/IMPLEMENTATION-PHASE3.md +732 -0
  15. astp_docs_server/corpus/open/LICENSE.txt +202 -0
  16. astp_docs_server/corpus/open/NOTICE +60 -0
  17. astp_docs_server/corpus/open/PATENTS.md +325 -0
  18. astp_docs_server/corpus/open/PROTOCOL-CONFORMANCE.md +470 -0
  19. astp_docs_server/corpus/open/RATIFICATION-5.0.0.md +56 -0
  20. astp_docs_server/corpus/open/RATIFICATION-6.0.0.md +71 -0
  21. astp_docs_server/corpus/open/RATIFICATION-PATENTS-1.0.0.md +116 -0
  22. astp_docs_server/corpus/open/RATIFICATION-PROTOCOL-CONFORMANCE-1.0.0.md +145 -0
  23. astp_docs_server/corpus/open/RATIFICATION-SPEC-6.0.2.md +136 -0
  24. astp_docs_server/corpus/open/README.md +230 -0
  25. astp_docs_server/corpus/open/SPEC.md +3220 -0
  26. astp_docs_server/corpus/open/VERSIONING.md +105 -0
  27. astp_docs_server/corpus/open/vectors/5.0.0/seal-constructions.json +402 -0
  28. astp_docs_server/corpus/open/vectors/6.0.0/context-commitment.json +353 -0
  29. astp_docs_server/corpus/open/vendor_manifest.json +31 -0
  30. astp_docs_server/mcp_server.py +145 -0
  31. astp_docs_server/open_corpus.py +235 -0
  32. astp_docs_server/vendor_manifest.py +124 -0
  33. astp_docs_server/web/__init__.py +7 -0
  34. astp_docs_server/web/__main__.py +5 -0
  35. astp_docs_server/web/answerer.py +307 -0
  36. astp_docs_server/web/app.py +151 -0
  37. astp_docs_server-0.1.0.dist-info/METADATA +107 -0
  38. astp_docs_server-0.1.0.dist-info/RECORD +42 -0
  39. astp_docs_server-0.1.0.dist-info/WHEEL +4 -0
  40. astp_docs_server-0.1.0.dist-info/entry_points.txt +3 -0
  41. astp_docs_server-0.1.0.dist-info/licenses/LICENSE.txt +202 -0
  42. astp_docs_server-0.1.0.dist-info/licenses/NOTICE +72 -0
@@ -0,0 +1,332 @@
1
+ # ASTP — Context-Commitment Conformance Test Vectors
2
+
3
+ **Version:** 1.0.0
4
+ **Status:** Stable — ratified with SPEC 6.0.0 in Episode of Record `80e5a2dd-3d9f-45d0-abfb-6489c8caf1b8` ([`docs/RATIFICATION-6.0.0.md`](./docs/RATIFICATION-6.0.0.md)); the values were generated while 6.0.0 was a draft and none changed at ratification
5
+ **Authors:** Scorched Earth Labs
6
+ **Date:** 2026-09-22
7
+ **Applies To:** SPEC.md 6.0.0 — §4.8 ContextEntryNode, §4.9 Erasure and ErasureTombstone, §5.7 Episode Root version 3, §5.7.3 Context Manifest, §5.8 `spine_algorithm_version` 3, §9.2–§9.3 as extended, G-41, G-42, G-43.
8
+ **Companions:** [CONFORMANCE-REPRODUCIBILITY.md](CONFORMANCE-REPRODUCIBILITY.md) (§5.6–§5.8, `spine_algorithm_version` 0–2), [CONFORMANCE-TRUST.md](CONFORMANCE-TRUST.md) (§16), [CONFORMANCE-LAYER3.md](CONFORMANCE-LAYER3.md) (§21).
9
+
10
+ ---
11
+
12
+ ## 1. Overview
13
+
14
+ These vectors test the one thing 6.0.0 adds — a sealed commitment to what an Episode's agents were *given* — and the one thing it states: **the seal proves history, not retention.** Every expected value below is pinned in [`vectors/6.0.0/context-commitment.json`](./vectors/6.0.0/context-commitment.json), generated by `vectors/6.0.0/generate.py` and never edited by hand, and is reproduced twice in `tests/conformance/test_context_commitment_v1_vectors.py`: once by the reference package and once by a `hashlib`-only implementation written from the SPEC text that imports nothing from `astp`.
15
+
16
+ The Episode is the one the ratified 5.0.0 vectors seal (`550e8400-e29b-41d4-a716-446655440000`, its seven Segments, five Signals and seven structural members), so the version 2 roots here are the ratified values byte for byte. A conforming implementation MUST pass all vectors marked **REQUIRED**. Format follows [CONFORMANCE-TRUST.md §1](CONFORMANCE-TRUST.md); hash values are lowercase hex and enter every construction as 32 raw bytes under the §5.1.1 field encoding.
17
+
18
+ Reference implementation: `astp/core/context_v1.py`; `astp.core.seal_v2.reproduce_episode_root` dispatches version 3.
19
+
20
+ **The shared inputs.** Seven context entries on the base Episode, provided at one-minute intervals from `2026-01-01T00:01:00.123Z`; identifiers are fixed for reproducibility and MUST be random in practice (§5.2). The salt is fixed for the same reason and MUST be 32 CSPRNG bytes in practice (§5.3).
21
+
22
+ | # | `entry_id` | `entry_type` | `capture_state` | `salted` | `verifiability_at_seal` | `entry_hash` |
23
+ |---|---|---|---|---|---|---|
24
+ | 1 | `…0200` | attachment | captured | false | verifiable | `b08d864e0d6d41321cecbd90483ee1396ece80368f72ca496f772dfd04f85663` |
25
+ | 2 | `…0201` | retrieval | captured | false | attested | `a4dabb6716c72fc11c6e2a3cc2569c67c807c7b4c2aa882fb0dfb7ac4ef3f885` |
26
+ | 3 | `…0202` | retrieval (low-entropy personal data) | captured | **true** | verifiable | `4f45678a1893131e24d4afddd1350fbfcc7ce3f7cb5fe4ec893c76e58ff92704` |
27
+ | 4 | `…0203` | external | captured | false | verifiable | `e88d185230ffbe206d333f59f4156fb7337b5ced90083e9b704727c73dea0397` |
28
+ | 5 | `…0204` | tool_output | captured | false | verifiable | `956432ad9964673fced63541a9e1e6c3bf864ac5452a3d9f393bf60c6b8a5572` |
29
+ | 6 | `…0205` | retrieval | **declared_incomplete** | false | — | `0dbaaff8a22a5d772c49350bb94fc6fa825a635660fb128c2420bdff070d4cc3` |
30
+ | 7 | `…0206` | retrieval, `resolves` #6 | captured | false | verifiable | `eb174452baa2d2eb6ba47329dec7af36150cc07c669cd3fc92c5cb696102a643` |
31
+
32
+ Full field values are in the vector file's `entries` list. The base Episode's version 2 components: `spine_root` `4420e38a22409317822ac14e3d5bc8069e6556c0fc08928290f76d23396daa01`, `signal_manifest_hash` `70dbc8e8f2e87b63e49187979b14ad56ba11219b42be132015c23dcad36acb43`, `structural_manifest_hash` `0a4b777227b87ee0d6c8efd5b2e9a5fc05c02e61d677f2460f868ab06779b537`, `exclusion_hash` `ddea405cc1729ebf63d93b16d723873ad02f9f64af3cc159bf86e5f999b02ce1`, `episode_root_hash` (version 2) `f0511b4d1be172554b9f87ec64d400d24a1409f1742ad72f628bf5ab7b7d33f0`.
33
+
34
+ ---
35
+
36
+ ## 2. Content Commitment (§4.8.3)
37
+
38
+ **CC-001** — Plain Content Commitment
39
+ - **Class:** REQUIRED
40
+ - **Spec Reference:** §4.8.3
41
+ - **Description:** `CONTEXT_CONTENT:v1:` over the bytes as provided, under the BYTES field encoding (tag `01`, u32 length, bytes).
42
+ - **Inputs:**
43
+ ```
44
+ content (hex): 255044462d312e370a312030206f626a203c3c202f54797065202f436174616c6f67203e3e20656e646f626a0a2525454f460a
45
+ construction: SHA3-256("CONTEXT_CONTENT:v1:" ‖ BYTES(content))
46
+ ```
47
+ - **Expected Output:**
48
+ ```
49
+ content_hash: 9fbce78d71635571ef48ac04c99f1f340ac5141749f62bde5a77b425192d0baf
50
+ ```
51
+ - **Failure Condition:** Any other value; a hash over the hex text of the content or over an extraction of it.
52
+
53
+ **CC-002** — Salted Content Commitment
54
+ - **Class:** REQUIRED
55
+ - **Spec Reference:** §4.8.3, G-42
56
+ - **Description:** `CONTEXT_CONTENT_SALTED:v1:` over the 32-byte salt and then the bytes; the salt is a separate BYTES field, so the boundary between salt and content cannot be moved.
57
+ - **Inputs:**
58
+ ```
59
+ content (utf-8): 1979-03-14
60
+ salt (hex): 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
61
+ construction: SHA3-256("CONTEXT_CONTENT_SALTED:v1:" ‖ BYTES(salt) ‖ BYTES(content))
62
+ ```
63
+ - **Expected Output:**
64
+ ```
65
+ content_hash (salted): f94cc9f2c956d78e7a1c9c17ac6ae74580da6a8cb8256d923f012afa8b0e501a
66
+ content_hash (plain, for contrast — what G-42 forbids for this content): 4ae9d00037107b8d1ed647f47bf2e4c97311929b4c5d6dd2a57b678fffb5c772
67
+ ```
68
+ - **Failure Condition:** Any other value; a salt of any length but 32 accepted; the plain construction used for content classified low-entropy personal data.
69
+
70
+ ---
71
+
72
+ ## 3. Entry Hash (§5.7.3)
73
+
74
+ **CM-002** — Entry Hash Binds Its Claims and Nothing Else
75
+ - **Class:** REQUIRED
76
+ - **Spec Reference:** §5.7.3, §4.8.6
77
+ - **Description:** the preimage is the thirteen fields in the stated order; `source_ref`, `content_ref`, `salt_ref`, `erasure_state` and any implementation classification are outside it. Changing any preimage field changes the hash; changing any side-channel field does not.
78
+ - **Inputs:**
79
+ ```
80
+ preimage: entry_id UUID ‖ episode_id UUID ‖ entry_type STRING ‖ provided_to STRING ‖ provided_at TIMESTAMP ‖ provided_before_sequence_index UINT|NULL ‖ capture_state STRING ‖ content_hash HASH|NULL ‖ salted BOOL ‖ verifiability_at_seal STRING|NULL ‖ media_type STRING|NULL ‖ source_version_hash HASH|NULL ‖ resolves_entry_hash HASH|NULL
81
+ entry #1 (attachment): see vectors "entries"[0]
82
+ ```
83
+ - **Expected Output:**
84
+ ```
85
+ entry_hash: b08d864e0d6d41321cecbd90483ee1396ece80368f72ca496f772dfd04f85663
86
+ unchanged when source_ref, content_ref, salt_ref, erasure_state change
87
+ changed when any preimage field changes (each tested)
88
+ ```
89
+ - **Failure Condition:** A side-channel field moving the hash (erasure would then move a sealed preimage); a preimage field not moving it.
90
+
91
+ **CM-002a** — Declared-Incomplete Entry
92
+ - **Class:** REQUIRED
93
+ - **Spec Reference:** §4.8.5, §5.7.3
94
+ - **Description:** NULL `content_hash`, `salted` false, NULL `verifiability_at_seal`, encoded as NULL fields (`00`); a captured entry MUST carry a content hash and a verifiability state.
95
+ - **Inputs:**
96
+ ```
97
+ entry #6: capture_state=declared_incomplete, content_hash=NULL, salted=false, verifiability_at_seal=NULL, media_type=NULL
98
+ ```
99
+ - **Expected Output:**
100
+ ```
101
+ entry_hash: 0dbaaff8a22a5d772c49350bb94fc6fa825a635660fb128c2420bdff070d4cc3
102
+ ```
103
+ - **Failure Condition:** A declared-incomplete entry accepted with content, or a captured entry accepted without verifiability.
104
+
105
+ ---
106
+
107
+ ## 4. Context Manifest (§5.7.3)
108
+
109
+ **CM-003** — Manifest Is Order-Independent; Removal and Posture Change It
110
+ - **Class:** REQUIRED
111
+ - **Spec Reference:** §5.7.3
112
+ - **Description:** the tree is the §5.4 version 2 tree over entry hashes sorted ascending bytewise; the manifest hash binds the posture and the count with the tree root.
113
+ - **Inputs:**
114
+ ```
115
+ entry hashes: the seven above, in any insertion order
116
+ capture_posture: declared_only
117
+ construction: SHA3-256("CONTEXT_MANIFEST:v1:" ‖ STRING(posture) ‖ UINT(n) ‖ HASH|NULL(context_tree_root))
118
+ ```
119
+ - **Expected Output:**
120
+ ```
121
+ context_tree_root (sorted leaves): e730a2153f39eb61b27888528000ffdc0b88a94a24f3daf29300126a9d42acc1
122
+ sorted leaf order (entry_id suffixes): 0205, 0202, 0204, 0201, 0200, 0203, 0206
123
+ context_manifest_hash (declared_only, 7): 94e7681209cb0315427622aee9b22abf832e5e35ba653cf7e692faa413e75df7
124
+ same entries, reverse insertion order: 94e7681209cb0315427622aee9b22abf832e5e35ba653cf7e692faa413e75df7
125
+ without entry #2: 54a733a5c6592d150f5374cb1327a44a72e2fd45a67ca20d07bda2a4cb4d5004
126
+ posture all_external instead: 68c1bc568cd1b4967dc8214a450dbb2e8a185841cddd76c5472451dfa70dcf92
127
+ ```
128
+ - **Failure Condition:** Insertion order changing the hash; a removed entry or a changed posture leaving it unchanged.
129
+
130
+ **CM-001** — Empty Manifest Is Defined
131
+ - **Class:** REQUIRED
132
+ - **Spec Reference:** §5.7.3, §5.7
133
+ - **Description:** an Episode with zero context entries composes `n = 0` and a NULL root, seals under version 3 and reproduces from stored state.
134
+ - **Inputs:**
135
+ ```
136
+ entries: none
137
+ capture_posture: none
138
+ ```
139
+ - **Expected Output:**
140
+ ```
141
+ context_manifest_hash (none, 0): 66be9e2d4febffe1afb2afb2e388aec945777f4f8b1ef58c1a5a234ea9eedb5d
142
+ context_manifest_hash (declared_only, 0): 0bcf1339549cd0673d1776d198b856dba92987798b0ba9c9838ee1a936076e7d
143
+ episode_root_hash (version 3, posture none): 490fb1f566a5ef75640f54d6d5550bd7706073809902d18348a870f188fd6882
144
+ ```
145
+ - **Failure Condition:** A version 3 seal refused for an Episode without entries; a tree root computed for zero leaves; the two postures giving one hash.
146
+
147
+ ---
148
+
149
+ ## 5. Episode Root Version 3 (§5.7, §5.8)
150
+
151
+ **ER3-001** — Six-Field Root Selected by `spine_algorithm_version` 3
152
+ - **Class:** REQUIRED
153
+ - **Spec Reference:** §5.7, §5.8
154
+ - **Description:** the version 2 root's five fields unchanged, then `context_manifest_hash`; the spine root is byte-identical to version 2.
155
+ - **Inputs:**
156
+ ```
157
+ episode_id: 550e8400-e29b-41d4-a716-446655440000
158
+ spine_root: 4420e38a22409317822ac14e3d5bc8069e6556c0fc08928290f76d23396daa01
159
+ signal_manifest_hash: 70dbc8e8f2e87b63e49187979b14ad56ba11219b42be132015c23dcad36acb43
160
+ structural_manifest_hash: 0a4b777227b87ee0d6c8efd5b2e9a5fc05c02e61d677f2460f868ab06779b537
161
+ exclusion_hash: ddea405cc1729ebf63d93b16d723873ad02f9f64af3cc159bf86e5f999b02ce1
162
+ context_manifest_hash: 94e7681209cb0315427622aee9b22abf832e5e35ba653cf7e692faa413e75df7
163
+ construction: SHA3-256("EPISODE_ROOT:v3:" ‖ UUID ‖ HASH ‖ HASH ‖ HASH ‖ HASH ‖ HASH)
164
+ ```
165
+ - **Expected Output:**
166
+ ```
167
+ episode_root_hash: 22675041b1af6243fe8c72f318ca853a6e775073355c718116c9ac0d19fe0e88
168
+ seal record identifiers: spine_algorithm_version 3, ordering_version 2, hash_version 2
169
+ ```
170
+ - **Failure Condition:** Any other value; a separate root-version identifier on the record (§5.8: one identifier selects the whole construction).
171
+
172
+ **CM-010** — A Version 2 Seal Is Unchanged by Context Entries
173
+ - **Class:** REQUIRED
174
+ - **Spec Reference:** §5.7, §5.8
175
+ - **Description:** context entries are members of no version 2 root; the same Episode, holding the seven entries, seals under version 2 to the ratified 5.0.0 value.
176
+ - **Inputs:**
177
+ ```
178
+ the base Episode with the seven entries present
179
+ spine_algorithm_version: 2
180
+ ```
181
+ - **Expected Output:**
182
+ ```
183
+ episode_root_hash (version 2): f0511b4d1be172554b9f87ec64d400d24a1409f1742ad72f628bf5ab7b7d33f0 (= vectors/5.0.0 episode_root_v2.hash)
184
+ episode_root_hash (version 3): 22675041b1af6243fe8c72f318ca853a6e775073355c718116c9ac0d19fe0e88 (differs)
185
+ ```
186
+ - **Failure Condition:** A version 2 root that changes when entries exist; a version 3 root equal to the version 2 root.
187
+
188
+ ---
189
+
190
+ ## 6. Inclusion Proofs (§9.2)
191
+
192
+ **CM-004** — One Entry Proven Without the Others
193
+ - **Class:** REQUIRED
194
+ - **Spec Reference:** §9.2, §5.7.3
195
+ - **Description:** the §9.2 proof over the context tree; `leaf_index` is the entry's position in the sorted leaf list; the verifier reproduces the tree root, then the manifest hash from posture and count, then the Episode root.
196
+ - **Inputs:**
197
+ ```
198
+ entry: #3 (entry_hash 4f45678a1893131e24d4afddd1350fbfcc7ce3f7cb5fe4ec893c76e58ff92704)
199
+ leaf_index: 1 leaf_count: 7
200
+ siblings: ["5c2e34164e93e1de4d62b5b498cb1c6cfb5e456627f26cae21e1ad0140228ae9", "f6d2ee6739f138deff2724d247ae8ceb5e70b112f1b74bbd5d929af5cff06429", "d5c030cda42f4363eac47a200e2055334f4507f42de83ca1fb3b1373c6cc2f08"]
201
+ capture_posture: declared_only entry_count: 7
202
+ ```
203
+ - **Expected Output:**
204
+ ```
205
+ context_tree_root: e730a2153f39eb61b27888528000ffdc0b88a94a24f3daf29300126a9d42acc1
206
+ context_manifest_hash: 94e7681209cb0315427622aee9b22abf832e5e35ba653cf7e692faa413e75df7
207
+ episode_root_hash: 22675041b1af6243fe8c72f318ca853a6e775073355c718116c9ac0d19fe0e88
208
+ ```
209
+ - **Failure Condition:** The proof verifying at any other leaf_index, under any other posture, or for any other count.
210
+
211
+ ---
212
+
213
+ ## 7. Append-Only Repair (§4.8.5)
214
+
215
+ **CM-005** — A Resolving Entry Binds the Resolved Entry's Hash
216
+ - **Class:** REQUIRED
217
+ - **Spec Reference:** §4.8.5, §5.7.3
218
+ - **Description:** entry #7 carries `resolves` = entry #6; its preimage's last field is #6's entry hash; #6 is unchanged.
219
+ - **Inputs:**
220
+ ```
221
+ resolved (declared_incomplete) entry_id: …0205
222
+ resolving entry_id: …0206, resolves = …0205
223
+ ```
224
+ - **Expected Output:**
225
+ ```
226
+ resolved entry_hash: 0dbaaff8a22a5d772c49350bb94fc6fa825a635660fb128c2420bdff070d4cc3
227
+ resolving entry_hash: eb174452baa2d2eb6ba47329dec7af36150cc07c669cd3fc92c5cb696102a643
228
+ ```
229
+ - **Failure Condition:** The resolving entry's hash unchanged when the resolved entry's hash changes; a `resolves` naming an entry outside the Episode, a captured entry, or an entry that itself resolves.
230
+
231
+ ---
232
+
233
+ ## 8. Erasure (§4.9; G-43)
234
+
235
+ **CM-006** — Erasure Is Content-Plane and Witnessed
236
+ - **Class:** REQUIRED
237
+ - **Spec Reference:** §4.9.2, §4.9.3, G-43
238
+ - **Description:** after erasure of entry #3: `content_ref` and `salt_ref` NULL, `erasure_state` tombstoned; the entry hash, the manifest hash and the Episode root are unchanged; a codicil tombstone binds the entry's unchanged hash.
239
+ - **Inputs:**
240
+ ```
241
+ entry #3 before: content_ref=blob://ctx/e3, salt_ref=salt://ctx/e3, erasure_state=present
242
+ tombstone: tombstone_id …0400, entry_id …0202, erased_at 2026-01-01T01:00:00.123000Z, erasure_authority GDPR-Art17, erasure_request_id dsr-2026-0142, erased_by privacy-officer
243
+ construction: SHA3-256("ERASURE_TOMBSTONE:v1:" ‖ UUID ‖ UUID ‖ UUID ‖ HASH(entry_hash) ‖ TIMESTAMP ‖ STRING ‖ STRING ‖ STRING)
244
+ ```
245
+ - **Expected Output:**
246
+ ```
247
+ entry #3 after: content_ref=NULL, salt_ref=NULL, erasure_state=tombstoned
248
+ entry_hash (unchanged): 4f45678a1893131e24d4afddd1350fbfcc7ce3f7cb5fe4ec893c76e58ff92704
249
+ tombstone_hash: 05dac249ff0fcbd8f9022f3e57e173821541b20f0b2af54d1f65d168276ef384
250
+ context_manifest_hash (unchanged): 94e7681209cb0315427622aee9b22abf832e5e35ba653cf7e692faa413e75df7
251
+ episode_root_hash (unchanged): 22675041b1af6243fe8c72f318ca853a6e775073355c718116c9ac0d19fe0e88
252
+ ```
253
+ - **Failure Condition:** Any sealed value moving; an erased entry reported as an integrity failure; a tombstone outside the codicil chain; content destroyed without the salt or the reverse.
254
+
255
+ **CM-007** — A Salted Commitment Confirms No Guess Once the Salt Is Gone
256
+ - **Class:** REQUIRED
257
+ - **Spec Reference:** §4.8.3, G-42
258
+ - **Description:** stated as a property, tested by construction: the plain commitment to `1979-03-14` equals a recomputation from the guess; the salted one does not, under no salt and under any wrong salt, across the whole low-entropy space.
259
+ - **Inputs:**
260
+ ```
261
+ guess: 1979-03-14 (and every 1979-03-DD)
262
+ plain content_hash: 4ae9d00037107b8d1ed647f47bf2e4c97311929b4c5d6dd2a57b678fffb5c772
263
+ salted content_hash: f94cc9f2c956d78e7a1c9c17ac6ae74580da6a8cb8256d923f012afa8b0e501a
264
+ ```
265
+ - **Expected Output:**
266
+ ```
267
+ SHA3-256("CONTEXT_CONTENT:v1:" ‖ BYTES(guess)) == plain content_hash (a plain commitment is an oracle)
268
+ SHA3-256("CONTEXT_CONTENT:v1:" ‖ BYTES(guess)) != salted content_hash
269
+ SHA3-256("CONTEXT_CONTENT_SALTED:v1:" ‖ BYTES(any wrong salt) ‖ BYTES(guess)) != salted content_hash
270
+ only the true salt reproduces it — verification of an un-erased salted entry needs that one entry's salt (§4.9.5)
271
+ ```
272
+ - **Failure Condition:** A salted commitment reproducible without its salt.
273
+
274
+ ---
275
+
276
+ ## 9. External Entries (§4.8.4)
277
+
278
+ **CM-008** — An External Entry Verifies Without a Fetch
279
+ - **Class:** REQUIRED
280
+ - **Spec Reference:** §4.8.4, §4.8.6
281
+ - **Description:** the URI and fetch metadata in `source_ref` are provenance outside the preimage; `source_version_hash` is NULL; a verifier MUST NOT re-fetch and treat a difference as a failure.
282
+ - **Inputs:**
283
+ ```
284
+ entry #4: entry_type=external, source_ref='https://rates.example/sheet?date=2026-01-01 status=200 etag="7f3a"', source_version_hash=NULL
285
+ source_ref changed to: 'https://rates.example/sheet?date=2026-01-02 status=404'
286
+ ```
287
+ - **Expected Output:**
288
+ ```
289
+ entry_hash before: e88d185230ffbe206d333f59f4156fb7337b5ced90083e9b704727c73dea0397
290
+ entry_hash after: e88d185230ffbe206d333f59f4156fb7337b5ced90083e9b704727c73dea0397 (unchanged)
291
+ ```
292
+ - **Failure Condition:** The entry hash depending on the URI or on the result of a fetch.
293
+
294
+ ---
295
+
296
+ ## 10. Governance at the Seal (G-41, G-42)
297
+
298
+ **CM-009** — Safe Only Together
299
+ - **Class:** REQUIRED
300
+ - **Spec Reference:** G-42, CM-009
301
+ - **Description:** an implementation that claims 6.0.0 context commitment and does not implement G-42 is non-conformant. In the reference package the rule is enforced where the seal happens: an entry the implementation classified as low-entropy personal data and committed plain is refused.
302
+ - **Inputs:**
303
+ ```
304
+ entry #3 with salted=false and the plain content_hash 4ae9d00037107b8d1ed647f47bf2e4c97311929b4c5d6dd2a57b678fffb5c772, classified low_entropy_personal
305
+ ```
306
+ - **Expected Output:**
307
+ ```
308
+ seal refused (G-42 violation)
309
+ the same entry with no classification seals — the classifier's quality is the implementation's residual risk (§4.9.5)
310
+ ```
311
+ - **Failure Condition:** A ConformanceDeclaration claiming 6.0.0 context commitment from an implementation that commits low-entropy personal data plain.
312
+
313
+ **G41-001** — Posture Required; an Undeclared Gap Blocks the Seal
314
+ - **Class:** REQUIRED
315
+ - **Spec Reference:** G-41, §5.7.3, §4.8.5
316
+ - **Description:** a version 3 seal without a `capture_posture` is refused; an entry whose write did not complete as `captured` or `declared_incomplete` blocks the seal.
317
+ - **Inputs:**
318
+ ```
319
+ seal with capture_posture = NULL
320
+ seal over an entry with capture_state = pending
321
+ ```
322
+ - **Expected Output:**
323
+ ```
324
+ both refused (G-41 violation)
325
+ ```
326
+ - **Failure Condition:** A version 3 seal without a posture; a seal over an entry in a non-terminal capture state.
327
+
328
+ ---
329
+
330
+ ## 11. Retained Constructions
331
+
332
+ Nothing here changes a value sealed under `spine_algorithm_version` 0, 1 or 2. RP-001 … RP-011 of [CONFORMANCE-REPRODUCIBILITY.md](CONFORMANCE-REPRODUCIBILITY.md) continue to apply unchanged, and CM-010 above is the statement of that for version 2 seals of Episodes that hold context entries.
@@ -0,0 +1,258 @@
1
+ # ASTP — Cross-Episode Linking & Grouping Conformance Test Vectors
2
+
3
+ **Version:** 1.1.0
4
+ **Status:** Stable
5
+ **Authors:** Scorched Earth Labs
6
+ **Date:** 2026-09-19
7
+ **Applies To:** SPEC.md §20 (Cross-Episode Linking & Grouping), 5.1.0 (SPEC §20 as of 5.1.0; see the note in §1)
8
+
9
+ ---
10
+
11
+ ## 1. Overview
12
+
13
+ > **SPEC 5.0.0 / 5.1.0 note.** SPEC §20 →2 now defines `EpisodeLink.content_hash` as `EPISODE_LINK:v2:` — binding each end's Episode root when that end was sealed at link creation, the type, exact strength, every inference signal (`LINK_SIGNAL:v2:`) in order and the threshold, with health, quarantine and `created_by` **out** of the preimage (they are lifecycle and provenance, the audit chain's). The 4.x preimage this document describes, which bound `health_state` and the quarantine fields and so changed whenever a link's health did, is retained in SPEC only as the definition of links already written; the `LINK_INTEGRITY` field snapshot is the bound set. The reference writer (`write_episode_link_sync`) fills `source_episode_root` / `target_episode_root` from the graph and stamps the 5.0.0 form from `astp` 0.5.0; links written before that carry the 4.x form this document describes (`compute_episode_link_content_hash_4x`). `MembershipRecord` and `ConformanceDeclaration` hashes are unchanged. The audit records these operations write are the version 2 audit record of SPEC §8 (`GENESIS` → NULL); `GroupingSystem` reserves only the protocol's own value — `astp_native` from astp 0.6.0, with `ariadne_native` retained as the value carried by records written before it (`group_system` is in the MembershipRecord preimage).
14
+
15
+ This document specifies the conformance test vectors for the **Cross-Episode Linking & Grouping** feature family of ASTP (the AI State Tree Protocol; SPEC §20). A conforming implementation MUST pass all vectors marked **REQUIRED**. Vectors marked **RECOMMENDED** test behaviors that conforming implementations SHOULD support.
16
+
17
+ These are **cross-implementation-consistency** vectors, not hardcoded golden-hash values. A hash vector specifies the canonical byte layout (ordered field set + canonicalization rules) and asserts that **two conforming implementations MUST produce identical bytes for the same input tuple**. This is wire-tier conformance (§20 →12.1): the protocol fixes the preimage, not a specific digest. Where a vector references SPEC §20, it uses the amendment's internal §1–§12 numbering scoped within §20 (cited as `§20 →N`).
18
+
19
+ Cross-Episode Linking & Grouping is organized into the following scopes (the reference deployment's adapter guide, `IMPLEMENTATION-CROSS-EPISODE-LINKING.md`, which lives with that adapter, follows the same order):
20
+
21
+ | Scope | Coverage | Vector Prefix |
22
+ |-------|----------|---------------|
23
+ | Cross-episode link schema + integrity | `EpisodeLink` hash, mutual exclusivity, audit deltas | `CEL-` |
24
+ | Link health & quarantine lifecycle | Health-state FSM, quarantine resolution/escalation | `LH-` |
25
+ | Inference signals & discovery | `Signal` recording, propose/reject audit events | `SG-` |
26
+ | Episode membership | `MembershipRecord` hash + succession chain | `MR-` |
27
+ | Grouping conformance | `ConformanceDeclaration` hash + SemVer versioning | `CD-` |
28
+
29
+ All hex values lowercase. All string fields UTF-8. Datetimes UTC-normalized ISO 8601. Floats canonicalized via Python `repr()` semantics (round-trippable, platform-stable).
30
+
31
+ > **Governance model note.** Unlike the BFM family (SPEC §19, G-19–G-35), Cross-Episode Linking & Grouping has **zero numbered SPEC G-rules**. Its governance is enforced through (a) the `LinkGovernanceError` taxonomy (mutual exclusivity), (b) the Link Health / quarantine FSM (§20 →6, →11.3), (c) the immutable-with-succession chain-integrity checks for `MembershipRecord` / `ConformanceDeclaration`, and (d) the `GroupingGovernanceError` SemVer taxonomy. The governance matrix in §7 reflects THIS structure, not invented G-numbers.
32
+
33
+ ---
34
+
35
+ ## 2. Cross-Episode Link Vectors (§20 →2, →3, →5)
36
+
37
+ ### 2.1 Hash Canonicalization
38
+
39
+ **CEL-001** — `EpisodeLink` content hash
40
+ - **Class:** REQUIRED — *4.x form, for links written before astp 0.5.0; the 5.0.0 construction is `EPISODE_LINK:v2:` over the §5.1.1 field encoding (SPEC §20 →2)*
41
+ - **Spec Reference:** §20 →2 (hash preimage note)
42
+ - **Description:** `compute_episode_link_content_hash()` MUST produce identical bytes across implementations given identical inputs. The canonical layout is the ordered field set, serialized as a JSON object with `sort_keys=False` and `separators=(",",":")`, then SHA3-256. Field order (from `_HASH_PREIMAGE_FIELDS`): `link_id`, `source_episode`, `target_episode`, `created_at`, `created_by`, `link_type`, `link_strength`, `is_inferred`, `inference_signals`, `inference_threshold`, `retroactive`, `health_state`, `health_checked_at`, `source_version`, `target_version`, `quarantine_reason`, `quarantined_at`.
43
+ - **Verification Protocol:** Cross-implementation consistency check. Two conforming implementations MUST produce identical `content_hash` bytes for the same input tuple.
44
+ - **Failure Condition:** Divergent bytes indicate a canonicalization error (wrong field order, alphabetical key sort, non-`repr` float encoding, non-UTC datetime, or an unexpectedly included/excluded field).
45
+
46
+ **CEL-002** — Quarantine-resolution field exclusion
47
+ - **Class:** REQUIRED
48
+ - **Spec Reference:** §20 →2 (hash preimage note)
49
+ - **Description:** `quarantine_resolved_at` and `quarantine_resolution` MUST NOT appear in the `EpisodeLink` content-hash preimage. Setting either field on an existing link (on quarantine close) MUST NOT change `content_hash`.
50
+ - **Failure Condition:** A link's `content_hash` changes when only `quarantine_resolved_at` / `quarantine_resolution` are set — the resolution lifecycle is committed by the audit log, not the content hash.
51
+
52
+ **CEL-003** — Health-state field inclusion
53
+ - **Class:** REQUIRED — *4.x form only; under `EPISODE_LINK:v2:` (SPEC §20 →2) `health_state` and `health_checked_at` are OUT of the preimage and re-stamping after a health transition MUST NOT change `content_hash`*
54
+ - **Spec Reference:** §20 →2, §20 →11.2.2 (`LINK_INTEGRITY` field snapshot)
55
+ - **Description:** Mutable health fields `health_state` and `health_checked_at` ARE part of the preimage (drift detection is cryptographically anchored to write-time state). The `LINK_INTEGRITY` proof's `field_snapshot` MUST cover exactly the CEL-001 field set. Re-stamping after a health transition MUST yield a new, deterministic hash.
56
+ - **Verification Protocol:** Recompute over stored canonical fields; MUST equal stored `content_hash`.
57
+
58
+ **CEL-004** — `LinkAcceptedDelta` audit binding
59
+ - **Class:** REQUIRED
60
+ - **Spec Reference:** §20 →5, §20 →11.4
61
+ - **Description:** Asserting a link (`assert_episode_link`) MUST emit a `LINK_ACCEPTED` `AuditRecord` on the **source-episode** chain, carrying forward delta (`link_id`, `source_episode`, `target_episode`, `link_type`, `link_strength`, `is_inferred`, `retroactive`) and reverse delta (`reverse_delete_link_id`). The record's `record_hash` MUST chain off `prior_audit_hash` and recompute deterministically.
62
+ - **Failure Condition:** A link node created with no corresponding `LINK_ACCEPTED` audit record, or an audit record whose recomputed hash diverges from stored.
63
+
64
+ ### 2.2 Mutual-Exclusivity Governance (§20 →3)
65
+
66
+ **CEL-005** — Link-type mutual exclusivity
67
+ - **Class:** REQUIRED
68
+ - **Spec Reference:** §20 →3 (Link Type Taxonomy)
69
+ - **Description:** `enforce_link_mutual_exclusivity()` MUST raise `LinkGovernanceError` when a new link type collides with an existing type on the same `(source_episode, target_episode)` pair. The exclusion set is exactly `{CONTINUES_FROM, SUPERSEDES}` and `{CONTINUES_FROM, BRANCHES_FROM}`. `SUPERSEDES` and `BRANCHES_FROM` MUST coexist; all other types compose freely.
70
+ - **Failure Condition:** A `(source, target)` pair carries both `CONTINUES_FROM` and `SUPERSEDES` (or both `CONTINUES_FROM` and `BRANCHES_FROM`), or a valid compatible pair is spuriously rejected.
71
+
72
+ **CEL-006** — Endpoint existence precondition
73
+ - **Class:** REQUIRED
74
+ - **Spec Reference:** §20 →2, IMPLEMENTATION §4.4
75
+ - **Description:** A link assertion whose source or target Episode does not exist MUST be rejected before any node/edge write (no orphan links). The `LINKED_TO {via: link_id}` edge MUST carry the link_id for one-hop edge→node lookup.
76
+
77
+ **CEL-007** — Resumption isolation rule
78
+ - **Class:** RECOMMENDED
79
+ - **Spec Reference:** §20 →3 (Resumption isolation rule)
80
+ - **Description:** On episode resumption the loader MUST follow `CONTINUES_FROM` and `SUPERSEDES` links (spine traversal) and MAY follow `INFORMED_BY` / `SPAWNED_FROM` up to one hop. `REFERENCES` links are non-loading — available for audit but MUST NOT trigger content retrieval on resumption.
81
+
82
+ ---
83
+
84
+ ## 3. Link Health & Quarantine Vectors (§20 →6, →11.3)
85
+
86
+ **LH-001** — Health-state FSM
87
+ - **Class:** REQUIRED
88
+ - **Spec Reference:** §20 →6 (Link Health State Machine)
89
+ - **Description:** The health FSM MUST admit exactly the transitions in §20 →6: `VALID → {STALE, FROZEN, BROKEN, QUARANTINED}`, `STALE → {BROKEN, QUARANTINED}`, `QUARANTINED → {VALID (CONFIRMED), BROKEN (DISSOLVED), ESCALATED (TTL)}`, `BROKEN → QUARANTINED (re-evaluation)`. There is NO automatic `QUARANTINED → BROKEN`; exit requires an explicit `QuarantineResolution`.
90
+ - **Verification Protocol:** Given a state and a transition trigger, the resulting state MUST match the FSM; illegal transitions MUST be rejected, not silently applied.
91
+ - **Failure Condition:** A quarantined link auto-transitions to `BROKEN` without an explicit resolution, or a major-version target advance auto-transitions to `BROKEN` (it MUST route to human review, remaining `VALID`/`STALE`).
92
+
93
+ **LH-002** — Quarantine resolution semantics
94
+ - **Class:** REQUIRED
95
+ - **Spec Reference:** §20 →11.3.1, `QuarantineResolution` enum
96
+ - **Description:** `CONFIRMED` → `health_state = VALID`; `DISSOLVED` → `health_state = BROKEN`; both set `quarantine_resolved_at` + `quarantine_resolution` and emit `LINK_QUARANTINE_RESOLVED`. `ESCALATED` keeps `health_state = QUARANTINED` and emits `QUARANTINE_ESCALATED`. Escalated links MUST NOT auto-resolve — human review is required to reach `CONFIRMED`/`DISSOLVED`.
97
+
98
+ **LH-003** — Per-Episode quarantine queue
99
+ - **Class:** REQUIRED
100
+ - **Spec Reference:** §20 →11.1.4 (ephemeral coordinator, quarantine queue scope)
101
+ - **Description:** The quarantine queue MUST be scoped per Episode and ordered by quarantine deadline. How the coordinator represents it — key names, data structure — is an implementation choice. A single global queue across all Episodes is non-conforming.
102
+ - **Failure Condition:** A single global quarantine queue is used, causing a quarantine event in one Episode to be processed in another's context.
103
+
104
+ **LH-004** — Quarantine escalation event
105
+ - **Class:** REQUIRED
106
+ - **Spec Reference:** §20 →11.3.3 (quarantine escalation)
107
+ - **Description:** On TTL breach a `QUARANTINE_ESCALATED` audit event MUST fire carrying `link_id`, `episode_id`, `quarantined_at`, `ttl_deadline`, `escalated_at`, `escalation_reason ∈ {TTL_EXCEEDED, INTEGRITY_UNRESOLVABLE, HUMAN_REQUIRED}`. The link remains `QUARANTINED`.
108
+
109
+ ---
110
+
111
+ ## 4. Inference Signal & Discovery Vectors (§20 →4, →5, →12.2)
112
+
113
+ **SG-001** — `Signal` recording completeness
114
+ - **Class:** REQUIRED
115
+ - **Spec Reference:** §20 →4 (Inference Signal Specification)
116
+ - **Description:** Every signal contributing to a candidate's composite score MUST be recorded in `inference_signals` with `signal_type ∈ {SEMANTIC_SIMILARITY, PARTICIPANT_OVERLAP, TEMPORAL_PROXIMITY, EXPLICIT_REFERENCE, SHARED_ARTIFACT}`, `signal_weight`, `signal_value`, `computed_at`. Signals are canonicalized into the CEL-001 preimage via `model_dump()` (recursively, in list order). Signal **combination** (weights, composite formula) is behavioral-tier (sovereign); signal **recording** is required.
117
+ - **Failure Condition:** A composite score computed from a signal not present in the recorded `inference_signals` list.
118
+
119
+ **SG-002** — `LINK_PROPOSED` audit-the-decision
120
+ - **Class:** REQUIRED
121
+ - **Spec Reference:** §20 →5, §20 →12.2, §1 (constants)
122
+ - **Description:** `propose_link_candidate()` (score in `[DISCOVERY_THRESHOLD, AUTO_ACCEPT_THRESHOLD)`) MUST emit an append-only `LINK_PROPOSED` audit event carrying `composite_score`, the full `inference_signals` breakdown, and `discovery_threshold_at_creation` + `auto_accept_threshold_at_creation` (the threshold values in effect at proposal time). No `EpisodeLink` node is created. The returned `audit_id` correlates later accept/reject decisions.
123
+ - **Failure Condition:** A proposal that omits the threshold-at-creation values, defeating post-hoc calibration.
124
+
125
+ **SG-003** — `CANDIDATE_REJECTED` sub-threshold recording
126
+ - **Class:** REQUIRED
127
+ - **Spec Reference:** §20 →5 (CANDIDATE_REJECTED note), §1
128
+ - **Description:** `record_candidate_rejection()` (score strictly `< DISCOVERY_THRESHOLD`) MUST emit an append-only `CANDIDATE_REJECTED` audit event with full signals + `discovery_threshold_at_creation`. Not surfaced for human review; exists purely so the calibration loop can analyze sub-threshold patterns.
129
+
130
+ **SG-004** — `LINK_REJECTED` proposal correlation
131
+ - **Class:** REQUIRED
132
+ - **Spec Reference:** §20 →5, `RejectionReason` enum
133
+ - **Description:** `record_link_rejection()` MUST emit `LINK_REJECTED` carrying `proposed_audit_event_id` (the `LINK_PROPOSED` it rejects) and `rejection_reason ∈ {LOW_CONFIDENCE, WRONG_RELATIONSHIP_TYPE, NOT_RELATED, DUPLICATE_OF_EXISTING}`. Rejection is a new forward event (append-only) — it MUST NOT mutate or delete the prior proposal record.
134
+ - **Failure Condition:** A rejection that overwrites the proposal, or omits the `proposed_audit_event_id` linkage.
135
+
136
+ ---
137
+
138
+ ## 5. Episode Membership Vectors (§20 →7, →11.3.2)
139
+
140
+ ### 5.1 Hash Canonicalization
141
+
142
+ **MR-001** — `MembershipRecord` content hash
143
+ - **Class:** REQUIRED
144
+ - **Spec Reference:** §20 →7, §20 →10 (forward-pointer exclusion)
145
+ - **Description:** `compute_membership_record_content_hash()` canonicalizes, in order (from `_MEMBERSHIP_HASH_PREIMAGE_FIELDS`): `record_id`, `episode_id`, `group_id`, `group_system`, `asserted_at`, `asserted_by`, `membership_role`, `supersedes_record_id`, `succession_reason`. Same §3-canonicalizer (SHA3-256, `sort_keys=False`) as CEL-001. `membership_role` is INCLUDED: role is content, so a role change requires a new record via succession.
146
+ - **Verification Protocol:** Cross-implementation consistency check. Two conforming implementations MUST produce identical bytes for the same input tuple.
147
+ - **Failure Condition:** `membership_role` omitted from the preimage (would let a role edit go uncommitted), or `superseded_by_record_id` included (would invalidate a sealed record's hash on later succession).
148
+
149
+ **MR-002** — Forward-pointer exclusion
150
+ - **Class:** REQUIRED
151
+ - **Spec Reference:** §20 →10, §20 →11.3.2
152
+ - **Description:** `superseded_by_record_id` MUST NOT appear in the `MembershipRecord` preimage. Setting it (when a later record supersedes this one) MUST NOT change this record's `content_hash`.
153
+ - **Failure Condition:** A prior record's `content_hash` changes when its `superseded_by_record_id` forward pointer is set during succession.
154
+
155
+ ### 5.2 Succession Chain
156
+
157
+ **MR-003** — Immutable-with-succession chain integrity
158
+ - **Class:** REQUIRED
159
+ - **Spec Reference:** §20 →7 (Immutability rule), §20 →11.3.2
160
+ - **Description:** `MembershipRecord` is append-only. A successor with `supersedes_record_id` set MUST be rejected unless the prior exists, is not already superseded, and shares the same `episode_id` + `(group_id, group_system)`. On success, `(new)-[:SUPERSEDES]->(prior)` is wired and the prior's `superseded_by_record_id` forward pointer is set. The **active** record for a `(episode_id, group_id, group_system)` tuple is the one with no `superseded_by_record_id`.
161
+ - **Failure Condition:** A prior record superseded twice, a cross-group/cross-episode succession accepted, or a prior record deleted/modified in place.
162
+
163
+ **MR-004** — `MEMBERSHIP_CHAIN` proof
164
+ - **Class:** REQUIRED
165
+ - **Spec Reference:** §20 →11.2.3
166
+ - **Description:** A `MEMBERSHIP_CHAIN` proof MUST re-verify the ordered `content_hash` of each record oldest-first and identify the single active (terminal) record. Any broken link or invalid hash MUST yield `BROKEN_CHAIN` / `INVALID`.
167
+
168
+ **MR-005** — Membership audit events
169
+ - **Class:** REQUIRED
170
+ - **Spec Reference:** §20 →11.4
171
+ - **Description:** `assert_membership_record()` MUST emit `MEMBERSHIP_RECORD_CREATED` unconditionally, and additionally `MEMBERSHIP_RECORD_SUPERSEDED` on any succession — carrying `old_role` + `new_role` so role-change history is queryable from the audit log alone. The `SUPERSEDED` record MUST chain directly off the `CREATED` record's `record_hash` (both describe one operation).
172
+
173
+ ---
174
+
175
+ ## 6. Grouping Conformance Vectors (§20 →8, Appendix A)
176
+
177
+ ### 6.1 Hash Canonicalization
178
+
179
+ **CD-001** — `ConformanceDeclaration` hash
180
+ - **Class:** REQUIRED
181
+ - **Spec Reference:** §20 →8, §20 →10
182
+ - **Description:** `compute_conformance_declaration_hash()` canonicalizes, in order (from `_DECLARATION_HASH_PREIMAGE_FIELDS`): `declaration_id`, `group_id`, `group_system`, `declared_at`, `declared_by`, `declaration_version`, `capabilities`. Same §3-canonicalizer. `capabilities` is a `list[Capability]`; each canonicalizes via `model_dump()` (recursively, in list order — `capability_id` + `description`). `superseded_by` is EXCLUDED (forward pointer, §20 →10).
183
+ - **Verification Protocol:** Cross-implementation consistency check. Two conforming implementations MUST produce identical bytes for the same input tuple.
184
+ - **Failure Condition:** `superseded_by` included, capability ordering not preserved, or field order divergence.
185
+
186
+ ### 6.2 SemVer Governance
187
+
188
+ **CD-002** — SemVer format enforcement
189
+ - **Class:** REQUIRED
190
+ - **Spec Reference:** §20 →8
191
+ - **Description:** `enforce_semver_format()` MUST reject any `declaration_version` that is not a strict `major.minor.patch` non-negative triplet (pre-release / build-metadata suffixes rejected), raising `GroupingGovernanceError`.
192
+
193
+ **CD-003** — Version-bump classification
194
+ - **Class:** REQUIRED
195
+ - **Spec Reference:** §20 →8 (version semantics table), Appendix A
196
+ - **Description:** `classify_version_bump(old, new)` MUST require `new > old` strictly (raise otherwise) and return `"major"` (major component changed), `"minor"` (minor changed), or `"patch"`. Level-skips (e.g. 1.0.0 → 3.0.0) are accepted as their highest changed component. Major → breaking; minor/patch → compatible.
197
+ - **Failure Condition:** A non-increasing version accepted, or a bump misclassified against the §20 →8 table.
198
+
199
+ **CD-004** — Bump audit-event selection
200
+ - **Class:** REQUIRED
201
+ - **Spec Reference:** §20 →11.4, §20 →8
202
+ - **Description:** `bump_conformance_declaration()` MUST emit `DECLARATION_SUPERSEDED` for a major bump and `DECLARATION_VERSION_BUMPED` for a minor/patch bump, wire `(old)-[:SUPERSEDED_BY]->(new)`, set the prior's `superseded_by` forward pointer, and anchor the audit event to the synthetic chain key `declaration:{group_system}:{group_id}`. Initial `register_conformance_declaration()` MUST NOT fire an audit event (only bumps do).
203
+ - **Failure Condition:** A major bump emitting `DECLARATION_VERSION_BUMPED` (or vice versa); an initial registration firing a spurious event; declaration events anchored to an episode chain instead of the synthetic key.
204
+
205
+ **CD-005** — Breaking-change reference (Appendix A)
206
+ - **Class:** RECOMMENDED
207
+ - **Spec Reference:** §20 Appendix A
208
+ - **Description:** Implementations SHOULD classify as **major** any change that renames/removes/retypes a field appearing in a `content_hash` preimage, or changes the hash algorithm / canonical serialization; as **minor** any new optional field, new `LinkType`, new `AuditEventType`, or new `LinkHealthState`; as **patch** documentation/threshold/editorial changes.
209
+
210
+ ---
211
+
212
+ ## 7. Governance Enforcement Matrix
213
+
214
+ Cross-Episode Linking & Grouping has **no numbered SPEC G-rules**. Governance is enforced via error taxonomies, the health/quarantine FSM, and succession-chain integrity. This matrix maps each enforcement mechanism to its vector(s).
215
+
216
+ | Mechanism | Enforcement surface | Vector(s) |
217
+ |-----------|---------------------|-----------|
218
+ | Link mutual exclusivity | `LinkGovernanceError` (`enforce_link_mutual_exclusivity`) | CEL-005 |
219
+ | Endpoint existence | `ValueError` in `write_episode_link_sync` | CEL-006 |
220
+ | Quarantine-field hash exclusion | `_HASH_PREIMAGE_FIELDS` (§20 →2) | CEL-002 |
221
+ | Link health FSM | Health-state transition rules (§20 →6) | LH-001, LH-002 |
222
+ | Per-Episode quarantine queue | Coordinator queue scope (§20 →11.1.4) | LH-003 |
223
+ | Quarantine escalation | `QUARANTINE_ESCALATED` (§20 →11.3.3) | LH-004 |
224
+ | Signal recording | Audit-the-decision (§20 →4, →12.2) | SG-001, SG-002, SG-003, SG-004 |
225
+ | Membership hash inclusion/exclusion | `_MEMBERSHIP_HASH_PREIMAGE_FIELDS` (§20 →7, →10) | MR-001, MR-002 |
226
+ | Membership succession integrity | Chain checks in `write_membership_record_sync` | MR-003, MR-004, MR-005 |
227
+ | Declaration forward-pointer exclusion | `_DECLARATION_HASH_PREIMAGE_FIELDS` (§20 →8, →10) | CD-001 |
228
+ | SemVer governance | `GroupingGovernanceError` (`enforce_semver_format` / `classify_version_bump`) | CD-002, CD-003, CD-004 |
229
+
230
+ ---
231
+
232
+ ## 8. Conformance Levels
233
+
234
+ ### Level 1: Protocol Conformance (Wire + State tiers)
235
+
236
+ Implementation passes all **REQUIRED** vectors in sections 2–6 and the governance matrix in section 7. This makes two implementations interoperable — they can exchange `EpisodeLink`, `MembershipRecord`, and `ConformanceDeclaration` structures and verify each other's hashes (wire tier, §20 →12.1), and their audit logs are comparable (state tier). No claim is made about storage layout, embedding model, or scoring algorithm.
237
+
238
+ ### Level 2: Full Conformance
239
+
240
+ Level 1 plus all **RECOMMENDED** vectors (CEL-007 resumption isolation, CD-005 breaking-change classification) and the storage-architecture obligations of §20 →11.1 / →11.5 (write-order invariant, active-record index, consistency-window SLA).
241
+
242
+ ### Behavioral tier (sovereign — no conformance requirement)
243
+
244
+ Signal-combination algorithm, embedding-model selection, threshold-calibration strategy, and internal indexing are implementation decisions (§20 →12.1). The protocol requires only that the *decision* is recorded (audit-the-decision, §20 →12.2), which is covered by the `SG-` vectors.
245
+
246
+ ---
247
+
248
+ ## 9. Cross-Reference
249
+
250
+ - **SPEC.md §20** — normative protocol surface (v3.2.1); internal §1–§12 numbering cited as `§20 →N`.
251
+ - **IMPLEMENTATION-CROSS-EPISODE-LINKING.md** — the reference deployment's adapter guide (non-normative; shipped with that adapter, not with the protocol).
252
+ - **CONFORMANCE-BFM.md** — sibling BFM vector set (SPEC §19); shares the cross-implementation-consistency vector format.
253
+ - **`astp/core/hash_canonical.py`** — the shared canonicalizer that every `CEL-`/`MR-`/`CD-` hash vector references.
254
+
255
+ ---
256
+
257
+ *ASTP Cross-Episode Linking & Grouping Conformance Test Vectors are maintained by Scorched Earth Labs.*
258
+ *Vector set version: 1.0.0 | Applies to SPEC.md: v3.2.1 §20*