Skip to content

Workspace presets: three hosted Machine profiles, derived from the handover, adopting an existing Folder - #13

Merged
immakermatty merged 7 commits into
mainfrom
claude/DEV-6594-presets
Sep 22, 2026
Merged

immakermatty merged 7 commits into
mainfrom
claude/DEV-6594-presets

Conversation

@immakermatty

@immakermatty immakermatty commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

Motivation

Real Machines are being handed over with /etc/lazurio/lazurio.machine.json, and the Principal decided (2026-09-22) how the Lazurio Folder on them is configured: three hosted workspace presets with exact names, derived from the handover instead of chosen from a free list, an immutable/mutable split in the Launchpad, and folder-init that adopts the Folder real Machines already have instead of demanding an empty layout. Nothing was implemented under the earlier names (hosted-private, hosted-team), so they are renamed without compatibility.

What changes

  • Schema re-pin — src/machine/lazurio-machine.v1.schema.json is byte-for-byte Machines v0.12.59 (commit dd0dbfaab4807f197f5f65f102fc74bd22496499): a oneOf with the Organization workspace-VM branch and the new personal-VM branch (machine.kind "personal-vm", owner {kind "principal", github_login, github_id}, host {kind "provider-estate", …}, required tailnet). MachineContext is the union; conformance tests cover both branches and every cross-branch mix.
  • Presets as data (src/folder/presets.ts): local, hosted-personal, hosted-organization-personal, hosted-organization-team — name, version 1, fixed axes, communication defaults, Personalspace policy, provider identity mode, surfaces, supervision. Validated as a whole; unknown name/version/combination fails before any mutation.
  • Derivation and allow-list: personal-vm → hosted-personal; workspace-vm with owner.team → hosted-organization-team; without → hosted-organization-personal; workstation → local. A personal VM never takes an Organization preset and vice versa. lazurio machine folder-init [--preset] records the derived preset by default and an allowed override as an explicit choice.
  • Preferences schema 2: preset reference (name, version, selection: derived|explicit) + immutable Machine binding projected from the handover (null on a workstation) + profile, under the existing revision discipline — the local state a future "set preset reference" typed request needs.
  • One change path: CLI profile-preview/profile-update --preset and the Launchpad send {expectedRevision, preset?, profile} to the same preview → apply use case. preset-not-allowed and preset-composition are blocked plans, never writes.
  • Adoption: the Folder owns exactly AGENTS.md and .lazurio/; organizations/ and personalspace/ may be non-empty and are never traversed, listed beyond existence, moved or written; launchpad.gen3.json/launchpad.gen3.local.json tolerated by name; any other top-level entry fails closed naming it. Organization presets accept the empty personalspace/ that Machines' workspace_baseline precreates and refuse a used one without deleting anything. Re-run → already-adopted, nothing changed.
  • Rendered AGENTS.md: short and factual, from preset + binding + profile — which Machine, whose, who the Principal is here, Personalspace boundary, where Organization repos live, identity mode, how work is done, pointer to Organization AGENTS.md and the Lazurio root rules. A relationships section renders only when the binding carries one.
  • Launchpad: "This Machine" shown read-only; preset (from the allow-list) and locale/detail/coordination changeable; access/purpose are no longer controls.
  • CLI reporting (found on the native run, fixed here): a group- or world-writable ~/Lazurio, organizations/ or personalspace/ (Ubuntu's default umask 0002) is refused before any mutation as {"kind":"blocked","reason":"folder-directory-shared","entry":…} with exit 2 instead of the generic failure; a wrong machine invocation (--json, duplicate/invalid choice, unknown preset) is a usage error printing the machine help on stderr with exit 2.
  • Docs: workspace-presets.md, machine-handover.md (re-pin, adoption, the exact resident command, JSON shapes, the mode 0755/0700 precondition), F10 amended 2026-09-22, acceptance and references renamed, docs/evidence/presets-linux-arm64-2026-09-22.md.

What deliberately does not change

  • No Dashboard / typed owner request transport — only the local state that makes it possible.
  • No relationships field in the vendored schema (upstream has none); the renderer path exists and is tested with a synthetic binding.
  • No persona (decision 0156 sweep pending); the purpose axis keeps its values until that sweep.
  • No enforcement of access: a preset records context and grants nothing; provider identity, surfaces and supervision are declared for their owners, not enforced by the preset.
  • personalspace/ presence for Organization presets is judged by existence and emptiness only (one opendir read, names never returned) — the minimum that makes the Principal's rule operable on the layout Machines delivers.

Verification

  • bun run check at head 41f0461 under a temporary HOME/XDG_*: biome clean, tsc clean, 496 pass / 4 skip / 0 fail (500 tests, 69 files), public-policy guard, proof build and smoke pass. No new dependencies.
  • Table-driven tests: derivation/allow-list, whole-composition validation, adoption (used work dirs, legacy files, foreign entries by name, group-writable directories by name, idempotence, personalspace conflict, binding changed, unrecognized state, interruption/resume, concurrency), both handover branches, rendered AGENTS.md snapshots per preset × locale, Launchpad immutable section / preset change / disallowed preset (409), machine usage errors (exit 2 + help).
  • Native run done (2026-09-22, fresh Tart clone of Ubuntu 24.04.4 ARM64, CLI compiled from 6481a4c, root-owned fixture handovers of all three kinds): derived presets, explicit allowed override with cs/technical, adoption of non-empty organizations/ + personalspace/ with launchpad.gen3.json present, already-adopted re-run with AGENTS.md byte-identical and personalspace/ untouched, preset-not-allowed, folder-foreign-entry, folder-personalspace-conflict with nothing deleted — verbatim log in docs/evidence/presets-linux-arm64-2026-09-22.md.

Not proven

  • A real Machines-delivered VM (the native run used fixture handovers written by hand as root).
  • A Launchpad preset change natively on a VM (covered by unit tests only).
  • The two CLI reporting fixes on the VM (they landed after the log was taken; covered by unit tests at head).

🤖 Generated with Claude Code

Note

Add hosted workspace presets, Machine binding, and existing-Folder adoption

  • Adds a fixed catalog of four workspace presets (local, hosted-personal, hosted-organization-personal, hosted-organization-team) with fixed access/purpose axes, personalspace policy, and provider identity, derived from the typed Machine handover context.
  • Bumps FolderPreferences state to schema version 2, adding a validated preset reference and immutable nullable MachineBinding; schema-1 state is now rejected.
  • Changes folder initialization from empty-folder-only to adopting an existing delivered Folder, preserving non-empty work directories and tolerated legacy files while refusing foreign entries, symlinks, shared-writable directories, and organization-preset personalspace conflicts.
  • Updates instruction rendering to template revision base-instructions-2 with a new InstructionSource (preset + Machine + profile), adds localized Machine sections, and replaces the Launchpad access/purpose controls with machine details and a preset selector.
  • Updates the CLI folder-init entrypoint to derive and authorize presets from observed handover, apply preset defaults with optional overrides, and report structured adoption refusals.
  • Risk: retireIncompletePreparation in retire-preparation.ts and validatePreparation in validate-preparation.ts pass a two-field preview object missing machine, which the updated previewFolder parser rejects; these calls throw a validation error instead of producing output.

Macroscope summarized 41f0461.

immakermatty and others added 7 commits September 22, 2026 11:31
…o branches)

The upstream identity schema now has a oneOf with a personal branch:
machine.kind "personal-vm", owner {kind "principal", github_login, github_id},
host {kind "provider-estate", estate_id, custody_repository, record_path} and
a required tailnet identity. Vendored byte-for-byte from tag v0.12.59
(commit dd0dbfaab4807f197f5f65f102fc74bd22496499); MachineContext is a union
of the two branches and conformance tests cover both with real-shaped
synthetic fixtures, including every cross-branch mix the schema refuses.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…dover

Presets are data in src/folder/presets.ts: local, hosted-personal,
hosted-organization-personal and hosted-organization-team, each a whole
composition (fixed access/purpose axes, communication defaults, Personalspace
policy, provider identity mode, surfaces, supervision). The preset is derived
from the typed handover fields only: personal-vm -> hosted-personal,
workspace-vm with owner.team -> hosted-organization-team, without ->
hosted-organization-personal; a workstation is local. A handover allows
exactly the presets of its Machine kind; a personal VM never takes an
Organization preset and vice versa.

Preferences schema 2 stores the preset reference (name, version 1, whether it
was derived or an explicit choice) and the immutable Machine binding projected
from the handover (null on a workstation). The whole composition is validated
on every parse. The profile-change request is now {preset?, profile} on the
same preview -> apply path for CLI and Launchpad; a disallowed preset or a
profile whose fixed axes disagree with the preset is a blocked plan, never a
write. `lazurio machine folder-init` derives the preset by default, accepts
--preset within the allow-list and takes the preset's communication defaults.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…mpty layout

folder-init no longer demands an empty organizations/ and personalspace/.
The Folder owns exactly AGENTS.md and .lazurio/ at the top level; the two
work directories may hold content and are never traversed, listed beyond
existence, moved or written; launchpad.gen3.json and launchpad.gen3.local.json
are tolerated by name; any other top-level entry fails closed naming it.
An Organization preset (Personalspace never present) accepts the empty
personalspace/ that Machines precreates and refuses a used one without
deleting anything. A re-run on an adopted Folder reports already-adopted and
changes nothing; a different handover or unrecognized state is refused by
name. Refusals surface from `lazurio machine folder-init` as blocked results
with reason, entry and a next step.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ding

The generated document is short context for every agent starting inside the
Folder: which Machine this is and whose (owner, team, tailnet node, host),
who the Principal is here (owner of a personal VM, the assigned operator of a
work VM, the connected Team member on a shared team VM), the Personalspace
boundary (present and intimate / never present), where Organization
repositories live, the provider identity mode, and how work is done (Draft
in a worktree and PR, the Organization's AGENTS.md and the Lazurio root
rules). A relationships section renders only when the recorded binding
carries one; upstream has no such field yet. Template revision
base-instructions-2; snapshots per preset and locale.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… one flow

/api/profile returns the preset reference, the presets the recorded handover
allows, the immutable Machine binding and the profile. The panel renders the
binding as text only, offers the preset from the allow-list and the three
communication axes; access and purpose are no longer controls, they belong to
the preset. Preview and apply take {expectedRevision, preset?, profile} on
the same core use case as the CLI; a preset outside the allow-list is a 409
blocked plan and a request carrying a Machine binding is refused.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…e-pin v0.12.59

workspace-presets.md records the implemented local preset model (names,
derivation and allow-list, immutable/mutable split, storage, adoption) next to
the typed-request direction that stays unimplemented. machine-handover.md
records the byte-for-byte re-pin to Machines v0.12.59 (commit dd0dbfa), both
schema branches, adoption instead of the empty layout, and the exact command a
Machines resident role calls after handover with its JSON result shapes.
Decision F10 is amended (2026-09-22, accepted by the Principal); F2,
acceptance, ARCHITECTURE and the remaining references use the new names.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ptions as usage errors

Found on the native Linux ARM64 run of 2026-09-22 (docs/evidence). Ubuntu's
default umask 0002 leaves a hand-created ~/Lazurio and its children 0775; the
ownership rule refused correctly but reported only the generic "Folder
operation failed" with exit 1. folder-init now refuses before any mutation
with {"kind":"blocked","reason":"folder-directory-shared","entry":…} and exit 2
for the Folder root, organizations/ and personalspace/, and the resident
precondition (mode 0755 or 0700) is documented. An unknown or malformed
option is a MachineUsageError: the machine help on stderr and exit 2, not a
failed Folder operation. The evidence file records the setting, the verbatim
log and both defects as found-and-fixed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@immakermatty
immakermatty marked this pull request as ready for review September 22, 2026 10:05

@agentrozjedemeai agentrozjedemeai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@immakermatty

Exact-head QA: APPROVED

Reviewed SHA: 41f04611f0cbe4791d7f303e90f2ffeecedfbf8b

Evidence:

  • The requested decision contract matches the implementation: handover-only derivation, the personal/workstation versus Organization allow-list boundary, explicit Organization-preset selection, and whole-composition validation are enforced in the state and shared profile-change path.
  • Folder adoption treats only AGENTS.md and .lazurio/ as owned. The implementation and focused tests preserve existing work in organizations/ and personalspace/, tolerate the two named legacy launchpad files, and refuse foreign, used-Personalspace, symlink, changed-layout and shared-directory cases before mutation.
  • Schema 1 preferences are refused; schema 2 stores a validated preset reference, immutable Machine binding and profile. The Launchpad exposes the binding read-only and sends only {expectedRevision, preset?, profile} through preview then apply. Local browser smoke exercised an allowed Organization preset change plus all three communication axes; revision advanced from 1 to 2 while the binding stayed unchanged and access/purpose remained absent from the controls.
  • The vendored schema is byte-identical to HumanAndMachine-ai/Machines commit dd0dbfaab4807f197f5f65f102fc74bd22496499; its SHA-256 is 2dc6dd3dd3b62c35e2ece4d6b48f4ea65d6e2f9f838db1e684a7628a9b7e9da1. GitHub Release v0.12.59 targets that same commit.
  • Local focused gate: 79 pass / 0 fail across handover, preset, state, change-profile, Launchpad and Machine-context suites. Full bun run check: 496 pass / 4 platform skips / 0 fail; lint, TypeScript, public-policy guard, proof build and proof smoke passed.
  • CI was rechecked on this exact SHA: check (ubuntu-24.04) and check (macos-14) are SUCCESS. No active review threads.
  • The native Ubuntu ARM64 evidence accurately distinguishes its fixture-handover scope from unproven real-Machines, native-Launchpad and post-log reporting-fix claims.

Conclusion: the requested local preset-model and existing-Folder adoption scope is coherent with F10 and has reproducible exact-head evidence.

@immakermatty
immakermatty merged commit 1208086 into main Sep 22, 2026
3 checks passed
@immakermatty
immakermatty deleted the claude/DEV-6594-presets branch September 22, 2026 10:33
immakermatty added a commit that referenced this pull request Sep 22, 2026
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

@agentrozjedemeai agentrozjedemeai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 — handover recovery přijme nový cizí top-level vstup

resumeInitialization u handoveru ověří pouze zaznamenané identity root / organizations / případně personalspace přes verifyHandoverLayout (src/folder/resume-initialization.ts:102–103, src/folder/handover-layout.ts:129–136). Nevolá však znovu requireAdoptableLayout a tedy před dalším zápisem nevynutí top-level allow-list.

Reprodukce na tomto exact headu:

  1. vytvořím validní organizační handover Folder;
  2. initializeHandoverFolder přeruším na checkpointu journal;
  3. do rootu Folderu zapíšu stray.txt;
  4. resumeInitialization vrátí { "kind": "recovered", "revision": 1 }, vytvoří AGENTS.md a stray.txt ponechá.

To odporuje explicitnímu kontraktu, že jakýkoli jiný top-level vstup Folderu initialization zastaví a pojmenuje. Zde recovery dále zapisuje do Folderu, který by fresh handover odmítl.

Prosím doplňte při handover recovery před jakýmkoli doplněním výstupu/archivací stejnou claimed allow-list kontrolu jako při initializaci — s Personalspace policy odvozenou z již validovaných preferences — a kontraktní test pro přerušení po journalu + nově přidaný stray.txt. Očekávání: named foreign-entry, žádný nový AGENTS.md ani jiný recovery zápis.

Lokálně jinak prošlo bun run check, focused handover/recovery 35/35, plná sériová sada 496 pass / 4 explicitní platformní skip / 0 fail a git diff --check; právě proto je třeba přidat chybějící regresní scénář.

immakermatty added a commit that referenced this pull request Oct 18, 2026
…l help states the data mount, the candidate smoke runs again (#487)

* Release: the candidate smoke accepts the preset a new Folder states

`scripts/smoke-candidate.ts`, the documented check of a locally built
candidate (docs/release-cycle.md, Build), expected `folder-init` to answer
exactly `{kind, revision}`. Since the workspace presets (#13) the answer
also names the preset, so the smoke failed on every candidate before it
reached the Launchpad. It now checks the kind, the revision and that a
preset is named; the rest of the smoke is unchanged and passes on a
candidate built from main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Content: install help and the parity table say a module's data mount follows it

Since #476 (B7 amendment of 2026-10-17) `lazurio organization install`
clones a module's declared data mount (repository_db_mount) on its declared
branch when the module is there after the install. Its help still said
repository databases are left out, and the parity table's row 15 said the
same. Both now state the shipped behavior: the data mount follows its
module, other database slots and infra stay out. Text only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Launchpad: a tile whose Open was refused shows the reason on the overview

Opening a workstation module from its tile starts it first. When the
start was refused (data-mount-denied, required-slot-missing, a missing
secret, …) the page said "<name> could not be opened. Its overview says
why." and showed the overview, but the overview only read the status
(Stopped) and never said why: the reason appeared only after pressing
Start there. With #476 a missing data mount is now the most common such
refusal.

The refused start's sentence is now handed to the overview it navigates
to and shown there exactly as after Start, and a refused start ends at
once instead of polling the status first. Nothing changes for a start
that succeeds, is pending or for a hosted Environment's tiles.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Launchpad: a refused Open from the overview itself shows its reason there too

Pablo's review of #487: the refusal reached the overview only when another module's overview was shown before; an Open from the same overview (its Open button or its favourite) left "Stopped" while the notice promised the overview says why. The reason is now put into the matching lifecycle whether it is fresh or already shown (lifecycleShown), taken once; another module's refusal stays for that module. Covered by tests/catalog-panel-lifecycle.test.ts (home tile, same overview, unrelated refusal).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tests: a recorded duration for the new catalog-panel lifecycle test

Without an entry the time-balanced shard split moved files between the two CI shards and exposed the known background-GitHub-peek flakiness (#425) three runs in a row on shard 1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Launchpad: the overview's lifecycle state in a module of its own

The test of lifecycleShown imported catalog-panel.ts, the whole Apps panel, into a parallel test worker; since then CI shard 1 failed on unrelated Launchpad tests each run (port in use, settings peeks). The pure state function now lives in src/launchpad/module-lifecycle-state.ts with type-only imports, and the test imports only that.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Launchpad: the overview's lifecycle state in a module of its own (rest of the move)

Completes 507ef1a, which committed only the test's rename: lifecycleShown and its types leave catalog-panel.ts for src/launchpad/module-lifecycle-state.ts (type-only imports), the test imports that module, and tests/timings.json names the renamed test file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
immakermatty added a commit that referenced this pull request Oct 25, 2026
…ermes-agent#17

The fork change that marks a collected sign-in session cancelled under its
lock is now a real pull request, approved for the release with #11, #13 and
#14; F63 named it only generically. The rule itself is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants