From 3c0c2aa4ffaff069ec698d9158cba44920a1c986 Mon Sep 17 00:00:00 2001 From: pythonlearner1025 Date: Tue, 25 Aug 2026 22:51:25 -0700 Subject: [PATCH 1/2] docs(plans): save a good workspace as a template Audit the path that returns a working workspace setup to a template. Part one keeps nine findings. Each one names the file and the line. Part two commits to one change: the browser workspace-to-template loop. Three independent reviews corrected this document before it landed. A fact check found 23 wrong or stale claims. An adversarial review found 11 blocking holes. A contribution review found no proof obligations. The largest correction is a security fault. The first draft route needed only member access. A viewer already reads raw workspace environment data. So any member could republish another member's secrets to the organization. The route now needs workspace control. The draft omits environment values. Templates now start private. Only an administrator publishes one. A publish review shows the exact startup script and the selected rule. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01KXjajE99TTzesZmjsX6MwJ --- plans/AI-OS-LOOP.md | 378 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 378 insertions(+) create mode 100644 plans/AI-OS-LOOP.md diff --git a/plans/AI-OS-LOOP.md b/plans/AI-OS-LOOP.md new file mode 100644 index 00000000..1f36de1e --- /dev/null +++ b/plans/AI-OS-LOOP.md @@ -0,0 +1,378 @@ +# AI OS loop — save a good workspace as a template + +Audit date: 2026-08-25. Source: `origin/main` at `c6902d0`. + +A template stores a reusable workspace setup. A workspace starts from a template or starts blank. +A recipe combines a template, harness, model, optional effort, and prompt. The root `README.md:28` starts an agent from a company template. +`README.md:30` then asks one developer to publish the improved setup. + +## Part one — findings + +### Gap 1 — no workspace-to-template path + +The create parser accepts `templateId` at `packages/control-plane/core/workspaces.ts:104`. The workspace insert omits it at `packages/control-plane/core/workspaces.ts:489`. +That insert also omits requested repositories. `WorkspaceView` exposes neither value at `packages/schema/src/workspace.ts:27`. +The template menu offers Edit and Delete at `packages/webapp/src/files/TemplatesHome.tsx:100`. The card offers New workspace separately at `packages/webapp/src/files/TemplatesHome.tsx:151`. +Users must rebuild a successful setup from memory. + +### Gap 2 — ready does not mean setup complete + +Phone home marks the workspace ready at `packages/control-plane/core/workspaces.ts:921`. The control plane schedules folder sync later at `packages/control-plane/core/workspaces.ts:935`. +Bootstrap starts repository cloning before phone home at `packages/control-plane/core/bootstrap.ts:748`. Cloning can finish before or after ready. +Users can start work before folders, repositories, environment, or startup work finishes. + +### Gap 3 — setup failures stay hidden + +Broker registration returns `no_broker_capacity` at `packages/control-plane/core/registry.ts:279`. The guest exits successfully at `packages/box/rootfs/usr/local/libexec/blitz-register:47`. +The watcher waits for `broker.json` at `packages/box/rootfs/etc/s6-overlay/s6-rc.d/watch/run:15`. +The create dialog hides a failed template load at `packages/webapp/src/CreateWorkspaceDialog.tsx:99`. The rules picker still shows fallbacks at `packages/webapp/src/AgentRulesPicker.tsx:147`. +Backend failures can look like valid empty states. + +### Gap 4 — viewer sharing breaks its promise + +The dialog promises chat replay at `packages/webapp/src/ShareWorkspaceDialog.tsx:177`. The control plane blocks viewer port `7444` at `packages/control-plane/core/workspaces.ts:735`. +The browser also shows one fixed hold message at `packages/webapp/src/CloudApp.tsx:1742`. +Viewers cannot review the agent chat. + +### Gap 5 — workspaces never stop + +The server defines five phases at `packages/schema/src/workspace.ts:3`. It defines no stopped or resumed phase. +The browser carries unused stop states at `packages/webapp/src/protocol.ts:3`. The quota counts non-destroyed workspaces at `packages/control-plane/core/workspaces.ts:495`. +Users must destroy workspaces to stop cost and release quota. + +### Gap 6 — new organizations start empty + +Organization creation writes only the organization, membership, and session at `packages/control-plane/core/identity/routes.ts:185`. +The create dialog shows both cards at `packages/webapp/src/CreateWorkspaceDialog.tsx:202` and `packages/webapp/src/CreateWorkspaceDialog.tsx:237`. +The recipe screen stops without templates at `packages/webapp/src/files/CreateRecipeScreen.tsx:203`. +New users must design a template before they see one work. + +### Gap 7 — agent rules have no history + +Any active member can write shared rules at `packages/control-plane/core/agent-rules.ts:165`. The PUT overwrites content at `packages/control-plane/core/agent-rules.ts:215`. +The box receives the selected content instead of the platform manual at `packages/control-plane/core/agent-rules.ts:155`. +One edit changes running workspaces and can fork the platform instructions. + +### Gap 8 — recipe triggers have no run end + +`plans/RECIPES.md:149` proposes triggers as future callers. Current `RecipeView` stores configuration only at `packages/schema/src/recipe.ts:11`. +No `plans/RECIPE-TRIGGERS.md` exists in the tree or reachable history. No current design defines run completion, stopping, or retention. +A noisy trigger could consume the organization quota. + +### Gap 9 — template editing leaks or loses changes + +The UI changes folder access before save at `packages/webapp/src/files/CreateTemplateScreen.tsx:243`. A failed save can leave private folders shared. +The PUT applies `agentRuleId` at `packages/control-plane/core/workspace-templates.ts:517`. The edit UI omits it at `packages/webapp/src/files/CreateTemplateScreen.tsx:270`. +Create and delete use separate mutations at `packages/control-plane/core/workspace-templates.ts:423` and `packages/control-plane/core/workspace-templates.ts:609`. +A failure can leave partial template state. + +## Part two — committed workspace-to-template loop + +This plan commits only to the browser loop. +It does not add agent credentials, readiness, lifecycle, rules, or recipe systems. + +### Stored data + +| Area | Committed change | +|---|---| +| Workspaces | Add `creation_source`, `source_template_id`, and nullable `requested_template_repos_json`. | +| Templates | Add `description`, `visibility`, `deleted_at`, `org_share_role`, and publication approval data. | +| Template folders | Add `guest_path`. | +| Template connections | Add explicit `scopes_json`. | +| Publication audit | Add append-only publish, unpublish, and delete records. | + +Future creates write all source fields with the workspace row. Blank creates record `'blank'` and an empty repository list. +Template creates record `'template'`, the selected template, and requested repositories. Those repositories describe requests, not clone results. +The migration leaves existing source fields null. Existing rows never stored these values, so no backfill can recover them. +The migration marks existing templates private and clears their default status. Shared recipe launches wait for publication. +This choice prevents silent publication of old secrets and scripts. +The Worker adds every new field to `BLITZDEV_CONFIG` at `packages/control-plane/scripts/lib/worker-source.mjs:101`. +Each new core file must enter `CORE_MANIFEST` in the same file. + +### Routes and flow + +The server keys each `reviewDigest` to exact template content. + +| Method and path | Authorization | Action | +|---|---|---| +| `GET /workspaces/:id/template-draft` | Workspace owner or organization administrator | Return a redacted draft. | +| `POST /workspace-templates` | Active member session | Create a private template. | +| `PUT /workspace-templates/:id` | Private creator, or administrator | Replace a template and remove publication approval. | +| `GET /workspace-templates` | Active member session | List permitted private templates and published templates. | +| `GET /workspace-templates/:id/publish-review` | Organization administrator | Return exact protected content and a review digest. | +| `POST /workspace-templates/:id/publish` | Organization administrator | Publish reviewed content and record the audit event. | +| `GET /workspace-templates/:id/launch-review` | Allowed template user | Return exact executable content without environment values. | +| `DELETE /workspace-templates/:id` | Private creator, or administrator | Soft-delete the template. | + +The browser requests a draft and shows every derived field. The user edits the draft and creates a private template. +An administrator uses a separate publication review. Workspace creation continues through the existing create route. + +### Authorization and secret data + +The draft route calls `canControlWorkspace`. That helper permits only owners and administrators at `packages/control-plane/core/workspace-access.ts:17`. +Viewer and editor access never permits draft creation. +Current workspace views return raw environment data to any opener at `packages/control-plane/core/workspace-records.ts:94`. +The implementation returns raw environment data only to workspace controllers. +The draft returns environment key names but no values. It copies no value into `draft.environment.env`. +The user enters each wanted value again. The browser shows the exact startup script as executable content. +The organization catalogue returns no environment names, values, or startup script. It returns counts and presence flags only. +The launch review returns the startup script and selected rule content. It never returns environment values. +The publish review shows environment values only to an administrator. The administrator confirms every literal value. +The review digest binds that confirmation to exact content. + +### Executable template governance + +Every new template starts private. Only its creator and administrators can use it. +Only an administrator can publish it to the organization. +The publish screen shows the exact startup script, selected rule, and changes since approval. +The server records the actor, review digest, action, and time. +Any template update removes approval. Any selected rule change makes the stored review digest fail. +The workspace create route then blocks the template. An administrator must review and publish it again. +Only the publish route sets `orgShareRole` or the organization default. +This rule limits automatic sharing to administrators. Template save never changes folder access. +The publish screen requires accessible organization folders or removes them. + +### Connection scope ceiling + +The current code cannot enforce a saved scope ceiling end to end. +Parsing overwrites scoped GitHub at `packages/control-plane/core/workspace-templates.ts:123`. Creation keeps provider names at `packages/control-plane/core/workspaces.ts:435`. +Token pulls request provider defaults at `packages/control-plane/core/connections/mint.ts:541`. +The loop must change the complete path before it exposes scoped template saving. + +1. The parser keeps an existing GitHub entry. +2. The parser expands absent legacy scopes to current provider defaults. +3. New canonical inputs always carry `scopes`. +4. An empty list permits only requests with no named scope. +5. Workspace creation intersects template and caller ceilings. +6. The workspace manifest stores the effective scopes. +7. Box token pulls request those stored scopes. +8. `manifestAllows` remains the final subset check. + +A caller can narrow a template ceiling. A caller cannot widen it. Caller absence keeps the template ceiling. +Repository requests add GitHub defaults only without a GitHub entry. The publish review shows that addition. + +### Folder paths and compatibility + +The server first accepts `folders` with current `folderIds`. It rejects requests that contain both fields. +It keeps `folderIds` until telemetry shows no legacy clients. It converts both forms into one internal shape. +The parser reuses the safety rule at `packages/control-plane/core/files/attachments.ts:43`. +It also rejects duplicate and overlapping paths. +It rejects `shared/agent-usage`, `CLAUDE.md`, and every requested repository target. +Template creation writes `guestPath` instead of current `NULL` at `packages/control-plane/core/workspace-templates.ts:309`. + +### Transactions, deletion, and limits + +One transaction covers create, replacement, default, and audit mutations. The implementation uses `packages/control-plane/core/db.ts:35`. +Delete sets `deleted_at` and clears publication in one transaction. Delete keeps template relations and publication history. +Existing workspaces keep `sourceTemplateId`. The catalogue hides deleted templates, and new workspaces cannot use them. +The draft uses stored configuration only. It does not inspect the workspace disk. +It cannot capture manual packages, files, repositories, project rules, Docker state, Git state, or home dotfiles. +Volumes, SSH keys, box host keys, recipes, and runtime state never enter a template. + +### Type sketches + +These types show the full committed wire shapes. +Markers compare each existing type with `packages/schema/src`. + +```ts +// UNCHANGED against packages/schema/src/environment.ts. +export interface WorkspaceEnvironment { env: Record; startupScript: string | null } +// NEW. +export interface TemplateFolderInput { id: string; guestPath: string | null } +// NEW. Write shape. +export interface TemplateConnectionInput { provider: string; scopes: string[] } +// CHANGED. Add `scopes`. +export interface TemplateConnectionView { provider: string; scopes: string[] } +// CHANGED. Canonical create shape. +export interface CreateWorkspaceTemplateRequest { + name: string; + description: string; + machineTypeId: string; + folders: TemplateFolderInput[]; + connections: TemplateConnectionInput[]; + environment?: WorkspaceEnvironment; + agentRuleId: string | null; + repos: string[]; +} +// NEW. Replacement makes environment handling explicit. +export type TemplateEnvironmentReplacement = + | { action: "keep" } + | { action: "replace"; value: WorkspaceEnvironment | null }; +// NEW. PUT does not reuse the create name. +export interface ReplaceWorkspaceTemplateRequest { + name: string; + description: string; + machineTypeId: string; + folders: TemplateFolderInput[]; + connections: TemplateConnectionInput[]; + environment: TemplateEnvironmentReplacement; + agentRuleId: string | null; + repos: string[]; +} +// NEW. +export interface PublishWorkspaceTemplateRequest { + reviewDigest: string; + orgShareRole: "editor" | "viewer" | null; + isOrgDefault: boolean; +} +// NEW. The catalogue never returns environment contents. +export interface TemplateEnvironmentSummary { variableCount: number; hasStartupScript: boolean } +// NEW. +export interface WorkspaceTemplatePermissions { update: boolean; delete: boolean; publish: boolean } +// CHANGED. Redacted catalogue view. +export interface WorkspaceTemplateView { + id: string; + name: string; + description: string; + machineTypeId: string; + createdAt: number; + createdBy: { name: string; avatarUrl: string | null }; + environment: TemplateEnvironmentSummary | null; + agentRuleId: string | null; + isOrgDefault: boolean; + visibility: "private" | "organization"; + orgShareRole: "editor" | "viewer" | null; + folders: { + id: string; + name: string; + guestPath: string | null; + role: "owner" | "admin" | "editor" | "viewer" | null; + }[]; + connections: TemplateConnectionView[]; + repos: string[]; + permissions: WorkspaceTemplatePermissions; +} +// UNCHANGED envelope with a changed nested view. +export interface ListWorkspaceTemplatesResponse { templates: WorkspaceTemplateView[] } +// UNCHANGED envelope with a changed nested view. +export interface CreateWorkspaceTemplateResponse { template: WorkspaceTemplateView } +// NEW. This array starts with one static item. +export interface TemplateCaptureLimitation { code: "stored-configuration-only"; detail: string } +// NEW. +export interface TemplateDraftResponse { + draft: CreateWorkspaceTemplateRequest; + redactedEnvironmentKeys: string[]; + omittedFolderCount: number; + source: { + creationSource: "blank" | "template" | null; + sourceTemplateId: string | null; + requestedTemplateRepos: string[] | null; + }; + captureLimitations: TemplateCaptureLimitation[]; +} +// NEW. Only administrators receive this response. +export interface PublishWorkspaceTemplateReviewResponse { template: CreateWorkspaceTemplateRequest; agentRule: WorkspaceTemplateLaunchReview["agentRule"]; changesSinceLastPublication: string[]; reviewDigest: string } +// NEW. This response contains no environment values. +export interface WorkspaceTemplateLaunchReview { + templateId: string; + startupScript: string | null; + agentRule: { + id: string | null; + name: string; + content: string; + updatedAt: number | null; + builtIn: boolean; + }; + publishedBy: { name: string; avatarUrl: string | null }; + publishedAt: number; +} +// CHANGED. Add three source fields. +export interface WorkspaceView { + id: string; + name: string; + machineTypeId: string; + phase: Phase; + retryAction: RetryAction; + canObserve: boolean; + launchable: boolean; + revision: number; + ssh: { + host: string; + port: number; + user: string; + hostPublicKey: string | null; + } | null; + volumeId: string | null; + error: string | null; + role: WorkspaceRole | null; + orgShareRole: "editor" | "viewer" | null; + owner: { name: string; avatarUrl: string | null }; + environment: WorkspaceEnvironment | null; + agentRuleId: string | null; + connections: string[]; + recipeId?: string; + creationSource: "blank" | "template" | null; + sourceTemplateId: string | null; + requestedTemplateRepos: string[] | null; +} +``` + +### Proof obligations + +Each test must record a failing result before its implementation change. +Each test must pass after the change and fail again after a local revert. + +| Change | Required test | Required failure before change | +|---|---|---| +| Source storage and migration | `packages/control-plane/test/template-provenance-migration.test.ts` and `packages/control-plane/test/blitzdev-schema.test.ts` | Columns remain absent, and future creates lose source data. | +| Browser and Worker contract | Fixture corpus and two parsers | The draft route returns 404. | +| Fidelity round trip | `packages/control-plane/test/workspace-template-roundtrip.test.ts` | The loop loses guest paths, requested repositories, scopes, or sharing intent. | +| Scope ceiling | `packages/control-plane/test/workspace-template-scope-ceiling.test.ts` | Parsing overwrites GitHub, creation drops it, or mint rejects it. | +| Authorization and publication | `packages/control-plane/test/workspace-template-permissions.test.ts` | A viewer reads a draft, or a member publishes executable content. | +| Atomic save and soft delete | `packages/control-plane/test/workspace-template-atomicity.test.ts` | Fault injection leaves partial rows or removes referenced provenance. | +| Browser loop | `packages/webapp/test/save-workspace-as-template.test.tsx` | No workspace action opens an editable, redacted draft. | +| Permission controls | `packages/webapp/test/template-management-permissions.test.tsx` | Unauthorized users see update, delete, or publish controls. | +| Managed build | `packages/control-plane/test/core-manifest.test.ts` and `packages/control-plane/test/blitzdev-emitter.test.ts` | Managed output omits a new core file or schema field. | + +Add `packages/schema/fixtures/template-draft/`. +Add `packages/control-plane/test/template-draft-conformance.test.ts`. +Add `packages/webapp/test/template-draft-conformance.test.ts`. + +The fixture corpus must cover valid, legacy, secret, cross-organization, and invalid path cases. +It must cover `folderIds`, `folders`, both fields, absent scopes, and empty scopes. +Control-plane and webapp conformance tests must parse the same fixture bytes. +`packages/control-plane/test/wire-drift.test.ts` must cover every changed shared field. + +Record one browser run across owner, viewer, member, and administrator accounts. +The run must show redaction, denial, private save, publication, launch review, and round-trip creation. + +Run all three repository gates: + +```sh +npm run typecheck +npm run lint:gate +npm test +``` + +### Build steps + +1. Add the failing fixtures and tests from the proof table. +2. Add migrations, future-create writers, `BLITZDEV_CONFIG`, and source view fields. +3. Add atomic template routes, publication controls, path handling, and scope enforcement. +4. Add the browser draft, review, publish, and permission flows. +5. Record the browser run and pass every proof obligation. + +### Named future changes + +- Readiness and setup reporting need a race-safe state machine and an execution barrier. +- Stop and resume need provider contracts, quota rules, and non-destructive microVM behavior. +- Viewer sharing needs chat policy and provider capability handling. +- Organization bootstrap needs a chosen seed or example and its ownership policy. +- Agent-rule revisions need immutable history, approval, pinning, composition, and the current `256 KiB` limit. +- Member API keys need scopes, expiry, revocation, fresh membership checks, and credential precedence. +- Recipe triggers and runs need identity, bounds, deduplication, terminal conditions, stopping, and retention. + +### Decisions open to veto + +- Derivation uses a read route. Template creation uses a separate write route. +- The first loop supports browser sessions only. +- Every template starts private. Only administrators publish organization templates. +- Drafts omit environment values. Users enter wanted values again. +- Publication uses exact review and a server-keyed digest. +- Scope ceilings must work through parsing, storage, creation, and minting. +- Existing template migrations default to private visibility. +- Template deletion uses soft deletion and keeps provenance. +- Workspace rows call repository values `requestedTemplateRepos`. +- Folder compatibility ends only after telemetry shows no legacy use. +- Template save never changes folder access. +- Volumes and runtime state never enter templates. From 8fdab7639d7af4172750719959b4de2ef48ec32e Mon Sep 17 00:00:00 2001 From: pythonlearner1025 Date: Wed, 26 Aug 2026 00:04:29 -0700 Subject: [PATCH 2/2] docs(plans): re-anchor the audit to e5f70e1 Two commits landed while three reviews ran. Entitlements shipped in #62. Box update shipped in #63. Ten cited lines moved. A recheck read both commits against all nine findings. Every finding holds. This change corrects the ten line numbers and pins the audit to one commit. Three new facts: A VM slot stays used in creating, ready, destroying, and error. Only destroyed frees one. The cap keeps one source, orgs.vm_limit. Entitlements writes that column, and org_entitlements holds seat limits only. A cloud-VM host can now swap the image of a running workspace. So created_at no longer identifies the running image. Gap 4 records that limit. Box update state never enters a template. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01KXjajE99TTzesZmjsX6MwJ --- plans/AI-OS-LOOP.md | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/plans/AI-OS-LOOP.md b/plans/AI-OS-LOOP.md index 1f36de1e..11814ec8 100644 --- a/plans/AI-OS-LOOP.md +++ b/plans/AI-OS-LOOP.md @@ -1,6 +1,7 @@ # AI OS loop — save a good workspace as a template -Audit date: 2026-08-25. Source: `origin/main` at `c6902d0`. +Audit date: 2026-08-26. Source: `origin/main` at `e5f70e1`. +Every line number in this document refers to that commit. `main` moves often. A template stores a reusable workspace setup. A workspace starts from a template or starts blank. A recipe combines a template, harness, model, optional effort, and prompt. The root `README.md:28` starts an agent from a company template. @@ -10,15 +11,15 @@ A recipe combines a template, harness, model, optional effort, and prompt. The r ### Gap 1 — no workspace-to-template path -The create parser accepts `templateId` at `packages/control-plane/core/workspaces.ts:104`. The workspace insert omits it at `packages/control-plane/core/workspaces.ts:489`. +The create parser accepts `templateId` at `packages/control-plane/core/workspaces.ts:105`. The workspace insert omits it at `packages/control-plane/core/workspaces.ts:490`. That insert also omits requested repositories. `WorkspaceView` exposes neither value at `packages/schema/src/workspace.ts:27`. The template menu offers Edit and Delete at `packages/webapp/src/files/TemplatesHome.tsx:100`. The card offers New workspace separately at `packages/webapp/src/files/TemplatesHome.tsx:151`. Users must rebuild a successful setup from memory. ### Gap 2 — ready does not mean setup complete -Phone home marks the workspace ready at `packages/control-plane/core/workspaces.ts:921`. The control plane schedules folder sync later at `packages/control-plane/core/workspaces.ts:935`. -Bootstrap starts repository cloning before phone home at `packages/control-plane/core/bootstrap.ts:748`. Cloning can finish before or after ready. +Phone home marks the workspace ready at `packages/control-plane/core/workspaces.ts:922`. The control plane schedules folder sync later at `packages/control-plane/core/workspaces.ts:936`. +Bootstrap starts repository cloning before phone home at `packages/control-plane/core/bootstrap.ts:765`. Cloning can finish before or after ready. Users can start work before folders, repositories, environment, or startup work finishes. ### Gap 3 — setup failures stay hidden @@ -30,14 +31,19 @@ Backend failures can look like valid empty states. ### Gap 4 — viewer sharing breaks its promise -The dialog promises chat replay at `packages/webapp/src/ShareWorkspaceDialog.tsx:177`. The control plane blocks viewer port `7444` at `packages/control-plane/core/workspaces.ts:735`. +The dialog promises chat replay at `packages/webapp/src/ShareWorkspaceDialog.tsx:177`. The control plane blocks viewer port `7444` at `packages/control-plane/core/workspaces.ts:736`. The browser also shows one fixed hold message at `packages/webapp/src/CloudApp.tsx:1742`. Viewers cannot review the agent chat. +The read-only terminal gate uses `created_at` as a proxy for box capability. +Box update landed in `e5f70e1`. A cloud-VM host can now swap the image of a running workspace. +So `created_at` no longer identifies the running image. A future fix must read reported box state. ### Gap 5 — workspaces never stop The server defines five phases at `packages/schema/src/workspace.ts:3`. It defines no stopped or resumed phase. -The browser carries unused stop states at `packages/webapp/src/protocol.ts:3`. The quota counts non-destroyed workspaces at `packages/control-plane/core/workspaces.ts:495`. +The browser carries unused stop states at `packages/webapp/src/protocol.ts:3`. The quota counts `VM_SLOT_PHASES` at `packages/control-plane/core/workspaces.ts:496`. +Those phases are `creating`, `ready`, `destroying`, and `error`. Only `destroyed` frees a slot. +The cap has one stored source, `orgs.vm_limit`. Entitlements can write that column, but `org_entitlements` holds seat limits only. Users must destroy workspaces to stop cost and release quota. ### Gap 6 — new organizations start empty @@ -86,7 +92,7 @@ Template creates record `'template'`, the selected template, and requested repos The migration leaves existing source fields null. Existing rows never stored these values, so no backfill can recover them. The migration marks existing templates private and clears their default status. Shared recipe launches wait for publication. This choice prevents silent publication of old secrets and scripts. -The Worker adds every new field to `BLITZDEV_CONFIG` at `packages/control-plane/scripts/lib/worker-source.mjs:101`. +The Worker adds every new field to `BLITZDEV_CONFIG` at `packages/control-plane/scripts/lib/worker-source.mjs:103`. Each new core file must enter `CORE_MANIFEST` in the same file. ### Routes and flow @@ -111,7 +117,7 @@ An administrator uses a separate publication review. Workspace creation continue The draft route calls `canControlWorkspace`. That helper permits only owners and administrators at `packages/control-plane/core/workspace-access.ts:17`. Viewer and editor access never permits draft creation. -Current workspace views return raw environment data to any opener at `packages/control-plane/core/workspace-records.ts:94`. +Current workspace views return raw environment data to any opener at `packages/control-plane/core/workspace-records.ts:99`. The implementation returns raw environment data only to workspace controllers. The draft returns environment key names but no values. It copies no value into `draft.environment.env`. The user enters each wanted value again. The browser shows the exact startup script as executable content. @@ -135,7 +141,7 @@ The publish screen requires accessible organization folders or removes them. ### Connection scope ceiling The current code cannot enforce a saved scope ceiling end to end. -Parsing overwrites scoped GitHub at `packages/control-plane/core/workspace-templates.ts:123`. Creation keeps provider names at `packages/control-plane/core/workspaces.ts:435`. +Parsing overwrites scoped GitHub at `packages/control-plane/core/workspace-templates.ts:123`. Creation keeps provider names at `packages/control-plane/core/workspaces.ts:436`. Token pulls request provider defaults at `packages/control-plane/core/connections/mint.ts:541`. The loop must change the complete path before it exposes scoped template saving. @@ -168,6 +174,7 @@ Existing workspaces keep `sourceTemplateId`. The catalogue hides deleted templat The draft uses stored configuration only. It does not inspect the workspace disk. It cannot capture manual packages, files, repositories, project rules, Docker state, Git state, or home dotfiles. Volumes, SSH keys, box host keys, recipes, and runtime state never enter a template. +Box update state never enters a template either. That covers `box_update_requested`, `box_image_reported`, and the running image. ### Type sketches