Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
345 commits
Select commit Hold shift + click to select a range
625db3d
GO-7383 I1 watches the other type slot too
requilence Aug 19, 2026
fa06d66
GO-7383 The type-slug guard's stored-key half becomes reachable
requilence Aug 19, 2026
dc87e6e
GO-7383 The property-slug guard's stored-key half becomes reachable too
requilence Aug 19, 2026
188aff7
GO-7383 A type_properties key that validates has to import (I2)
requilence Aug 19, 2026
e5dc7d7
GO-7383 The batch scans resolve property terms before they key on them
requilence Aug 19, 2026
697674f
GO-7383 CheckTargetTypes asks the arms in the converter's order
requilence Aug 19, 2026
e32c84e
GO-7383 A vocabulary may not shadow the bundled table (§11.1's real p…
requilence Aug 19, 2026
62afd41
GO-7383 The type census reserves only what the document spells
requilence Aug 19, 2026
02b2688
GO-7383 The invariants' own preconditions, written down (SPEC v0.15)
requilence Aug 19, 2026
160cb5c
GO-7383 Every dropped type property says so
requilence Aug 19, 2026
6366800
GO-7383 The cell shorthand reads the mark it sets
requilence Aug 19, 2026
eb15bc9
GO-7383 A generated row or column claims its grid too
requilence Aug 19, 2026
e8dbcc6
GO-7383 A table nobody can reach reserves nothing
requilence Aug 19, 2026
3dc878a
GO-7383 A pointer segment is escaped, and one fault stays one issue
requilence Aug 19, 2026
a838b88
GO-7383 A date preset only decides anything on a date property (§6.2)
requilence Aug 19, 2026
4ae2519
GO-7383 An inert date preset is said out loud, not refused (§6.2)
requilence Aug 19, 2026
d12a35b
GO-7383 The propertyNames sweep finishes reading the schema
requilence Aug 19, 2026
9ccef7a
GO-7383 Corpus shapes for the two gates the reader now applies
requilence Aug 19, 2026
e620f60
GO-7383 Record the v0.16 rules (SPEC)
requilence Aug 19, 2026
6c4c2b8
GO-7383 Drop the batch's unread copy of a property's target types
requilence Aug 19, 2026
9d7134c
GO-7383 A legend entry for every term a reader would bind elsewhere
requilence Aug 19, 2026
922e309
GO-7383 Report the type the envelope has no position for (SS3)
requilence Aug 19, 2026
a1fac23
GO-7383 One declared format through both doors of the SS2a array
requilence Aug 19, 2026
5e7c11c
GO-7383 A counting preset's operand has to be a day count (SS6.2)
requilence Aug 19, 2026
92f86fb
GO-7383 Two warnings that addressed the wrong place (SS13)
requilence Aug 19, 2026
05ce118
GO-7383 Record the v0.17 rules (SPEC)
requilence Aug 19, 2026
b4f2613
GO-7383 An empty recommended-role list is normalization, not drift
requilence Aug 20, 2026
844a9cb
GO-7383 A refs key may qualify an option name with its property
requilence Aug 21, 2026
3c144c3
GO-7383 Carry the option id beside the name it is spelled by
requilence Aug 21, 2026
ef76132
GO-7383 The invariants cover the option legend (I1, I2)
requilence Aug 21, 2026
37fa7cb
GO-7383 Record the qualified option legend (SPEC v0.18)
requilence Aug 21, 2026
98ea914
GO-7383 One function answers "which option does this name mean?"
requilence Aug 21, 2026
31bebc9
GO-7383 A qualified refs key has to name a property the document uses
requilence Aug 21, 2026
9df653f
GO-7383 The filter compiler emits the vocabulary this package reads
requilence Aug 21, 2026
ebca137
GO-7383 Pin the object-id avoid-set the block relabeler depends on
requilence Aug 21, 2026
920450b
GO-7383 Delete object-reference compaction
requilence Aug 21, 2026
e3d3973
GO-7383 Replace the `#` grammar with a nested `option_ids` legend
requilence Aug 21, 2026
affccb2
GO-7383 SPEC v0.20: three legends, and the layers section
requilence Aug 21, 2026
a0af19f
GO-7383 An unwritable spelling never reaches the option legend
requilence Aug 21, 2026
12f47de
GO-7383 OptionName is the import-side liveness check too
requilence Aug 21, 2026
57f98e2
GO-7383 The object census, position by position
requilence Aug 21, 2026
4628ca2
GO-7383 Legend member order, asserted on the bytes
requilence Aug 21, 2026
019c3cc
GO-7383 A document carrying `refs` is refused
requilence Aug 21, 2026
8e7fcaf
GO-7383 What the fragment's label-plan gate actually decides
requilence Aug 21, 2026
04d1ffe
GO-7383 The object/file arm the `refs` legend left behind
requilence Aug 21, 2026
e0c2ab9
GO-7383 A document carrying `refs` is told what happened
requilence Aug 21, 2026
c5cc9bf
GO-7383 What OmitIds gives up, and what a legend fault costs
requilence Aug 21, 2026
30a6d3b
GO-7383 Make the envelope-member order test catch a lost sort every run
requilence Aug 21, 2026
7cb7856
GO-7383 The two key legends admit what they write (SPEC v0.21)
requilence Aug 21, 2026
5d3c3d6
GO-7383 The key legends name every non-bundled spelling (SPEC v0.21 #2)
requilence Aug 21, 2026
ee6c0f9
GO-7383 A key slot has to name something, at all 16 (SPEC v0.21 #3)
requilence Aug 21, 2026
118fe66
GO-7383 A fragment carries what its keys mean (SPEC v0.21 #4, §13.1)
requilence Aug 21, 2026
53247e1
GO-7383 Close the wave-2 audit's findings
requilence Aug 21, 2026
9b43f24
GO-7383 The reserved index.json names move into the platform's `_` na…
requilence Aug 21, 2026
8e2b863
GO-7383 `kind` is the sole authority on what a template is (SPEC v0.2…
requilence Aug 21, 2026
e18926e
GO-7383 §15 carries the evidence, not just the verdict (SPEC v0.22 #3)
requilence Aug 21, 2026
cd20e61
GO-7383 The template gate reads the document's own legend, on both sides
requilence Aug 21, 2026
1e881e2
GO-7383 Transient properties: stripped on export, dropped on import
requilence Aug 22, 2026
1b637ef
GO-7383 The id census counts what the document SPELLS (§9a)
requilence Aug 22, 2026
29c4346
GO-7383 Export lifts transparent containers (§7a)
requilence Aug 22, 2026
7d59f90
GO-7383 Import lifts transparent containers, at all three entry point…
requilence Aug 22, 2026
f97d5ed
GO-7383 SPEC v0.23: transparent containers (§7a)
requilence Aug 22, 2026
aae0049
GO-7383 The census probe must not report the emit's issues twice
requilence Aug 22, 2026
a9ac8b9
GO-7383 Two comments say where a container is actually handled
requilence Aug 22, 2026
1b8b14c
GO-7383 A cell cannot be a container, in EITHER spelling (§7a)
requilence Aug 22, 2026
70636fb
GO-7383 The census reserves a cell's derived id, and says what it costs
requilence Aug 22, 2026
de9e078
GO-7383 A resolver seam that names a space member (§3)
requilence Aug 22, 2026
7376a0a
GO-7383 Attribution is dropped on import, on both keys (§3)
requilence Aug 22, 2026
fdf1b8d
GO-7383 `creator` is the member's name, not a 135-character id (§3)
requilence Aug 22, 2026
695ee49
GO-7383 SPEC v0.24: attribution is a name, not an address
requilence Aug 22, 2026
3546d3a
GO-7383 Attribution is a name or nothing, at both seams
requilence Aug 22, 2026
59b29b0
GO-7383 Typed `icon` and `cover` envelope fields (§2b)
requilence Aug 22, 2026
9536ed4
GO-7383 The bundle index takes the same icon (§2c)
requilence Aug 22, 2026
3758722
GO-7383 SPEC v0.25: icon and cover are typed envelope fields
requilence Aug 22, 2026
3e211a0
GO-7383 ANOMALIES: the leaked Notion cover paths, measured
requilence Aug 22, 2026
8b911d0
GO-7383 A restricted icon slot names one union, not two (§2b)
requilence Aug 22, 2026
06b219a
GO-7383 Normalize a key label through the format's own grammar
requilence Aug 23, 2026
6d6a8b0
GO-7383 A slugless space key spells its NAME, not its bson id
requilence Aug 23, 2026
3f37955
GO-7383 A derived label never takes a spelling already answered
requilence Aug 23, 2026
7f43923
GO-7383 SPEC v0.26: the label rule (§1, §3, §6.2.1, §11, §13)
requilence Aug 23, 2026
bd1d9e2
GO-7383 Point KeyVocabulary implementors at the label rule
requilence Aug 23, 2026
0a4c2f8
GO-7383 A non-ASCII label survives the whole codec (I1)
requilence Aug 23, 2026
ab52756
GO-7383 A combining mark is part of an identifier, not noise (§6.2.1)
requilence Aug 23, 2026
2cf34f9
GO-7383 The sweep can dump readable documents, and knows attribution …
requilence Aug 23, 2026
3b6d4e8
GO-7383 An object-name seam and the space id ride Options (§9)
requilence Aug 23, 2026
2f286ed
GO-7383 The informative #name reference suffix (§9)
requilence Aug 23, 2026
9a5d295
GO-7383 The participant fold: identity in, composite out (§9)
requilence Aug 23, 2026
0863ca4
GO-7383 Attribution is a resolvable id again, named by its suffix (§3)
requilence Aug 23, 2026
e34626e
GO-7383 An empty-identity composite addresses nobody and is omitted (§3)
requilence Aug 23, 2026
33b0435
GO-7383 SPEC v0.27: references get a readable half; identity returns …
requilence Aug 23, 2026
2fdf1fa
GO-7383 A caption is only written where it can be taken off (§9)
requilence Aug 23, 2026
082a21a
GO-7383 A reader that names no space says so, instead of corrupting (§9)
requilence Aug 23, 2026
fa974e0
GO-7383 Three guards the fold had, and nothing pinned (§9)
requilence Aug 23, 2026
33d1a70
GO-7383 SPEC v0.28: the separator earns its keep at both ends (§9, §11)
requilence Aug 23, 2026
9d0d6f7
GO-7383 Two docs still explaining the behaviour v0.27 reverted
requilence Aug 23, 2026
78249d0
GO-7383 Say what the reader supplies: a space, not a vocabulary
requilence Aug 23, 2026
0777c20
GO-7383 Seven system-stamped keys stop writing their emptiness (§15 #12)
requilence Aug 23, 2026
8327f45
GO-7383 A key is spelled the way its name reads (§1, §3)
requilence Aug 23, 2026
494d4c0
GO-7383 A falsy filter value is still the query (§6.2)
requilence Aug 23, 2026
6e98e13
GO-7383 An absent format says nothing, not "text" (§3)
requilence Aug 23, 2026
98d8f70
GO-7383 A relation states its format in the format's own vocabulary (…
requilence Aug 23, 2026
aca1cb2
GO-7383 Pin the SS2d contract: tests for the lift, both packages
requilence Aug 23, 2026
371c453
GO-7383 A bundled slug for a denied key stays writable in reference s…
requilence Aug 23, 2026
715042b
GO-7383 Pin the reference-slot slug for lifted keys
requilence Aug 23, 2026
4fef59a
GO-7383 Record the relation envelope in the SPEC (v0.31, SS2d)
requilence Aug 23, 2026
38b1281
GO-7383 Pin the target-key census duty (SS2d)
requilence Aug 23, 2026
60cab73
GO-7383 Drop the throwaway corpus-sweep harness
requilence Aug 23, 2026
8d8c66f
GO-7383 Warn on the phantom field-name property (SS2d)
requilence Aug 23, 2026
dd73add
GO-7383 The §2d guard covers every kind that IS a relation
requilence Aug 23, 2026
d09d5d3
GO-7383 Pin isRelationKind where it is actually load-bearing
requilence Aug 23, 2026
fe883b4
GO-7383 One list of which kinds are relations, in all three places (§2d)
requilence Aug 24, 2026
f811140
GO-7383 The schema descriptions name the three relation kinds too
requilence Aug 24, 2026
04d0064
GO-7383 Pin each half of the denied-key slug exemption (test gap 1)
requilence Aug 24, 2026
bc93070
GO-7383 Pin the off-relation drop warning per key (test gap 2)
requilence Aug 24, 2026
4d56793
GO-7383 Pin the phantom-member warning for all three field names (tes…
requilence Aug 24, 2026
60c9878
GO-7383 Pin relationFormatName's NaN/Inf/int32-range guard (test gap 4)
requilence Aug 24, 2026
9803831
GO-7383 Pin the TypeResolver empty-answer contract (test gap 5)
requilence Aug 24, 2026
c9da0be
GO-7383 sub_object is deprecated and stays outside §2d
requilence Aug 24, 2026
5d4e936
GO-7383 Record cross-space key reuse as a follow-up (§15 #16)
requilence Aug 24, 2026
3039325
GO-7383 The analytics triple is a moment, not an object (§3)
requilence Aug 24, 2026
f59b152
GO-7383 `map` is a format a store carries, not one an author declares…
requilence Aug 24, 2026
4b469cd
GO-7383 One property, one shape: $defs/propertyDefinition (§2e)
requilence Aug 24, 2026
cdca7ab
GO-7383 relation_settings: the §2d definition becomes a group (v0.32)
requilence Aug 24, 2026
e1d4687
GO-7383 type_settings: everything defining the type, in one gated sub…
requilence Aug 24, 2026
890e016
GO-7383 The property dictionary: one file naming every property a bun…
requilence Aug 24, 2026
f3d94d0
GO-7383 The manifest, and the omission the dictionary pays for (§2c, …
requilence Aug 24, 2026
ca7c188
GO-7383 Pin the tools' unknown-installed-key warning helper (§2f)
requilence Aug 24, 2026
e40b7ac
GO-7383 stripAttribution learns the trailing comma the §2a lift exposed
requilence Aug 24, 2026
eda1871
GO-7383 A legacy bare-key target is respelled, not lost (§2d, §11)
requilence Aug 24, 2026
ec3433a
GO-7383 revision guards a type's own name, and setOf is a self-refere…
requilence Aug 24, 2026
42772dd
GO-7383 The two tools agree about one bundle (§2f)
requilence Aug 24, 2026
9daebaa
GO-7383 Name the step that carries 69% of the reader flow (§2f)
requilence Aug 24, 2026
0900fdb
GO-7383 A definition that names no key has no identity (§2a, §2d)
requilence Aug 24, 2026
7a0d029
GO-7383 An entry that cannot be read back is not an entry (§2f)
requilence Aug 24, 2026
af778fd
GO-7383 Four corrections from the review, none of them behavioural ex…
requilence Aug 24, 2026
46566ba
GO-7383 State the precedence, and record two follow-ups (§2f, §15)
requilence Aug 24, 2026
b92e43b
GO-7383 The space's own object spells its settings (§2, v0.33)
requilence Aug 24, 2026
3393993
GO-7383 A shareable bundle stops carrying the space's secrets (§3)
requilence Aug 24, 2026
6d8cf21
GO-7383 A bundle carries no space document (§2c, v0.34)
requilence Aug 24, 2026
47b8ee2
GO-7383 The space icon travels in index.json, and the omission fires …
requilence Aug 24, 2026
3d3de2a
GO-7383 The bundle index is the lifted index, not a copy of three fields
requilence Aug 24, 2026
3bd3b36
GO-7383 A document says which grammar it follows (§2c, §2f, v0.35)
requilence Aug 24, 2026
3329884
GO-7383 The schema states the inline markup grammar (§8)
requilence Aug 24, 2026
b5e3f15
GO-7383 The published schema states the vocabularies the codec enforces
requilence Aug 24, 2026
e6806d8
GO-7383 The property dictionary says when a key is the wrong spelling…
requilence Aug 25, 2026
1d54ce8
GO-7383 The dictionary spells properties in snake_case too (§2f, v0.36)
requilence Aug 25, 2026
539a45a
GO-7383 A type is spelled one way outside a document too (§2c, §2f)
requilence Aug 25, 2026
28be92e
GO-7383 A name is identity enough to declare a property (§2a, §2f)
requilence Aug 25, 2026
069d911
GO-7383 Rename the legends to say what their values are
requilence Aug 25, 2026
6da434d
GO-7383 One word stops meaning two things: internal_key is the stored…
requilence Aug 25, 2026
3831f03
GO-7383 An internal_key resolves verbatim, and the new identity surfa…
requilence Aug 25, 2026
5280231
GO-7383 The convert tool says out loud where the mint is owed
requilence Aug 25, 2026
1d94635
GO-7383 SPEC v0.37: the key/spelling split, written down
requilence Aug 25, 2026
1d68e1e
GO-7383 The format stops calling a property a relation: kinds, the §2…
requilence Aug 25, 2026
88a7590
GO-7383 The bundled relation-spelled keys take property aliases, and …
requilence Aug 25, 2026
4432bab
GO-7383 SPEC v0.38: the property rename, written down
requilence Aug 25, 2026
4211aa0
GO-7383 The spelling rule says alias where it now means alias
requilence Aug 25, 2026
b0a97f9
GO-7383 The select vocabulary travels with the property that owns it …
requilence Aug 25, 2026
db2b304
GO-7383 A property declared by name gets a minted internal key (§2e)
requilence Aug 25, 2026
15d801f
GO-7383 The deprecated profile object does not travel (§2c)
requilence Aug 25, 2026
8ce9aca
GO-7383 An inline vocabulary is written in the order the space shows it
requilence Aug 25, 2026
d305876
GO-7383 Record why an unordered option falls back to its name
requilence Aug 25, 2026
532bdd3
GO-7383 The validator catches a view slot naming a property nothing d…
requilence Aug 25, 2026
f7cc9f5
GO-7383 Add the authoring subset schemas
requilence Aug 25, 2026
3508c50
GO-7383 The subset is a test, not a claim: worked example plus invari…
requilence Aug 25, 2026
af14dde
GO-7383 SPEC v0.40: the authoring subset, written down
requilence Aug 25, 2026
d54807c
GO-7383 Close the first subset break: member-format coupling
requilence Aug 25, 2026
0a6c3a3
GO-7383 A disagreeing identity pair is reported, not silently resolve…
requilence Aug 25, 2026
3ddf153
GO-7383 An object's featured list is deprecated: the type owns it (§3)
requilence Aug 25, 2026
ef60541
GO-7383 Record the layout/resolved_layout deprecation as §15 #19
requilence Aug 25, 2026
3100e69
GO-7383 A reserved homepage is translated on the way into the index (…
requilence Aug 25, 2026
fb04324
GO-7383 The slot that names a property is spelled property, everywhere
requilence Aug 25, 2026
8554ed3
GO-7383 Pin the one-spelling rule and the vacated spelling's refusal
requilence Aug 25, 2026
f42be48
GO-7383 SPEC v0.41: one spelling for the property-naming slot, writte…
requilence Aug 25, 2026
caee781
GO-7383 The batch scanner's comments name the definition member as it…
requilence Aug 25, 2026
2c0bbbd
GO-7383 Two shipped claims that were not reproducible
requilence Aug 25, 2026
785fc18
GO-7383 The file machinery's per-device answers are deprecated: they …
requilence Aug 25, 2026
5567b87
GO-7383 Name-over-number becomes a per-key vocabulary, and junk strin…
requilence Aug 25, 2026
a9656c0
GO-7383 layout_align is named, not numbered (§3)
requilence Aug 25, 2026
0c7c0ab
GO-7383 origin and import_type are named, not numbered (§3)
requilence Aug 25, 2026
cfdcf68
GO-7383 SPEC v0.42: enum properties are named or gone, written down
requilence Aug 25, 2026
e5e6bcc
GO-7383 An icon's file holds two address spaces, and the format says …
requilence Aug 25, 2026
79c3d98
GO-7383 A deleted type is named by its id, and still owns no spelling…
requilence Aug 26, 2026
fe205b1
GO-7383 The warning channel reports the document, not the format (§12)
requilence Aug 26, 2026
dc69990
GO-7383 A shared bundle stops carrying every file's encryption keys (§3)
requilence Aug 26, 2026
29fecc6
GO-7383 The file's content addresses stop travelling too, and the thin
requilence Aug 26, 2026
a9d205c
GO-7383 A missing object is not written as if it existed (§9)
requilence Aug 26, 2026
7f1aacc
GO-7383 SPEC v0.44: the missing-reference rule, written down (§9)
requilence Aug 26, 2026
f705260
GO-7383 A select value drops the dangling-option sentinel too (§9)
requilence Aug 26, 2026
9ca825a
GO-7383 The importer knows every predefined widget target the client …
requilence Aug 26, 2026
9bb6d03
GO-7383 The index widget says everything the widget object's blocks s…
requilence Aug 26, 2026
98825e7
GO-7383 A bundle carries no widget document (§2c)
requilence Aug 26, 2026
003b011
GO-7383 The worked example's sidebar uses the richer widget shape (§2g)
requilence Aug 26, 2026
0c6d288
GO-7383 SPEC v0.45: the sidebar is the index's, written down
requilence Aug 26, 2026
12befa1
GO-7383 An inline option can state its stored key (§2f)
requilence Aug 26, 2026
925c699
GO-7383 image_kind travels by name, not as a proto integer (§3)
requilence Aug 26, 2026
6f11fc3
GO-7383 Analytics keys do not travel in the root block's fields eithe…
requilence Aug 26, 2026
3c12fcb
GO-7383 An icon whose image object was deleted is dropped (§2b)
requilence Aug 26, 2026
bcfb79d
GO-7383 Record the option-document redundancy as a §15 follow-up
requilence Aug 26, 2026
a04cf5f
GO-7383 SPEC v0.46: the manifest no longer locates options (§2c)
requilence Aug 26, 2026
f42b027
GO-7383 Land the exporter design document
requilence Aug 26, 2026
a3314f1
GO-7383 Extract the collection layer behind collect.Collector
requilence Aug 26, 2026
e95bd63
GO-7383 SPEC v0.47: the manifest binds file blobs (§2c)
requilence Aug 26, 2026
95781f7
GO-7383 The compose package: bundle-level composition, shared by prod…
requilence Aug 26, 2026
645b9d0
GO-7383 Migrate the roundtrip harness onto the shared compose package
requilence Aug 26, 2026
5839b83
GO-7383 The native AnyBlock JSON exporter (core/block/export/anyblock)
requilence Aug 26, 2026
cb2bd47
GO-7383 Mark the exporter design implemented, record the follow-ups
requilence Aug 26, 2026
03f2149
GO-7383 SPEC §13: name compose/ in the package layout
requilence Aug 26, 2026
f1e589e
GO-7383 Pin blob streaming and the manifest binding end to end
requilence Aug 26, 2026
212b187
GO-7383 anyblockvalidate: cross-check the manifest's blob bindings
requilence Aug 26, 2026
a736718
GO-7383 Participant documents are named by their folded envelope id
requilence Aug 26, 2026
8bc495a
GO-7383 A native-exporter corpus sweep: anyblockroundtrip -native
requilence Aug 26, 2026
028922e
GO-7383 The option vocabulary's sort is total: id breaks the name tie
requilence Aug 26, 2026
c72f6e8
GO-7383 Record the native corpus verification in the design status
requilence Aug 26, 2026
31f0808
GO-7383 Review minors: I1 on empty manifest paths, blob-suffix assert…
requilence Aug 26, 2026
f07140e
GO-7383 Discovery skips manifest-bound blobs — the skip rule §15 #1 p…
requilence Aug 26, 2026
7f084a9
GO-7383 The manifest files map gets its reader: anyblockconvert binds…
requilence Aug 26, 2026
4cd772d
GO-7383 Blob failures are counted, close-after-write is typed and proved
requilence Aug 26, 2026
2894b7e
GO-7383 Cover the whole-space collection path for both closures
requilence Aug 26, 2026
dd72e54
GO-7383 A failed blob stream leaves no partial file behind
requilence Aug 26, 2026
c7afaf0
GO-7383 Design status: record the full review-round defect list
requilence Aug 26, 2026
52df8b7
GO-7383 Ignore the cmd tool binaries that keep landing at the repo root
requilence Aug 27, 2026
0610501
GO-7383 A participant document does not carry created_date (§3, v0.47)
requilence Aug 27, 2026
118a9a4
GO-7383 Export.Format gains AnyBlockJSON (proto only)
requilence Aug 27, 2026
cfd156a
GO-7383 The exporter takes its doc set and its emit pool from the caller
requilence Aug 27, 2026
754dd06
GO-7383 Export_AnyBlockJSON reaches the native exporter
requilence Aug 27, 2026
699c70b
GO-7383 Design status: Q6 is settled, and §1.5 grew a runner seam
requilence Aug 27, 2026
43ceb97
GO-7383 The bundle survives the zip writer too
requilence Aug 27, 2026
8fba18c
GO-7383 Drop the review artefacts, and the citations that outlived them
requilence Aug 27, 2026
1ccf34e
GO-7383 Mint api keys through the format's slug mechanism
requilence Aug 27, 2026
1ce0340
GO-7383 Two bundled renames ahead of raw-name key addressing
requilence Aug 28, 2026
4c3a83f
GO-7383 Every property and type key is spelled by its display name
requilence Aug 28, 2026
4a2dbb0
GO-7383 Attribution keys yield in the collision plan
requilence Aug 28, 2026
869cd67
GO-7383 A filter template on a non-object property warns, never refuses
requilence Aug 28, 2026
2c439f3
GO-7383 The authoring schema reserves all ten listing words
requilence Aug 28, 2026
41d43bb
GO-7383 Spec corrections: falsified claims, dead citations, settled i…
requilence Aug 28, 2026
d24eae8
GO-7383 Record the four implementation calls in the study's addendum
requilence Aug 28, 2026
235ef7c
GO-7383 The census stops reserving spellings for keys the emit drops
requilence Aug 28, 2026
81d4a24
GO-7383 The authoring subset states its rules on the resolved propert…
requilence Aug 28, 2026
3261a5c
GO-7383 A property definition's name IS its spelling
requilence Aug 28, 2026
0c04ea0
GO-7383 The key fold normalizes after the strip, not only before
requilence Aug 28, 2026
3c22a3e
GO-7383 A candidate list is a set, and an ambiguity reports its own slot
requilence Aug 28, 2026
dee2273
GO-7383 The format says property, and the rename reaches existing spaces
requilence Aug 28, 2026
510f3d7
GO-7383 A name that is not valid UTF-8 is not a spelling
requilence Aug 28, 2026
1c88758
GO-7383 The spec says what the code does: yields, warnings, spellings
requilence Aug 28, 2026
fc1cc70
GO-7383 The bundle an authoring agent is shown first speaks the format
requilence Aug 28, 2026
575a87d
GO-7383 A reader does not revise the system objects of somebody's space
requilence Aug 28, 2026
46d3908
GO-7383 The format documents say what the format does
requilence Aug 28, 2026
33c5d49
GO-7383 An error's reader does not have the repo, and neither did the…
requilence Aug 28, 2026
fc3613c
GO-7383 The format freezes at version 2, and the spec stops narrating…
requilence Aug 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,9 @@ build
doc/dependency_decisions.yml
.serena/
.ralphex/progress/

# compiled cmd/ tool binaries, which land at the repo root when built with
# `go build ./cmd/<name>` and have been committed by accident more than once
/anyblockroundtrip
/anyblockconvert
/anyblockvalidate
554 changes: 554 additions & 0 deletions cmd/anyblockconvert/batch.go

Large diffs are not rendered by default.

327 changes: 327 additions & 0 deletions cmd/anyblockconvert/batch_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,327 @@
package main

// Options sort on orderId+name concatenated (database.OrderMap.BuildOrder),
// so an option with no order id is compared by *name* against everyone
// else's order id: "Abandoned" would sort ahead of the whole declared
// vocabulary because 'A' < the lexid alphabet, while "Zebra" would sort
// behind it. Every option therefore needs an order id.

import (
"fmt"
"os"
"path/filepath"
"sort"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/anyproto/anytype-heart/cmd/internal/anyblockbatch"
"github.com/anyproto/anytype-heart/core/domain"
"github.com/anyproto/anytype-heart/pkg/lib/anyblockjson"
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
"github.com/anyproto/anytype-heart/util/constant"
)

// vocab is a declared vocabulary that names no colors.
func vocab(names ...string) []anyblockjson.OptionDefinition {
out := make([]anyblockjson.OptionDefinition, 0, len(names))
for _, n := range names {
out = append(out, anyblockjson.OptionDefinition{Name: n})
}
return out
}

// mintedColors maps option name to the relationOptionColor minted for it,
// for one property.
func mintedColors(t *testing.T, b *batch, key string) map[string]string {
t.Helper()
out := map[string]string{}
for _, p := range b.pending {
if p.sbType != model.SmartBlockType_STRelationOption {
continue
}
d := p.snapshot.Details.Fields
if d[detailRelationKey].GetStringValue() != key {
continue
}
name := d[detailName].GetStringValue()
out[name] = d[detailRelationOptionColor].GetStringValue()
}
return out
}

// mintedOptions returns option names in the order BuildOrder would compare
// them: the orderId and name concatenated.
func mintedOptions(t *testing.T, b *batch) []string {
t.Helper()
type opt struct{ sortKey, name string }
var opts []opt
for _, p := range b.pending {
if p.sbType != model.SmartBlockType_STRelationOption {
continue
}
d := p.snapshot.Details.Fields
name := d[detailName].GetStringValue()
order := d[detailOrderId].GetStringValue()
require.NotEmpty(t, order, "option %q has no order id", name)
opts = append(opts, opt{order + name, name})
}
sort.Slice(opts, func(i, j int) bool { return opts[i].sortKey < opts[j].sortKey })
names := make([]string, 0, len(opts))
for _, o := range opts {
names = append(names, o.name)
}
return names
}

func TestBatch_DeclaredVocabularyKeepsDeclarationOrder(t *testing.T) {
b := newBatch(map[string]anyblockbatch.FormatInfo{
"stage": {
Format: model.RelationFormat_status,
Options: vocab("Backlog", "In progress", "In review", "Blocked", "Done"),
},
}, nil)
assert.Equal(t,
[]string{"Backlog", "In progress", "In review", "Blocked", "Done"},
mintedOptions(t, b),
"declaration order, not alphabetical")
}

// a value no vocabulary declares must land after the declared ones whatever
// its name — this is the case that used to get an empty order id
func TestBatch_UndeclaredValueSortsAfterVocabulary(t *testing.T) {
b := newBatch(map[string]anyblockbatch.FormatInfo{
"stage": {
Format: model.RelationFormat_status,
Options: vocab("Backlog", "In progress", "Done"),
},
}, nil)
// "Abandoned" sorts first alphabetically and would jump the queue
b.OptionId(domain.RelationKey("stage"), "Abandoned")
b.OptionId(domain.RelationKey("stage"), "Zebra")

assert.Equal(t,
[]string{"Backlog", "In progress", "Done", "Abandoned", "Zebra"},
mintedOptions(t, b))
}

// with nothing declared, discovery order still produces real order ids
func TestBatch_UndeclaredOnlyStillGetsOrderIds(t *testing.T) {
b := newBatch(map[string]anyblockbatch.FormatInfo{
"stage": {Format: model.RelationFormat_status},
}, nil)
b.OptionId(domain.RelationKey("stage"), "Zebra")
b.OptionId(domain.RelationKey("stage"), "Abandoned")
assert.Equal(t, []string{"Zebra", "Abandoned"}, mintedOptions(t, b),
"first seen first, not alphabetical")
}

// order ids are per property, so two selects do not interleave
func TestBatch_OrderIdsArePerProperty(t *testing.T) {
b := newBatch(map[string]anyblockbatch.FormatInfo{
"stage": {Format: model.RelationFormat_status, Options: vocab("A", "B")},
"priority": {Format: model.RelationFormat_status, Options: vocab("Low", "High")},
}, nil)
firsts := map[string]string{}
for _, p := range b.pending {
if p.sbType != model.SmartBlockType_STRelationOption {
continue
}
d := p.snapshot.Details.Fields
key := d[detailRelationKey].GetStringValue()
if _, seen := firsts[key]; !seen {
firsts[key] = d[detailOrderId].GetStringValue()
}
}
require.Len(t, firsts, 2)
assert.Equal(t, firsts["stage"], firsts["priority"],
"each property starts its own sequence at the same midpoint")
}

func TestBatch_DeclaredColorsReachTheOption(t *testing.T) {
b := newBatch(map[string]anyblockbatch.FormatInfo{
"stage": {Format: model.RelationFormat_status, Options: []anyblockjson.OptionDefinition{
{Name: "Backlog", Color: "grey"},
{Name: "In progress", Color: "blue"},
{Name: "Done", Color: "lime"},
}},
}, nil)
assert.Equal(t,
map[string]string{"Backlog": "grey", "In progress": "blue", "Done": "lime"},
mintedColors(t, b, "stage"))
}

// A vocabulary that declares no colors still gets distinct ones: the palette
// is cycled in declaration order, so a five-status select does not render as
// five identical chips. Deterministic, unlike constant.RandomOptionColor.
func TestBatch_ColorlessVocabularyCyclesThePalette(t *testing.T) {
newFixture := func() *batch {
return newBatch(map[string]anyblockbatch.FormatInfo{
"tag": {Format: model.RelationFormat_tag,
Options: vocab("design", "research", "infra", "ops")},
}, nil)
}
want := map[string]string{
"design": "grey", "research": "yellow", "infra": "orange", "ops": "red",
}

assert.Equal(t, want, mintedColors(t, newFixture(), "tag"))
assert.Equal(t, want, mintedColors(t, newFixture(), "tag"),
"same input, same colors on every run")
}

// the cycle never hands out a color the vocabulary names explicitly
func TestBatch_ColorCycleSkipsDeclaredColors(t *testing.T) {
b := newBatch(map[string]anyblockbatch.FormatInfo{
"stage": {Format: model.RelationFormat_status, Options: []anyblockjson.OptionDefinition{
{Name: "Backlog"},
{Name: "In progress", Color: "yellow"}, // second in the palette
{Name: "Done"},
}},
}, nil)
assert.Equal(t,
map[string]string{"Backlog": "grey", "In progress": "yellow", "Done": "orange"},
mintedColors(t, b, "stage"),
"Done takes orange, not the claimed yellow")
}

// a vocabulary claiming the whole palette leaves the cycle nothing to pick:
// it must still terminate and produce a real color
func TestBatch_ColorCycleTerminatesWhenPaletteFullyClaimed(t *testing.T) {
declared := make([]anyblockjson.OptionDefinition, 0, len(constant.OptionColors())+1)
for i, c := range constant.OptionColors() {
declared = append(declared, anyblockjson.OptionDefinition{
Name: fmt.Sprintf("claim%d", i), Color: c.String()})
}
declared = append(declared, anyblockjson.OptionDefinition{Name: "uncolored"})

b := newBatch(map[string]anyblockbatch.FormatInfo{
"tag": {Format: model.RelationFormat_tag, Options: declared},
}, nil)
assert.Contains(t, constant.OptionColors(),
constant.OptionColor(mintedColors(t, b, "tag")["uncolored"]))
}

// a value nobody declared continues its property's cycle rather than
// restarting it, so it does not collide with the vocabulary's first color
func TestBatch_DiscoveredValueContinuesTheColorCycle(t *testing.T) {
b := newBatch(map[string]anyblockbatch.FormatInfo{
"stage": {Format: model.RelationFormat_status, Options: vocab("Backlog", "Done")},
}, nil)
b.OptionId(domain.RelationKey("stage"), "Abandoned")

assert.Equal(t,
map[string]string{"Backlog": "grey", "Done": "yellow", "Abandoned": "orange"},
mintedColors(t, b, "stage"))
}

// colors are per property: one select's palette position is not advanced by
// another's, the way order ids are already scoped (TestBatch_OrderIdsArePerProperty)
func TestBatch_ColorsArePerProperty(t *testing.T) {
b := newBatch(map[string]anyblockbatch.FormatInfo{
"stage": {Format: model.RelationFormat_status, Options: vocab("A", "B")},
"priority": {Format: model.RelationFormat_status, Options: vocab("Low", "High")},
}, nil)
assert.Equal(t, map[string]string{"A": "grey", "B": "yellow"}, mintedColors(t, b, "stage"))
assert.Equal(t, map[string]string{"Low": "grey", "High": "yellow"}, mintedColors(t, b, "priority"))
}

// A property's target types are written as ids, split the same way
// PropertyId splits relations: a type this bundle defines is referenced by
// its own document id so the importer relinks it, a bundled type by its
// bundled url — the form recommendedRelations already uses for _br<key>.
func TestBatch_ObjectTypeIdsSplitLocalAndBundled(t *testing.T) {
b := newBatch(nil, map[string]string{"wikiPerson": "type-person"})
got := b.objectTypeIds(anyblockjson.PropertyDefinition{
Key: "owner",
ObjectTypes: []string{"wikiPerson", "participant"},
})
assert.Equal(t, []string{"type-person", "_otparticipant"}, got)
}

func TestBatch_NoObjectTypesLeavesPropertyUntargeted(t *testing.T) {
b := newBatch(nil, nil)
assert.Nil(t, b.objectTypeIds(anyblockjson.PropertyDefinition{Key: "owner"}))
}

// The converter half of anyblockbatch's CheckTargetTypes ordering: a type this
// bundle DEFINES wins over the bundled type of the same name, even when its
// document carries no id — and then the target is the empty string, which
// names nothing anywhere. This is not a behaviour to keep, it is the reason
// CheckTargetTypes has to ask typeIds before it asks the bundle; the lint
// rejects such a bundle so this never runs in anger. If this assertion ever
// changes, TestCheckTargetTypes_BundledKeyDefinedLocallyWithoutIdIsReported
// has to change with it.
func TestBatch_IdlessLocalTypeYieldsAnEmptyTargetId(t *testing.T) {
require.True(t, bundle.HasObjectTypeByKey("page"),
"the fixture only bites while `page` really is bundled, i.e. while the two arms disagree")
b := newBatch(nil, map[string]string{"page": ""})
assert.Equal(t, []string{""}, b.objectTypeIds(anyblockjson.PropertyDefinition{
Key: "owner",
ObjectTypes: []string{"page"},
}), "the local arm wins and has nothing to offer — not the bundled url")
}

// End to end, over the real seam: a bundle whose property_internal_keys legend backs a
// slug (§3) must mint the property's Relation object and its declared select
// vocabulary under the STORED key, because that is the key the value's detail
// is written under. When the format table was keyed by the spelling, the
// format was never found: the value passed through raw, no Relation was minted
// at all, and the declared options sat on a relation nothing referenced.
func TestBatch_LegendBackedPropertyMintsItsRelationAndOptions(t *testing.T) {
const storedKey = "6a32d4856761631534b22f85"
const legend = `"property_internal_keys": {"priority": "` + storedKey + `"},`

dir := t.TempDir()
typeDoc := filepath.Join(dir, "task.type.json")
require.NoError(t, os.WriteFile(typeDoc, []byte(`{"version": 2, "kind": "object_type",
"internal_key": "task", "id": "type-task", `+legend+`
"type_settings": {"property_definitions": [{"property": "priority", "name": "Priority", "format": "select",
"options": ["High", "Low"]}]}}`), 0o644))

formats, err := anyblockbatch.ScanFormats([]string{typeDoc})
require.NoError(t, err)
b := newBatch(formats, map[string]string{"task": "type-task"})

// types first, exactly as OrderTypesFirst arranges the walk — the type
// document is what drives PropertyId, and so what mints the Relation
_, _, _, err = convertFile(dir, typeDoc, b, false, nil)
require.NoError(t, err)

_, snap := convertDoc(t, b, "one.json", `{"version": 2, "id": "obj-1", "type": "task", `+legend+`
"properties": {"priority": "High"}}`)

// the value reached the detail under the stored key, as an option id
optionID := snap.Details.GetFields()[storedKey]
require.NotNil(t, optionID, "the legend-backed value must land on the stored key")
got := optionID.GetListValue().GetValues()
require.Len(t, got, 1, "a resolved select value is an option id list, not the raw string %q",
optionID.GetStringValue())

// and that id is the DECLARED option, minted up front by newBatch under
// the same stored key — not one discovered from the value with no order id
var relations, options int
byID := map[string]*model.SmartBlockSnapshotBase{}
for _, p := range b.pending {
byID[p.id] = p.snapshot
switch p.sbType {
case model.SmartBlockType_STRelation:
relations++
assert.Equal(t, storedKey, p.snapshot.Details.Fields[detailRelationKey].GetStringValue())
assert.Equal(t, "Priority", p.snapshot.Details.Fields[detailName].GetStringValue())
case model.SmartBlockType_STRelationOption:
options++
assert.Equal(t, storedKey, p.snapshot.Details.Fields[detailRelationKey].GetStringValue())
assert.NotEmpty(t, p.snapshot.Details.Fields[detailOrderId].GetStringValue(),
"a declared option is pre-minted with an order id; one discovered from a value is not")
}
}
assert.Equal(t, 1, relations, "exactly one Relation object, for the stored key")
assert.Equal(t, 2, options, "both declared options, and no third discovered under the spelling")

require.Contains(t, byID, got[0].GetStringValue())
assert.Equal(t, "High",
byID[got[0].GetStringValue()].Details.Fields[detailName].GetStringValue())
}
Loading
Loading