1use std::fmt::Write as _;
2
3use serde::Serialize;
4
5pub const SEMANTIC_CAPABILITY_REGISTRY_SCHEMA_VERSION: u32 = 1;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
9#[serde(rename_all = "snake_case")]
10pub enum SemanticCapabilityClass {
11 Native,
12 Bounded,
13 Opaque,
14 Unsupported,
15}
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
19#[serde(rename_all = "snake_case")]
20pub enum SemanticCapabilityStatus {
21 Admitted,
22 Deferred,
23}
24
25#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
27pub struct SemanticCapability {
28 pub id: &'static str,
29 pub class: SemanticCapabilityClass,
30 pub status: SemanticCapabilityStatus,
31 pub source_form: &'static str,
32 pub semantic_owner: &'static str,
33 pub type_rule: &'static str,
34 pub dependency_rule: &'static str,
35 pub resume_policy: &'static str,
36 pub artifact_impact: &'static str,
37 pub proof_fixture: &'static str,
38 #[serde(skip_serializing_if = "Option::is_none")]
39 pub rejection_reason: Option<&'static str>,
40}
41
42#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
44pub struct SemanticCapabilityRegistry {
45 pub schema_version: u32,
46 pub capabilities: Vec<SemanticCapability>,
47}
48
49#[must_use]
50pub fn build_semantic_capability_registry() -> SemanticCapabilityRegistry {
51 SemanticCapabilityRegistry {
52 schema_version: SEMANTIC_CAPABILITY_REGISTRY_SCHEMA_VERSION,
53 capabilities: vec![
54 admitted(
55 "component",
56 SemanticCapabilityClass::Native,
57 "export class Name extends Component",
58 "component definition and instance plan",
59 "compiler-recognized component declaration",
60 "compiler-owned instance identity and composition",
61 "resumable through component artifacts",
62 "HTML, component runtime, resume artifacts",
63 "fixtures/0062-component-declarations/input/ValidComponents.tsx",
64 ),
65 admitted(
66 "component_invocation",
67 SemanticCapabilityClass::Bounded,
68 "resolved JSX component invocation with compiler-supported inputs and Slots",
69 "component invocation, composition, instance, ownership, and lifecycle products",
70 "statically resolved component target with compiler-supported composition grammar",
71 "compiler-owned invocation topology, caller/callee ownership, and instance identity",
72 "component instances, Slots, and Context bindings use existing resume identities",
73 "component runtime and resume artifacts",
74 "component_fixtures::component_composition_fixture_covers_topology_slots_caller_ownership_and_blocking",
75 ),
76 admitted(
77 "state",
78 SemanticCapabilityClass::Bounded,
79 "field = state(serializableInitializer)",
80 "component-instance State storage",
81 "compiler-supported serializable initializer",
82 "compiler-derived reads and Action writes",
83 "resumable when the value schema is supported",
84 "runtime State slots and bindings",
85 "examples/counter/src/Counter.tsx",
86 ),
87 admitted(
88 "serializable_state_replacement",
89 SemanticCapabilityClass::Bounded,
90 "field = state(recordOrArray); @action() { this.field = serializableRecordOrArray; }",
91 "component-instance State storage, Action plan, runtime field write, and resume codec",
92 "recursively serializable record or array literal at the whole-field boundary",
93 "Action-owned field replacement; no nested mutable alias or untracked write",
94 "existing State resume codec carries the complete replacement value",
95 "existing action manifest assign operand and State runtime slot",
96 "runtime_browser::serializable_record_state_replacement_executes_from_compiler_generated_runtime",
97 ),
98 admitted(
99 "static_action_parameters",
100 SemanticCapabilityClass::Bounded,
101 "method = action((value: primitive) => { this.field = value; }); onClick={() => this.method(serializableLiteral)}",
102 "Action parameter, State write, event binding, ordinary-instance plan, and runtime batch",
103 "one or more explicitly typed primitive parameters supplied by exact serializable callback literals",
104 "the compiler carries static arguments from the event declaration into the one completed Action batch; no DOM payload or closure capture is read",
105 "arguments are static manifest data; the resulting State replacement uses the existing State resume codec",
106 "template-manifest schema v5 and component-runtime schema v4 ordinary-event argument records with assign_parameter action operation",
107 "runtime_browser::static_callback_argument_updates_state_through_compiler_action_parameter",
108 ),
109 admitted(
110 "action_parameter_state_types",
111 SemanticCapabilityClass::Bounded,
112 "method = action((value: primitive) => { this.stateField = value; })",
113 "Action parameter and component-instance State type boundary",
114 "parameter and State must have the same compiler-known primitive kind, declared or inferred from the State initializer",
115 "type compatibility is checked before Action/runtime lowering; it adds no dynamic dependency",
116 "the existing compatible State slot and resume codec remain authoritative",
117 "canonical PSC1044 diagnostic; no new runtime or artifact field",
118 "tests::component_graph_rejects_action_parameter_state_type_mismatch",
119 ),
120 admitted(
121 "serializable_action_locals",
122 SemanticCapabilityClass::Bounded,
123 "method = action(() => { const local = serializableLiteral; this.stateField = local; })",
124 "Action-local declaration, complete State write, and existing Action batch",
125 "one exact serializable local initializer with primitive compatibility to the target State field",
126 "the local is compile-time data only; it introduces no runtime dependency, capture, or alias",
127 "the resulting complete-State replacement uses the existing State resume codec",
128 "existing assign Action operand and runtime State write; no new artifact field",
129 "runtime_browser::serializable_action_local_updates_state_from_compiler_generated_runtime",
130 ),
131 admitted(
132 "structured_serializable_action_locals",
133 SemanticCapabilityClass::Bounded,
134 "method = action(() => { const local = serializableRecordOrArray; this.stateField = local; })",
135 "Action-local declaration, complete structured State write, and existing Action batch",
136 "recursive literal shape compatible with the State initializer: exact object keys and homogeneous array element shape",
137 "the local is resolved to one compiler-issued literal operand with no runtime alias, capture, or dependency",
138 "the existing complete-State resume codec owns the structured value",
139 "existing assign Action operand; no new runtime evaluator or artifact field",
140 "runtime_browser::structured_serializable_action_local_executes_from_compiler_generated_runtime",
141 ),
142 admitted(
143 "action",
144 SemanticCapabilityClass::Bounded,
145 "method = action(() => { /* compiler-supported State writes */ })",
146 "completed Action batch",
147 "compiler-supported parameters and body",
148 "compiler-derived State writes and event activation",
149 "lazy activation and resume through emitted action records",
150 "runtime action chunks",
151 "examples/counter/src/Counter.tsx",
152 ),
153 admitted(
154 "computed",
155 SemanticCapabilityClass::Bounded,
156 "get value()",
157 "compiler-owned Computed value",
158 "getter-only supported expression subset",
159 "compiler-derived dependency graph",
160 "recomputed from resumed State; no authored cache",
161 "Computed IR and runtime artifact",
162 "fixtures/0047-computed-diamond/input/ComputedDiamond.tsx",
163 ),
164 admitted(
165 "effect",
166 SemanticCapabilityClass::Bounded,
167 "effectField = effect(() => { /* compiler-known capability program */ })",
168 "terminal capability program",
169 "compiler-known capability arguments only",
170 "compiler-derived affected Action batches",
171 "not independently resumed; runs by compiler policy",
172 "effect IR and runtime capability artifact",
173 "fixtures/0053-effect-initial-runtime/input/InitialEffectRuntime.tsx",
174 ),
175 admitted(
176 "context",
177 SemanticCapabilityClass::Bounded,
178 "legacy decorators: @context() static value; @provide(\"Owner.value\"); @consume(\"Owner.value\")",
179 "Context declaration, provider, and consumer identities",
180 "declared structural type boundary",
181 "compiler-owned visibility and provider selection",
182 "compiler-owned Context slots and resume records",
183 "Context IR and runtime artifact",
184 "fixtures/0059-context-runtime-matrix/input/ContextRuntimeMatrix.tsx",
185 ),
186 admitted(
187 "slot",
188 SemanticCapabilityClass::Bounded,
189 "content: SlotContent = slot(); <slot />",
190 "Slot declaration and caller-owned content",
191 "compiler-owned SlotContent marker",
192 "compiler-owned lexical owner and binding",
193 "resumable through Slot-binding records",
194 "component runtime Slot programs",
195 "fixtures/0062-component-declarations/input/ValidComponents.tsx",
196 ),
197 admitted(
198 "keyed_structural_list",
199 SemanticCapabilityClass::Bounded,
200 "compiler-supported iterable.map((item) => <element key={item.id} />)",
201 "template graph, compiler-issued keyed item identity, and structural runtime plan",
202 "explicit unique primitive key and supported item template grammar",
203 "compiler-owned iterable dependency and keyed instance lifecycle",
204 "keyed structural identity is carried by compiler artifacts and resume anchors",
205 "template/component runtime structural list records",
206 "runtime_browser::keyed_lists_reconcile_in_a_real_browser",
207 ),
208 admitted(
209 "jsx_html_attribute_aliases",
210 SemanticCapabilityClass::Bounded,
211 "className and htmlFor on compiler-supported intrinsic elements",
212 "template lowering before semantic analysis, manifests, static HTML, and runtime bindings",
213 "exact className or htmlFor spelling with a static or compiler-supported string binding",
214 "compiler-derived binding dependency after normalization to class or for",
215 "uses the existing attribute-binding State resume path",
216 "canonical HTML class/for template and runtime attribute records",
217 "template_graph::tests::normalizes_jsx_html_attribute_aliases",
218 ),
219 admitted(
220 "typed_aria_bindings",
221 SemanticCapabilityClass::Bounded,
222 "dynamic role plus aria-label/aria-describedby/aria-errormessage/aria-controls/aria-current/aria-live string or aria-invalid/aria-busy/aria-expanded/aria-pressed/aria-hidden boolean JSX attribute",
223 "template attribute binding, semantic type assignment, ordinary runtime binding, and DOM attribute update",
224 "exact compiler-known boolean or string contract for the admitted ARIA name",
225 "the attribute expression uses the existing compiler-derived State/Computed dependency path",
226 "no independent resume value; the existing binding source resumes through its owner",
227 "existing template/component runtime attribute binding records",
228 "runtime_browser::typed_aria_attribute_updates_in_a_real_browser",
229 ),
230 admitted(
231 "keyboard_action_event",
232 SemanticCapabilityClass::Bounded,
233 "onKeydown={this.actionMethod} on a compiler-supported element",
234 "template event, Action batch, ordinary-instance event record, and delegated runtime listener",
235 "exact keydown event with an existing zero-parameter compiler Action",
236 "event activates the compiler-owned Action batch only; keyboard payload fields are not projected",
237 "uses the existing Action activation and State resume path",
238 "existing template and ordinary event artifacts with event_type keydown",
239 "runtime_browser::structured_serializable_action_local_executes_from_compiler_generated_runtime",
240 ),
241 admitted(
242 "form",
243 SemanticCapabilityClass::Bounded,
244 "legacy decorators: @form(); @field(\"form\"); @submit(\"form\")",
245 "Form, Field, validation, and submission identities",
246 "compiler-supported control and serializable field types",
247 "compiler-owned Form plans and explicit host markers",
248 "resumable through Form schema and records",
249 "Forms runtime artifact",
250 "fixtures/framework/resume-forms.tsx",
251 ),
252 admitted(
253 "module_bindings",
254 SemanticCapabilityClass::Bounded,
255 "local/relative import, export, re-export, namespace import, and local type alias",
256 "CompilationUnit, ModuleGraph, BindingTable, and local type bindings",
257 "local and relative binding forms retained by the parser",
258 "compiler-resolved module edges and named bindings",
259 "module bindings carry no independent runtime state",
260 "canonical binding-table product",
261 "binding_table::tests::resolves_relative_named_default_and_namespace_imports",
262 ),
263 deferred(
264 "advanced_types",
265 SemanticCapabilityClass::Unsupported,
266 "generic utilities, structural imported types, and checker-dependent TypeScript semantics",
267 "N1 advanced type products",
268 "no versioned compiler-owned TypeScript front-end contract yet",
269 "cannot derive canonical structural type bindings",
270 "no resume policy before admission",
271 "no artifact representation",
272 "N1-B must define advanced type semantics",
273 ),
274 admitted(
275 "semantic_package_bindings",
276 SemanticCapabilityClass::Bounded,
277 "named or default external import with a caller-supplied semantic package contract",
278 "SemanticPackageContract and BindingTable",
279 "validated schema, SHA-256 integrity, declared named/default export",
280 "declared binding only; executable use awaits its semantic-kind admission",
281 "binding itself owns no independently resumable state",
282 "canonical binding-table product records package/version/integrity/export",
283 "binding_table::tests::resolves_external_imports_only_through_semantic_package_contracts",
284 ),
285 admitted(
286 "external_package_inputs",
287 SemanticCapabilityClass::Native,
288 "any platform-compatible package import used outside a Presolve semantic position",
289 "TypeScript module resolution and the project Vite publication pass",
290 "the package manager and Vite must resolve the selected browser export",
291 "Presolve records no semantic dependency for an erased or ordinary bundled use",
292 "the package owns its own runtime behavior; Presolve owns no resumable state for this class",
293 "the package is present only in Vite output and the publication inventory",
294 "examples/package-interop plus application_semantic_model::tests::file_route_assembly_admits_only_authority_proven_terminal_package_invocations",
295 ),
296 admitted(
297 "terminal_package_invocation",
298 SemanticCapabilityClass::Bounded,
299 "decorator-free action(() => { namedPackageImport(); }) with one synchronous zero-argument result-discarded call",
300 "TypeScript declaration identity, canonical V2 Action endpoint, package invocation artifact, and Vite registry",
301 "one exact named import; the call must be the sole statement and must accept zero arguments",
302 "the invocation participates in diagnostics and Action event ownership but declares no reactive dependencies",
303 "the Action endpoint is restored through the normal event plan; the package owns no snapshot slot",
304 "package-invocations.runtime.json and /presolve.package-invocations.js",
305 "examples/package-interop and real-browser click proof",
306 ),
307 admitted(
308 "semantic_package_pure_identity",
309 SemanticCapabilityClass::Bounded,
310 "direct call to a declared pure package export with pure_operation: identity in @computed()",
311 "SemanticPackageContract, ExpressionGraph, canonical IR, and runtime-computed artifact",
312 "one declared argument; result inherits the argument semantic type and serialization boundary",
313 "inherits the argument's compiler-derived reactive dependencies; no package code executes",
314 "input-only; no package-owned state or activation record",
315 "runtime-computed schema v4 pure-package-call instruction with package provenance",
316 "runtime_browser::pure_package_contracts_execute_only_the_compiler_lowered_operation_in_a_real_browser",
317 ),
318 admitted(
319 "template_interpolation",
320 SemanticCapabilityClass::Bounded,
321 "untagged template literal in a supported @computed() getter",
322 "parser, ExpressionGraph, canonical IR, and runtime-computed artifact",
323 "cooked static segments and compiler-supported interpolation expressions",
324 "compiler-derived union of every interpolation dependency",
325 "serializable string result; no independent resume record",
326 "runtime-computed schema v5 template instruction, retained in schema v6",
327 "runtime_browser::template_interpolations_execute_from_compiler_generated_runtime_programs",
328 ),
329 admitted(
330 "static_index_access",
331 SemanticCapabilityClass::Bounded,
332 "this.value[non-negative integer literal] or this.value[\"string literal\"] in a supported @computed() getter",
333 "parser, ExpressionGraph, canonical IR, and runtime-computed artifact",
334 "tuple, array, or object read with a literal string or non-negative integer index",
335 "compiler-derived dependency on the indexed object only; the literal index has no reactive dependency",
336 "serializable result when the selected value is serializable; no independent resume record",
337 "runtime-computed schema v6 get-index instruction, retained in schema v8",
338 "runtime_browser::static_index_accesses_execute_from_compiler_generated_runtime_programs",
339 ),
340 admitted(
341 "boolean_computed_conditional",
342 SemanticCapabilityClass::Bounded,
343 "boolean condition ? compiler-supported consequent : compiler-supported alternate in @computed()",
344 "parser, ExpressionGraph, canonical IR, and runtime-computed artifact",
345 "boolean condition with serializable compiler-supported branch values",
346 "compiler-derived union of condition and both branch dependencies",
347 "serializable selected result; no independent resume record",
348 "runtime-computed schema v7 select instruction, retained in schema v8",
349 "runtime_browser::boolean_conditional_computed_values_execute_from_compiler_generated_runtime_programs",
350 ),
351 admitted(
352 "builtin_math_abs",
353 SemanticCapabilityClass::Bounded,
354 "Math.abs(value) in a supported @computed() getter",
355 "compiler-registered BuiltinPureOperation, canonical IR, and runtime-computed artifact",
356 "exactly one compiler-supported numeric operand",
357 "inherits the operand's compiler-derived dependency set",
358 "serializable numeric result; no package or independent resume record",
359 "runtime-computed schema v9 unary abs operation",
360 "runtime_browser::registered_math_abs_executes_from_compiler_generated_runtime_programs",
361 ),
362 admitted(
363 "builtin_math_min_max",
364 SemanticCapabilityClass::Bounded,
365 "Math.min(left, right) or Math.max(left, right) in a supported @computed() getter",
366 "compiler-registered BuiltinPureOperation, canonical binary IR, and runtime-computed artifact",
367 "exactly two compiler-supported numeric operands",
368 "union of the compiler-derived dependencies of both operands",
369 "serializable numeric result; no package or independent resume record",
370 "runtime-computed schema v10 binary min/max operations",
371 "runtime_browser::registered_math_min_max_execute_from_compiler_generated_runtime_programs",
372 ),
373 admitted(
374 "builtin_math_rounding",
375 SemanticCapabilityClass::Bounded,
376 "Math.floor(value), Math.ceil(value), or Math.round(value) in a supported @computed() getter",
377 "compiler-registered BuiltinPureOperation, canonical unary IR, and runtime-computed artifact",
378 "exactly one compiler-supported numeric operand",
379 "inherits the operand's compiler-derived dependency set",
380 "serializable numeric result; no package or independent resume record",
381 "runtime-computed schema v12, including unary floor/ceil/round operations",
382 "runtime_browser::registered_math_rounding_executes_from_compiler_generated_runtime_programs",
383 ),
384 deferred(
385 "semantic_package_exports",
386 SemanticCapabilityClass::Unsupported,
387 "all other pure operations and capability, resource, codec, or component package exports",
388 "N1-A2 semantic-kind lowering products",
389 "declared export metadata is not yet an executable compiler semantic",
390 "cannot derive reactive, capability, resource, codec, or component behavior from a binding alone",
391 "no export-kind resume policy has been admitted",
392 "no package-kind IR or artifact provenance has been admitted",
393 "N1-A2 must admit each package kind through full-path lowering",
394 ),
395 admitted(
396 "resources",
397 SemanticCapabilityClass::Bounded,
398 "legacy decorator: @resource(\"importedEndpoint\") field!: Resource<Data, Error>",
399 "integrity-checked semantic-package endpoint, Resource declaration and activation identity, browser runtime artifact, and generated runtime",
400 "one exactly imported semantic-package resource endpoint, Resource<Data, Error> field type, exact host-supplied runtime module location, and client/shared execution boundary; server/shared endpoints are admitted only through a route-loader handoff",
401 "one compiler-owned cold activation per planned component instance; direct same-owner @computed() .data/.error/.state reads and terminal invalidation are admitted, while inputs and retry remain deferred",
402 "snapshot policy restores validated state/data/error triples before dependent Computeds without importing the endpoint; reload has no snapshot slot and starts one post-restore generation; malformed, pending, or cancelled snapshots cold-fallback",
403 "runtime resource schema v3 plus runtime-computed schema v12, embedded page artifacts, exact resume slots/codecs, and exact runtime module coordinate",
404 "runtime_browser::host_bound_resource_endpoint_activates_in_a_real_browser",
405 ),
406 admitted(
407 "route_loaders",
408 SemanticCapabilityClass::Bounded,
409 "legacy decorator: @loader(\"importedEndpoint\") field!: Resource<Data, Error> on a conventional route page",
410 "compiler route graph, binding table, semantic-package contract, and RouteLoaderPlanV1 publication",
411 "one integrity-bound server/shared resource export with route_parameters input, valid cache policy, and typed failure",
412 "route parameters and cache policy are closed package facts; no browser reactive dependency or package source is evaluated",
413 "server handoffs have no browser resume record or executor; a route carrying one is classified node-only",
414 "route-loaders.plan.json schema v1 and the compiler-issued Node release inventory",
415 "ergonomic_project::default_check_and_build_publish_a_compiler_route_loader_handoff",
416 ),
417 admitted(
418 "server_actions",
419 SemanticCapabilityClass::Bounded,
420 "empty synchronous @serverAction(\"importedEndpoint\") method on a conventional route page",
421 "compiler route graph, binding table, semantic-package contract, and RouteServerActionPlanV1 publication",
422 "one integrity-bound server_action export with FormData -> ServerActionResult, cold_fallback, form_data input, json or redirect response, and typed failure",
423 "the declaration has no parameters or body effects; the compiler records the closed server capability without reading package or application method source",
424 "server actions are cold-fallback handoffs with no browser resume record or executor; a route carrying one is classified node-only",
425 "route-server-actions.plan.json schema v1 and the compiler-issued Node release inventory",
426 "ergonomic_project::default_check_and_build_publish_a_compiler_route_server_action_handoff",
427 ),
428 admitted(
429 "opaque_typescript",
430 SemanticCapabilityClass::Opaque,
431 "legacy decorators: @action() @opaque(\"package\", \"export\") method(): void {}",
432 "compiler-issued opaque terminal activation attached to one Action method",
433 "empty synchronous zero-parameter Action; exact imported opaque semantic-package export with () -> void client terminal contract",
434 "terminal after the compiler-owned Action batch; no State, Form, Context, Resource, or render dependency access",
435 "opaque terminals force cold resume fallback; no opaque snapshot codec exists",
436 "opaque.runtime.json schema v1, embedded page metadata, and exact integrity-bound runtime module coordinate",
437 "runtime_browser::integrity_bound_opaque_terminal_runs_only_from_a_compiler_action_in_a_real_browser",
438 ),
439 ],
440 }
441}
442
443#[must_use]
444pub fn semantic_capability_registry_json() -> String {
445 serde_json::to_string_pretty(&build_semantic_capability_registry())
446 .expect("semantic capability registry should serialize")
447 + "\n"
448}
449
450#[must_use]
454pub fn semantic_capability_matrix_text() -> String {
455 let registry = build_semantic_capability_registry();
456 let mut output = format!(
457 "Presolve semantic capability matrix (schema v{})\n\n",
458 registry.schema_version
459 );
460 output.push_str("id | class | status | source form | proof fixture\n");
461 output.push_str("--- | --- | --- | --- | ---\n");
462 for capability in registry.capabilities {
463 let proof_fixture = if capability.proof_fixture.is_empty() {
464 "-"
465 } else {
466 capability.proof_fixture
467 };
468 writeln!(
469 output,
470 "{} | {} | {} | {} | {}",
471 capability.id,
472 capability_class_text(capability.class),
473 capability_status_text(capability.status),
474 capability.source_form,
475 proof_fixture
476 )
477 .expect("writing capability matrix cannot fail");
478 if let Some(reason) = capability.rejection_reason {
479 writeln!(output, " rejection: {reason}")
480 .expect("writing capability matrix cannot fail");
481 }
482 }
483 output
484}
485
486#[must_use]
489pub fn semantic_capability_migration_text() -> String {
490 let registry = build_semantic_capability_registry();
491 let deferred = registry
492 .capabilities
493 .iter()
494 .filter(|capability| capability.status == SemanticCapabilityStatus::Deferred)
495 .collect::<Vec<_>>();
496 let mut output = format!(
497 "Presolve semantic compatibility guide (registry schema v{})\n\n",
498 registry.schema_version
499 );
500 output.push_str("Compatibility policy\n\n");
501 output.push_str("- The registry is the exact compiler capability boundary.\n");
502 output.push_str("- Admitted records are supported only under their listed compiler rules.\n");
503 output.push_str("- Deferred records have no compatibility fallback, source rewrite, or opaque escape hatch.\n\n");
504 output.push_str("Migration guide\n\n");
505 for capability in &deferred {
506 writeln!(
507 output,
508 "- {}: {}\n reason: {}",
509 capability.id,
510 capability.source_form,
511 capability
512 .rejection_reason
513 .expect("deferred capability has a rejection reason")
514 )
515 .expect("writing capability migration guide cannot fail");
516 }
517 output.push_str("\nRejected syntax catalog\n\n");
518 for capability in deferred {
519 writeln!(
520 output,
521 "- {} | {} | {}",
522 capability.id,
523 capability_class_text(capability.class),
524 capability
525 .rejection_reason
526 .expect("deferred capability has a rejection reason")
527 )
528 .expect("writing rejected syntax catalog cannot fail");
529 }
530 output
531}
532
533fn capability_class_text(class: SemanticCapabilityClass) -> &'static str {
534 match class {
535 SemanticCapabilityClass::Native => "native",
536 SemanticCapabilityClass::Bounded => "bounded",
537 SemanticCapabilityClass::Opaque => "opaque",
538 SemanticCapabilityClass::Unsupported => "unsupported",
539 }
540}
541
542fn capability_status_text(status: SemanticCapabilityStatus) -> &'static str {
543 match status {
544 SemanticCapabilityStatus::Admitted => "admitted",
545 SemanticCapabilityStatus::Deferred => "deferred",
546 }
547}
548
549#[allow(clippy::too_many_arguments)]
550fn admitted(
551 id: &'static str,
552 class: SemanticCapabilityClass,
553 source_form: &'static str,
554 semantic_owner: &'static str,
555 type_rule: &'static str,
556 dependency_rule: &'static str,
557 resume_policy: &'static str,
558 artifact_impact: &'static str,
559 proof_fixture: &'static str,
560) -> SemanticCapability {
561 SemanticCapability {
562 id,
563 class,
564 status: SemanticCapabilityStatus::Admitted,
565 source_form,
566 semantic_owner,
567 type_rule,
568 dependency_rule,
569 resume_policy,
570 artifact_impact,
571 proof_fixture,
572 rejection_reason: None,
573 }
574}
575
576#[allow(clippy::too_many_arguments)]
577fn deferred(
578 id: &'static str,
579 class: SemanticCapabilityClass,
580 source_form: &'static str,
581 semantic_owner: &'static str,
582 type_rule: &'static str,
583 dependency_rule: &'static str,
584 resume_policy: &'static str,
585 artifact_impact: &'static str,
586 rejection_reason: &'static str,
587) -> SemanticCapability {
588 SemanticCapability {
589 id,
590 class,
591 status: SemanticCapabilityStatus::Deferred,
592 source_form,
593 semantic_owner,
594 type_rule,
595 dependency_rule,
596 resume_policy,
597 artifact_impact,
598 proof_fixture: "",
599 rejection_reason: Some(rejection_reason),
600 }
601}
602
603#[cfg(test)]
604mod tests {
605 use super::*;
606
607 #[test]
608 fn registry_is_versioned_stable_and_explains_deferred_families() {
609 let registry = build_semantic_capability_registry();
610 assert_eq!(registry.schema_version, 1);
611 assert_eq!(
612 registry
613 .capabilities
614 .iter()
615 .map(|capability| capability.id)
616 .collect::<Vec<_>>(),
617 vec![
618 "component",
619 "component_invocation",
620 "state",
621 "serializable_state_replacement",
622 "static_action_parameters",
623 "action_parameter_state_types",
624 "serializable_action_locals",
625 "structured_serializable_action_locals",
626 "action",
627 "computed",
628 "effect",
629 "context",
630 "slot",
631 "keyed_structural_list",
632 "jsx_html_attribute_aliases",
633 "typed_aria_bindings",
634 "keyboard_action_event",
635 "form",
636 "module_bindings",
637 "advanced_types",
638 "semantic_package_bindings",
639 "external_package_inputs",
640 "terminal_package_invocation",
641 "semantic_package_pure_identity",
642 "template_interpolation",
643 "static_index_access",
644 "boolean_computed_conditional",
645 "builtin_math_abs",
646 "builtin_math_min_max",
647 "builtin_math_rounding",
648 "semantic_package_exports",
649 "resources",
650 "route_loaders",
651 "server_actions",
652 "opaque_typescript"
653 ]
654 );
655 assert!(registry
656 .capabilities
657 .iter()
658 .filter(|capability| capability.status == SemanticCapabilityStatus::Deferred)
659 .all(|capability| capability.rejection_reason.is_some()));
660 assert!(semantic_capability_registry_json().contains("\"semantic_package_bindings\""));
661 assert!(semantic_capability_registry_json().contains("\"static_index_access\""));
662 let matrix = semantic_capability_matrix_text();
663 assert_eq!(matrix, semantic_capability_matrix_text());
664 assert!(matrix.starts_with("Presolve semantic capability matrix (schema v1)\n\n"));
665 assert_eq!(
666 matrix.matches("\ncomponent | native | admitted |").count(),
667 1
668 );
669 assert!(matrix.contains("advanced_types | unsupported | deferred |"));
670 assert!(matrix.contains(" rejection: N1-B must define advanced type semantics\n"));
671 let migration = semantic_capability_migration_text();
672 assert_eq!(migration, semantic_capability_migration_text());
673 assert!(
674 migration.starts_with("Presolve semantic compatibility guide (registry schema v1)\n\n")
675 );
676 assert!(!migration.contains("- opaque_typescript:"));
677 assert!(!migration.contains("- resources:"));
678 assert!(registry.capabilities.iter().any(|capability| {
679 capability.id == "resources"
680 && capability.status == SemanticCapabilityStatus::Admitted
681 && capability.class == SemanticCapabilityClass::Bounded
682 }));
683 assert!(registry.capabilities.iter().any(|capability| {
684 capability.id == "opaque_typescript"
685 && capability.status == SemanticCapabilityStatus::Admitted
686 && capability.class == SemanticCapabilityClass::Opaque
687 }));
688 }
689}