Workspace and lock contracts
Workspace manifest and lockfile behavior for dependency resolution, reproducibility, and update flows.
Document layout: Feature capability layoutconforms
## Purpose## Requirements
Workspace and lock contracts Specification
Purpose
Workspace manifest and lockfile behavior for dependency resolution, reproducibility, and update flows.
Requirements
Requirement: Hub authority: Decision [D-TOOL-MAN-0001]
The Beskid standard SHALL enforce the following migrated contract section. Accepted ADR decisions are binding; uppercase requirement keywords retain their BCP-14 meaning.
Hub owns workspace and lock rules.
Stable ID: BSP-REQ-4CF0609B398D
Legacy source: site/spec-content/platform-spec/tooling/manifests-and-lockfiles/workspace-and-lock-contracts/adr/0001-hub-authority/content.md
Source SHA-256: be3de69d53c52480d2d9a280c3d95015699a663f15470169e426811aed536194
Scenario: Conformance exercises Decision
- GIVEN an implementation claims conformance with this capability
- WHEN behavior governed by this contract section is exercised
- THEN every MUST, SHALL, REQUIRED, prohibition, and accepted decision in the section is satisfied
Requirement: CLI lock mutations: Decision [D-TOOL-MAN-0002]
The Beskid standard SHALL enforce the following migrated contract section. Accepted ADR decisions are binding; uppercase requirement keywords retain their BCP-14 meaning.
fetch/lock must use CLI resolver.
Stable ID: BSP-REQ-370E7A9A4EAD
Legacy source: site/spec-content/platform-spec/tooling/manifests-and-lockfiles/workspace-and-lock-contracts/adr/0002-lock-mutations-via-cli/content.md
Source SHA-256: 2b402d2da94d7cf7f022b3c6b390e3b6f38b043c2356980f82bd85632b3e8929
Scenario: Conformance exercises Decision
- GIVEN an implementation claims conformance with this capability
- WHEN behavior governed by this contract section is exercised
- THEN every MUST, SHALL, REQUIRED, prohibition, and accepted decision in the section is satisfied
Informative Source Provenance
The records below preserve migration history and are not normative except where text was extracted into a requirement above.
Source Record: Workspace and lock contracts
Authority: informative provenance
Legacy path: /platform-spec/tooling/manifests-and-lockfiles/workspace-and-lock-contracts/
Source: site/spec-content/platform-spec/tooling/manifests-and-lockfiles/workspace-and-lock-contracts/content.md
SHA-256: 6e25b41663a0ec7d75d49768da0f0c1d8813162a72ac3bf4d54a5b73e92821f4
Migrated source text
<SpecSection title="What this feature specifies" id="what-this-feature-specifies">
`Workspace and lock contracts` defines one operational contract that a newcomer can follow end-to-end: first the model, then execution flow, then strict guarantees, concrete examples, and verification guidance.
</SpecSection>
<SpecSection title="Implementation anchors" id="implementation-anchors">
- Lock/update commands in `compiler/crates/beskid_cli/src/commands/`
- Project and dependency resolution in `compiler/crates/beskid_analysis/src/resolve/mod.rs`
- Workspace tests in `compiler/crates/beskid_tests/src/analysis/pipeline/core.rs`
- Corelib package tests in `compiler/crates/beskid_tests/src/projects/corelib/layout.rs`
</SpecSection>
<SpecSection title="Decisions" id="decisions">
No open decisions. **`D-TOOL-MAN-0001`** (hub authority), **`0002`** (lock mutations via CLI)—see **`adr/`** and the **ADRs** tab.
</SpecSection>
## Decisions
<!-- spec:generate:adr-index -->
No open decisions. Closed choices are normative ADRs under **`adr/`** (`D-TOOL-MAN-0001` … `D-TOOL-MAN-0002`); use the reader **ADRs** tab for expandable detail.
<!-- /spec:generate:adr-index -->
## Articles
<!-- spec:generate:article-index -->
- [Contracts and edge cases](./articles/contracts-and-edge-cases/)
- [Design model](./articles/design-model/)
- [Examples](./articles/examples/)
- [FAQ and troubleshooting](./articles/faq-and-troubleshooting/)
- [Flow and algorithm](./articles/flow-and-algorithm/)
- [Verification and traceability](./articles/verification-and-traceability/)
<!-- /spec:generate:article-index -->
Source Record: Hub authority
Authority: informative provenance
Legacy path: /platform-spec/tooling/manifests-and-lockfiles/workspace-and-lock-contracts/adr/0001-hub-authority/
Source: site/spec-content/platform-spec/tooling/manifests-and-lockfiles/workspace-and-lock-contracts/adr/0001-hub-authority/content.md
SHA-256: be3de69d53c52480d2d9a280c3d95015699a663f15470169e426811aed536194
Migrated source text
## Context
Lock alignment.
## Decision
Hub owns workspace and lock rules.
## Consequences
CLI/IDE defer.
## Verification anchors
resolution tests.
Source Record: CLI lock mutations
Authority: informative provenance
Legacy path: /platform-spec/tooling/manifests-and-lockfiles/workspace-and-lock-contracts/adr/0002-lock-mutations-via-cli/
Source: site/spec-content/platform-spec/tooling/manifests-and-lockfiles/workspace-and-lock-contracts/adr/0002-lock-mutations-via-cli/content.md
SHA-256: 2b402d2da94d7cf7f022b3c6b390e3b6f38b043c2356980f82bd85632b3e8929
Migrated source text
## Context
Resolver bypass risk.
## Decision
fetch/lock must use CLI resolver.
## Consequences
Matches VSC-0002.
## Verification anchors
CLI integration.
Source Record: Contracts and edge cases
Authority: informative provenance
Legacy path: /platform-spec/tooling/manifests-and-lockfiles/workspace-and-lock-contracts/articles/contracts-and-edge-cases/
Source: site/spec-content/platform-spec/tooling/manifests-and-lockfiles/workspace-and-lock-contracts/articles/contracts-and-edge-cases/content.md
SHA-256: 42d5a92739decad9a38d9230e725aa207677eace977d6ccb463a56013e4a8cb7
Migrated source text
## Contracts
- **Lock header version** — Only `# Project.lock v1` is accepted; unknown headers **must** error.
- **Required lock fields** — Each dependency entry **must** include materialized and source roots for replay.
- **Registry alias resolution** — Unknown aliases **must** fail at fetch time with actionable messages.
- **Workspace member integrity** — Every `member` path **must** resolve to a `Project.proj` under the workspace root.
- **Reproducible materialization** — `materialized_root` paths **must** be stable for a given lock file on the same machine class (OS-specific subtrees allowed when documented in compiler feature).
## Edge cases
| Case | Behavior |
| --- | --- |
| Missing lock under strict CLI policy | `build`/`run` fail before compile |
| Stale lock after manifest edit | `lock`/`update` rewrite; LSP invalidates compilation cache |
| Partial fetch failure | Unresolved dependency policy (`Warn` vs `Error`) controls whether graph proceeds |
| Duplicate dependency names | Validation error during graph build |
| Corelib-only path fallback | Implicit `Std` edge omitted for corelib workspace shards to avoid cycles |
## pckg integration
Published workspace bundles on the registry use server-side validation in `compiler/crates/beskid_pckg_server/Services/Workspace/` (`WorkspacePublishService`, `WorkspacePackageManifest`). Client-side `beskid pckg` commands **must** emit manifests compatible with those validators before upload.
Source Record: Design model
Authority: informative provenance
Legacy path: /platform-spec/tooling/manifests-and-lockfiles/workspace-and-lock-contracts/articles/design-model/
Source: site/spec-content/platform-spec/tooling/manifests-and-lockfiles/workspace-and-lock-contracts/articles/design-model/content.md
SHA-256: 938767a15964abbba3da129843b2ebf16b6df33ee0c6518b9eb68b8b50b2285b
Migrated source text
## Purpose
Tooling-facing contract for **`.bws`** workspace manifests, per-member **`.bproj`** graphs, and **`Project.lock`** v1 files that pin materialized dependency roots. The compiler realizes graph resolution and lock sync in `beskid_analysis::projects`; this article defines what operators and CLI commands may assume without restating full manifest key tables.
## Artifacts
```mermaid
flowchart TB
WS[CoreLib.bws]
PM[Member *.bproj]
Lock[Project.lock v1]
Mat[Materialized package roots]
WS --> PM
PM --> Lock
Lock --> Mat
Mat --> Graph[Project DAG + CompilePlan]
```
| Artifact | Role |
| --- | --- |
| **`.bws` workspace manifest** | Declares `member` projects, registry URL aliases, optional dependency overrides; legacy **`Workspace.proj`** → **E1895** |
| **`.bproj` member manifest** | Declares dependencies, targets, project kind (`Host`, `Mod`, `Template`, `Aggregate`) — see **[Project manifest contract](/platform-spec/tooling/manifests-and-lockfiles/project-manifest-contract/)** |
| **`Project.lock`** | Records `root_manifest`, resolved dependency names, `manifest` paths, `source_root`, `materialized_root` |
## Workspace block and extras
Known fields on `workspace { }`: **`name`**, **`resolver`** (default `v1`). Additional keys land in **`workspace.extras`**.
| Extra key | Semantics |
| --- | --- |
| **`defaultTestMember`** | Member label selected when resolving a workspace path without `--workspace-member` and without a source path under a member tree |
| **`schema`** | Informational workspace schema id (for example `beskid.workspace.v1`); not a compiler graph input |
Example:
```text
workspace {
name = "corelib"
resolver = v1
schema = "beskid.workspace.v1"
defaultTestMember = "corelib_tests"
}
```
## Member blocks and merged metadata
Each `member "<label>" { path = "..." }` block **must** include a unique label and **`path`** relative to the workspace root. Keys other than **`path`** are stored in **`member.extras`**—publish and editor metadata merged from the workspace manifest, **not** a substitute for the member's `.bproj`:
| Extra key | Role |
| --- | --- |
| **`package`** | Registry package id for the member (may differ from `.bproj` `name`) |
| **`description`** | Human-readable summary for registry UI and docs |
| **`category`** | Coarse kind hint (for example `Library`, `Test`) |
| **`tags`** | Search/filter tags (Bsol bracket list or repeated assignments per Bsol profile) |
```text
member "foundation" {
path = "packages/foundation"
package = "corelib_foundation"
description = "Low-level primitives shared by Beskid corelib workspace packages."
category = "Library"
tags = ["corelib", "foundation", "beskid"]
}
```
Member selection when `--workspace-member` is omitted: longest-prefix match of the input path against member roots, then **`defaultTestMember`**, then the first declared member.
## Resolution rules surface
`WorkspaceResolutionRules` (in `resolver.rs`) carries:
- `workspace_root` and `workspace_members`
- `overrides_by_dependency` (version pins)
- `registry_aliases` and `registry_urls` for `beskid fetch` / registry client
Registry dependencies resolve through **`beskid_pckg`** HTTP flows; path dependencies normalize under the consumer project root.
## Lockfile semantics (tooling view)
- Header **must** be `# Project.lock v1`.
- Each dependency line **must** include `name`, `manifest`, `project`, `source_root`, `materialized_root`.
- `beskid lock` synchronizes lock content from the current `CompilePlan` (`sync_project_lockfile` in `workflow.rs`).
- LSP may replay `effective_roots_from_lockfile` when a prepared workspace cache is absent.
## Workspace readme
The `workspace` block **may** include `readme = "relative/path.md"` with the same publishing behavior as project readmes (pack → root `README.md` in `.bpk`). Details live under **[Project manifest contract — design model](/platform-spec/tooling/manifests-and-lockfiles/project-manifest-contract/design-model/#package-readme-readme)**.
## Implementation anchors
- Lock format + sync: `compiler/crates/beskid_analysis/src/projects/workflow.rs`
- Graph resolver: `compiler/crates/beskid_analysis/src/projects/graph/resolver.rs`
- Member selection: `compiler/crates/beskid_analysis/src/projects/manifest_resolve.rs`
- CLI: `compiler/crates/beskid_cli/src/commands/lock.rs`, `fetch.rs`, `update.rs`
- Lock replay: `compiler/crates/beskid_analysis/src/projects/assembly/roots.rs`
Source Record: Examples
Authority: informative provenance
Legacy path: /platform-spec/tooling/manifests-and-lockfiles/workspace-and-lock-contracts/articles/examples/
Source: site/spec-content/platform-spec/tooling/manifests-and-lockfiles/workspace-and-lock-contracts/articles/examples/content.md
SHA-256: dcf36d39d4514a9ed62ba689ddca5eedba5733c25723aef7b6c80db63c7e94a0
Migrated source text
## Minimal workspace manifest
```text
workspace {
name = "demo-workspace"
}
member {
id = "app"
path = "apps/demo/Project.proj"
}
registry {
default = "https://pckg.example/api/"
}
```
Run `beskid lock --project apps/demo/Project.proj --workspace-member app` when multiple members exist.
## Lock file excerpt
```text
# Project.lock v1
root_manifest = apps/demo/Project.proj
project_name = demo-app
dependency {
name = corelib_foundation
manifest = .beskid/packages/corelib_foundation/Project.proj
project = corelib_foundation
source_root = .beskid/packages/corelib_foundation
materialized_root = .beskid/packages/corelib_foundation
}
```
Exact paths depend on fetch layout; treat materialized roots as opaque but stable for a given lock revision.
## CI sequence
```bash
beskid fetch --project apps/demo/Project.proj
beskid lock --project apps/demo/Project.proj
git add apps/demo/Project.lock
beskid build --project apps/demo/Project.proj
```
Commit the lock when reproducibility is required; use `update` locally when bumping dependency ranges in manifests.
Source Record: FAQ and troubleshooting
Authority: informative provenance
Legacy path: /platform-spec/tooling/manifests-and-lockfiles/workspace-and-lock-contracts/articles/faq-and-troubleshooting/
Source: site/spec-content/platform-spec/tooling/manifests-and-lockfiles/workspace-and-lock-contracts/articles/faq-and-troubleshooting/content.md
SHA-256: 5b207151650484dd7793d862dc707300b976fbb9963c6894555a9e813a3a7d9c
Migrated source text
## `Project.lock` out of date
Run `beskid lock` or `beskid update` from the project root. If manifests changed on another branch, merge lock conflicts explicitly—do not hand-edit `materialized_root` without understanding fetch layout.
## Registry alias not found
Add the alias to `Workspace.proj` `registry { }` blocks or use the `default` URL. Verify `beskid pckg` config points at the same host (`PckgClientConfig`).
## Member project not in graph
Check `member.path` is relative to the workspace root and ends with `Project.proj`. Typos in `member.id` break `--workspace-member` CLI flags.
## LSP uses wrong dependency roots
Delete `.beskid` cache only when documentation recommends it; otherwise refresh lock and rescan workspace. Ensure focused project matches the lock’s `root_manifest`.
## Workspace publish rejected by pckg
Server validators (`WorkspacePackageManifest`, `PackageArtifactValidator`) enforce readme, docs, and member consistency—align local `beskid pckg pack` output with **[pckg client contract](/platform-spec/tooling/registry-client/pckg-client-contract/)** before retrying upload.
Source Record: Flow and algorithm
Authority: informative provenance
Legacy path: /platform-spec/tooling/manifests-and-lockfiles/workspace-and-lock-contracts/articles/flow-and-algorithm/
Source: site/spec-content/platform-spec/tooling/manifests-and-lockfiles/workspace-and-lock-contracts/articles/flow-and-algorithm/content.md
SHA-256: 19de0968582bd68e46b4fe974bb42695d3808a4f648b24bfe2c1877104f83314
Migrated source text
## `beskid lock` flow
```mermaid
sequenceDiagram
participant User
participant CLI as beskid lock
participant Analysis as beskid_analysis
participant Disk as Project.lock
User->>CLI: --project / member
CLI->>Analysis: resolve graph (Warn on unresolved)
Analysis->>Analysis: build CompilePlan + lock entries
Analysis->>Disk: sync_project_lockfile
CLI-->>User: synchronized message
```
1. Resolve workspace member or standalone `Project.proj`.
2. Walk dependencies (path + registry), applying workspace overrides.
3. Materialize or verify package roots per policy.
4. Serialize `ProjectLockfileV1` beside the root manifest.
5. Skip write when on-disk lock already matches expected content.
## `beskid fetch` / `beskid update`
- **`fetch`** resolves registry aliases to URLs (`registry_urls`), downloads packages via **`beskid_pckg`**, and lays out materialized roots referenced by future lock lines.
- **`update`** reapplies resolution with a stricter unresolved policy (typically failing on drift) and refreshes materialized trees.
Both commands **must** share graph construction with `lock` so CI and local machines produce comparable `materialized_root` paths.
## LSP lock replay
When the language server lacks an in-memory prepared workspace:
1. Read `Project.lock` from the project root.
2. `effective_roots_from_lockfile` seeds dependency manifest paths.
3. Subsequent analysis uses the same roots until invalidation.
## Corelib path injection during resolve
While walking the DAG, host projects without explicit `Std` receive implicit corelib path attachment (`default_corelib_dependency_path` in `resolver.rs`). Workspace shards under `compiler/corelib/packages/*` **must not** create cyclic back-links to the aggregate `beskid_corelib` package.
## Tests
`compiler/crates/beskid_tests/src/analysis/pipeline/core.rs` and `projects/corelib/layout.rs` exercise workspace + lock layouts.
Source Record: Verification and traceability
Authority: informative provenance
Legacy path: /platform-spec/tooling/manifests-and-lockfiles/workspace-and-lock-contracts/articles/verification-and-traceability/
Source: site/spec-content/platform-spec/tooling/manifests-and-lockfiles/workspace-and-lock-contracts/articles/verification-and-traceability/content.md
SHA-256: 2c4a3531601ade798e7640a3340e4863c7512c5c71cac568fcf5cba712c661fa
Migrated source text
## Compiler tests
| Topic | Path |
| --- | --- |
| Pipeline + lock sync | `compiler/crates/beskid_tests/src/analysis/pipeline/core.rs` |
| Corelib workspace layout | `compiler/crates/beskid_tests/src/projects/corelib/layout.rs` |
| Lock parse errors | `projects/workflow.rs` unit tests |
## pckg server tests
| Topic | Path |
| --- | --- |
| Workspace publish | `compiler/crates/beskid_pckg_server/tests/Integration/WorkspacePublishIntegrationTests.cs` |
| Manifest metadata | `compiler/crates/beskid_pckg_server/tests/Unit/PackageManifestMetadataReaderTests.cs` |
| Artifact validation | `compiler/crates/beskid_pckg_server/tests/Unit/PackageArtifactValidatorTests.cs` |
## Traceability
Tooling articles describe operator-visible contracts; compiler feature pages own diagnostic code bands for lock parse failures. Changes to `Project.lock` v1 **must** update both sides and bump any fixture locks in tests.
## CI
Superrepo and `compiler` CI run workspace tests on every graph change. Website spec verify: `cd site/website && bun run verify:trudoc -- --preset ci` after MDX edits.