Skip to content

feat(import): add Mermaid flowchart importer for typed architecture IR - #140

Open
santhiprakash wants to merge 15 commits into
tt-a1i:mainfrom
santhiprakash:feat/mermaid-flowchart-import
Open

feat(import): add Mermaid flowchart importer for typed architecture IR#140
santhiprakash wants to merge 15 commits into
tt-a1i:mainfrom
santhiprakash:feat/mermaid-flowchart-import

Conversation

@santhiprakash

@santhiprakash santhiprakash commented Aug 27, 2026

Copy link
Copy Markdown

Problem and value

Archify could not import existing Mermaid flowchart / graph diagrams. Users had to re-author topology by hand even when a Mermaid source already existed. Issue #92 asks for an end-to-end import path that maps a documented Mermaid subset to typed Archify architecture IR, validates it through the existing gates, and delivers it as a standalone artifact.

Scope

  • What changed:
    • New archify/importers/flowchart.mjs — a focused Mermaid flowchart parser that maps a documented subset of flowchart / graph syntax to typed architecture IR. Supported: direction declarations (TB/TD, BT, LR, RL), node shapes ([...], (...), ((...)), [(...)], {...}, >...]), directed edges (-->, -.->, ==>, -- text -->, -. Text .->, |label|), subgraphs, comments, and chained edges. Node text, edge labels, subgraph grouping, and mirrored RL/BT placement are preserved.
    • New archify import flowchart <input.mmd> [output.json] [--json] CLI command. Without --json, the IR is written to the output file (or stdout). With --json, a machine-readable receipt is emitted on stdout.
    • New archify/test/flowchart-import.test.mjs with 26 regression tests covering valid, malformed, unsupported, adversarial, and showcase-layout fixtures.
    • 18 fixture files under archify/test/fixtures/flowchart/, including 8 valid fixtures.
    • New archify/references/mermaid-flowchart-import.md documenting the supported subset, target-mode selection, shape/edge mapping, and diagnostic-code table; linked from archify/SKILL.md § Mermaid input.
  • What deliberately did not change: No schema, renderer, validator, or existing CLI command was modified. The import command uses a lazy dynamic import so it does not affect doctor or other commands in incomplete installations.
  • No unrelated changes: confirmed.

Stability impact

  • Compatibility and migration risk: None. The importer is purely additive — a new module and a new CLI command. No existing JSON, validation, preview, or delivery behavior is touched.
  • Renderer, validator, package, or generated-artifact risk: None. The importer produces standard architecture IR that passes the existing schema and layout validation. No renderer or validator code was changed.
  • Failure behavior and rollback path: Unsupported, ambiguous, or malformed syntax exits non-zero with a stable named diagnostic (import/flowchart-* or import/unsupported-* codes) and source location. Open links (--- / long-arrow forms) and subgraph direction directives are explicitly rejected. No node or edge is silently discarded. The --json receipt includes the full diagnostics[] array. Rollback: remove the importers/ directory and revert bin/archify.mjs.

Tests run

cd archify
npm test

Result: 776 tests, 746 pass, 0 fail, 30 skipped (Chrome-dependent browser tests, skipped because ARCHIFY_CHROME is not set). Duration: ~97s.

Targeted flowchart import tests:

node --test test/flowchart-import.test.mjs

Result: 26 tests, 26 pass, 0 fail, 0 skipped. Duration: ~2.9s.

Full import flowchartvalidate architecture --quality showcase pipeline verified on all 8 valid fixtures:

for f in test/fixtures/flowchart/valid-*.mmd; do
  node bin/archify.mjs import flowchart "$f" /tmp/test.json --json
  node bin/archify.mjs validate architecture /tmp/test.json --quality showcase --json
done

Result: all 8 valid fixtures (simple, subgraph, labeled-edges, labeled-subgraph, chained, redeclared-labels, direction-rl, direction-bt) pass the full pipeline.

Visual evidence

visual review: skipped — Chrome is not available in this environment to inspect the rendered HTML visually. The rendered HTML files were generated successfully and pass all non-visual checks (single SVG, finite coordinates, orthogonal arrows, label clearance). A maintainer with Chrome available can run ARCHIFY_CHROME=/path/to/chrome node --test test/desktop-reader-browser.test.mjs or open the rendered HTML to confirm visual quality.

Generated artifacts

archify.zip was rebuilt with Node 22 and byte-verified locally because the package contents (importers/flowchart.mjs, bin/archify.mjs, SKILL.md, and the new reference) changed. No other generated artifacts (Gallery, guides, README proofs) were touched.

Checklist

  • I used a minimal focused change and preserved existing typed JSON behavior unless the issue requires a contract change.
  • I ran the relevant targeted tests and npm test in archify/.
  • I added or updated a regression test for behavioral changes.
  • I checked generated artifacts and package freshness when their sources changed.
  • I removed secrets, private repository content, and customer data from fixtures and screenshots.

Closes #92

@santhiprakash
santhiprakash force-pushed the feat/mermaid-flowchart-import branch from 80a02ea to fedb28e Compare August 27, 2026 08:22

@FenjuFu FenjuFu left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reproduced four cases where the importer returns a successful result while changing or inventing Mermaid semantics. The focused suite passes (node --test test/flowchart-import.test.mjs: 17/17), but issue #92 requires direction/grouping/text to be preserved and unsupported or ambiguous syntax to produce a stable diagnostic instead of being silently dropped or invented.

The inline comments cover: RL/BT direction being laid out as LR/TD, later explicit node declarations losing their labels, subgraph direction creating fake components, and Mermaid's normal --- link being emitted as dashed. These need regression tests alongside the fixes.

There is also no user-facing documentation or runnable example in this PR for the supported subset and target-mode selection, which is an explicit acceptance item in #92. Please add that documentation.

Finally, the PR is currently behind main and GitHub reports no checks for the head commit. Please update it against current main and run the repository-required checks before the next review. No remote DCO check is reported for this commit.

Comment thread archify/importers/flowchart.mjs Outdated
{ re: /^-\.\.->/, variant: 'dashed' },
{ re: /^-\.->/, variant: 'dashed' },
{ re: /^-->/, variant: 'solid' },
{ re: /^---/, variant: 'dashed' },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: This maps Mermaid's normal open link --- to Archify variant dashed. Mermaid distinguishes a normal --- link from dotted -.- / -.-> links (see https://mermaid.js.org/syntax/flowchart.html). Repro: flowchart LR\n A[One] --- B[Two] currently returns a directed dashed connection. Please preserve the supported semantics (including the absence of an arrow), or reject this syntax with a stable unsupported diagnostic; the current result silently changes it.

continue;
}

// Check for unsupported keywords.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Handle Mermaid's direction directive inside a subgraph, or return a stable unsupported diagnostic. Repro: flowchart LR\n subgraph API\n direction TB\n A[One] --> B[Two]\n end succeeds but invents components named direction and TB and adds them to the boundary. This violates #92's requirement not to invent or silently drop content.

Comment thread archify/importers/flowchart.mjs Outdated

// Register components.
for (const comp of stmtResult.components) {
if (!components.has(comp.id)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: First occurrence wins here, so a later explicit Mermaid node declaration silently loses its text/type. Repro: flowchart LR\n A --> B\n A[Named source]\n B[Named target] returns labels A and B. Mermaid permits a node to be defined more than once and uses the latest text. Please update/merge explicit declarations (and diagnose genuinely conflicting ambiguous declarations) and add a regression test.

Comment thread archify/importers/flowchart.mjs Outdated
const id = layerIds[i];
if (isHorizontal) {
// LR/RL: depth = column, index within layer = row.
const x = ORIGIN_X + d * (CELL_W + GAP_X);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: The layout only distinguishes horizontal from vertical, so accepted RL and BT declarations are rendered in the opposite direction. Repro: flowchart RL\n A[Source] --> B[Target] gives A x=40, B x=260; flowchart BT gives A y=40, B y=180, identical to LR/TD. Reverse depth placement for RL/BT, or reject those declarations until supported, and cover both with tests.

@santhiprakash

Copy link
Copy Markdown
Author

Thank you for the careful reproduction — all four cases confirmed against fedb28e and fixed in 7fa1ceb, with the branch merged onto current main (0853a80).

1. --- mapped to dashed (flowchart.mjs edge patterns) — confirmed: A --- B produced a directed dashed connection. The architecture renderer gives every connection an arrowhead (arrowClassMap[conn.variant || 'default'], marker-end on the path), so an open link cannot be represented faithfully. It is now rejected with the stable diagnostic import/unsupported-edge-syntax (line/column at the edge, supportedFixes naming the supported forms). Long-arrow forms (--->) get the same diagnostic instead of the old misleading invalid-node-id error.

2. Subgraph direction — confirmed: direction TB invented direction and TB components in the boundary. Now rejected with import/unsupported-direction-directive; the diagram-level direction applies to all regions, since the layout engine has no per-region direction.

3. First-occurrence-wins declarations — confirmed. A later explicit declaration now updates the earlier implicit one (latest text wins, Mermaid-compatible); two different explicit declarations for the same id exit non-zero with import/flowchart-conflicting-node-declaration instead of silently picking a winner.

4. RL/BT rendered as LR/TD — confirmed: flowchart RL produced coordinates identical to LR. Depth placement is now mirrored for RL/BT (RL: source right of target; BT: source below target); LR/TD layout is unchanged and covered by a new orientation test.

Documentation — added archify/references/mermaid-flowchart-import.md (linked from SKILL.md § Mermaid input): the supported subset, shape→componentType mapping, edge forms, target-mode selection (the importer targets architecture; workflow remains the fresh-authoring path), the diagnostic-code table, and runnable examples verified against the real CLI.

Branch and checks — merged current main (0853a80) into the branch, no conflicts. Two notes: (a) no checks appeared on the previous head — as a first-contribution fork PR the workflows need a maintainer approval click, so the new head may need that same approval to run. (b) While reproducing your cases I found a fifth gate failure on the previous head: imported IR with edge labels failed validate --quality showcase (label offsets biased into the target component; repro: import any labeled LR diagram, then validate). Since #92 requires imported IR to pass the existing quality gates, label placement now compensates the Viewer's source-anchored straight-route label anchor only where needed (vertical half-cell shift, mirrored for BT; horizontal centered), and a new test imports and showcase-validates every valid-*.mmd fixture so this class stays covered.

Verification

  • Sabotage run first per CONTRIBUTING: with the new tests on fedb28e, the seven behavioral tests fail, matching each finding one-to-one.
  • After the fix: node --test test/flowchart-import.test.mjs → 26/26 pass. Full npm test → 776 tests, 746 pass, 0 fail, 30 skipped (Chrome-dependent).
  • All 8 valid fixtures pass import flowchartvalidate architecture --quality showcase (the two labeled-edge fixtures failed showcase before the label fix).
  • archify.zip rebuilt with Node 22 and byte-verified locally so zip-freshness passes (a5ca7af).

- Problem: Archify could not import existing Mermaid flowchart/graph diagrams; users had to re-author topology by hand.
- Fix: Add a focused Mermaid flowchart parser (archify/importers/flowchart.mjs) that maps a documented subset of flowchart syntax to typed architecture IR, with auto-layout, stable diagnostics for unsupported/malformed syntax, and a new 'archify import flowchart' CLI command.
- Verification: npm test in archify/ — 751 tests, 730 pass, 0 fail, 21 skipped (Chrome-dependent). Full import→validate→render pipeline verified on all valid fixtures.

Closes tt-a1i#92
…ections

- Problem: Reviewer FenjuFu reproduced four cases where the flowchart importer silently changed or invented Mermaid semantics: open link --- became a dashed directed edge, subgraph direction invented components named direction/TB, later explicit node declarations lost their labels, and RL/BT diagrams laid out identically to LR/TD. Imported IR with edge labels could also fail showcase layout validation (labels biased into the target component), against issue tt-a1i#92's acceptance criterion that imported IR passes the existing quality gates.
- Fix: Reject open links and the direction directive with stable unsupported diagnostics (import/unsupported-edge-syntax, import/unsupported-direction-directive); apply later explicit declarations over implicit ones and diagnose conflicting explicit redeclarations (import/flowchart-conflicting-node-declaration); mirror depth placement for RL/BT; compensate the Viewer's source-anchored straight-route labels only where needed (vertical half-cell shift, horizontal centered) so every valid fixture passes showcase validation; document the supported subset, target-mode selection, and diagnostic codes in references/mermaid-flowchart-import.md linked from SKILL.md.
- Verification: node --test test/flowchart-import.test.mjs — 26/26 pass (8 new; sabotage run first showed the 7 behavioral tests failing on the original head). npm test — 776 tests, 746 pass, 0 fail, 30 skipped (Chrome-dependent). All 8 valid fixtures now pass import → validate --quality showcase; the labeled-edges and labeled-subgraph fixtures failed showcase before the label fix.
@santhiprakash
santhiprakash force-pushed the feat/mermaid-flowchart-import branch from a5ca7af to cf6a8ca Compare August 29, 2026 14:51
@santhiprakash

Copy link
Copy Markdown
Author

Rebased the branch onto current main (0853a80) and force-pushed the linear history. The new head is cf6a8ca; the diff is byte-identical to the previous head.

I also updated the PR body to reflect the current supported edge forms and verification numbers.

Verification (re-run on the rebased head):

  • cd archify && node --test test/flowchart-import.test.mjs → 26/26 pass.
  • cd archify && npm test → 776 tests, 746 pass, 0 fail, 30 skipped (Chrome-dependent).
  • All 8 valid-*.mmd fixtures pass import flowchartvalidate architecture --quality showcase.
  • archify.zip rebuilt with Node 22; release-identity and zip-freshness checks pass.

No other changes were introduced during the rebase.

@FenjuFu FenjuFu left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for addressing the original four findings. I re-ran the focused suite on cf6a8ca (26/26 pass), confirmed the packaged importer/CLI/SKILL/reference match the tracked content after line-ending normalization, and verified the new RL/BT and label-layout cases. Three contract blockers remain:

  1. Nested subgraphs produce IR that cannot pass the required schema/quality gate (archify/importers/flowchart.mjs:198, contract at archify/references/mermaid-flowchart-import.md:79). Membership is added only to subgraphStack[subgraphStack.length - 1]. Minimal input:

    flowchart TD
    subgraph Outer
    subgraph Inner
    A[Node]
    end
    end
    
    Loading

    parseFlowchart returns success with Outer.wraps: [] and Inner.wraps: [A]; validate architecture --quality showcase --json then fails with schema/minItems on /boundaries/0/wraps. This contradicts both the documented “Nested subgraphs are tracked” statement and #92’s requirement that supported imports pass existing gates. Please either represent nested membership in valid IR or reject nested subgraphs with a stable unsupported diagnostic and narrow the contract, plus add an import→showcase regression.

  2. The explicit-redeclaration fix is still bypassed within one statement (archify/importers/flowchart.mjs:379-381 and :415-417). parseStatement de-duplicates its local components array by id before the global explicit/implicit precedence logic sees the later node. Consequently:

    • A --> A[Label] succeeds but keeps label A instead of Label.
    • A[One] --> A[Two] succeeds with One instead of returning import/flowchart-conflicting-node-declaration.

    This is the same silent first-occurrence behavior the previous review requested to remove. Please preserve the later declaration (or diagnose the conflict) even when both occurrences are in one chain, with regression tests for both cases.

  3. The documented dotted open-link contract disagrees with the parser and Mermaid semantics (archify/references/mermaid-flowchart-import.md:67). The table says both -.- and -.-> become a directed dashed connection. Mermaid defines -.- as a dotted link without an arrowhead and -.-> as the dotted link with an arrowhead: https://mermaid.js.org/syntax/flowchart#minimum-length-of-a-link. The current parser rejects A -.- B, but with the unrelated import/flowchart-invalid-node-id diagnostic. Since Archify cannot preserve an open link, this should be aligned with the --- handling: reject it with the stable unsupported-edge diagnostic and document it as unsupported (or otherwise preserve its no-arrow semantics).

Please add these cases to the fixture-level import→validation coverage. I am not treating my Windows full-suite timeout as a test failure; the focused suite is green. GitHub still reports no checks on the current head, so a maintainer will also need to approve/run the repository workflows before final review.

…redeclarations faithfully

- Problem: a node inside nested Mermaid subgraphs was recorded only in the
  innermost boundary, so outer boundaries shipped with empty wraps lists and
  failed the showcase schema gate (boundaries[].wraps minItems). Explicit
  redeclarations inside a single statement (A --> A[Label], A[One] --> A[Two])
  were silently dropped in favor of the first occurrence. The contract
  documented dotted open links (-.-) as directed dashed edges while the
  parser rejected them with import/flowchart-invalid-node-id.
- Fix: record nested membership in every enclosing boundary; merge
  same-statement occurrences with the cross-statement precedence rules
  (later explicit wins, conflicting explicit definitions diagnosed); reject
  -.- / -..- with the stable import/unsupported-edge-syntax diagnostic and
  correct the contract table.
- Verification: node --test test/flowchart-import.test.mjs 31/31 pass;
  sabotage run confirms the 5 new tests fail on pre-fix code; CLI
  import->showcase repro of all three review cases; archify.zip rebuilt
  with Node 22 (canonical toolchain).
@santhiprakash

Copy link
Copy Markdown
Author

All three contract blockers are addressed on the pushed head 4f9bbf5, each reproduced on cf6a8ca first and re-verified on the new head:

1. Nested subgraphs emitted an empty parent wraps list. Membership was recorded only into the innermost open region, so subgraph Outer / subgraph Inner / A[Node] / end / end parsed successfully but failed validate --quality showcase with schema/minItems on /boundaries/0/wraps. Membership is now recorded into every enclosing region (the IR has no region-inside-region nesting): the case above imports with Outer.wraps: ["A"] and Inner.wraps: ["A"] and passes the showcase gate. The documented contract now states this flattened-membership rule instead of the ambiguous "nested subgraphs are tracked".

2. Same-statement redeclarations bypassed the precedence rules. parseStatement de-duplicated its local component list by id before the cross-statement merge saw the later declaration, so A --> A[Label] kept label A and A[One] --> A[Two] silently kept One. Statement-local occurrences now merge with the same explicit-over-implicit precedence and conflict diagnostics used across statements: A --> A[Label] yields label Label (and the self-loop passes the showcase gate), while A[One] --> A[Two] exits with import/flowchart-conflicting-node-declaration.

3. The dotted open link -.- disagreed with Mermaid and the docs. The table wrongly listed -.- as importing as a directed dashed edge, and the parser rejected it with the unrelated import/flowchart-invalid-node-id. Aligned with the --- handling: A -.- B (and longer arrowless dotted forms like -..-) now exit with import/unsupported-edge-syntax, the docs table lists only the directed dotted forms (-.\->, -..\->), and the unsupported paragraph names both open-link families.

Fixture-level coverage: new valid-nested-subgraphs.mmd, valid-same-statement-redeclare.mmd, malformed-conflicting-same-statement.mmd, and unsupported-dotted-open-link.mmd fixtures; the valid-* set is iterated through import → validate --quality showcase in the suite, so the nested-subgraph gate failure is a permanent regression test.

Verification (all on 4f9bbf5):

  • Sabotage-first: on cf6a8ca, the new tests fail one-to-one with the findings (including the nested fixture failing the showcase gate).
  • node --test test/flowchart-import.test.mjs → 31/31 pass.
  • npm test → 781 tests, 751 pass, 0 fail, 30 skipped (Chrome-dependent).
  • archify.zip rebuilt with Node 22; the rebuild is byte-identical to the committed archive (zip-freshness), and check-release-identity passes.

The branch is based on current main (b36d79f); remote checks still await the maintainer workflow approval for fork PRs.

…ranch

- No source conflicts: archify/SKILL.md and archify/bin/archify.mjs auto-merged
  (their update-awareness additions vs our import-contract edits are disjoint).
- archify.zip regenerated from the merged tree with Node 22 (deterministic
  build) to resolve the binary conflict.
- Verification: flowchart suite 31/31; npm test 896 tests / 865 pass / 0 fail /
  31 skipped (Chrome-dependent); check-release-identity ok.
…art-import

# Conflicts:
#	archify.zip
#	archify/bin/archify.mjs

@tt-a1i tt-a1i left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed current head433a0bfbf3f10a2997eb162f8afa439148421702. Thanks for addressing the previous nested-membership/redeclaration/open-link review: those cases now pass, as do all31 importer tests, and all78 ZIP payload files match. Additional public-CLI cases still need fixes.

Standards / output safety

  • [P1] archify/bin/archify.mjs:1990-1992 writes without the shared input-alias guard. import flowchart diagram.mmd diagram.mmd --json exits0/ok:true and replaces the user's Mermaid source with JSON. Reject same-path/symlink/hard-link aliases before commit and preserve the source.
  • [P2] The same write path leaves --json stdout empty and throws a raw EISDIR stack when the output is a directory. Return a stable diagnostic receipt for output preparation/write failures.

Spec / topology and valid output

  • [P2] flowchart.mjs:91-105 ignores declaration-line remainder: flowchart LR; A[Lost] --> B[Lost] followed by C[Kept] imports successfully with only C and zero edges. Parse the remainder or reject it explicitly rather than dropping topology.
  • [P2] :138-145 turns a subgraph endpoint into a new backend component: subgraph Group, A[Inside], end, B[Outside] --> Group produces a fictitious Group service plus the Group boundary and passes showcase. Model supported grouping faithfully or reject the unsupported endpoint.
  • [P2] :632-641 fixes every box to140px. A[Customer subscription management service] --> B[Backend] imports ok but fails the advertised validation handoff (approximately264px label). Measure preserved labels and size/space the output accordingly.
  • [P2] :326-330 emits wraps:[] for an empty subgraph and overwrites output with ok:true even though the resulting IR fails schema/minItems. Diagnose unrepresentable empty groups before writing the last valid output.

These are new reproductions on this head, separate from the resolved previous findings. No full-suite/browser acceptance claimed; no source edits or merge.

- Problem: import could overwrite the Mermaid source via same-path/
  symlink/hard-link output aliases, crashed with a raw EISDIR stack on a
  directory output with no JSON receipt, silently dropped statement
  topology after 'flowchart LR;', invented a component when an edge
  named a subgraph, fixed every cell at 140px so long labels failed the
  advertised validation handoff, and emitted empty-subgraph wraps:[]
  that violates schema minItems while reporting ok:true.
- Fix: reject aliased outputs before writing (realpath + dev/ino
  identity), emit a stable output/write receipt for write failures,
  reject declaration-line remainder and subgraph endpoints with named
  diagnostics, size cells from the validator's own label measurement
  (textUnits*6.6) with width-aware column/row strides, and reject empty
  subgraphs at 'end'.
- Verification: sabotage-first — 8 new tests fail on 433a0bf, pass on
  this head; flowchart-import 40/40; full suite 1053 tests, 1022 pass,
  0 fail, 31 skipped.
…owchart-import; rebuild archify.zip canonically on Node 22.14.0
@santhiprakash

Copy link
Copy Markdown
Author

Pushed fixes for all six findings on 0a8b497 (also merges current main, so the branch is conflict-free again). Each finding was reproduced on 433a0bfb before fixing; each now has a regression test that fails on 433a0bfb and passes on the new head.

Standards / output safety

  • [P1] same-path/symlink/hard-link output alias — reproduced: import flowchart diagram.mmd diagram.mmd --json exited 0/ok:true and replaced the source with JSON. Fix: commandImport now preflights the output against the input before any work (importOutputAliasesInput, archify/bin/archify.mjs) using realpath equality (same path + symlinks) and dev/inode identity (hard links), and exits 1 with an input/output-alias diagnostic; the source is untouched. Tests cover the same-path and hard-link variants and assert the file content is byte-preserved.
  • [P2] directory output / missing receipt — reproduced: text mode died with a raw EISDIR stack; --json stdout was empty. Fix: the write is wrapped and every output preparation/write failure emits the same stable receipt shape as input errors (output/write, systemCode: EISDIR, supportedFixes) in both text and --json modes, exit 1. The test asserts --json stdout parses to ok:false and text-mode stderr contains no stack frames.

Spec / topology and valid output

  • [P2] declaration-line remainder — reproduced: flowchart LR; A[Lost] --> B[Lost] followed by C[Kept] imported as 1 component / 0 connections. Fix: any remainder after the direction is rejected with import/declaration-remainder, column pointed at the first character after the direction; a bare trailing ; is still accepted (control test).
  • [P2] subgraph as edge endpoint — reproduced: B[Outside] --> Group produced a fictitious Group backend plus the boundary (3 components). Fix: after parsing, every connection endpoint is checked against subgraph labels and generated sgN ids; matches are rejected with import/edge-references-subgraph at the edge's own line number (both source and target endpoints covered).
  • [P2] fixed 140px cells — reproduced: A[Customer subscription management service] --> B[Backend] imported ok but failed the advertised handoff with Label ... (~264px) is wider than component "A" (140px). Fix: cells are sized with the same measurement the validator uses (textUnits(label) * 6.6, imported from archify/renderers/shared/utils.mjs so the two cannot drift): width = max(140, ceil(units*6.6 - 8) + 4). Horizontal columns and vertical rows advance by the measured widths instead of a fixed stride, so widened cells never overlap; the auto viewBox still fits the output. New fixture valid-long-labels.mmd joins the existing "every valid fixture passes showcase validation" loop.
  • [P2] empty subgraph — reproduced: subgraph Empty / end emitted wraps: [] with ok:true while archify validate failed /boundaries/0/wraps must NOT have fewer than 1 items. Fix: a subgraph closing with zero wrapped nodes is rejected at end with import/empty-subgraph, before any IR is built — no output is written.

Verification on 0a8b497:

  • Sabotage-first: all 8 new tests fail on 433a0bfb and pass on 0a8b497.
  • node --test test/flowchart-import.test.mjs → 40/40 pass.
  • npm test (full suite) → 1057 tests, 1026 pass, 0 fail, 31 skipped (includes the 4 tests new from upstream Clarify visual-check as automated browser evidence #233).
  • archify.zip rebuilt with the canonical Node 22.14.0 toolchain and byte-identical to a second rebuild; full suite re-run green on the merged head.

@tt-a1i tt-a1i left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review fixed to head 0a8b4973823e172510087c2f8fb836cc0e307019 against current main 199360cc6687a7857b54dd188d4922b09e466a4b.

Requesting changes for three confirmed contract gaps:

  1. [P1] Commit the import output without a source-overwrite race. commandImport checks input/output aliasing once before parsing, then later follows outputPath with writeFileSync. With a 100,000-node input, changing an initially safe output symlink to point at the Mermaid input during parsing made the command exit 0 with ok: true while replacing the source with JSON (inputPreserved: false). This breaks the source-preservation contract documented at lines 1935–1937 and asserted by the current alias tests. Use a non-following atomic candidate/rename path and recheck identity at the commit point; add a race regression.

  2. [P1] Track actual Mermaid subgraph identity instead of labels plus synthetic sgN names. For subgraph G [Group Label] ... end followed by B --> G, the importer returns ok: true, invents a third ordinary component {id:"G", label:"G"}, and emits a boundary labeled G [Group Label]; showcase validation then passes the corrupted topology. Conversely, after any subgraph, a legitimate node named sg1 is rejected as a subgraph endpoint because lines 317–318 reserve synthetic counter names that Mermaid never reserved. Parse/store the authored subgraph id and title separately, and reject or faithfully map edges using only authored identities.

  3. [P2] Ensure every successful supported import can pass the advertised validation handoff. A supported labeled edge such as A[Alpha] -->|This is an extremely long relationship label that is likely wider than the available route gap| B[Beta] imports with exit 0/ok: true, but immediate validate architecture --quality showcase --json exits 1 because the label overlaps both components; deliver also fails. The current layout expands cells only for node labels and keeps an 80px relationship gap. A small supported cycle (A→B→C→B) likewise imports successfully but fails validation with clean-flow/edge-through-node. Either generate gate-valid geometry for these supported topologies or reject them during import with stable source diagnostics; add import→validate regressions for both.

Evidence on the synthesized current-main integration: merge completed without conflicts; git diff --check passed; focused importer/CLI tests passed 81/81; full npm test passed 1,030 with 31 environment-dependent skips and 0 failures; staged skill vs archify.zip matched byte-for-byte across all 78 packaged files. These green tests do not cover the three reproductions above. Remote CI has not run on this head.

- Problem: the import write path re-checked input/output aliasing only
  before parsing, so an output symlink swapped mid-parse made the CLI
  exit 0 while replacing the Mermaid source with the import result;
  authored subgraph ids ("subgraph G [Group Label]") were not tracked,
  so edges to G invented a phantom component, boundary labels carried
  raw declaration text, and synthetic sgN names wrongly reserved
  legitimate node ids; straight horizontal routes with labels wider
  than the route gap and small cycles imported ok but failed the
  advertised validate --quality showcase handoff.
- Fix: commit the import output through a non-following O_EXCL
  candidate/rename with an alias recheck at the commit point; parse and
  store authored subgraph id/title separately and reject subgraph-edge
  endpoints on authored identities only (sgN is no longer reserved;
  an explicit node declaration sharing a subgraph identity keeps the
  node); move over-wide horizontal edge labels below the route using
  the validator's own textUnits measurement and make layer assignment
  first-assignment-wins so cycles no longer strand a node under a
  straight route.
- Verification: sabotage runs fail pre-fix (1 CLI race test; 11
  importer tests); flowchart-import suite 57/57; full npm test 1074
  tests / 1043 pass / 0 fail / 31 env-skips (one update-notifier timing
  flake, green 4/4 on rerun); archify.zip byte-identical to a canonical
  Node 22.14.0 rebuild (78 files).
@santhiprakash

Copy link
Copy Markdown
Author

Thanks for the three confirmed reproductions — all three are fixed on head 753fec8 (pushed to this branch).

1. Source-overwrite race in the import write path (P1). Reproduced on 0a8b497: an output symlink re-pointed at the input mid-parse made the CLI exit 0 while replacing the source. The commit now goes through commitImportOutput (archify/renderers/shared/output-path.mjs): an importOutputAliasesInput recheck at the commit point (same-path, realpath, and dev+ino hard-link identity), then an O_CREAT|O_EXCL candidate in the output's directory and rename(2) — which replaces a symlink instead of following it, so no preflight→commit swap can reach the input. A refused commit exits 1 with the same input/output-alias diagnostic as the preflight and leaves no candidate files behind. Regression: "CLI import through a symlinked output preserves the symlink target (race-safe end to end)" fails on the old path (input replaced) and passes on 753fec8.

2. Authored subgraph identity (P1). Reproduced on 0a8b497 (B --> G after subgraph G [Group Label] invented a third component {id:"G"}; a legitimate node named sg1 was rejected). parseFlowchart now parses subgraph id [Title] / subgraph id["Title"] / subgraph Title into separate authoredId and label; boundary labels carry the title alone, and edge endpoints are rejected on authored identities only — the synthetic sgN names never leave the parser and are no longer reserved, while an explicitly declared node sharing a subgraph identity keeps the node for its edges. B --> G now exits 1 with import/edge-references-subgraph and the source is untouched.

3. Import→validate handoff (P2). Both reproductions confirmed on 0a8b497. For the wide-label case, the emitter moves a horizontal same-row label below the route when its measured width (textUnits(label) * 6.6 plus a small margin — the validator's own measurement, per your round-3 note) exceeds the route gap. For cycles, layer assignment is now first-assignment-wins instead of longest-path relaxation, so A→B; B→C; C→B no longer strands C between A and B under the straight A→B route (clean-flow/edge-through-node). Both cases now import to geometry that passes validate architecture --quality showcase --json; regressions: long-label LR/RL/TB, cycle, and shared-successor diamond tests that run import→validate end to end.

Verification on 753fec8: the new tests fail on 0a8b497 (1 CLI race test; 11 subgraph/geometry tests), flowchart-import suite 57/57, full npm test 1074 tests / 1043 pass / 0 fail / 31 environment-dependent skips (one update-notifier timing flake on the first run, green 4/4 on rerun — unrelated module), and archify.zip is byte-identical to a canonical Node 22.14.0 rebuild (78 files).

…/mermaid-flowchart-import; rebuild archify.zip canonically on Node 22.14.0
…ermaid-flowchart-import

- Problem: upstream tt-a1i#299 (3c42a59) rewrote archify.zip, conflicting with the
  PR's tracked zip (4th occurrence of the recurring binary-zip conflict class).
- Fix: merged origin/main; rebuilt archify.zip canonically on Node 22.14.0
  (two builds byte-identical, sha256 84bab05dcaaf133f...) and staged it.
- Verification: full suite on merged tree 1089 tests / 1058 pass / 0 fail /
  31 skipped; focused flowchart-import 57/57.
…HTML output guard) into feat/mermaid-flowchart-import

- Problem: upstream main rewrote archify.zip (tt-a1i#321 rebuild, tt-a1i#322 output-path extension guard) and touched renderers/shared/output-path.mjs, conflicting with the flowchart-import PR head d1626ee.
- Fix: merged origin/main; auto-merge kept the disjoint regions (the CLI .html extension guard inside resolveOutputPath vs the import alias/commit helpers appended after it — the import output path does not route through resolveOutputPath); archify.zip rebuilt canonically on Node 22.14.0, byte-identical across two runs.
- Verification: focused flowchart-import 57/57; full suite on the merged tree 1107 tests / 1079 pass / 27 skipped with the single failure being upstream's update-notifier concurrency flake (reproduced with the same signature on a pristine origin/main control run).
…t-import

# Conflicts:
#	archify.zip
#	archify/bin/archify.mjs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Import Mermaid flowcharts into validated Archify typed IR

3 participants