fix: preserve string and table Profile scope authoring (T-710)

Resolve MR sequence 5 profile_authoring_rejects_valid_string_scope_intents. Decodal 0.4 has no union constraint: retain an opaque scope range with non-materialized table completion hints so string intents remain valid. Keep shared Backend semantic validation authoritative for scope forms/types/unknown keys without changing persisted values or weakening worker/feature editor diagnostics.

Prove actual-provider native/WASM scalar/table, inline/import/patch, nested intent/deny_write/symlink_policy completion and evaluation invariance. Add real save/projection/Runtime scope equivalence and invalid-scope save non-loss coverage. Regenerated WASM artifact is byte-identical; the changed shape is transported via the fingerprint-bound Backend schema metadata.

Validation: root cargo check; config-source39; value_profiles4; unfiltered server643+11; worker-runtime340+17+17; server-api --features typescript77; wasm32 release/wasm-bindgen0.2.117/Binaryen132 -Oz; Deno39; deno task check0/0; fmt/diff checks. Logs target/t710-scope-{check,server,server-api,runtime}.log. No target/ref integration changes.
This commit is contained in:
2026-10-07 02:25:16 +09:00
parent 3558e4aae1
commit 61f0469986
5 changed files with 258 additions and 5 deletions
@@ -569,6 +569,93 @@ Deno.test("generated WASM authors value-based Profiles using the Backend schema
);
}
}
for (const field of ["scope", "delegation_scope"]) {
for (
const [prefix, label] of [["int", "intent"], ["deny", "deny_write"], [
"sym",
"symlink_policy",
]]
) {
const body =
`{ profile.entries = [{ profile.${field} = { ${prefix} } }]; } as WorkspaceConfigSchema`;
const cursor = body.lastIndexOf(prefix) + prefix.length;
const result = complete_current("main.dcdl", body, cursor, true) as {
items: Array<{ label: string }>;
};
assertEquals(
result.items.some((item) => item.label === label),
true,
`${field}.${label}`,
);
}
}
for (
const [recipe, expected] of [
['{ scope = "workspace_read"; delegation_scope = "workspace_write"; }', {
scope: "workspace_read",
delegation_scope: "workspace_write",
}],
['{ scope = "workspace_write"; delegation_scope = "workspace_read"; }', {
scope: "workspace_write",
delegation_scope: "workspace_read",
}],
[
'{ scope = { intent = "workspace_read"; }; delegation_scope = { intent = "workspace_write"; deny_write = ["private"]; symlink_policy = "resolved"; }; }',
{
scope: { intent: "workspace_read" },
delegation_scope: {
intent: "workspace_write",
deny_write: ["private"],
symlink_policy: "resolved",
},
},
],
] as const
) {
for (
const form of [
recipe,
'import "./scoped.dcdl"',
'(import "./scoped.dcdl") // { description = "patched"; }',
]
) {
const scopedContent = content.replace(
"profile = {};",
`profile = ${form};`,
);
const scopedTree = {
...tree,
entries: {
"main.dcdl": {
...tree.entries["main.dcdl"],
content: scopedContent,
content_digest: await digestText(scopedContent),
},
"scoped.dcdl": {
path: "scoped.dcdl",
content_type: "decodal",
content: recipe,
content_digest: await digestText(recipe),
},
},
};
assertEquals(
analyze_snapshot(scopedTree, "main.dcdl", undefined),
[],
form,
);
const result = evaluate_snapshot(
scopedTree,
valueContract,
) as typeof evaluated;
assertEquals(
result.projections[0].data_json.profile.entries[0].profile,
form.includes("patched")
? { ...expected, description: "patched" }
: expected,
);
}
}
// Neither completion nor diagnostic shape checking changes the saved value.
assertEquals(evaluate_snapshot(tree, valueContract), evaluated);
});