From 48579104105282bfc7dde74a57b29547c3b1223a Mon Sep 17 00:00:00 2001 From: Hare Date: Fri, 14 Aug 2026 09:19:13 +0900 Subject: [PATCH] web: retire profile-specific editor --- .../src/lib/workspace/settings/model.test.ts | 30 --- .../src/lib/workspace/settings/model.ts | 8 +- .../src/lib/workspace/settings/profile-api.ts | 178 ++++--------- .../lib/workspace/settings/profile-routes.ts | 22 -- .../lib/workspace/settings/profile-types.ts | 61 +---- .../settings/profiles/+page.svelte | 233 +++--------------- .../trees/[sourceTreeId]/+page.svelte | 173 ------------- .../profiles/trees/[sourceTreeId]/+page.ts | 5 - 8 files changed, 91 insertions(+), 619 deletions(-) delete mode 100644 web/workspace/src/lib/workspace/settings/profile-routes.ts delete mode 100644 web/workspace/src/routes/w/[workspaceId]/settings/profiles/trees/[sourceTreeId]/+page.svelte delete mode 100644 web/workspace/src/routes/w/[workspaceId]/settings/profiles/trees/[sourceTreeId]/+page.ts diff --git a/web/workspace/src/lib/workspace/settings/model.test.ts b/web/workspace/src/lib/workspace/settings/model.test.ts index c4b80221..b8cbac6d 100644 --- a/web/workspace/src/lib/workspace/settings/model.test.ts +++ b/web/workspace/src/lib/workspace/settings/model.test.ts @@ -6,10 +6,6 @@ import { SETTINGS_SECTIONS, settingsSectionHref, } from "./model.ts"; -import { - profileSourceTreeSettingsHref, - virtualProfilePathForCreate, -} from "./profile-routes.ts"; declare const Deno: { test(name: string, fn: () => void): void; @@ -106,29 +102,3 @@ Deno.test("diagnostic labels preserve severity and code", () => { "diagnostic label should be bounded and stable", ); }); - -Deno.test("profile source tree routes use encoded scoped ids", () => { - const href = profileSourceTreeSettingsHref("workspace a", "project/tree"); - assert( - href === "/w/workspace%20a/settings/profiles/trees/project%2Ftree", - `unexpected href ${href}`, - ); - assert(!href.includes("/home/"), "route must not contain host paths"); -}); - -Deno.test("profile source create paths are normalized to virtual profile paths", () => { - assert( - virtualProfilePathForCreate("alpha.dcdl") === "profiles/alpha.dcdl", - "bare file names are scoped", - ); - assert( - virtualProfilePathForCreate("profiles/alpha.dcdl") === - "profiles/alpha.dcdl", - "virtual paths are preserved", - ); - assert( - virtualProfilePathForCreate("project:profiles/alpha.dcdl") === - "project:profiles/alpha.dcdl", - "safe virtual namespaces are preserved", - ); -}); diff --git a/web/workspace/src/lib/workspace/settings/model.ts b/web/workspace/src/lib/workspace/settings/model.ts index 9aab0873..2a89efd8 100644 --- a/web/workspace/src/lib/workspace/settings/model.ts +++ b/web/workspace/src/lib/workspace/settings/model.ts @@ -108,7 +108,7 @@ export const SETTINGS_SECTIONS: readonly SettingsSection[] = [ bullets: [ "Virtual paths and imports resolve inside the committed Workspace tree, never from browser or Server host paths.", "Browser analysis is advisory; Server evaluation is required before an atomic revision commit.", - "Profile, Skill, Prompt, and Plugin consumers remain on their existing authorities until their follow-up cutovers.", + "Profile launch data is projected from this active revision; remaining Skill, Prompt, and Plugin consumers migrate in their follow-up cutovers.", ], }, { @@ -116,11 +116,11 @@ export const SETTINGS_SECTIONS: readonly SettingsSection[] = [ label: "Profile Sources", status: "editable", summary: - "Manage the workspace-scoped Decodal Profile registry and source files used by Backend-published launch profile discovery.", + "Inspect the Profile launch projection derived from the active Workspace configuration revision.", bullets: [ "Selectors are source-qualified (builtin:* or project:*); raw profile source paths, archive content, archive digests, resource handles, and runtime tokens are not exposed.", - "Profile source edits are validated through the Backend ProfileSourceArchive/Decodal boundary before they are persisted.", - "Launch profile candidates refresh from the same Backend projection after registry or source updates.", + "Profile declarations and sources are edited only through the shared Workspace configuration editor and evaluate-before-commit contract.", + "Launch candidates and Profile archives carry the same active config revision, tree digest, and projection digest.", ], }, { diff --git a/web/workspace/src/lib/workspace/settings/profile-api.ts b/web/workspace/src/lib/workspace/settings/profile-api.ts index b23fc3f6..56ae0332 100644 --- a/web/workspace/src/lib/workspace/settings/profile-api.ts +++ b/web/workspace/src/lib/workspace/settings/profile-api.ts @@ -1,166 +1,72 @@ -import { workspaceApiJson, workspaceApiJsonWithBody } from "../api/http"; import type { - ProfileSettingsMutationResponse, ProfileSettingsResponse, WorkspaceMetadataMutationResponse, WorkspaceMetadataSettingsResponse, - WorkspaceProfileSourceDetailResponse, - WorkspaceProfileSourceTreeFileResponse, - WorkspaceProfileSourceTreeResponse, } from "./profile-types"; -export function fetchWorkspaceMetadataSettings( +export type WorkspaceProfileApi = { + getMetadata(workspaceId: string): Promise; + updateMetadata( + workspaceId: string, + displayName: string, + expectedRevision: string, + ): Promise; + getProfiles(workspaceId: string): Promise; +}; + +async function requestJson(input: RequestInfo | URL, init?: RequestInit): Promise { + const response = await fetch(input, init); + if (!response.ok) { + throw new Error(`request failed: ${response.status}`); + } + return (await response.json()) as T; +} + +export async function fetchWorkspaceMetadataSettings( workspaceId: string, ): Promise { - return workspaceApiJson( + return await requestJson( `/api/w/${encodeURIComponent(workspaceId)}/settings/workspace`, ); } -export function updateWorkspaceMetadataSettings( +export async function updateWorkspaceMetadataSettings( workspaceId: string, request: { display_name: string; revision: string }, ): Promise { - return workspaceApiJsonWithBody( + return await requestJson( `/api/w/${encodeURIComponent(workspaceId)}/settings/workspace`, { method: "PUT", + headers: { "content-type": "application/json" }, body: JSON.stringify(request), }, ); } -export function fetchProfileSettings( - workspaceId: string, -): Promise { - return workspaceApiJson( +export async function fetchProfileSettings(workspaceId: string): Promise { + return await requestJson( `/api/w/${encodeURIComponent(workspaceId)}/settings/profiles`, ); } -export function createProfileSource( - workspaceId: string, - request: { - name: string; - description?: string; - content: string; - registry_revision: string; - }, -): Promise { - return workspaceApiJsonWithBody( - `/api/w/${encodeURIComponent(workspaceId)}/settings/profiles`, - { - method: "POST", - body: JSON.stringify(request), +export function createWorkspaceProfileApi(): WorkspaceProfileApi { + return { + async getMetadata(workspaceId) { + return await requestJson( + `/api/w/${encodeURIComponent(workspaceId)}/settings/metadata`, + ); }, - ); -} - -export function updateProfileRegistry( - workspaceId: string, - request: { - registry_revision: string; - default_profile?: string | null; - profiles: Array< - { - name: string; - description?: string | null; - profile_source_id?: string | null; - } - >; - }, -): Promise { - return workspaceApiJsonWithBody( - `/api/w/${encodeURIComponent(workspaceId)}/settings/profiles/registry`, - { - method: "PUT", - body: JSON.stringify(request), + async updateMetadata(workspaceId, displayName, expectedRevision) { + return await requestJson( + `/api/w/${encodeURIComponent(workspaceId)}/settings/metadata`, + { + method: "PUT", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ display_name: displayName, expected_revision: expectedRevision }), + }, + ); }, - ); -} - -export function fetchProfileSource( - workspaceId: string, - sourceId: string, -): Promise { - return workspaceApiJson( - `/api/w/${encodeURIComponent(workspaceId)}/settings/profiles/${ - encodeURIComponent(sourceId) - }`, - ); -} - -export function updateProfileSource( - workspaceId: string, - sourceId: string, - request: { content: string; revision: string }, -): Promise { - return workspaceApiJsonWithBody( - `/api/w/${encodeURIComponent(workspaceId)}/settings/profiles/${ - encodeURIComponent(sourceId) - }`, - { method: "PUT", body: JSON.stringify(request) }, - ); -} - -export function deleteProfileSource( - workspaceId: string, - sourceId: string, - request: { registry_revision: string; source_revision: string }, -): Promise { - return workspaceApiJsonWithBody( - `/api/w/${encodeURIComponent(workspaceId)}/settings/profiles/${ - encodeURIComponent(sourceId) - }`, - { method: "DELETE", body: JSON.stringify(request) }, - ); -} - -export function fetchProfileSourceTree( - workspaceId: string, - sourceTreeId: string, -): Promise { - return workspaceApiJson( - `/api/w/${encodeURIComponent(workspaceId)}/settings/profiles/trees/${ - encodeURIComponent(sourceTreeId) - }`, - ); -} - -export function fetchProfileTreeFile( - workspaceId: string, - sourceTreeId: string, - path: string, -): Promise { - return workspaceApiJson( - `/api/w/${encodeURIComponent(workspaceId)}/settings/profiles/trees/${ - encodeURIComponent(sourceTreeId) - }/file?path=${encodeURIComponent(path)}`, - ); -} - -export function writeProfileTreeFile( - workspaceId: string, - sourceTreeId: string, - request: { path: string; content: string; revision?: string | null }, -): Promise { - return workspaceApiJsonWithBody( - `/api/w/${encodeURIComponent(workspaceId)}/settings/profiles/trees/${ - encodeURIComponent(sourceTreeId) - }/file`, - { method: "PUT", body: JSON.stringify(request) }, - ); -} - -export function deleteProfileTreeFile( - workspaceId: string, - sourceTreeId: string, - request: { path: string; revision: string }, -): Promise { - return workspaceApiJsonWithBody( - `/api/w/${encodeURIComponent(workspaceId)}/settings/profiles/trees/${ - encodeURIComponent(sourceTreeId) - }/file`, - { method: "DELETE", body: JSON.stringify(request) }, - ); + getProfiles: fetchProfileSettings, + }; } diff --git a/web/workspace/src/lib/workspace/settings/profile-routes.ts b/web/workspace/src/lib/workspace/settings/profile-routes.ts deleted file mode 100644 index d217bc35..00000000 --- a/web/workspace/src/lib/workspace/settings/profile-routes.ts +++ /dev/null @@ -1,22 +0,0 @@ -export function profileSettingsHref(workspaceId: string): string { - return `/w/${encodeURIComponent(workspaceId)}/settings/profiles`; -} - -export function profileSourceTreeSettingsHref( - workspaceId: string, - sourceTreeId: string, -): string { - return `${profileSettingsHref(workspaceId)}/trees/${ - encodeURIComponent(sourceTreeId) - }`; -} - -export function virtualProfilePathForCreate(input: string): string { - const trimmed = input.trim(); - if (!trimmed) return ""; - if (trimmed.startsWith("project:") || trimmed.startsWith("workspace:")) { - return trimmed; - } - if (trimmed.startsWith("profiles/")) return trimmed; - return `profiles/${trimmed}`; -} diff --git a/web/workspace/src/lib/workspace/settings/profile-types.ts b/web/workspace/src/lib/workspace/settings/profile-types.ts index d5d525e1..85fdf323 100644 --- a/web/workspace/src/lib/workspace/settings/profile-types.ts +++ b/web/workspace/src/lib/workspace/settings/profile-types.ts @@ -29,7 +29,7 @@ export type WorkspaceProfileSummary = { export type WorkspaceProfileSourceSummary = { profile_source_id: string; display_path: string; - kind: "decodal" | string; + kind: "virtual_config" | string; content_type: string; content_digest: string; provenance: "project_profile_source_tree" | string; @@ -42,64 +42,11 @@ export type WorkspaceProfileSourceSummary = { export type ProfileSettingsResponse = { workspace_id: string; registry_revision: string; + config_revision?: number | null; + tree_digest?: string | null; + projection_digest?: string | null; default_profile?: string | null; profiles: WorkspaceProfileSummary[]; sources: WorkspaceProfileSourceSummary[]; - source_trees: WorkspaceProfileSourceTreeSummary[]; - diagnostics: Diagnostic[]; -}; - -export type WorkspaceProfileSourceDetailResponse = { - workspace_id: string; - profile: WorkspaceProfileSummary; - source: WorkspaceProfileSourceSummary; - content: string; - diagnostics: Diagnostic[]; -}; - -export type ProfileSettingsMutationResponse = { - workspace_id: string; - settings: ProfileSettingsResponse; - diagnostics: Diagnostic[]; -}; - -export type WorkspaceProfileSourceTreeSummary = { - source_tree_id: string; - label: string; - root_path: string; - kind: "decodal_source_tree" | string; - content_type: string; - content_digest: string; - provenance: "project_profile_source_tree" | string; - editable: boolean; - revision: string; - file_count: number; - diagnostics: Diagnostic[]; -}; - -export type WorkspaceProfileSourceTreeFileSummary = { - path: string; - kind: "decodal" | string; - content_type: string; - content_digest: string; - provenance: "project_profile_source_tree" | string; - editable: boolean; - revision: string; - size_bytes: number; - diagnostics: Diagnostic[]; -}; - -export type WorkspaceProfileSourceTreeResponse = { - workspace_id: string; - tree: WorkspaceProfileSourceTreeSummary; - files: WorkspaceProfileSourceTreeFileSummary[]; - diagnostics: Diagnostic[]; -}; - -export type WorkspaceProfileSourceTreeFileResponse = { - workspace_id: string; - source_tree_id: string; - file: WorkspaceProfileSourceTreeFileSummary; - content: string; diagnostics: Diagnostic[]; }; diff --git a/web/workspace/src/routes/w/[workspaceId]/settings/profiles/+page.svelte b/web/workspace/src/routes/w/[workspaceId]/settings/profiles/+page.svelte index 8a2d0a46..a9a8636b 100644 --- a/web/workspace/src/routes/w/[workspaceId]/settings/profiles/+page.svelte +++ b/web/workspace/src/routes/w/[workspaceId]/settings/profiles/+page.svelte @@ -1,130 +1,30 @@ - - - Profile source tree · Yoi Workspace - - -
-
-
-

Profile source tree

-
-
- - {#if loading} -

Loading source tree…

- {:else if tree} -

- {tree.tree.root_path} · {tree.tree.file_count} files · {tree.tree.content_type} · {tree.tree.content_digest} -

-
- - -
-
-
-

Files

-
    - {#each tree.files as file (file.path)} -
  • - - {file.content_type} · {file.content_digest} - {file.size_bytes} bytes · rev {file.revision} - -
  • - {/each} -
-
- {#if selectedFile} -
-
-
-

{selectedFile.file.content_type}

-

{selectedFile.file.path}

-
-
- - -
-
- (draftContent = value)} ariaLabel={`Decodal source ${selectedFile.file.path}`} /> -
- {/if} -
- {/if} - - {#if message}

{message}

{/if} - -
diff --git a/web/workspace/src/routes/w/[workspaceId]/settings/profiles/trees/[sourceTreeId]/+page.ts b/web/workspace/src/routes/w/[workspaceId]/settings/profiles/trees/[sourceTreeId]/+page.ts deleted file mode 100644 index b96d2552..00000000 --- a/web/workspace/src/routes/w/[workspaceId]/settings/profiles/trees/[sourceTreeId]/+page.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type { PageLoad } from "./$types"; - -export const load: PageLoad = ({ params }) => ({ - sourceTreeId: params.sourceTreeId, -});