feat: register Workspace Profiles as Decodal values (T-710)
Project the evaluated Workspace Profile value into a revision-bound JSON archive without source-path registration or Decodal re-evaluation. Preserve partial-value omissions and explicit overrides, validate before save, and reject legacy registrations with a documented explicit migration.
Fix array-element schema completion and provide fingerprint-bound authoring shapes independently of materialization defaults. Carry metadata through REST, regenerate TypeScript/OpenAPI/WASM artifacts, and exercise save/projection/Runtime consumption and persisted Manifest restore boundaries.
Validation: root cargo check; cargo test -p config-source; cargo test -p worker-runtime; cargo test -p server-api --features typescript; focused profile_settings/value_profiles/archive tests; 39 web config-source/profile API tests; deno task check; cargo fmt --all -- --check; git diff --check HEAD.
Full workspace-server test: 641 passed, one pre-existing ticket_checker_suppresses_stale_and_clean_results_and_diagnoses_invalid_output assertion failure. The same filter fails at base 3c424fd17d in a separately archived source tree and isolated target directory. Remaining server crate tests pass with only that filter skipped (641 lib + 11 bin); unrelated checker code is unchanged.
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
"dev": "deno run -A npm:vite@8.3.2 dev",
|
||||
"dev:backend": "cd ../.. && cargo run -p yoi-workspace-server --bin yoi-server -- serve --listen 127.0.0.1:8787",
|
||||
"check": "deno run -A npm:@sveltejs/kit@3.0.0 sync && deno run -A npm:svelte-check@4.7.6 --tsconfig ./tsconfig.json",
|
||||
"test": "deno test --allow-read=src,test,tests,../../docs --allow-env=LOG,VSCODE_TEXTMATE_DEBUG,NODE_ENV tests/workspace-model.test.ts tests/repository-api.test.ts tests/workspace-catalog.test.ts tests/profile-api.test.ts tests/skill-api.test.ts tests/tooltip-contract.test.ts tests/bevel-contract.test.ts tests/bevel-line-contract.test.ts tests/typography-contract.test.ts src/lib/workspace/auth/model.test.ts src/lib/workspace/protocol-validator.test.ts tests/auth-api.test.ts src/lib/workspace/api/http.test.ts src/lib/workspace/api/workers.test.ts src/lib/workspace/api/ticket-browser.test.ts src/lib/workspace/header/breadcrumb-model.test.ts src/lib/workspace/console/chat-submit.test.ts src/lib/workspace/console/composer-delivery.test.ts test/composer-history.test.ts tests/composer-paste.test.ts src/lib/workspace/console/composer-command.test.ts src/lib/workspace/console/composer-draft.test.ts src/lib/workspace/console/composer-completion.test.ts src/lib/workspace/console/composer-invocation.test.ts src/lib/workspace/console/file-completions.test.ts test/console/ansi.test.ts src/lib/workspace/console/history.test.ts src/lib/workspace/console/model.test.ts src/lib/workspace/console/turn-navigation.test.ts src/lib/workspace/console/run-status.test.ts src/lib/workspace/companion/api.test.ts tests/workdir-api.test.ts tests/workdir-permissions.test.ts src/lib/workspace/console/tasks.test.ts test/ticket-detail-route-reuse.test.ts test/spa-shell.test.ts test/objective-pages.test.ts test/repositories/ui.test.ts src/lib/workspace/console/worker-console.ui.test.ts src/lib/workspace/settings/model.test.ts tests/runtime-connection.test.ts tests/runtime-management.test.ts tests/runtime-management-source.test.ts src/lib/workspace/sidebar/override-stack.test.ts src/lib/workspace/sidebar/route-ownership.test.ts src/lib/workspace/sidebar/workers.test.ts src/lib/workspace/sidebar/workspace-switcher.test.ts src/lib/workspace/sidebar/worker-subscription.test.ts src/lib/workspace/sidebar/worker-workdir-meta.test.ts src/lib/workspace/sidebar/worker-launch.test.ts tests/runtime-workers.test.ts test/sidebar/worker-actions.test.ts src/lib/workspace/tickets/merge-request-resources.test.ts src/lib/workspace/tickets/ticket-panel.test.ts test/merge-request-status.test.ts test/config-source/decodal-grammar.test.ts test/config-source/editor-state.test.ts test/config-source/fixed-schema-wrapper.test.ts test/config-source/toolchain.test.ts test/config-source/wasm-parity.test.ts test/config-source/api.test.ts tests/memory-api.test.ts test/repository-access/api.test.ts test/repository-access/loader.test.ts test/repository-access/model.test.ts test/repository-access/structure.test.ts test/repository-ssh-connection-ui.test.ts",
|
||||
"test": "deno test --allow-read=src,test,tests,../../docs,../../resources/config-schema --allow-env=LOG,VSCODE_TEXTMATE_DEBUG,NODE_ENV tests/workspace-model.test.ts tests/repository-api.test.ts tests/workspace-catalog.test.ts tests/profile-api.test.ts tests/skill-api.test.ts tests/tooltip-contract.test.ts tests/bevel-contract.test.ts tests/bevel-line-contract.test.ts tests/typography-contract.test.ts src/lib/workspace/auth/model.test.ts src/lib/workspace/protocol-validator.test.ts tests/auth-api.test.ts src/lib/workspace/api/http.test.ts src/lib/workspace/api/workers.test.ts src/lib/workspace/api/ticket-browser.test.ts src/lib/workspace/header/breadcrumb-model.test.ts src/lib/workspace/console/chat-submit.test.ts src/lib/workspace/console/composer-delivery.test.ts test/composer-history.test.ts tests/composer-paste.test.ts src/lib/workspace/console/composer-command.test.ts src/lib/workspace/console/composer-draft.test.ts src/lib/workspace/console/composer-completion.test.ts src/lib/workspace/console/composer-invocation.test.ts src/lib/workspace/console/file-completions.test.ts test/console/ansi.test.ts src/lib/workspace/console/history.test.ts src/lib/workspace/console/model.test.ts src/lib/workspace/console/turn-navigation.test.ts src/lib/workspace/console/run-status.test.ts src/lib/workspace/companion/api.test.ts tests/workdir-api.test.ts tests/workdir-permissions.test.ts src/lib/workspace/console/tasks.test.ts test/ticket-detail-route-reuse.test.ts test/spa-shell.test.ts test/objective-pages.test.ts test/repositories/ui.test.ts src/lib/workspace/console/worker-console.ui.test.ts src/lib/workspace/settings/model.test.ts tests/runtime-connection.test.ts tests/runtime-management.test.ts tests/runtime-management-source.test.ts src/lib/workspace/sidebar/override-stack.test.ts src/lib/workspace/sidebar/route-ownership.test.ts src/lib/workspace/sidebar/workers.test.ts src/lib/workspace/sidebar/workspace-switcher.test.ts src/lib/workspace/sidebar/worker-subscription.test.ts src/lib/workspace/sidebar/worker-workdir-meta.test.ts src/lib/workspace/sidebar/worker-launch.test.ts tests/runtime-workers.test.ts test/sidebar/worker-actions.test.ts src/lib/workspace/tickets/merge-request-resources.test.ts src/lib/workspace/tickets/ticket-panel.test.ts test/merge-request-status.test.ts test/config-source/decodal-grammar.test.ts test/config-source/editor-state.test.ts test/config-source/fixed-schema-wrapper.test.ts test/config-source/toolchain.test.ts test/config-source/wasm-parity.test.ts test/config-source/api.test.ts tests/memory-api.test.ts test/repository-access/api.test.ts test/repository-access/loader.test.ts test/repository-access/model.test.ts test/repository-access/structure.test.ts test/repository-ssh-connection-ui.test.ts",
|
||||
"test:component": "deno run -A npm:vitest@5.0.3 run --config vitest.config.ts",
|
||||
"build": "deno run -A npm:vite@8.3.2 build",
|
||||
"preview": "deno run -A npm:vite@8.3.2 preview",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @generated by yoi-companion-openapi-typescript@1. Do not edit.
|
||||
// Input: openapi/server-api.json
|
||||
// Input contract: 0.1.0 (sha256:fd06f382c6d33a5c3cc98ef9e264545923c53fa95a9be555a85680f98c6065da)
|
||||
// Input contract: 0.1.0 (sha256:a6a7147c9a2f465a5da044e9ba21947cb3d86eacbfe3905696483917e794d5cf)
|
||||
// Options: companion-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
|
||||
// Output: web/workspace/src/lib/generated/companion-api.ts
|
||||
// Regenerate/check: cargo run -q -p server-api --example generate_companion_api_types -- --check
|
||||
|
||||
@@ -237,6 +237,10 @@ export type ConfigSchemaContribution = {
|
||||
version: string;
|
||||
source: string;
|
||||
projection_validator?: ConfigProjectionValidator;
|
||||
/**
|
||||
* Completion-only shape for partial values; never materialized as defaults.
|
||||
*/
|
||||
authoring_source?: string;
|
||||
source_digest: string;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @generated by yoi-repository-openapi-typescript@1. Do not edit.
|
||||
// Input: openapi/server-api.json
|
||||
// Input contract: 0.1.0 (sha256:fd06f382c6d33a5c3cc98ef9e264545923c53fa95a9be555a85680f98c6065da)
|
||||
// Input contract: 0.1.0 (sha256:a6a7147c9a2f465a5da044e9ba21947cb3d86eacbfe3905696483917e794d5cf)
|
||||
// Options: repository-operation-schema-closure,openapi-3.1,format:uint64=safe-number,strict-safe-integers,sorted-output
|
||||
// Output: web/workspace/src/lib/generated/repository-api.ts
|
||||
// Regenerate/check: cargo run -q -p server-api --example generate_repository_openapi_types -- --check
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @generated by yoi-runtime-openapi-typescript@1. Do not edit.
|
||||
// Input: openapi/server-api.json
|
||||
// Input contract: 0.1.0 (sha256:fd06f382c6d33a5c3cc98ef9e264545923c53fa95a9be555a85680f98c6065da)
|
||||
// Input contract: 0.1.0 (sha256:a6a7147c9a2f465a5da044e9ba21947cb3d86eacbfe3905696483917e794d5cf)
|
||||
// Options: runtime-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
|
||||
// Output: web/workspace/src/lib/generated/runtime-api.ts
|
||||
// Regenerate/check: cargo run -q -p server-api --example generate_runtime_api_types -- --check
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @generated by yoi-ticket-browser-openapi-typescript@1. Do not edit.
|
||||
// Input: openapi/server-api.json
|
||||
// Input contract: 0.1.0 (sha256:fd06f382c6d33a5c3cc98ef9e264545923c53fa95a9be555a85680f98c6065da)
|
||||
// Input contract: 0.1.0 (sha256:a6a7147c9a2f465a5da044e9ba21947cb3d86eacbfe3905696483917e794d5cf)
|
||||
// Options: ticket-objective-merge-request-browser-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
|
||||
// Output: web/workspace/src/lib/generated/ticket-api.ts
|
||||
// Regenerate/check: cargo run -q -p server-api --example generate_ticket_api_types -- --check
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @generated by yoi-worker-openapi-typescript@1. Do not edit.
|
||||
// Input: openapi/server-api.json
|
||||
// Input contract: 0.1.0 (sha256:fd06f382c6d33a5c3cc98ef9e264545923c53fa95a9be555a85680f98c6065da)
|
||||
// Input contract: 0.1.0 (sha256:a6a7147c9a2f465a5da044e9ba21947cb3d86eacbfe3905696483917e794d5cf)
|
||||
// Options: worker-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
|
||||
// Output: web/workspace/src/lib/generated/worker-launch-api.ts
|
||||
// Regenerate/check: cargo run -q -p server-api --example generate_worker_launch_api_types -- --check
|
||||
|
||||
@@ -187,7 +187,7 @@ function parseContribution(value: unknown): ConfigSchemaContribution {
|
||||
const item = record(
|
||||
value,
|
||||
["provider_id", "namespace", "version", "source", "source_digest"],
|
||||
["projection_validator"],
|
||||
["projection_validator", "authoring_source"],
|
||||
);
|
||||
const projection = item.projection_validator === undefined
|
||||
? undefined
|
||||
@@ -197,6 +197,12 @@ function parseContribution(value: unknown): ConfigSchemaContribution {
|
||||
namespace: boundedString(item.namespace),
|
||||
version: boundedString(item.version),
|
||||
source: boundedString(item.source, MAX_SCHEMA_SOURCE_BYTES),
|
||||
...(item.authoring_source === undefined ? {} : {
|
||||
authoring_source: boundedString(
|
||||
item.authoring_source,
|
||||
MAX_SCHEMA_SOURCE_BYTES,
|
||||
),
|
||||
}),
|
||||
...(projection === undefined ? {} : { projection_validator: projection }),
|
||||
source_digest: boundedString(item.source_digest, MAX_DIGEST_BYTES),
|
||||
};
|
||||
|
||||
Binary file not shown.
+1
-1
@@ -1,4 +1,4 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { ConfigProjectionValidator } from "./ConfigProjectionValidator";
|
||||
|
||||
export type ConfigSchemaContribution = { provider_id: string, namespace: string, version: string, source: string, projection_validator?: ConfigProjectionValidator | null, source_digest: string, };
|
||||
export type ConfigSchemaContribution = { provider_id: string, namespace: string, version: string, source: string, authoring_source?: string | null, projection_validator?: ConfigProjectionValidator | null, source_digest: string, };
|
||||
|
||||
@@ -244,3 +244,41 @@ Deno.test("config source API surfaces bounded failed evaluation details", async
|
||||
}
|
||||
assert(message.includes("structured diagnostics"));
|
||||
});
|
||||
|
||||
Deno.test("config source API carries bounded optional authoring schemas without changing validation source", () => {
|
||||
const body = withSchema("validation");
|
||||
const authoringSource = "shape\n".repeat(6000);
|
||||
const contribution = {
|
||||
...body.contract.schema_bundle.contributions[0],
|
||||
authoring_source: authoringSource,
|
||||
};
|
||||
const withAuthoring = {
|
||||
...body,
|
||||
contract: {
|
||||
...body.contract,
|
||||
schema_bundle: {
|
||||
...body.contract.schema_bundle,
|
||||
contributions: [contribution],
|
||||
},
|
||||
},
|
||||
};
|
||||
const parsed = parseWorkspaceConfigTreeResponse(withAuthoring);
|
||||
assertEquals(
|
||||
parsed.contract.schema_bundle.contributions[0].authoring_source,
|
||||
authoringSource,
|
||||
);
|
||||
assertEquals(parsed.contract.schema_bundle.source, "validation");
|
||||
for (const invalid of [null, 42, {}, "é".repeat(4 * 1024 * 1024) + "x"]) {
|
||||
assertThrows(() =>
|
||||
parseWorkspaceConfigTreeResponse({
|
||||
...withAuthoring,
|
||||
contract: {
|
||||
...body.contract,
|
||||
schema_bundle: {
|
||||
...body.contract.schema_bundle,
|
||||
contributions: [{ ...contribution, authoring_source: invalid }],
|
||||
},
|
||||
},
|
||||
}), ConfigSourceApiError);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -381,3 +381,113 @@ Deno.test("generated WASM completes asserted WorkspaceConfigSchema keys", () =>
|
||||
assertEquals(result.from, 2);
|
||||
assertEquals(result.items.some((item) => item.label === "profile"), true);
|
||||
});
|
||||
|
||||
Deno.test("generated WASM authors value-based Profiles using the Backend schema without materializing authoring fields", async () => {
|
||||
const source = await Deno.readTextFile(
|
||||
new URL(
|
||||
"../../../../resources/config-schema/profile.dcdl",
|
||||
import.meta.url,
|
||||
),
|
||||
);
|
||||
const authoring_source = await Deno.readTextFile(
|
||||
new URL(
|
||||
"../../../../resources/config-schema/profile-authoring.dcdl",
|
||||
import.meta.url,
|
||||
),
|
||||
);
|
||||
const schema = compose_schema_bundle([{
|
||||
provider_id: "builtin:profile",
|
||||
namespace: "profile",
|
||||
version: "2",
|
||||
source,
|
||||
source_digest: await digestText(source),
|
||||
authoring_source,
|
||||
}]) as WorkspaceConfigSchemaBundle;
|
||||
const content =
|
||||
'{ profile = { entries = [{ selector = "project:alpha"; profile = {}; }]; }; } as WorkspaceConfigSchema';
|
||||
const tree: ConfigTreeSnapshot = {
|
||||
revision: 8,
|
||||
digest: "sha256:value-profile-tree",
|
||||
entries: {
|
||||
"main.dcdl": {
|
||||
path: "main.dcdl",
|
||||
content_type: "decodal",
|
||||
content,
|
||||
content_digest: await digestText(content),
|
||||
},
|
||||
},
|
||||
};
|
||||
const valueContract = {
|
||||
...contract,
|
||||
entrypoints: ["main.dcdl"],
|
||||
schema_bundle: schema,
|
||||
fingerprint: await toolchainFingerprint(["main.dcdl"], schema),
|
||||
};
|
||||
set_snapshot(tree);
|
||||
set_schema_bundle(schema);
|
||||
const evaluated = evaluate_snapshot(tree, valueContract) as {
|
||||
projections: Array<
|
||||
{ data_json: { profile: { entries: Array<{ profile: unknown }> } } }
|
||||
>;
|
||||
};
|
||||
assertEquals(
|
||||
evaluated.projections[0].data_json.profile.entries[0].profile,
|
||||
{},
|
||||
);
|
||||
for (
|
||||
const [body, token, label] of [
|
||||
[
|
||||
"{ profile = { entries = [{ sel }] } } as WorkspaceConfigSchema",
|
||||
"sel",
|
||||
"selector",
|
||||
],
|
||||
[
|
||||
"{ profile = { entries = [{ pro }] } } as WorkspaceConfigSchema",
|
||||
"pro",
|
||||
"profile",
|
||||
],
|
||||
[
|
||||
"{ profile = { entries = [{ profile = { wor } }] } } as WorkspaceConfigSchema",
|
||||
"wor",
|
||||
"worker",
|
||||
],
|
||||
[
|
||||
"{ profile = { entries = [{ profile = { worker = { mo } } }] } } as WorkspaceConfigSchema",
|
||||
"mo",
|
||||
"mode",
|
||||
],
|
||||
[
|
||||
"{ profile = { entries = [{ profile = { feature = { ta } } }] } } as WorkspaceConfigSchema",
|
||||
"ta",
|
||||
"task",
|
||||
],
|
||||
]
|
||||
) {
|
||||
const cursor = body.lastIndexOf(token) + token.length;
|
||||
const result = complete_current("main.dcdl", body, cursor, true) as {
|
||||
from: number;
|
||||
items: Array<{ label: string }>;
|
||||
};
|
||||
assertEquals(
|
||||
result.items.some((item) => item.label === label),
|
||||
true,
|
||||
label,
|
||||
);
|
||||
assertEquals(result.from, cursor - token.length);
|
||||
}
|
||||
const invalidContent = content.replace("profile = {};", "profile = 42;");
|
||||
const invalid = {
|
||||
...tree,
|
||||
entries: {
|
||||
"main.dcdl": { ...tree.entries["main.dcdl"], content: invalidContent },
|
||||
},
|
||||
};
|
||||
const diagnostics = analyze_snapshot(
|
||||
invalid,
|
||||
"main.dcdl",
|
||||
undefined,
|
||||
) as Array<
|
||||
unknown
|
||||
>;
|
||||
assertEquals(diagnostics.length > 0, true);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user