Skip to content

feat(leanix-bridge): Phase 2 & 3, CLI, tooling docs (Draw.io + CLI)#2768

Merged
davydkov merged 32 commits intolikec4:mainfrom
sraphaz:feat/leanix-bridge-sync-reconcile-reports
Mar 19, 2026
Merged

feat(leanix-bridge): Phase 2 & 3, CLI, tooling docs (Draw.io + CLI)#2768
davydkov merged 32 commits intolikec4:mainfrom
sraphaz:feat/leanix-bridge-sync-reconcile-reports

Conversation

@sraphaz
Copy link
Copy Markdown
Collaborator

@sraphaz sraphaz commented Mar 17, 2026

Summary

This PR extends the LeanIX bridge (from PR #2746) with Phase 2 (inbound: inventory snapshot, reconciliation), Phase 3 (impact analysis, drift detection, ADR generation, governance checks), first-class CLI commands, and documentation updates for Draw.io and CLI as requested.


Changes

1. LeanIX bridge package (@likec4/leanix-bridge)

Phase 2 – Inbound

  • Inventory snapshot: fetchLeanixInventorySnapshot(client, options?) — fetches a read-only snapshot of LeanIX fact sheets and relations (paginated); returns LeanixInventorySnapshot.
  • Reconciliation: reconcileInventoryWithManifest(snapshot, manifest, options?) — compares manifest to LeanIX snapshot; returns ReconciliationResult (matched, unmatchedInLikec4, unmatchedInLeanix, ambiguous). Optional dryRun improves matching.

Phase 3 – Reports & governance

  • Drift report: buildDriftReport(manifest, snapshot) — produces a DriftReport listing mismatches (title, description, relations) between manifest and snapshot.
  • Impact report: impactReportFromSyncPlan(syncPlan) — computes impact analysis from a sync plan; returns ImpactReport with affected entities and severity.
  • ADR generation: generateAdrFromReconciliation(reconciliation, options?) and generateAdrFromDriftReport(driftReport, options?) — generate ADR-style markdown from reconciliation or drift.
  • Governance checks: runGovernanceChecks(manifest, options?) — runs configurable governance rules; returns GovernanceReport with pass/fail and violation messages.

Exports & quality

  • All new functions and types are exported from packages/leanix-bridge/src/index.ts.
  • Runtime type guards: isBridgeManifest(obj), isLeanixInventorySnapshot(obj) for parsed JSON.
  • Unit tests for mapping, to-bridge-manifest, to-leanix-inventory-dry-run, sync-to-leanix, drawio-leanix-roundtrip, bridge-artifacts, and for all Phase 2/3 modules (adr-generation, drift-report, governance-checks, impact-report, leanix-inventory-snapshot, reconcile). All tests pass.

2. CLI (likec4)

  • Codegen: likec4 gen leanix-dry-run, likec4 gen leanix-inventory-snapshot -o <dir>, likec4 gen leanix-reconcile -o <dir>.
  • Sync: likec4 sync leanix [--dry-run | --apply] -o <dir> — dry-run (plan only) or apply (live sync); uses manifest/dry-run artifacts; --apply requires LEANIX_API_TOKEN.
  • Export: likec4 export drawio --profile leanix -o <dir> — already present; documented in tooling docs.
  • Shared: packages/likec4/src/cli/bridge/leanix-client.ts (env-based client), packages/likec4/src/cli/bridge/shared.ts (model adapter, artifact builders).

3. Generators

  • Draw.io parse: Adjustments in packages/generators/src/drawio/parse-drawio.ts for compatibility with bridge-managed metadata (no breaking changes).
  • Exports: packages/generators/src/index.ts — re-exports as needed for consumers.

4. Documentation (tooling)

  • Draw.io (apps/docs/src/content/docs/tooling/drawio.mdx): Export profiles (default, leanix), bridge-managed attributes, CLI example, link to bridge package.
  • CLI (apps/docs/src/content/docs/tooling/cli.mdx): --profile option for Export to Draw.io, example with --profile leanix.

5. E2E

  • e2e/src/likec4-cli-bridge.spec.ts — tests for gen leanix-dry-run and sync leanix --dry-run (artifact layout and structure).

6. Other

  • Changeset: @likec4/docs-astro patch, @likec4/generators and likec4 minor, @likec4/leanix-bridge minor.
  • .gitignore: documentação de apoio em docs/ remains ignored; no support docs committed.

Testing

  • pnpm test in packages/leanix-bridge: all unit tests pass.
  • pnpm ci:typecheck: passes (including @likec4/leanix-bridge).
  • E2E: run from e2e/ with a project that has a LikeC4 model; optional LEANIX_API_TOKEN for snapshot/sync tests.

Checklist

  • I've thoroughly read the latest contribution guidelines.
  • My commit messages follow conventional spec.
  • I've added tests to cover my changes (unit tests for bridge and Phase 2/3; e2e for CLI bridge flows).
  • New APIs and CLI commands documented (README, tooling docs).
  • Draw.io and CLI tooling pages updated (Export profiles, --profile).
  • No intentional breaking changes to existing public APIs.
  • Code formatted with dprint; follows repository conventions.

Related

  • Builds on PR #2746 (LeanIX bridge + Draw.io profile leanix).
  • Implements Phase 2 (inventory snapshot, reconciliation) and Phase 3 (drift, impact, ADR, governance) of the LeanIX bridge roadmap.

Summary by CodeRabbit

  • New Features

    • Draw.io export profiles: default or leanix via --profile (CLI & generator)
    • CLI: gen leanix-dry-run, gen leanix-inventory-snapshot, gen leanix-reconcile
    • New sync command: sync leanix with dry-run / apply modes; artifact writer for manifest/dry‑run/report
    • Reporting & analysis: inventory snapshot, reconciliation, sync planning/apply, impact/drift reports, governance checks, ADR generation
  • Documentation

    • CLI docs updated with --profile and LeanIX usage notes
  • Tests

    • Added e2e and unit tests covering CLI flows, reconciliation, reports, and sync logic

sraphaz added 11 commits March 13, 2026 22:17
…nsibility, unit test

- Add cli/bridge/shared.ts: asBridgeModel, buildBridgeArtifacts, writeBridgeArtifacts, ERR_*, BRIDGE_ARTIFACT_NAMES (remove G5 duplication, G25 magic strings)
- Refactor leanix-dry-run handler to use shared; refactor sync leanix to use shared + createLeanixClientFromEnv
- Fix leanix-dry-run.spec: unit test with inline fixture (no fromWorkspace), 4 tests pass; matches repo pattern

Made-with: Cursor
- Phase 2: leanix-inventory-snapshot, reconcile (inbound)
- Phase 3: drift-report, impact-report, adr-generation, governance-checks
- CLI: gen leanix-inventory-snapshot, gen leanix-reconcile; sync leanix; bridge leanix-client
- Docs: Draw.io Export profiles (default/leanix), CLI --profile option
- Refactors (Uncle Bob): sync-to-leanix, to-bridge-manifest, to-leanix-inventory-dry-run, report, leanix-api-client, drawio-leanix-roundtrip
- E2E: likec4-cli-bridge.spec.ts

Made-with: Cursor
…t tests; review responses doc; parse-drawio TODO

Made-with: Cursor
….gitignore docs

- README: Phase 2 command descriptions, --profile leanix note, Phase 2/3 example
- CLI: timer in try/finally (inventory-snapshot, dry-run, reconcile)
- CLI: SyncCmdArgs + isSyncCmdArgs type guard; runSyncLeanix JSDoc
- leanix-reconcile: type guards from @likec4/leanix-bridge/validate
- leanix-bridge: validate.ts (isBridgeManifest, isLeanixInventorySnapshot)
- leanix-bridge: reconcile destructure candidate; adr formatIsoDateString defensive
- leanix-bridge: fetchAllRelations parallel (concurrency); fetchAllFactSheets retry; fetchPage(cursor)
- Specs: stronger assertions (dry-run, inventory-snapshot mock + relations + likec4IdAttribute)
- e2e + root: @likec4/leanix-bridge override/tarball for install; pretest:e2e packs leanix-bridge
- .gitignore: documentação de apoio em docs/

Made-with: Cursor
…rrors, timer finally, validate guards, relation retry

Made-with: Cursor
- README: Phase 2 example use reconcileInventoryWithManifest(snapshot, manifest)
- leanix-inventory-snapshot: validate maxFactSheets (non-negative int); cap result in page loop; single withGraphQLRetry for fetchPage and relations
- validate: isRecord + explicit type checks (no 'as'); isBridgeManifest/isLeanixInventorySnapshot strict
- sync leanix: guard dryRun&&apply throw; plan.errors fail command (like apply path)
- leanix-client: trim env vars, blank token/baseUrl treated as unset
- leanix-reconcile: log on workspace load fallback
- JSDoc: leanixInventorySnapshotHandler, leanixDryRunHandler, createLeanixClientFromEnv, asBridgeModel, buildBridgeArtifacts, writeBridgeArtifacts, toReport, mergeWithDefault

Made-with: Cursor
…op tracking docs de apoio

- .changeset: @likec4/generators and likec4 as minor (semver for new CLI/API)
- adr-generation: add Decision section to generateAdrFromDriftReport
- reconcile: use NAME_TYPE_SEP instead of hardcoded \\0
- docs/PR-REVIEW-AND-BOT-RESPONSES.md: remove from repo (keep in .gitignore)

Made-with: Cursor
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 17, 2026

🦋 Changeset detected

Latest commit: 533ede7

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 20 packages
Name Type
@likec4/docs-astro Minor
@likec4/generators Minor
@likec4/leanix-bridge Minor
likec4 Minor
@likec4/playground Minor
@likec4/language-services Minor
@likec4/vite-plugin Minor
@likec4/mcp Minor
likec4-vscode Minor
@likec4/style-preset Minor
@likec4/styles Minor
@likec4/config Minor
@likec4/core Minor
@likec4/diagram Minor
@likec4/language-server Minor
@likec4/layouts Minor
@likec4/log Minor
@likec4/react Minor
@likec4/tsconfig Minor
@likec4/vscode-preview Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 17, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a new @likec4/leanix-bridge package (Phase 2/3) with inventory snapshot fetching, reconciliation, drift/impact reports, ADR generation, governance checks, GraphQL sync helpers, CLI commands (gen leanix-*, sync leanix), Draw.io leanix export/profile support, generator parse tweaks, docs, tests, and CI/workspace wiring.

Changes

Cohort / File(s) Summary
LeanIX bridge core
packages/leanix-bridge/src/index.ts, packages/leanix-bridge/README.md, packages/leanix-bridge/src/contracts.ts
Public API expanded with many new exports (fetch/reconcile/impact/drift/adr/governance/validate), README updated, and LEANIX_PROVIDER constant added.
Mapping utilities
packages/leanix-bridge/src/mapping.ts
Added FALLBACK constants, mergeWithDefault, getFactSheetType, getRelationType; default mapping behavior made robust.
Inventory snapshot
packages/leanix-bridge/src/leanix-inventory-snapshot.ts, .../leanix-inventory-snapshot.spec.ts
New snapshot model and fetchLeanixInventorySnapshot with pagination, batching, retry logic, and optional likec4Id handling.
Reconciliation & report
packages/leanix-bridge/src/reconcile.ts, .../reconcile.spec.ts, packages/leanix-bridge/src/report.ts
Added reconcileInventoryWithManifest and related types; renamed toReportbuildBridgeReport and updated call sites.
Drift & impact
packages/leanix-bridge/src/drift-report.ts, .../drift-report.spec.ts, packages/leanix-bridge/src/impact-report.ts, .../impact-report.spec.ts
Added buildDriftReport, impactReportFromSyncPlan and tests producing structured statuses and human summaries.
Governance & ADR generation
packages/leanix-bridge/src/governance-checks.ts, .../governance-checks.spec.ts, packages/leanix-bridge/src/adr-generation.ts, .../adr-generation.spec.ts
New governance checks API (runGovernanceChecks, types) and ADR markdown generators (generateAdrFromReconciliation, generateAdrFromDriftReport) with tests.
Validation helpers
packages/leanix-bridge/src/validate.ts, .../validate.spec.ts
Runtime type guards isBridgeManifest and isLeanixInventorySnapshot with comprehensive tests.
Sync / GraphQL operations
packages/leanix-bridge/src/leanix-graphql-operations.ts, packages/leanix-bridge/src/sync-to-leanix.ts, .../sync-to-leanix.spec.ts
Centralized GraphQL helpers (find/patch/create relation), refactored sync logic to use them, added resolveExistingFactSheetId helper and tests.
Bridge artifact utilities
packages/likec4/src/cli/bridge/shared.ts, packages/likec4/src/cli/bridge/leanix-client.ts
Model→bridge adapters, artifact builders/writers, env-based LeanIX client helpers (createLeanixClientFromEnv, requireLeanixClient) and constants.
CLI codegen & sync commands
packages/likec4/src/cli/codegen/..., packages/likec4/src/cli/sync/..., packages/likec4/src/cli/index.ts
Added gen subcommands (leanix-dry-run, leanix-inventory-snapshot, leanix-reconcile) and sync leanix command with handlers for build/plan/apply and exported types.
Generators / Draw.io
packages/generators/src/drawio/parse-drawio.ts, .../parse-drawio.spec.ts, .../generate-drawio.spec.ts, packages/generators/src/index.ts
Added vertexDepth and isUsableBridgeId helpers affecting FQN/bridgeId assignment; some tests relaxed/skipped for LeanIX profile; re-exported decompressDrawioDiagram.
Docs & CLI options
apps/docs/src/content/docs/tooling/drawio.mdx, apps/docs/src/content/docs/tooling/cli.mdx
Documented new Draw.io leanix export profile, --profile CLI option, and generator profile/LeanIX-specific options and examples.
E2E & unit tests
e2e/src/likec4-cli-bridge.spec.ts, various *.spec.ts
New integration e2e tests for CLI gen/sync workflows and many unit tests across bridge modules.
Build / workspace updates
packages/likec4/package.json, packages/likec4/tsconfig.cli.json, e2e/package.json, package.json
Added @likec4/leanix-bridge workspace dependency, pnpm overrides, tsconfig path refs, and pretest filters.
CI, .gitignore, changeset
.github/workflows/checks.yaml, .github/workflows/e2e-update-screenshots.yaml, .gitignore, .changeset/leanix-phase2-phase3-cli-docs.md
Added leanix-bridge to CI install lists, YAML scalar quoting normalization, expanded docs-related .gitignore patterns, and new changeset documenting Phase2/3 CLI/docs updates.

Sequence Diagram(s)

sequenceDiagram
    rect rgba(220,230,241,0.5)
    actor User
    participant CLI as likec4 CLI
    participant Workspace as LikeC4 Workspace
    participant Bridge as Bridge utils
    participant LeanIX as LeanIX API
    participant Reconcile as Reconcile module
    participant Reporter as Drift/Impact/ADR
    end

    User->>CLI: run sync leanix [--dry-run|--apply]
    CLI->>Workspace: open workspace & layout model
    Workspace-->>CLI: layouted model
    CLI->>Bridge: buildBridgeArtifacts(manifest, dryRun)
    Bridge-->>CLI: write artifacts (manifest, dryRun, report)
    CLI->>LeanIX: fetchLeanixInventorySnapshot (optional)
    LeanIX-->>CLI: snapshot
    CLI->>Reconcile: reconcileInventoryWithManifest(manifest, snapshot)
    Reconcile-->>CLI: reconciliation result
    CLI->>Reporter: buildDriftReport / impactReport / generateAdr
    Reporter-->>CLI: reports
    CLI->>User: write outputs & log summary
Loading
sequenceDiagram
    rect rgba(235,247,233,0.5)
    participant Generator as Draw.io generator
    participant Parser as parse-drawio
    participant Mapping as leanix mapping
    participant Output as Bridge manifest
    end

    Generator->>Parser: parse diagram (vertices, edges)
    Parser->>Parser: compute vertexDepth & validate bridgeId usability
    Parser->>Mapping: assign FQNs or bridge IDs
    Mapping-->>Output: produce elements for manifest
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Suggested reviewers

  • davydkov

"🐰 Hop hop, I stitched a bridge so neat,
Snapshots, reconciles, and ADRs to meet,
Drift and checks keep the garden right,
CLI hops in day or night,
Celebrate Phase Two and Three — a carrot treat!"

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.79% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly and specifically summarizes the main changes: Phase 2 & 3 features for the LeanIX bridge, plus CLI commands and tooling documentation.
Description check ✅ Passed The PR description comprehensively covers the objectives, lists all major changes across modules, documents new APIs and CLI commands, includes testing information, and completes the provided checklist.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sraphaz
Copy link
Copy Markdown
Collaborator Author

sraphaz commented Mar 17, 2026

@coderabbitai can you please review this?

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 17, 2026

@sraphaz Sure! I'll start reviewing the PR right away.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Nitpick comments (5)
.gitignore (1)

138-139: Optional: Remove redundant pattern.

The pattern docs/PR-*-REVIEW.md on line 138 is redundant since docs/PR-*.md on line 139 already matches files like PR-123-REVIEW.md. You can safely remove line 138 to simplify the ignore rules.

♻️ Simplify by removing redundant pattern
-docs/PR-*-REVIEW.md
 docs/PR-*.md
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.gitignore around lines 138 - 139, The .gitignore contains a redundant
pattern: remove the specific pattern "docs/PR-*-REVIEW.md" because the broader
pattern "docs/PR-*.md" already matches those files; delete the
"docs/PR-*-REVIEW.md" entry so only "docs/PR-*.md" remains.
packages/likec4/src/cli/codegen/leanix-reconcile.ts (1)

79-85: Promote dry-run enrichment fallback from debug to warn.

If workspace enrichment fails, reconciliation still runs but with reduced matching quality; warn makes that degradation visible to normal users.

🔎 Suggested tweak
-      logger.debug(
+      logger.warn(
         `Could not load workspace for dryRun enrichment; proceeding without it: ${
           err instanceof Error ? err.message : String(err)
         }`,
       )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/likec4/src/cli/codegen/leanix-reconcile.ts` around lines 79 - 85, In
the catch block that currently logs "Could not load workspace for dryRun
enrichment; proceeding without it..." using logger.debug and then sets dryRun =
undefined, change the log call to logger.warn so the failure is visible to
normal users; keep the existing error formatting (err instanceof Error ?
err.message : String(err)) and preserve setting dryRun = undefined and
surrounding behavior in the same catch block to maintain functionality.
e2e/src/likec4-cli-bridge.spec.ts (1)

12-63: Consider extracting shared e2e artifact assertions.

Both tests repeat the same cleanup, directory listing, and JSON validation flow; a small helper would reduce duplication and future drift.

♻️ Refactor sketch
+function resetOutDir() {
+  rmSync(outDirAbs, { recursive: true, force: true })
+  mkdirSync(outDirAbs, { recursive: true })
+}
+
+function expectBridgeArtifacts(expect: any) {
+  const entries = readdirSync(outDirAbs, { withFileTypes: true }).map(e => e.name).sort()
+  expect(entries).toContain('manifest.json')
+  expect(entries).toContain('leanix-dry-run.json')
+  expect(entries).toContain('report.json')
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/src/likec4-cli-bridge.spec.ts` around lines 12 - 63, The two tests
'LikeC4 CLI - gen leanix-dry-run produces manifest, leanix-dry-run, report' and
'LikeC4 CLI - sync leanix --dry-run produces same artifacts and does not require
token' duplicate cleanup, dir creation, listing and JSON assertions; extract
that logic into a small helper (e.g., createCleanOutDir(outDirAbs) for
rmSync/mkdirSync and assertE2EArtifacts(outDirAbs) for checking entries and
parsing manifest/leanix-dry-run/report) and call those helpers from both test
bodies (replace the repeated rmSync/mkdirSync, readdirSync checks and JSON
parse/assert blocks). Ensure helper names are unique (createCleanOutDir,
assertE2EArtifacts) so you can locate and replace the repeated code in the
tests.
packages/leanix-bridge/src/governance-checks.ts (1)

43-49: Consider simplifying the nested ternary for readability.

The nested ternary on line 47 works correctly but is flagged for complexity. A minor refactor would improve clarity.

♻️ Simplified buildCheck return
 function buildCheck(
   id: string,
   name: string,
   passed: boolean,
   failedMessage?: string,
 ): GovernanceCheckResult {
-  return {
-    id,
-    name,
-    passed,
-    ...(passed ? {} : failedMessage !== undefined ? { message: failedMessage } : {}),
-  }
+  const result: GovernanceCheckResult = { id, name, passed }
+  if (!passed && failedMessage !== undefined) {
+    result.message = failedMessage
+  }
+  return result
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/leanix-bridge/src/governance-checks.ts` around lines 43 - 49, The
return object in the function that produces the governance check (the block
returning id, name, passed, ...(passed ? {} : failedMessage !== undefined ? {
message: failedMessage } : {})) is using a nested ternary that hurts
readability; replace it with a simple conditional before the return (e.g.,
compute a messageProps variable if !passed and failedMessage exists, or set it
to an empty object) and then spread messageProps into the returned object,
referencing the same identifiers id, name, passed, and failedMessage to locate
the code.
packages/leanix-bridge/src/reconcile.ts (1)

73-160: Consider extracting helper functions to reduce cognitive complexity.

SonarCloud flags this function with cognitive complexity of 34 (allowed: 30). The matching logic is sound, but the function could be more maintainable if split into smaller helpers.

♻️ Suggested refactoring approach

Extract the three matching strategies into separate functions:

+function tryMatchByManifestFactSheetId(
+  entity: ManifestEntity,
+  canonicalId: CanonicalId,
+  snapshotById: Map<string, LeanixFactSheetSnapshotItem>,
+  usedFactSheetIds: Set<string>,
+): MatchedPair | null {
+  const leanixExternal = entity.external?.[LEANIX_PROVIDER]
+  const manifestFactSheetId = leanixExternal?.factSheetId ?? leanixExternal?.externalId
+  if (manifestFactSheetId != null) {
+    const fs = snapshotById.get(manifestFactSheetId)
+    if (fs) {
+      usedFactSheetIds.add(fs.id)
+      return { canonicalId, factSheetId: fs.id, name: fs.name, type: fs.type }
+    }
+  }
+  return null
+}
+
+function tryMatchByLikec4Id(
+  canonicalId: CanonicalId,
+  snapshotByLikec4Id: Map<string, LeanixFactSheetSnapshotItem>,
+  usedFactSheetIds: Set<string>,
+): MatchedPair | null {
+  const byLikec4 = snapshotByLikec4Id.get(canonicalId)
+  if (byLikec4 && !usedFactSheetIds.has(byLikec4.id)) {
+    usedFactSheetIds.add(byLikec4.id)
+    return { canonicalId, factSheetId: byLikec4.id, name: byLikec4.name, type: byLikec4.type }
+  }
+  return null
+}

Then call these helpers in sequence within the main loop.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/leanix-bridge/src/reconcile.ts` around lines 73 - 160, The function
reconcileInventoryWithManifest is too complex; extract the three matching
strategies into small helpers to reduce cognitive complexity: create
matchByManifestFactSheetId(manifestFactSheetId, snapshotById, matched,
usedFactSheetIds) to handle the manifest.external LEANIX_PROVIDER lookup and
immediate match, matchByLikec4Id(canonicalId, snapshotByLikec4Id, matched,
usedFactSheetIds) to handle the snapshotByLikec4Id lookup and match, and
matchByNameAndType(canonicalId, dryRunByCanonicalId, snapshotByNameAndType,
usedFactSheetIds, unmatchedInLikec4, ambiguous, matched) to compute
entityName/entityType, build nameTypeKey, filter unused candidates and push
either unmatchedInLikec4, a single matched entry, or ambiguous; then simplify
the main loop in reconcileInventoryWithManifest to call these three helpers in
order (short-circuiting when a match is made) and pass in the maps/sets
(snapshotById, snapshotByLikec4Id, snapshotByNameAndType, dryRunByCanonicalId,
usedFactSheetIds) and arrays (matched, unmatchedInLikec4, ambiguous) so the
top-level function becomes a thin orchestrator and the complex matching logic
lives in the new functions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.changeset/leanix-phase2-phase3-cli-docs.md:
- Around line 11-13: Update the release notes to use the canonical exported API
names instead of shorthand: replace occurrences of leanixInventorySnapshot,
reconcile, buildImpactReport, and generateAdrReport with the exact exported
function/command names as defined in this PR's public API (use the precise
export identifiers from the changed files), and ensure the Phase 2 / Phase 3
entries and the Refactors list match the concrete exported names used in the
codebase.

In `@packages/generators/src/drawio/parse-drawio.spec.ts`:
- Around line 118-122: The current test in parse-drawio.spec.ts relaxed the
assertion by allowing either actor output or a container fallback (variables
hasActor and hasContainer); revert to the strict contract by asserting only the
actor mapping: require hasActor to be true (expect(hasActor).toBe(true)) and
remove the OR acceptance. Then add a new, separate test that exercises the
fallback path (e.g., a dedicated fixture or manipulated input that lacks raw
style) and asserts hasContainer is true there so regressions in actor handling
remain visible; reference the existing hasActor and hasContainer checks to
implement both the strict actor test and the isolated fallback test.

In `@packages/generators/src/drawio/parse-drawio.ts`:
- Around line 730-735: vertexDepth currently recurses without cycle detection
and can overflow on cyclic parent links; replace the recursive implementation in
function vertexDepth(v: DrawioCell, idToVertex: Map<string, DrawioCell>) with a
cycle-safe, cached algorithm: use a memo Map<string, number> for computed depths
and a temporary Set<string> (or stack) to detect cycles while walking parents
(follow v.parent via idToVertex), return 0 when a cycle or missing parent is
encountered, cache each computed depth to avoid repeated work, and ensure any
other place using the same logic (the related depth computation referenced
nearby) uses the same memoized, cycle-guarded routine.

In `@packages/leanix-bridge/README.md`:
- Line 127: Update the README entry for buildDriftReport to match the actual
implementation: change the documented signature from buildDriftReport(manifest,
snapshot) to buildDriftReport(reconciliation: ReconciliationResult) and describe
that it accepts a single ReconciliationResult (from drift-report.ts) and returns
a DriftReport; ensure you reference the ReconciliationResult parameter name and
return type to keep docs and the buildDriftReport function signature consistent.
- Line 130: The README lists runGovernanceChecks(manifest, options?) but the
actual exported function signature is runGovernanceChecks(reconciliation:
ReconciliationResult, options: GovernanceCheckOptions = {}): GovernanceReport;
update the README entry for runGovernanceChecks to show the correct parameter
name and types (ReconciliationResult and optional GovernanceCheckOptions) and
briefly describe that it accepts a ReconciliationResult rather than a manifest
and returns a GovernanceReport with pass/fail and violations.

In `@packages/leanix-bridge/src/validate.ts`:
- Around line 16-31: The current type guard isBridgeManifest performs only
shallow checks and can return false positives; update it (and the similar guard
around lines 37-44) to validate nested shapes: ensure obj['entities'] and
obj['views'] are non-null objects whose values are Records (not arrays) with
each entry matching the expected entity/view schema (check required string
fields and types), and ensure obj['relations'] is an array whose items are
objects with the required relation properties (e.g., source, target, type as
strings or expected types). Locate and harden the predicates in isBridgeManifest
(and the other guard) to iterate and validate nested entries’ keys/types so the
runtime narrowing is safe.

In `@packages/likec4/src/cli/codegen/leanix-inventory-snapshot.ts`:
- Around line 35-41: The code forwards an empty-string likec4IdAttribute to
fetchLeanixInventorySnapshot because the check uses != null only; update the
construction of the options object passed to fetchLeanixInventorySnapshot
(around variables likec4IdAttribute, requireLeanixClient,
fetchLeanixInventorySnapshot) to ignore blank values by verifying
likec4IdAttribute is a non-empty string (e.g., check typeof likec4IdAttribute
=== 'string' && likec4IdAttribute.trim() !== '') before spreading {
likec4IdAttribute } into the options so empty or whitespace-only strings are not
sent to the API.

In `@packages/likec4/src/cli/sync/index.ts`:
- Around line 49-57: The CLI currently defines options 'dry-run' (default true)
and 'apply' and then forces dryRun=false when apply=true, which silently
overrides an explicit --dry-run; update the yargs setup to declare these options
as mutually exclusive using yargs.conflicts('apply', 'dry-run') (or the reverse)
so the parser rejects passing both flags, and then compute the effective mode
after parsing only when neither flag was explicitly provided (i.e., derive
dryRun from parsed flags with a sensible default), replacing the existing forced
assignment logic around apply/dryRun in the sync command setup.

---

Nitpick comments:
In @.gitignore:
- Around line 138-139: The .gitignore contains a redundant pattern: remove the
specific pattern "docs/PR-*-REVIEW.md" because the broader pattern
"docs/PR-*.md" already matches those files; delete the "docs/PR-*-REVIEW.md"
entry so only "docs/PR-*.md" remains.

In `@e2e/src/likec4-cli-bridge.spec.ts`:
- Around line 12-63: The two tests 'LikeC4 CLI - gen leanix-dry-run produces
manifest, leanix-dry-run, report' and 'LikeC4 CLI - sync leanix --dry-run
produces same artifacts and does not require token' duplicate cleanup, dir
creation, listing and JSON assertions; extract that logic into a small helper
(e.g., createCleanOutDir(outDirAbs) for rmSync/mkdirSync and
assertE2EArtifacts(outDirAbs) for checking entries and parsing
manifest/leanix-dry-run/report) and call those helpers from both test bodies
(replace the repeated rmSync/mkdirSync, readdirSync checks and JSON parse/assert
blocks). Ensure helper names are unique (createCleanOutDir, assertE2EArtifacts)
so you can locate and replace the repeated code in the tests.

In `@packages/leanix-bridge/src/governance-checks.ts`:
- Around line 43-49: The return object in the function that produces the
governance check (the block returning id, name, passed, ...(passed ? {} :
failedMessage !== undefined ? { message: failedMessage } : {})) is using a
nested ternary that hurts readability; replace it with a simple conditional
before the return (e.g., compute a messageProps variable if !passed and
failedMessage exists, or set it to an empty object) and then spread messageProps
into the returned object, referencing the same identifiers id, name, passed, and
failedMessage to locate the code.

In `@packages/leanix-bridge/src/reconcile.ts`:
- Around line 73-160: The function reconcileInventoryWithManifest is too
complex; extract the three matching strategies into small helpers to reduce
cognitive complexity: create matchByManifestFactSheetId(manifestFactSheetId,
snapshotById, matched, usedFactSheetIds) to handle the manifest.external
LEANIX_PROVIDER lookup and immediate match, matchByLikec4Id(canonicalId,
snapshotByLikec4Id, matched, usedFactSheetIds) to handle the snapshotByLikec4Id
lookup and match, and matchByNameAndType(canonicalId, dryRunByCanonicalId,
snapshotByNameAndType, usedFactSheetIds, unmatchedInLikec4, ambiguous, matched)
to compute entityName/entityType, build nameTypeKey, filter unused candidates
and push either unmatchedInLikec4, a single matched entry, or ambiguous; then
simplify the main loop in reconcileInventoryWithManifest to call these three
helpers in order (short-circuiting when a match is made) and pass in the
maps/sets (snapshotById, snapshotByLikec4Id, snapshotByNameAndType,
dryRunByCanonicalId, usedFactSheetIds) and arrays (matched, unmatchedInLikec4,
ambiguous) so the top-level function becomes a thin orchestrator and the complex
matching logic lives in the new functions.

In `@packages/likec4/src/cli/codegen/leanix-reconcile.ts`:
- Around line 79-85: In the catch block that currently logs "Could not load
workspace for dryRun enrichment; proceeding without it..." using logger.debug
and then sets dryRun = undefined, change the log call to logger.warn so the
failure is visible to normal users; keep the existing error formatting (err
instanceof Error ? err.message : String(err)) and preserve setting dryRun =
undefined and surrounding behavior in the same catch block to maintain
functionality.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 87f6ee29-117c-4488-91b3-622dec4ccd2c

📥 Commits

Reviewing files that changed from the base of the PR and between 585d4bb and 335c19b.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (42)
  • .changeset/leanix-phase2-phase3-cli-docs.md
  • .github/workflows/checks.yaml
  • .github/workflows/e2e-update-screenshots.yaml
  • .gitignore
  • apps/docs/src/content/docs/tooling/cli.mdx
  • apps/docs/src/content/docs/tooling/drawio.mdx
  • e2e/package.json
  • e2e/src/likec4-cli-bridge.spec.ts
  • package.json
  • packages/generators/src/drawio/generate-drawio.spec.ts
  • packages/generators/src/drawio/parse-drawio.spec.ts
  • packages/generators/src/drawio/parse-drawio.ts
  • packages/generators/src/index.ts
  • packages/leanix-bridge/README.md
  • packages/leanix-bridge/src/adr-generation.spec.ts
  • packages/leanix-bridge/src/adr-generation.ts
  • packages/leanix-bridge/src/drift-report.spec.ts
  • packages/leanix-bridge/src/drift-report.ts
  • packages/leanix-bridge/src/governance-checks.spec.ts
  • packages/leanix-bridge/src/governance-checks.ts
  • packages/leanix-bridge/src/impact-report.spec.ts
  • packages/leanix-bridge/src/impact-report.ts
  • packages/leanix-bridge/src/index.ts
  • packages/leanix-bridge/src/leanix-inventory-snapshot.spec.ts
  • packages/leanix-bridge/src/leanix-inventory-snapshot.ts
  • packages/leanix-bridge/src/mapping.ts
  • packages/leanix-bridge/src/reconcile.spec.ts
  • packages/leanix-bridge/src/reconcile.ts
  • packages/leanix-bridge/src/report.ts
  • packages/leanix-bridge/src/validate.ts
  • packages/likec4/package.json
  • packages/likec4/src/cli/bridge/leanix-client.ts
  • packages/likec4/src/cli/bridge/shared.ts
  • packages/likec4/src/cli/codegen/index.ts
  • packages/likec4/src/cli/codegen/leanix-dry-run.spec.ts
  • packages/likec4/src/cli/codegen/leanix-dry-run.ts
  • packages/likec4/src/cli/codegen/leanix-inventory-snapshot.ts
  • packages/likec4/src/cli/codegen/leanix-reconcile.ts
  • packages/likec4/src/cli/index.ts
  • packages/likec4/src/cli/sync/index.ts
  • packages/likec4/src/cli/sync/leanix.ts
  • packages/likec4/tsconfig.cli.json

…hangeset

- .gitignore: remove redundant docs/PR-*-REVIEW.md
- leanix-reconcile: log dryRun enrichment failure as warn
- governance-checks: simplify buildCheck (no nested ternary)
- leanix-bridge README: fix buildDriftReport/runGovernanceChecks signatures
- leanix-inventory-snapshot: do not pass blank likec4IdAttribute to API
- e2e: extract createCleanOutDir, assertE2EArtifacts; add JSDoc
- reconcile: extract tryMatchByManifestFactSheetId, tryMatchByLikec4Id, resolveByNameAndType; add JSDoc
- changeset: use canonical API names in release notes

Made-with: Cursor
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/leanix-bridge/README.md (1)

116-131: ⚠️ Potential issue | 🟡 Minor

Update parameter names in API documentation to match implementations.

The API section has two parameter naming inconsistencies:

  • Line 128: impactReportFromSyncPlan(syncPlan) should be impactReportFromSyncPlan(plan) — the implementation uses plan: SyncPlan as the parameter name.
  • Line 129: generateAdrFromDriftReport(driftReport, options?) should be generateAdrFromDriftReport(drift, options?) — the implementation uses drift: DriftReport as the parameter name.

All other documented signatures (reconcileInventoryWithManifest, buildDriftReport, generateAdrFromReconciliation, runGovernanceChecks) match their implementations.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/leanix-bridge/README.md` around lines 116 - 131, The README API docs
use parameter names that don't match the implementations; update the signatures
for impactReportFromSyncPlan and generateAdrFromDriftReport so they use the
actual parameter names used in code: change impactReportFromSyncPlan(syncPlan)
to impactReportFromSyncPlan(plan) and change
generateAdrFromDriftReport(driftReport, options?) to
generateAdrFromDriftReport(drift, options?), ensuring the rest of the signatures
remain unchanged and references to these functions (impactReportFromSyncPlan,
generateAdrFromDriftReport) are consistent.
🧹 Nitpick comments (2)
packages/leanix-bridge/src/reconcile.ts (1)

109-119: Redundant null check after length validation.

After confirming candidates.length === 1, the element at index 0 is guaranteed to exist. The if (candidate) check on line 111 is defensive but unnecessary.

♻️ Simplified version
   } else if (candidates.length === 1) {
-    const candidate = candidates[0]
-    if (candidate) {
-      matched.push({
-        canonicalId,
-        factSheetId: candidate.id,
-        name: candidate.name,
-        type: candidate.type,
-      })
-      usedFactSheetIds.add(candidate.id)
-    }
+    const candidate = candidates[0]!
+    matched.push({
+      canonicalId,
+      factSheetId: candidate.id,
+      name: candidate.name,
+      type: candidate.type,
+    })
+    usedFactSheetIds.add(candidate.id)
   } else {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/leanix-bridge/src/reconcile.ts` around lines 109 - 119, The check
`if (candidate)` is redundant because you already confirmed `candidates.length
=== 1`; remove the unnecessary `if (candidate)` guard and directly use `const
candidate = candidates[0]` to push into `matched` and add to `usedFactSheetIds`
(update the block around the `candidates.length === 1` branch that defines
`candidate`, the `matched.push({...})` call, and the
`usedFactSheetIds.add(candidate.id)` call).
packages/leanix-bridge/src/governance-checks.ts (1)

95-100: Consider using a fresh timestamp for the governance report.

The generatedAt field is copied from reconciliation.generatedAt, which reflects when the reconciliation was performed, not when the governance checks ran. If these can happen at different times (e.g., reconciliation is cached and governance checks run later), consider using a fresh timestamp for traceability.

♻️ Suggested change
+export function runGovernanceChecks(
+  reconciliation: ReconciliationResult,
+  options: GovernanceCheckOptions = {},
+): GovernanceReport {
+  const opts = { ...DEFAULT_OPTIONS, ...options }
+  const checks: GovernanceCheckResult[] = []
+  const { summary } = reconciliation
   // ... checks logic ...
   const passed = checks.every(c => c.passed)
   return {
     passed,
-    generatedAt: reconciliation.generatedAt,
+    generatedAt: new Date().toISOString(),
     checks,
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/leanix-bridge/src/governance-checks.ts` around lines 95 - 100, The
governance report currently returns generatedAt: reconciliation.generatedAt
which uses the reconciliation timestamp; update the return in the function that
builds the report (the object containing passed, generatedAt, checks) to set
generatedAt to a fresh timestamp (e.g., Date.now() or new Date().toISOString())
so the governance-checks.ts report reflects when checks were run rather than
when reconciliation occurred; keep the rest of the structure (passed, checks)
unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/likec4/src/cli/codegen/leanix-reconcile.ts`:
- Around line 79-86: The catch currently swallows all errors when loading the
workspace for dryRun enrichment (logger.warn ... dryRun = undefined); change it
to distinguish optional vs user-supplied failures: detect whether the
workspace/project was explicitly provided by the user (the flag/variable that
triggers loading the workspace) and if so surface the error (rethrow or
logger.error + exit) instead of silently degrading, otherwise keep the warning
and set dryRun = undefined; update the catch in leanix-reconcile.ts around the
dryRun/workspace load to inspect the input flags/variables and branch
accordingly.

---

Outside diff comments:
In `@packages/leanix-bridge/README.md`:
- Around line 116-131: The README API docs use parameter names that don't match
the implementations; update the signatures for impactReportFromSyncPlan and
generateAdrFromDriftReport so they use the actual parameter names used in code:
change impactReportFromSyncPlan(syncPlan) to impactReportFromSyncPlan(plan) and
change generateAdrFromDriftReport(driftReport, options?) to
generateAdrFromDriftReport(drift, options?), ensuring the rest of the signatures
remain unchanged and references to these functions (impactReportFromSyncPlan,
generateAdrFromDriftReport) are consistent.

---

Nitpick comments:
In `@packages/leanix-bridge/src/governance-checks.ts`:
- Around line 95-100: The governance report currently returns generatedAt:
reconciliation.generatedAt which uses the reconciliation timestamp; update the
return in the function that builds the report (the object containing passed,
generatedAt, checks) to set generatedAt to a fresh timestamp (e.g., Date.now()
or new Date().toISOString()) so the governance-checks.ts report reflects when
checks were run rather than when reconciliation occurred; keep the rest of the
structure (passed, checks) unchanged.

In `@packages/leanix-bridge/src/reconcile.ts`:
- Around line 109-119: The check `if (candidate)` is redundant because you
already confirmed `candidates.length === 1`; remove the unnecessary `if
(candidate)` guard and directly use `const candidate = candidates[0]` to push
into `matched` and add to `usedFactSheetIds` (update the block around the
`candidates.length === 1` branch that defines `candidate`, the
`matched.push({...})` call, and the `usedFactSheetIds.add(candidate.id)` call).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9453bea6-d81b-4991-8307-5a4b9c4c8923

📥 Commits

Reviewing files that changed from the base of the PR and between 335c19b and 3dc62ad.

📒 Files selected for processing (8)
  • .changeset/leanix-phase2-phase3-cli-docs.md
  • .gitignore
  • e2e/src/likec4-cli-bridge.spec.ts
  • packages/leanix-bridge/README.md
  • packages/leanix-bridge/src/governance-checks.ts
  • packages/leanix-bridge/src/reconcile.ts
  • packages/likec4/src/cli/codegen/leanix-inventory-snapshot.ts
  • packages/likec4/src/cli/codegen/leanix-reconcile.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • .gitignore
  • e2e/src/likec4-cli-bridge.spec.ts
  • .changeset/leanix-phase2-phase3-cli-docs.md

sraphaz added 2 commits March 16, 2026 23:12
- leanix-bridge: contracts, validate (type guards), report (buildBridgeReport),
  leanix-graphql-operations, sync (resolveExistingFactSheetId), adr-generation,
  inventory (mapNodeToFactSheetItem), CLI runSyncPlan/runSyncApply
- config: landingPage single source (LandingPageSchema), validate once and merge;
  smoke test for landingPage; skip block for Vitest quirk
- core guards: hasProp curried + two-arg; _hasProp Record<string,unknown>;
  spec describe aligned to two-argument form

Made-with: Cursor
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

♻️ Duplicate comments (1)
packages/likec4/src/cli/codegen/leanix-reconcile.ts (1)

97-107: ⚠️ Potential issue | 🟠 Major

Don't silently degrade when explicit workspace/project loading fails.

This catch block turns every workspace/project load error into a warning. If the user asked for a specific project and typoed it, the command still exits successfully with a lower-fidelity reconciliation report, which hides the real configuration problem.

🛠️ Proposed fix
     let dryRun: LeanixInventoryDryRun | undefined
     try {
       dryRun = await loadDryRunFromWorkspace(workspacePath, project, useDotBin)
     } catch (err) {
-      logger.warn(
-        `Could not load workspace for dryRun enrichment; proceeding without it: ${
-          err instanceof Error ? err.message : String(err)
-        }`,
-      )
-      dryRun = undefined
+      const msg = err instanceof Error ? err.message : String(err)
+      if (project) {
+        logger.error(`Failed to load workspace for dryRun enrichment: ${msg}`)
+        throw err
+      }
+      logger.warn(`Could not load workspace for dryRun enrichment; proceeding without it: ${msg}`)
+      dryRun = undefined
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/likec4/src/cli/codegen/leanix-reconcile.ts` around lines 97 - 107,
The catch currently swallows all errors from loadDryRunFromWorkspace which hides
real config issues; update the catch in the block around
loadDryRunFromWorkspace(workspacePath, project, useDotBin) to only degrade to a
warning when the project argument was not explicitly provided, but if project
(or any explicit workspace selector) was passed, rethrow the error (or return a
non-zero failure) instead of assigning dryRun = undefined; use the existing
logger.warn for the non-fatal case and preserve the original error when
rethrowing so callers can fail fast and surface typos or bad configuration.
🧹 Nitpick comments (4)
packages/config/src/schema.ts (1)

300-329: Consider omitting landingPage from the main parse to avoid double validation.

The current approach validates landingPage twice: once explicitly via LandingPageSchema.safeParse (line 304) and again as part of LikeC4ProjectJsonConfigSchema.safeParse (line 310). While the workaround for Zod v4 stripping optional union keys is documented, you could use .omit({ landingPage: true }) on the main schema parse to avoid redundant validation.

Also, the cast on line 314 (as unknown as LikeC4ProjectConfig) bypasses type safety. This is necessary because generators isn't part of LikeC4ProjectJsonConfigSchema, but it's worth noting the coupling.

♻️ Optional: Avoid double validation
-  const parsed = LikeC4ProjectJsonConfigSchema.safeParse(config)
+  const parsed = LikeC4ProjectJsonConfigSchema.omit({ landingPage: true }).safeParse(config)

This ensures landingPage is validated only once via the explicit LandingPageSchema.safeParse call.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/config/src/schema.ts` around lines 300 - 329, validateProjectConfig
currently validates landingPage twice—once with LandingPageSchema.safeParse and
again when parsing LikeC4ProjectJsonConfigSchema—so change the main parse to
omit landingPage before safeParse (use LikeC4ProjectJsonConfigSchema.omit({
landingPage: true }).safeParse(config)) and then merge the previously
validatedLandingPage into the resulting data; keep the existing GeneratorsSchema
branch but remove the unsafe broad cast (`as unknown as LikeC4ProjectConfig`) by
tightening the inferred type from the omitted-schema parse (use the z.infer
result of the omitted schema) so types line up when you spread in landingPage
and generators.
packages/core/src/types/guards.ts (1)

15-15: Replace the as cast with a record type guard.

Line 15 still depends on a cast, which weakens narrowing and conflicts with the repo’s TS guidance.

♻️ Proposed refactor
+function isRecord(value: unknown): value is Record<string, unknown> {
+  return value != null && typeof value === 'object'
+}
+
 function _hasProp(value: unknown, path: string): boolean {
   invariant(typeof path === 'string', 'Path must be string')
-  return value != null && typeof value === 'object' && (value as Record<string, unknown>)[path] != null
+  return isRecord(value) && value[path] != null
 }

As per coding guidelines: “TypeScript-first repo; use explicit types. Avoid using any, casts with as.”

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/types/guards.ts` at line 15, The expression in guards.ts
uses a cast "(value as Record<string, unknown>)[path]" which violates the
TS-first rule; instead add and use a proper type guard (e.g., export function
isRecord(v: unknown): v is Record<string, unknown>) and replace the cast with a
runtime check like "isRecord(value) && value[path] != null" inside the existing
predicate (the function that currently returns value != null && typeof value ===
'object' && (value as Record<string, unknown>)[path] != null). Ensure the new
isRecord guard is exported from the same module and used in place of the as-cast
to allow correct narrowing.
packages/leanix-bridge/src/sync-to-leanix.spec.ts (1)

35-83: Expose the updateFactSheet branch in this mock.

The new likec4IdAttribute path can fall back to name/type and then backfill the attribute, but this mock currently discards updateFactSheet, so the suite can't assert that branch. Recording those calls and adding one focused test would protect the new idempotency path from regressing.

Based on learnings, "Aim to cover new features with relevant tests; keep test names descriptive."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/leanix-bridge/src/sync-to-leanix.spec.ts` around lines 35 - 83, The
mock in createSyncMockClient discards updateFactSheet calls so tests can't
assert backfill behavior; modify the graphql handler inside createSyncMockClient
to capture updateFactSheet variables (id and patches) into a local map or array
(e.g., updatedPatchesById) and return the existing updateFactSheet response
(updateFactSheet: { factSheet: { id: variables?.['id'] } }); then expose an
accessor on the returned object (e.g., getUpdatedPatches(id) or getAllUpdates())
so tests can assert that updateFactSheet was called with the expected id and
likec4IdAttribute patches; keep the existing createFactSheet/createRelation
flows intact and update types for the new accessor.
packages/leanix-bridge/src/adr-generation.ts (1)

77-80: Reuse the exported ADR options shape.

This public API redefines the same title/status pair that AdrGenerationOptions already owns, so the two ADR generators can drift apart. Reuse Pick<AdrGenerationOptions, 'title' | 'status'> (or an exported sibling type) here.

♻️ Proposed refactor
+type DriftAdrGenerationOptions = Pick<AdrGenerationOptions, 'title' | 'status'>
+
 export function generateAdrFromDriftReport(
   drift: DriftReport,
-  options: { title?: string; status?: string } = {},
+  options: DriftAdrGenerationOptions = {},
 ): string {
As per coding guidelines, 'Agent types and interfaces should be defined separately and reused across the codebase.'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/leanix-bridge/src/adr-generation.ts` around lines 77 - 80, The
function generateAdrFromDriftReport currently redefines the { title?: string;
status?: string } shape in its options parameter; update its signature to reuse
the exported AdrGenerationOptions type (e.g., use Pick<AdrGenerationOptions,
'title' | 'status'> or an exported sibling type) so both ADR generators share
the same option shape and cannot drift apart—locate generateAdrFromDriftReport
and replace the inline options type with the reused AdrGenerationOptions-derived
type.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@e2e/src/likec4-cli-bridge.spec.ts`:
- Around line 59-63: The dry-run test "LikeC4 CLI - sync leanix --dry-run
produces same artifacts and does not require token" currently spawns the CLI via
the tagged `$` call and inherits the parent environment, so any exported
LEANIX_API_TOKEN will leak into the subprocess; update the invocation that calls
`$({ cwd: projectRoot })\`likec4 sync leanix ...\`` to explicitly clear
LEANIX_API_TOKEN in the env (e.g., pass an env option to the `$` call that
spreads the current env but sets LEANIX_API_TOKEN to an empty string or
undefined) so the subprocess cannot use a token during this dry-run; locate the
call near the test and modify that `$` invocation (and related setup such as
createCleanOutDir/outDirAbs) to include the env override.

In `@packages/config/src/schema.spec.ts`:
- Around line 270-275: Update the smoke test "does not throw when config has
valid landingPage (smoke)" to also assert that the validated result preserves
the landingPage field: call validateConfig(config) and add an expectation that
result.landingPage is equal to the original landingPage object (or at least
defined and matches { redirect: true }), referencing the existing test and the
validateConfig function so the assertion covers the Zod v4 behavior that
optional union keys are not stripped.

In `@packages/core/src/types/guards.spec.ts`:
- Around line 27-45: The test block duplicates the two-arg hasProp tests and
uses unsafe "as any" casts; replace it to exercise the curried overload
implemented in guards.ts (the hasProp(path)(value) form) by calling
hasProp('some') and applying the returned predicate to properly typed objects
(e.g., { some?: string | undefined | null } or declared variables) instead of
using "as any"; update the four cases (present non-nullish, undefined value,
null value, missing property) to assert the predicate's boolean results for the
curried form so the curried overload is covered and no any-casts remain.

In `@packages/leanix-bridge/src/leanix-graphql-operations.ts`:
- Around line 29-32: The current logic in leanix-graphql-operations.ts blindly
picks the first match from data.allFactSheets.edges (the variables edges and
first produced from client.graphql<AllFactSheetsResult>) which can incorrectly
proceed when multiple fact sheets match; change both occurrences (the blocks
that set edges/first and return first?.id) to explicitly check for edges.length
> 1 and reject early (throw a clear error or return a distinct failure)
indicating a non-unique lookup, only returning the id when edges.length === 1
(and handling 0 as null/no-match). Ensure the error message identifies the query
and the provided identity (e.g., name/type) so duplicate hits are surfaced
instead of silently using the first edge.

In `@packages/leanix-bridge/src/leanix-inventory-snapshot.ts`:
- Around line 208-223: The current GraphQL query in const query only fetches
factSheet.relations without pagination, which truncates results; update the
query to request relations(first: $first, after: $after) and include pageInfo {
hasNextPage endCursor } and nodes (or edges.node) so you can iterate; then
implement cursor-based pagination in the code that calls this query (where fact
sheets are processed) to repeatedly fetch pages using variables { id, first,
after } and loop using pageInfo.hasNextPage / pageInfo.endCursor until all
relations are accumulated for each fact sheet (ensure the same change is applied
to the related block noted at lines 225-247).

In `@packages/leanix-bridge/src/reconcile.ts`:
- Around line 68-74: The matching logic adds fs.id to usedFactSheetIds but
doesn't prevent a duplicate match; change the code that resolves
manifestFactSheetId (using entity.external[LEANIX_PROVIDER],
snapshotById.get(manifestFactSheetId)) to first check if fs.id is already in
usedFactSheetIds and return null if it is, only calling
usedFactSheetIds.add(fs.id) and returning the match when the id was not
previously claimed; update the matching block around manifestFactSheetId,
snapshotById, and usedFactSheetIds accordingly and add a regression test in
reconcile.spec.ts that creates two entities pointing at the same LeanIX id to
assert only one match occurs.

In `@packages/leanix-bridge/src/sync-to-leanix.ts`:
- Around line 152-159: The helper currently aborts and drops the fact sheet if
patchFactSheetAttribute throws; change it so reuse of the resolved byNameType is
best-effort: after finding byNameType (via findFactSheetByNameAndType) attempt
patchFactSheetAttribute(fs.likec4Id) inside a try/catch, and on error log a
warning (including error details and context like byNameType and fs.likec4Id)
but still return byNameType; keep the early return for byAttr as-is (from
findFactSheetByLikec4IdAttribute).

---

Duplicate comments:
In `@packages/likec4/src/cli/codegen/leanix-reconcile.ts`:
- Around line 97-107: The catch currently swallows all errors from
loadDryRunFromWorkspace which hides real config issues; update the catch in the
block around loadDryRunFromWorkspace(workspacePath, project, useDotBin) to only
degrade to a warning when the project argument was not explicitly provided, but
if project (or any explicit workspace selector) was passed, rethrow the error
(or return a non-zero failure) instead of assigning dryRun = undefined; use the
existing logger.warn for the non-fatal case and preserve the original error when
rethrowing so callers can fail fast and surface typos or bad configuration.

---

Nitpick comments:
In `@packages/config/src/schema.ts`:
- Around line 300-329: validateProjectConfig currently validates landingPage
twice—once with LandingPageSchema.safeParse and again when parsing
LikeC4ProjectJsonConfigSchema—so change the main parse to omit landingPage
before safeParse (use LikeC4ProjectJsonConfigSchema.omit({ landingPage: true
}).safeParse(config)) and then merge the previously validatedLandingPage into
the resulting data; keep the existing GeneratorsSchema branch but remove the
unsafe broad cast (`as unknown as LikeC4ProjectConfig`) by tightening the
inferred type from the omitted-schema parse (use the z.infer result of the
omitted schema) so types line up when you spread in landingPage and generators.

In `@packages/core/src/types/guards.ts`:
- Line 15: The expression in guards.ts uses a cast "(value as Record<string,
unknown>)[path]" which violates the TS-first rule; instead add and use a proper
type guard (e.g., export function isRecord(v: unknown): v is Record<string,
unknown>) and replace the cast with a runtime check like "isRecord(value) &&
value[path] != null" inside the existing predicate (the function that currently
returns value != null && typeof value === 'object' && (value as Record<string,
unknown>)[path] != null). Ensure the new isRecord guard is exported from the
same module and used in place of the as-cast to allow correct narrowing.

In `@packages/leanix-bridge/src/adr-generation.ts`:
- Around line 77-80: The function generateAdrFromDriftReport currently redefines
the { title?: string; status?: string } shape in its options parameter; update
its signature to reuse the exported AdrGenerationOptions type (e.g., use
Pick<AdrGenerationOptions, 'title' | 'status'> or an exported sibling type) so
both ADR generators share the same option shape and cannot drift apart—locate
generateAdrFromDriftReport and replace the inline options type with the reused
AdrGenerationOptions-derived type.

In `@packages/leanix-bridge/src/sync-to-leanix.spec.ts`:
- Around line 35-83: The mock in createSyncMockClient discards updateFactSheet
calls so tests can't assert backfill behavior; modify the graphql handler inside
createSyncMockClient to capture updateFactSheet variables (id and patches) into
a local map or array (e.g., updatedPatchesById) and return the existing
updateFactSheet response (updateFactSheet: { factSheet: { id: variables?.['id']
} }); then expose an accessor on the returned object (e.g.,
getUpdatedPatches(id) or getAllUpdates()) so tests can assert that
updateFactSheet was called with the expected id and likec4IdAttribute patches;
keep the existing createFactSheet/createRelation flows intact and update types
for the new accessor.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: afd8e991-1701-4986-8301-23a7e4e6a4a0

📥 Commits

Reviewing files that changed from the base of the PR and between 3dc62ad and 6ff16b1.

📒 Files selected for processing (25)
  • e2e/src/likec4-cli-bridge.spec.ts
  • packages/config/src/schema.spec.ts
  • packages/config/src/schema.ts
  • packages/core/src/types/guards.spec.ts
  • packages/core/src/types/guards.ts
  • packages/leanix-bridge/README.md
  • packages/leanix-bridge/src/adr-generation.ts
  • packages/leanix-bridge/src/bridge-artifacts.spec.ts
  • packages/leanix-bridge/src/contracts.ts
  • packages/leanix-bridge/src/drawio-leanix-roundtrip.ts
  • packages/leanix-bridge/src/index.ts
  • packages/leanix-bridge/src/leanix-graphql-operations.ts
  • packages/leanix-bridge/src/leanix-inventory-snapshot.spec.ts
  • packages/leanix-bridge/src/leanix-inventory-snapshot.ts
  • packages/leanix-bridge/src/reconcile.spec.ts
  • packages/leanix-bridge/src/reconcile.ts
  • packages/leanix-bridge/src/report.ts
  • packages/leanix-bridge/src/sync-to-leanix.spec.ts
  • packages/leanix-bridge/src/sync-to-leanix.ts
  • packages/leanix-bridge/src/validate.spec.ts
  • packages/leanix-bridge/src/validate.ts
  • packages/likec4/src/cli/bridge/shared.ts
  • packages/likec4/src/cli/codegen/leanix-dry-run.spec.ts
  • packages/likec4/src/cli/codegen/leanix-reconcile.ts
  • packages/likec4/src/cli/sync/leanix.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/leanix-bridge/src/drawio-leanix-roundtrip.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/likec4/src/cli/codegen/leanix-dry-run.spec.ts
  • packages/leanix-bridge/src/leanix-inventory-snapshot.spec.ts
  • packages/leanix-bridge/src/validate.ts
  • packages/likec4/src/cli/bridge/shared.ts

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/config/src/schema.ts (1)

302-326: Remove unsafe casts and type the parsed values directly.

At Line 314 and Line 326, as casts bypass type safety. Prefer typed schema outputs (or exported aliases) so data and generators are inferred without coercion.

Refactor sketch
+export type LandingPageConfig = z.infer<typeof LandingPageSchema>
+
-  let validatedLandingPage: z.infer<typeof LandingPageSchema> | null = null
+  let validatedLandingPage: LandingPageConfig | null = null

-  let data = parsed.data as unknown as LikeC4ProjectConfig
+  let data: LikeC4ProjectConfig = parsed.data
// outside this hunk, to avoid cast at Line 326:
const GeneratorFnSchema = z.custom<GeneratorFn>((v): v is GeneratorFn => typeof v === 'function')
export const GeneratorsSchema = z.record(z.string(), GeneratorFnSchema)

As per coding guidelines: TypeScript-first repo; use explicit types and avoid casts with as.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/config/src/schema.ts` around lines 302 - 326, The code uses unsafe
"as" casts for parsed results (data and generators); update the Zod schemas and
parsing usage so TypeScript infers correct types instead of coercing: export a
GeneratorFnSchema (e.g., z.custom<GeneratorFn>(...)) and make GeneratorsSchema a
z.record(z.string(), GeneratorFnSchema), ensure LikeC4ProjectJsonConfigSchema is
typed to return LikeC4ProjectConfig via z.infer, then replace the casts around
parsed.data and genParsed.data by using those inferred types (assign parsed.data
directly to a typed variable and return generators as genParsed.data) so no "as"
coerce remains in the logic that builds `data` and the returned object.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/config/src/schema.ts`:
- Around line 318-329: The current logic silently ignores invalid generators
because GeneratorsSchema.safeParse failures just fall through and return data
without generators; update the branch handling around generatorsInput /
GeneratorsSchema.safeParse so that when genParsed.success is false you throw or
surface a clear error (include genParsed.error) instead of returning the
original data—mirror the behavior in defineConfig (which raises on invalid
input) so callers get a validation failure; locate the code using
generatorsInput, GeneratorsSchema and GeneratorsSchema.safeParse and replace the
silent-fallthrough with an explicit throw or error propagation including the
parse error details.

---

Nitpick comments:
In `@packages/config/src/schema.ts`:
- Around line 302-326: The code uses unsafe "as" casts for parsed results (data
and generators); update the Zod schemas and parsing usage so TypeScript infers
correct types instead of coercing: export a GeneratorFnSchema (e.g.,
z.custom<GeneratorFn>(...)) and make GeneratorsSchema a z.record(z.string(),
GeneratorFnSchema), ensure LikeC4ProjectJsonConfigSchema is typed to return
LikeC4ProjectConfig via z.infer, then replace the casts around parsed.data and
genParsed.data by using those inferred types (assign parsed.data directly to a
typed variable and return generators as genParsed.data) so no "as" coerce
remains in the logic that builds `data` and the returned object.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5d57c0cf-e809-497e-b5e3-35787ea340d3

📥 Commits

Reviewing files that changed from the base of the PR and between 6ff16b1 and aaac888.

📒 Files selected for processing (1)
  • packages/config/src/schema.ts

…d schema; likec4 test exclude lib

Made-with: Cursor
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
packages/config/build.config.ts (1)

10-12: Avoid as cast; prefer a type-safe approach.

The cast as string[] assumes config.external is always a string array, but rolldown supports multiple types for this field. Consider a safer pattern:

🔧 Suggested improvement
     rolldownConfig: (config) => {
-      config.external = [...(config.external as string[]), '@likec4/config']
+      const existing = Array.isArray(config.external) ? config.external : []
+      config.external = [...existing, '@likec4/config']
     },

As per coding guidelines: "Avoid using any, casts with as".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/config/build.config.ts` around lines 10 - 12, The current
rolldownConfig uses an unsafe cast on config.external; update rolldownConfig to
normalize config.external in a type-safe way instead of using "as string[]":
inspect config.external in rolldownConfig (the config.external field may be
undefined, a string, or an array), then produce a new external value that
preserves non-string/array types but safely appends '@likec4/config' when
external is a string or array; use Array.isArray and typeof checks to branch and
assign back to config.external (refer to rolldownConfig and config.external).
devops/vitest.ts (1)

24-26: Consider using distDir helper for consistency.

Line 25 manually constructs the path while line 26 uses the new distEntry helper. For consistency, consider refactoring line 25 to use distDir:

♻️ Suggested refactor
       // More specific first so subpath resolves
-      '@likec4/config/node': resolve(packages('config', 'dist', 'node')),
+      '@likec4/config/node': resolve(distDir('config'), 'node'),
       '@likec4/config': distEntry('config'),

This alias resolves to a directory with an entry defined in package.json exports (./dist/node/index.mjs), which the resolver will find automatically. Line 26 explicitly specifies the entry file, making this intentional asymmetry that works as designed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@devops/vitest.ts` around lines 24 - 26, Refactor the alias for
'@likec4/config/node' to use the distDir helper instead of manually resolving
packages('config', 'dist', 'node'); specifically replace the
resolve(packages('config', 'dist', 'node')) usage with distDir('config', 'node')
so it consistently mirrors the distEntry('config') pattern and lets the resolver
pick the package export (keep the '@likec4/config' entry using
distEntry('config') unchanged).
packages/generators/src/likec4/operators/base.ts (1)

819-824: Replace cast-based narrowing with a proper type guard.

Lines 824 and 832 use as casts which violates the repo guideline to avoid type assertions. Line 824 can be replaced with a type guard function instead.

♻️ Proposed refactor
+function isZodSchemaCondition(value: unknown): value is z.ZodSchema {
+  return value !== null && typeof value === 'object' && 'safeParse' in value
+}
+
 export function guard(
   condition: Function | z.ZodSchema,
   ...ops: Ops<any>
 ) {
   return operation('guard', ({ ctx, out }) => {
-    if (
-      condition !== null &&
-      typeof condition === 'object' &&
-      'safeParse' in condition
-    ) {
-      const parsed = (condition as z.ZodSchema).safeParse(ctx)
+    if (isZodSchemaCondition(condition)) {
+      const parsed = condition.safeParse(ctx)
       if (parsed.success) {
         executeOnCtx({ ctx: parsed.data, out }, ops)
       } else {
         throw new Error(`Guard failed: ${z.prettifyError(parsed.error)}`)
       }
       return
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/generators/src/likec4/operators/base.ts` around lines 819 - 824,
Replace the cast-based narrowing around the "condition" checks with a proper
type guard: implement a predicate like isZodSchema(value): value is z.ZodSchema
and use it where currently checking 'safeParse' in condition and when calling
safeParse, replacing both occurrences of (condition as z.ZodSchema) so the
compiler understands the narrowed type without using "as" casts; update the
logic in the block that uses safeParse and any subsequent branches that relied
on the cast (e.g., the parsed handling) to use the new isZodSchema guard before
invoking condition.safeParse.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/generators/src/likec4/operators/base.ts`:
- Around line 832-833: The guard currently silently returns when `condition` is
`false` or `null`, hiding invalid inputs; replace that silent return with an
explicit thrown error so callers fail fast: in the block that checks `if
((condition as unknown) === false || condition == null)` throw a descriptive
Error (e.g. `throw new Error(\`Invalid guard condition: expected function or Zod
schema, got ${typeof condition}\`)`) instead of returning, or alternatively
remove that check entirely and rely on the subsequent `invariant(typeof
condition === 'function')` in the same function to surface invalid inputs—update
the code around the `condition` check and the `invariant` call accordingly.

---

Nitpick comments:
In `@devops/vitest.ts`:
- Around line 24-26: Refactor the alias for '@likec4/config/node' to use the
distDir helper instead of manually resolving packages('config', 'dist', 'node');
specifically replace the resolve(packages('config', 'dist', 'node')) usage with
distDir('config', 'node') so it consistently mirrors the distEntry('config')
pattern and lets the resolver pick the package export (keep the '@likec4/config'
entry using distEntry('config') unchanged).

In `@packages/config/build.config.ts`:
- Around line 10-12: The current rolldownConfig uses an unsafe cast on
config.external; update rolldownConfig to normalize config.external in a
type-safe way instead of using "as string[]": inspect config.external in
rolldownConfig (the config.external field may be undefined, a string, or an
array), then produce a new external value that preserves non-string/array types
but safely appends '@likec4/config' when external is a string or array; use
Array.isArray and typeof checks to branch and assign back to config.external
(refer to rolldownConfig and config.external).

In `@packages/generators/src/likec4/operators/base.ts`:
- Around line 819-824: Replace the cast-based narrowing around the "condition"
checks with a proper type guard: implement a predicate like isZodSchema(value):
value is z.ZodSchema and use it where currently checking 'safeParse' in
condition and when calling safeParse, replacing both occurrences of (condition
as z.ZodSchema) so the compiler understands the narrowed type without using "as"
casts; update the logic in the block that uses safeParse and any subsequent
branches that relied on the cast (e.g., the parsed handling) to use the new
isZodSchema guard before invoking condition.safeParse.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8a7b7f3b-d876-405f-8bc8-d20668fb7748

📥 Commits

Reviewing files that changed from the base of the PR and between aaac888 and 73434e7.

⛔ Files ignored due to path filters (1)
  • packages/generators/src/likec4/operators/__snapshots__/likec4.generate.snap is excluded by !**/*.snap
📒 Files selected for processing (8)
  • devops/vitest.ts
  • packages/config/build.config.ts
  • packages/config/src/index.ts
  • packages/config/src/node/index.ts
  • packages/config/src/node/load-config.ts
  • packages/generators/src/likec4/operators/base.ts
  • packages/generators/src/likec4/operators/views.spec.ts
  • packages/likec4/vitest.config.ts

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can you revert changes in packages/generators/src/likec4/?
I believe it is out of scope for this PR

@sraphaz
Copy link
Copy Markdown
Collaborator Author

sraphaz commented Mar 17, 2026

Hi @davydkov,

Thank you for the review and the clear suggestions. I've applied all of them (config cycle, vitest aliases, leanix-bridge as devDep, CLI subcommands, e2e/workflows, CodeRabbit follow-ups).

In short: we had introduced a config cycle, treated leanix-bridge as a runtime dep, and hadn't aligned vitest/e2e with the package layout—so things "worked" in the monorepo but were brittle and wrong for publish/CI. Your suggestions fix that: single-direction config, devDep for the bridge so the CLI stays lean, and aliases/workflows that match how the package is consumed. I got the rationale and am happy to iterate if anything still doesn't sit right. Thanks again.

@sraphaz sraphaz requested a review from davydkov March 17, 2026 11:46
sraphaz added 4 commits March 17, 2026 08:51
- config: assert landingPage preserved in schema smoke test; throw on invalid generators
- core: guards.spec curried tests, remove as any
- leanix-bridge: reject non-unique lookup hits; relations pagination; duplicate manifest id check; backfill warning not block reuse

Made-with: Cursor
- No defaults on dry-run/apply so explicit user input is preserved
- conflicts('dry-run','apply') rejects both flags; derive dryRun after parse

Made-with: Cursor
- likec4 CLI: ensureProject, handlers (serve, preview, codegen), ensure-libs, check-update, package-meta, bridge shared
- leanix-bridge: interfaces and options (reconcile, contracts, model-input, mapping, sync, inventory, governance, drift, impact, adr, api-client)

Made-with: Cursor
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Have you tried this?

import { version } from '../../package.json' with { type: 'json' }

@davydkov davydkov merged commit cf5acbc into likec4:main Mar 19, 2026
14 checks passed
@likec4-ci likec4-ci bot mentioned this pull request Mar 17, 2026
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.

2 participants