The front door to the Swift Institute: the public package inventory
(Workspace.json), the bounded default checkout
(Selection.json), machine-checked facts about that checkout
(workspace doctor), an isolated local development checkout for Xcode (workspace sync),
and local-source composition for cross-package work (workspace compose / restore /
verify).
| Command | What it does |
|---|---|
sync |
Clone missing repositories and fast-forward eligible ones. Never rewrites work. |
doctor |
Report what is measurably true about this checkout. |
compose |
Point one package's dependency at your local checkout of it, so edits are picked up. |
restore |
Undo a composition, returning the manifest to its declared form byte-for-byte. |
verify |
Report which source a dependency actually compiled from, read from resolved state. |
context install|check |
Install or verify the checkout-root agent entry point and canonical skill projections. |
navigation install|check |
Install or verify the pinned cclsp/SourceKit-LSP integration for this checkout. |
package <action> |
Run SwiftPM build, test, resolution, and administration through the Swift coordinator. |
Swift Institute builds a layered ecosystem of independent Swift packages. Three layers are realised:
| Layer | Family | GitHub org |
|---|---|---|
| 1 | Primitives — atomic, dependency-light identity and value types | swift-primitives |
| 2 | Standards — models of externally defined formats, protocols, and specifications | swift-standards |
| 3 | Foundations — operational capabilities composed from the lower layers | swift-foundations |
Dependencies flow downward; same-layer edges are permitted only when they express a genuine semantic prerequisite and the graph stays acyclic. Names above Layer 3 — components, applications — are reservations recording intent. Never read such a name as evidence that the thing exists.
Specification packages live in orgs named for the issuing authority:
swift-ietf (the swift-rfc-* family),
swift-iso, swift-w3c,
swift-whatwg, and further authority, vendor, and
jurisdiction orgs on the same pattern. The swift-*-standard family inside swift-standards
models de-facto systems (Git, SwiftPM, GitHub, …) and is a different family from the
authority specification packages.
Every package is one repository; there is no monorepo. This repository clones selected
packages as independent checkouts materialized in the org hierarchy — one root per layer
organization (swift-primitives/, swift-standards/, swift-foundations/), with packages
owned by a specification-authority, vendor, or jurisdiction organization nested one level
deeper under their layer root (for example swift-standards/swift-ietf/<package>) — and
composes them into a single Xcode workspace. Selection.json contains only canonical
owner/repository identities and decides which inventory entries participate in the default
checkout. Placement and ordering derive from Workspace.json alone: each selected entry's
organization and layer fields decide the path, and inventory order decides synchronization
and Xcode order. Tools never infer a location from a package's name or by scanning the tree,
and materialized paths are regenerable state — when a repository transfers between
organizations, both documents must be updated explicitly before sync can proceed.
- Inventory: Workspace.json is the public roster of packages this workspace manages, intended to grow to every public, non-archived Institute package.
- Default checkout: Selection.json is a membership list of canonical
owner/repositoryidentities. It deliberately does not repeat package metadata, paths, or ordering. - Checkout facts:
workspace doctormeasures the checkout directly — identities, remotes, branches, upstreams, toolchain, and workspace references.
sync and doctor load both files and fail before repository work if the selection is
missing, malformed, duplicated, or names an entry absent from the inventory. They never treat
an invalid selection as permission to operate on the complete inventory. compose, restore,
and verify resolve their explicitly named operands against the complete inventory instead;
an operand does not have to remain in the default selection once it is already checked out.
Prefer running doctor over trusting any written snapshot: repository-state prose is a
measurement with a timestamp, and it drifts.
Open objectives are public GitHub issues on the relevant repositories:
gh issue list --repo swift-institute/WorkspaceNo Institute access is required. Everything below works from a clone of this repository alone, against public repositories, with no credentials and no internal tooling. If a step here needs anything you cannot get, that is a defect — please open an issue.
Requires macOS 26, Xcode 26.6, Swift 6.3.3, and Git. The optional navigation setup additionally requires Node 18 or newer and Bun.
git clone https://github.com/swift-institute/Workspace.git
cd Workspace
swift run --package-path Application workspace sync
open institute.xcworkspaceThe committed Selection.json bounds that first synchronization. To change the default
checkout, edit its repositories list using the exact owner/repository identity from
Workspace.json; selection-file order has no effect.
The third command is slow the first time, and it is silent while it works. Before it does
anything visible, swift run resolves and compiles the command-line application and its whole
dependency graph. Two costs stack up, and both are silent:
- Resolution. SwiftPM fetches the full transitive dependency graph — around 200 repositories — before compiling anything. On a first run with a cold package cache this is network-bound, so how long it takes depends on your connection more than your machine.
- Compilation. Roughly 5,900 build steps, about 4,200 of them compilations. Measured with sources already local, that alone took 4–7 minutes depending on the machine and its load; treat it as a floor, with fetching on top.
The earliest minutes print nothing at all while SwiftPM evaluates manifests, and the rest print
nothing either: no progress bar, no percentage, nothing until the build finishes and sync
prints its plan. Silence there is expected, not a hang.
That first swift run is the unavoidable self-hosting bootstrap. Once it has
produced Application/.build/debug/workspace, run all later SwiftPM work
through the coordinator rather than invoking raw build, test, or
package-administration commands.
Install the shared agent entry point after cloning the surrounding Coenttb checkout:
swift run --package-path Application workspace context installThis validates every canonical skill before projecting it, writes the generated
root AGENTS.md and CLAUDE.md, and fails closed on any path it does not own.
Workspace owns the reproducible integration boundary between cclsp and Xcode's SourceKit-LSP:
Application/.build/debug/workspace navigation install
Application/.build/debug/workspace navigation checkinstall clones the public sourcekit-lsp-adapter line at the exact revision
compiled into Workspace, installs dependencies from cclsp's frozen Bun
lockfile, builds its Node executable, and writes two generated files beneath
the physical organization hierarchy:
.workspace/navigation/cclsp.json— one SourceKit-LSP server for the Workspace Application and each currently materializedWorkspace.jsonrepository;.workspace/navigation/mcp-server.json— the command, arguments, and environment an MCP client registers.
The command prints the descriptor path. Client applications own their own registration format, so Workspace does not rewrite a user's global client configuration. The descriptor is the canonical value to translate into that format.
SourceKit-LSP is launched through the generated workspace navigation serve
invocation. That typed boundary removes TOOLCHAINS, resolves
sourcekit-lsp through xcrun, and refuses a binary outside the Xcode selected
by xcode-select. cclsp remains a distinct third-party TypeScript tool: it is
not a Swift package, is not listed in Workspace.json, and is never resolved
from a personal fork or a fixed machine path.
The current generated configuration is deliberately per-package. A single deduplicated Institute-wide index requires a larger IndexStore merge and stabilizing acceptance probe; that exact Full-Swift remainder is tracked in issue #25. Workspace does not claim that per-package navigation is equivalent to cross-package index coverage.
Any earlier ad hoc merged-index pipeline and any prebuilt index bundle are retired, unsupported, and not prerequisites for navigation. A clean machine installs from the public cclsp revision and generates its own configuration through the Workspace commands above; it does not copy old index artifacts.
The bootstrapped executable owns SwiftPM concurrency, job count, and build state:
Application/.build/debug/workspace package build --package-path Application
Application/.build/debug/workspace package test --package-path Application --fresh
Application/.build/debug/workspace package resolve --package-path ApplicationBuilds are serialized through a machine-wide advisory lock and compile with
three jobs. --fresh is available for build and test evidence: it uses a
unique scratch directory beside the package and removes it before returning.
Additional SwiftPM arguments use repeated --argument options, for example
--argument=--filter --argument=Unit; coordinator-owned path, state, and job
options cannot be overridden. The coordinator never edits
Package.resolved.
sync prints its complete plan before changing repositories. It clones missing repositories
into the org hierarchy described above and only fast-forwards an existing repository when it
is clean, currently on main, tracks origin/main, and has no local commits. It never
resets, cleans, stashes, rebases, switches a feature branch, or overwrites a repository.
Preview the plan without changing files or Git metadata:
swift run --package-path Application workspace sync --dry-runThe org hierarchy materializes beside the physical checkout. Workspace resolves the
checkout through symlinks first, then uses exactly its parent as the organization directory;
invoking the tool through a symlink does not redirect that hierarchy. For a clone at
X/Workspace:
X/
├── Workspace/ this repository: Application/, Workspace.json, Selection.json,
│ institute.xcworkspace
├── swift-primitives/ ┐
├── swift-standards/ ├ materialization roots: independent repositories,
└── swift-foundations/ ┘ none part of this repository
Each package under those roots is an independent repository with its own history, remote, CI, and license. Committing their contents to this repository is always wrong — work on a package inside its own checkout and open the pull request on its own repository.
The roots sit beside the clone rather than inside it so the checkout stays a plain repository
and the hierarchy reads as the organization itself. sync creates repositories only beneath
those inventory-derived roots. Clone and update validation may use collision-resistant
temporary siblings in the same organization directory, and the generated
institute.xcworkspace remains inside the Workspace checkout. Materialized paths are
regenerable state — if a repository moves between organizations, its inventory entry changes
and sync materializes the new location, so nothing durable should reference one of these
paths as though it were stable.
Before inspecting or writing a materialized path, Workspace rejects . and .. traversal,
symbolic links and non-directories in existing path prefixes, and any prefix that resolves
outside the physical organization directory. These checks assume a stable local filesystem
namespace: they are repeated safety snapshots, not a descriptor-relative guarantee against
another process replacing a directory concurrently.
doctor reports what is measurably true about your checkout right now — never a written
snapshot:
swift run --package-path Application workspace doctorA healthy contributor run looks like this:
toolchain: ok (population 4)
workspace-reference: ok (population 1)
materialization: ok (population 5)
working-state: ok (population 5)
resolved-pins: ok (population 0)
manifest-identity: ok (population 5)
inventory-currency: not run (institute-internal)
7 checks: 6 ok, 1 not run (institute-internal); measured populations: toolchain 4,
workspace-reference 1, materialization 5, working-state 5, resolved-pins 0,
manifest-identity 5
doctor: passed — 6 check(s) measured, 1 not run (institute-internal), 0 warning(s).
Every check ends in exactly one of four states, and they are deliberately never printed the same way:
| Result | Meaning |
|---|---|
ok (population n) |
The check ran over n subjects and found nothing wrong. |
warning findings / error findings |
The check ran and lists what it found. |
unmeasured — <reason> |
The check could not establish what it needed to measure. This is not a pass. |
not run (institute-internal) |
The check is out of scope for a contributor run. This is not a failure. |
Exit status follows: 0 when everything was measured and no errors were found (warnings still
exit 0), 1 when a check measured an error, and 2 if anything was unmeasured. An
unmeasured check outranks an error precisely because it is worse: a failure to measure hides an
unknown number of both. A run containing one is never described as passing — "we could
not look" is a different answer from "we looked and it was fine", and conflating them is how a
broken check masquerades as a green one.
inventory-currency needs an authenticated GitHub client that the contributor path does not
carry, so it reports not run. That is the expected result and it does not fail your
checkout. If a step ever demands credentials or a repository you cannot read, that is a
defect worth reporting.
The population is the check's evidence that it actually measured something. A check that
silently evaluated zero subjects would print exactly the same reassuring ok as one that
examined all of them, so the count is printed to make the difference visible: materialization: ok (population 5) says five repositories were inspected, not that inspection was skipped.
ok (population 0) therefore means something specific: the check ran, its controls fired
correctly, and there were genuinely zero subjects in existence to measure. Above,
resolved-pins: ok (population 0) means no materialized repository has a Package.resolved
yet, so there are no pins to compare against their branch tips. For repository-subject checks,
an empty population measured against a non-empty selection is reported unmeasured, never
ok — that case is a failure to measure, and it is reported as one. The institute-only
inventory-currency check is the exception: it compares the complete inventory with live
discovery and reports their union as its measured population.
Each check also carries a known-positive and a known-negative control that run through the same
evaluation path as the real subjects. If the control that must fire does not, the check aborts
as unmeasured rather than reporting a green it did not earn.
For each selected repository, doctor distinguishes the active sibling location from the
superseded location inside the Workspace checkout:
| On-disk state | Result |
|---|---|
| Git repository only at the sibling location | Canonical and ok. |
| Git repository only inside the Workspace checkout | Legacy and an error. |
| Git repositories at both locations | An error; the sibling is active and the legacy checkout is left untouched. |
| No Git repository at either location | Absent and an error. |
| A location cannot be formed or safely inspected | Invalid and an error. |
Workspace never migrates or deletes a legacy checkout. Only the active sibling repository enters the downstream working-state, resolved-pin, and manifest-identity checks; a legacy-only tree never satisfies them.
Dirty worktrees, untracked files, detached HEADs, feature branches, and stale resolved pins are warnings — they may hold your unpushed work, so they are reported and left alone. Identity, remote, upstream, divergence, toolchain, missing-package, and workspace-reference problems are errors.
Every package depends on its siblings by URL, so an edit to a dependency normally has to be pushed before the package consuming it can see the change. That is the wrong loop for work that spans two repositories at once.
compose closes it: it rewrites one .package(url:) clause in the consumer's manifest to a
.package(path:) clause pointing at the dependency's own checkout in this workspace, so builds
compile the source you are editing. restore puts the manifest back. verify reports which
source actually compiled, so you never have to trust your own memory of which state you left
things in.
Both packages must be named in Workspace.json and checked out. If one is
not already checked out because it is outside the committed default, add its canonical
identity to Selection.json, then run sync before composing it.
Say you are changing swift-color-standard and want swift-color to compile against your
local copy.
1. Compose. Point the consumer at your local checkout:
swift run --package-path Application workspace compose \
--consumer swift-color --dependency swift-color-standardComposed swift-color → swift-color-standard (local development source).
manifest: <checkout-parent>/swift-foundations/swift-color/Package.swift
now: .package(path: "<checkout-parent>/swift-standards/swift-color-standard")
was: .package(url: "https://github.com/swift-standards/swift-color-standard.git", branch: "main")
⚠️ This manifest now carries a machine-local absolute path.
Do NOT commit or push it — it resolves only on this machine.
The written path is deliberately absolute. That means the composed manifest is worthless on any other machine — which is the point: if it escapes, it fails loudly at resolution instead of quietly resolving to some other copy of the package. Treat a composed manifest as an uncommittable local state.
2. Edit and build. Change swift-color-standard in its own checkout and build swift-color
normally. It now compiles your local source.
3. Verify — which source actually compiled:
swift run --package-path Application workspace verify \
--consumer swift-color --dependency swift-color-standardThis reads SwiftPM's own resolved state; it never infers the answer from the manifest. It also compares that against the composition ledger and warns if the two disagree — which means the last resolve predates your current composition, and you need to re-resolve before believing anything.
4. Restore before you commit or push:
swift run --package-path Application workspace restore \
--consumer swift-color --dependency swift-color-standardThe declared .package(url:) clause comes back byte-for-byte — the original text is stored
when composing and replayed verbatim, not regenerated, so nothing about the manifest's
formatting drifts. Your work in the dependency's checkout, including unpushed commits, is never
touched: restore only edits the consumer's manifest.
restore finishes by evaluating the restored manifest in an isolated temporary directory, and
tells you so:
Structural check (resolve-free): the restored manifest evaluates, swift-color-standard
is declared by URL again, and no local path leaked. This does NOT resolve
dependencies and is NOT a remote-reproducibility guarantee.
Read that limit literally. The check does confirm three things: the restored manifest still evaluates, the dependency is declared by URL again rather than by path, and no machine-local path survived.
It does not resolve anything. It does not contact a remote, does not confirm the declared URL exists, does not confirm the branch still has the commit you built against, and does not prove your colleague or CI can build what you just restored. A green structural check means "the composition was removed cleanly" — not "this builds from its canonical sources."
Confirming that last part is a step this tool deliberately leaves to you: run a full resolve and build afterwards. If the dependency's local commits were never pushed, your restored consumer will resolve to a remote that does not have them, and only a real resolve will tell you.
One composition per consumer/dependency pair at a time — compose again without restoring and it
refuses rather than stacking edits. If the composed clause has been hand-edited out of the
manifest, restore refuses to guess and says so. Both packages must be workspace repositories;
arbitrary local packages, multi-root setups, and Xcode-side composition are out of scope.
Issues are the channel — for questions as much as for defects:
gh issue list --repo swift-institute/WorkspaceThere is no private support path and no internal-only documentation: this README is the whole contributor surface. A step that does not work as described here, or that turns out to need access you do not have, is a defect — please open an issue rather than working around it.
Contributions come through the same path this README describes — there is no second, internal one. Pick up an issue, work in the package's own repository at its org-layout checkout, and open a pull request there; each package is an independent repository with its own history and CI.
Before opening a pull request, run doctor and make sure the package builds and tests from its
own repository. doctor reports which of its checks apply to your setup; checks that need
Institute access report that they did not run rather than failing your checkout.
The current roster contains a three-repository proof chain spanning all three layers —
swift-dimension-primitives → swift-color-standard → swift-color — plus swift-url-routing
and swift-http-body for an active migration workspace. The Xcode workspace uses only
relative sibling-layout references (../swift-foundations/swift-color, …); non-selected
transitive dependencies still resolve from their canonical remote URLs.
Licensed under the terms in LICENSE.md.