foundry

From FOLIO Licenses and Agreements wrappers to a UI artifact supply chain

  • foundry
  • folio
  • lattice
  • stripes
  • publisher
  • artifacts
  • erm
  • licenses
  • agreements

The first Foundry frontend proof was intentionally narrow. Take ui-licenses, wrap it in something Lattice can load, put it behind Envoy, and see whether the real FOLIO Licenses application can run outside Okapi.

That was the right place to start, but it could never be the place to stop.

If every FOLIO UI module needed its own hand-built wrapper project, Foundry would have replaced one maintenance problem with another. The useful question was whether Licenses taught us a generic wrapping pattern, or whether it was just a bespoke success.

What Licenses proved first

ui-licenses did the first job: it exposed the runtime shape that a real Stripes module expects.

That meant more than rendering a React component. The wrapper had to provide a Stripes-like runtime island: routing, module metadata, Okapi-shaped context, permissions, translations, CSS expectations, protected API paths, and enough browser/auth behaviour for the module to call its backend through Envoy.

The first wrapper was necessarily hands-on. It answered questions such as:

  • what should Lattice pass to the MFE?
  • what should Envoy translate at the FOLIO compatibility boundary?
  • which parts are module-specific binding data?
  • which parts are really generic Stripes adapter machinery?

Once Licenses worked, the important extraction was clear.

The module-specific wrapper should say:

  • this is the package;
  • this is the root component;
  • this is the route;
  • these are the translations;
  • these are the expected interfaces and protected paths.

The adapter should own the generic Stripes island.

That became lattice-stripes-module-adapter.

Why a publisher is needed

Even with an adapter, hand-wrapping every module release would be the wrong operating model.

FOLIO UI modules release over time. Branch heads move. Adapter compatibility will change. Some modules will need metadata overrides. Some releases will fail. Published artifacts need provenance. Release artifacts should be stable. Branch-head artifacts should be mutable.

That is build and release automation, not application runtime composition.

So the next boundary became lattice-stripes-module-publisher.

The publisher is not Loom. It does not activate a workspace. It does not decide which apps a user gets. It does not become a product catalog.

Its job is simpler and more mechanical:

  1. read tracked UI modules;
  2. inspect releases and branch heads;
  3. detect the declared @folio/stripes range;
  4. select a compatible adapter version;
  5. generate a temporary wrapper;
  6. build a module-federation artifact;
  7. publish it to an S3-compatible artifact store;
  8. record provenance in tracking state.

That tracking state is useful operational evidence, but it is not runtime truth. Loom should later select from available artifacts when activating capabilities for a workspace.

Agreements proved dependency generalization

Licenses alone hid a defect.

The first generated wrappers had a Licenses-shaped dependency set. That was enough for Licenses, but not enough for Agreements.

When Agreements was added, the generated build failed because ui-agreements declares dependencies that Licenses does not need, including packages such as acquisition components and registry helpers. That failure was useful. It showed that the wrapper generator must not bake in Licenses assumptions.

The fix was generic: generated wrappers now merge the source UI module’s declared dependencies and peer dependencies, while keeping adapter-owned framework packages such as React, React DOM, and @folio/stripes under adapter control.

After that, Agreements built and published.

That is the kind of proof a second module is supposed to provide. It did not merely add another artifact. It removed a Licenses-specific assumption from the publisher.

Current published Agreements evidence:

releases/agreements/v12.1.1/adapter-0.1.0/
next/agreements/master/

Open Access proved the adapter range

Open Access tested a different question.

The current adapter is still young, so the compatibility matrix matters. We do not want a publisher that blindly says every Stripes module is supported, and we do not want a matrix that is so conservative it blocks modules unnecessarily.

ui-oa declares @folio/stripes ^10.0.0. It built and published successfully with adapter 0.1.0.

That gave us evidence to set the current adapter range at Stripes 10.0.0 and above, while skipping older releases below that line.

Current published Open Access evidence:

releases/open-access/v3.0.0/adapter-0.1.0/
next/open-access/master/

This is still evidence, not a universal claim. Future modules may force new adapter versions or module-specific fixes. The important point is that adapter compatibility is now explicit publisher state rather than tribal knowledge in a wrapper repository.

ILL proved metadata overrides

ILL added a Knowledge Integration module to the evidence set.

That matters because Foundry is not only about upstream FOLIO applications. It also needs to carry K-Int capabilities such as ILL through the same artifact model.

ill-ui had one concrete wrinkle: the repository and package metadata did not directly imply the translation namespace the wrapper needed. The publisher therefore gained module metadata support for:

translationNamespace: ui-ill-ui

With that supplied, ILL built and published.

Current published ILL evidence:

releases/ill/v2.1.3/adapter-0.1.0/
next/ill/main/

Again, the value is not only that ILL has an artifact. The value is that the publisher now has a place for module-specific facts without collapsing the adapter into a set of one-off wrappers.

Release artifacts and next artifacts mean different things

The publisher now treats released versions and branch heads differently.

Release paths are intended to be stable:

releases/{moduleId}/{sourceVersion}/adapter-{adapterVersion}/

A release artifact should not be overwritten by accident. If an adapter fix requires rebuilding an existing release, that should be an explicit republish operation.

Branch-head artifacts are intentionally mutable:

next/{moduleId}/{branch}/

Those are useful for tracking current main or master while preserving the distinction between “this is a released module version” and “this is the current branch head.”

That distinction will matter when Loom starts selecting operational refs for workspace activation. A demo workspace might use released artifacts; a developer workflow might intentionally use next.

What has been proved

The current evidence set is:

ModuleRelease artifactBranch-head artifact
Licensesreleases/licenses/v12.1.1/adapter-0.1.0/next/licenses/master/
Agreementsreleases/agreements/v12.1.1/adapter-0.1.0/next/agreements/master/
Open Accessreleases/open-access/v3.0.0/adapter-0.1.0/next/open-access/master/
ILLreleases/ill/v2.1.3/adapter-0.1.0/next/ill/main/

The publisher can run dry-run-first inspection, choose adapter versions, build generated wrappers, publish immutable release artifacts, publish mutable branch-head artifacts, verify remote artifact keys, and update tracking state with provenance.

The artifact target today is Cloudflare R2 through an S3-compatible API. That is a current target, not the core abstraction.

What this does not prove yet

This is not the same as activating all four capabilities in a workspace.

Licenses is the current end-to-end runtime proof. Agreements, Open Access, and ILL are artifact-supply evidence. Their backend services, tenant provisioning, platform-service needs, permissions, data seeding, and Lattice activation paths still need their own work.

That distinction is important. The publisher makes UI artifacts available. Loom must still decide what a workspace has activated. Executors must still provision backend services and tenants. Envoy must still publish routes. Lattice must still consume the runtime descriptor and load the selected apps.

The point is not that the whole multi-capability demo is complete.

The point is that the frontend artifact bottleneck is no longer a hand-built Licenses wrapper.

Why this matters for the roadmap

The next strategic questions are about operational composition and Kubernetes demo deployment:

Can Loom accept an operational descriptor for a workspace, select available UI artifacts and backend service refs, and drive reconciliation without becoming a product/offering system?

That question depends on having UI artifacts to select.

The widening from Licenses to Agreements, Open Access, and ILL gives the roadmap something concrete to stand on. It shows that Foundry can start building a shared artifact supply chain for FOLIO and K-Int UI modules, while still keeping runtime activation in Loom and application composition in Lattice.

That is the architectural line worth preserving: the publisher supplies artifacts; Loom selects operational intent; Lattice composes the workspace; Envoy routes the traffic.