Fix review findings: importer DoS, sync data loss, views/profile rendering, docs
Source importers: lstat before open so FIFOs no longer hang the directory walk; replace the quadratic Rust macro regex with a linear scan. Three-way sync: report page add/add and delete-vs-modify as conflicts instead of silently overwriting or resurrecting; canonical comparison so key order is not a change; the sync action withholds output and fails on conflicts unless --force. Story publishing escapes <, >, & and U+2028/9 inside the embedded JSON. Views: drop parentId of unselected containers, re-layout instead of manual geometry, validate before serialising; flatten() merges identical nodes repeated across pages so C4 output works with every analysis action. Dark theme edge labels get a background; tube-map corridors sit above the stations; C4 containers keep the swimlane style and orphan relationships land on the matching page; router keeps container header bands as obstacles; sequence self-messages loop on one side. Docs: SKILL.md lists all 23 CLI actions and how each capability family is invoked, task wrappers for query/test/what-if/doctor, capability tables and --page scope corrected, maintenance snippet uses the real synchronous action signature, duplicated rule bullets moved to the rule references, coverage matrix wording made verifiable. Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -56,6 +56,48 @@ test("what-if outgoing reachability stops at failure-isolating edges", () => {
|
||||
assert.throws(() => simulateFailure(IR, "missing"), /unknown node/i);
|
||||
});
|
||||
|
||||
test("semantic operations merge an element projected onto several pages", () => {
|
||||
const shared: DiagramIRV2 = {
|
||||
version: 2,
|
||||
pages: [
|
||||
{ id: "context", title: "Context", nodes: [
|
||||
{ id: "shop", label: "Shop", kind: "c4-system", properties: { c4Type: "system" }, provenance: { line: 2 } },
|
||||
{ id: "buyer", label: "Buyer", kind: "actor" },
|
||||
], edges: [{ id: "uses", source: "buyer", target: "shop" }] },
|
||||
{ id: "containers", title: "Containers", nodes: [
|
||||
{ id: "shop", label: "Shop", kind: "container", properties: { c4Type: "system" }, provenance: { line: 2 } },
|
||||
{ id: "buyer", label: "Buyer", kind: "actor" },
|
||||
{ id: "api", label: "API", kind: "service", parentId: "shop" },
|
||||
], edges: [{ id: "uses", source: "buyer", target: "shop" }, { id: "calls", source: "buyer", target: "api" }] },
|
||||
],
|
||||
};
|
||||
const system = projectLinkedViews(shared, ["system"])[0];
|
||||
assert.deepEqual(system.nodes.map((node) => node.id), ["shop", "buyer", "api"]);
|
||||
assert.equal(system.nodes.find((node) => node.id === "shop")?.kind, "c4-system", "specific kind wins over the presentational container promotion");
|
||||
assert.equal(system.nodes.find((node) => node.id === "api")?.parentId, undefined, "parent that is not a container in the merged model is dropped");
|
||||
assert.deepEqual(system.edges.map((edge) => edge.id), ["uses", "calls"]);
|
||||
assert.deepEqual(queryDiagram(shared, { from: "buyer", to: "api" }).path, ["buyer", "api"]);
|
||||
assert.equal(runPolicies(shared, ["no-orphans"]).findings.length, 0);
|
||||
assert.deepEqual(simulateFailure(shared, "buyer").impacted, ["api", "shop"]);
|
||||
assert.throws(() => queryDiagram({ ...shared, pages: [shared.pages[0], { ...shared.pages[1], edges: [{ id: "uses", source: "api", target: "shop" }] }] }, {}), /ambiguous.*edge.*uses/i);
|
||||
});
|
||||
|
||||
test("views keep container parents that are part of the selection and detach the rest", () => {
|
||||
const nested: DiagramIRV2 = { version: 2, pages: [{ id: "p", title: "P", nodes: [
|
||||
{ id: "outer", label: "Outer", kind: "container", geometry: { x: 100, y: 100, width: 400, height: 300 }, properties: { importance: 5 } },
|
||||
{ id: "inner", label: "Inner", kind: "container", parentId: "outer", geometry: { x: 20, y: 40, width: 200, height: 150 } },
|
||||
{ id: "leaf", label: "Leaf", kind: "service", parentId: "inner", geometry: { x: 10, y: 50, width: 120, height: 60 }, properties: { importance: 9 } },
|
||||
], edges: [] }] };
|
||||
const [executive] = projectLinkedViews({ ...nested, pages: [{ ...nested.pages[0], nodes: nested.pages[0].nodes.slice(0, 2).concat(Array.from({ length: 11 }, (_, index) => ({ id: `n${index}`, label: `N${index}`, properties: { importance: 8 } })), nested.pages[0].nodes.slice(2)) }] }, ["executive"]);
|
||||
const leaf = executive.nodes.find((node) => node.id === "leaf")!;
|
||||
assert.equal(executive.nodes.some((node) => node.id === "inner"), false);
|
||||
assert.equal(leaf.parentId, undefined);
|
||||
assert.deepEqual(leaf.geometry, { x: 130, y: 190, width: 120, height: 60 });
|
||||
const [system] = projectLinkedViews(nested, ["system"]);
|
||||
assert.equal(system.nodes.find((node) => node.id === "leaf")?.parentId, "inner");
|
||||
assert.deepEqual(system.nodes.find((node) => node.id === "leaf")?.geometry, { x: 10, y: 50, width: 120, height: 60 });
|
||||
});
|
||||
|
||||
test("semantic operations fail closed on ambiguous page-local IDs", () => {
|
||||
const ambiguous: DiagramIRV2 = {
|
||||
version: 2,
|
||||
|
||||
Reference in New Issue
Block a user