Packages and project environments
A reusable Lumicron package can be two files. The package defines engineering code; lumicron, lumicron_pdk, and lumicron_validate are the Python APIs that code uses.
Start with Examples/Packages/SimplePDK and OBandLibrary in the Lumicron repository. The complete ecosystem is small enough to read before learning the resolver.
Inspect an environment in Lumicron
Click the project/environment name at the bottom left of the window, or open Script → Packages in the inspector. The Inspect menu switches between Project Environment and each resolved package. Choosing a package changes the inspection view; it does not activate a different process or alter the layout.
The environment view lists packages, versions and capabilities. Edit Dependencies… opens the project’s lumicron.toml; Refresh Dependencies checks Git branch tips and local packages. It asks before changing package versions or locked commits. Package Details shows the local source or Git locator, the locked Git commit where applicable, dependencies, and Show Package in Finder.
Technology and component packages expose Layers, Stack, PCells and Routes. A component library shows its own PCells and identifies the technology supplying its layers, stack and route profiles. Select O-band and C-band separately in the example to inspect their distinct Guide definitions. Validation packages list declared checks, not completed verification results. Utility packages list their entry files; inspect those files for their Python functions.
The environment is project-scoped. There is currently no app-wide package library or package browser. Add Git Package… adds a dependency from a repository URL; Add Local Package… lets you choose a package folder. Local packages remain in their source folders; Git packages are cached under the project’s .lumicron/ directory. Opening the project verifies an existing inspection cache without importing code; use Refresh Dependencies when the cache is absent or local files have changed.
Start a project with local and Git dependencies
- Use New Project in Lumicron. It creates an editable
lumicron.tomland script. - In Script → Packages → Project Environment, choose Add Git Package…. Enter the repository URL, branch/tag/commit (HEAD uses the default branch), and optional package folder. Choose Review to fetch and read package metadata.
- Review the declared package IDs, versions, capabilities, technology targets and exact Git commits, including new transitive dependencies. Add to Project runs the package definitions, saves the dependency and lock, and opens its inspector. Existing dependencies stay pinned; this action never silently replaces an existing package ID or advances other branches.
- To add a local library, choose Add Local Package…, select its folder containing
lumicron.toml, review the package IDs and capabilities, then choose Add to Project. Lumicron records a relative path from the project to that folder. - Select packages in the inspector to review the resolved resources.
- In the design script, import the package by its Python name, for example
import example_oband.all as oband. The native Run action activates the project’s environment for that execution.
Review does not execute package Python or change the project’s manifest, lock or inspection data; it can populate the project Git cache. Add to Project executes trusted package code to build inspection data. If import or a handled file-write failure occurs, the previous project files are retained/restored. A process kill or power loss is not a three-file transaction: a mixed artifact pair is rejected by existing digest validation. Changed project/package inputs invalidate the review.
Use HTTPS or SSH repository URLs with your existing Git credential helper or SSH agent. An absolute local Git repository path also works. Lumicron does not store passwords/tokens in this form. Hosting-service sign-in, SSH setup and remote repository creation are separate from adding an engineering package.
For manual manifest edits, choose Edit Dependencies… and add one table for each direct dependency. Libraries can declare their own technology. Save the manifest, then choose Refresh Dependencies. It shows available package versions and Git commits before updating the manifest and lock. You can keep the current pins.
For example, keep the generated [project] section and add:
[dependencies."example.oband"]
path = "../OBandLibrary"
[dependencies."team.routing"]
git = "ssh://git.example.org/team/routing.git"
ref = "main"The Git URL is illustrative; substitute a repository containing a package whose manifest declares team.routing. Each dependency key must match that package’s ID. Local paths are relative to the declaring manifest, so the first entry expects an OBandLibrary folder beside the project. Keep both the manifest and resolved lock in source control.
TOML declares which packages belong to the environment. Import a declared package as <package_id_with_dots_and_hyphens_replaced_by_underscores>.all. For example, cornerstone.soi220.oband becomes cornerstone_soi220_oband.all. The import uses the project’s exact lock and keeps process ownership and environment evidence. Arbitrary folders are not registered packages.
Your project’s own Git repository is separate from its dependencies. The Script Git navigator can initialize a local repository, stage and commit files, and open Repository Settings… to add a remote, fetch, pull (fast-forward only), or push. Create the remote repository with your hosting provider and configure authentication before connecting it. Commit authored sources, lumicron.toml and lumicron.lock; keep .lumicron/ caches out of source control. Adding a package does not publish your project.
A tiny process and its component library
SimplePDK/lumicron.toml declares the identity and the file to load:
schema = 1
[package]
id = "example.simple"
version = "1.0.0"
[capabilities]
technology = "technology.py"technology.py contains an ordinary layer table:
import lumicron_pdk as lpdk
LAYER = lpdk.LayerTable(
CORE=lpdk.Layer(1, 0, stipple=lpdk.Stipple.SPARSE_DOTS)
)A component library declares its own identity and names the process it uses:
schema = 1
[package]
id = "example.oband"
version = "0.1.0"
process = "example.simple"
[capabilities]
components = "components.py"
[dependencies."example.simple"]
path = "../SimplePDK"
version = "1.0.0"In components.py, import the declared process and define components with the existing Layout API:
import lumicron as lm
import example_simple.all as tech
@lm.pcell(symbol="waveguide")
def Guide(length=30):
c = lm.CELL("Guide")
c.Place(c.add(lm.Rectangle(length, 2, layer=tech.LAYER.CORE))).at((length / 2, 0))
c.add(lm.PORT("o1", position=(0, 0), direction=180, width=2, layer=tech.LAYER.CORE))
c.add(lm.PORT("o2", position=(length, 0), direction=0, width=2, layer=tech.LAYER.CORE))
return cNo __init__.py, manual registry, or duplicate layer definition is required. The component’s library is example.oband; its process is example.simple. The C-band example exposes another Guide without replacing this one.
A tapeout consumes packages
A project manifest has [project] instead of [package]. Its portable ID is created once and kept in source control. A clone keeps this semantic ID. Each checkout retains separate app-owned conversations and live verification sessions.
schema = 1
[project]
id = "my-team-tapeout"
[dependencies."example.oband"]
path = "../OBandLibrary"Run the project script in Lumicron. Its declared environment is activated for the run. From an ordinary Python process, activate it explicitly:
from lumicron.packages import resolve
environment = resolve("/path/to/tapeout")
with environment.activate():
import example_oband.all as library
component = library.Guide()Package Python is trusted executable code. Resolving source bytes alone does not import it. Running a script, generating a native inspection projection, and qualification do execute package definitions. Activation is scoped and does not permit concurrent package environments in different Python threads. Native script runs use separate processes.
Requirements, lock, and local edits
lumicron.toml is authored intent. lumicron.lock records exact package IDs, versions, capabilities, process targets, dependencies, source locators, content SHA-256 digests, and Git revisions. Commit both files. Ignore .lumicron/, virtual environments, and Python caches.
resolve() creates a lock when none exists. With an existing lock it verifies requirements and package bytes. A changed local dependency is an error until the user chooses Refresh Dependencies in Lumicron. That action also checks Git branch tips and asks before changing package versions or locked commits. For an explicit terminal update:
python -m lumicron.packages resolve . --updateThe command also executes package introspection and writes a derived native inspection cache. python -m lumicron.packages inspect . prints the verified lock without importing package code. Project opening reads the cache and checks its inputs; missing or changed inputs make the projection unavailable. It never updates a branch tip merely because a project was reopened.
Only exact version constraints are supported in this first resolver. One package ID has one resolved version. Conflicting sources/versions, cycles, missing dependencies, and undeclared dependency imports during package initialization fail visibly. Local paths are relative to the manifest that declares them. Package content includes ordinary source/assets, excluding Git metadata, .lumicron, Python caches and its root lock. Put generated outputs outside package source trees so they do not unexpectedly change content identity.
Git sources and offline reconstruction
Git is optional. Local-path projects work without it. A Git dependency uses standard Git transport, SSH, credential helpers and system configuration:
[dependencies."example.oband"]
git = "ssh://git.example.org/team/oband.git"
ref = "main"
# subdir = "packages/oband" # optional path inside that repositoryThe lock stores the resolved immutable commit. Updating the remote does not update a locked project. resolve . --update fetches intentionally. The exact cached revision works while the remote is unavailable. A clone without that cache needs the remote to reconstruct it. Relative dependencies of a Git package must remain inside its pinned repository. No GitHub login or provider integration is required or implied. Bundled source locators are reserved for shipped engineering package resources; this release ships the examples as ordinary local sources. A future registry can provide another source adapter; no registry is implemented.
Multiple processes, components and validation
The demonstration tapeout combines two process packages and two optical component libraries. A process owns its layer definitions. Libraries targeting the same process do not create duplicate layer owners. Native Layers groups include the process name; component catalogs retain qualified identities while displaying friendly names.
GDS has a global layer/datatype encoding. If two resolved processes declare the same pair, physical export from that environment fails, even if a particular layout does not use both layers. Lumicron does not remap fabrication layers or choose whichever technology loaded first. Use distinct process encodings or separate process outputs. Environment identity does not turn incompatible fabrication technologies into one process.
A validation package has a validation capability pointing to ordinary Python. The SimpleValidation example calls lumicron_validate.dfx.port_orientation with an authored instance ID and port. Utility packages work the same way: GratingTools exports a small alignment function and needs no process. Capability names are extensible; native views understand technology, components and validation.
Validation discovery lists provider IDs, process targets and explicit functions or DRC decks. Users call/select the provider deliberately. Decks are not merged. Native DRC/LVS and simulation retain their existing evidence scopes; this package model does not certify a multi-process native simulation or automatically bind a package provider to the native DRC session.
Organize your repository freely
New Project creates a manifest, a small design/top.py, README and .gitignore. These are editable starting files. Rename design, move the script, or put it at the root. Environment resolution does not inventory or prescribe design folders. FlexibleTapeout demonstrates an intentionally unusual structure.
python -m lumicron.packages new-project MyTapeout
python -m lumicron.packages initialize existing-repository
python -m lumicron.packages new-package MyComponents --id team.components --capability componentsInitialization adds only minimal project metadata and preserves existing files. New Package and Initialize are terminal/backend operations in this phase; there is no separate native wizard. Technology, components, validation and utilities are templates over the same manifest, not different package systems.
Identity and generated evidence
Package ID answers who owns a definition. Process ID identifies its technology. Environment ID binds exact resolved package content and dependency revisions. Source locator answers where the bytes came from. Changing a remote URL does not rename a package. Absolute cache paths are excluded from environment identity.
Authored lai_/lra_ identity remains separate. Another Git commit does not rename an authored object. Existing source-anchor continuity limits still apply when programs or source files change. Package/environment evidence is stored in reserved cell custom-data projections and travels with supported Layout/GDS semantic/.lum serialization. Digest association is local evidence, not cryptographic authenticity. A generated artifact can describe an older environment without being a current result for today’s inputs.
Legacy archive compatibility
A resolved environment with no technology packages can still use the existing Load PDK .lumpdk workflow. Its archive digest remains the legacy evidence owner. Once a technology package is declared, the environment owns the process context; an unrelated archive cannot silently add another process. An unresolved or stale environment must be resolved before Lumicron can prove that this fallback applies. Use Refresh Dependencies in Script’s Packages inspector (also available when inspection is unavailable), or the explicit terminal command above. New Project resolves its empty environment as part of creation.
Project branches, conflicts, and recovery
Git owns the repository, branch, index, commits and conflicts. Lumicron observes those records, including changes made in Terminal. Repository Settings supports branch changes, fetch, fast-forward pull and ordinary non-force push. Save dirty editors and preserve tracked changes before changing branches. Lumicron never silently stashes, discards changes or resolves a conflict. Detached HEAD remains a detached checkout; no branch is invented.
A branch can contain a different manifest and lock. Its old environment inspection cache then becomes unavailable. Choose Refresh Dependencies to reconstruct that branch’s exact packages. An upstream update is a separate reviewed choice; Keep Current preserves the lock. An exact cached Git package remains usable offline even if its transport mirror was removed. Explicit Refresh can reconstruct a damaged owned checkout only when the reconstructed bytes agree with the lock. An unavailable remote still prevents fetching an uncached revision. Mutable local packages need their authored files restored or an explicit refresh of their new content; they are never replaced as if they were disposable Git cache.
Resolve conflict markers in lumicron.toml before resolving dependency intent; resolve markers in lumicron.lock before trusting exact resolution. Conflicted Schematic and Notebook documents retain their bytes and require conflict resolution. Lumicron does not choose either side or offer semantic merging. After repairing the files, refresh the project and its dependencies.
Commit authored sources, package declarations and locks. Generated engineering artifacts and historical results can be ordinary versioned files when your team chooses to retain them. .lumicron/, interpreter caches and virtual environments are machine-local. Existing projects’ ignore rules are not automatically rewritten. A project copy preserves its semantic identity; app-owned sessions remain separate. Relative local dependencies must move with their required relative arrangement. Historical run snapshots retain their original package/model inputs and paths; a new Git HEAD does not rewrite them or prove them current.
Run the project pipeline locally
A pipeline is one ordinary project-owned command. Lumicron runs the command; your script, test tool or Makefile owns its checks and any step names. Add this optional table to the existing schema1 project manifest:
[pipeline]
command = ["/bin/sh", "pipeline.sh"]For example, pipeline.sh can invoke the same checks used by CI:
#!/bin/sh
set -eu
: "${LUMICRON_PYTHON:?Set the supported Lumicron Python interpreter}"
exec "$LUMICRON_PYTHON" -B checks.pyThe Script Git navigator’s Run Pipeline Locally… opens the command and output view. Running requires an explicit button press and saved editors. Opening or inspecting a project never executes its pipeline. The working directory is the project root. LUMICRON_PYTHON supplies the selected interpreter and LUMICRON_PROJECT_ROOT supplies the root path. Python checks use the same existing packages.resolve(root).activate() context when importing engineering packages. There is no separate pipeline implementation of Layout, DRC, DFX, Registry or LVS.
A terminal or CI worker using the supported runtime can run the identical binding:
python -m lumicron.packages._pipeline run /path/to/projectThis private application adapter requires an existing exact lock and validates package inputs before running. It does not update dependencies. Resolve first when setting up a fresh clone. It also rejects changed manifest, lock or package inputs after execution. Script arguments are passed literally; shell expansion occurs only when the configured command explicitly invokes a shell.
The view shows live output, actual exit status, duration and cancellation. Logs are retained under .lumicron/pipeline-runs/. Native runs have a 30-minute limit and retain up to 2 MiB stdout plus 64 KiB stderr, with explicit truncation notice. The result describes that invocation, not a permanent current-design certificate. Fix a failing check or configuration and run again. A project command is executable content: review it before running it. Lumicron adds no commit, stage, push, reset, clean or checkout; if your command performs one, the refreshed Git state shows it. Hosted CI YAML interpretation, cloud dashboards and provider authentication are outside this local-command boundary.
When a fresh clone has an exact lock but no available inspection projection, Keep Current reconstructs that locked environment after declining an update. It does not rewrite the manifest or lock, accept changed local package bytes, or advance the reviewed Git revision. An already available projection is left alone.