Skill Tracer
Skills › Archify › archify › README.md

README.md

337 lines · archify/renderers/workflow/README.md at a406594

Open on GitHub

Most marks come from matching words in the file, so treat them as pointers.

1# Workflow Renderer
2
3Render `diagram_type: "workflow"` JSON files into the standard Archify HTML
4template.
5
6```bash
7node archify/renderers/workflow/render-workflow.mjs input.workflow.json output.html
8```
9
10The renderer validates input against `archify/schemas/workflow.schema.json`
11with the bundled standalone validator. No dependency installation is required.
13If `output.html` is omitted, the renderer uses the required `meta.output` value
14from the JSON file.
16After rendering, run the artifact checker:
18```bash
19node archify/scripts/check-render-output.mjs output.html
20```
22It catches final-SVG issues that are easiest to see in a browser: non-finite
23SVG values, accidental two-point diagonal arrows, and arrows crossing the
24legend.

Workflow Renderer

Render diagram_type: "workflow" JSON files into the standard Archify HTML template.

node archify/renderers/workflow/render-workflow.mjs input.workflow.json output.html

The renderer validates input against archify/schemas/workflow.schema.json with the bundled standalone validator. No dependency installation is required.

If output.html is omitted, the renderer uses the required meta.output value from the JSON file.

After rendering, run the artifact checker:

node archify/scripts/check-render-output.mjs output.html

It catches final-SVG issues that are easiest to see in a browser: non-finite SVG values, accidental two-point diagonal arrows, and arrows crossing the legend.

Raw lines 1 to 25
Runs code scripts/check-render-output.mjs :19
26## Input
28Workflow JSON files must set:
30```json
32 "schema_version": 2,
33 "diagram_type": "workflow",
34 "meta": {
35 "title": "Agent Tool Call Workflow",
36 "output": "agent-tool-call.html"
37 },
38 "lanes": [],
39 "phases": [],
40 "groups": [],
41 "mainPath": [],
42 "nodes": [],
43 "edges": [],
44 "cards": []
46```
48Use `schema_version: 2` for new workflows. Its readable layout compiler treats
49every `col` as a logical rank in `0..5` and derives geometry from the measured
50document. `schema_version: 1` remains the fixed legacy contract for existing
51sources; valid v1 output is preserved byte-for-byte and never silently
52reinterpreted as v2.
54Omit `meta.viewBox` for the common v2 case so the compiler can use intrinsic
55measured bounds. In v1, the omitted width remains fixed at 720 and height is
56derived from lane count. A complete worked example lives at
57`archify/examples/agent-tool-call.workflow.json`; its `schema_version` selects
58the applicable contract.
60The schema lives at:
62```text
63archify/schemas/workflow.schema.json
64```

Input

Workflow JSON files must set:

{
  "schema_version": 2,
  "diagram_type": "workflow",
  "meta": {
    "title": "Agent Tool Call Workflow",
    "output": "agent-tool-call.html"
  },
  "lanes": [],
  "phases": [],
  "groups": [],
  "mainPath": [],
  "nodes": [],
  "edges": [],
  "cards": []
}

Use schema_version: 2 for new workflows. Its readable layout compiler treats every col as a logical rank in 0..5 and derives geometry from the measured document. schema_version: 1 remains the fixed legacy contract for existing sources; valid v1 output is preserved byte-for-byte and never silently reinterpreted as v2.

Omit meta.viewBox for the common v2 case so the compiler can use intrinsic measured bounds. In v1, the omitted width remains fixed at 720 and height is derived from lane count. A complete worked example lives at archify/examples/agent-tool-call.workflow.json; its schema_version selects the applicable contract.

The schema lives at:

archify/schemas/workflow.schema.json
Raw lines 26 to 65
Starts an agent Agent Tool :35
66## Migration and layout receipt
68Migrate an existing v1 source into a separate v2 file:
70```bash
71node archify/bin/archify.mjs migrate workflow old.json new.json --to-schema 2 --json
72```
74Running the command again with its schema-v2 output as the new source is an
75idempotent verification pass: the destination bytes and geometry stay unchanged.
77If a legacy v1 source is blocked solely because `meta.output` is missing or no
78longer portable, supply its replacement for the separate v2 destination:
80```bash
81node archify/bin/archify.mjs migrate workflow old.json new.json --to-schema 2 --output reports/workflow.html --json
82```
84`--output` must itself be a portable POSIX-relative `.html` path. It updates
85only the verified destination candidate; the source bytes remain unchanged and
86all non-output schema and compiler diagnostics still block migration.
88The command never overwrites the source by default. It maps absolute
89`via[*][0]`, `labelAt[0]`, and `channelX` values from legacy to solved rank
90space, preserves y coordinates unless a reported vertical constraint needs
91author input, expands an explicit viewBox only for an unambiguous containment
92repair, and writes the destination only after v2 compilation and artifact
93checks pass. Ambiguous explicit pins fail without producing the destination.
95Migration preserves `semanticChecks` and validates those requirements against the
96complete authored topology. Temporary layout planning may omit routed edges,
97but authors do not need to remove or relax their contracts to use migration.
98The complete migrated document must still satisfy every authored requirement.
100Inspect the stable author-facing v2 plan with:
102```bash
103node archify/bin/archify.mjs validate workflow input.workflow.json --layout-json
104```
106The receipt reports the selected contract, measured `viewBox` and
107`requiredViewBox`, solved columns, nodes, edges, labels, and causal diagnostics.
108It deliberately omits solver iterations and candidate scores. A complete plan
109rejected by a late layout, canvas, or text check also includes this geometry with
110`ok: false`; validation still exits non-zero and writes no HTML. Its `viewBox`
111is the attempted frame at rejection, while `requiredViewBox` is the measured
112footprint of that same rejected plan. Labels use their anchor coordinates,
113not the top-left corner of the label mask. Early or incomplete failures retain
114only the contract and causal diagnostics. Failed geometry is repair evidence,
115not acceptance; migration failure reports may also gain measured canvas evidence
116without accepting the rejected plan.

Migration and layout receipt

Migrate an existing v1 source into a separate v2 file:

node archify/bin/archify.mjs migrate workflow old.json new.json --to-schema 2 --json

Running the command again with its schema-v2 output as the new source is an idempotent verification pass: the destination bytes and geometry stay unchanged.

If a legacy v1 source is blocked solely because meta.output is missing or no longer portable, supply its replacement for the separate v2 destination:

node archify/bin/archify.mjs migrate workflow old.json new.json --to-schema 2 --output reports/workflow.html --json

--output must itself be a portable POSIX-relative .html path. It updates only the verified destination candidate; the source bytes remain unchanged and all non-output schema and compiler diagnostics still block migration.

The command never overwrites the source by default. It maps absolute via[*][0], labelAt[0], and channelX values from legacy to solved rank space, preserves y coordinates unless a reported vertical constraint needs author input, expands an explicit viewBox only for an unambiguous containment repair, and writes the destination only after v2 compilation and artifact checks pass. Ambiguous explicit pins fail without producing the destination.

Migration preserves semanticChecks and validates those requirements against the complete authored topology. Temporary layout planning may omit routed edges, but authors do not need to remove or relax their contracts to use migration. The complete migrated document must still satisfy every authored requirement.

Inspect the stable author-facing v2 plan with:

node archify/bin/archify.mjs validate workflow input.workflow.json --layout-json

The receipt reports the selected contract, measured viewBox and requiredViewBox, solved columns, nodes, edges, labels, and causal diagnostics. It deliberately omits solver iterations and candidate scores. A complete plan rejected by a late layout, canvas, or text check also includes this geometry with ok: false; validation still exits non-zero and writes no HTML. Its viewBox is the attempted frame at rejection, while requiredViewBox is the measured footprint of that same rejected plan. Labels use their anchor coordinates, not the top-left corner of the label mask. Early or incomplete failures retain only the contract and causal diagnostics. Failed geometry is repair evidence, not acceptance; migration failure reports may also gain measured canvas evidence without accepting the rejected plan.

Raw lines 66 to 117
Runs code bin/archify.mjs :71
118## Legend
120The default legend derives component kinds from `nodes[].type`. Supported
121`meta.legend.entries` keys, in stable order, are `frontend`, `backend`,
122`security`, `messagebus`, `database`, `cloud`, and `external`. Labels and
123visibility may be overridden through the shared legend contract; only kinds
124backed by rendered nodes receive Semantic Legend controls.

Legend

The default legend derives component kinds from nodes[].type. Supported meta.legend.entries keys, in stable order, are frontend, backend, security, messagebus, database, cloud, and external. Labels and visibility may be overridden through the shared legend contract; only kinds backed by rendered nodes receive Semantic Legend controls.

Raw lines 118 to 125
126## Layout contracts

Layout contracts

Raw lines 126 to 127
128### Fixed v1
130| Constant | Value |
131|----------|-------|
132| viewBox | default `[720, auto]` — auto height = 52 + lanes×104 + (lanes−1)×20 + 124 |
133| Lane frame | x 40, width 640, height 104, gap 20; first lane top at y 52 |
134| Lane title strip | top 30px of each lane; node boxes must stay below it |
135| Column centers (`col` 0–5) | x = 88, 220, 300, 430, 500, 625 |
136| Phase headers | Optional `phases[]` render above the first lane, spanning `fromCol..toCol` |
137| Lane groups | Optional `groups[]` frame parallel work or branch work inside one lane |
138| Exception lanes | Set `lane.variant: "exception"` for retry, denial, fallback, or failure paths |
139| Main path lint | Optional `mainPath[]` checks that happy-path steps have matching edges and do not move backward |
140| Default node | 92×52 (height 68 when `tag` is set) |
141| Node spacing | ≥8px between nodes in the same lane |
142| Edge length | straight segments must span ≥28px |
143| Legend row | y = lane bottom + 44; viewBox height must be ≥ legend y + 18 |
145Column-center gaps are 132 / 80 / 130 / 70 / 125 px: columns 1↔2 (80px) and
1463↔4 (70px) cannot both hold default-width 92px nodes in the same lane. Such an
147invalid v1 source receives one causal `workflow/column-capacity` diagnostic and
148a verified migration-to-v2 repair; v1 never falls through to adaptive layout.

Fixed v1

ConstantValue
viewBoxdefault [720, auto] — auto height = 52 + lanes×104 + (lanes−1)×20 + 124
Lane framex 40, width 640, height 104, gap 20; first lane top at y 52
Lane title striptop 30px of each lane; node boxes must stay below it
Column centers (col 0–5)x = 88, 220, 300, 430, 500, 625
Phase headersOptional phases[] render above the first lane, spanning fromCol..toCol
Lane groupsOptional groups[] frame parallel work or branch work inside one lane
Exception lanesSet lane.variant: "exception" for retry, denial, fallback, or failure paths
Main path lintOptional mainPath[] checks that happy-path steps have matching edges and do not move backward
Default node92×52 (height 68 when tag is set)
Node spacing≥8px between nodes in the same lane
Edge lengthstraight segments must span ≥28px
Legend rowy = lane bottom + 44; viewBox height must be ≥ legend y + 18

Column-center gaps are 132 / 80 / 130 / 70 / 125 px: columns 1↔2 (80px) and 3↔4 (70px) cannot both hold default-width 92px nodes in the same lane. Such an invalid v1 source receives one causal workflow/column-capacity diagnostic and a verified migration-to-v2 repair; v1 never falls through to adaptive layout.

Raw lines 128 to 149
150### Readable v2
152| Invariant | Contract |
153|----------|----------|
154| Logical columns | `col` is an integer in `0..5`; pixel centers are measured output |
155| Adjacent-rank baseline | 120px center distance before document-specific constraints |
156| Same-lane node clearance | ≥8px when vertical node intervals overlap |
157| Facing direct edge | clear gap ≥`max(28px, measured label mask width + 8px)` |
158| Automatic route rhythm | direct segment ≥28px; endpoint stub ≥8px; interior turn segment ≥16px |
159| Implicit viewBox | intrinsic content bounds plus contract padding |
160| Explicit viewBox | containment capacity; too-small input reports exact `requiredViewBox` and contributors |
161| Lane measurement | A same-column vertical stack (two or more distinct `yOffset` values) opts an implicit, unpinned workflow into per-lane measurement. Explicit `meta.viewBox`, `via`, `labelAt`, `channelX`, or `channelY`, and workflows without a stack retain shared-height v2 geometry for compatibility. |
163Unpinned v2 drafts reserve 32px between overlapping same-lane nodes for routes,
164and omitted node widths may grow from 92px to 200px to fit their text. Documents
165with an explicit `meta.viewBox` or absolute `via`, `labelAt`, `channelX`, or
166`channelY` pins retain the existing width defaults and 8px rank-clearance
167constraint, in both compilation and migration planning. Authored widths remain
168authoritative.
170Automatic showcase canvases check sublabels against the desktop reading floor
171without enlarging text beyond its existing 8px slot. If the canvas cannot keep
172that slot readable, `workflow/sublabel-readability` reports the required font
173and canvas width. Standard quality and explicit canvases retain their previous
174text-fitting behavior and the existing composition/browser quality checks.
176The compiler applies constraints only to actual related or overlapping
177same-lane nodes, so a wide node in an unrelated lane does not expand every
178rank. Legacy centers are a soft preference after correctness constraints, not
179a geometry promise. Phase and group frames derive from the solved rank bands.
180Automatic routes are normalized once and the same final scene drives
181validation and SVG serialization. Long automatic labels compare direct-gutter
182growth with a legal channel instead of widening every downstream rank. Measured
183multi-row legends participate in intrinsic height and explicit viewBox
184capacity.
186The Issue #250 shape already has a v2 representation without an authored lane
187size. Keep the three stages in one grouped lane, omit `meta.viewBox`, and center
188their offsets around zero:
190```json
192 "schema_version": 2,
193 "diagram_type": "workflow",
194 "meta": { "title": "stack", "output": "stack.html" },
195 "lanes": [{ "id": "cage", "label": "one cage" }],
196 "groups": [{ "id": "g", "label": "cage", "lane": "cage", "fromCol": 1, "toCol": 3 }],
197 "nodes": [
198 { "id": "a", "lane": "cage", "col": 2, "type": "security", "label": "stageA", "yOffset": -90 },
199 { "id": "b", "lane": "cage", "col": 2, "type": "security", "label": "stageB", "yOffset": 0 },
200 { "id": "c", "lane": "cage", "col": 2, "type": "security", "label": "stageC", "yOffset": 90 }
201 ],
202 "edges": [
203 { "from": "a", "to": "b", "fromSide": "bottom", "toSide": "top" },
204 { "from": "b", "to": "c", "fromSide": "bottom", "toSide": "top" }
205 ]
207```
209An implicit readable-v2 vertical stack whose measured lane height exceeds the
210104px baseline declares `data-reader-fit="width-first"`, as does any implicit
211readable-v2 canvas that would otherwise overflow the 1440×900 desktop page at
212full width. The desktop Viewer uses
213available reading width and normal page scrolling to keep tall stages readable,
214with the existing 1.5× automatic enlargement cap. This changes only displayed
215size: canonical SVG geometry and explicit `meta.viewBox` workflows retain their
216authored contracts. Embed, Present, and print retain their existing fitting modes.
217`visual-check` still checks readability, horizontal containment, and clipping;
218the declaration does not weaken node or route validation.
220Authored `via`, `labelAt`, `channelX`, and `channelY` are absolute hard pins in
221v2; an infeasible pin returns `workflow/explicit-pin-conflict` rather than being
222silently moved. `fromSide` and `toSide` remain direction constraints. A route
223preset restricts the automatic candidate family but is not itself an absolute
224coordinate pin. When either endpoint side is omitted, the v2 compiler chooses
225a feasible side; an authored side restricts that endpoint to the named port.
226An infeasible preset returns `workflow/route-preset-conflict` with the actual
227rejected `evidence.invariant`. For node clearance it also identifies the node,
228segment, endpoint role, and required clearance. Use that obstacle to revise
229the connected placement or route constraints, then revalidate; minimum segment
230lengths alone do not establish a clear path. Concrete alternatives for
231`workflow/route-preset-conflict` are checked by replanning the complete workflow
232at the active quality profile.

Readable v2

InvariantContract
Logical columnscol is an integer in 0..5; pixel centers are measured output
Adjacent-rank baseline120px center distance before document-specific constraints
Same-lane node clearance≥8px when vertical node intervals overlap
Facing direct edgeclear gap ≥max(28px, measured label mask width + 8px)
Automatic route rhythmdirect segment ≥28px; endpoint stub ≥8px; interior turn segment ≥16px
Implicit viewBoxintrinsic content bounds plus contract padding
Explicit viewBoxcontainment capacity; too-small input reports exact requiredViewBox and contributors
Lane measurementA same-column vertical stack (two or more distinct yOffset values) opts an implicit, unpinned workflow into per-lane measurement. Explicit meta.viewBox, via, labelAt, channelX, or channelY, and workflows without a stack retain shared-height v2 geometry for compatibility.

Unpinned v2 drafts reserve 32px between overlapping same-lane nodes for routes, and omitted node widths may grow from 92px to 200px to fit their text. Documents with an explicit meta.viewBox or absolute via, labelAt, channelX, or channelY pins retain the existing width defaults and 8px rank-clearance constraint, in both compilation and migration planning. Authored widths remain authoritative.

Automatic showcase canvases check sublabels against the desktop reading floor without enlarging text beyond its existing 8px slot. If the canvas cannot keep that slot readable, workflow/sublabel-readability reports the required font and canvas width. Standard quality and explicit canvases retain their previous text-fitting behavior and the existing composition/browser quality checks.

The compiler applies constraints only to actual related or overlapping same-lane nodes, so a wide node in an unrelated lane does not expand every rank. Legacy centers are a soft preference after correctness constraints, not a geometry promise. Phase and group frames derive from the solved rank bands. Automatic routes are normalized once and the same final scene drives validation and SVG serialization. Long automatic labels compare direct-gutter growth with a legal channel instead of widening every downstream rank. Measured multi-row legends participate in intrinsic height and explicit viewBox capacity.

The Issue #250 shape already has a v2 representation without an authored lane size. Keep the three stages in one grouped lane, omit meta.viewBox, and center their offsets around zero:

{
  "schema_version": 2,
  "diagram_type": "workflow",
  "meta": { "title": "stack", "output": "stack.html" },
  "lanes": [{ "id": "cage", "label": "one cage" }],
  "groups": [{ "id": "g", "label": "cage", "lane": "cage", "fromCol": 1, "toCol": 3 }],
  "nodes": [
    { "id": "a", "lane": "cage", "col": 2, "type": "security", "label": "stageA", "yOffset": -90 },
    { "id": "b", "lane": "cage", "col": 2, "type": "security", "label": "stageB", "yOffset": 0 },
    { "id": "c", "lane": "cage", "col": 2, "type": "security", "label": "stageC", "yOffset": 90 }
  ],
  "edges": [
    { "from": "a", "to": "b", "fromSide": "bottom", "toSide": "top" },
    { "from": "b", "to": "c", "fromSide": "bottom", "toSide": "top" }
  ]
}

An implicit readable-v2 vertical stack whose measured lane height exceeds the 104px baseline declares data-reader-fit="width-first", as does any implicit readable-v2 canvas that would otherwise overflow the 1440×900 desktop page at full width. The desktop Viewer uses available reading width and normal page scrolling to keep tall stages readable, with the existing 1.5× automatic enlargement cap. This changes only displayed size: canonical SVG geometry and explicit meta.viewBox workflows retain their authored contracts. Embed, Present, and print retain their existing fitting modes. visual-check still checks readability, horizontal containment, and clipping; the declaration does not weaken node or route validation.

Authored via, labelAt, channelX, and channelY are absolute hard pins in v2; an infeasible pin returns workflow/explicit-pin-conflict rather than being silently moved. fromSide and toSide remain direction constraints. A route preset restricts the automatic candidate family but is not itself an absolute coordinate pin. When either endpoint side is omitted, the v2 compiler chooses a feasible side; an authored side restricts that endpoint to the named port. An infeasible preset returns workflow/route-preset-conflict with the actual rejected evidence.invariant. For node clearance it also identifies the node, segment, endpoint role, and required clearance. Use that obstacle to revise the connected placement or route constraints, then revalidate; minimum segment lengths alone do not establish a clear path. Concrete alternatives for workflow/route-preset-conflict are checked by replanning the complete workflow at the active quality profile.

Raw lines 150 to 233
234## Design Rules
236- Use lanes for ownership or runtime boundaries.
237- Use phase headers for high-level story beats such as Intake, Plan, Execute, and Report.
238- Use groups for parallel checks, branch handling, or bounded work within a lane; every group must contain at least one node.
239- For sequential stages stacked inside one container, use one v2 lane and one group, keep the stages in one column, and omit `meta.viewBox`. `yOffset` is relative to the center of the lane's content area, so center a three-stage stack with `-90 / 0 / 90` rather than `0 / 90 / 180`. With compiler-owned routes and canvas, the compiler expands only that lane.
240- Use `lane.variant: "exception"` for human wait, denial, retry, fallback, and failure lanes instead of mixing those paths into the happy path.
241- Set `mainPath` when the diagram has a clear happy path; the renderer validates that consecutive ids have matching edges and move left-to-right.
242- Place nodes with lane IDs and `col` indexes in `0..5`, not raw SVG coordinates.
243- Preserve semantic edge labels. Readable v2 allocates measured label clearance;
244 when a label does not fit, repair the reported capacity or route constraint
245 instead of deleting meaning.
246- Use labels for decisions, approvals, protocols, async traces, return paths,
247 and any other relationship meaning not fully implied by its endpoints.
248- Prefer route presets — `drop` (bend between lanes; `bias` 0–1 picks where),
249 `outside-right`, `return-left`, `bottom-channel`, and `up-channel` — before
250 using raw `via` points. `straight` and the default `auto` cover the rest.
251- Keep workflow examples compact enough to render well in narrow chat/browser
252 previews.

Design Rules

  • Use lanes for ownership or runtime boundaries.
  • Use phase headers for high-level story beats such as Intake, Plan, Execute, and Report.
  • Use groups for parallel checks, branch handling, or bounded work within a lane; every group must contain at least one node.
  • For sequential stages stacked inside one container, use one v2 lane and one group, keep the stages in one column, and omit meta.viewBox. yOffset is relative to the center of the lane's content area, so center a three-stage stack with -90 / 0 / 90 rather than 0 / 90 / 180. With compiler-owned routes and canvas, the compiler expands only that lane.
  • Use lane.variant: "exception" for human wait, denial, retry, fallback, and failure lanes instead of mixing those paths into the happy path.
  • Set mainPath when the diagram has a clear happy path; the renderer validates that consecutive ids have matching edges and move left-to-right.
  • Place nodes with lane IDs and col indexes in 0..5, not raw SVG coordinates.
  • Preserve semantic edge labels. Readable v2 allocates measured label clearance; when a label does not fit, repair the reported capacity or route constraint instead of deleting meaning.
  • Use labels for decisions, approvals, protocols, async traces, return paths, and any other relationship meaning not fully implied by its endpoints.
  • Prefer route presets — drop (bend between lanes; bias 0–1 picks where), outside-right, return-left, bottom-channel, and up-channel — before using raw via points. straight and the default auto cover the rest.
  • Keep workflow examples compact enough to render well in narrow chat/browser previews.
Raw lines 234 to 253
254### Optional semantic checks
256Layout validation cannot infer domain truth from labels or cards. When source
257evidence establishes roots, terminals, mandatory direct relationships, or
258mandatory directed reachability, encode those facts in `semanticChecks`:
260```json
261"semanticChecks": {
262 "allowedRoots": ["request", "resource_catalog"],
263 "allowedTerminals": ["reply", "audit_log"],
264 "requiredEdges": [
265 { "from": "dispatch", "to": "dispatch_ledger" }
266 ],
267 "requiredPaths": [
268 { "from": "event_ledger", "to": "runtime_host" }
269 ]
271```
273When `allowedRoots` or `allowedTerminals` is present, it is the complete allow
274list for zero-incoming or zero-outgoing nodes respectively. `requiredEdges`
275requires one exact authored direction; `requiredPaths` permits intermediate
276nodes but follows authored edge direction. These checks run before layout, do
277not alter SVG or receipt bytes, and must not be weakened merely to resolve a
278route or composition diagnostic. Omit fields whose domain facts are unknown.
280Schema violations exit non-zero with path-prefixed messages annotated with the
281element's id or label. The renderer additionally fails when it can detect
282layout problems, including node overlap, nodes outside their lanes, invalid
283phase/group column ranges, empty groups, broken `mainPath` steps, unknown edge
284targets, labels colliding with nodes or other labels, labels wider than their
285node, legends outside the viewBox, or straight arrows that are too short to
286read cleanly. The shared Clean Flow Gate also rejects edges crossing unrelated
287nodes with 2px clearance; lanes, phases, and groups remain intentional
288pass-through containers. Text width is estimated CJK-aware: fullwidth glyphs
289count as two units.
291Diagnostics are causal: a rank-capacity failure suppresses derivative short
292edge, endpoint-direction, and label-overlap findings. `supportedFixes[]` can
293contain concrete edits checked by the compiler's fix search or general repair
294guidance from shared composition checks. General guidance is not a verified
295edit. After any edit, rerun the complete `finalize` command. A diagnostic never
296proposes removing a semantic label when label presence does not cause the
297failed invariant.
299Set `meta.quality_profile` to `showcase` for polished delivery. Unrelated proper
300X crossings then fail with `composition/proper-crossing`; default `standard`
301keeps them as artifact-receipt warnings. Collinear lane corridors are outside
302the proper-X rule, but a separate gate warns in `standard` and fails in
303`showcase` when unrelated edges overlap for at least 8px. V1 keeps its authored
304shared-endpoint contract. V2 also checks shared endpoints, including explicitly
305controlled routes: long or mixed-style trunks, counterflow, proper interior X
306crossings and overlapping independent arrowheads are not exempt. Pins are
307preserved, not silently repaired; unresolved v2 corridor/arrowhead collisions
308warn in `standard` and fail in `showcase`.
310V2 permits a same-direction shared terminal stub of at most 24 SVG units only
311when both relationships share the actual source or target port, effective
312variant, stroke width and role. A nonterminal overlap is never such a stub;
313forward-collinear waypoints do not split a long trunk into permitted pieces.
314Compatible short merges may share their terminal arrowhead. Automatic routes
315consider separate ports, reserve absolute routes at contested nodes, and prefer
316clear paths over shorter ambiguous ones. Crowded automatic corridors can use a
317bounded local adjustment without moving nodes or changing explicit coordinates.
319The SVG carries `data-layout-contract="readable-v2"` and each edge's role so
320artifact checks apply the same classification to actual visible path geometry,
321not stale composition-point metadata. These internal output attributes do not
322add authoring schema fields. Other diagram types retain their existing rules.
323V2 proper-crossing diagnostics retain the relationship IDs, intersection point,
324and supported fixes in both compiler/layout-JSON receipts and final HTML checks;
325`standard` reports warnings while `showcase` rejects the crossing. Both analyses
326merge forward-collinear waypoints without rewriting authored paths; real bends
327and reversals remain endpoint touches rather than being merged into an X.
329The older per-edge `data-composition-routing="workflow-v2-auto"` marker remains
330for compatibility with first-round exported HTML that has no root layout
331contract and with older artifact checkers. Marker-only artifacts retain their
332narrower automatic-pair crossing/counterflow policy; the root `readable-v2`
333contract is authoritative when present and also checks explicit routes. Do not
334remove the marker-only path as dead code without retiring that export format.
335Showcase also
336rejects any route segment below 8px and any interior turn segment below 16px;
337ordinary 8–15px endpoint stubs remain valid for fixed lane gaps.

Optional semantic checks

Layout validation cannot infer domain truth from labels or cards. When source evidence establishes roots, terminals, mandatory direct relationships, or mandatory directed reachability, encode those facts in semanticChecks:

"semanticChecks": {
  "allowedRoots": ["request", "resource_catalog"],
  "allowedTerminals": ["reply", "audit_log"],
  "requiredEdges": [
    { "from": "dispatch", "to": "dispatch_ledger" }
  ],
  "requiredPaths": [
    { "from": "event_ledger", "to": "runtime_host" }
  ]
}

When allowedRoots or allowedTerminals is present, it is the complete allow list for zero-incoming or zero-outgoing nodes respectively. requiredEdges requires one exact authored direction; requiredPaths permits intermediate nodes but follows authored edge direction. These checks run before layout, do not alter SVG or receipt bytes, and must not be weakened merely to resolve a route or composition diagnostic. Omit fields whose domain facts are unknown.

Schema violations exit non-zero with path-prefixed messages annotated with the element's id or label. The renderer additionally fails when it can detect layout problems, including node overlap, nodes outside their lanes, invalid phase/group column ranges, empty groups, broken mainPath steps, unknown edge targets, labels colliding with nodes or other labels, labels wider than their node, legends outside the viewBox, or straight arrows that are too short to read cleanly. The shared Clean Flow Gate also rejects edges crossing unrelated nodes with 2px clearance; lanes, phases, and groups remain intentional pass-through containers. Text width is estimated CJK-aware: fullwidth glyphs count as two units.

Diagnostics are causal: a rank-capacity failure suppresses derivative short edge, endpoint-direction, and label-overlap findings. supportedFixes[] can contain concrete edits checked by the compiler's fix search or general repair guidance from shared composition checks. General guidance is not a verified edit. After any edit, rerun the complete finalize command. A diagnostic never proposes removing a semantic label when label presence does not cause the failed invariant.

Set meta.quality_profile to showcase for polished delivery. Unrelated proper X crossings then fail with composition/proper-crossing; default standard keeps them as artifact-receipt warnings. Collinear lane corridors are outside the proper-X rule, but a separate gate warns in standard and fails in showcase when unrelated edges overlap for at least 8px. V1 keeps its authored shared-endpoint contract. V2 also checks shared endpoints, including explicitly controlled routes: long or mixed-style trunks, counterflow, proper interior X crossings and overlapping independent arrowheads are not exempt. Pins are preserved, not silently repaired; unresolved v2 corridor/arrowhead collisions warn in standard and fail in showcase.

V2 permits a same-direction shared terminal stub of at most 24 SVG units only when both relationships share the actual source or target port, effective variant, stroke width and role. A nonterminal overlap is never such a stub; forward-collinear waypoints do not split a long trunk into permitted pieces. Compatible short merges may share their terminal arrowhead. Automatic routes consider separate ports, reserve absolute routes at contested nodes, and prefer clear paths over shorter ambiguous ones. Crowded automatic corridors can use a bounded local adjustment without moving nodes or changing explicit coordinates.

The SVG carries data-layout-contract="readable-v2" and each edge's role so artifact checks apply the same classification to actual visible path geometry, not stale composition-point metadata. These internal output attributes do not add authoring schema fields. Other diagram types retain their existing rules. V2 proper-crossing diagnostics retain the relationship IDs, intersection point, and supported fixes in both compiler/layout-JSON receipts and final HTML checks; standard reports warnings while showcase rejects the crossing. Both analyses merge forward-collinear waypoints without rewriting authored paths; real bends and reversals remain endpoint touches rather than being merged into an X.

The older per-edge data-composition-routing="workflow-v2-auto" marker remains for compatibility with first-round exported HTML that has no root layout contract and with older artifact checkers. Marker-only artifacts retain their narrower automatic-pair crossing/counterflow policy; the root readable-v2 contract is authoritative when present and also checks explicit routes. Do not remove the marker-only path as dead code without retiring that export format. Showcase also rejects any route segment below 8px and any interior turn segment below 16px; ordinary 8–15px endpoint stubs remain valid for fixed lane gaps.

Raw lines 254 to 337
Starts an agent dispatch :265