aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/sisudoc
Commit message (Collapse)AuthorAgeFilesLines
* --serial default behaviour (--parallel an option)Ralph Amissah8 days1-31/+25
| | | | | | | | | | serial processing, it turns out, is significantly faster and more efficient for tested use-cases, which came as a surprise. As the parallelization option buys nothing, serial processing is set as default. Parallel processing remains as an option (where available, as before). (assisted by Claude-Code)
* parallelise: show_abstraction & ocda_db as the restRalph Amissah9 days1-0/+9
| | | | | | | | | | | | | | | | | | | | | | | | Fix issue with consistency (flags run serial & parallel inconsistenly). Both write one file per document per language and share no handle, so they belong on the list with html, epub, text and sqlite_discrete. The guard above the list still takes out --pod, --pod2, --source and the shared sqlite db actions before it is reached, so those stay serial as they were; checked. However measurement tests show parallel turn out to result in a processing slowdown, on the (35) sample markup documents, run on multiple passes: run on 16 cores a slow down of about 20% for eleven times the cpu! --text 4.83-5.10 s wall 48-52 s user --text --serial 3.94-4.05 s wall 4.2-4.4 s user will make serial run the default. (assisted by Claude-Code)
* ssp: abstraction directory cleared onceRalph Amissah9 days1-13/+33
| | | | | | | | | | | | | | | | | abstraction directory cleared once, by whichever language is first The .ssp writer clears stale files out of pod/<doc>/media/abstraction/, and that one directory is shared by every language of a document. The clearing is now done once per directory per run, by whichever language reaches it first, with the lock held across it. A language that finds the directory already prepared has passed through that same lock before writing, so the clearing it skipped had completed before its own write began: no .ssp produced on a given run can be removed during it. (removes possibility of a race condition on parallelisation) (assisted by Claude-Code)
* sqlite: schema version, & fail run on writes failRalph Amissah9 days2-4/+67
| | | | | | | | | | | | | | | | | | | | | | | | | Spine now declares sqlite_db_schema_version and stamps it into the database as PRAGMA user_version when the tables are created, in both the shared and the discrete DDL blocks. On opening an existing database it compares, and says once per run which version it found and which it writes. Failures are now tallied (shared, the output can run in parallel), reported one line each on stderr naming the operation, and main exits 1 without printing "run complete, ok". Two tests under test/, both taking the spine binary as their first argument and building their own database from data/pod unless $SpinePOD says otherwise: test-search-db-schema.sh names: every column the search form uses exists, and spine's declaration, the database's stamp and the search form's expectation all agree test-search-cgi.sh behaviour: the real search binary answers real requests against a fresh database, no web server involved, the probe values read out of whichever database it is given (assisted by Claude-Code)
* html metadata: a link to the ocda.dbRalph Amissah9 days2-4/+27
| | | | | | | The metadata page gains a line between the markup source and the source digests. (assisted by Claude-Code)
* --ocda-db replaces --show-abstraction-dbRalph Amissah9 days5-23/+43
| | | | | | | | | ocda (object centric document abstraction) flag --ocda-db (or --abstraction-db) replaces --show-abstraction-db rename results in consequently large diff
* output: the abstraction artefacts live with the podRalph Amissah9 days4-28/+106
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | pod/ holds all document source representations: pod/<doc>/ source tree pod/<doc>/media/abstraction/<uid>.ssp abstraction, as text pod/<doc>.zip tree, zipped, .ssp included pod/<doc>.digests.txt sha256s of what is in them pod/<uid>.ocda.db abstraction, as sqlite db <lang>/abstraction/ is gone. pod/<doc>/media/abstraction/<uid>.ssp preferred as having the images (found within the pod tree) which .ssp needs to reproduce a document but does not carry on its own. The .ocda.db sits carries the images as well and (like the pod.zip) can be used to reproduce a document directly. digests.txt now covers the database as well as the zip, the source and the .ssp; (as does the metadata html page). Two ordering issues addressed: - the pod builder clean-slates pod/<doc>/ before regenerating it, and the .ssp is now written before that runs. The clean slate now leaves media/abstraction/ alone, and the .ssp writer clears that directory itself on the first language of a run, so a .ssp for a language the document no longer is removed and cannot be bundled. - for a multi-language document the .ssp files accumulate one language at a time and are bundled on the last, which is why the directory cannot simply be emptied by whichever (language) gets there first. (assisted by Claude-Code)
* ocda loader: one way in whatever the sourceRalph Amissah9 days3-0/+224
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | sisudoc.ocda.abstraction.load names the five things a document can be read from, tells them apart, and loads the two that are self-describing artefacts: .sst / .ssm + images the markup source pod (dir) + images the same, bundled pod .zip the same, zipped .ssp + images the abstraction, as text .ocda.db the abstraction, sqlite, images inside abstractionSourceOf(path) is the detection, by name and for a directory by whether it holds pod.manifest. abstractionLoad(path) returns a LoadedAbstraction: the source kind, whether it was loaded, why not when it was not, and the document itself. The three source forms are deliberately not loaded here. Reading them is the parser's job (sisudoc.ocda.meta.metadoc spineAbstraction) and it needs the manifest, environment and configuration that spine.d assembles, none of which belongs in a loader. What this gives that case is the dispatch and a plain statement of where it is handled, rather than a silent empty result. spine --abstraction-source=<path> says what a path is and, for an artefact, loads it and reports what came back: the document, title and author, header block sizes, object counts and the objects in each section. Exit 0 when an abstraction was loaded, 1 when not. (assisted by Claude-Code)
* ocda db: <doc>.ocda.db, and a script to run 4 testsRalph Amissah9 days3-4/+4
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The per document database is now written as <doc_uid>.ocda.db rather than <doc_uid>.abstraction.db. Shorter, and it says what is in the file: the object centric document abstraction, not "some abstraction". .ocda.db pairs with .ssp and cannot be mistaken for the collection search database (spine.search.db). test/run-tests.sh runs the four in sequence, one line of result each, and a summary. It re-runs itself inside nix shell "nixpkgs#sqlite" if sqlite3 is not on PATH, so this is all that is needed: SpinePOD=../../markup/sisudoc-spine-samples/markup/pod-samples/pod \ ./test/run-tests.sh ./bin/spine-ldc The tests are independent (with test-abstraction-ssp.sh run first): 1 test-abstraction-ssp.sh is first because it is the one that says whether the abstraction itself moved; if it fails the others are answering a different question than you think 2 test-abstraction-ssp-roundtrip.sh reads the committed reference set, so it is a statement about the current binary only if 1 passes 3 test-abstraction-db.sh and 4 test-abstraction-db-roundtrip.sh generate both artefacts themselves and depend on nothing committed (assisted by Claude-Code)
* ocda db: built from the .ssp, (tethered)Ralph Amissah9 days4-10/+49
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The ocda database is now built from the .ssp itself: the writer's lines are emitted, read straight back by ssp_in, and the objects that come out populate the database. Anything the .ssp does not carry, the database will not have either, by construction (rather than by test). [instead of as previously through a second walk over the in-memory abstraction] - spineAbstractionTxt is split: sspDocumentLines(doc) returns the whole .ssp as lines, and the file writer emits them. Output neutral, the reference test confirms. - spineAbstractionDb takes the abstraction as an argument rather than taking doc.abstraction. - sspRoundTripAbstraction(doc) in ssp_in is the join: lines out, lines in, abstraction returned. Both call sites in spine.d use it. - the header blocks and the image blobs still come from doc_matters (as: the .ssp does not carry image bytes). All (35) markup sample sourced databases built through the .ssp have byte identical SQL dumps to the one built directly before the change. That comparison also found one reader inaccuracy, which the .ssp round trip could not see because the writer omits the field either way: an absent identifier was restored as the ocn in every case, but for an object with no ocn it was empty ("a"~N identifiers are always written). Fixed; the two artefacts checking each other is what caught it. (assisted by Claude-Code)
* a reader for the ocda db, and its round tripRalph Amissah9 days3-0/+307
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | sisudoc.ocda.abstraction.db_in reads a <doc>.abstraction.db back into ObjGenericComposite[][string], the same value ssp_in returns from a .ssp, so a consumer need not know which artefact it was handed. It mixes in the .ssp reader for that shared document struct rather than declaring a second one. --db-round-trip=<file.abstraction.db> reads a database and emits it as .ssp on stdout, through sspObjectRecord as the other round trip does. Held against the .ssp written from the same document, this says whether the two artefacts really carry the same thing: not a count of fields, as test-abstraction-db.sh does, but the whole document reconstructed from the database and compared to the text. SpinePOD=... ./test/test-abstraction-db-roundtrip.sh ./bin/spine-ldc PASS: all 35 databases re-emit their document's .ssp exactly It passed on the first run over the whole sample set, which is evidence that the database is now field-complete against the .ssp rather than merely counting the same. Four tests with different checks: test-abstraction-ssp.sh the abstraction has not changed test-abstraction-db.sh the two serialisations agree, field by field test-abstraction-ssp-roundtrip.sh the .ssp can be read back whole test-abstraction-db-roundtrip.sh the .db can be read back whole (assisted by Claude-Code)
* ocda: clean heading text used for navigationRalph Amissah9 days3-20/+97
| | | | | | heading text used for navigation is normalised, and | escaped (assisted by Claude-Code)
* ocda: the eight slot arrays are fixed lengthRalph Amissah9 days1-5/+5
| | | | | | | | | | | | | | | | | | | | | | markedup_ancestors, collapsed_ancestors, the two dom status arrays and heading_ancestors_text were declared as int[] / string[] with a literal default: int[] markedup_ancestors = [ 0, 0, 0, 0, 0, 0, 0, 0, ]; which is one array, shared by every default constructed object: write into one by index and you write into all of them. They are now int[8] / string[8], where that is impossible. Output neutral confirmed by reference test. Provides a fix: Previously the parser always assigned these whole and with .dup so it never went through the shared array. This left a trap in previous code which caught the new .ssp reader, whose first version wrote by index and gave every object in a document the last one's ancestors. (assisted by Claude-Code)
* ocda: a reader for .ssp, and a round trip checkRalph Amissah9 days3-0/+381
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | sisudoc.ocda.abstraction.ssp_in reads a .ssp file back into ObjGenericComposite[][string], the same value the parser produces, so anything that consumes the abstraction can be fed from a .ssp instead of from markup. The three header blocks come back as key/value with their order preserved. --ssp-round-trip=<file.ssp> loads a file and emits it again on stdout, using sspObjectRecord, the writer's own definition of a record. So the check is against the writer, not against a second description of the format: ./bin/spine-ldc --ssp-round-trip=test/reference/abstraction/<doc>.ssp \ | diff test/reference/abstraction/<doc>.ssp - BUG as yet to FIX 27 of the 35 reference documents round trip byte identically. The other 8 fail on two defects in the *writer* that the round trip found, and which are left for a decision: 1. .heading_ancestors_text and .lev4_subtoc can carry a raw newline, because a heading's text may contain a line break. The value then spans two physical lines and the format's rule that a value runs to the end of the line is broken. 194 and 22 occurrences, in the seven live-manual translations. 2. .heading_ancestors_text joins its eight slots with "|" while the text in them may itself contain "|". 22 occurrences in revisiting_the_autonomous_contract. Both need an escape (or normalisation at source) and both change the .ssp, so require a decision and another reference regeneration. ocda: export the .ssp reader from the abstraction package package.d is the re-export surface for consumers that want to reach the abstraction without depending on the directory layout; the reader belongs there beside the writer. (assisted by Claude-Code)
* ssp: definition of an object record, for readersRalph Amissah9 days1-301/+310
| | | | | | | | | | | | | | | | | | | one definition of an object record, so a reader can be held to it. Pure refactor: the per-object emission moves out of the write loop into template sspObjectRecord, taking the object and its section and returning the lines. The writer mixes it in and calls it. The point is the reader that follows. With one definition of what an object record looks like, a round trip can be checked against the writer itself rather than against a second, hand-kept description of the format, which is how the .ssp and the abstraction db drifted apart in the first place. (emitted text is byte identical) (assisted by Claude-Code)
* .ssp: three additional fields the writers readRalph Amissah9 days2-2/+36
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | With these the .ssp carries every field the output writers read. The two that were missing were both read by the sqlite search writer, and one of them is needed by anything that wants to know where an object sits in a segmented output: - .segment_html_is / .segment_epub_is (tags.html_segment_anchor_tag_is, epub_segment_anchor_tag_is), the segment an object *belongs to*, as against .segment (in_segment_html), which is the segment a heading *opens* and is set on headings only: 32 of 36 headings and 0 of 109 paragraphs in the autonomous contract. Without them a reader cannot tell which segment a paragraph is in. - .identifier for non-heading objects, the "a"~N series given to objects whose number is suppressed. A heading already carried it on its declaration line; every other object now carries it as a property, on the same condition, that it differs from the ocn. The same two segment fields are added to the abstraction db (segment_html_is, segment_epub_is) and to the cross-check test, so the two serialisations stay level. .ssp reference regenerated output run (--text --latex --epub --html --curate) byte identical to before the change, as it must be since no output writer reads either artefact. (assisted by Claude-Code)
* ocda db: full text index and three viewsRalph Amissah9 days1-0/+21
| | | | | | | | | | | | | | | | | | | | | | | | | An FTS5 virtual table over the object text, external content (content='objects', content_rowid='id') so the text is not stored twice, built from the rows once they are written. This is per document, for exploring one abstraction; the collection wide search database is a separate thing and is unaffected. SELECT o.ocn, o.text FROM objects_fts f JOIN objects o ON o.id=f.rowid WHERE f.text MATCH 'mercatoria'; War and Peace, 12,135 objects: 100 full text queries in 0.23s including 100 process starts, 400 hits for Napoleon. Three views name the shapes worth asking for, so that datasette or anyone opening the file finds them without writing SQL: outline (headings with their levels and interval), citable (ocn > 0), document_files (what the file carries). Over the document sample the index size is/adds about a quarter that of the files. (assisted by Claude-Code)
* ocda db: rows for repeatable fields & images insideRalph Amissah9 days1-0/+186
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Four things in the abstraction are open ended lists that a single row cannot hold, and which the database therefore could not express at all: a paragraph's extracted links, its anchor tags, a heading's lev4 subtoc entries, and the image records. Each now has a table keyed on the object, ordered by seq: object_images, object_links, object_anchors, object_subtoc And the images themselves are carried in the file, in a files table with role, name, bytes, sha256, pixel size and the blob. The point is self-sufficiency: html copies every image into its output tree, epub and odt place them in their zips, and xelatex reads them from disk, so an abstraction without the bytes cannot produce a document, only its text and structure. The table is deliberately generic rather than an images feature, so that the same mechanism can later carry the source .sst or the pod's configuration by adding a row with another role, and so that a reader can ask whether a database carries its images (select count(*) from files where role='image') and fall back to a directory beside it when the answer is zero. Bytes are stored exactly as read and the digest is taken over them. Only images the document references are carried: the sisu manual's media/image holds 10 files and 5 are in its image list, so 5 are stored. (assisted by Claude-Code)
* ocda db: structural fields (arrays as JSON)Ralph Amissah9 days1-82/+174
| | | | | | | | | | | | | | | | | | | | | | | | | Added the structure ocda was extended to compute as JSON arrays (a column each). JSON arrays, allows SQL can reach inside them (with CHECK(json_valid()) on each) select ocn from objects where json_extract(ancestors,'$[4]') = 9; select ocn from objects where json_array_length(children) > 3; ancestors and table_widths change format for this reason; nothing outside spine reads the file yet, and metadata now records what it is: schema.name, schema.version, source.filename, source.language. INTEGER PRIMARY KEY in place of AUTOINCREMENT, which was paying for a guarantee not needed here and creating sqlite_sequence, and UNIQUE(section, seq), which is the table's actual key. Note for anyone editing this file: the schema is a D string handed to sqlite, so comments inside it must be SQL comments. A D /+ +/ comment there aborts the statement and leaves the table missing, silently. (assisted by Claude-Code)
* sqlite db abstraction: make readable as read-onlyRalph Amissah9 days2-12/+10
| | | | | | | | | | drop WAL so the file can be read read-only: journal_mode=WAL was set in the file header and A WAL database cannot be opened by a reader that cannot also write beside it (this ruled out a read-only directory, read-only media, and anything that wants to read the artefact where it sits). (assisted by Claude-Code)
* ocda: remove dead fieldsRalph Amissah9 days4-18/+16
| | | | | | | remove four dead fields, two of them serialised, none of the them were assigned (or called for reading). (assisted by Claude-Code)
* ocda a fix & ssp code-style use curly bracesRalph Amissah2026-09-032-72/+147
| | | | | | | | ocda: fixed inverted object_number_off on body paragraphs ssp: make use of curly braces (code-style) (assisted by Claude-Code)
* ocda: bug fix related to dummy headingsRalph Amissah2026-09-031-0/+1
| | | | | | | | | | | | | | | | | --+# clears dummy_heading_multiple_objects, fixed until fix, following any ---# ... --+# block every ~# object in the document was marked dummy_heading, (and the output generators that honour that flag suppressed its text: so ~# behaved as -#. One line added to correct the --+# branch. ~# suppresses the object's number only, content is still shown. -# is for a heading introduced solely to segment an output: object number suppressed and the heading text omitted from the body where the output format allows, the toc entry kept. Both were already correct in isolation; only the leaked block flag broke ~#. (assisted by Claude-Code)
* abstraction/.ssp: fix doc structure representationRalph Amissah2026-08-304-55/+162
| | | | | | | | | | | | fix to --abstraction/.ssp: fix document structure, representation parent, ancestors, children. Make parent_ocn, the marked-up and collapsed ancestor arrays, and the children correctly represent the document structure. fixed (assisted by Claude-Code) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01836BxBb8LvVKJ8sBg7Dx6U
* .ssp: doc structure related fixes (& to epub toc_nav)Ralph Amissah2026-08-283-8/+7
| | | | | | | | | | | | | | | | | | | | | | | | | | for document abstraction and its .ssp output, removed the requirement of including --abstraction & --serial flags to produce correct output (for: .dom_status, .dom_status_collapsed & .last_descendant) - meta_processing_xml_dom() includes show_abstraction, so --pod2 and --show-abstraction run the dom pass; last_descendant is derived from that pass via after_doc_get_descendants() The accumulators are now verified as eight wide locals of docAbstraction(), so each document starts clean and no two threads share one. - bug: the four dom accumulators were template scope (shared) and nine wide, while their end of document reset was eight wide, so the first document of a run differed from the rest and parallel runs raced on one buffer, (which also mis-nested epub toc_nav) test/ reference .ssp regenerated: accelerando only, trailing zero dropped. test-abstraction-ssp.sh now runs parallel and diffs output against a serial run. (assisted by Claude-Code)
* image digest: include headings in image scanRalph Amissah2026-08-281-17/+21
| | | | | | | | | | | | | | Assumed that headings do not include images, obj_digest() looked for images only in the non-heading branch, however, the markup does not forbid one, and an object type left out of the scan carries neither digest nor .ssp record, silently. Open Issue: image_list, which drives pod bundling and the @doc_has images count, is gathered only from para objects (and only the first image of each), so an image in a heading or in a block body is recorded in the abstraction but not bundled. (assisted by Claude-Code)
* doc_has: count inline links & notes, always 0, fixRalph Amissah2026-08-281-0/+26
| | | | | | | | | | | | | Count them over the assembled body objects, (the body alone). Previously dochas["inline_links"], ["inline_notes"] and ["inline_notes_star"] were initialised and read but never incremented, so @doc_has reported 0 inline links and 0 notes for every document while the block counters (codeblock, poem, group, block, quote, table) were counted. create_abstraction_db.d records the same values as document metadata. (assisted by Claude-Code)
* has.images: set flag but was not assigned, fixRalph Amissah2026-08-283-0/+20
| | | | | | | | | | | every object with an .image record should now carry the images flag. Previously DocObj_Has_.images was read but never written anywhere in the tree, so no object carried it: .ssp emitted has_images = 0 for every object of every document. (assisted by Claude-Code)
* image digest: single read, cache file read & hashRalph Amissah2026-08-282-2/+11
| | | | | | | | | | | | | | | | | | | | | | | | For image digests cache file read & hash, one read per image file Fix issue: images referenced from several objects were read and hashed once per reference. [as an example in the sisu-manual sm_tux.png accounts for 6 of the document's image records (12 across the sample set), each of them a fresh read of the same bytes, and source_pod.d hashes the file again for digests.txt.] Memoise on the full path within safeComputeHashAndSize. static is thread-local in D, so documents processed in parallel each get their own cache and no synchronisation is involved. Files are not expected to change during a run. Output is unchanged: identical .ssp output. (An image that is missing or unreadable now warns once to stdout rather than once per reference). (assisted by Claude-Code)
* .ssp digest: add image file's own pixel dimensionsRalph Amissah2026-08-283-5/+34
| | | | | | | | | | | | | | | Complete image file digest identity record: add px:<width>x<height> to the .ssp image record, read from the image file header (imageformats.read_image_info, no decode): .image: won_benkler_2_1.png sha256:5BA4...D128 bytes:93861 px:420x342 (The w##h## dimensions in the markup are display dimensions, not the file's: they are author set, or where given as w0h0 are derived by _image_dimensions and capped to a maximum display width, and they become the width/height attributes of the output format image display). (assisted by Claude-Code)
* .ssp digests: report missing or unreadable imageRalph Amissah2026-08-282-7/+22
| | | | | | | | | | | | | | | | | | | | image digest: record missing or unreadable image, do not abort safeComputeHashAndSize() open image with guard, so a document naming an image absent from media/image/ throws ErrnoException out of the abstraction stage. as with _image_dimensions() (which warns and continues where issue with image) warn to stdout and record the fact in the abstraction: .image: ffa.png missing:true the abstraction would now state that the object embeds an image it could not read, rather than the .ssp resemble one produced from a complete pod. The sha256 summary line marks the same case MISSING OR UNREADABLE. (assisted by Claude-Code)
* .ssp: image as repeatable self-describing propertyRalph Amissah2026-08-281-26/+14
| | | | | | | | | | | a repeatable record, one line per image, filename first: .image: won_benkler_2_1.png sha256:5BA492C2...D128 bytes:93861 an object embedding more than one image emits more than one .image line. (assisted by Claude-Code)
* sha summary, mostly cosmetic, some work on imagesRalph Amissah2026-08-264-12/+34
|
* digests (sha256) a revisitRalph Amissah2026-07-315-28/+87
|
* digest (sha256) for document root heading (title)Ralph Amissah2026-07-311-0/+3
| | | | | | corrects an omission this hould have been included to start with reference tests texts regenerated
* change search result symbols for show index & textRalph Amissah2026-07-041-3/+3
|
* ocda public surface + dub.json import-path and dyaml cleanupsRalph Amissah2026-05-251-0/+68
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Three small follow-ups to the ocda/outputs split: 1. Add src/sisudoc/ocda/package.d (module sisudoc.ocda) as a 2-line public re-export of sisudoc.ocda.abstraction. Provides downstream consumers with a canonical "import sisudoc.ocda;" entry point and a stable handle for eventual peer-repo packaging of the abstraction library. 2. Fix the D import-path root in dub.json so it matches the declared module names: - spine:abstraction sub-package "importPaths": [ "./src/sisudoc" ] -> [ "./src" ] - main package buildTypes (dmd, ldc2, ldmd2, gdc, gdmd) "-I=src/sisudoc" -> "-I=src" The modules are named sisudoc.ocda.* / sisudoc.outputs.* / sisudoc.* so the filesystem-based resolver needs to see ./src as the root (so <root>/sisudoc/ocda/X.d resolves). 3. Replace dyaml sub-package's destructive preGenerateCommands ("rm -rf ./src/ext_depends/D-YAML/{examples,testsuite}") with declarative excludedSourceFiles globs. The two directories do not exist in the vendored D-YAML tree, so the rm was a no-op in practice; the glob form is defensive (would silently skip them if they were ever re-introduced) and removes the destructive side-effect from every build. (assisted by Claude-Code)
* ocda + outputs split: module/import + dub.json fixupsRalph Amissah2026-05-2550-271/+271
| | | | | | | | | | | Modules and imports rewritten to sisudoc.ocda.* and sisudoc.outputs.*; dub.json excludedSourceFiles and the spine:abstraction sub-package sourcePaths collapsed to ./src/sisudoc/ocda. Verified: nix build .#spine-overlay-ldc clean. (assisted by Claude-Code)
* separate abstraction lib from output processingRalph Amissah2026-05-2549-0/+0
| | | | | | create new directories under ./src/sisudoc ocda & outputs in order to separate the document abstraction library from downstream output processing (stuff broken till paths & modules fixed)
* org files out of sync, fixsisudoc-spine_v0.20.0Ralph Amissah2026-05-233-1963/+0
| | | | (also cgi_sqlite_search_form.d did not belong here)
* css: html (additional) tags alignmentRalph Amissah2026-05-221-0/+204
| | | | | | | | | | | | css: align body-flow <ul>/<li> & <details>/<summary> with <p> Not used by sisudoc-spine but for hand-authored body-flow markup such as the current homepage / body-flow, added block to each of the four html CSS string heredocs in src/sisudoc/io_out/xmls_css.d Existing tags are left in place and untouched. (assisted by Claude-Code)
* decouple abstraction phase1:2Ralph Amissah2026-05-223-7/+10
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | phase1 step2: move SSP serialiser into sisudoc.abstraction package git mv src/sisudoc/io_out/create_abstraction_txt.d to src/sisudoc/abstraction/ssp.d Module rename: sisudoc.io_out.create_abstraction_txt -> sisudoc.abstraction.ssp Completes phase1: after this commit the sisudoc.abstraction package has zero outgoing edges into sisudoc.io_out. The library produces both the in-memory document object model AND the .ssp text serialisation without referencing any output-side module. The serialiser previously imported sisudoc.io_out.paths_output for the single purpose of constructing the .ssp output path. That import is dropped; the path construction is inlined as three lines of std.path (chainPath / asNormalizedPath / array) producing <output_path>/<language>/abstraction/<doc_uid_out>.ssp - byte-for-byte the same path the previous spineOutPaths!() call produced. Updated: - src/sisudoc/abstraction/ssp.d - module decl + inline path - src/sisudoc/abstraction/package.d - public import .ssp - src/sisudoc/spine.d - import sisudoc.abstraction.ssp (x2) Completes decouple abstraction phase1 (assisted by Claude-Code)
* decouple abstraction phase1:1Ralph Amissah2026-05-221-0/+85
| | | | | | | | | | | | | | | | | phase1 step1: introduce sisudoc.abstraction package re-export surface Create src/sisudoc/abstraction/package.d as a library-facing re-export module for the document-abstraction stage. The surface currently re-exports: - sisudoc.meta.metadoc (spineAbstraction, A-layer entry) - sisudoc.meta.metadoc_from_src (docAbstraction, B-layer entry) No code moves; no behaviour change. The package exists so external consumers can `import sisudoc.abstraction;` and reach the entry points without depending on spine's internal directory layout. (assisted by Claude-Code)
* decouple abstraction phase0:2Ralph Amissah2026-05-224-6/+6
| | | | | | | | | | | | | | | phase0 step2: move curation modules from meta/ to io_out/curate/ Curation modules moved to src/sisudoc/io_out/curate/, module declarations renamed sisudoc.io_out.curate.metadoc_curate* from sisudoc.meta.metadoc_curate* and updated spine.d imports. File contents are otherwise unchanged. Completes phase0: meta/ now has zero io_out imports - the abstraction core's outgoing deps are now only: meta/ internals + io_in/ + ext_depends/D-YAML (assisted by Claude-Code)
* decouple abstraction phase0:1Ralph Amissah2026-05-221-2/+0
| | | | | | | | | | | | | phase0: drop vestigial io_out.hub coupling from meta/metadoc.d phase0 step1: abstraction-library extraction/decoupling: meta/ should not import io_out/. Removed unused call to `import sisudoc.io_out.hub;` `mixin outputHub;` from `template spineAbstraction()`. (the load-bearing UFCS site is spine.d:92 which has its own `mixin outputHub). (assisted by Claude-Code)
* source_pod: --pod2 include (doc abstraction) .sspsisudoc-spine_v0.19.0Ralph Amissah2026-05-161-23/+32
| | | | | | | | | | | | | | | - include all (doc abstraction) .ssp in pod zip and in digests - fixed: for multi-language pods built with --pod2, only the last language's .ssp file was being written into pod.zip and listed in .digests.txt each languages' .ssp files were on disk in the pod directory (copied during their own per-language passes) but were not in final zip as it was being built once for each language and writing over previous, (only the last one remaining). The solution is to follow the pattern already used to avoid this by .sstm and .ssi, namely wait for the last language and iterate the manifest_list_of_languages internaly. (assisted by Claude-Code)
* latex: some fixes for xelatex 2025Ralph Amissah2026-05-161-4/+4
| | | | (assisted by Claude-Code)
* sqlite: stop on missing/unwritable sqlite-db-pathRalph Amissah2026-05-161-4/+24
| | | | | | - fatal error on missing/unwritable --sqlite-db-path (assisted by Claude-Code)
* org headers rearranged (& odd hilighting issue)Ralph Amissah2026-05-042-3/+1
| | | | | - odd hilighting issue ... must result from my org config, but "fix" makes things easier for me.
* add children_headings to document abstractionRalph Amissah2026-04-223-12/+22
| | | | | | | | | | | | | | | | | Add int[] children_headings field to DocObj_MetaInfo_ and compute it in the post-processing pass of metadoc_from_src.d, right after last_descendant_ocn. Single O(n) pass builds a parent_ocn -> child heading OCNs map, then assigns to each heading object. Useful for tree-structured output. The .ssp serializer now reads directly from the abstraction field instead of pre-computing its own map. metadoc_object_setter.d: +1 line (field declaration) metadoc_from_src.d: +17 lines (computation) create_abstraction_txt.d: -10 lines (simplified) Co-Authored-By: Anthropic Claude Opus 4.6 (1M context)