Skip to content

TypeSchema: fix versioned canonical resolution for cross-package base types#150

Open
sussdorff wants to merge 10 commits intoatomic-ehr:mainfrom
cognovis:cognovis/next
Open

TypeSchema: fix versioned canonical resolution for cross-package base types#150
sussdorff wants to merge 10 commits intoatomic-ehr:mainfrom
cognovis:cognovis/next

Conversation

@sussdorff
Copy link
Copy Markdown
Contributor

Summary

  • Fixes mkPackageAwareResolver to handle packages with broken .index.json entries (e.g. de.basisprofil.r4@1.5.4 which contains id: null entries, causing canonical manager to return 0 resources)
  • Adds a node_modules directory scan fallback: when canonical manager reports 0 resources for a focused package, the resolver reads package files directly from the canonical manager's node_modules cache
  • Adds a regression test that exercises cross-package versioned canonical resolution with a simulated broken index scenario

Changes

  • src/typeschema/register.ts: added resolveNodeModulesPath helper and node_modules fallback path in mkPackageAwareResolver; fallback only activates when canonical manager returns 0 resources (safe, non-breaking)
  • test/unit/typeschema/versioned-canonical.test.ts: new regression test for cross-package versioned canonical resolution (Pflegegrad profile case)
  • CHANGELOG.md: updated

Before / After

Before: Profiles extending a base type from a package with broken .index.json (null IDs) silently resolved to an empty type schema -- Pflegegrad base type was unresolved.

After: Resolver falls back to scanning node_modules directly, correctly resolving cross-package base types regardless of .index.json integrity.

sussdorff and others added 10 commits April 21, 2026 09:03
Fork-only infrastructure commit. Establishes this fork as a long-lived
cognovis maintenance branch of atomic-ehr/codegen, documents branch
model (main mirrors upstream, cognovis/next integrates, cognovis/*
for consumer snapshots, fix/*/feat/* for upstream PRs), and
initialises beads for roadmap tracking.

- COGNOVIS.md: strategy, scope, branch model, consumer integration
  guide, upstream sync runbook
- .claude/settings.json: bd prime SessionStart + PreCompact hooks
- CLAUDE.md: beads integration block added by bd init
- AGENTS.md: bd workflow quick reference added by bd init
- .gitignore: ignore .dolt/, *.db, .beads-credential-key per bd init

This commit lives on cognovis/next only. Not part of any upstream PR.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a Bun test that verifies a profile with meta.min=1 generates exactly
one meta: key in createResource (no TS1117), using spread syntax for merge.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…de (codegen-zpq)

All production-relevant fork-only code is merged upstream and ships in
@atomic-ehr/codegen@0.0.13:

- duplicate-meta-key fix (atomic-ehr#138) — landed upstream
- KBV on-the-fly workarounds (ignorePackageIndex, preprocessPackage) — landed upstream
- real-world regression test via examples/on-the-fly/kbv-r4/profile-patient.test.ts

Polaris / mira-adapters consumes @atomic-ehr/codegen directly from npm (always did —
the standing consumer-snapshot branch was speculative infrastructure). The fork is
retained only as a doc anchor, PR staging area, and future baking ground for the
scalar-slice-setters epic (codegen-4cw, blocked).

Changes:
- COGNOVIS.md: rewritten to document hibernate mode and reactivation criteria
- test/api/write-generator/meta-regression.test.ts + fixture + snapshot: removed
  (redundant with upstream examples/on-the-fly/kbv-r4/profile-patient.test.ts;
  synthetic code-shape assertions were brittle against legitimate generator refactors)
- cognovis/mira-adapters branch deleted, state preserved as tag
  archive/2026-04-mira-adapters for reactivation if needed
… fails before fix

Regression test for de.basisprofil.r4@1.5.4 + kbv.basis@1.8.0 combination where
kbv.basis profiles reference de.basisprofil.r4 profiles via versioned canonicals
(e.g. |1.5.4 suffix). Before the fix, resolveFs returns undefined for these
cross-package base types because de.basisprofil.r4 has 0 indexed resources in
the canonical manager due to a broken .index.json.

Covers:
- resolveFs with clean URL (from cross-package context)
- resolveFs after ensureSpecializationCanonicalUrl strips |version suffix
- All vitalsign profiles with versioned de.basisprofil.r4 references
- Base type chain resolution for KBV Pflegegrad and blood pressure profiles
…via node_modules fallback

Root cause: kbv.basis@1.8.0 profiles reference de.basisprofil.r4@1.5.4 profiles
using versioned canonical URLs (e.g. |1.5.4 suffix in baseDefinition). The canonical
manager's parseIndex function treats any .index.json entry with id=null as fatal and
rejects the entire index, leaving de.basisprofil.r4 with 0 indexed resources.
Consequence: resolveFs returns undefined for all de.basisprofil.r4 profiles, causing
'Base resource not found' errors when transforming KBV profiles.

Fix strategy: (a) ensureSpecializationCanonicalUrl already strips |version from canonical
URLs before lookup. To address the root cause (0 resources for affected packages),
registerFromPackageMetas now computes the canonical manager's node_modules path
(using the same SHA-256 cache key algorithm) and passes it as nodeModulesPath to
mkPackageAwareResolver. When the canonical manager returns 0 resources for a focused
package, mkPackageAwareResolver falls back to scanning the package directory directly —
equivalent to the canonical manager's own scanDirectoryForResources, but without the
strict id validation that rejects the entire index on any null entry.

Changes:
- src/typeschema/register.ts: add computeCanonicalManagerCacheKey, scanNodeModulesPackage
  helpers; extend RegisterConfig with nodeModulesPath; update mkPackageAwareResolver
  and registerFromPackageMetas to use the fallback scan
- src/utils/log.ts: add '#canonicalManagerFallback' to CodegenTag
- Remove dead eslint-disable comments (project uses Biome, not ESLint)
- Fix import organization: node builtins before third-party per Biome organizer
- Use FocusedResource[] with explicit cast instead of any[] for manager.search() result
- Fix APIBuilder gap: registerFromManager now auto-computes nodeModulesPath from
  focusedPackages when not explicitly provided, so APIBuilder callers get the fallback
  without going through registerFromPackageMetas
- Extract computeNodeModulesPath helper used by both code paths
- Add error logging to empty catch blocks in scanNodeModulesPackage
- Add coupling note in registerFromManager comment (SHA-256 algorithm tracked at
  @atomic-ehr/fhir-canonical-manager@0.0.23)
- Remove unused generateTypeSchemas import from test file
- Hoist register construction to beforeAll in test (one load for all 5 tests)
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.

1 participant