aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorRalph Amissah <ralph.amissah@gmail.com>2026-09-21 13:43:45 -0400
committerRalph Amissah <ralph.amissah@gmail.com>2026-09-22 16:18:01 -0400
commitd1cbf39bf7bf9517b937fa8d808240ed961b6dee (patch)
tree951a1b1fb844bc53849be43589261231fe50e424
parentocda db: carry catalogues & text blobs compressed (diff)
dispatch: pod materialised if source required
actions requiring source first materialise the pod One route per argument, decided by what was asked for. --source, --pod, --pod2, --show-abstraction and --ocda-db need original markup, so an .ocda.db given with any of them is written back out as a pod and parsed. Asked only to render, it is read as an abstraction as before. To ensure consistency, where a pod is materialized by an argument, that pod is used for the whole run, including for html and epub that could have been built from the loaded abstraction instead. --show-abstraction and --ocda-db join the markup side deliberately. They could be satisfied by re-serialising the abstraction already loaded, and were. But an artefact is a statement about the source: written from a loaded abstraction it says only that the loader is self consistent, where written from the markup it says what this spine (whatever current version) makes of that document today. The chain is checkable against itself: --ocda-db from a database now goes database, pod, parse, database, and returns the same 9,908,224 bytes it started from. --show-abstraction likewise re-emits the .ssp files byte for byte. A .ssp carries no markup by construction and a database written before the format carried it has none, so both are refused for those actions, once and by name, and the rest of the run goes on. (assisted by Claude-Code)
-rw-r--r--org/spine.org69
-rw-r--r--src/sisudoc/spine.d69
2 files changed, 114 insertions, 24 deletions
diff --git a/org/spine.org b/org/spine.org
index 494b918..d0df888 100644
--- a/org/spine.org
+++ b/org/spine.org
@@ -152,10 +152,22 @@ string program_name = "spine";
if (!(_opt_action.skip_output)) {
outputHubInitialize!()(_opt_action, program_info);
}
- if (_opt_action.source_or_pod) {
- writeln("WARNING: --source and --pod2 are not available from an abstraction",
- " source: no artefact carries the markup. Skipped for: ",
- _artefact_args.join(", "));
+ /+ ↓ what is left here needs the markup and could not get it.
+ A .ocda.db that carries source never reaches this loop: it was
+ written back out as a pod above and is being parsed. So an artefact
+ here is a .ssp, which carries no markup by construction, or a
+ database that has none, which has already said so by name. Either
+ way the actions that need the source are not available for it, and
+ the rest of the run goes on.
+ +/
+ if (_opt_action.needs_markup_source) {
+ string[] _not_available;
+ if (_opt_action.source_or_pod) { _not_available ~= "--source/--pod"; }
+ if (_opt_action.show_abstraction) { _not_available ~= "--show-abstraction"; }
+ if (_opt_action.ocda_db) { _not_available ~= "--ocda-db"; }
+ writeln("WARNING: an abstraction alone does not carry markup, so these",
+ " are skipped: ", _not_available.join(", "));
+ writeln(" for: ", _artefact_args.join(", "));
}
foreach (_artefact; _artefact_args) {
/+ ↓ a .ocda.db holds every language of its document, so one artefact can
@@ -1053,6 +1065,25 @@ struct OptActions {
@trusted bool source_or_pod() {
return (opts["pod"] || opts["pod2"] || opts["source"]) ? true : false;
}
+ /+ ↓ the actions that need the markup and not only the abstraction.
+ .
+ --source, --pod and --pod2 write the markup out. --show-abstraction
+ and --ocda-db write an artefact, and an artefact is a statement
+ about the source: derived from a loaded abstraction it would say
+ only that the loader is consistent with itself, where derived from
+ the markup it says what this spine makes of that document today,
+ which is the question worth answering and the one --ocda-verify
+ asks.
+ .
+ Given a database that carries source, these route the argument
+ through the pod: it is written back out and parsed. Given one that
+ does not, or a .ssp, which carries no markup at all, they are
+ refused, and any rendering asked for in the same run still happens
+ from the abstraction.
+ +/
+ @trusted bool needs_markup_source() {
+ return (source_or_pod || show_abstraction || ocda_db) ? true : false;
+ }
@trusted bool sqlite_discrete() {
return opts["sqlite-discrete"];
}
@@ -1457,21 +1488,29 @@ foreach (arg; args[1..$]) {
/+ ↓ a database asked for as a source: write the pod back out of it and carry on
with the pod.
.
- --source and --pod2 need the original markup file, included in a 2.0
- database. The pod is written to a directory of this run's making and takes
- the place of the argument, so everything after this point is handling a
- pod, and the document is built by the same code over the same bytes as the
+ The actions that need the source markup are --source, --pod, --pod2,
+ --show-abstraction and --ocda-db, and a 2.0 database has it. The pod
+ is written to a directory of this run's making and takes the place of
+ the argument, so everything after this point is handling a pod, and
+ the document is built by the same code over the same bytes as the
original. That is what makes "identical output" a consequence.
.
+ One route per argument, never a mix. An argument that materialises is
+ a pod for the whole run: every output asked for comes from the markup,
+ including the html and epub that could have been rendered from the
+ loaded abstraction instead. Two routes for one argument would mean two
+ answers to "what is this document", and the run could not say which it
+ had given.
+ .
Before the config discovery below, because that walks the argument looking
for a .dr/ above it, and the argument it should walk is the pod rather than
the database.
.
- A database without original markup still refuses, as it has to: no artefact
+ A database without source markup still refuses, as it has to: no artefact
written by an older spine carries any.
+/
string[] _pod_materialisations;
-if (_opt_action.source_or_pod) {
+if (_opt_action.needs_markup_source) {
import sisudoc.ocda.abstraction.pod_from_db;
import std.process : thisProcessID;
mixin spinePodFromDb;
@@ -1493,8 +1532,14 @@ if (_opt_action.source_or_pod) {
writeln("pod from database: ", arg.baseName, " -> ", _mat.pod_dir);
}
} else {
- stderr.writeln("WARNING: --source and --pod2 need the markup, and ",
- arg.baseName, " ", _mat.note, "; skipped");
+ /+ ↓ the argument stays, and is read as an abstraction below: what
+ was asked for that needs the markup cannot be done, and what
+ does not still can. Which actions those are is said once, by
+ the artefact loop this argument now falls to, rather than
+ twice here as well.
+ +/
+ _args_after ~= arg;
+ stderr.writeln("WARNING: ", arg.baseName, " ", _mat.note);
}
}
_resolved_args = _args_after;
diff --git a/src/sisudoc/spine.d b/src/sisudoc/spine.d
index bb7dbb8..a93e48e 100644
--- a/src/sisudoc/spine.d
+++ b/src/sisudoc/spine.d
@@ -706,6 +706,25 @@ string program_name = "spine";
@trusted bool source_or_pod() {
return (opts["pod"] || opts["pod2"] || opts["source"]) ? true : false;
}
+ /+ ↓ the actions that need the markup and not only the abstraction.
+ .
+ --source, --pod and --pod2 write the markup out. --show-abstraction
+ and --ocda-db write an artefact, and an artefact is a statement
+ about the source: derived from a loaded abstraction it would say
+ only that the loader is consistent with itself, where derived from
+ the markup it says what this spine makes of that document today,
+ which is the question worth answering and the one --ocda-verify
+ asks.
+ .
+ Given a database that carries source, these route the argument
+ through the pod: it is written back out and parsed. Given one that
+ does not, or a .ssp, which carries no markup at all, they are
+ refused, and any rendering asked for in the same run still happens
+ from the abstraction.
+ +/
+ @trusted bool needs_markup_source() {
+ return (source_or_pod || show_abstraction || ocda_db) ? true : false;
+ }
@trusted bool sqlite_discrete() {
return opts["sqlite-discrete"];
}
@@ -1097,21 +1116,29 @@ string program_name = "spine";
/+ ↓ a database asked for as a source: write the pod back out of it and carry on
with the pod.
.
- --source and --pod2 need the original markup file, included in a 2.0
- database. The pod is written to a directory of this run's making and takes
- the place of the argument, so everything after this point is handling a
- pod, and the document is built by the same code over the same bytes as the
+ The actions that need the source markup are --source, --pod, --pod2,
+ --show-abstraction and --ocda-db, and a 2.0 database has it. The pod
+ is written to a directory of this run's making and takes the place of
+ the argument, so everything after this point is handling a pod, and
+ the document is built by the same code over the same bytes as the
original. That is what makes "identical output" a consequence.
.
+ One route per argument, never a mix. An argument that materialises is
+ a pod for the whole run: every output asked for comes from the markup,
+ including the html and epub that could have been rendered from the
+ loaded abstraction instead. Two routes for one argument would mean two
+ answers to "what is this document", and the run could not say which it
+ had given.
+ .
Before the config discovery below, because that walks the argument looking
for a .dr/ above it, and the argument it should walk is the pod rather than
the database.
.
- A database without original markup still refuses, as it has to: no artefact
+ A database without source markup still refuses, as it has to: no artefact
written by an older spine carries any.
+/
string[] _pod_materialisations;
- if (_opt_action.source_or_pod) {
+ if (_opt_action.needs_markup_source) {
import sisudoc.ocda.abstraction.pod_from_db;
import std.process : thisProcessID;
mixin spinePodFromDb;
@@ -1133,8 +1160,14 @@ string program_name = "spine";
writeln("pod from database: ", arg.baseName, " -> ", _mat.pod_dir);
}
} else {
- stderr.writeln("WARNING: --source and --pod2 need the markup, and ",
- arg.baseName, " ", _mat.note, "; skipped");
+ /+ ↓ the argument stays, and is read as an abstraction below: what
+ was asked for that needs the markup cannot be done, and what
+ does not still can. Which actions those are is said once, by
+ the artefact loop this argument now falls to, rather than
+ twice here as well.
+ +/
+ _args_after ~= arg;
+ stderr.writeln("WARNING: ", arg.baseName, " ", _mat.note);
}
}
_resolved_args = _args_after;
@@ -1833,10 +1866,22 @@ string program_name = "spine";
if (!(_opt_action.skip_output)) {
outputHubInitialize!()(_opt_action, program_info);
}
- if (_opt_action.source_or_pod) {
- writeln("WARNING: --source and --pod2 are not available from an abstraction",
- " source: no artefact carries the markup. Skipped for: ",
- _artefact_args.join(", "));
+ /+ ↓ what is left here needs the markup and could not get it.
+ A .ocda.db that carries source never reaches this loop: it was
+ written back out as a pod above and is being parsed. So an artefact
+ here is a .ssp, which carries no markup by construction, or a
+ database that has none, which has already said so by name. Either
+ way the actions that need the source are not available for it, and
+ the rest of the run goes on.
+ +/
+ if (_opt_action.needs_markup_source) {
+ string[] _not_available;
+ if (_opt_action.source_or_pod) { _not_available ~= "--source/--pod"; }
+ if (_opt_action.show_abstraction) { _not_available ~= "--show-abstraction"; }
+ if (_opt_action.ocda_db) { _not_available ~= "--ocda-db"; }
+ writeln("WARNING: an abstraction alone does not carry markup, so these",
+ " are skipped: ", _not_available.join(", "));
+ writeln(" for: ", _artefact_args.join(", "));
}
foreach (_artefact; _artefact_args) {
/+ ↓ a .ocda.db holds every language of its document, so one artefact can