Versioned repository mounts
Dokumentace/Pracovní kopie a projekty
Versioned repository mounts8 min čtení

Versioned repository mounts

Zdrojová dokumentace je zatím dostupná v angličtině. Ovládání čtečky odpovídá zvolenému jazyku.

Na této stránce

A repository can compose independent repositories as virtual directories. The parent owns its files and the mount declarations; each child retains its own history, permissions, locks, working copy, and submissions. This first version supports one level of mounts through the same server endpoint, including a gateway.

Declare and select mounts

Submit declarations in the parent's .scribe/config.toml. No second manifest or lockfile is needed. For example, the fictional orbit-game repository can pin its shared engine separately from its large art-source repository:

toml
version = 1

[mounts.engine]
path = 'engine'
repository = '01010101010101010101010101010101'
branch = '02020202020202020202020202020202'
revision = '42'

[mounts.art]
path = 'art-source'
repository = '03030303030303030303030303030303'
branch = '04040404040404040404040404040404'
revision = '18'

[profiles.programmer]
include = ['game', 'engine']

[profiles.artist]
include = ['game', 'engine', 'art-source']

Replace the example IDs and revisions with actual repository/branch identities and accepted revisions. IDs are 32 lowercase hexadecimal digits. The revision is a canonical unsigned decimal string, preserving all 64 bits through TOML, TypeScript, and JSON. Pins never float with the child branch head. Branch IDs survive renames. The declaration is read at the parent's selected branch/revision, not from unsubmitted local edits or exclusively from main.

Mount names follow profile-name rules. A configuration declares at most 64 mounts; a 65th is rejected even when the file stays within the 64 KiB config limit. Paths follow portable canonical path rules. Overlapping paths, case/normalization collisions, config-file overlaps, and self-mounts are rejected. A mount path cannot name a .scribe directory (in any letter case) or lie inside the root .scribe/. The server rejects parent files at, above, or below mount paths, including merges. When adding a mount over existing parent files, remove those parent files in the same submission or an earlier one. Accepted history remains unchanged.

Retiring a mount is judged the same way, against the mounts accepted at the parent change: one change may retire [mounts.NAME] or write files at its path, never both. Collaborators still have the child workspace on disk at that path when the change arrives, so the retirement has to be accepted history before anything may take the path. Whatever the server accepts, a client refuses to displace a nested workspace and reports the path as failed instead; move the child workspace out to unblock it.

Profiles select mounts using the existing inclusion prefixes. Selecting engine selects that child; selecting engine/Source creates a child view for Source. A broader inclusion or the full repository selects every mount it covers. Unselected mounts require no target requests and transfer no target content.

sh
scribe workspace create orbit-game ./work --profile programmer
cd work
scribe workspace mounts
scribe sync

workspace mounts lists declarations at the parent branch head and indicates selection using the current saved view. It does not probe child access. sync reports each mount as ready, excluded, unavailable, or retained; JSON includes the name, path, repository ID, branch ID, revision string, and detail.

Add, change, or remove a mount

Mount declarations are ordinary parent configuration edits. To add one, obtain the child's stable repository and branch IDs and an accepted revision. scribe repo info CHILD shows its repository and main-branch IDs; scribe branch list inside an existing child workspace shows its other branches. Preserve the existing config's profiles, lock rules and other declarations while adding the new [mounts.NAME] table. For an already tracked config:

sh
scribe edit .scribe/config.toml
# Add the mount table in your editor, using actual IDs and a quoted revision.
scribe submit -m "Mount the shared engine"
scribe project sync

If the configuration does not exist yet, create it with version = 1 and the mount table (run mkdir -p .scribe first), then use scribe add .scribe/config.toml before submitting. Include the config in your workspace view before editing it.

To change only a revision, scribe project update NAME --revision N stages the pin under normal lock rules; review and submit it. To change a path, repository, or branch, edit the table and submit. To remove a mount, remove its entire table and submit the config. Existing child directories are retained, as described under edit, update, and recover.

On an authenticated server, advancing a pin is Writer work; adding, removing, renaming, or retargeting a mount on main requires a Maintainer, because it decides where every collaborator's nested workspace lives.

Each shared declaration or pin change becomes effective through a parent commit. Editing a file or staging a pin alone does not publish it. Profiles and local view overrides can select fewer mounts without changing their shared pins or creating a parent commit. Different parent branches and historical revisions can contain different mount sets and pins.

The web interface offers Add mount, edit, and remove actions, subject to the same permissions: a Writer can advance a pin, and changing the topology on main needs a Maintainer. The editor keeps the revision pin editable for Writers on main, disables topology controls with an explanation, and checks permissions again when preparing the change. Writers can edit topology on other branches; accepting those changes onto main requires a Maintainer. Permission refusals preserve entered values and refresh the displayed permissions; uncertain submissions retain their original operation ID for outcome recovery. Those actions submit the same parent configuration changes; they do not edit or delete the child repository. See web mount editing.

Permissions and failure

Reading A's declaration never grants access to B. A reader can see the declared path, IDs, and pin because those bytes belong to A. Each selected child's metadata, registration, and downloads use B's repository identity and the caller's own credential. A child uses its own active lock policy for edits and submissions.

Sync resolves metadata for the entire selected composition before applying files. If selected B is inaccessible, accessible parent files and other selected mounts can still sync. The overall result is incomplete and the mount is reported as unavailable. Once every parent entry is applied, the parent watermark publishes and its journal closes: an unreadable child never leaves the parent workspace in a permanent "sync in progress" state that would block moves, merges, or view changes. The failure stays recorded for the parent, and the next scribe sync retries the mount. Errors use the same unavailable message for missing and forbidden targets. Grant access and rerun scribe sync, or cancel/change the selection to exclude the mount. Unfinished child downloads stay in that child's own journal and resume there.

Revocation denies future remote operations according to the existing authorization policy. Downloaded files and local edits are preserved. Access cannot retract already downloaded content. Parent access grants no child write permission.

Edit, update, and recover

An initialized child is an independent workspace with its own local state in engine/.scribe/_state/. Commands run inside it address the child repository. Parent discovery and recursive add, including --force, do not enter child workspaces. Explicit parent operations across a recorded mount boundary are refused.

Ordinary parent files, such as game/src/foo.cpp, remain editable with scribe edit and scribe submit. A directory becomes an independent repository only when declared as a mount; the name game does not make it one. See the practical walkthrough for single-repository edits and changes that span the parent and engine.

Use project orchestration for root-level status, explicit pin staging, and recoverable child-first publication. For independent submissions, submit child changes first, then stage the parent pin with scribe project update and submit that config change. There is no cross-repository atomic submission. A child sync uses its saved mount pin by default; an explicit revision can be used when working on the child independently. Parent sync refuses to silently rewind child work submitted after the last applied pin; update the parent pin or reconcile the child explicitly. Local modifications, opened work, and untracked files block repinning or changing the child view. Interrupted child downloads remain resumable.

Deselecting or deleting a mount declaration retains its local directory. The parent no longer contacts that child and continues to protect the directory from parent scans. Removed declarations appear as retained in sync output. To reclaim space or reuse a path, explicitly move the retained workspace outside the parent once its work is safe. Changing repository or branch identity at an occupied mount path also requires preserving that existing workspace elsewhere first. Historical parent sync cannot overwrite files inside a physical child workspace.

A child with its own mount declarations is reported as unsupported before its files are synced; recursive mounts and cycles are not traversed. Mounting an existing repository does not create a fork. Cross-repository forks are outside the active roadmap; use branches within the child for its customizations.

SDK and web

The TypeScript SDK exposes client.mounts(parent, selection) for declarations without target probes and client.resolveMount(declaration) for an independently authorized target check. The latter returns available with repository/branch names, or unavailable without those names. Operational failures propagate.

HTTP GET /api/v1/repositories/:repo/mounts?branch=…&revision=… returns { mounts: [...] }, with the same pin strings and permission states. It accepts browser/public reads and PATs with contents:read. Responses use Cache-Control: private, no-store; target authorization is not served from the parent tree cache. Parent authorization failure remains an HTTP error.

The web browser shows virtual mount cards separately from parent files. Available cards link into the child at its branch ID and pinned revision using the current namespace routes. Unavailable cards have no link and reveal no fetched target name. Following a link performs the child's normal access checks again. The server still enforces mount boundaries for edits made through the SDK or web UI.

Zdroj docs/repository-mounts.mdVerze 2b8dcccb