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:
2026-10-07 00:14:51 +09:00
parent 3c424fd17d
commit 58462d5844
24 changed files with 1708 additions and 319 deletions
@@ -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);
});