diff --git a/src/core/weave/mesh_support_pages.ts b/src/core/weave/mesh_support_pages.ts
index 662f0f7..f72b9b8 100644
--- a/src/core/weave/mesh_support_pages.ts
+++ b/src/core/weave/mesh_support_pages.ts
@@ -432,11 +432,11 @@ function planInitialMeshSupportResourcePageWeave(input: {
const versionedInventory = versionedSupportResources.find((support) =>
support.path === "_mesh/_inventory"
);
- const updatedMeshMetadataTurtle = versionedInventory === undefined
+ const updatedMeshMetadataTurtle = versionedSupportResources.length === 0
? input.currentMeshMetadataTurtle
- : renderInitialMeshMetadataWithMeshInventoryProgression(
+ : renderInitialMeshMetadataWithSupportProgression(
input.currentMeshMetadataTurtle,
- versionedInventory,
+ versionedSupportResources,
);
return {
@@ -457,7 +457,7 @@ function planInitialMeshSupportResourcePageWeave(input: {
}]),
],
updatedFiles: [
- ...(versionedInventory === undefined ? [] : [{
+ ...(versionedSupportResources.length === 0 ? [] : [{
path: "_mesh/_meta/meta.ttl",
contents: updatedMeshMetadataTurtle,
}]),
@@ -469,41 +469,45 @@ function planInitialMeshSupportResourcePageWeave(input: {
};
}
-function renderInitialMeshMetadataWithMeshInventoryProgression(
+function renderInitialMeshMetadataWithSupportProgression(
currentMeshMetadataTurtle: string,
- versionedInventory: MeshSupportResource,
+ versionedSupportResources: readonly MeshSupportResource[],
): string {
const metadataWithPrefixes = ensureXsdPrefix(currentMeshMetadataTurtle);
let blocks = splitTurtleBlocks(metadataWithPrefixes);
- blocks = upsertSubjectBlockAfter(
- blocks,
- "_mesh",
- "_mesh/_inventory",
- renderInitialMeshInventoryMetaProgressionBlock(versionedInventory),
- );
- blocks = upsertSubjectBlockAfter(
- blocks,
- "_mesh/_inventory",
- versionedInventory.historyPath!,
- renderInitialMeshInventoryHistoryMetaProgressionBlock(versionedInventory),
- );
+ let anchorPath = "_mesh";
+ for (const support of versionedSupportResources) {
+ blocks = upsertSubjectBlockAfter(
+ blocks,
+ anchorPath,
+ support.path,
+ renderInitialMeshSupportMetaProgressionBlock(support),
+ );
+ blocks = upsertSubjectBlockAfter(
+ blocks,
+ support.path,
+ support.historyPath!,
+ renderInitialMeshSupportHistoryMetaProgressionBlock(support),
+ );
+ anchorPath = support.historyPath!;
+ }
return `${blocks.join("\n\n")}\n`;
}
-function renderInitialMeshInventoryMetaProgressionBlock(
- versionedInventory: MeshSupportResource,
+function renderInitialMeshSupportMetaProgressionBlock(
+ support: MeshSupportResource,
): string {
- return `<_mesh/_inventory> a sflo:MeshInventory, sflo:DigitalArtifact, sflo:RdfDocument ;
- sflo:currentArtifactHistory <${versionedInventory.historyPath!}> ;
+ return `<${support.path}>
+ sflo:currentArtifactHistory <${support.historyPath!}> ;
sflo:nextHistoryOrdinal "2"^^xsd:nonNegativeInteger .`;
}
-function renderInitialMeshInventoryHistoryMetaProgressionBlock(
- versionedInventory: MeshSupportResource,
+function renderInitialMeshSupportHistoryMetaProgressionBlock(
+ support: MeshSupportResource,
): string {
- return `<${versionedInventory.historyPath!}> a sflo:ArtifactHistory ;
- sflo:latestHistoricalState <${versionedInventory.statePath!}> ;
+ return `<${support.historyPath!}>
+ sflo:latestHistoricalState <${support.statePath!}> ;
sflo:nextStateOrdinal "2"^^xsd:nonNegativeInteger .`;
}
@@ -720,7 +724,7 @@ function appendInitialSupportHistoryFactsToBlock(
block: string,
historyPath: string,
): string {
- if (block.includes("sflo:currentArtifactHistory")) {
+ if (block.includes("sflo:hasArtifactHistory")) {
return block;
}
if (!block.endsWith(" .")) {
@@ -730,7 +734,7 @@ function appendInitialSupportHistoryFactsToBlock(
}
return `${
block.slice(0, -2)
- } ;\n sflo:hasArtifactHistory <${historyPath}> ;\n sflo:currentArtifactHistory <${historyPath}> ;\n sflo:nextHistoryOrdinal "2"^^xsd:nonNegativeInteger .`;
+ } ;\n sflo:hasArtifactHistory <${historyPath}> .`;
}
function renderInitialSupportHistoryBlock(
@@ -740,8 +744,6 @@ function renderInitialSupportHistoryBlock(
return `<${historyPath}> a sflo:ArtifactHistory ;
sflo:historyOrdinal "1"^^xsd:nonNegativeInteger ;
sflo:hasHistoricalState <${statePath}> ;
- sflo:latestHistoricalState <${statePath}> ;
- sflo:nextStateOrdinal "2"^^xsd:nonNegativeInteger ;
sflo:hasResourcePage <${historyPath}/index.html> .`;
}
diff --git a/src/core/weave/mesh_support_pages_test.ts b/src/core/weave/mesh_support_pages_test.ts
index 02c764c..b68dc67 100644
--- a/src/core/weave/mesh_support_pages_test.ts
+++ b/src/core/weave/mesh_support_pages_test.ts
@@ -1,4 +1,10 @@
-import { assert, assertEquals, assertThrows } from "@std/assert";
+import {
+ assert,
+ assertEquals,
+ assertFalse,
+ assertStringIncludes,
+ assertThrows,
+} from "@std/assert";
import { compareRdfContent } from "../../../dependencies/github.com/spectacular-voyage/accord/src/checker/compare_rdf.ts";
import { planMeshSupportResourcePages } from "./mesh_support_pages.ts";
import { WeaveInputError } from "./errors.ts";
@@ -51,6 +57,77 @@ Deno.test("mesh-support page-only append rejects a missing support subject", ()
);
});
+Deno.test("initial mesh-support history keeps mutable progression in MeshMetadata only", () => {
+ const plan = planMeshSupportResourcePages({
+ meshBase,
+ currentMeshInventoryTurtle: initialSupportInventoryTurtle(),
+ currentMeshMetadataTurtle: initialMeshMetadataTurtle(),
+ currentMeshConfigTurtle: "# current config bytes\n",
+ supportHistoryPolicies: {
+ meshMetadata: "versioned",
+ meshInventory: "versioned",
+ config: "versioned",
+ },
+ });
+ const updatedInventory =
+ plan.updatedFiles.find((file) =>
+ file.path === "_mesh/_inventory/inventory.ttl"
+ )?.contents ?? "";
+ const updatedMetadata =
+ plan.updatedFiles.find((file) => file.path === "_mesh/_meta/meta.ttl")
+ ?.contents ?? "";
+
+ for (
+ const mutablePredicate of [
+ "sflo:currentArtifactHistory",
+ "sflo:nextHistoryOrdinal",
+ "sflo:latestHistoricalState",
+ "sflo:nextStateOrdinal",
+ ]
+ ) {
+ assertFalse(
+ updatedInventory.includes(mutablePredicate),
+ "MeshInventory retained mutable predicate " + mutablePredicate,
+ );
+ }
+ for (
+ const supportPath of [
+ "_mesh/_meta",
+ "_mesh/_inventory",
+ "_mesh/_config",
+ ]
+ ) {
+ const historyPath = supportPath + "/_history001";
+ assertStringIncludes(
+ updatedInventory,
+ "sflo:hasArtifactHistory <" + historyPath + ">",
+ );
+ assertStringIncludes(
+ updatedInventory,
+ "sflo:hasHistoricalState <" + historyPath + "/_s0001>",
+ );
+ assertStringIncludes(updatedMetadata, "<" + supportPath + ">");
+ assertStringIncludes(
+ updatedMetadata,
+ "sflo:currentArtifactHistory <" + historyPath + ">",
+ );
+ assertStringIncludes(
+ updatedMetadata,
+ "<" + historyPath + ">",
+ );
+ assertStringIncludes(
+ updatedMetadata,
+ "sflo:latestHistoricalState <" + historyPath + "/_s0001>",
+ );
+ }
+ assertEquals(
+ plan.createdFiles.find((file) =>
+ file.path === "_mesh/_meta/_history001/_s0001/ttl/meta.ttl"
+ )?.contents,
+ updatedMetadata,
+ );
+});
+
function planCurrentOnlyMeshSupportPages(
currentMeshInventoryTurtle: string,
) {
@@ -110,6 +187,43 @@ function requestedSupportPageFactsTurtle(): string {
`;
}
+function initialSupportInventoryTurtle(): string {
+ return [
+ "@base <" + meshBase + "> .",
+ "@prefix sflo: .",
+ "",
+ "<_mesh> a sflo:SemanticMesh .",
+ "",
+ "<_mesh/_meta> a sflo:MeshMetadata, sflo:DigitalArtifact, sflo:RdfDocument ;",
+ " sflo:hasWorkingLocatedFile <_mesh/_meta/meta.ttl> .",
+ "",
+ "<_mesh/_inventory> a sflo:MeshInventory, sflo:DigitalArtifact, sflo:RdfDocument ;",
+ " sflo:hasWorkingLocatedFile <_mesh/_inventory/inventory.ttl> .",
+ "",
+ "<_mesh/_config> a sflo:DigitalArtifact, sflo:RdfDocument ;",
+ " sflo:hasWorkingLocatedFile <_mesh/_config/config.ttl> .",
+ "",
+ "<_mesh/_meta/meta.ttl> a sflo:LocatedFile, sflo:RdfDocument .",
+ "",
+ "<_mesh/_inventory/inventory.ttl> a sflo:LocatedFile, sflo:RdfDocument .",
+ "",
+ "<_mesh/_config/config.ttl> a sflo:LocatedFile, sflo:RdfDocument .",
+ "",
+ ].join("\n");
+}
+
+function initialMeshMetadataTurtle(): string {
+ return [
+ "@base <" + meshBase + "> .",
+ "@prefix sflo: .",
+ "",
+ "<_mesh> a sflo:SemanticMesh .",
+ "",
+ "<_mesh/_meta> a sflo:MeshMetadata, sflo:DigitalArtifact, sflo:RdfDocument .",
+ "",
+ ].join("\n");
+}
+
function encode(value: string): Uint8Array {
return new TextEncoder().encode(value);
}
diff --git a/src/core/weave/weave_test.ts b/src/core/weave/weave_test.ts
index a3a2bca..f78314a 100644
--- a/src/core/weave/weave_test.ts
+++ b/src/core/weave/weave_test.ts
@@ -212,16 +212,22 @@ Deno.test("planMeshSupportResourcePages adds current support ResourcePages inclu
);
assertEquals(
plan.updatedFiles.map((file) => file.path),
- ["_mesh/_inventory/inventory.ttl"],
+ ["_mesh/_meta/meta.ttl", "_mesh/_inventory/inventory.ttl"],
);
- const inventory = plan.updatedFiles[0]?.contents ?? "";
+ const inventory =
+ plan.updatedFiles.find((file) =>
+ file.path === "_mesh/_inventory/inventory.ttl"
+ )?.contents ?? "";
+ const metadata =
+ plan.updatedFiles.find((file) => file.path === "_mesh/_meta/meta.ttl")
+ ?.contents ?? "";
assertStringIncludes(
inventory,
"sfcfg:hasConfig <_mesh/_config> ;\n sflo:hasResourcePage <_mesh/index.html> .",
);
assertStringIncludes(
inventory,
- "sflo:hasWorkingLocatedFile <_mesh/_config/config.ttl> ;\n sflo:hasResourcePage <_mesh/_config/index.html> ;\n sflo:hasArtifactHistory <_mesh/_config/_history001> ;",
+ "sflo:hasWorkingLocatedFile <_mesh/_config/config.ttl> ;\n sflo:hasResourcePage <_mesh/_config/index.html> ;\n sflo:hasArtifactHistory <_mesh/_config/_history001> .",
);
assertStringIncludes(
inventory,
@@ -245,6 +251,19 @@ Deno.test("planMeshSupportResourcePages adds current support ResourcePages inclu
"sflo:currentArtifactHistory <_mesh/_meta/_history001>",
),
);
+ assertFalse(
+ inventory.includes(
+ "sflo:currentArtifactHistory <_mesh/_config/_history001>",
+ ),
+ );
+ assertStringIncludes(
+ metadata,
+ "sflo:currentArtifactHistory <_mesh/_config/_history001> ;",
+ );
+ assertStringIncludes(
+ metadata,
+ "sflo:latestHistoricalState <_mesh/_config/_history001/_s0001> ;",
+ );
assertStringIncludes(
inventory,
"<_mesh/_config/_history001/_s0001/ttl> a sflo:ArtifactManifestation, sflo:RdfDocument ;",
@@ -321,7 +340,10 @@ Deno.test("planMeshSupportResourcePages keeps support ResourcePage facts when pa
},
});
- const inventory = plan.updatedFiles[0]?.contents ?? "";
+ const inventory =
+ plan.updatedFiles.find((file) =>
+ file.path === "_mesh/_inventory/inventory.ttl"
+ )?.contents ?? "";
assertStringIncludes(
inventory,
"sflo:hasResourcePage <_mesh/_config/index.html>",
@@ -340,7 +362,7 @@ Deno.test("planMeshSupportResourcePages keeps support ResourcePage facts when pa
);
assertStringIncludes(
inventory,
- "sflo:hasArtifactHistory <_mesh/_config/_history001> ;",
+ "sflo:hasArtifactHistory <_mesh/_config/_history001> .",
);
assertStringIncludes(
inventory,
@@ -4215,6 +4237,12 @@ Deno.test("planWeave renders an extracted term from a nested source without a ro
const input = await createExtractedBobWeaveInput();
input.currentMeshInventoryTurtle = input.currentMeshInventoryTurtle
.replace(" sflo:hasKnop ;\n", "")
+ .replace(
+ `<_mesh> sflo:hasKnop .
+
+`,
+ "",
+ )
.replace(
` a sflo:Knop ;
sflo:hasWorkingKnopInventoryFile ;
diff --git a/src/runtime/weave/page_model_assembly.ts b/src/runtime/weave/page_model_assembly.ts
index ad55df8..16e226a 100644
--- a/src/runtime/weave/page_model_assembly.ts
+++ b/src/runtime/weave/page_model_assembly.ts
@@ -127,8 +127,8 @@ export async function visitResourcePageModels(
() =>
collectHistoryGroupsByResourcePath(
meshState.meshBase,
- meshState.currentMeshInventoryTurtle,
- "Could not parse the current MeshInventory while collecting ResourcePage histories.",
+ `${meshState.currentMeshInventoryTurtle}\n${meshState.currentMeshMetadataTurtle}`,
+ "Could not parse the current MeshInventory and MeshMetadata while collecting ResourcePage histories.",
),
);
const meshInventoryPagePaths = await timeOptional(
diff --git a/src/runtime/weave/raw_source_panels.ts b/src/runtime/weave/raw_source_panels.ts
index e37e4d9..17c102c 100644
--- a/src/runtime/weave/raw_source_panels.ts
+++ b/src/runtime/weave/raw_source_panels.ts
@@ -70,11 +70,17 @@ export async function collectMeshSupportRawSourcePanels(
string,
readonly ResourcePageRawSourcePanelModel[]
>();
- const quads = parseInventoryQuads(
+ const inventoryQuads = parseInventoryQuads(
meshState.meshBase,
meshState.currentMeshInventoryTurtle,
"Could not parse the current MeshInventory while collecting mesh support source panels.",
);
+ const metadataQuads = parseInventoryQuads(
+ meshState.meshBase,
+ meshState.currentMeshMetadataTurtle,
+ "Could not parse the current MeshMetadata while collecting mesh support source panels.",
+ );
+ const quads = [...inventoryQuads, ...metadataQuads];
if (
!(await addLatestHistoricalRawSourcePanelForCurrentArtifact(
diff --git a/tests/integration/weave_test.ts b/tests/integration/weave_test.ts
index 5d9ef5c..57eb4a8 100644
--- a/tests/integration/weave_test.ts
+++ b/tests/integration/weave_test.ts
@@ -148,7 +148,10 @@ Deno.test("executeWeave materializes current support ResourcePages for a docs-ro
"docs/_mesh/index.html",
].sort(),
);
- assertEquals(result.updatedPaths, ["docs/_mesh/_inventory/inventory.ttl"]);
+ assertEquals(result.updatedPaths, [
+ "docs/_mesh/_meta/meta.ttl",
+ "docs/_mesh/_inventory/inventory.ttl",
+ ]);
const inventory = await Deno.readTextFile(
join(workspaceRoot, "docs/_mesh/_inventory/inventory.ttl"),
);
@@ -158,7 +161,7 @@ Deno.test("executeWeave materializes current support ResourcePages for a docs-ro
);
assertStringIncludes(
inventory,
- "sflo:hasWorkingLocatedFile <_mesh/_config/config.ttl> ;\n sflo:hasResourcePage <_mesh/_config/index.html> ;\n sflo:hasArtifactHistory <_mesh/_config/_history001> ;",
+ "sflo:hasWorkingLocatedFile <_mesh/_config/config.ttl> ;\n sflo:hasResourcePage <_mesh/_config/index.html> ;\n sflo:hasArtifactHistory <_mesh/_config/_history001> .",
);
assertStringIncludes(
inventory,
@@ -178,6 +181,22 @@ Deno.test("executeWeave materializes current support ResourcePages for a docs-ro
"sflo:currentArtifactHistory <_mesh/_meta/_history001>",
),
);
+ assertFalse(
+ inventory.includes(
+ "sflo:currentArtifactHistory <_mesh/_config/_history001>",
+ ),
+ );
+ const metadata = await Deno.readTextFile(
+ join(workspaceRoot, "docs/_mesh/_meta/meta.ttl"),
+ );
+ assertStringIncludes(
+ metadata,
+ "sflo:currentArtifactHistory <_mesh/_config/_history001> ;",
+ );
+ assertStringIncludes(
+ metadata,
+ "sflo:latestHistoricalState <_mesh/_config/_history001/_s0001> ;",
+ );
const configPage = await Deno.readTextFile(
join(workspaceRoot, "docs/_mesh/_config/index.html"),
);