Psi now treats launcher-owned startup basis construction as the canonical way to make extension dependencies available at startup.
The launcher reads user and project extension manifests before psi.main
starts, expands recognized psi-owned defaults, validates the effective manifest,
and supplies the resulting dependency basis at launch time.
Runtime introspection and reload/apply still exist, but they are no longer the primary startup dependency mechanism.
- launcher owns startup dependency availability
- runtime owns post-startup extension behavior, introspection, and reload/apply convenience paths
This closes the phase-order problem where manifests discovered after JVM startup could not reliably influence the initial classpath.
User scope:
~/.psi/agent/extensions.edn
Project scope:
.psi/extensions.edn
Merge semantics:
- user and project manifests are read independently
- effective entries merge by library symbol key
- project scope overrides user scope for the same lib key
- run
psi ... - launcher determines effective cwd/worktree
- launcher reads user and project manifests
- launcher merges them with project-over-user precedence by lib key
- launcher expands recognized psi-owned defaults
- launcher infers deterministic
:psi/initfor recognized psi-owned libs when omitted - launcher validates the expanded manifest
- launcher constructs startup basis and launches psi
The manifest uses a top-level :deps map.
Any dep entry with :psi/init is treated as an extension dependency.
Entries without :psi/init are allowed as support deps and appear as
non-extension entries in introspection.
Example:
{:deps
{my/local-ext
{:local/root "/Users/me/dev/my-local-ext"
:psi/init my.local.ext/init
:psi/enabled true}
io.github.me/cool-ext
{:git/url "https://github.com/me/cool-ext"
:git/sha "abc123def456"
:psi/init cool.ext/init}
io.github.someone/packaged-ext
{:mvn/version "0.2.1"
:psi/init packaged.ext/init
:psi/enabled false}
;; support dep, not an extension entry
org.clojure/data.json
{:mvn/version "2.5.1"}}}Recognized psi-owned extension libs can use concise manifest entries.
Canonical minimal form:
{:deps
{psi/mementum {}
psi/munera {}
psi/work-on {}}}For those recognized libs, the launcher supplies missing fields from its explicit psi-owned extension catalog, including:
- source identity
- version/ref identity
:deps/root:psi/init
Selective override remains valid:
{:deps
{psi/mementum {:git/sha "override-sha"}
psi/munera {:psi/init extensions.munera/init}}}Important rules:
- minimal
{}syntax is valid only for recognized psi-owned extension libs in the launcher catalog - explicit manifest fields override launcher defaults field-by-field
- launcher must still validate the final entry as one coherent coordinate family
- launcher does not infer
:psi/initfor arbitrary third-party libraries
Each expanded dep entry must use exactly one dependency coordinate family:
:local/root- git coordinates
:mvn/version
Validation rules currently enforced by launcher expansion:
- top-level manifest must be a map
:depsmust be a map- dependency entry must be a map
- effective dependency entry must resolve to one coherent coordinate family
- git dependency entries require
:git/urland one of:git/shaor:git/tag - minimal psi-owned syntax is allowed only for recognized catalogued psi-owned libs
Startup:
- launcher builds the startup basis before psi starts
- recognized psi-owned extension deps can become concrete launch-time deps without repeated boilerplate
- third-party deps must remain explicit enough to validate and enter the startup basis
Runtime:
- introspection still reports user/project/effective extension state
- reload/apply remains useful as a convenience path after startup
:restart-requiredremains a runtime convenience/recovery status, not the canonical startup contract
For recognized psi-owned minimal manifest entries such as:
{:deps {psi/mementum {}
psi/work-on {}}}ownership is split deliberately:
- launcher expands the concise manifest syntax into concrete startup deps and puts those extension namespaces on the JVM classpath
- runtime computes install state, activates extensions, reports diagnostics, and supports reload/apply after startup
This means launcher-started psi is the authoritative proof path for classpath-sensitive behavior.
Direct bootstrap or in-process test paths that do not cross the launcher boundary are useful for runtime activation testing, but they are not equivalent proofs of launcher-owned classpath construction.
For recognized psi-owned minimal entries, runtime activation is canonicalized through :psi/init and the live registry identity is stable manifest:{lib} rather than a source file path, for example:
manifest:psi/mementummanifest:psi/work-on
The canonical read surface exposes:
:psi.extensions/user-manifest:psi.extensions/project-manifest:psi.extensions/effective:psi.extensions/diagnostics:psi.extensions/last-apply
Example query:
{:action "query"
:query "[:psi.extensions/user-manifest
:psi.extensions/project-manifest
:psi.extensions/effective
:psi.extensions/diagnostics
:psi.extensions/last-apply]"
:entity "{:psi.agent-session/session-id \"sid\"}"}Current categories include:
:malformed-entry:duplicate-init:missing-git-sha:project-local-root-nonreproducible:load-failure:restart-required
Manifest-installed non-file-backed extensions activated by :psi/init use
stable live-registry identities of the form:
manifest:{lib}
Examples:
manifest:psi/mementummanifest:psi/work-on
- add entries to
~/.psi/agent/extensions.ednor.psi/extensions.edn - start psi with
psi ... - inspect extension state via introspection when needed
Use explicit :local/root plus :psi/init for non-catalog third-party work, or
minimal {} syntax for recognized psi-owned extensions when launcher defaults are desired.
- add a manifest entry with explicit git/mvn coordinates and
:psi/init, or use recognized psi-owned minimal syntax - start psi through the launcher so the basis is built before startup
- inspect
:psi.extensions/effective,:psi.extensions/diagnostics, and:psi.extensions/last-applyas needed