summaryrefslogtreecommitdiffhomepage
path: root/gleam/src/ocda/model.gleam
AgeCommit message (Collapse)AuthorFilesLines
2026-10-02gleam sisudoc-ocda-libRalph Amissah1-0/+49
2026-10-02model 2.1: ocn_range, the verses a poem holdsRalph Amissah1-0/+4
The format now states, for a container, the first and last ocn of what it holds: .ocn_range in the .ssp, ocn_range_first and ocn_range_last in the .ocda.db. A poem is the one container, and since 2.1 it carries ocn 0 and comes before its verses. Both fields in the model, read by both loaders, in both languages, and written by the dump. A 2.0 database has neither column, so each loader asks sqlite whether it is there and reads a 2.0 file as having no ranges, which is what 2.0 meant; checked on a published 2.0 database, D and Gleam giving the same dump. Over the 36 document-languages written by spine at 2.1, the four dumps (D and Gleam, .ssp and .ocda.db) are byte identical, 36 of 36. (assisted by Claude-Code)
2026-10-02images from the source, and the epub segment fieldsRalph Amissah1-0/+4
Two additions the epub writer needed and could not have without. ocda.images: an image's bytes, from whichever input form the document came from. The model names images, with digest, byte size and pixel size, and does not hold them - which is right, an abstraction is not a bundle of assets - so something has to know where they are, and the answer depends on the input form rather than on the output. Hence the library. .ocda.db the files table, role 'image', by name .sisupod the archive entry whose path ends in /image/<name> .zip the same .ssp beside it: a pod puts the abstraction in media/abstraction/ and its images in media/image/, so the path is documented rather than guessed canGiveImages says whether a source can answer at all, because a writer has to know before it emits an <img>: a manifest item whose file is absent and an <img> whose file is not in the manifest are both epubcheck errors. A .ssp outside a pod has nowhere to look, and that is absence rather than error - the document is still readable. Reading a blob needed sqlite3_column_blob rather than _column_text: an image read as text stops at the first zero byte, and a png's ninth byte is zero. And two more model fields: segment_epub and segment_epub_is. The epub segments at heading levels 1 to 4 where the html segments at level 4 alone, so the two are different files and the abstraction records both. Nine of the 36 documents have images; the four-way agreement still holds at 36 of 36 each way. (assisted by Claude-Code)
2026-10-02two fields the model was missing, found by writing an html writerRalph Amissah1-0/+4
anchor_tags and heading_lev_anchor. Both are in the specification's property table and neither was read, so they went to the unknown counts and a writer over the library had no way to emit a link target. The cost was visible the moment an html writer existed: 154 of its internal links pointed at nothing, because a note reference points at the endnote's anchor and the endnote's anchor is an anchor_tag. heading_lev_anchor is the name a document's own cross references use, 1,854 of them in the reference collection. The .ssp carries them as repeated .anchor_tag and as .heading_lev_anchor; the database as the object_anchors child table and the heading_lev_anchor column. Both loaders read both now, the dump reports them, and the four-way agreement still holds: 36 of 36 each way. One thing to watch when adding a column to the database query: the read is by position, so a column inserted in the middle shifts every one after it. Adding heading_lev_anchor after segment_html_is broke all 36 documents until it moved to the end of the select. (assisted by Claude-Code)
2026-10-02ocda-dump, and the four disagreements it foundRalph Amissah1-0/+270
The agreement check compared ten fields of each object. That was enough to find the blank-first-line defect and not enough for anything subtler, so this adds ocda-dump: the whole loaded model written out canonically, every field, sorted dictionaries, sections in format order, defaults omitted. It is an oracle, not an output format. Two implementations of this model can now be compared with diff. Run against the reference collection it found four disagreements between the two loaders, none of which the ten-field check could see. 1. .identifier and .anchor. The .ssp emits a property only when it carries information, so the identifier is absent when it is simply the ocn and the anchor is absent when it is the segment name; the database stores both unconditionally. Over one document that is 1,314 identifiers and 69 anchors. The loader now derives them, which is what the model means: an object's identifier is its ocn unless it says otherwise. 2. make and doc_has. The database has one metadata table where the text form has three blocks, and tells them apart by a key prefix. The loader now unprefixes them, so a writer asks doc.make["footer"] whichever serialisation it was handed and never doc.metadata["make.footer"]. 3. The JSON columns were read by a character scan that stripped brackets and quotes, which is wrong the moment a value holds an escape: JSONValue.toString escapes "/" as "\/", so every heading containing a slash came back with a backslash in it. All 36 document-languages differed on heading_ancestors_text. Now parsed as JSON. 4. translation.ocn_aligned is in the database and not in the .ssp, legitimately: it says whether the languages share one numbering, and a .ssp is one language and cannot know. It is a fact about the file rather than the document, so the loader leaves it out rather than letting a writer tell which serialisation it was given. Metadata values are also stripped now, as the text loader strips them: one topic_register value carried surrounding spaces in the database that the text form cannot represent. After all four: 36 of 36 document-languages give byte identical dumps from their .ssp and from their .ocda.db. (assisted by Claude-Code)