fix: align Profile authoring with compaction and reasoning contracts (T-710)
Resolve MR seq9 supported-compaction-fields and numeric-extraction-reasoning findings. Cover all canonical CompactionConfigPartial settings, token aliases and ratio helpers; accept string effort and signed integer extraction budgets without materializing editor defaults. Audit the remaining Profile field groups against their real serde contracts.
Share an actual-provider value corpus across native, generated WASM, and shared save/projection/verified Runtime archive consumption. Prove inline/import/patch exact values and effective Manifest settings; gate canonical compaction field coverage against serialization of the actual partial type. Keep prior scope union and worker/feature diagnostics fixes intact.
Validation: root cargo check; config-source40; unfiltered server644+11; Runtime340+17+17; API typescript77; Deno39; deno task check0/0; wasm32 release regeneration byte-identical with wasm-bindgen0.2.117/Binaryen132; cargo fmt/diff checks. Logs target/t710-contract-{check,config-source,server,api,runtime}.log. Reviewer seq9 independently confirmed seq3/5/7 resolution and correct main/relative-cwd routing. No target integration/ref mutations.
This commit is contained in:
@@ -2738,6 +2738,110 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fixture_decodal(value: &serde_json::Value) -> String {
|
||||||
|
match value {
|
||||||
|
serde_json::Value::Object(fields) => format!(
|
||||||
|
"{{ {} }}",
|
||||||
|
fields
|
||||||
|
.iter()
|
||||||
|
.map(|(key, value)| format!("{key} = {};", fixture_decodal(value)))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(" ")
|
||||||
|
),
|
||||||
|
serde_json::Value::Array(values) => format!(
|
||||||
|
"[{}]",
|
||||||
|
values
|
||||||
|
.iter()
|
||||||
|
.map(fixture_decodal)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", ")
|
||||||
|
),
|
||||||
|
serde_json::Value::Null => panic!("fixture omission must not be represented as null"),
|
||||||
|
value => value.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn actual_profile_authoring_value_corpus_preserves_supported_forms() {
|
||||||
|
let cases: Vec<serde_json::Value> = serde_json::from_str(include_str!(
|
||||||
|
"../../../resources/config-schema/profile-authoring-values.json"
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
for case in cases {
|
||||||
|
let recipe = fixture_decodal(&case["profile"]);
|
||||||
|
for (form, patched) in [
|
||||||
|
(recipe.clone(), false),
|
||||||
|
(r#"import "./recipe.dcdl""#.into(), false),
|
||||||
|
(
|
||||||
|
r#"(import "./recipe.dcdl") // { description = "patched"; }"#.into(),
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
] {
|
||||||
|
let source = format!(
|
||||||
|
r#"{{ profile.entries = [{{ selector = "project:corpus"; profile = {form}; }}]; }} as WorkspaceConfigSchema"#
|
||||||
|
);
|
||||||
|
let snapshot = ConfigTreeSnapshot::from_entries(
|
||||||
|
10,
|
||||||
|
[entry("main.dcdl", &source), entry("recipe.dcdl", &recipe)],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let schema = actual_profile_schema();
|
||||||
|
let environment =
|
||||||
|
SnapshotEnvironment::new(snapshot).with_schema_bundle(schema.clone());
|
||||||
|
let contract =
|
||||||
|
ToolchainContract::with_schema_bundle(1, vec![path("main.dcdl")], 1, schema);
|
||||||
|
let before = environment.evaluate_contract(&contract).unwrap();
|
||||||
|
let diagnostics = environment.analyze(&path("main.dcdl"), None);
|
||||||
|
assert!(
|
||||||
|
diagnostics.is_empty(),
|
||||||
|
"{}: {form}: {diagnostics:?}",
|
||||||
|
case["name"]
|
||||||
|
);
|
||||||
|
let mut expected = case["profile"].clone();
|
||||||
|
if patched {
|
||||||
|
expected["description"] = serde_json::json!("patched");
|
||||||
|
}
|
||||||
|
assert_eq!(
|
||||||
|
before.projections[0]
|
||||||
|
.data_json
|
||||||
|
.pointer("/profile/entries/0/profile"),
|
||||||
|
Some(&expected),
|
||||||
|
"{}",
|
||||||
|
case["name"]
|
||||||
|
);
|
||||||
|
assert_eq!(environment.evaluate_contract(&contract).unwrap(), before);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let source = "{} as WorkspaceConfigSchema";
|
||||||
|
let environment = SnapshotEnvironment::new(
|
||||||
|
ConfigTreeSnapshot::from_entries(10, [entry("main.dcdl", source)]).unwrap(),
|
||||||
|
)
|
||||||
|
.with_schema_bundle(actual_profile_schema());
|
||||||
|
for (profile_path, prefix, label) in [
|
||||||
|
("compaction", "reta", "retained_tokens"),
|
||||||
|
("compaction", "prune_min", "prune_min_savings"),
|
||||||
|
("compaction", "req", "request"),
|
||||||
|
("compaction", "wor", "worker"),
|
||||||
|
("compaction", "compact_ret", "compact_retained_tokens"),
|
||||||
|
("feature.memory.extraction", "reas", "reasoning"),
|
||||||
|
("feature.subjektiv.extraction", "reas", "reasoning"),
|
||||||
|
] {
|
||||||
|
let marked = format!(
|
||||||
|
"{{ profile.entries = [{{ profile.{profile_path} = {{ {prefix}| }}; }}]; }} as WorkspaceConfigSchema"
|
||||||
|
);
|
||||||
|
let cursor = marked.find('|').unwrap();
|
||||||
|
let source = marked.replace('|', "");
|
||||||
|
let result = environment
|
||||||
|
.complete_config(&path("main.dcdl"), &source, cursor, true)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
assert!(
|
||||||
|
result.items.iter().any(|item| item.label == label),
|
||||||
|
"{source}: {result:?}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn completion_authoring_sources_compose_with_validation_source_fallbacks() {
|
fn completion_authoring_sources_compose_with_validation_source_fallbacks() {
|
||||||
let schema = WorkspaceConfigSchemaBundle::compose([
|
let schema = WorkspaceConfigSchemaBundle::compose([
|
||||||
|
|||||||
@@ -303,6 +303,141 @@ async fn value_profiles_scalar_and_table_scopes_analyze_save_and_resolve_equally
|
|||||||
assert_eq!(scopes[1], scopes[2]);
|
assert_eq!(scopes[1], scopes[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn profile_fixture_decodal(value: &serde_json::Value) -> String {
|
||||||
|
match value {
|
||||||
|
serde_json::Value::Object(fields) => format!(
|
||||||
|
"{{ {} }}",
|
||||||
|
fields
|
||||||
|
.iter()
|
||||||
|
.map(|(key, value)| format!("{key} = {};", profile_fixture_decodal(value)))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(" ")
|
||||||
|
),
|
||||||
|
serde_json::Value::Array(values) => format!(
|
||||||
|
"[{}]",
|
||||||
|
values
|
||||||
|
.iter()
|
||||||
|
.map(profile_fixture_decodal)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", ")
|
||||||
|
),
|
||||||
|
serde_json::Value::Null => panic!("fixture omission must not be represented as null"),
|
||||||
|
value => value.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn value_profiles_authoring_corpus_preserves_effective_runtime_settings() {
|
||||||
|
let cases: Vec<serde_json::Value> = serde_json::from_str(include_str!(
|
||||||
|
"../../../../../resources/config-schema/profile-authoring-values.json"
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
// The canonical token fixture must exercise every serializable setting,
|
||||||
|
// so adding a field to CompactionConfigPartial cannot silently outgrow the
|
||||||
|
// editor-shape coverage. Aliases/ratio helpers have separate corpus cases.
|
||||||
|
let default = serde_json::to_value(manifest::CompactionConfigPartial::default()).unwrap();
|
||||||
|
let canonical = cases
|
||||||
|
.iter()
|
||||||
|
.find(|case| case["name"] == "canonical_compaction_tokens")
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
default
|
||||||
|
.as_object()
|
||||||
|
.unwrap()
|
||||||
|
.keys()
|
||||||
|
.collect::<std::collections::BTreeSet<_>>(),
|
||||||
|
canonical["profile"]["compaction"]
|
||||||
|
.as_object()
|
||||||
|
.unwrap()
|
||||||
|
.keys()
|
||||||
|
.filter(|name| name.as_str() != "kind")
|
||||||
|
.collect()
|
||||||
|
);
|
||||||
|
for case in cases {
|
||||||
|
let dir = tempfile::tempdir().unwrap();
|
||||||
|
let api = test_api(dir.path()).await;
|
||||||
|
let recipe = profile_fixture_decodal(&case["profile"]);
|
||||||
|
for (index, (form, patched)) in [
|
||||||
|
(recipe.clone(), false),
|
||||||
|
(r#"import "./recipe.dcdl""#.into(), false),
|
||||||
|
(
|
||||||
|
r#"(import "./recipe.dcdl") // { description = "patched"; }"#.into(),
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
|
let extra = if index == 0 {
|
||||||
|
vec![config_source::ConfigTreeChange::Create {
|
||||||
|
path: config_source::VirtualPath::parse("recipe.dcdl").unwrap(),
|
||||||
|
content_type: config_source::ConfigContentType::Decodal,
|
||||||
|
content: recipe.clone(),
|
||||||
|
}]
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
};
|
||||||
|
let authored = format!("{} as WorkspaceConfigSchema", source(form));
|
||||||
|
let state = commit_workspace_config_tree(
|
||||||
|
&api,
|
||||||
|
TEST_WORKSPACE_ID,
|
||||||
|
&request(&api, &authored, extra),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let environment = config_source::SnapshotEnvironment::new(state.snapshot.clone())
|
||||||
|
.with_schema_bundle(state.contract.schema_bundle.clone());
|
||||||
|
let diagnostics = environment.analyze(
|
||||||
|
&config_source::VirtualPath::parse("main.dcdl").unwrap(),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
assert!(diagnostics.is_empty(), "{}: {diagnostics:?}", case["name"]);
|
||||||
|
let mut expected = case["profile"].clone();
|
||||||
|
if *patched {
|
||||||
|
expected["description"] = serde_json::json!("patched");
|
||||||
|
}
|
||||||
|
let evaluated = environment.evaluate_contract(&state.contract).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
evaluated.projections[0]
|
||||||
|
.data_json
|
||||||
|
.pointer("/profile/entries/0/profile"),
|
||||||
|
Some(&expected)
|
||||||
|
);
|
||||||
|
let projection =
|
||||||
|
project_profiles_from_workspace_config(TEST_WORKSPACE_ID, &state).unwrap();
|
||||||
|
let bundle = build_virtual_profile_config_bundle(
|
||||||
|
&projection,
|
||||||
|
&state,
|
||||||
|
TEST_WORKSPACE_ID,
|
||||||
|
"created",
|
||||||
|
"project:alpha",
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
let resolved = bundle
|
||||||
|
.profile_source_archive
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.verify()
|
||||||
|
.unwrap()
|
||||||
|
.resolve_profile(
|
||||||
|
"project:alpha",
|
||||||
|
Path::new("/runtime-worker"),
|
||||||
|
"corpus-worker",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let manifest = serde_json::to_value(resolved).unwrap();
|
||||||
|
for (pointer, value) in case["manifest_expect"].as_object().unwrap() {
|
||||||
|
assert_eq!(
|
||||||
|
manifest.pointer(pointer),
|
||||||
|
Some(value),
|
||||||
|
"{}: {pointer}",
|
||||||
|
case["name"]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn value_profiles_invalid_saves_preserve_the_active_config() {
|
async fn value_profiles_invalid_saves_preserve_the_active_config() {
|
||||||
let dir = tempfile::tempdir().unwrap();
|
let dir = tempfile::tempdir().unwrap();
|
||||||
|
|||||||
@@ -47,6 +47,8 @@ The schema contribution separates its materialized registration schema from an o
|
|||||||
|
|
||||||
`scope` and `delegation_scope` retain both supported forms: a string such as `"workspace_read"` / `"workspace_write"`, or a table such as `{ intent = "workspace_read"; deny_write = ["private"]; symlink_policy = "resolved"; }`. Decodal 0.4 has no union range, so these fields are opaque to the editor constraint pass; a non-materialized table hint supplies nested completion without rejecting the string form or rewriting it. The shared Backend save validator checks scope forms, intent values and table fields (including their types and unknown keys). Other structurally typed fields such as `worker.mode` and `feature.task.enabled` retain editor type diagnostics. Completion hints never become Profile defaults or saved values.
|
`scope` and `delegation_scope` retain both supported forms: a string such as `"workspace_read"` / `"workspace_write"`, or a table such as `{ intent = "workspace_read"; deny_write = ["private"]; symlink_policy = "resolved"; }`. Decodal 0.4 has no union range, so these fields are opaque to the editor constraint pass; a non-materialized table hint supplies nested completion without rejecting the string form or rewriting it. The shared Backend save validator checks scope forms, intent values and table fields (including their types and unknown keys). Other structurally typed fields such as `worker.mode` and `feature.task.enabled` retain editor type diagnostics. Completion hints never become Profile defaults or saved values.
|
||||||
|
|
||||||
|
Extraction `reasoning` in both `feature.memory` and `feature.subjektiv`, like `engine.reasoning`, accepts either a string effort or a signed integer budget. These union values are also opaque to editor constraints and semantically checked on save. Compaction authoring covers the complete token settings and their supported `compact_*` aliases, plus the ratio helper's `request` / `worker` aliases; values are not renamed or defaulted during Workspace evaluation. The shared test corpus `resources/config-schema/profile-authoring-values.json` proves exact values in native/WASM analysis and effective settings through save/projection/Runtime consumption. Its canonical compaction case must cover every serialized `CompactionConfigPartial` field, making future contract drift visible in tests.
|
||||||
|
|
||||||
## Manifests
|
## Manifests
|
||||||
|
|
||||||
A resolved Manifest is the concrete contract used to create or restore a Worker. It carries defaults, resolved paths, permissions, scope, prompt references, provider/model decisions, and runtime identity.
|
A resolved Manifest is the concrete contract used to create or restore a Worker. It carries defaults, resolved paths, permissions, scope, prompt references, provider/model decisions, and runtime identity.
|
||||||
|
|||||||
@@ -0,0 +1,116 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "canonical_compaction_tokens",
|
||||||
|
"profile": {
|
||||||
|
"compaction": {
|
||||||
|
"kind": "tokens",
|
||||||
|
"prune_enabled": false,
|
||||||
|
"prune_protected_tokens": 3000,
|
||||||
|
"prune_min_savings": 1000,
|
||||||
|
"threshold": 80000,
|
||||||
|
"request_threshold": 90000,
|
||||||
|
"retained_tokens": 12000,
|
||||||
|
"overview_target_tokens": 1000,
|
||||||
|
"overview_warning_tokens": 2000,
|
||||||
|
"overview_deadline_tokens": 3000,
|
||||||
|
"worker_context_max_tokens": 20000,
|
||||||
|
"finish_warning_remaining_tokens": 1000,
|
||||||
|
"final_reserve_tokens": 2000,
|
||||||
|
"worker_max_turns": 5,
|
||||||
|
"summary_target_tokens": 1000,
|
||||||
|
"summary_max_tokens": 2000,
|
||||||
|
"auto_read_budget_tokens": 500,
|
||||||
|
"result_context_max_tokens": 4000,
|
||||||
|
"model": { "ref": "codex-oauth/gpt-5.6-sol" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"manifest_expect": {
|
||||||
|
"/compaction/prune_enabled": false,
|
||||||
|
"/compaction/prune_protected_tokens": 3000,
|
||||||
|
"/compaction/prune_min_savings": 1000,
|
||||||
|
"/compaction/threshold": 80000,
|
||||||
|
"/compaction/request_threshold": 90000,
|
||||||
|
"/compaction/retained_tokens": 12000,
|
||||||
|
"/compaction/overview_target_tokens": 1000,
|
||||||
|
"/compaction/overview_warning_tokens": 2000,
|
||||||
|
"/compaction/overview_deadline_tokens": 3000,
|
||||||
|
"/compaction/worker_context_max_tokens": 20000,
|
||||||
|
"/compaction/finish_warning_remaining_tokens": 1000,
|
||||||
|
"/compaction/final_reserve_tokens": 2000,
|
||||||
|
"/compaction/worker_max_turns": 5,
|
||||||
|
"/compaction/summary_target_tokens": 1000,
|
||||||
|
"/compaction/summary_max_tokens": 2000,
|
||||||
|
"/compaction/auto_read_budget_tokens": 500,
|
||||||
|
"/compaction/result_context_max_tokens": 4000,
|
||||||
|
"/compaction/model/ref": "codex-oauth/gpt-5.6-sol"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "compaction_token_aliases_without_kind",
|
||||||
|
"profile": {
|
||||||
|
"compaction": {
|
||||||
|
"compact_threshold": 80000,
|
||||||
|
"compact_request_threshold": 90000,
|
||||||
|
"compact_retained_tokens": 12000,
|
||||||
|
"compact_worker_max_input_tokens": 20000,
|
||||||
|
"compact_worker_max_turns": 5,
|
||||||
|
"compact_auto_read_budget": 500
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"manifest_expect": {
|
||||||
|
"/compaction/threshold": 80000,
|
||||||
|
"/compaction/request_threshold": 90000,
|
||||||
|
"/compaction/retained_tokens": 12000,
|
||||||
|
"/compaction/worker_context_max_tokens": 20000,
|
||||||
|
"/compaction/worker_max_turns": 5,
|
||||||
|
"/compaction/auto_read_budget_tokens": 500
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "compaction_ratio_aliases",
|
||||||
|
"profile": {
|
||||||
|
"model": { "context_window": 100000 },
|
||||||
|
"compaction": {
|
||||||
|
"kind": "ratio",
|
||||||
|
"threshold": 0.9,
|
||||||
|
"request": 0.8,
|
||||||
|
"worker": 0.2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"manifest_expect": {
|
||||||
|
"/compaction/threshold": 90000,
|
||||||
|
"/compaction/request_threshold": 80000,
|
||||||
|
"/compaction/worker_context_max_tokens": 20000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "numeric_extraction_reasoning",
|
||||||
|
"profile": {
|
||||||
|
"engine": { "reasoning": -1 },
|
||||||
|
"feature": {
|
||||||
|
"memory": { "extraction": { "enabled": true, "reasoning": 4096 } },
|
||||||
|
"subjektiv": { "extraction": { "enabled": true, "reasoning": 4096 } }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"manifest_expect": {
|
||||||
|
"/engine/reasoning": -1,
|
||||||
|
"/feature/memory/profile/extraction/reasoning": 4096,
|
||||||
|
"/feature/subjektiv/profile/extraction/reasoning": 4096
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "string_extraction_reasoning",
|
||||||
|
"profile": {
|
||||||
|
"feature": {
|
||||||
|
"memory": { "extraction": { "enabled": true, "reasoning": "high" } },
|
||||||
|
"subjektiv": {
|
||||||
|
"extraction": { "enabled": true, "reasoning": "medium" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"manifest_expect": {
|
||||||
|
"/feature/memory/profile/extraction/reasoning": "high",
|
||||||
|
"/feature/subjektiv/profile/extraction/reasoning": "medium"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -9,7 +9,8 @@ let
|
|||||||
capability = Unknown; context_window = Int; max_context_window = Int;
|
capability = Unknown; context_window = Int; max_context_window = Int;
|
||||||
};
|
};
|
||||||
Extraction = {
|
Extraction = {
|
||||||
enabled = Bool; model = Model; reasoning = String;
|
# ReasoningControl accepts string effort or signed integer budget.
|
||||||
|
enabled = Bool; model = Model; reasoning = Unknown;
|
||||||
threshold = Int; worker_max_turns = Int;
|
threshold = Int; worker_max_turns = Int;
|
||||||
};
|
};
|
||||||
# Profile scope accepts either a string intent or a table. Decodal 0.4 has
|
# Profile scope accepts either a string intent or a table. Decodal 0.4 has
|
||||||
@@ -56,9 +57,19 @@ let
|
|||||||
env = { inherit = [...String]; set = { ...Unknown }; };
|
env = { inherit = [...String]; set = { ...Unknown }; };
|
||||||
}]; };
|
}]; };
|
||||||
compaction = {
|
compaction = {
|
||||||
|
# Token settings (CompactionConfigPartial) and ratio helper fields.
|
||||||
|
# Threshold/context values accept integer tokens or numeric ratios.
|
||||||
kind = String; threshold = Unknown; request_threshold = Unknown;
|
kind = String; threshold = Unknown; request_threshold = Unknown;
|
||||||
prune_enabled = Bool; worker_context_max_tokens = Unknown;
|
worker_context_max_tokens = Unknown; request = Unknown; worker = Unknown;
|
||||||
worker_max_turns = Int; model = Model;
|
prune_enabled = Bool; prune_protected_tokens = Int; prune_min_savings = Int;
|
||||||
|
retained_tokens = Int;
|
||||||
|
overview_target_tokens = Int; overview_warning_tokens = Int; overview_deadline_tokens = Int;
|
||||||
|
finish_warning_remaining_tokens = Int; final_reserve_tokens = Int;
|
||||||
|
worker_max_turns = Int; summary_target_tokens = Int; summary_max_tokens = Int;
|
||||||
|
auto_read_budget_tokens = Int; result_context_max_tokens = Int; model = Model;
|
||||||
|
# Supported token aliases remain authorable without rewriting recipes.
|
||||||
|
compact_threshold = Int; compact_request_threshold = Int; compact_retained_tokens = Int;
|
||||||
|
compact_worker_max_input_tokens = Int; compact_worker_max_turns = Int; compact_auto_read_budget = Int;
|
||||||
};
|
};
|
||||||
web = {
|
web = {
|
||||||
enabled = Bool; allow_private_addresses = Bool;
|
enabled = Bool; allow_private_addresses = Bool;
|
||||||
|
|||||||
@@ -382,6 +382,22 @@ Deno.test("generated WASM completes asserted WorkspaceConfigSchema keys", () =>
|
|||||||
assertEquals(result.items.some((item) => item.label === "profile"), true);
|
assertEquals(result.items.some((item) => item.label === "profile"), true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function fixtureDecodal(value: unknown): string {
|
||||||
|
if (Array.isArray(value)) return `[${value.map(fixtureDecodal).join(", ")}]`;
|
||||||
|
if (value !== null && typeof value === "object") {
|
||||||
|
return `{ ${
|
||||||
|
Object.entries(value).map(([key, value]) =>
|
||||||
|
`${key} = ${fixtureDecodal(value)};`
|
||||||
|
).join(" ")
|
||||||
|
} }`;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
typeof value === "string" || typeof value === "number" ||
|
||||||
|
typeof value === "boolean"
|
||||||
|
) return JSON.stringify(value);
|
||||||
|
throw new Error("fixture omission must not be represented as null/undefined");
|
||||||
|
}
|
||||||
|
|
||||||
Deno.test("generated WASM authors value-based Profiles using the Backend schema without materializing authoring fields", async () => {
|
Deno.test("generated WASM authors value-based Profiles using the Backend schema without materializing authoring fields", async () => {
|
||||||
const source = await Deno.readTextFile(
|
const source = await Deno.readTextFile(
|
||||||
new URL(
|
new URL(
|
||||||
@@ -656,6 +672,85 @@ Deno.test("generated WASM authors value-based Profiles using the Backend schema
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const cases = JSON.parse(
|
||||||
|
await Deno.readTextFile(
|
||||||
|
new URL(
|
||||||
|
"../../../../resources/config-schema/profile-authoring-values.json",
|
||||||
|
import.meta.url,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
) as Array<{ name: string; profile: Record<string, unknown> }>;
|
||||||
|
for (const fixture of cases) {
|
||||||
|
const recipe = fixtureDecodal(fixture.profile);
|
||||||
|
for (
|
||||||
|
const [form, patched] of [[recipe, false], [
|
||||||
|
'import "./recipe.dcdl"',
|
||||||
|
false,
|
||||||
|
], [
|
||||||
|
'(import "./recipe.dcdl") // { description = "patched"; }',
|
||||||
|
true,
|
||||||
|
]] as const
|
||||||
|
) {
|
||||||
|
const fixtureContent = content.replace(
|
||||||
|
"profile = {};",
|
||||||
|
`profile = ${form};`,
|
||||||
|
);
|
||||||
|
const fixtureTree = {
|
||||||
|
...tree,
|
||||||
|
entries: {
|
||||||
|
"main.dcdl": {
|
||||||
|
...tree.entries["main.dcdl"],
|
||||||
|
content: fixtureContent,
|
||||||
|
content_digest: await digestText(fixtureContent),
|
||||||
|
},
|
||||||
|
"recipe.dcdl": {
|
||||||
|
path: "recipe.dcdl",
|
||||||
|
content_type: "decodal",
|
||||||
|
content: recipe,
|
||||||
|
content_digest: await digestText(recipe),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
assertEquals(
|
||||||
|
analyze_snapshot(fixtureTree, "main.dcdl", undefined),
|
||||||
|
[],
|
||||||
|
`${fixture.name}: ${form}`,
|
||||||
|
);
|
||||||
|
const result = evaluate_snapshot(
|
||||||
|
fixtureTree,
|
||||||
|
valueContract,
|
||||||
|
) as typeof evaluated;
|
||||||
|
assertEquals(
|
||||||
|
result.projections[0].data_json.profile.entries[0].profile,
|
||||||
|
patched
|
||||||
|
? { ...fixture.profile, description: "patched" }
|
||||||
|
: fixture.profile,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (
|
||||||
|
const [path, prefix, label] of [
|
||||||
|
["compaction", "reta", "retained_tokens"],
|
||||||
|
["compaction", "prune_min", "prune_min_savings"],
|
||||||
|
["compaction", "req", "request"],
|
||||||
|
["compaction", "wor", "worker"],
|
||||||
|
["compaction", "compact_ret", "compact_retained_tokens"],
|
||||||
|
["feature.memory.extraction", "reas", "reasoning"],
|
||||||
|
["feature.subjektiv.extraction", "reas", "reasoning"],
|
||||||
|
]
|
||||||
|
) {
|
||||||
|
const body =
|
||||||
|
`{ profile.entries = [{ profile.${path} = { ${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,
|
||||||
|
`${path}.${label}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
// Neither completion nor diagnostic shape checking changes the saved value.
|
// Neither completion nor diagnostic shape checking changes the saved value.
|
||||||
assertEquals(evaluate_snapshot(tree, valueContract), evaluated);
|
assertEquals(evaluate_snapshot(tree, valueContract), evaluated);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user