Workspace hierarchy delivery
Status: Source candidate, verified in code. The seven container.* operations
(storage migration 35, workspace-containers) and the four project.create.* /
project.rename.* operations (migration 36, project-metadata-previews) exist
in protocol, SDK, Node and CLI. CLI help labels all eleven commands
source_implemented_candidate; the registry's agent exposure table
(LOCAL_NODE_METHOD_EXPOSURE) marks every mutation human-only. Of these, the
Desktop renderer allowlist admits only container.list, container.show and
container.index.list (the cockpit loads its Workspace and Project index
through container.index.list pages); there is no create or rename UI. Planned: acceptance
closure (R0), the naming migration (R1b), repository-free execution (R2),
Workspace-owned Sessions (R3) and client create and rename flows (R9), as
scheduled in the roadmap.
Acceptance stories live in portfolio stories and
dogfood suites.
Identity model
- Workspace (organization) is the
containeraggregate withcontainer_*IDs (maia.container/v1). - Execution Environment keeps the existing execution
workspace.*operations andworkspace_*IDs, which Sessions, receipts and events reference. It is never renamed in place or rewritten into an organizational Workspace. - Browsing Workspaces never calls execution
workspace.selectand never retargets running Sessions. - The later rename (
container.*→workspace.*, executionworkspace.*→environment.*) follows the naming contract and R1b. It does not repeat migration 35 or rewrite IDs (decision).
Container operations
container.list, container.show, container.index.list,
container.create.preview, container.create.confirm,
container.rename.preview and container.rename.confirm.
- Create and rename need a confirmation bound to actor, normalized input, revision, expiry and target. Node owns proofs, validation, compare-and-set, idempotency and receipts.
- Reads are bounded:
limit1–100, optionalafterCursor, optionalnextCursor. Duplicate names are allowed; names are not identities. Rename changes only the container label, never a Project or a filesystem path. container.index.listreads every Workspace with its Projects (ID, name, revision and the root path of the Project's first repository, absent for a metadata-only Project) and each Workspace's Project count, in pages of up to 500 rows: a row is a Project, or a Workspace with none. A Workspace a page cuts continues first on the next page, so a client merges entries by container ID. One indexed query serves a page (a range scan ofcontainersandprojects_container_sequence, stopped atlimit + 1rows), next to aggregate reads for the cursor snapshot; the cursor is signed per actor and snapshot of Workspaces, Projects and repositories, so any of them changing between pages fails withstorage_conflictand the Desktop cockpit restarts its read up to twice (decision).project.registerandsession.createstay wire-compatible. Registration creates its own container (container_plus the Project ID suffix) in the same transaction, through the shared Project insertion path.
Preserving migration rules
Migration 35 (services/node/src/storage/migrations/v030-v039/v035.ts) is the
reference. Any later hierarchy migration, including R1b's, follows the same
rules:
- One additive, checksummed migration. Migration 35 gave each existing Project one container with a deterministic ID and the Project's name; unrelated Projects are never merged, and an empty database stays empty.
- Preserve Project, repository, checkout, Execution Environment and Session IDs, historical receipt and event payloads and Git filesystem identities. Do not rebuild execution tables or change runtime kinds.
- Enforce new relationships with SQLite-safe additive constraints. Migration 35 uses triggers instead of a table rebuild or disabled foreign keys, and a guard table that makes malformed or unresolved historical identities fail closed and roll the migration back.
- Test on disposable real SQLite: empty initialization, populated upgrade, identity and payload preservation, rollback, checksum mismatch, compatible registration, proof expiry and mismatch, conflicting command reuse, replay, stale rename and cancelled preview.
- No live profile migration until these tests and independent review pass and a backup and recovery procedure exists.
Project metadata protocol/SDK handoff (candidate)
Implemented in protocol, SDK, Node (migration 36) and CLI
(maia project create and maia project rename, each two-phase: --input-stdin previews and --confirm-stdin confirms).
Evidence: services/node/src/project-workspace/project-workspace.integration.test.ts,
services/node/src/project-workspace/storage/project-metadata-store.test.ts and
services/node/src/project-workspace/project-workspace.test.ts. R0 must still
close acceptance (current artifact digest, parity and independent review) before
release. The operations are human-only; schema registration is not
authorization. Schemas: packages/protocol/src/project-workspace/effect-schema.ts.
Inputs
- Create preview:
commandId, existingcontainerId, positiveexpectedContainerRevisionandname(Project name rules; duplicate labels allowed). The caller never chooses the Project ID. - Rename preview:
commandId,containerId,projectId, positive ProjectexpectedRevisionandname. - Confirm: the preview input plus
inputDigest(sha256:and 64 lowercase hex digits) and the Node-issued 43-character URL-safeproof.
Results
- Preview: the normalized input,
method, Node-derivedactorId,issuedAtstrictly beforeexpiresAt, the digest and the proof. Create also returns the Node-reservedprojectId. - Confirm:
actorId,commandId,containerId,inputDigest,delivery(neworduplicate), the unchangedProjectshape and a receipt whose actor, command, method, Project target, revision and timestamp match it. Create addsexpectedContainerRevisionandcontainerRevision(exactly expected + 1), with the Project at revision 1. Rename addsexpectedRevisionand advances the Project revision by exactly one. No confirmation returns a proof. The Project record gains no container, repository, directory or readiness field. - Stored proofs bind actor, method, normalized input, command, Project, container, revision and the issue and expiry times. Schemas check syntax and relations only; Node authenticates.
One Node writer
project.registerand metadata create share one transactional Project insertion path (insertProjectRowinservices/node/src/project-workspace/storage/store.ts). Metadata create inserts only the Project row and its membership: no repository, checkout, Execution Environment, path, Session or focus change.- Create validates the container, compare-and-sets and advances its revision once, inserts the reserved Project and stores the receipt in one transaction. Rename checks membership, compare-and-sets only the Project revision and changes only label, revision and update time.
- Both confirmations reconcile an existing receipt before rejecting a consumed
or expired proof. An exact retry returns the original result, even after later
renames; a changed binding fails with
storage_conflict. A missing Project or container returnsresource_not_found. container.showpages membership; itslimitof 100 is a page bound, not a capacity. The reader fetcheslimit + 1rows and signs cursors per actor, method, container and snapshot. Tests cover pages beyond 100 Projects and cursor invalidation after insert or rename.- A metadata-only Project reads through
project.showwith no repositories and is not Session-ready. Repository-free execution needs R2's directory identity, authorization and containment contract; a missing repository never authorizes an ambient working directory.
Imported from Maia OS docs at 3dc3e07