diff --git a/docs/projection-primitives.md b/docs/projection-primitives.md index 9e7c318..e5032e8 100644 --- a/docs/projection-primitives.md +++ b/docs/projection-primitives.md @@ -7,14 +7,33 @@ Command Generation owns only the mechanics: - read a source value from the operation value map; - split declared selector strings into exact dot paths; - resolve object fields and list indexes; -- return a selected-output wrapper with `values`, `missing`, and `available_selectors`. +- return a selected-output wrapper with `values` only when every requested selector resolves; +- reject selector requests with more than 32 selectors, any selector longer + than 256 UTF-8 bytes, or more than 512 cumulative selector-name UTF-8 bytes + before projection; +- return a bounded selector-validation error when any selector is unknown. + +Selector validation is atomic. A request with any unknown selector does not return +partial projected values. The validation error reports the requested selectors, +the unknown selectors, a small selector sample, the available selector count, +bounded suggestions, and discovery commands. It intentionally omits the complete +selector catalog from the error path. + +Selector request validation is also atomic. A request that exceeds the selector +count or UTF-8 byte budgets returns an `invalid-selector-request` error instead +of dropping, truncating, or mutating selectors. Validation-error payloads are +constructed to stay below the 6 KB upstream envelope: selector suggestions use +a fixed limit, selector samples and host command strings are budgeted, and +oversized host strings are omitted from the ordinary error envelope. Host packages own the semantics: - payload construction; - selector names and command names; - view policy, ordering, labels, and text; -- whether a missing selector is acceptable; +- the exact selector inventory and detail commands, supplied as + `selector_inventory_command` and `selector_detail_command` when validation + errors need to point callers at discovery; - any user-facing interpretation of the projected values. The primitive intentionally does not evaluate expressions, execute embedded language snippets, infer selectors from prose, or encode host package vocabulary. diff --git a/src/command_generation/primitive_executor.py b/src/command_generation/primitive_executor.py index e8cb7af..109e9f6 100644 --- a/src/command_generation/primitive_executor.py +++ b/src/command_generation/primitive_executor.py @@ -406,7 +406,9 @@ def _assemble_package_resource_manifest( files = _resolve_dotted_value(manifest, files_path) bundled_skill_files = _resolve_dotted_value(manifest, bundled_skills_path) return { - "files": _manifest_path_list(files or [], source=f"{manifest_from}.{files_path}"), + "files": _manifest_path_list( + files or [], source=f"{manifest_from}.{files_path}" + ), "default_files": _string_list( fields.get("default_files", []), source="payload.assemble fields.default_files", @@ -578,7 +580,9 @@ def _emit_output( if output_format == "json": return json.dumps(result, indent=2, sort_keys=True) + "\n" if isinstance(result, dict): - declared_view = _emit_declared_text_view(result, arguments.get("text_views", [])) + declared_view = _emit_declared_text_view( + result, arguments.get("text_views", []) + ) if declared_view is not None: return declared_view if str(arguments.get("text_style", "")) == "current-memory" and isinstance( @@ -622,7 +626,9 @@ def _emit_declared_text_view(result: dict[str, Any], views: Any) -> str | None: declared_views: list[Mapping[str, Any]] = [] for view in views: if not isinstance(view, Mapping): - raise PrimitiveExecutionError("output.emit text_views entries must be objects") + raise PrimitiveExecutionError( + "output.emit text_views entries must be objects" + ) _validate_declared_text_view(view) declared_views.append(view) default_view: Mapping[str, Any] | None = None @@ -636,13 +642,17 @@ def _emit_declared_text_view(result: dict[str, Any], views: Any) -> str | None: return None -def _declared_text_view_matches(result: dict[str, Any], view: Mapping[str, Any]) -> bool: +def _declared_text_view_matches( + result: dict[str, Any], view: Mapping[str, Any] +) -> bool: match = view.get("match", {}) if not isinstance(match, Mapping) or not match: return False for path, expected in match.items(): if not _is_declared_text_scalar(expected): - raise PrimitiveExecutionError("output.emit text view match values must be JSON scalars") + raise PrimitiveExecutionError( + "output.emit text view match values must be JSON scalars" + ) found, actual = _field_by_path(result, str(path)) if not found or not _declared_text_scalar_equal(actual, expected): return False @@ -660,7 +670,9 @@ def _validate_declared_text_view(view: Mapping[str, Any]) -> None: if isinstance(match, Mapping): for expected in match.values(): if not _is_declared_text_scalar(expected): - raise PrimitiveExecutionError("output.emit text view match values must be JSON scalars") + raise PrimitiveExecutionError( + "output.emit text view match values must be JSON scalars" + ) if "lines" in view: _validate_declared_text_lines(view["lines"]) @@ -676,7 +688,9 @@ def _validate_declared_text_line(line: Any) -> None: if isinstance(line, str): return if not isinstance(line, Mapping): - raise PrimitiveExecutionError("output.emit text view lines must be strings or objects") + raise PrimitiveExecutionError( + "output.emit text view lines must be strings or objects" + ) discriminators = {"when", "for_each", "json", "template", "literal"} present = [key for key in discriminators if key in line] if len(present) != 1: @@ -686,23 +700,39 @@ def _validate_declared_text_line(line: Any) -> None: key = present[0] if key == "literal": if set(line) != {"literal"}: - raise PrimitiveExecutionError("output.emit literal line must only declare literal") - _validate_declared_text_string(line["literal"], "output.emit literal line value must be a string") + raise PrimitiveExecutionError( + "output.emit literal line must only declare literal" + ) + _validate_declared_text_string( + line["literal"], "output.emit literal line value must be a string" + ) return if key == "template": if set(line) != {"template"}: - raise PrimitiveExecutionError("output.emit template line must only declare template") - _validate_declared_text_string(line["template"], "output.emit template line value must be a string") + raise PrimitiveExecutionError( + "output.emit template line must only declare template" + ) + _validate_declared_text_string( + line["template"], "output.emit template line value must be a string" + ) return if key == "json": if set(line) != {"json"}: - raise PrimitiveExecutionError("output.emit json line must only declare json") - _validate_declared_text_string(line["json"], "output.emit json line path must be a string") + raise PrimitiveExecutionError( + "output.emit json line must only declare json" + ) + _validate_declared_text_string( + line["json"], "output.emit json line path must be a string" + ) return if key == "when": if set(line) != {"when", "lines"}: - raise PrimitiveExecutionError("output.emit when line must declare when and lines") - _validate_declared_text_string(line["when"], "output.emit when line path must be a string") + raise PrimitiveExecutionError( + "output.emit when line must declare when and lines" + ) + _validate_declared_text_string( + line["when"], "output.emit when line path must be a string" + ) _validate_declared_text_lines(line["lines"]) return spec = line["for_each"] @@ -710,17 +740,25 @@ def _validate_declared_text_line(line: Any) -> None: raise PrimitiveExecutionError("output.emit for_each line must be an object") if "path" not in spec: raise PrimitiveExecutionError("output.emit for_each line must declare path") - _validate_declared_text_string(spec["path"], "output.emit for_each path must be a string") + _validate_declared_text_string( + spec["path"], "output.emit for_each path must be a string" + ) nested_forms = [name for name in ("lines", "template") if name in spec] if len(nested_forms) != 1: - raise PrimitiveExecutionError("output.emit for_each line must declare exactly one of lines or template") + raise PrimitiveExecutionError( + "output.emit for_each line must declare exactly one of lines or template" + ) expected_keys = {"path", nested_forms[0]} if set(spec) != expected_keys: - raise PrimitiveExecutionError("output.emit for_each line has unsupported fields") + raise PrimitiveExecutionError( + "output.emit for_each line has unsupported fields" + ) if "lines" in spec: _validate_declared_text_lines(spec["lines"]) else: - _validate_declared_text_string(spec["template"], "output.emit for_each template must be a string") + _validate_declared_text_string( + spec["template"], "output.emit for_each template must be a string" + ) def _validate_declared_text_string(value: Any, message: str) -> None: @@ -729,11 +767,15 @@ def _validate_declared_text_string(value: Any, message: str) -> None: def _render_declared_text_view(result: dict[str, Any], view: Mapping[str, Any]) -> str: - rendered = _render_declared_text_lines(view.get("lines", []), current=result, root=result) + rendered = _render_declared_text_lines( + view.get("lines", []), current=result, root=result + ) return "\n".join(rendered).rstrip() + "\n" -def _render_declared_text_lines(lines: Any, *, current: Any, root: dict[str, Any]) -> list[str]: +def _render_declared_text_lines( + lines: Any, *, current: Any, root: dict[str, Any] +) -> list[str]: if not isinstance(lines, Sequence) or isinstance(lines, (str, bytes, bytearray)): raise PrimitiveExecutionError("output.emit text view lines must be a list") rendered: list[str] = [] @@ -742,32 +784,46 @@ def _render_declared_text_lines(lines: Any, *, current: Any, root: dict[str, Any return rendered -def _render_declared_text_line(line: Any, *, current: Any, root: dict[str, Any]) -> list[str]: +def _render_declared_text_line( + line: Any, *, current: Any, root: dict[str, Any] +) -> list[str]: if isinstance(line, str): return [_render_declared_text_template(line, current=current, root=root)] if not isinstance(line, Mapping): - raise PrimitiveExecutionError("output.emit text view lines must be strings or objects") + raise PrimitiveExecutionError( + "output.emit text view lines must be strings or objects" + ) if "when" in line: found, value = _declared_text_value(line["when"], current=current, root=root) if not found or not _declared_text_truthy(value): return [] - return _render_declared_text_lines(line.get("lines", []), current=current, root=root) + return _render_declared_text_lines( + line.get("lines", []), current=current, root=root + ) if "for_each" in line: spec = line["for_each"] if not isinstance(spec, Mapping): raise PrimitiveExecutionError("output.emit for_each line must be an object") - found, value = _declared_text_value(spec.get("path", ""), current=current, root=root) + found, value = _declared_text_value( + spec.get("path", ""), current=current, root=root + ) if not found or value in (None, ""): return [] - if not isinstance(value, Sequence) or isinstance(value, (str, bytes, bytearray)): - raise PrimitiveExecutionError("output.emit for_each path must resolve to a list") + if not isinstance(value, Sequence) or isinstance( + value, (str, bytes, bytearray) + ): + raise PrimitiveExecutionError( + "output.emit for_each path must resolve to a list" + ) nested_lines = spec.get("lines") if nested_lines is None: nested_lines = [str(spec.get("template", "{}"))] return [ nested for item in value - for nested in _render_declared_text_lines(nested_lines, current=item, root=root) + for nested in _render_declared_text_lines( + nested_lines, current=item, root=root + ) ] if "json" in line: found, value = _declared_text_value(line["json"], current=current, root=root) @@ -779,37 +835,60 @@ def _render_declared_text_line(line: Any, *, current: Any, root: dict[str, Any]) ensure_ascii=False, ).splitlines() if "template" in line: - return [_render_declared_text_template(str(line["template"]), current=current, root=root)] + return [ + _render_declared_text_template( + str(line["template"]), current=current, root=root + ) + ] if "literal" in line: return [str(line["literal"])] - raise PrimitiveExecutionError("output.emit text view line object must declare when, for_each, json, template, or literal") + raise PrimitiveExecutionError( + "output.emit text view line object must declare when, for_each, json, template, or literal" + ) -def _render_declared_text_template(template: str, *, current: Any, root: dict[str, Any]) -> str: +def _render_declared_text_template( + template: str, *, current: Any, root: dict[str, Any] +) -> str: def replace(match: re.Match[str]) -> str: token = match.group(1) - found, value = _declared_text_placeholder_value(token, current=current, root=root) + found, value = _declared_text_placeholder_value( + token, current=current, root=root + ) return _declared_text_format(value if found else "") return _DECLARED_TEXT_TEMPLATE_PATTERN.sub(replace, template) -def _declared_text_placeholder_value(token: str, *, current: Any, root: dict[str, Any]) -> tuple[bool, Any]: +def _declared_text_placeholder_value( + token: str, *, current: Any, root: dict[str, Any] +) -> tuple[bool, Any]: parts = token.split("|") found, value = _declared_text_value(parts[0], current=current, root=root) for raw_filter in parts[1:]: name, _, argument = raw_filter.partition(":") if name == "len": - value = len(value) if isinstance(value, Sequence) and not isinstance(value, (str, bytes, bytearray)) else 0 + value = ( + len(value) + if isinstance(value, Sequence) + and not isinstance(value, (str, bytes, bytearray)) + else 0 + ) found = True elif name == "join": separator = argument if not found or value is None: value = "" - elif isinstance(value, Sequence) and not isinstance(value, (str, bytes, bytearray)): + elif isinstance(value, Sequence) and not isinstance( + value, (str, bytes, bytearray) + ): if not all(_is_declared_text_scalar(item) for item in value): - raise PrimitiveExecutionError("output.emit join filter requires a list of JSON scalars") - value = separator.join(_declared_text_format_scalar(item) for item in value) + raise PrimitiveExecutionError( + "output.emit join filter requires a list of JSON scalars" + ) + value = separator.join( + _declared_text_format_scalar(item) for item in value + ) else: raise PrimitiveExecutionError("output.emit join filter requires a list") found = True @@ -818,11 +897,15 @@ def _declared_text_placeholder_value(token: str, *, current: Any, root: dict[str value = argument found = True else: - raise PrimitiveExecutionError(f"unsupported output.emit text view filter: {name!r}") + raise PrimitiveExecutionError( + f"unsupported output.emit text view filter: {name!r}" + ) return found, value -def _declared_text_value(path: Any, *, current: Any, root: dict[str, Any]) -> tuple[bool, Any]: +def _declared_text_value( + path: Any, *, current: Any, root: dict[str, Any] +) -> tuple[bool, Any]: path_text = str(path or "") if path_text in {"", "."}: return True, current @@ -841,7 +924,9 @@ def _declared_text_truthy(value: Any) -> bool: def _declared_text_format(value: Any) -> str: if not _is_declared_text_scalar(value): - raise PrimitiveExecutionError("output.emit text view placeholders require JSON scalars; use json lines for arrays or objects") + raise PrimitiveExecutionError( + "output.emit text view placeholders require JSON scalars; use json lines for arrays or objects" + ) return _declared_text_format_scalar(value) @@ -908,10 +993,14 @@ def _declared_text_canonical_json_value(value: Any) -> Any: return value -def _view_payload(*, values: dict[str, Any], arguments: dict[str, Any]) -> dict[str, Any]: +def _view_payload( + *, values: dict[str, Any], arguments: dict[str, Any] +) -> dict[str, Any]: source_name = str(arguments.get("source") or "result") if source_name not in values: - raise PrimitiveExecutionError(f"payload.view source value is missing: {source_name!r}") + raise PrimitiveExecutionError( + f"payload.view source value is missing: {source_name!r}" + ) fields = _string_list(arguments.get("fields", []), source="payload.view fields") limits = arguments.get("limits", {}) if not isinstance(limits, Mapping): @@ -919,7 +1008,9 @@ def _view_payload(*, values: dict[str, Any], arguments: dict[str, Any]) -> dict[ payload = values[source_name] viewed: dict[str, Any] = { "kind": str(arguments.get("view_kind") or "command-generation/payload-view/v1"), - "source_command": str(arguments.get("source_command") or values.get("operation_id") or ""), + "source_command": str( + arguments.get("source_command") or values.get("operation_id") or "" + ), "values": {}, } missing: list[str] = [] @@ -944,7 +1035,9 @@ def _limited_view_value(value: Any, *, limit: Any) -> Any: return value -def _transaction_plan(*, values: dict[str, Any], arguments: dict[str, Any]) -> dict[str, Any]: +def _transaction_plan( + *, values: dict[str, Any], arguments: dict[str, Any] +) -> dict[str, Any]: resources_from = str(arguments.get("resources_from", "resources")) raw_resources = values.get(resources_from, arguments.get("resources", [])) if not isinstance(raw_resources, list): @@ -979,7 +1072,9 @@ def _transaction_plan(*, values: dict[str, Any], arguments: dict[str, Any]) -> d ) target_root_value = str(arguments.get("target_root_value", "target_root")) plan: dict[str, Any] = { - "kind": str(arguments.get("plan_kind", "command-generation/transaction-plan/v1")), + "kind": str( + arguments.get("plan_kind", "command-generation/transaction-plan/v1") + ), "dry_run": True, "target_root": str(values.get(target_root_value, "")), "schema_ref": str(arguments.get("schema_ref", "")), @@ -1017,44 +1112,362 @@ def _validate_resource_path(path: str) -> str: return resource_path -def _project_payload(*, values: dict[str, Any], arguments: dict[str, Any]) -> dict[str, Any]: +_MAX_PROJECTION_SELECTORS = 32 +_MAX_PROJECTION_SELECTOR_BYTES = 256 +_MAX_PROJECTION_SELECTOR_REQUEST_BYTES = 512 +_MAX_SELECTOR_ERROR_TEXT_BYTES = 128 +_MAX_SELECTOR_INVENTORY_SAMPLE_PATH_BYTES = 96 +_MAX_SELECTOR_INVENTORY_SAMPLE_BYTES = 384 +_MAX_SELECTOR_ERROR_ENVELOPE_BYTES = 6_000 +_SELECTOR_INVENTORY_SAMPLE_LIMIT = 8 +_SELECTOR_SUGGESTION_LIMIT = 1 + + +def _project_payload( + *, values: dict[str, Any], arguments: dict[str, Any] +) -> dict[str, Any]: source_name = str(arguments.get("source") or "result") - source_command = str(arguments.get("source_command") or values.get("operation_id") or "") - selected_output_kind = str(arguments.get("selected_output_kind") or "command-generation/selected-output/v1") + source_command = str( + arguments.get("source_command") or values.get("operation_id") or "" + ) + selected_output_kind = str( + arguments.get("selected_output_kind") or "command-generation/selected-output/v1" + ) if source_name not in values: - raise PrimitiveExecutionError(f"payload.project source value is missing: {source_name!r}") + raise PrimitiveExecutionError( + f"payload.project source value is missing: {source_name!r}" + ) payload = values[source_name] - selectors = _projection_selectors(values=values, arguments=arguments) + selector_request = _projection_selectors(values=values, arguments=arguments) + selectors = selector_request["selectors"] + request_error = selector_request["error"] + if request_error is not None: + return _selector_request_validation_error( + selectors=selectors, + request_error=request_error, + source_command=source_command, + selected_output_kind=selected_output_kind, + ) if not selectors: return _plain_output_result(payload) + missing = [ + selector for selector in selectors if not _path_exists(payload, selector) + ] + if missing: + return _selector_validation_error( + payload=payload, + selectors=selectors, + missing=missing, + source_command=source_command, + selected_output_kind=selected_output_kind, + discovery_command=str(arguments.get("selector_inventory_command") or ""), + detail_command=str(arguments.get("selector_detail_command") or ""), + ) selected: dict[str, Any] = { "kind": selected_output_kind, "source_command": source_command, "values": {}, } - missing: list[str] = [] projected_values = cast(dict[str, Any], selected["values"]) for selector in selectors: - found, value = _field_by_path(payload, selector) - if found: - projected_values[selector] = _plain_output_result(value) - else: - missing.append(selector) - if missing: - selected["missing"] = missing - selected["selector_rule"] = "Comma-separated dot paths select exact JSON fields; unknown fields are reported in missing." - selected["available_selectors"] = _available_selectors_for_payload(payload) + _, value = _field_by_path(payload, selector) + projected_values[selector] = _plain_output_result(value) return selected -def _projection_selectors(*, values: dict[str, Any], arguments: dict[str, Any]) -> list[str]: +def _projection_selectors( + *, values: dict[str, Any], arguments: dict[str, Any] +) -> dict[str, Any]: raw_selectors = arguments.get("selectors") if raw_selectors is None: select_value_name = str(arguments.get("select_value") or "select") raw_selectors = values.get(select_value_name) - if isinstance(raw_selectors, Sequence) and not isinstance(raw_selectors, (str, bytes, bytearray)): - return [str(item).strip() for item in raw_selectors if str(item).strip()] - return [token.strip() for token in str(raw_selectors or "").split(",") if token.strip()] + if isinstance(raw_selectors, Sequence) and not isinstance( + raw_selectors, (str, bytes, bytearray) + ): + return _projection_selectors_from_sequence(raw_selectors) + return _projection_selectors_from_string(str(raw_selectors or "")) + + +def _projection_selector_result( + selectors: list[str], error: dict[str, Any] | None = None +) -> dict[str, Any]: + return {"selectors": selectors, "error": error} + + +def _projection_selector_limit_error( + *, + reason: str, + requested_selector_count: int, + selector_request_bytes: int, + selector_index: int | None = None, + selector_bytes: int | None = None, +) -> dict[str, Any]: + error: dict[str, Any] = { + "reason": reason, + "requested_selector_count": requested_selector_count, + "selector_request_bytes": selector_request_bytes, + "max_selectors": _MAX_PROJECTION_SELECTORS, + "max_selector_bytes": _MAX_PROJECTION_SELECTOR_BYTES, + "max_selector_request_bytes": _MAX_PROJECTION_SELECTOR_REQUEST_BYTES, + } + if selector_index is not None: + error["selector_index"] = selector_index + if selector_bytes is not None: + error["selector_bytes"] = selector_bytes + return error + + +def _projection_selectors_from_sequence(raw_selectors: Sequence[Any]) -> dict[str, Any]: + selectors: list[str] = [] + requested_selector_count = 0 + selector_request_bytes = 0 + for item in raw_selectors: + token = str(item).strip() + if not token: + continue + token_bytes = _utf8_size(token) + requested_selector_count += 1 + if requested_selector_count > _MAX_PROJECTION_SELECTORS: + return _projection_selector_result( + selectors, + _projection_selector_limit_error( + reason="too-many-selectors", + requested_selector_count=requested_selector_count, + selector_request_bytes=selector_request_bytes, + selector_index=requested_selector_count - 1, + ), + ) + if token_bytes > _MAX_PROJECTION_SELECTOR_BYTES: + return _projection_selector_result( + selectors, + _projection_selector_limit_error( + reason="selector-too-long", + requested_selector_count=requested_selector_count, + selector_request_bytes=selector_request_bytes + token_bytes, + selector_index=requested_selector_count - 1, + selector_bytes=token_bytes, + ), + ) + if ( + selector_request_bytes + token_bytes + > _MAX_PROJECTION_SELECTOR_REQUEST_BYTES + ): + return _projection_selector_result( + selectors, + _projection_selector_limit_error( + reason="selector-request-too-large", + requested_selector_count=requested_selector_count, + selector_request_bytes=selector_request_bytes + token_bytes, + selector_index=requested_selector_count - 1, + ), + ) + selector_request_bytes += token_bytes + selectors.append(token) + return _projection_selector_result(selectors) + + +def _projection_selectors_from_string(raw_selectors: str) -> dict[str, Any]: + selectors: list[str] = [] + requested_selector_count = 0 + selector_request_bytes = 0 + token_chars: list[str] = [] + token_bytes = 0 + pending_whitespace = 0 + seen_non_whitespace = False + + def append_selector() -> dict[str, Any] | None: + nonlocal requested_selector_count, selector_request_bytes, token_chars + nonlocal token_bytes, pending_whitespace + token = "".join(token_chars) + token_chars = [] + appended_token_bytes = token_bytes + token_bytes = 0 + pending_whitespace = 0 + if not token: + return None + requested_selector_count += 1 + if requested_selector_count > _MAX_PROJECTION_SELECTORS: + return _projection_selector_limit_error( + reason="too-many-selectors", + requested_selector_count=requested_selector_count, + selector_request_bytes=selector_request_bytes, + selector_index=requested_selector_count - 1, + ) + if ( + selector_request_bytes + appended_token_bytes + > _MAX_PROJECTION_SELECTOR_REQUEST_BYTES + ): + return _projection_selector_limit_error( + reason="selector-request-too-large", + requested_selector_count=requested_selector_count, + selector_request_bytes=selector_request_bytes + appended_token_bytes, + selector_index=requested_selector_count - 1, + ) + selector_request_bytes += appended_token_bytes + selectors.append(token) + return None + + for char in raw_selectors: + if char == ",": + error = append_selector() + if error is not None: + return _projection_selector_result(selectors, error) + seen_non_whitespace = False + continue + if char.isspace() and not seen_non_whitespace: + continue + if char.isspace(): + pending_whitespace += 1 + continue + if pending_whitespace: + token_chars.extend(" " * pending_whitespace) + token_bytes += pending_whitespace + pending_whitespace = 0 + seen_non_whitespace = True + token_chars.append(char) + token_bytes += _utf8_size(char) + if token_bytes > _MAX_PROJECTION_SELECTOR_BYTES: + requested_selector_count += 1 + return _projection_selector_result( + selectors, + _projection_selector_limit_error( + reason="selector-too-long", + requested_selector_count=requested_selector_count, + selector_request_bytes=selector_request_bytes + token_bytes, + selector_index=requested_selector_count - 1, + selector_bytes=token_bytes, + ), + ) + error = append_selector() + return _projection_selector_result(selectors, error) + + +def _selector_validation_kind(selected_output_kind: str) -> str: + if "/selected-output/" in selected_output_kind: + kind = selected_output_kind.replace( + "/selected-output/", "/selector-validation-error/", 1 + ) + elif selected_output_kind.endswith("/selected-output"): + kind = f"{selected_output_kind.removesuffix('/selected-output')}/selector-validation-error" + else: + kind = "command-generation/selector-validation-error/v1" + if _utf8_size(kind) <= _MAX_SELECTOR_ERROR_TEXT_BYTES: + return kind + return "command-generation/selector-validation-error/v1" + + +def _utf8_size(value: str) -> int: + return len(value.encode("utf-8")) + + +def _utf8_sort_key(value: str) -> bytes: + return value.encode("utf-8") + + +def _bounded_selector_error_text(value: str) -> str: + return value if _utf8_size(value) <= _MAX_SELECTOR_ERROR_TEXT_BYTES else "" + + +def _selector_error_json_size(payload: dict[str, Any]) -> int: + return _utf8_size(json.dumps(payload, ensure_ascii=False, separators=(",", ":"))) + + +def _fit_selector_error_envelope(payload: dict[str, Any]) -> dict[str, Any]: + if _selector_error_json_size(payload) <= _MAX_SELECTOR_ERROR_ENVELOPE_BYTES: + return payload + suggestions = payload.get("suggestions") + if isinstance(suggestions, dict): + suggestions.clear() + if _selector_error_json_size(payload) <= _MAX_SELECTOR_ERROR_ENVELOPE_BYTES: + return payload + inventory = payload.get("selector_inventory") + if isinstance(inventory, dict): + inventory["sample"] = [] + inventory["discovery_command"] = "" + inventory["inventory_command"] = "" + if _selector_error_json_size(payload) <= _MAX_SELECTOR_ERROR_ENVELOPE_BYTES: + return payload + payload["requested_selectors"] = [] + payload["unknown_selectors"] = [] + return payload + + +def _selector_suggestions( + unknown: str, sample: list[str], *, limit: int = 3 +) -> list[str]: + terms = [part for part in unknown.replace("_", ".").split(".") if part] + matches: list[str] = [] + for selector in sample: + selector_terms = selector.split(".") + if unknown in selector or any( + term in selector_terms or term in selector for term in terms + ): + matches.append(selector) + if len(matches) >= limit: + return matches + return sample[:limit] + + +def _selector_validation_error( + *, + payload: Any, + selectors: list[str], + missing: list[str], + source_command: str, + selected_output_kind: str, + discovery_command: str, + detail_command: str, +) -> dict[str, Any]: + sample_limit = _SELECTOR_INVENTORY_SAMPLE_LIMIT + available_count, sample = _selector_inventory_summary( + payload, sample_limit=sample_limit + ) + error = { + "kind": _selector_validation_kind(selected_output_kind), + "status": "invalid-selector", + "source_command": _bounded_selector_error_text(source_command), + "requested_selectors": selectors[:32], + "unknown_selectors": missing[:32], + "selector_inventory": { + "status": "omitted-from-validation-error", + "available_count": available_count, + "sample": sample, + "sample_limit": sample_limit, + "discovery_command": _bounded_selector_error_text(discovery_command), + "inventory_command": _bounded_selector_error_text(detail_command), + "rule": "Full selector inventories are omitted from validation errors; use the inventory command for complete details.", + }, + "suggestions": { + selector: _selector_suggestions( + selector, sample, limit=_SELECTOR_SUGGESTION_LIMIT + ) + for selector in missing + }, + "validation_rule": "Selector requests are atomic: any unknown selector prevents partial projection output.", + } + return _fit_selector_error_envelope(error) + + +def _selector_request_validation_error( + *, + selectors: list[str], + request_error: dict[str, Any], + source_command: str, + selected_output_kind: str, +) -> dict[str, Any]: + error = { + "kind": _selector_validation_kind(selected_output_kind), + "status": "invalid-selector-request", + "source_command": _bounded_selector_error_text(source_command), + "requested_selectors": selectors, + "selector_request": { + "status": "rejected", + **request_error, + }, + "validation_rule": "Selector requests are bounded and atomic: too many selectors or overlong selectors are rejected before projection.", + } + return _fit_selector_error_envelope(error) def _plain_output_result(result: Any) -> Any: @@ -1305,7 +1718,9 @@ def _field_by_path(payload: Any, dotted_path: str) -> tuple[bool, Any]: if isinstance(current, Mapping) and part in current: current = current[part] continue - if isinstance(current, Sequence) and not isinstance(current, (str, bytes, bytearray)): + if isinstance(current, Sequence) and not isinstance( + current, (str, bytes, bytearray) + ): try: current = current[int(part)] continue @@ -1315,19 +1730,56 @@ def _field_by_path(payload: Any, dotted_path: str) -> tuple[bool, Any]: return True, current -def _available_selectors_for_payload(payload: Any, prefix: str = "") -> list[str]: - selectors: list[str] = [] - if isinstance(payload, Mapping): - for key in sorted(str(item) for item in payload): - path = f"{prefix}.{key}" if prefix else key - selectors.append(path) - selectors.extend(_available_selectors_for_payload(payload.get(key), path)) - elif isinstance(payload, Sequence) and not isinstance(payload, (str, bytes, bytearray)): - for index, item in enumerate(payload[:10]): - path = f"{prefix}.{index}" if prefix else str(index) - selectors.append(path) - selectors.extend(_available_selectors_for_payload(item, path)) - return selectors +def _path_exists(payload: Any, dotted_path: str) -> bool: + return _field_by_path(payload, dotted_path)[0] + + +def _selector_inventory_summary( + payload: Any, *, sample_limit: int +) -> tuple[int, list[str]]: + count = 0 + sample_candidates: list[str] = [] + + def record_sample(path: str) -> None: + if sample_limit <= 0: + return + path_bytes = _utf8_size(path) + if path_bytes > _MAX_SELECTOR_INVENTORY_SAMPLE_PATH_BYTES: + return + sample_candidates.append(path) + sample_candidates.sort(key=_utf8_sort_key) + if len(sample_candidates) > sample_limit: + sample_candidates.pop() + + def budgeted_sample() -> list[str]: + sample: list[str] = [] + sample_bytes = 0 + for path in sample_candidates: + path_bytes = _utf8_size(path) + if sample_bytes + path_bytes > _MAX_SELECTOR_INVENTORY_SAMPLE_BYTES: + break + sample.append(path) + sample_bytes += path_bytes + return sample + + def visit(current: Any, prefix: str) -> None: + nonlocal count + if isinstance(current, Mapping): + entries = current.items() + elif isinstance(current, Sequence) and not isinstance( + current, (str, bytes, bytearray) + ): + entries = enumerate(current) + else: + return + for key, value in entries: + path = f"{prefix}.{key}" if prefix else str(key) + count += 1 + record_sample(path) + visit(value, path) + + visit(payload, "") + return count, budgeted_sample() def _resolve_inside(root: Path, relative: str) -> Path: diff --git a/src/command_generation/primitive_registry.py b/src/command_generation/primitive_registry.py index b48678e..8a6f809 100644 --- a/src/command_generation/primitive_registry.py +++ b/src/command_generation/primitive_registry.py @@ -169,7 +169,7 @@ def to_jsonable(self) -> list[dict[str, Any]]: { "id": "payload.project", "kind": "portable", - "description": "Project exact dot-path selectors from a payload into a generic selected-output wrapper.", + "description": "Project exact dot-path selectors atomically, returning a bounded validation error for unknown selectors.", "target_support": {"python": "implemented", "typescript": "implemented"}, }, { diff --git a/src/command_generation/targets/python.py b/src/command_generation/targets/python.py index 2c3ddb8..46df4d4 100644 --- a/src/command_generation/targets/python.py +++ b/src/command_generation/targets/python.py @@ -45,10 +45,15 @@ def _runtime_consumed_operation_outputs( continue operation = json.loads(source.read_text(encoding="utf-8")) ir_plan = operation.get("ir_plan", {}) - if not isinstance(ir_plan, dict) or ir_plan.get("status") not in {"representative", "complete"}: + if not isinstance(ir_plan, dict) or ir_plan.get("status") not in { + "representative", + "complete", + }: continue emitted.add(operation_path) - outputs.append(GeneratedOutput(root / operation_path, _json_block(operation) + "\n")) + outputs.append( + GeneratedOutput(root / operation_path, _json_block(operation) + "\n") + ) return outputs @@ -64,15 +69,23 @@ def _python_resource_copy_outputs( generated_root = root / str(copy["generated_root"]) required_marker = str(copy.get("required_marker") or "") if required_marker and not (source_root / required_marker).is_file(): - raise FileNotFoundError(f"missing required resource marker: {(source_root / required_marker).as_posix()}") + raise FileNotFoundError( + f"missing required resource marker: {(source_root / required_marker).as_posix()}" + ) for source in _resource_copy_source_files(source_root): relative = source.relative_to(source_root) - outputs.append(GeneratedOutput(generated_root / relative, source.read_text(encoding="utf-8"))) + outputs.append( + GeneratedOutput( + generated_root / relative, source.read_text(encoding="utf-8") + ) + ) return outputs def _module_name_for_operation(operation_id: str) -> str: - return "".join(character if character.isalnum() else "_" for character in operation_id).strip("_") + return "".join( + character if character.isalnum() else "_" for character in operation_id + ).strip("_") def _python_commands_package_module( @@ -83,9 +96,14 @@ def _python_commands_package_module( regenerate_command: str, ) -> str: operation_executor = _operation_executor_binding(package) - operation_ids = {str(operation_id) for operation_id in operation_executor.get("supported_operation_ids", [])} + operation_ids = { + str(operation_id) + for operation_id in operation_executor.get("supported_operation_ids", []) + } direct_handlers = { - str(handler["operation_id"]): handler for handler in binding.get("runtime_module_handlers", []) if isinstance(handler, dict) + str(handler["operation_id"]): handler + for handler in binding.get("runtime_module_handlers", []) + if isinstance(handler, dict) } operation_ids.update(direct_handlers) imports = [] @@ -122,15 +140,21 @@ def _python_command_module( ) -> str: operation_executor = _operation_executor_binding(package) direct_handlers = { - str(handler["operation_id"]): handler for handler in binding.get("runtime_module_handlers", []) if isinstance(handler, dict) + str(handler["operation_id"]): handler + for handler in binding.get("runtime_module_handlers", []) + if isinstance(handler, dict) } if operation_id in direct_handlers: handler = direct_handlers[operation_id] if handler.get("handler") == "module_front_door": runtime_module_file = _runtime_module_file_for_package(package) if runtime_module_file == "cli": - rendered_handler = _render_module_front_door_runtime_handler("run", handler) - run_body = rendered_handler.split("def run(args: argparse.Namespace) -> int:\n", 1)[1] + rendered_handler = _render_module_front_door_runtime_handler( + "run", handler + ) + run_body = rendered_handler.split( + "def run(args: argparse.Namespace) -> int:\n", 1 + )[1] support_imports = "import contextlib\nimport io\nimport json\nfrom ..cli import build_generated_parser\n" else: run_body = f" from ..{runtime_module_file} import _run_generated_operation\n\n return _run_generated_operation({operation_id!r}, args)\n" @@ -138,14 +162,20 @@ def _python_command_module( elif handler.get("handler") == "argparse_function_call": runtime_module_file = _runtime_module_file_for_package(package) if runtime_module_file == "cli": - rendered_handler = _render_argparse_function_call_handler("run", handler) - run_body = rendered_handler.split("def run(args: argparse.Namespace) -> int:\n", 1)[1] + rendered_handler = _render_argparse_function_call_handler( + "run", handler + ) + run_body = rendered_handler.split( + "def run(args: argparse.Namespace) -> int:\n", 1 + )[1] else: run_body = f" from ..{runtime_module_file} import _run_generated_operation\n\n return _run_generated_operation({operation_id!r}, args)\n" support_imports = "" else: import_module = str(handler["import_module"]) - imported_function = str(handler.get("function") or _runtime_adapter_function_name(operation_id)) + imported_function = str( + handler.get("function") or _runtime_adapter_function_name(operation_id) + ) local_binding = _local_runtime_binding_for_import(package, import_module) if local_binding is not None: local_import = _command_module_import_for_binding(local_binding) @@ -158,7 +188,9 @@ def _python_command_module( "def invoke(_values: Mapping[str, Any]) -> object:\n" f" raise RuntimeError({operation_id!r} + ' has no generated operation callable')\n" ) - typing_import = "from typing import Any\nfrom collections.abc import Mapping\n\n" + typing_import = ( + "from typing import Any\nfrom collections.abc import Mapping\n\n" + ) else: run_body = f" return run_operation_ir(generated_operation_contract({operation_id!r}), args)\n" invoke_function = ( @@ -166,9 +198,13 @@ def _python_command_module( "def invoke(values: Mapping[str, Any]) -> object:\n" f" return run_operation_callable(generated_operation_contract({operation_id!r}), values)\n" ) - executor_module = str(operation_executor.get("module_file", "operation_executor")) + executor_module = str( + operation_executor.get("module_file", "operation_executor") + ) support_imports = f"from ..cli import generated_operation_contract\nfrom ..{executor_module} import run_operation_callable, run_operation_ir\n" - typing_import = "from collections.abc import Mapping\nfrom typing import Any\n\n" + typing_import = ( + "from collections.abc import Mapping\nfrom typing import Any\n\n" + ) return ( '"""Generated executable command projection.\n\n' f"Source: {source_path}\n" @@ -196,21 +232,37 @@ def _python_command_module_outputs( regenerate_command: str, ) -> list[GeneratedOutput]: operation_executor = _operation_executor_binding(package) - operation_ids = {str(operation_id) for operation_id in operation_executor.get("supported_operation_ids", [])} + operation_ids = { + str(operation_id) + for operation_id in operation_executor.get("supported_operation_ids", []) + } operation_ids.update( - str(handler["operation_id"]) for handler in binding.get("runtime_module_handlers", []) if isinstance(handler, dict) + str(handler["operation_id"]) + for handler in binding.get("runtime_module_handlers", []) + if isinstance(handler, dict) ) outputs = [ GeneratedOutput( root / "commands" / "__init__.py", - _python_commands_package_module(package, binding, source_path=source_path, regenerate_command=regenerate_command), + _python_commands_package_module( + package, + binding, + source_path=source_path, + regenerate_command=regenerate_command, + ), ) ] for operation_id in sorted(operation_ids): outputs.append( GeneratedOutput( root / "commands" / f"{_module_name_for_operation(operation_id)}.py", - _python_command_module(package, operation_id, binding, source_path=source_path, regenerate_command=regenerate_command), + _python_command_module( + package, + operation_id, + binding, + source_path=source_path, + regenerate_command=regenerate_command, + ), ) ) return outputs @@ -246,7 +298,11 @@ def _host_support_label( if host_manifest.generated_root is None: return support_path.name try: - return support_path.resolve().relative_to(host_manifest.generated_root.resolve().parent).as_posix() + return ( + support_path.resolve() + .relative_to(host_manifest.generated_root.resolve().parent) + .as_posix() + ) except ValueError: return support_path.name @@ -262,7 +318,10 @@ def _python_primitive_executor_module( support_label = "none" support_import = "" if host_manifest.python_primitive_support_path is not None: - support_label = _host_support_label(host_manifest=host_manifest, support_path=host_manifest.python_primitive_support_path) + support_label = _host_support_label( + host_manifest=host_manifest, + support_path=host_manifest.python_primitive_support_path, + ) support_import = ( "\n\n" "from .host_primitive_support import execute_host_primitive as _execute_configured_host_primitive\n\n" @@ -291,7 +350,10 @@ def _python_host_primitive_support_module( ) -> str: if host_manifest.python_primitive_support_path is None: return "" - support_label = _host_support_label(host_manifest=host_manifest, support_path=host_manifest.python_primitive_support_path) + support_label = _host_support_label( + host_manifest=host_manifest, + support_path=host_manifest.python_primitive_support_path, + ) support = host_manifest.python_primitive_support_path.read_text(encoding="utf-8") return ( '"""Generated target-local host primitive support module.\n\n' @@ -306,8 +368,12 @@ def _python_host_primitive_support_module( ) -def _python_operation_composition_module(*, source_path: str, regenerate_command: str) -> str: - operation_composition_path = Path(__file__).parent.parent / "operation_composition.py" +def _python_operation_composition_module( + *, source_path: str, regenerate_command: str +) -> str: + operation_composition_path = ( + Path(__file__).parent.parent / "operation_composition.py" + ) operation_composition = operation_composition_path.read_text(encoding="utf-8") return ( '"""Generated target-local operation composition helpers.\n\n' @@ -321,7 +387,9 @@ def _python_operation_composition_module(*, source_path: str, regenerate_command ) -def _python_resource_primitives_module(*, source_path: str, regenerate_command: str) -> str: +def _python_resource_primitives_module( + *, source_path: str, regenerate_command: str +) -> str: return ( '"""Generated target-local resource and output primitives.\n\n' f"Source: {source_path}\n" @@ -531,7 +599,9 @@ def _python_resource_primitives_module(*, source_path: str, regenerate_command: def _handler_function_name(primitive: str) -> str: - return "_handle_" + "".join(character if character.isalnum() else "_" for character in primitive) + return "_handle_" + "".join( + character if character.isalnum() else "_" for character in primitive + ) def _render_value_kwargs(kwargs: dict[str, Any]) -> str: @@ -543,7 +613,9 @@ def _render_value_kwargs(kwargs: dict[str, Any]) -> str: return ", ".join(rendered) -def _handler_import_module(package: dict[str, Any], import_module: str, *, operation_executor: bool) -> str: +def _handler_import_module( + package: dict[str, Any], import_module: str, *, operation_executor: bool +) -> str: local_binding = _local_runtime_binding_for_import(package, import_module) if local_binding is None: return import_module @@ -552,10 +624,14 @@ def _handler_import_module(package: dict[str, Any], import_module: str, *, opera return _command_module_import_for_binding(local_binding) -def _render_function_call_handler(package: dict[str, Any], function_name: str, handler: dict[str, Any]) -> str: +def _render_function_call_handler( + package: dict[str, Any], function_name: str, handler: dict[str, Any] +) -> str: imported_name = str(handler["function"]) kwargs = _render_value_kwargs(handler.get("kwargs", {})) - import_module = _handler_import_module(package, str(handler["import_module"]), operation_executor=True) + import_module = _handler_import_module( + package, str(handler["import_module"]), operation_executor=True + ) return ( f"def {function_name}(values: dict[str, Any], _arguments: dict[str, Any], _context: PrimitiveContext) -> Any:\n" f" from {import_module} import {imported_name}\n\n" @@ -563,7 +639,9 @@ def _render_function_call_handler(package: dict[str, Any], function_name: str, h ) -def _render_conditional_function_call_handler(package: dict[str, Any], function_name: str, handler: dict[str, Any]) -> str: +def _render_conditional_function_call_handler( + package: dict[str, Any], function_name: str, handler: dict[str, Any] +) -> str: condition_value = str(handler["condition_value"]) true_handler = handler["if_true"] false_handler = handler["if_false"] @@ -571,8 +649,12 @@ def _render_conditional_function_call_handler(package: dict[str, Any], function_ false_name = str(false_handler["function"]) true_kwargs = _render_value_kwargs(true_handler.get("kwargs", {})) false_kwargs = _render_value_kwargs(false_handler.get("kwargs", {})) - true_import_module = _handler_import_module(package, str(true_handler["import_module"]), operation_executor=True) - false_import_module = _handler_import_module(package, str(false_handler["import_module"]), operation_executor=True) + true_import_module = _handler_import_module( + package, str(true_handler["import_module"]), operation_executor=True + ) + false_import_module = _handler_import_module( + package, str(false_handler["import_module"]), operation_executor=True + ) return ( f"def {function_name}(values: dict[str, Any], _arguments: dict[str, Any], _context: PrimitiveContext) -> Any:\n" f" if values.get({condition_value!r}):\n" @@ -583,7 +665,9 @@ def _render_conditional_function_call_handler(package: dict[str, Any], function_ ) -def _render_generated_target_root_handler(function_name: str, handler: dict[str, Any]) -> str: +def _render_generated_target_root_handler( + function_name: str, handler: dict[str, Any] +) -> str: project_markers = tuple(str(marker) for marker in handler["project_markers"]) return ( f"def {function_name}(values: dict[str, Any], _arguments: dict[str, Any], _context: PrimitiveContext) -> Any:\n" @@ -592,7 +676,9 @@ def _render_generated_target_root_handler(function_name: str, handler: dict[str, ) -def _render_runtime_emit_handler(function_name: str, handler: dict[str, Any], *, runtime_module_file: str) -> str: +def _render_runtime_emit_handler( + function_name: str, handler: dict[str, Any], *, runtime_module_file: str +) -> str: runtime_function = str(handler["runtime_function"]) result_value = str(handler["result_value"]) format_value = str(handler["format_value"]) @@ -649,7 +735,9 @@ def _render_runtime_handler( ) -def _local_runtime_binding_functions(package: dict[str, Any], binding: dict[str, Any]) -> list[str]: +def _local_runtime_binding_functions( + package: dict[str, Any], binding: dict[str, Any] +) -> list[str]: source_import_module = str(binding["source_import_module"]) functions: set[str] = set() @@ -668,18 +756,35 @@ def collect_handler_function(handler: dict[str, Any]) -> None: python_runtime_binding = package.get("python_runtime_binding", {}) if isinstance(python_runtime_binding, dict): for handler in python_runtime_binding.get("runtime_module_handlers", []): - if isinstance(handler, dict) and handler.get("handler") in {"module_front_door", "argparse_function_call"}: + if isinstance(handler, dict) and handler.get("handler") in { + "module_front_door", + "argparse_function_call", + }: continue - if isinstance(handler, dict) and handler.get("import_module") == source_import_module: - functions.add(str(handler.get("function") or _runtime_adapter_function_name(str(handler["operation_id"])))) + if ( + isinstance(handler, dict) + and handler.get("import_module") == source_import_module + ): + functions.add( + str( + handler.get("function") + or _runtime_adapter_function_name(str(handler["operation_id"])) + ) + ) return sorted(functions) -def _local_runtime_generated_overrides(binding: dict[str, Any]) -> dict[str, dict[str, Any]]: +def _local_runtime_generated_overrides( + binding: dict[str, Any], +) -> dict[str, dict[str, Any]]: overrides = binding.get("generated_function_overrides", []) if not isinstance(overrides, list): return {} - return {str(item["function"]): item for item in overrides if isinstance(item, dict) and item.get("function")} + return { + str(item["function"]): item + for item in overrides + if isinstance(item, dict) and item.get("function") + } def _python_local_runtime_helper_block() -> str: @@ -692,7 +797,7 @@ def _python_local_runtime_helper_block() -> str: " if isinstance(value, list):\n" " return [_serialise_value(item) for item in value]\n" " return value\n\n\n" - "def _field_by_path(payload: Any, path: str) -> tuple[bool, Any]:\n" + "def _field_by_path(payload: Any, path: str, *, copy_value: bool = True) -> tuple[bool, Any]:\n" " current = payload\n" " for part in path.split('.'):\n" " if isinstance(current, dict) and part in current:\n" @@ -705,36 +810,209 @@ def _python_local_runtime_helper_block() -> str: " except (ValueError, IndexError):\n" " return (False, None)\n" " return (False, None)\n" - " return (True, copy.deepcopy(current))\n\n\n" - "def _selector_tokens(select: str | None) -> list[str]:\n" - " return [token.strip() for token in str(select or '').split(',') if token.strip()]\n\n\n" - "def _available_selectors_for_payload(payload: Any, prefix: str = '') -> list[str]:\n" + " return (True, copy.deepcopy(current) if copy_value else None)\n\n\n" + "_MAX_PROJECTION_SELECTORS = 32\n" + "_MAX_PROJECTION_SELECTOR_BYTES = 256\n" + "_MAX_PROJECTION_SELECTOR_REQUEST_BYTES = 512\n" + "_MAX_SELECTOR_ERROR_TEXT_BYTES = 128\n" + "_MAX_SELECTOR_INVENTORY_SAMPLE_PATH_BYTES = 96\n" + "_MAX_SELECTOR_INVENTORY_SAMPLE_BYTES = 384\n" + "_MAX_SELECTOR_ERROR_ENVELOPE_BYTES = 6000\n" + "_SELECTOR_INVENTORY_SAMPLE_LIMIT = 8\n" + "_SELECTOR_SUGGESTION_LIMIT = 1\n\n\n" + "def _utf8_size(value: str) -> int:\n" + " return len(value.encode('utf-8'))\n\n\n" + "def _utf8_sort_key(value: str) -> bytes:\n" + " return value.encode('utf-8')\n\n\n" + "def _bounded_selector_error_text(value: str) -> str:\n" + " return value if _utf8_size(value) <= _MAX_SELECTOR_ERROR_TEXT_BYTES else ''\n\n\n" + "def _selector_error_json_size(payload: dict[str, Any]) -> int:\n" + " return _utf8_size(json.dumps(payload, ensure_ascii=False, separators=(',', ':')))\n\n\n" + "def _fit_selector_error_envelope(payload: dict[str, Any]) -> dict[str, Any]:\n" + " if _selector_error_json_size(payload) <= _MAX_SELECTOR_ERROR_ENVELOPE_BYTES:\n" + " return payload\n" + " suggestions = payload.get('suggestions')\n" + " if isinstance(suggestions, dict):\n" + " suggestions.clear()\n" + " if _selector_error_json_size(payload) <= _MAX_SELECTOR_ERROR_ENVELOPE_BYTES:\n" + " return payload\n" + " inventory = payload.get('selector_inventory')\n" + " if isinstance(inventory, dict):\n" + " inventory['sample'] = []\n" + " inventory['discovery_command'] = ''\n" + " inventory['inventory_command'] = ''\n" + " if _selector_error_json_size(payload) <= _MAX_SELECTOR_ERROR_ENVELOPE_BYTES:\n" + " return payload\n" + " payload['requested_selectors'] = []\n" + " payload['unknown_selectors'] = []\n" + " return payload\n\n\n" + "def _selector_tokens(select: str | None) -> dict[str, Any]:\n" " selectors: list[str] = []\n" - " if isinstance(payload, dict):\n" - " for key in sorted(str(item) for item in payload):\n" - " path = f'{prefix}.{key}' if prefix else key\n" - " selectors.append(path)\n" - " selectors.extend(_available_selectors_for_payload(payload.get(key), path))\n" - " elif isinstance(payload, list):\n" - " for index, item in enumerate(payload[:10]):\n" - " path = f'{prefix}.{index}' if prefix else str(index)\n" - " selectors.append(path)\n" - " selectors.extend(_available_selectors_for_payload(item, path))\n" - " return selectors\n\n\n" - "def _select_payload_fields(payload: dict[str, Any], *, select: str | None, source_command: str, selected_output_kind: str) -> dict[str, Any]:\n" + " requested_selector_count = 0\n" + " selector_request_bytes = 0\n" + " token_chars: list[str] = []\n" + " token_bytes = 0\n" + " pending_whitespace = 0\n" + " seen_non_whitespace = False\n\n" + " def limit_error(*, reason: str, selector_request_bytes_value: int, selector_index: int | None = None, selector_bytes: int | None = None) -> dict[str, Any]:\n" + " error: dict[str, Any] = {'reason': reason, 'requested_selector_count': requested_selector_count, 'selector_request_bytes': selector_request_bytes_value, 'max_selectors': _MAX_PROJECTION_SELECTORS, 'max_selector_bytes': _MAX_PROJECTION_SELECTOR_BYTES, 'max_selector_request_bytes': _MAX_PROJECTION_SELECTOR_REQUEST_BYTES}\n" + " if selector_index is not None:\n" + " error['selector_index'] = selector_index\n" + " if selector_bytes is not None:\n" + " error['selector_bytes'] = selector_bytes\n" + " return error\n\n" + " def append_selector() -> dict[str, Any] | None:\n" + " nonlocal requested_selector_count, selector_request_bytes, token_chars, token_bytes, pending_whitespace\n" + " token = ''.join(token_chars)\n" + " token_chars = []\n" + " appended_token_bytes = token_bytes\n" + " token_bytes = 0\n" + " pending_whitespace = 0\n" + " if not token:\n" + " return None\n" + " requested_selector_count += 1\n" + " if requested_selector_count > _MAX_PROJECTION_SELECTORS:\n" + " return limit_error(reason='too-many-selectors', selector_request_bytes_value=selector_request_bytes, selector_index=requested_selector_count - 1)\n" + " if selector_request_bytes + appended_token_bytes > _MAX_PROJECTION_SELECTOR_REQUEST_BYTES:\n" + " return limit_error(reason='selector-request-too-large', selector_request_bytes_value=selector_request_bytes + appended_token_bytes, selector_index=requested_selector_count - 1)\n" + " selector_request_bytes += appended_token_bytes\n" + " selectors.append(token)\n" + " return None\n\n" + " for char in str(select or ''):\n" + " if char == ',':\n" + " error = append_selector()\n" + " if error is not None:\n" + " return {'selectors': selectors, 'error': error}\n" + " seen_non_whitespace = False\n" + " continue\n" + " if char.isspace() and not seen_non_whitespace:\n" + " continue\n" + " if char.isspace():\n" + " pending_whitespace += 1\n" + " continue\n" + " if pending_whitespace:\n" + " token_chars.extend(' ' * pending_whitespace)\n" + " token_bytes += pending_whitespace\n" + " pending_whitespace = 0\n" + " seen_non_whitespace = True\n" + " token_chars.append(char)\n" + " token_bytes += _utf8_size(char)\n" + " if token_bytes > _MAX_PROJECTION_SELECTOR_BYTES:\n" + " requested_selector_count += 1\n" + " error = limit_error(reason='selector-too-long', selector_request_bytes_value=selector_request_bytes + token_bytes, selector_index=requested_selector_count - 1, selector_bytes=token_bytes)\n" + " return {'selectors': selectors, 'error': error}\n" + " error = append_selector()\n" + " return {'selectors': selectors, 'error': error}\n\n\n" + "def _selector_inventory_summary(payload: Any, sample_limit: int = 8) -> tuple[int, list[str]]:\n" + " count = 0\n" + " sample_candidates: list[str] = []\n" + " def record_sample(path: str) -> None:\n" + " if sample_limit <= 0:\n" + " return\n" + " path_bytes = _utf8_size(path)\n" + " if path_bytes > _MAX_SELECTOR_INVENTORY_SAMPLE_PATH_BYTES:\n" + " return\n" + " sample_candidates.append(path)\n" + " sample_candidates.sort(key=_utf8_sort_key)\n" + " if len(sample_candidates) > sample_limit:\n" + " sample_candidates.pop()\n" + "\n" + " def budgeted_sample() -> list[str]:\n" + " sample: list[str] = []\n" + " sample_bytes = 0\n" + " for path in sample_candidates:\n" + " path_bytes = _utf8_size(path)\n" + " if sample_bytes + path_bytes > _MAX_SELECTOR_INVENTORY_SAMPLE_BYTES:\n" + " break\n" + " sample.append(path)\n" + " sample_bytes += path_bytes\n" + " return sample\n" + "\n" + " def visit(current: Any, prefix: str) -> None:\n" + " nonlocal count\n" + " if isinstance(current, dict):\n" + " entries = current.items()\n" + " elif isinstance(current, list):\n" + " entries = enumerate(current)\n" + " else:\n" + " return\n" + " for key, value in entries:\n" + " path = f'{prefix}.{key}' if prefix else str(key)\n" + " count += 1\n" + " record_sample(path)\n" + " visit(value, path)\n" + " visit(payload, '')\n" + " return count, budgeted_sample()\n\n\n" + "def _selector_validation_kind(selected_output_kind: str) -> str:\n" + " if '/selected-output/' in selected_output_kind:\n" + " kind = selected_output_kind.replace('/selected-output/', '/selector-validation-error/', 1)\n" + " elif selected_output_kind.endswith('/selected-output'):\n" + " kind = f\"{selected_output_kind.removesuffix('/selected-output')}/selector-validation-error\"\n" + " else:\n" + " kind = 'command-generation/selector-validation-error/v1'\n" + " if _utf8_size(kind) <= _MAX_SELECTOR_ERROR_TEXT_BYTES:\n" + " return kind\n" + " return 'command-generation/selector-validation-error/v1'\n\n\n" + "def _selector_suggestions(unknown: str, available: list[str], *, limit: int = 3) -> list[str]:\n" + " terms = [part for part in unknown.replace('_', '.').split('.') if part]\n" + " matches: list[str] = []\n" + " for selector in available:\n" + " selector_terms = selector.split('.')\n" + " if unknown in selector or any(term in selector_terms or term in selector for term in terms):\n" + " matches.append(selector)\n" + " if len(matches) >= limit:\n" + " return matches\n" + " return available[:limit]\n\n\n" + "def _selector_validation_error(*, payload: Any, selectors: list[str], missing: list[str], source_command: str, selected_output_kind: str, discovery_command: str, detail_command: str) -> dict[str, Any]:\n" + " sample_limit = _SELECTOR_INVENTORY_SAMPLE_LIMIT\n" + " available_count, available = _selector_inventory_summary(payload, sample_limit=sample_limit)\n" + " error = {\n" + " 'kind': _selector_validation_kind(selected_output_kind),\n" + " 'status': 'invalid-selector',\n" + " 'source_command': _bounded_selector_error_text(source_command),\n" + " 'requested_selectors': selectors,\n" + " 'unknown_selectors': missing,\n" + " 'selector_inventory': {\n" + " 'status': 'omitted-from-validation-error',\n" + " 'available_count': available_count,\n" + " 'sample': available,\n" + " 'sample_limit': sample_limit,\n" + " 'discovery_command': _bounded_selector_error_text(discovery_command),\n" + " 'inventory_command': _bounded_selector_error_text(detail_command),\n" + " 'rule': 'Full selector inventories are omitted from validation errors; use the inventory command for complete details.',\n" + " },\n" + " 'suggestions': {selector: _selector_suggestions(selector, available, limit=_SELECTOR_SUGGESTION_LIMIT) for selector in missing},\n" + " 'validation_rule': 'Selector requests are atomic: any unknown selector prevents partial projection output.',\n" + " }\n\n\n" + " return _fit_selector_error_envelope(error)\n\n\n" + "def _selector_request_validation_error(*, selectors: list[str], request_error: dict[str, Any], source_command: str, selected_output_kind: str) -> dict[str, Any]:\n" + " error = {\n" + " 'kind': _selector_validation_kind(selected_output_kind),\n" + " 'status': 'invalid-selector-request',\n" + " 'source_command': _bounded_selector_error_text(source_command),\n" + " 'requested_selectors': selectors,\n" + " 'selector_request': {'status': 'rejected', **request_error},\n" + " 'validation_rule': 'Selector requests are bounded and atomic: too many selectors or overlong selectors are rejected before projection.',\n" + " }\n\n\n" + " return _fit_selector_error_envelope(error)\n\n\n" + "def _select_payload_fields(payload: dict[str, Any], *, select: str | None, source_command: str, selected_output_kind: str, discovery_command: str, detail_command: str) -> dict[str, Any]:\n" " values: dict[str, Any] = {}\n" " missing: list[str] = []\n" - " for selector in _selector_tokens(select):\n" + " selector_request = _selector_tokens(select)\n" + " selectors = selector_request['selectors']\n" + " request_error = selector_request['error']\n" + " if request_error is not None:\n" + " return _selector_request_validation_error(selectors=selectors, request_error=request_error, source_command=source_command, selected_output_kind=selected_output_kind)\n" + " missing = [selector for selector in selectors if not _field_by_path(payload, selector, copy_value=False)[0]]\n" + " if missing:\n" + " return _selector_validation_error(payload=payload, selectors=selectors, missing=missing, source_command=source_command, selected_output_kind=selected_output_kind, discovery_command=discovery_command, detail_command=detail_command)\n" + " for selector in selectors:\n" " found, value = _field_by_path(payload, selector)\n" " if found:\n" " values[selector] = value\n" " else:\n" " missing.append(selector)\n" " selected: dict[str, Any] = {'kind': selected_output_kind, 'source_command': source_command, 'values': values}\n" - " if missing:\n" - " selected['missing'] = missing\n" - " selected['selector_rule'] = 'Comma-separated dot paths select exact JSON fields; unknown fields are reported in missing.'\n" - " selected['available_selectors'] = _available_selectors_for_payload(payload)\n" " return selected\n\n\n" "def _selector_refs(*, command: str, answer: Any, compact_profile_ref: str = '') -> list[str]:\n" " refs = [ref for ref in (compact_profile_ref, command) if ref]\n" @@ -842,13 +1120,33 @@ def _python_local_runtime_generated_function( if implementation == "sectioned_payload_select": payload_value = str(override.get("payload_value") or "payload") source_command = str(override.get("source_command") or "command") - common_sections = [str(section) for section in override.get("common_sections", [])] - selected_output_kind = str(override.get("selected_output_kind") or "command-generation/selected-output/v1") - sectioned_payload_kind = str(override.get("sectioned_payload_kind") or "command-generation/sectioned-resource/v1") + common_sections = [ + str(section) for section in override.get("common_sections", []) + ] + selected_output_kind = str( + override.get("selected_output_kind") + or "command-generation/selected-output/v1" + ) + sectioned_payload_kind = str( + override.get("sectioned_payload_kind") + or "command-generation/sectioned-resource/v1" + ) compact_profile_ref = str(override.get("compact_profile_ref") or "") - section_command_ref = str(override.get("section_command_ref") or f"{source_command} --format json") - section_detail_command = str(override.get("section_detail_command") or f"{source_command} --section
--format json") - full_detail_command = str(override.get("full_detail_command") or f"{source_command} --verbose --format json") + selector_inventory_command = str( + override.get("selector_inventory_command") or "" + ) + selector_detail_command = str(override.get("selector_detail_command") or "") + section_command_ref = str( + override.get("section_command_ref") or f"{source_command} --format json" + ) + section_detail_command = str( + override.get("section_detail_command") + or f"{source_command} --section
--format json" + ) + full_detail_command = str( + override.get("full_detail_command") + or f"{source_command} --verbose --format json" + ) return ( f"def {function}(values: dict[str, Any], _arguments: dict[str, Any], _context: Any) -> dict[str, Any]:\n" f" payload = values[{payload_value!r}]\n" @@ -859,7 +1157,7 @@ def _python_local_runtime_generated_function( f" payload = _tiny_sectioned_payload(payload, common_sections={common_sections!r}, sectioned_payload_kind={sectioned_payload_kind!r}, section_detail_command={section_detail_command!r}, full_detail_command={full_detail_command!r})\n" " select = values.get('select')\n" " if select is not None:\n" - f" payload = _select_payload_fields(payload, select=str(select), source_command={source_command!r}, selected_output_kind={selected_output_kind!r})\n" + f" payload = _select_payload_fields(payload, select=str(select), source_command={source_command!r}, selected_output_kind={selected_output_kind!r}, discovery_command={selector_inventory_command!r}, detail_command={selector_detail_command!r})\n" " return _serialise_value(payload)\n" ) if implementation == "json_resource_load": @@ -873,8 +1171,14 @@ def _python_local_runtime_generated_function( f" return read_json_object(resource_root, {relative_path!r})\n" ) if implementation == "json_output_with_source_fallback": - selected_output_kind = str(override.get("selected_output_kind") or "command-generation/selected-output/v1") - sectioned_payload_kind = str(override.get("sectioned_payload_kind") or "command-generation/sectioned-resource/v1") + selected_output_kind = str( + override.get("selected_output_kind") + or "command-generation/selected-output/v1" + ) + sectioned_payload_kind = str( + override.get("sectioned_payload_kind") + or "command-generation/sectioned-resource/v1" + ) delegation_outcomes_kind = str(override.get("delegation_outcomes_kind") or "") return ( f"def {function}(values: dict[str, Any], arguments: dict[str, Any], context: Any) -> Any:\n" @@ -908,7 +1212,9 @@ def _python_local_runtime_generated_function( f" from {source_import_module} import {function} as source_function\n\n" " return source_function(values, arguments, context)\n" ) - raise ValueError(f"unsupported generated local runtime implementation: {implementation!r}") + raise ValueError( + f"unsupported generated local runtime implementation: {implementation!r}" + ) def _python_local_runtime_binding_module( @@ -926,7 +1232,11 @@ def _python_local_runtime_binding_module( for function in functions: if function in overrides: function_blocks.append( - _python_local_runtime_generated_function(function, overrides[function], source_import_module=source_import_module) + _python_local_runtime_generated_function( + function, + overrides[function], + source_import_module=source_import_module, + ) ) else: function_blocks.append( @@ -955,7 +1265,10 @@ def _python_local_runtime_binding_module( "# Export semantics: generated wrappers perform live source-module lookup at call time.\n" "# Monkeypatching this facade is local to the facade; it is not forwarded back into source modules.\n" "# Replace individual bindings here with generated/codegen-owned primitives as those operations migrate.\n" - f"# Regenerate with: {regenerate_command}\n\n" + helper_block + "\n\n".join(function_blocks) + "\n\n" + f"# Regenerate with: {regenerate_command}\n\n" + + helper_block + + "\n\n".join(function_blocks) + + "\n\n" "__all__ = [\n" f" {exported},\n" "]\n" @@ -984,11 +1297,15 @@ def _python_operation_executor_module( regenerate_command: str, ) -> str: runtime_module_file = _runtime_module_file_for_package(package) - supported_operation_ids = sorted(str(operation_id) for operation_id in binding["supported_operation_ids"]) + supported_operation_ids = sorted( + str(operation_id) for operation_id in binding["supported_operation_ids"] + ) initial_values = [] callable_initial_values = [] for item in binding["initial_values"]: - initial_values.append(f" {str(item['name'])!r}: getattr(args, {str(item['arg'])!r}, {item.get('default')!r}),") + initial_values.append( + f" {str(item['name'])!r}: getattr(args, {str(item['arg'])!r}, {item.get('default')!r})," + ) callable_initial_values.append( f" {str(item['name'])!r}: values.get({str(item['name'])!r}, {item.get('default')!r})," ) @@ -1001,25 +1318,50 @@ def _python_operation_executor_module( handler_items.append(f" {primitive!r}: {function_name},") handler_kind = str(handler["handler"]) if handler_kind == "runtime_handler": - handlers.append(_render_runtime_handler(package, function_name, handler, runtime_module_file=runtime_module_file)) + handlers.append( + _render_runtime_handler( + package, + function_name, + handler, + runtime_module_file=runtime_module_file, + ) + ) elif handler_kind == "function_call": - handlers.append(_render_function_call_handler(package, function_name, handler)) + handlers.append( + _render_function_call_handler(package, function_name, handler) + ) elif handler_kind == "conditional_function_call": - handlers.append(_render_conditional_function_call_handler(package, function_name, handler)) + handlers.append( + _render_conditional_function_call_handler( + package, function_name, handler + ) + ) elif handler_kind == "generated_target_root_resolve": - handlers.append(_render_generated_target_root_handler(function_name, handler)) + handlers.append( + _render_generated_target_root_handler(function_name, handler) + ) elif handler_kind == "runtime_emit": needs_json = True - handlers.append(_render_runtime_emit_handler(function_name, handler, runtime_module_file=runtime_module_file)) + handlers.append( + _render_runtime_emit_handler( + function_name, handler, runtime_module_file=runtime_module_file + ) + ) else: - raise ValueError(f"unsupported Python operation executor handler: {handler_kind!r}") - supported_set = ",\n ".join(repr(operation_id) for operation_id in supported_operation_ids) + raise ValueError( + f"unsupported Python operation executor handler: {handler_kind!r}" + ) + supported_set = ",\n ".join( + repr(operation_id) for operation_id in supported_operation_ids + ) root_functions = [] context_roots = [] for root in binding.get("context_roots", []): root_function = _handler_function_name(f"context.root.{root['name']}") root_functions.append(_render_context_root_function(root)) - context_roots.append(f" {str(root['name'])!r}: {root_function}(),") + context_roots.append( + f" {str(root['name'])!r}: {root_function}()," + ) roots_block = "\n".join(context_roots) if roots_block: roots_block = "\n" + roots_block + "\n " @@ -1053,7 +1395,9 @@ def _python_operation_executor_module( " values = run_operation_values(\n" " operation,\n" " initial_values={\n" - ' "operation_id": operation.get("id"),\n' + "\n".join(initial_values) + "\n" + ' "operation_id": operation.get("id"),\n' + + "\n".join(initial_values) + + "\n" " },\n" " )\n" " emitted = values.get('emitted')\n" @@ -1065,7 +1409,9 @@ def _python_operation_executor_module( " result = run_operation_values(\n" " operation,\n" " initial_values={\n" - ' "operation_id": operation.get("id"),\n' + "\n".join(callable_initial_values) + "\n" + ' "operation_id": operation.get("id"),\n' + + "\n".join(callable_initial_values) + + "\n" " },\n" " ).get('result')\n" " return result\n\n\n" @@ -1091,19 +1437,41 @@ def _python_operation_executor_module( def _runtime_adapter_function_name(operation_id: str) -> str: - return "_run_" + "".join(character if character.isalnum() else "_" for character in operation_id) + "_adapter" + return ( + "_run_" + + "".join( + character if character.isalnum() else "_" for character in operation_id + ) + + "_adapter" + ) -def _render_argparse_function_call_handler(function_name: str, handler: dict[str, Any]) -> str: +def _render_argparse_function_call_handler( + function_name: str, handler: dict[str, Any] +) -> str: import_module = str(handler["import_module"]) imported_function = str(handler["function"]) support_import_module = str(handler.get("support_import_module") or import_module) result_mode = str(handler.get("result", "return_zero")) emit_payload = handler.get("emit_payload", {}) - emit_import_module = str(emit_payload.get("import_module") or support_import_module) if isinstance(emit_payload, dict) else "" - emit_function = str(emit_payload.get("function") or "_emit_payload") if isinstance(emit_payload, dict) else "_emit_payload" - emit_format_attr = str(emit_payload.get("format_attr") or "format") if isinstance(emit_payload, dict) else "format" - argument_specs = [spec for spec in handler.get("arguments", []) if isinstance(spec, dict)] + emit_import_module = ( + str(emit_payload.get("import_module") or support_import_module) + if isinstance(emit_payload, dict) + else "" + ) + emit_function = ( + str(emit_payload.get("function") or "_emit_payload") + if isinstance(emit_payload, dict) + else "_emit_payload" + ) + emit_format_attr = ( + str(emit_payload.get("format_attr") or "format") + if isinstance(emit_payload, dict) + else "format" + ) + argument_specs = [ + spec for spec in handler.get("arguments", []) if isinstance(spec, dict) + ] lines = [f"def {function_name}(args: argparse.Namespace) -> int:"] kwargs: list[str] = [] needs_support: set[str] = set() @@ -1120,7 +1488,9 @@ def _render_argparse_function_call_handler(function_name: str, handler: dict[str lines.append(f" {variable_name} = bool(getattr(args, {attr!r}, False))") elif kind == "list_attr": attr = str(spec["attr"]) - lines.append(f" {variable_name} = list(getattr(args, {attr!r}, []) or [])") + lines.append( + f" {variable_name} = list(getattr(args, {attr!r}, []) or [])" + ) elif kind == "target_root": attr = str(spec.get("attr") or "target") default_current = bool(spec.get("default_current", True)) @@ -1130,29 +1500,45 @@ def _render_argparse_function_call_handler(function_name: str, handler: dict[str if validate_command: needs_support.add("_validate_target_root") if default_current: - lines.append(f" {variable_name} = _resolve_target_root(getattr(args, {attr!r}, None))") + lines.append( + f" {variable_name} = _resolve_target_root(getattr(args, {attr!r}, None))" + ) else: - lines.append(f" {variable_name} = _resolve_target_root(getattr(args, {attr!r}, None)) if getattr(args, {attr!r}, None) else None") + lines.append( + f" {variable_name} = _resolve_target_root(getattr(args, {attr!r}, None)) if getattr(args, {attr!r}, None) else None" + ) if not allow_none: lines.append(f" if {variable_name} is None:") - lines.append(" raise ValueError('target root resolution returned None')") + lines.append( + " raise ValueError('target root resolution returned None')" + ) if validate_command: if allow_none: lines.append(f" if {variable_name} is not None:") - lines.append(f" _validate_target_root(command_name={validate_command!r}, target_root={variable_name})") + lines.append( + f" _validate_target_root(command_name={validate_command!r}, target_root={variable_name})" + ) else: - lines.append(f" _validate_target_root(command_name={validate_command!r}, target_root={variable_name})") + lines.append( + f" _validate_target_root(command_name={validate_command!r}, target_root={variable_name})" + ) elif kind == "diagnostic_profile": default = str(spec.get("default") or "tiny") needs_support.add("_diagnostic_profile") - lines.append(f" {variable_name} = _diagnostic_profile(args, default={default!r})") + lines.append( + f" {variable_name} = _diagnostic_profile(args, default={default!r})" + ) elif kind == "module_descriptors": - needs_support.update({"_module_operations", "_validate_descriptor_contract"}) + needs_support.update( + {"_module_operations", "_validate_descriptor_contract"} + ) lines.append(f" {variable_name} = _module_operations()") if bool(spec.get("validate", True)): lines.append(f" _validate_descriptor_contract({variable_name})") else: - raise ValueError(f"unsupported argparse_function_call argument kind: {kind!r}") + raise ValueError( + f"unsupported argparse_function_call argument kind: {kind!r}" + ) kwargs.append(f"{name}={variable_name}") if needs_support: imported = ", ".join(sorted(needs_support)) @@ -1164,17 +1550,23 @@ def _render_argparse_function_call_handler(function_name: str, handler: dict[str elif result_mode == "emit_payload": lines.append(f" payload = {call}") lines.append(f" from {emit_import_module} import {emit_function}") - lines.append(f" {emit_function}(payload=payload, format_name=getattr(args, {emit_format_attr!r}, 'text'))") + lines.append( + f" {emit_function}(payload=payload, format_name=getattr(args, {emit_format_attr!r}, 'text'))" + ) lines.append(" return 0") elif result_mode == "return_zero": lines.append(f" {call}") lines.append(" return 0") else: - raise ValueError(f"unsupported argparse_function_call result mode: {result_mode!r}") + raise ValueError( + f"unsupported argparse_function_call result mode: {result_mode!r}" + ) return "\n".join(lines) + "\n" -def _render_module_front_door_runtime_handler(function_name: str, handler: dict[str, Any]) -> str: +def _render_module_front_door_runtime_handler( + function_name: str, handler: dict[str, Any] +) -> str: command_attr = str(handler["command_attr"]) target_attr = str(handler.get("target_attr", "target")) format_attr = str(handler.get("format_attr", "format")) @@ -1184,7 +1576,9 @@ def _render_module_front_door_runtime_handler(function_name: str, handler: dict[ include_module_program = bool(handler.get("include_module_program", False)) help_payload_import = str(handler["help_payload_import_module"]) help_payload_function = str(handler["help_payload_function"]) - help_text_import = str(handler.get("help_text_import_module") or help_payload_import) + help_text_import = str( + handler.get("help_text_import_module") or help_payload_import + ) help_text_function = str(handler["help_text_function"]) missing_message = str(handler["missing_module_message"]) replacements = [ @@ -1286,9 +1680,14 @@ def _python_runtime_handler_module( ) -> str: operation_executor = _operation_executor_binding(package) executor_module = str(operation_executor["module_file"]) - operation_ids = {str(operation_id) for operation_id in operation_executor["supported_operation_ids"]} + operation_ids = { + str(operation_id) + for operation_id in operation_executor["supported_operation_ids"] + } direct_handlers = { - str(handler["operation_id"]): handler for handler in binding.get("runtime_module_handlers", []) if isinstance(handler, dict) + str(handler["operation_id"]): handler + for handler in binding.get("runtime_module_handlers", []) + if isinstance(handler, dict) } operation_ids.update(direct_handlers) handler_functions = [] @@ -1298,9 +1697,13 @@ def _python_runtime_handler_module( if operation_id in direct_handlers: handler = direct_handlers[operation_id] if handler.get("handler") == "module_front_door": - handler_functions.append(_render_module_front_door_runtime_handler(function_name, handler)) + handler_functions.append( + _render_module_front_door_runtime_handler(function_name, handler) + ) elif handler.get("handler") == "argparse_function_call": - handler_functions.append(_render_argparse_function_call_handler(function_name, handler)) + handler_functions.append( + _render_argparse_function_call_handler(function_name, handler) + ) else: import_module = str(handler["import_module"]) imported_function = str(handler.get("function") or function_name) @@ -1336,7 +1739,8 @@ def _python_runtime_handler_module( "from . import generated_operation_contract\n" "from . import run_generated_command\n" "from . import supports_generated_command\n" - f"from .{executor_module} import run_operation_ir\n\n\n" + "def _program_name() -> str:\n" + f"from .{executor_module} import run_operation_ir\n\n\n" + + "def _program_name() -> str:\n" ' invoked = sys.argv[0].replace("\\\\", "/").rsplit("/", 1)[-1]\n' f" if invoked == {package['program']!r}:\n" " return invoked\n" @@ -1376,7 +1780,12 @@ def _python_runtime_adapter_module( ) -> str: weak_agent_routing = _weak_agent_routing_for_target(target, maturity_levels) runnable = str( - target.get("maturity_level_ref") in {"runtime-backed-read-only-adapter", "weak-agent-safe-adapter", "mutation-capable-adapter"} + target.get("maturity_level_ref") + in { + "runtime-backed-read-only-adapter", + "weak-agent-safe-adapter", + "mutation-capable-adapter", + } ) runtime_module_file = _runtime_module_file_for_package(package) main_function = "" @@ -1649,7 +2058,9 @@ def _python_runtime_adapter_module( ) -def _python_module(package: dict[str, Any], *, source_path: str, regenerate_command: str) -> str: +def _python_module( + package: dict[str, Any], *, source_path: str, regenerate_command: str +) -> str: return ( '"""Generated command package metadata.\n\n' f"Source: {source_path}\n" @@ -1689,7 +2100,9 @@ def render_python_outputs( ) -> list[GeneratedOutput]: outputs: list[GeneratedOutput] = [] module_path = root / "cli.py" - outputs.append(GeneratedOutput(root / "__init__.py", "from .cli import * # noqa: F403\n")) + outputs.append( + GeneratedOutput(root / "__init__.py", "from .cli import * # noqa: F403\n") + ) outputs.append( GeneratedOutput( root / "command_package.json", @@ -1705,11 +2118,19 @@ def render_python_outputs( ) ) if _is_runtime_backed_python_target(target): - outputs.extend(_runtime_consumed_operation_outputs(package, repo_root=repo_root, root=root)) - outputs.extend(_python_resource_copy_outputs(package, repo_root=repo_root, root=root)) + outputs.extend( + _runtime_consumed_operation_outputs(package, repo_root=repo_root, root=root) + ) + outputs.extend( + _python_resource_copy_outputs(package, repo_root=repo_root, root=root) + ) operation_executor = _operation_executor_binding(package) if operation_executor: - executor_module_path = Path(str(operation_executor.get("module_file", "operation_executor")).replace(".", "/")) + executor_module_path = Path( + str( + operation_executor.get("module_file", "operation_executor") + ).replace(".", "/") + ) outputs.append( GeneratedOutput( root / executor_module_path.with_suffix(".py"), @@ -1722,7 +2143,10 @@ def render_python_outputs( ) ) python_runtime_binding = package.get("python_runtime_binding", {}) - if python_runtime_binding.get("render_runtime_module") is True and operation_executor: + if ( + python_runtime_binding.get("render_runtime_module") is True + and operation_executor + ): outputs.extend( _python_command_module_outputs( package, @@ -1735,7 +2159,9 @@ def render_python_outputs( outputs.append( GeneratedOutput( root / "primitives" / "__init__.py", - _python_primitives_module(source_path=source_path, regenerate_command=regenerate_command), + _python_primitives_module( + source_path=source_path, regenerate_command=regenerate_command + ), ) ) outputs.append( @@ -1762,7 +2188,9 @@ def render_python_outputs( outputs.append( GeneratedOutput( root / "primitives" / "operation_composition.py", - _python_operation_composition_module(source_path=source_path, regenerate_command=regenerate_command), + _python_operation_composition_module( + source_path=source_path, regenerate_command=regenerate_command + ), ) ) outputs.append( @@ -1777,7 +2205,9 @@ def render_python_outputs( for local_runtime_binding in _local_runtime_bindings(package): if not _local_runtime_binding_functions(package, local_runtime_binding): continue - local_runtime_module_path = Path(str(local_runtime_binding["module_file"]).replace(".", "/")) + local_runtime_module_path = Path( + str(local_runtime_binding["module_file"]).replace(".", "/") + ) outputs.append( GeneratedOutput( root / local_runtime_module_path.with_suffix(".py"), @@ -1808,5 +2238,12 @@ def render_python_outputs( ) ) return outputs - outputs.append(GeneratedOutput(module_path, _python_module(package, source_path=source_path, regenerate_command=regenerate_command))) + outputs.append( + GeneratedOutput( + module_path, + _python_module( + package, source_path=source_path, regenerate_command=regenerate_command + ), + ) + ) return outputs diff --git a/src/command_generation/targets/typescript.py b/src/command_generation/targets/typescript.py index 9a90621..37e9765 100644 --- a/src/command_generation/targets/typescript.py +++ b/src/command_generation/targets/typescript.py @@ -37,10 +37,16 @@ def _typescript_resource_copy_outputs( generated_root = root / "resources" / str(copy["generated_root"]) required_marker = str(copy.get("required_marker") or "") if required_marker and not (source_root / required_marker).is_file(): - raise FileNotFoundError(f"missing required resource marker: {(source_root / required_marker).as_posix()}") + raise FileNotFoundError( + f"missing required resource marker: {(source_root / required_marker).as_posix()}" + ) for source in _resource_copy_source_files(source_root): relative = source.relative_to(source_root) - outputs.append(GeneratedOutput(generated_root / relative, source.read_text(encoding="utf-8"))) + outputs.append( + GeneratedOutput( + generated_root / relative, source.read_text(encoding="utf-8") + ) + ) operation_contract_root = repo_root / str(package["operation_contract_root"]) native_ids = _typescript_native_operation_ids(package) @@ -49,7 +55,11 @@ def _typescript_resource_copy_outputs( for operation_ref in _command_operation_refs(command): operation_id = str(operation_ref.get("id", "")) operation_path = str(operation_ref.get("path", "")) - if operation_id not in native_ids or not operation_path or operation_path in emitted_operation_paths: + if ( + operation_id not in native_ids + or not operation_path + or operation_path in emitted_operation_paths + ): continue source = operation_contract_root / operation_path operation = ( @@ -64,7 +74,12 @@ def _typescript_resource_copy_outputs( outputs.append( GeneratedOutput( root / "resources" / operation_path, - _json_block(_typescript_executable_operation(operation, operation_id=operation_id)) + "\n", + _json_block( + _typescript_executable_operation( + operation, operation_id=operation_id + ) + ) + + "\n", ) ) return outputs @@ -80,7 +95,9 @@ def _typescript_native_operation_ids(package: dict[str, Any]) -> set[str]: return operation_ids -def _typescript_executable_operation(operation: dict[str, Any], *, operation_id: str) -> dict[str, Any]: +def _typescript_executable_operation( + operation: dict[str, Any], *, operation_id: str +) -> dict[str, Any]: ir_plan = operation.get("ir_plan", {}) steps = ir_plan.get("steps", []) if isinstance(ir_plan, dict) else [] if isinstance(steps, list) and steps: @@ -126,7 +143,9 @@ def _typescript_package_json( "private": True, "type": "module", "files": ["src", "resources"], - "bin": {entrypoint: "./src/cli.mjs" for entrypoint in target["entrypoints"]} if _is_runnable_typescript_target(target) else {}, + "bin": {entrypoint: "./src/cli.mjs" for entrypoint in target["entrypoints"]} + if _is_runnable_typescript_target(target) + else {}, "scripts": {"test": "node --test test/command-package.test.mjs"}, "agenticWorkspace": { "generated": True, @@ -153,7 +172,9 @@ def _typescript_package_json( return _json_block(payload) + "\n" -def _typescript_module(package: dict[str, Any], *, source_path: str, regenerate_command: str) -> str: +def _typescript_module( + package: dict[str, Any], *, source_path: str, regenerate_command: str +) -> str: return ( "// Generated command package metadata.\n" f"// Source: {source_path}\n" @@ -287,13 +308,19 @@ def _typescript_native_runtime_helpers(*, recovery_command: str) -> str: ) -def _host_runtime_support_label(*, host_manifest: CommandGenerationHostManifest, support_path: Path) -> str: +def _host_runtime_support_label( + *, host_manifest: CommandGenerationHostManifest, support_path: Path +) -> str: if not support_path.is_absolute(): return support_path.as_posix() if host_manifest.generated_root is None: return support_path.name try: - return support_path.resolve().relative_to(host_manifest.generated_root.resolve().parent).as_posix() + return ( + support_path.resolve() + .relative_to(host_manifest.generated_root.resolve().parent) + .as_posix() + ) except ValueError: return support_path.name @@ -313,9 +340,7 @@ def _typescript_runtime_module( support_path=host_manifest.typescript_primitive_support_path, ) support_import = "import { executeHostPrimitive as configuredHostPrimitive } from './hostPrimitiveSupport.mjs';\n" - configured_host_primitive_call = ( - " if (typeof configuredHostPrimitive === 'function') return configuredHostPrimitive(primitive, values, args, operationId);\n" - ) + configured_host_primitive_call = " if (typeof configuredHostPrimitive === 'function') return configuredHostPrimitive(primitive, values, args, operationId);\n" return f"""// Generated native TypeScript operation runtime. // Source: {source_path} // Host primitive support: {support_label} @@ -473,27 +498,264 @@ class RuntimeError extends Error {{}} return [true, current]; }} -function selectorTokens(value) {{ - if (Array.isArray(value)) return value.map(String).map((item) => item.trim()).filter(Boolean); - return String(value ?? '').split(',').map((item) => item.trim()).filter(Boolean); +const MAX_PROJECTION_SELECTORS = 32; +const MAX_PROJECTION_SELECTOR_BYTES = 256; +const MAX_PROJECTION_SELECTOR_REQUEST_BYTES = 512; +const MAX_SELECTOR_ERROR_TEXT_BYTES = 128; +const MAX_SELECTOR_INVENTORY_SAMPLE_PATH_BYTES = 96; +const MAX_SELECTOR_INVENTORY_SAMPLE_BYTES = 384; +const MAX_SELECTOR_ERROR_ENVELOPE_BYTES = 6000; +const SELECTOR_INVENTORY_SAMPLE_LIMIT = 8; +const SELECTOR_SUGGESTION_LIMIT = 1; + +function utf8Size(value) {{ + return new TextEncoder().encode(String(value)).length; +}} + +function utf8Compare(left, right) {{ + const leftBytes = new TextEncoder().encode(String(left)); + const rightBytes = new TextEncoder().encode(String(right)); + const length = Math.min(leftBytes.length, rightBytes.length); + for (let index = 0; index < length; index += 1) {{ + if (leftBytes[index] !== rightBytes[index]) return leftBytes[index] - rightBytes[index]; + }} + return leftBytes.length - rightBytes.length; +}} + +function boundedSelectorErrorText(value) {{ + const text = String(value ?? ''); + return utf8Size(text) <= MAX_SELECTOR_ERROR_TEXT_BYTES ? text : ''; +}} + +function selectorErrorJsonSize(payload) {{ + return utf8Size(JSON.stringify(payload)); +}} + +function fitSelectorErrorEnvelope(payload) {{ + if (selectorErrorJsonSize(payload) <= MAX_SELECTOR_ERROR_ENVELOPE_BYTES) return payload; + if (isObject(payload.suggestions)) {{ + for (const key of Object.keys(payload.suggestions)) delete payload.suggestions[key]; + }} + if (selectorErrorJsonSize(payload) <= MAX_SELECTOR_ERROR_ENVELOPE_BYTES) return payload; + if (isObject(payload.selector_inventory)) {{ + payload.selector_inventory.sample = []; + payload.selector_inventory.discovery_command = ''; + payload.selector_inventory.inventory_command = ''; + }} + if (selectorErrorJsonSize(payload) <= MAX_SELECTOR_ERROR_ENVELOPE_BYTES) return payload; + payload.requested_selectors = []; + payload.unknown_selectors = []; + return payload; +}} + +function selectorLimitError(reason, requestedSelectorCount, selectorRequestBytes, selectorIndex = null, selectorBytes = null) {{ + const error = {{ + reason, + requested_selector_count: requestedSelectorCount, + selector_request_bytes: selectorRequestBytes, + max_selectors: MAX_PROJECTION_SELECTORS, + max_selector_bytes: MAX_PROJECTION_SELECTOR_BYTES, + max_selector_request_bytes: MAX_PROJECTION_SELECTOR_REQUEST_BYTES + }}; + if (selectorIndex !== null) error.selector_index = selectorIndex; + if (selectorBytes !== null) error.selector_bytes = selectorBytes; + return error; }} -function availableSelectorsForPayload(payload, prefix = '') {{ +function selectorTokensFromArray(value) {{ const selectors = []; - if (isObject(payload)) {{ - for (const key of Object.keys(payload).map(String).sort()) {{ - const path = prefix ? `${{prefix}}.${{key}}` : key; - selectors.push(path); - selectors.push(...availableSelectorsForPayload(payload[key], path)); + let requestedSelectorCount = 0; + let selectorRequestBytes = 0; + for (const item of value) {{ + const token = String(item).trim(); + if (!token) continue; + const tokenBytes = utf8Size(token); + requestedSelectorCount += 1; + if (requestedSelectorCount > MAX_PROJECTION_SELECTORS) {{ + return {{ selectors, error: selectorLimitError('too-many-selectors', requestedSelectorCount, selectorRequestBytes, requestedSelectorCount - 1) }}; + }} + if (tokenBytes > MAX_PROJECTION_SELECTOR_BYTES) {{ + return {{ selectors, error: selectorLimitError('selector-too-long', requestedSelectorCount, selectorRequestBytes + tokenBytes, requestedSelectorCount - 1, tokenBytes) }}; }} - }} else if (Array.isArray(payload)) {{ - for (const [index, item] of payload.slice(0, 10).entries()) {{ - const path = prefix ? `${{prefix}}.${{index}}` : String(index); - selectors.push(path); - selectors.push(...availableSelectorsForPayload(item, path)); + if (selectorRequestBytes + tokenBytes > MAX_PROJECTION_SELECTOR_REQUEST_BYTES) {{ + return {{ selectors, error: selectorLimitError('selector-request-too-large', requestedSelectorCount, selectorRequestBytes + tokenBytes, requestedSelectorCount - 1) }}; }} + selectorRequestBytes += tokenBytes; + selectors.push(token); }} - return selectors; + return {{ selectors, error: null }}; +}} + +function selectorTokensFromString(value) {{ + const selectors = []; + let requestedSelectorCount = 0; + let selectorRequestBytes = 0; + let token = ''; + let tokenBytes = 0; + let pendingWhitespace = 0; + let seenNonWhitespace = false; + function appendSelector() {{ + if (!token) return null; + const appendedTokenBytes = tokenBytes; + requestedSelectorCount += 1; + if (requestedSelectorCount > MAX_PROJECTION_SELECTORS) {{ + token = ''; + tokenBytes = 0; + pendingWhitespace = 0; + return selectorLimitError('too-many-selectors', requestedSelectorCount, selectorRequestBytes, requestedSelectorCount - 1); + }} + if (selectorRequestBytes + appendedTokenBytes > MAX_PROJECTION_SELECTOR_REQUEST_BYTES) {{ + token = ''; + tokenBytes = 0; + pendingWhitespace = 0; + return selectorLimitError('selector-request-too-large', requestedSelectorCount, selectorRequestBytes + appendedTokenBytes, requestedSelectorCount - 1); + }} + selectorRequestBytes += appendedTokenBytes; + selectors.push(token); + token = ''; + tokenBytes = 0; + pendingWhitespace = 0; + return null; + }} + for (const char of String(value ?? '')) {{ + if (char === ',') {{ + const error = appendSelector(); + if (error) return {{ selectors, error }}; + seenNonWhitespace = false; + continue; + }} + if (/\\s/u.test(char) && !seenNonWhitespace) continue; + if (/\\s/u.test(char)) {{ + pendingWhitespace += 1; + continue; + }} + if (pendingWhitespace) {{ + token += ' '.repeat(pendingWhitespace); + tokenBytes += pendingWhitespace; + pendingWhitespace = 0; + }} + seenNonWhitespace = true; + token += char; + tokenBytes += utf8Size(char); + if (tokenBytes > MAX_PROJECTION_SELECTOR_BYTES) {{ + requestedSelectorCount += 1; + return {{ + selectors, + error: selectorLimitError('selector-too-long', requestedSelectorCount, selectorRequestBytes + tokenBytes, requestedSelectorCount - 1, tokenBytes) + }}; + }} + }} + return {{ selectors, error: appendSelector() }}; +}} + +function selectorTokens(value) {{ + if (Array.isArray(value)) return selectorTokensFromArray(value); + return selectorTokensFromString(value); +}} + +function selectorInventorySummary(payload, sampleLimit = 8) {{ + let count = 0; + const sampleCandidates = []; + function recordSample(path) {{ + if (sampleLimit <= 0) return; + const pathBytes = utf8Size(path); + if (pathBytes > MAX_SELECTOR_INVENTORY_SAMPLE_PATH_BYTES) return; + sampleCandidates.push(path); + sampleCandidates.sort(utf8Compare); + if (sampleCandidates.length > sampleLimit) sampleCandidates.pop(); + }} + function budgetedSample() {{ + const sample = []; + let sampleBytes = 0; + for (const path of sampleCandidates) {{ + const pathBytes = utf8Size(path); + if (sampleBytes + pathBytes > MAX_SELECTOR_INVENTORY_SAMPLE_BYTES) break; + sample.push(path); + sampleBytes += pathBytes; + }} + return sample; + }} + function visit(current, prefix) {{ + if (Array.isArray(current)) {{ + for (let index = 0; index < current.length; index += 1) {{ + const path = prefix ? `${{prefix}}.${{index}}` : String(index); + count += 1; + recordSample(path); + visit(current[index], path); + }} + return; + }} + if (isObject(current)) {{ + for (const key in current) {{ + if (!Object.prototype.hasOwnProperty.call(current, key)) continue; + const path = prefix ? `${{prefix}}.${{key}}` : key; + count += 1; + recordSample(path); + visit(current[key], path); + }} + }} + }} + visit(payload, ''); + return {{ count, sample: budgetedSample() }}; +}} + +function selectorValidationKind(selectedOutputKind) {{ + const kind = String(selectedOutputKind ?? ''); + let validationKind = 'command-generation/selector-validation-error/v1'; + if (kind.includes('/selected-output/')) validationKind = kind.replace('/selected-output/', '/selector-validation-error/'); + else if (kind.endsWith('/selected-output')) validationKind = `${{kind.slice(0, -'/selected-output'.length)}}/selector-validation-error`; + return utf8Size(validationKind) <= MAX_SELECTOR_ERROR_TEXT_BYTES ? validationKind : 'command-generation/selector-validation-error/v1'; +}} + +function selectorSuggestions(unknown, available, limit = 3) {{ + const terms = String(unknown).replaceAll('_', '.').split('.').filter(Boolean); + const matches = []; + for (const selector of available) {{ + const selectorTerms = String(selector).split('.'); + if (String(selector).includes(String(unknown)) || terms.some((term) => selectorTerms.includes(term) || String(selector).includes(term))) {{ + matches.push(selector); + }} + if (matches.length >= limit) return matches; + }} + return available.slice(0, limit); +}} + +function selectorValidationError(payload, selectors, missing, sourceCommand, selectedOutputKind, discoveryCommand, detailCommand) {{ + const sampleLimit = SELECTOR_INVENTORY_SAMPLE_LIMIT; + const {{ count, sample: available }} = selectorInventorySummary(payload, sampleLimit); + const suggestions = {{}}; + for (const selector of missing) suggestions[selector] = selectorSuggestions(selector, available, SELECTOR_SUGGESTION_LIMIT); + const error = {{ + kind: selectorValidationKind(selectedOutputKind), + status: 'invalid-selector', + source_command: boundedSelectorErrorText(sourceCommand), + requested_selectors: selectors, + unknown_selectors: missing, + selector_inventory: {{ + status: 'omitted-from-validation-error', + available_count: count, + sample: available, + sample_limit: sampleLimit, + discovery_command: boundedSelectorErrorText(discoveryCommand), + inventory_command: boundedSelectorErrorText(detailCommand), + rule: 'Full selector inventories are omitted from validation errors; use the inventory command for complete details.' + }}, + suggestions, + validation_rule: 'Selector requests are atomic: any unknown selector prevents partial projection output.' + }}; + return fitSelectorErrorEnvelope(error); +}} + +function selectorRequestValidationError(selectors, requestError, sourceCommand, selectedOutputKind) {{ + const error = {{ + kind: selectorValidationKind(selectedOutputKind), + status: 'invalid-selector-request', + source_command: boundedSelectorErrorText(sourceCommand), + requested_selectors: selectors, + selector_request: {{ status: 'rejected', ...requestError }}, + validation_rule: 'Selector requests are bounded and atomic: too many selectors or overlong selectors are rejected before projection.' + }}; + return fitSelectorErrorEnvelope(error); }} function projectPayload(values, args) {{ @@ -501,24 +763,22 @@ class RuntimeError extends Error {{}} if (!Object.prototype.hasOwnProperty.call(values, sourceName)) throw new RuntimeError(`payload.project source value is missing: ${{sourceName}}`); const payload = values[sourceName]; const selectValueName = String(args.select_value ?? 'select'); - const selectors = selectorTokens(args.selectors ?? values[selectValueName]); + const selectedOutputKind = String(args.selected_output_kind ?? 'command-generation/selected-output/v1'); + const sourceCommand = String(args.source_command ?? values.operation_id ?? ''); + const selectorRequest = selectorTokens(args.selectors ?? values[selectValueName]); + const selectors = selectorRequest.selectors; + if (selectorRequest.error) return selectorRequestValidationError(selectors, selectorRequest.error, sourceCommand, selectedOutputKind); if (selectors.length === 0) return payload; - const selected = {{ - kind: String(args.selected_output_kind ?? 'command-generation/selected-output/v1'), - source_command: String(args.source_command ?? values.operation_id ?? ''), - values: {{}} - }}; - const missing = []; + const discoveryCommand = String(args.selector_inventory_command ?? ''); + const detailCommand = String(args.selector_detail_command ?? ''); + const missing = selectors.filter((selector) => !fieldByPath(payload, selector)[0]); + if (missing.length) return selectorValidationError(payload, selectors, missing, sourceCommand, selectedOutputKind, discoveryCommand, detailCommand); + const selected = {{ kind: selectedOutputKind, source_command: sourceCommand, values: {{}} }}; for (const selector of selectors) {{ const [found, value] = fieldByPath(payload, selector); if (found) selected.values[selector] = value; else missing.push(selector); }} - if (missing.length) {{ - selected.missing = missing; - selected.selector_rule = 'Comma-separated dot paths select exact JSON fields; unknown fields are reported in missing.'; - selected.available_selectors = availableSelectorsForPayload(payload); - }} return selected; }} @@ -1041,7 +1301,9 @@ def _typescript_host_primitive_support_module( host_manifest=host_manifest, support_path=host_manifest.typescript_primitive_support_path, ) - support = host_manifest.typescript_primitive_support_path.read_text(encoding="utf-8") + support = host_manifest.typescript_primitive_support_path.read_text( + encoding="utf-8" + ) return ( "// Generated target-local host primitive support module.\n" f"// Source: {source_path}\n" @@ -1062,15 +1324,21 @@ def _typescript_cli_module( source_path: str, regenerate_command: str, ) -> str: - command_names = sorted(command["command"]["name"] for command in package["commands"]) + command_names = sorted( + command["command"]["name"] for command in package["commands"] + ) rendered_commands = json.dumps(command_names) - rendered_interfaces = json.dumps(_typescript_interface_payload(package), indent=2, sort_keys=True) + rendered_interfaces = json.dumps( + _typescript_interface_payload(package), indent=2, sort_keys=True + ) native_operation_ids = sorted(_typescript_native_operation_ids(package)) rendered_native_operation_ids = json.dumps(native_operation_ids) weak_agent_status = _weak_agent_routing_for_target(target, maturity_levels) recovery_command = f"{target['entrypoints'][0]} --help" boundary_summary = "TypeScript CLI boundary: generated parser, validation, and command execution are Node/TypeScript only." - native_helpers = _typescript_native_runtime_helpers(recovery_command=recovery_command) + native_helpers = _typescript_native_runtime_helpers( + recovery_command=recovery_command + ) return ( "#!/usr/bin/env node\n" "// Generated runnable adapter.\n" @@ -1239,7 +1507,9 @@ def _typescript_mock_runtime() -> str: def _typescript_required_option_case(package: dict[str, Any]) -> dict[str, Any] | None: - def find_required(interface: dict[str, Any], path: list[str]) -> dict[str, Any] | None: + def find_required( + interface: dict[str, Any], path: list[str] + ) -> dict[str, Any] | None: for option in interface.get("options", []): if isinstance(option, dict) and option.get("required") is True: flags = option.get("flags", []) @@ -1303,16 +1573,28 @@ def _typescript_sample_invocations(command: dict[str, Any]) -> dict[str, Any]: command_name = str(command.get("command", {}).get("name", "")).strip() interface = command.get("interface", {}) if not command_name or not isinstance(interface, dict): - return {"json_args": [], "spaced_args": [], "path": [], "requires_subcommand": False, "format_path": []} + return { + "json_args": [], + "spaced_args": [], + "path": [], + "requires_subcommand": False, + "format_path": [], + } path = [command_name] current = interface requires_subcommand = False while True: - subcommands = [item for item in current.get("subcommands", []) if isinstance(item, dict)] - subcommands_required = bool(subcommands and current.get("subcommands_required") is not False) + subcommands = [ + item for item in current.get("subcommands", []) if isinstance(item, dict) + ] + subcommands_required = bool( + subcommands and current.get("subcommands_required") is not False + ) if not subcommands_required: break - first_subcommand = sorted(subcommands, key=lambda item: str(item.get("name", "")))[0] + first_subcommand = sorted( + subcommands, key=lambda item: str(item.get("name", "")) + )[0] subcommand_name = str(first_subcommand.get("name", "")).strip() if not subcommand_name: break @@ -1323,14 +1605,22 @@ def _typescript_sample_invocations(command: dict[str, Any]) -> dict[str, Any]: required_positionals = [ item for item in current.get("arguments", []) - if isinstance(item, dict) and item.get("nargs") != "?" and item.get("default") is None + if isinstance(item, dict) + and item.get("nargs") != "?" + and item.get("default") is None + ] + required_options = [ + item + for item in current.get("options", []) + if isinstance(item, dict) and item.get("required") is True ] - required_options = [item for item in current.get("options", []) if isinstance(item, dict) and item.get("required") is True] json_args = list(path) spaced_args: list[str] = [] spaced_arg_index: int | None = None for argument in required_positionals: - value = _typescript_sample_value(argument, fallback=str(argument.get("name") or "value")) + value = _typescript_sample_value( + argument, fallback=str(argument.get("name") or "value") + ) if spaced_arg_index is None and argument.get("type") != "integer": spaced_arg_index = len(json_args) json_args.append(value) @@ -1352,18 +1642,27 @@ def _typescript_sample_invocations(command: dict[str, Any]) -> dict[str, Any]: ( item for item in current.get("options", []) - if isinstance(item, dict) and item.get("name") == "format" and _typescript_option_flag(item) + if isinstance(item, dict) + and item.get("name") == "format" + and _typescript_option_flag(item) ), None, ) format_path = list(json_args) if format_option is not None: - json_args.extend([_typescript_option_flag(format_option), _typescript_sample_value(format_option, fallback="json")]) + json_args.extend( + [ + _typescript_option_flag(format_option), + _typescript_sample_value(format_option, fallback="json"), + ] + ) dry_run_option = next( ( item for item in current.get("options", []) - if isinstance(item, dict) and item.get("name") == "dry_run" and _typescript_option_flag(item) + if isinstance(item, dict) + and item.get("name") == "dry_run" + and _typescript_option_flag(item) ), None, ) @@ -1382,10 +1681,16 @@ def _typescript_sample_invocations(command: dict[str, Any]) -> dict[str, Any]: def _typescript_test(package: dict[str, Any], target: dict[str, Any]) -> str: - expected_commands = sorted(command["command"]["name"] for command in package["commands"]) + expected_commands = sorted( + command["command"]["name"] for command in package["commands"] + ) rendered_expected = json.dumps(expected_commands) sample_command = expected_commands[0] - sample_command_record = next(command for command in package["commands"] if command["command"]["name"] == sample_command) + sample_command_record = next( + command + for command in package["commands"] + if command["command"]["name"] == sample_command + ) sample_invocations = _typescript_sample_invocations(sample_command_record) sample_path = sample_invocations["path"] sample_requires_subcommand = bool(sample_invocations["requires_subcommand"]) @@ -1406,7 +1711,9 @@ def _typescript_test(package: dict[str, Any], target: dict[str, Any]) -> str: " assert.match(result.stdout, /Node\\/TypeScript only/);\n" " assert.doesNotMatch(result.stdout, /Python runtime handoff/);\n" ) - imports = "import assert from 'node:assert/strict';\nimport test from 'node:test';\n" + imports = ( + "import assert from 'node:assert/strict';\nimport test from 'node:test';\n" + ) if runnable: imports += "import { spawnSync } from 'node:child_process';\nimport { fileURLToPath } from 'node:url';\n" imports += "import { mkdirSync, readFileSync, rmSync } from 'node:fs';\n" @@ -1550,7 +1857,9 @@ def _typescript_test(package: dict[str, Any], target: dict[str, Any]) -> str: return body -def _target_scoped_package_resource(package: dict[str, Any], target: dict[str, Any], *, manifest_schema_version: str) -> dict[str, Any]: +def _target_scoped_package_resource( + package: dict[str, Any], target: dict[str, Any], *, manifest_schema_version: str +) -> dict[str, Any]: scoped = package_resource_with_generation_metadata( package, manifest_schema_version=manifest_schema_version, @@ -1596,15 +1905,31 @@ def render_typescript_outputs( ), GeneratedOutput( root / "src" / "commandPackage.ts", - _typescript_module(package, source_path=source_path, regenerate_command=regenerate_command), + _typescript_module( + package, source_path=source_path, regenerate_command=regenerate_command + ), ), GeneratedOutput( root / "resources" / "command_package.json", - _json_block(_target_scoped_package_resource(package, target, manifest_schema_version=manifest_schema_version)) + "\n", + _json_block( + _target_scoped_package_resource( + package, target, manifest_schema_version=manifest_schema_version + ) + ) + + "\n", ), ] - outputs.extend(_typescript_resource_copy_outputs(package, repo_root=repo_root, root=root, host_manifest=host_manifest)) - outputs.append(GeneratedOutput(root / "test" / "command-package.test.mjs", _typescript_test(package, target))) + outputs.extend( + _typescript_resource_copy_outputs( + package, repo_root=repo_root, root=root, host_manifest=host_manifest + ) + ) + outputs.append( + GeneratedOutput( + root / "test" / "command-package.test.mjs", + _typescript_test(package, target), + ) + ) if _is_runnable_typescript_target(target): outputs.append( GeneratedOutput( diff --git a/tests/primitive_conformance.py b/tests/primitive_conformance.py index 6d5dd36..7cbb8e1 100644 --- a/tests/primitive_conformance.py +++ b/tests/primitive_conformance.py @@ -128,8 +128,10 @@ def main() -> int: arguments={"source_command": "fixture.skills"}, context=context, ) - assert selected_payload["values"] == {"actions.0.path": "review", "message": "Skills"} - assert selected_payload["missing"] == ["missing"] + assert selected_payload["status"] == "invalid-selector" + assert selected_payload["unknown_selectors"] == ["missing"] + assert "values" not in selected_payload + assert "available_selectors" not in selected_payload emitted_json = execute_primitive( "output.emit", diff --git a/tests/test_primitive_executor.py b/tests/test_primitive_executor.py index 3052fe4..ae209b0 100644 --- a/tests/test_primitive_executor.py +++ b/tests/test_primitive_executor.py @@ -31,7 +31,9 @@ def primitive_context(tmp_path: Path) -> PrimitiveContext: return PrimitiveContext(cwd=tmp_path, roots={"package": package_root}) -def test_path_target_root_resolve_uses_context_cwd(primitive_context: PrimitiveContext) -> None: +def test_path_target_root_resolve_uses_context_cwd( + primitive_context: PrimitiveContext, +) -> None: target_root = execute_primitive( "path.target_root.resolve", values={"target": "target"}, @@ -61,7 +63,9 @@ def test_filesystem_read_is_rooted(primitive_context: PrimitiveContext) -> None: ) -def test_filesystem_glob_returns_stable_relative_files(primitive_context: PrimitiveContext) -> None: +def test_filesystem_glob_returns_stable_relative_files( + primitive_context: PrimitiveContext, +) -> None: files = execute_primitive( "filesystem.glob", values={}, @@ -69,10 +73,15 @@ def test_filesystem_glob_returns_stable_relative_files(primitive_context: Primit context=primitive_context, ) - assert files == [{"relative_path": "alpha.txt"}, {"relative_path": "nested/beta.txt"}] + assert files == [ + {"relative_path": "alpha.txt"}, + {"relative_path": "nested/beta.txt"}, + ] -def test_filesystem_primitives_can_use_value_roots(primitive_context: PrimitiveContext) -> None: +def test_filesystem_primitives_can_use_value_roots( + primitive_context: PrimitiveContext, +) -> None: target = primitive_context.cwd / "target" (target / "feedback.md").write_text("ok", encoding="utf-8") (target / "fixtures").mkdir() @@ -94,7 +103,9 @@ def test_filesystem_primitives_can_use_value_roots(primitive_context: PrimitiveC ) == [{"relative_path": "fixtures/case.json"}] -def test_json_parse_uses_named_source_value(primitive_context: PrimitiveContext) -> None: +def test_json_parse_uses_named_source_value( + primitive_context: PrimitiveContext, +) -> None: registry = execute_primitive( "json.parse", values={"registry_text": '{"skills": [{"id": "review"}]}'}, @@ -104,7 +115,9 @@ def test_json_parse_uses_named_source_value(primitive_context: PrimitiveContext) assert registry == {"skills": [{"id": "review"}]} -def test_toml_table_counts_returns_stable_counts(primitive_context: PrimitiveContext) -> None: +def test_toml_table_counts_returns_stable_counts( + primitive_context: PrimitiveContext, +) -> None: target = primitive_context.cwd / "target" manifest = target / ".agentic-workspace" / "memory" / "repo" manifest.mkdir(parents=True) @@ -150,11 +163,17 @@ def test_toml_table_counts_returns_stable_counts(primitive_context: PrimitiveCon } -def test_toml_table_counts_reports_missing_file(primitive_context: PrimitiveContext) -> None: +def test_toml_table_counts_reports_missing_file( + primitive_context: PrimitiveContext, +) -> None: result = execute_primitive( "toml.table.counts", values={"target_root": str(primitive_context.cwd / "target")}, - arguments={"base_value": "target_root", "path": "missing.toml", "table": "notes"}, + arguments={ + "base_value": "target_root", + "path": "missing.toml", + "table": "notes", + }, context=primitive_context, ) @@ -163,14 +182,18 @@ def test_toml_table_counts_reports_missing_file(primitive_context: PrimitiveCont assert result["table_status"] == "missing" -def test_payload_assemble_supports_file_and_skill_records(primitive_context: PrimitiveContext) -> None: +def test_payload_assemble_supports_file_and_skill_records( + primitive_context: PrimitiveContext, +) -> None: file_payload = execute_primitive( "payload.assemble", values={ "target_root": str((primitive_context.cwd / "target").resolve()), "files": [{"relative_path": "alpha.txt"}], }, - arguments={"fields": {"dry_run": True, "message": "Files", "actions_from": "files"}}, + arguments={ + "fields": {"dry_run": True, "message": "Files", "actions_from": "files"} + }, context=primitive_context, ) skill_payload = execute_primitive( @@ -194,7 +217,9 @@ def test_payload_assemble_supports_file_and_skill_records(primitive_context: Pri assert skill_payload["actions"][0]["path"] == "review" -def test_payload_assemble_supports_template_records(primitive_context: PrimitiveContext) -> None: +def test_payload_assemble_supports_template_records( + primitive_context: PrimitiveContext, +) -> None: payload = execute_primitive( "payload.assemble", values={ @@ -208,11 +233,28 @@ def test_payload_assemble_supports_template_records(primitive_context: Primitive "target_root": {"$value": "target_root"}, "route_report_summary": { "feedback": { - "status": {"$exists_status": {"value": "feedback_exists", "present": "present", "missing": "missing"}}, - "path": {"$join_path": {"base": "target_root", "path": "feedback.md"}}, + "status": { + "$exists_status": { + "value": "feedback_exists", + "present": "present", + "missing": "missing", + } + }, + "path": { + "$join_path": { + "base": "target_root", + "path": "feedback.md", + } + }, }, "fixtures": { - "status": {"$count_status": {"value": "fixture_files", "present": "present", "missing": "missing"}}, + "status": { + "$count_status": { + "value": "fixture_files", + "present": "present", + "missing": "missing", + } + }, "fixture_count": {"$count": "fixture_files"}, }, }, @@ -226,7 +268,9 @@ def test_payload_assemble_supports_template_records(primitive_context: Primitive assert payload["route_report_summary"]["fixtures"]["fixture_count"] == 1 -def test_payload_assemble_supports_template_field_selectors(primitive_context: PrimitiveContext) -> None: +def test_payload_assemble_supports_template_field_selectors( + primitive_context: PrimitiveContext, +) -> None: payload = execute_primitive( "payload.assemble", values={ @@ -241,8 +285,15 @@ def test_payload_assemble_supports_template_field_selectors(primitive_context: P "template": { "status": {"$field": {"value": "table_counts", "path": "status"}}, "nested": { - "note_count": {"$field": {"value": "table_counts", "path": ["note_count"]}}, - "required_count": {"$field": {"value": "table_counts", "path": "required_count"}}, + "note_count": { + "$field": {"value": "table_counts", "path": ["note_count"]} + }, + "required_count": { + "$field": { + "value": "table_counts", + "path": "required_count", + } + }, }, } } @@ -250,10 +301,15 @@ def test_payload_assemble_supports_template_field_selectors(primitive_context: P context=primitive_context, ) - assert payload == {"status": "present", "nested": {"note_count": 3, "required_count": 1}} + assert payload == { + "status": "present", + "nested": {"note_count": 3, "required_count": 1}, + } -def test_payload_assemble_selects_templates_by_declared_mode(primitive_context: PrimitiveContext) -> None: +def test_payload_assemble_selects_templates_by_declared_mode( + primitive_context: PrimitiveContext, +) -> None: payload = execute_primitive( "payload.assemble", values={"mode": "compact", "items": ["a", "b", "c"]}, @@ -277,7 +333,9 @@ def test_payload_assemble_selects_templates_by_declared_mode(primitive_context: assert payload == {"summary": 3} -def test_payload_assemble_selects_falsy_declared_mode_values(primitive_context: PrimitiveContext) -> None: +def test_payload_assemble_selects_falsy_declared_mode_values( + primitive_context: PrimitiveContext, +) -> None: payload = execute_primitive( "payload.assemble", values={"mode": False}, @@ -301,7 +359,9 @@ def test_payload_assemble_selects_falsy_declared_mode_values(primitive_context: assert payload == {"selected": "false-key"} -def test_payload_assemble_builds_package_resource_manifest_payload(primitive_context: PrimitiveContext) -> None: +def test_payload_assemble_builds_package_resource_manifest_payload( + primitive_context: PrimitiveContext, +) -> None: payload = execute_primitive( "payload.assemble", values={ @@ -334,7 +394,9 @@ def test_payload_assemble_builds_package_resource_manifest_payload(primitive_con } -def test_payload_view_projects_allowlisted_fields_with_limits(primitive_context: PrimitiveContext) -> None: +def test_payload_view_projects_allowlisted_fields_with_limits( + primitive_context: PrimitiveContext, +) -> None: view = execute_primitive( "payload.view", values={ @@ -364,8 +426,12 @@ def test_payload_view_projects_allowlisted_fields_with_limits(primitive_context: } -def test_payload_view_rejects_non_object_limits(primitive_context: PrimitiveContext) -> None: - with pytest.raises(PrimitiveExecutionError, match="payload.view limits must be an object"): +def test_payload_view_rejects_non_object_limits( + primitive_context: PrimitiveContext, +) -> None: + with pytest.raises( + PrimitiveExecutionError, match="payload.view limits must be an object" + ): execute_primitive( "payload.view", values={"result": {"status": "ready"}}, @@ -374,7 +440,9 @@ def test_payload_view_rejects_non_object_limits(primitive_context: PrimitiveCont ) -def test_transaction_plan_builds_dry_run_plan_with_package_owned_apply_hooks(primitive_context: PrimitiveContext) -> None: +def test_transaction_plan_builds_dry_run_plan_with_package_owned_apply_hooks( + primitive_context: PrimitiveContext, +) -> None: plan = execute_primitive( "transaction.plan", values={ @@ -413,8 +481,16 @@ def test_transaction_plan_builds_dry_run_plan_with_package_owned_apply_hooks(pri } -def test_transaction_plan_rejects_paths_outside_relative_resource_namespace(primitive_context: PrimitiveContext) -> None: - for path in ("", "/absolute.md", "../escape.md", "nested/../escape.md", "nested//empty.md"): +def test_transaction_plan_rejects_paths_outside_relative_resource_namespace( + primitive_context: PrimitiveContext, +) -> None: + for path in ( + "", + "/absolute.md", + "../escape.md", + "nested/../escape.md", + "nested//empty.md", + ): with pytest.raises( PrimitiveExecutionError, match="transaction.plan resource path must be relative", @@ -426,7 +502,9 @@ def test_transaction_plan_rejects_paths_outside_relative_resource_namespace(prim ) -def test_payload_project_selects_exact_paths_and_reports_missing(primitive_context: PrimitiveContext) -> None: +def test_payload_project_rejects_unknown_selectors_atomically( + primitive_context: PrimitiveContext, +) -> None: result = execute_primitive( "payload.project", values={ @@ -437,17 +515,115 @@ def test_payload_project_selects_exact_paths_and_reports_missing(primitive_conte "items": [{"name": "alpha"}, {"name": "beta"}], }, }, + arguments={ + "selector_inventory_command": "fixture.show selectors", + "selector_detail_command": "fixture.show details", + }, context=primitive_context, ) - assert result["kind"] == "command-generation/selected-output/v1" + assert result["kind"] == "command-generation/selector-validation-error/v1" + assert result["status"] == "invalid-selector" assert result["source_command"] == "fixture.show" - assert result["values"] == {"items.0.name": "alpha", "summary.count": 2} - assert result["missing"] == ["missing.value"] - assert "items.1.name" in result["available_selectors"] + assert result["requested_selectors"] == [ + "items.0.name", + "summary.count", + "missing.value", + ] + assert result["unknown_selectors"] == ["missing.value"] + assert "values" not in result + assert "available_selectors" not in result + assert result["selector_inventory"]["status"] == "omitted-from-validation-error" + assert result["selector_inventory"]["available_count"] >= 4 + assert ( + len(result["selector_inventory"]["sample"]) + <= result["selector_inventory"]["sample_limit"] + ) + assert result["selector_inventory"]["discovery_command"] == "fixture.show selectors" + assert result["selector_inventory"]["inventory_command"] == "fixture.show details" + assert result["suggestions"]["missing.value"] + + +def test_payload_project_rejects_selector_request_limit_violations( + primitive_context: PrimitiveContext, +) -> None: + too_many = [f"field{i}" for i in range(33)] + result = execute_primitive( + "payload.project", + values={"payload": {f"field{i}": i for i in range(33)}}, + arguments={ + "source": "payload", + "source_command": "fixture.status", + "selectors": too_many, + "selected_output_kind": "fixture/selected-output/v1", + }, + context=primitive_context, + ) + + assert result["kind"] == "fixture/selector-validation-error/v1" + assert result["status"] == "invalid-selector-request" + assert result["requested_selectors"] == too_many[:32] + assert result["selector_request"]["reason"] == "too-many-selectors" + assert result["selector_request"]["requested_selector_count"] == 33 + assert result["selector_request"]["max_selectors"] == 32 + assert "values" not in result + + overlong_selector = "a" * 257 + result = execute_primitive( + "payload.project", + values={"payload": {"status": "ready"}}, + arguments={ + "source": "payload", + "source_command": "fixture.status", + "selectors": [overlong_selector], + }, + context=primitive_context, + ) + assert result["status"] == "invalid-selector-request" + assert result["requested_selectors"] == [] + assert result["selector_request"]["reason"] == "selector-too-long" + assert result["selector_request"]["selector_bytes"] == 257 + assert result["selector_request"]["max_selector_bytes"] == 256 -def test_payload_project_can_use_declared_selector_list(primitive_context: PrimitiveContext) -> None: + too_large = [f"{'s' * 14}{index:02d}" for index in range(31)] + [f"{'s' * 15}31"] + result = execute_primitive( + "payload.project", + values={"payload": {}}, + arguments={ + "source": "payload", + "source_command": "fixture.status", + "selectors": too_large, + }, + context=primitive_context, + ) + + assert result["status"] == "invalid-selector-request" + assert len(result["requested_selectors"]) == 31 + assert result["selector_request"]["reason"] == "selector-request-too-large" + assert result["selector_request"]["selector_request_bytes"] == 513 + assert result["selector_request"]["max_selector_request_bytes"] == 512 + + astral = "\U0001f600" + result = execute_primitive( + "payload.project", + values={"payload": {}}, + arguments={ + "source": "payload", + "source_command": "fixture.status", + "selectors": [astral * 65], + }, + context=primitive_context, + ) + + assert result["status"] == "invalid-selector-request" + assert result["selector_request"]["reason"] == "selector-too-long" + assert result["selector_request"]["selector_bytes"] == 260 + + +def test_payload_project_can_use_declared_selector_list( + primitive_context: PrimitiveContext, +) -> None: result = execute_primitive( "payload.project", values={ @@ -472,7 +648,29 @@ def test_payload_project_can_use_declared_selector_list(primitive_context: Primi } -def test_operation_fragments_compose_reusable_step_groups(primitive_context: PrimitiveContext) -> None: +def test_payload_project_uses_host_validation_error_kind( + primitive_context: PrimitiveContext, +) -> None: + result = execute_primitive( + "payload.project", + values={"payload": {"status": "ready"}}, + arguments={ + "source": "payload", + "source_command": "fixture.status", + "selectors": ["status", "missing"], + "selected_output_kind": "fixture/selected-output/v1", + }, + context=primitive_context, + ) + + assert result["kind"] == "fixture/selector-validation-error/v1" + assert result["unknown_selectors"] == ["missing"] + assert "values" not in result + + +def test_operation_fragments_compose_reusable_step_groups( + primitive_context: PrimitiveContext, +) -> None: operation = { "id": "fixture.report", "ir_plan": { @@ -509,13 +707,17 @@ def test_operation_fragments_compose_reusable_step_groups(primitive_context: Pri operation, initial_values={"format": "json"}, context=primitive_context, - handlers={"fixture.make-result": lambda values, arguments, context: {"status": "ok"}}, + handlers={ + "fixture.make-result": lambda values, arguments, context: {"status": "ok"} + }, ) assert json.loads(values["emitted"]) == {"status": "ok"} -def test_run_operation_steps_can_project_payload_fields(primitive_context: PrimitiveContext) -> None: +def test_run_operation_steps_can_project_payload_fields( + primitive_context: PrimitiveContext, +) -> None: operation = { "id": "fixture.project", "ir_plan": { @@ -542,7 +744,11 @@ def test_run_operation_steps_can_project_payload_fields(primitive_context: Primi operation, initial_values={}, context=primitive_context, - handlers={"fixture.make-result": lambda values, arguments, context: {"summary": {"status": "ready"}}}, + handlers={ + "fixture.make-result": lambda values, arguments, context: { + "summary": {"status": "ready"} + } + }, ) assert values["selected"]["values"] == {"summary.status": "ready"} @@ -553,32 +759,56 @@ def test_operation_fragments_reject_cycles(primitive_context: PrimitiveContext) "id": "fixture.report", "ir_plan": { "fragments": [ - {"id": "a", "steps": [{"id": "call_b", "uses_fragment": "b", "description": "Call b."}]}, - {"id": "b", "steps": [{"id": "call_a", "uses_fragment": "a", "description": "Call a."}]}, + { + "id": "a", + "steps": [ + {"id": "call_b", "uses_fragment": "b", "description": "Call b."} + ], + }, + { + "id": "b", + "steps": [ + {"id": "call_a", "uses_fragment": "a", "description": "Call a."} + ], + }, ], "steps": [{"id": "call_a", "uses_fragment": "a", "description": "Call a."}], }, } - with pytest.raises(PrimitiveExecutionError, match="operation ir_plan fragment cycle: a -> b -> a"): + with pytest.raises( + PrimitiveExecutionError, match="operation ir_plan fragment cycle: a -> b -> a" + ): run_operation_steps(operation, initial_values={}, context=primitive_context) -def test_output_emit_supports_json_and_text(primitive_context: PrimitiveContext) -> None: +def test_output_emit_supports_json_and_text( + primitive_context: PrimitiveContext, +) -> None: payload = { "dry_run": True, "message": "Skills", "actions": [{"kind": "skill", "id": "review", "path": "review/SKILL.md"}], } - emitted_json = execute_primitive("output.emit", values={"result": payload, "format": "json"}, context=primitive_context) - emitted_text = execute_primitive("output.emit", values={"result": payload, "format": "text"}, context=primitive_context) + emitted_json = execute_primitive( + "output.emit", + values={"result": payload, "format": "json"}, + context=primitive_context, + ) + emitted_text = execute_primitive( + "output.emit", + values={"result": payload, "format": "text"}, + context=primitive_context, + ) assert json.loads(emitted_json)["actions"][0]["id"] == "review" assert emitted_text == "Skills\n- review/SKILL.md\n" -def test_output_emit_supports_declared_text_views(primitive_context: PrimitiveContext) -> None: +def test_output_emit_supports_declared_text_views( + primitive_context: PrimitiveContext, +) -> None: payload = { "kind": "fixture/report/v1", "profile": "compact", @@ -610,7 +840,13 @@ def test_output_emit_supports_declared_text_views(primitive_context: PrimitiveCo "Record count: {records|len}", {"literal": "Values:"}, {"json": "values"}, - {"when": "warnings", "lines": ["Warnings:", {"for_each": {"path": "warnings", "template": "- {}"}}]}, + { + "when": "warnings", + "lines": [ + "Warnings:", + {"for_each": {"path": "warnings", "template": "- {}"}}, + ], + }, { "for_each": { "path": "records", @@ -640,7 +876,9 @@ def test_output_emit_supports_declared_text_views(primitive_context: PrimitiveCo ) -def test_output_emit_serializes_module_result_objects(primitive_context: PrimitiveContext) -> None: +def test_output_emit_serializes_module_result_objects( + primitive_context: PrimitiveContext, +) -> None: @dataclass class Action: kind: str @@ -668,8 +906,16 @@ def to_dict(self) -> dict[str, object]: "actions": [{"kind": "create", "path": "AGENTS.md"}], } - emitted_json = execute_primitive("output.emit", values={"result": ModuleResult(), "format": "json"}, context=primitive_context) - emitted_text = execute_primitive("output.emit", values={"result": ModuleResult(), "format": "text"}, context=primitive_context) + emitted_json = execute_primitive( + "output.emit", + values={"result": ModuleResult(), "format": "json"}, + context=primitive_context, + ) + emitted_text = execute_primitive( + "output.emit", + values={"result": ModuleResult(), "format": "text"}, + context=primitive_context, + ) emitted_payload = json.loads(emitted_json) assert emitted_payload["actions"][0]["path"] == "AGENTS.md" @@ -680,22 +926,31 @@ def to_dict(self) -> dict[str, object]: dataclass_json = execute_primitive( "output.emit", values={ - "result": DataclassResult(False, "Planned", [Action("create", primitive_context.cwd / "plan.md")]), + "result": DataclassResult( + False, "Planned", [Action("create", primitive_context.cwd / "plan.md")] + ), "format": "json", }, context=primitive_context, ) - assert json.loads(dataclass_json)["actions"][0]["path"] == str(primitive_context.cwd / "plan.md") + assert json.loads(dataclass_json)["actions"][0]["path"] == str( + primitive_context.cwd / "plan.md" + ) contract_json = execute_primitive( "output.emit", - values={"result": ContractResult(primitive_context.cwd / "raw.md"), "format": "json"}, + values={ + "result": ContractResult(primitive_context.cwd / "raw.md"), + "format": "json", + }, context=primitive_context, ) assert json.loads(contract_json)["path"] == "contract-owned/path.md" -def test_removed_transitional_primitives_are_not_generic_executor_behavior(primitive_context: PrimitiveContext) -> None: +def test_removed_transitional_primitives_are_not_generic_executor_behavior( + primitive_context: PrimitiveContext, +) -> None: for primitive in ( "workspace.root.resolve", "payload.status", @@ -707,10 +962,14 @@ def test_removed_transitional_primitives_are_not_generic_executor_behavior(primi "transaction.apply", ): with pytest.raises(PrimitiveExecutionError, match="unsupported host primitive"): - execute_primitive(primitive, values={"result": {}}, context=primitive_context) + execute_primitive( + primitive, values={"result": {}}, context=primitive_context + ) -def test_python_function_call_resolves_checked_in_arguments(primitive_context: PrimitiveContext) -> None: +def test_python_function_call_resolves_checked_in_arguments( + primitive_context: PrimitiveContext, +) -> None: result = execute_primitive( "python.function.call", values={"payload_text": '{"status": "ok"}'}, @@ -727,7 +986,9 @@ def test_python_function_call_resolves_checked_in_arguments(primitive_context: P assert result == {"status": "ok"} -def test_operation_call_maps_positional_keyword_and_coerced_values(primitive_context: PrimitiveContext) -> None: +def test_operation_call_maps_positional_keyword_and_coerced_values( + primitive_context: PrimitiveContext, +) -> None: runtime_module = types.ModuleType("fixture_operation_runtime") calls: list[dict[str, object]] = [] @@ -788,11 +1049,15 @@ def archive_operation( assert calls == [result] -def test_operation_dispatch_selects_branch_specific_function_and_mapping(primitive_context: PrimitiveContext) -> None: +def test_operation_dispatch_selects_branch_specific_function_and_mapping( + primitive_context: PrimitiveContext, +) -> None: runtime_module = types.ModuleType("fixture_dispatch_runtime") calls: list[dict[str, object]] = [] - def archive_parent(parent_lane_closeout: str, *, retain_archive: bool) -> dict[str, object]: + def archive_parent( + parent_lane_closeout: str, *, retain_archive: bool + ) -> dict[str, object]: call = { "branch": "parent", "parent_lane_closeout": parent_lane_closeout, @@ -801,7 +1066,9 @@ def archive_parent(parent_lane_closeout: str, *, retain_archive: bool) -> dict[s calls.append(call) return call - def archive_execplan(execplan_id: str, *, retain_archive: bool) -> dict[str, object]: + def archive_execplan( + execplan_id: str, *, retain_archive: bool + ) -> dict[str, object]: call = { "branch": "execplan", "execplan_id": execplan_id, @@ -864,17 +1131,25 @@ def archive_execplan(execplan_id: str, *, retain_archive: bool) -> dict[str, obj assert calls == [parent_result, execplan_result] -def test_python_function_call_rejects_unresolved_targets(primitive_context: PrimitiveContext) -> None: +def test_python_function_call_rejects_unresolved_targets( + primitive_context: PrimitiveContext, +) -> None: with pytest.raises(PrimitiveExecutionError, match="cannot resolve"): execute_primitive( "python.function.call", values={}, - arguments={"import_module": "json", "function": "missing_function", "kwargs": {}}, + arguments={ + "import_module": "json", + "function": "missing_function", + "kwargs": {}, + }, context=primitive_context, ) -def test_python_function_call_rejects_missing_value_bindings(primitive_context: PrimitiveContext) -> None: +def test_python_function_call_rejects_missing_value_bindings( + primitive_context: PrimitiveContext, +) -> None: with pytest.raises(PrimitiveExecutionError, match="cannot resolve value"): execute_primitive( "python.function.call", @@ -888,7 +1163,9 @@ def test_python_function_call_rejects_missing_value_bindings(primitive_context: ) -def test_run_operation_steps_executes_declared_dataflow(primitive_context: PrimitiveContext) -> None: +def test_run_operation_steps_executes_declared_dataflow( + primitive_context: PrimitiveContext, +) -> None: operation = { "ir_plan": { "steps": [ @@ -902,7 +1179,13 @@ def test_run_operation_steps_executes_declared_dataflow(primitive_context: Primi { "id": "assemble", "uses": "payload.assemble", - "arguments": {"fields": {"dry_run": True, "message": "Skills", "actions_from": "registry.skills"}}, + "arguments": { + "fields": { + "dry_run": True, + "message": "Skills", + "actions_from": "registry.skills", + } + }, "outputs": ["result"], }, {"id": "emit", "uses": "output.emit", "outputs": ["emitted"]}, @@ -910,12 +1193,16 @@ def test_run_operation_steps_executes_declared_dataflow(primitive_context: Primi } } - values = run_operation_steps(operation, initial_values={"format": "json"}, context=primitive_context) + values = run_operation_steps( + operation, initial_values={"format": "json"}, context=primitive_context + ) assert json.loads(values["emitted"])["actions"][0]["source"] == "review" -def test_run_operation_steps_honors_simple_when_conditions(primitive_context: PrimitiveContext) -> None: +def test_run_operation_steps_honors_simple_when_conditions( + primitive_context: PrimitiveContext, +) -> None: operation = { "ir_plan": { "steps": [ @@ -923,13 +1210,24 @@ def test_run_operation_steps_honors_simple_when_conditions(primitive_context: Pr "id": "skip_text", "uses": "payload.assemble", "when": {"value": "format", "equals": "text"}, - "arguments": {"fields": {"dry_run": True, "message": "Text", "actions_from": "files"}}, + "arguments": { + "fields": { + "dry_run": True, + "message": "Text", + "actions_from": "files", + } + }, "outputs": ["result"], }, { "id": "emit_json", "uses": "payload.assemble", - "when": {"all": [{"value": "format", "equals": "json"}, {"not": {"value": "verbose", "equals": True}}]}, + "when": { + "all": [ + {"value": "format", "equals": "json"}, + {"not": {"value": "verbose", "equals": True}}, + ] + }, "arguments": {"fields": {"template": {"message": "JSON"}}}, "outputs": ["result"], }, @@ -937,6 +1235,10 @@ def test_run_operation_steps_honors_simple_when_conditions(primitive_context: Pr } } - values = run_operation_steps(operation, initial_values={"format": "json", "verbose": False}, context=primitive_context) + values = run_operation_steps( + operation, + initial_values={"format": "json", "verbose": False}, + context=primitive_context, + ) assert values["result"] == {"message": "JSON"} diff --git a/tests/test_public_api.py b/tests/test_public_api.py index ecdf06d..50ef144 100644 --- a/tests/test_public_api.py +++ b/tests/test_public_api.py @@ -47,10 +47,20 @@ current_target_proof_evidence_inventory, structured_target_proof_evidence_inventory, ) -from command_generation.conformance import TypescriptFunctionConformanceTarget, run_typescript_function_conformance_case +from command_generation.conformance import ( + TypescriptFunctionConformanceTarget, + run_typescript_function_conformance_case, +) from command_generation.primitive_executor import PrimitiveContext, execute_primitive -from command_generation.targets.contract import PYTHON_TARGET_LAYOUT_VERSION, TYPESCRIPT_TARGET_LAYOUT_VERSION -from command_generation.targets.python import _python_command_module, _python_local_runtime_binding_module, _python_runtime_handler_module +from command_generation.targets.contract import ( + PYTHON_TARGET_LAYOUT_VERSION, + TYPESCRIPT_TARGET_LAYOUT_VERSION, +) +from command_generation.targets.python import ( + _python_command_module, + _python_local_runtime_binding_module, + _python_runtime_handler_module, +) def _maturity_policy() -> dict[str, object]: @@ -77,7 +87,13 @@ def _maturity_policy() -> dict[str, object]: "completion_gate": { "state": "satisfied", "scope": "python-only", - "satisfied_by": [{"id": "fixture-conformance", "proof": "pytest", "evidence": "non-AW fixture test"}], + "satisfied_by": [ + { + "id": "fixture-conformance", + "proof": "pytest", + "evidence": "non-AW fixture test", + } + ], }, }, "generated_package_maturity": { @@ -91,7 +107,7 @@ def _maturity_policy() -> dict[str, object]: "weak_agent_routing": "allowed-read-only", "runnable": True, } - ] + ], }, "non_python_runtime_binding": { "selected_model": "native runtime", @@ -125,7 +141,12 @@ def _fixture_manifest(tmp_path: Path) -> dict[str, object]: "arguments": {"root": "todo.package-payload", "path": "todos.json"}, "outputs": ["todo_text"], }, - {"id": "parse_todos", "uses": "json.parse", "arguments": {"source": "todo_text"}, "outputs": ["todos"]}, + { + "id": "parse_todos", + "uses": "json.parse", + "arguments": {"source": "todo_text"}, + "outputs": ["todos"], + }, { "id": "assemble", "uses": "payload.assemble", @@ -194,10 +215,18 @@ def _fixture_manifest(tmp_path: Path) -> dict[str, object]: } ], }, - "operation_ref": {"id": "todo.list.report", "path": "operations/todo.list.report.json"}, + "operation_ref": { + "id": "todo.list.report", + "path": "operations/todo.list.report.json", + }, "runtime_binding": { "kind": "operation-primitive-sequence", - "primitive_refs": ["filesystem.read", "json.parse", "payload.assemble", "output.emit"], + "primitive_refs": [ + "filesystem.read", + "json.parse", + "payload.assemble", + "output.emit", + ], }, "schemas": {"input": [], "output": []}, "effect_hints": { @@ -221,17 +250,29 @@ def _fixture_manifest(tmp_path: Path) -> dict[str, object]: "runtime_module_file": "cli", "render_runtime_module": True, "resource_copies": [ - {"source_root": "payload", "generated_root": "_payload", "required_marker": "todos.json"} + { + "source_root": "payload", + "generated_root": "_payload", + "required_marker": "todos.json", + } ], "operation_executor": { "module_file": "primitives.operation_executor", "supported_operation_ids": ["todo.list.report"], "initial_values": [ {"name": "format", "arg": "format", "default": "json"}, - {"name": "output_format", "arg": "format", "default": "json"}, + { + "name": "output_format", + "arg": "format", + "default": "json", + }, ], "context_roots": [ - {"name": "todo.package-payload", "generated_root": "_payload", "required_marker": "todos.json"} + { + "name": "todo.package-payload", + "generated_root": "_payload", + "required_marker": "todos.json", + } ], "handlers": [ { @@ -267,7 +308,9 @@ def _fixture_manifest_with_typescript(tmp_path: Path) -> dict[str, object]: return manifest -def _fixture_manifest_with_typescript_append_option(tmp_path: Path) -> dict[str, object]: +def _fixture_manifest_with_typescript_append_option( + tmp_path: Path, +) -> dict[str, object]: manifest = _fixture_manifest_with_typescript(tmp_path) package = cast(dict[str, object], cast(list[object], manifest["packages"])[0]) command = cast(dict[str, object], cast(list[object], package["commands"])[0]) @@ -285,7 +328,12 @@ def _fixture_manifest_with_typescript_append_option(tmp_path: Path) -> dict[str, operation = json.loads(operation_path.read_text(encoding="utf-8")) steps = cast(list[object], cast(dict[str, object], operation["ir_plan"])["steps"]) assemble = cast(dict[str, object], steps[2]) - template = cast(dict[str, object], cast(dict[str, object], cast(dict[str, object], assemble["arguments"])["fields"])["template"]) + template = cast( + dict[str, object], + cast( + dict[str, object], cast(dict[str, object], assemble["arguments"])["fields"] + )["template"], + ) template["tags"] = {"$value": "tags"} operation_path.write_text(json.dumps(operation, indent=2), encoding="utf-8") return manifest @@ -295,7 +343,10 @@ def _fixture_manifest_with_nested_cli_shapes(tmp_path: Path) -> dict[str, object manifest = _fixture_manifest_with_typescript(tmp_path) package = cast(dict[str, object], cast(list[object], manifest["packages"])[0]) command = cast(dict[str, object], cast(list[object], package["commands"])[0]) - operation_ref = {"id": "todo.list.report", "path": "operations/todo.list.report.json"} + operation_ref = { + "id": "todo.list.report", + "path": "operations/todo.list.report.json", + } command["interface"] = { "name": "list", "help": "List todos.", @@ -332,7 +383,12 @@ def _fixture_manifest_with_nested_cli_shapes(tmp_path: Path) -> dict[str, object } ], } - operation_executor = cast(dict[str, object], cast(dict[str, object], package["python_runtime_binding"])["operation_executor"]) + operation_executor = cast( + dict[str, object], + cast(dict[str, object], package["python_runtime_binding"])[ + "operation_executor" + ], + ) operation_executor["initial_values"] = [ {"name": "format", "arg": "format", "default": "json"}, {"name": "output_format", "arg": "format", "default": "json"}, @@ -342,8 +398,16 @@ def _fixture_manifest_with_nested_cli_shapes(tmp_path: Path) -> dict[str, object ] operation_path = tmp_path / "contracts" / "operations" / "todo.list.report.json" operation = json.loads(operation_path.read_text(encoding="utf-8")) - assemble = cast(dict[str, object], cast(list[object], cast(dict[str, object], operation["ir_plan"])["steps"])[2]) - template = cast(dict[str, object], cast(dict[str, object], cast(dict[str, object], assemble["arguments"])["fields"])["template"]) + assemble = cast( + dict[str, object], + cast(list[object], cast(dict[str, object], operation["ir_plan"])["steps"])[2], + ) + template = cast( + dict[str, object], + cast( + dict[str, object], cast(dict[str, object], assemble["arguments"])["fields"] + )["template"], + ) template["project"] = {"$value": "project"} template["priority"] = {"$value": "priority"} template["tags"] = {"$value": "tags"} @@ -351,7 +415,9 @@ def _fixture_manifest_with_nested_cli_shapes(tmp_path: Path) -> dict[str, object return manifest -def _fixture_manifest_with_typescript_sample_edge_shapes(tmp_path: Path) -> dict[str, object]: +def _fixture_manifest_with_typescript_sample_edge_shapes( + tmp_path: Path, +) -> dict[str, object]: manifest = _fixture_manifest_with_typescript(tmp_path) package = cast(dict[str, object], cast(list[object], manifest["packages"])[0]) command = cast(dict[str, object], cast(list[object], package["commands"])[0]) @@ -390,7 +456,12 @@ def _fixture_manifest_with_typescript_sample_edge_shapes(tmp_path: Path) -> dict }, ], } - operation_executor = cast(dict[str, object], cast(dict[str, object], package["python_runtime_binding"])["operation_executor"]) + operation_executor = cast( + dict[str, object], + cast(dict[str, object], package["python_runtime_binding"])[ + "operation_executor" + ], + ) operation_executor["initial_values"] = [ {"name": "format", "arg": "format", "default": "json"}, {"name": "output_format", "arg": "format", "default": "json"}, @@ -400,8 +471,16 @@ def _fixture_manifest_with_typescript_sample_edge_shapes(tmp_path: Path) -> dict ] operation_path = tmp_path / "contracts" / "operations" / "todo.list.report.json" operation = json.loads(operation_path.read_text(encoding="utf-8")) - assemble = cast(dict[str, object], cast(list[object], cast(dict[str, object], operation["ir_plan"])["steps"])[2]) - template = cast(dict[str, object], cast(dict[str, object], cast(dict[str, object], assemble["arguments"])["fields"])["template"]) + assemble = cast( + dict[str, object], + cast(list[object], cast(dict[str, object], operation["ir_plan"])["steps"])[2], + ) + template = cast( + dict[str, object], + cast( + dict[str, object], cast(dict[str, object], assemble["arguments"])["fields"] + )["template"], + ) template["count"] = {"$value": "count"} template["confirmed"] = {"$value": "confirmed"} template["limit"] = {"$value": "limit"} @@ -409,7 +488,9 @@ def _fixture_manifest_with_typescript_sample_edge_shapes(tmp_path: Path) -> dict return manifest -def _fixture_manifest_with_host_owned_python_primitive(tmp_path: Path) -> dict[str, object]: +def _fixture_manifest_with_host_owned_python_primitive( + tmp_path: Path, +) -> dict[str, object]: manifest = _fixture_manifest(tmp_path) (tmp_path / "todo_host_primitive_support.py").write_text( "def execute_host_primitive(primitive, *, values, arguments, context):\n" @@ -423,7 +504,10 @@ def _fixture_manifest_with_host_owned_python_primitive(tmp_path: Path) -> dict[s package = cast(dict[str, object], cast(list[object], manifest["packages"])[0]) command = cast(dict[str, object], cast(list[object], package["commands"])[0]) runtime_binding = cast(dict[str, object], command["runtime_binding"]) - runtime_binding["primitive_refs"] = [*cast(list[str], runtime_binding["primitive_refs"]), "todo.decorate"] + runtime_binding["primitive_refs"] = [ + *cast(list[str], runtime_binding["primitive_refs"]), + "todo.decorate", + ] operation_path = tmp_path / "contracts" / "operations" / "todo.list.report.json" operation = json.loads(operation_path.read_text(encoding="utf-8")) steps = cast(list[object], cast(dict[str, object], operation["ir_plan"])["steps"]) @@ -440,7 +524,9 @@ def _fixture_manifest_with_host_owned_python_primitive(tmp_path: Path) -> dict[s return manifest -def _fixture_manifest_with_host_owned_typescript_primitive(tmp_path: Path) -> dict[str, object]: +def _fixture_manifest_with_host_owned_typescript_primitive( + tmp_path: Path, +) -> dict[str, object]: manifest = _fixture_manifest_with_typescript(tmp_path) (tmp_path / "todoHostPrimitiveSupport.mjs").write_text( "export function executeHostPrimitive(primitive, values) {\n" @@ -451,10 +537,17 @@ def _fixture_manifest_with_host_owned_typescript_primitive(tmp_path: Path) -> di ) package = cast(dict[str, object], cast(list[object], manifest["packages"])[0]) targets = cast(list[object], package["targets"]) - package["targets"] = [target for target in targets if cast(dict[str, object], target)["kind"] == "typescript"] + package["targets"] = [ + target + for target in targets + if cast(dict[str, object], target)["kind"] == "typescript" + ] command = cast(dict[str, object], cast(list[object], package["commands"])[0]) runtime_binding = cast(dict[str, object], command["runtime_binding"]) - runtime_binding["primitive_refs"] = [*cast(list[str], runtime_binding["primitive_refs"]), "todo.decorate"] + runtime_binding["primitive_refs"] = [ + *cast(list[str], runtime_binding["primitive_refs"]), + "todo.decorate", + ] operation_path = tmp_path / "contracts" / "operations" / "todo.list.report.json" operation = json.loads(operation_path.read_text(encoding="utf-8")) steps = cast(list[object], cast(dict[str, object], operation["ir_plan"])["steps"]) @@ -480,14 +573,19 @@ def test_package_owned_schema_loads_fixture_manifest(tmp_path: Path) -> None: schema = json.loads(command_package_schema_path().read_text(encoding="utf-8")) assert schema["$id"] == "command-generation/command-package-ir.schema.json" assert schema["title"] == "Command Generation Command Package IR" - assert schema["properties"]["schema_version"]["const"] == "command-generation/command-package-ir/v1" + assert ( + schema["properties"]["schema_version"]["const"] + == "command-generation/command-package-ir/v1" + ) assert schema["x-command-generation-doc-role"] == "contract-reference" assert "x-agentic-workspace-doc-role" not in schema assert loaded["schema_version"] == "command-generation/command-package-ir/v1" assert loaded["packages"][0]["id"] == "todo-fixture" -def test_package_owned_schema_accepts_legacy_aw_schema_version_alias(tmp_path: Path) -> None: +def test_package_owned_schema_accepts_legacy_aw_schema_version_alias( + tmp_path: Path, +) -> None: manifest = _fixture_manifest(tmp_path) manifest["schema_version"] = "agentic-workspace/command-package-ir/v1" manifest_path = tmp_path / "command_package_ir.json" @@ -499,7 +597,9 @@ def test_package_owned_schema_accepts_legacy_aw_schema_version_alias(tmp_path: P assert loaded["packages"][0]["id"] == "todo-fixture" -def test_loaded_legacy_schema_alias_renders_canonical_generation_metadata(tmp_path: Path) -> None: +def test_loaded_legacy_schema_alias_renders_canonical_generation_metadata( + tmp_path: Path, +) -> None: manifest = _fixture_manifest(tmp_path) manifest["schema_version"] = "agentic-workspace/command-package-ir/v1" manifest_path = tmp_path / "command_package_ir.json" @@ -512,12 +612,15 @@ def test_loaded_legacy_schema_alias_renders_canonical_generation_metadata(tmp_pa source_path="command_package_ir.json", regenerate_command="python generate.py", ) - rendered = {output.path.relative_to(tmp_path).as_posix(): output.content for output in outputs} - - assert json.loads(rendered["todo_cli_pkg/command_package.json"])["generation_metadata"]["source_ir"] == { - "schema_version": "command-generation/command-package-ir/v1" + rendered = { + output.path.relative_to(tmp_path).as_posix(): output.content + for output in outputs } + assert json.loads(rendered["todo_cli_pkg/command_package.json"])[ + "generation_metadata" + ]["source_ir"] == {"schema_version": "command-generation/command-package-ir/v1"} + def test_target_extension_schema_copies_match() -> None: repo_root = Path(__file__).resolve().parents[1] @@ -528,7 +631,9 @@ def test_target_extension_schema_copies_match() -> None: def test_public_api_exports_have_compatibility_classification() -> None: classification = command_generation_api.PUBLIC_API_CLASSIFICATION - docs = (Path(__file__).resolve().parents[1] / "docs" / "public-api.md").read_text(encoding="utf-8") + docs = (Path(__file__).resolve().parents[1] / "docs" / "public-api.md").read_text( + encoding="utf-8" + ) assert set(classification) == set(command_generation_api.__all__) assert set(classification.values()) == {"stable"} @@ -545,8 +650,12 @@ def test_public_api_exports_have_compatibility_classification() -> None: def test_stable_public_api_exports_are_audited_with_contracts() -> None: classification = command_generation_api.PUBLIC_API_CLASSIFICATION - docs = (Path(__file__).resolve().parents[1] / "docs" / "public-api.md").read_text(encoding="utf-8") - audit = docs.split("## Stable API Audit", 1)[1].split("## Host Manifest And Primitive Support", 1)[0] + docs = (Path(__file__).resolve().parents[1] / "docs" / "public-api.md").read_text( + encoding="utf-8" + ) + audit = docs.split("## Stable API Audit", 1)[1].split( + "## Host Manifest And Primitive Support", 1 + )[0] assert "Host-facing purpose" in audit assert "Stable contract" in audit @@ -557,7 +666,9 @@ def test_stable_public_api_exports_are_audited_with_contracts() -> None: def test_public_api_audit_captures_post_separation_host_shape() -> None: - docs = (Path(__file__).resolve().parents[1] / "docs" / "public-api.md").read_text(encoding="utf-8") + docs = (Path(__file__).resolve().parents[1] / "docs" / "public-api.md").read_text( + encoding="utf-8" + ) assert "`python_primitive_support_path`" in docs assert "`typescript_primitive_support_path`" in docs @@ -610,12 +721,25 @@ def test_non_aw_fixture_renders_and_runs_python_command(tmp_path: Path) -> None: payload = json.loads(result.stdout) assert payload["item_count"] == 2 assert payload["requested_format"] == "json" - assert "agentic_workspace" not in (tmp_path / "todo_cli_pkg" / "cli.py").read_text(encoding="utf-8") - package_resource = json.loads((tmp_path / "todo_cli_pkg" / "command_package.json").read_text(encoding="utf-8")) + assert "agentic_workspace" not in (tmp_path / "todo_cli_pkg" / "cli.py").read_text( + encoding="utf-8" + ) + package_resource = json.loads( + (tmp_path / "todo_cli_pkg" / "command_package.json").read_text(encoding="utf-8") + ) metadata = package_resource["generation_metadata"] - assert metadata["schema_version"] == "command-generation/generated-artifact-metadata/v1" - assert metadata["generator"] == {"package": "command-generation", "version": package_version("command-generation")} - assert metadata["source_ir"]["schema_version"] == "command-generation/command-package-ir/v1" + assert ( + metadata["schema_version"] + == "command-generation/generated-artifact-metadata/v1" + ) + assert metadata["generator"] == { + "package": "command-generation", + "version": package_version("command-generation"), + } + assert ( + metadata["source_ir"]["schema_version"] + == "command-generation/command-package-ir/v1" + ) assert metadata["target"] == { "kind": "python", "package_name": "todo-fixture", @@ -665,7 +789,9 @@ def test_non_aw_fixture_renders_python_operation_callable(tmp_path: Path) -> Non assert stale == [] sys.path.insert(0, str(tmp_path)) try: - invoke = importlib.import_module("todo_cli_pkg.commands.todo_list_report").invoke + invoke = importlib.import_module( + "todo_cli_pkg.commands.todo_list_report" + ).invoke result = invoke({"format": "json", "output_format": "text"}) finally: @@ -681,7 +807,9 @@ def test_non_aw_fixture_renders_python_operation_callable(tmp_path: Path) -> Non } -def test_non_aw_fixture_python_cli_covers_nested_required_positional_and_append(tmp_path: Path) -> None: +def test_non_aw_fixture_python_cli_covers_nested_required_positional_and_append( + tmp_path: Path, +) -> None: manifest = _fixture_manifest_with_nested_cli_shapes(tmp_path) stale = generate_command_packages( @@ -717,7 +845,9 @@ def test_non_aw_fixture_python_cli_covers_nested_required_positional_and_append( assert payload["tags"] == ["docs", "tests"] -def test_non_aw_fixture_python_cli_validates_required_nested_option(tmp_path: Path) -> None: +def test_non_aw_fixture_python_cli_validates_required_nested_option( + tmp_path: Path, +) -> None: generate_command_packages( _fixture_manifest_with_nested_cli_shapes(tmp_path), repo_root=tmp_path, @@ -747,7 +877,9 @@ def test_non_aw_fixture_python_cli_validates_required_nested_option(tmp_path: Pa assert "--priority" in result.stderr -def test_non_aw_fixture_python_host_owned_primitive_success_path(tmp_path: Path) -> None: +def test_non_aw_fixture_python_host_owned_primitive_success_path( + tmp_path: Path, +) -> None: registry = PrimitiveRegistry.from_definitions( [ { @@ -789,7 +921,9 @@ def test_non_aw_fixture_python_host_owned_primitive_success_path(tmp_path: Path) assert json.loads(result.stdout)["host_marker"] == "decorated-by-python-host" -def test_non_aw_fixture_python_host_owned_primitive_requires_support_module(tmp_path: Path) -> None: +def test_non_aw_fixture_python_host_owned_primitive_requires_support_module( + tmp_path: Path, +) -> None: registry = PrimitiveRegistry.from_definitions( [ { @@ -827,12 +961,17 @@ def test_non_aw_fixture_python_host_owned_primitive_requires_support_module(tmp_ assert "unsupported host primitive: 'todo.decorate'" in result.stderr -def test_non_aw_fixture_accepts_host_primitive_registry_extension(tmp_path: Path) -> None: +def test_non_aw_fixture_accepts_host_primitive_registry_extension( + tmp_path: Path, +) -> None: manifest = _fixture_manifest_with_typescript(tmp_path) package = cast(dict[str, object], cast(list[object], manifest["packages"])[0]) command = cast(dict[str, object], cast(list[object], package["commands"])[0]) runtime_binding = cast(dict[str, object], command["runtime_binding"]) - runtime_binding["primitive_refs"] = [*cast(list[str], runtime_binding["primitive_refs"]), "todo.audit"] + runtime_binding["primitive_refs"] = [ + *cast(list[str], runtime_binding["primitive_refs"]), + "todo.audit", + ] operation_path = tmp_path / "contracts" / "operations" / "todo.list.report.json" operation = json.loads(operation_path.read_text(encoding="utf-8")) steps = cast(list[object], cast(dict[str, object], operation["ir_plan"])["steps"]) @@ -851,7 +990,10 @@ def test_non_aw_fixture_accepts_host_primitive_registry_extension(tmp_path: Path "id": "todo.audit", "kind": "host-owned", "description": "Fixture host-owned audit primitive.", - "target_support": {"python": "host-implemented", "typescript": "host-implemented"}, + "target_support": { + "python": "host-implemented", + "typescript": "host-implemented", + }, "owner": "todo fixture", } ] @@ -864,10 +1006,16 @@ def test_non_aw_fixture_accepts_host_primitive_registry_extension(tmp_path: Path regenerate_command="python generate.py", host_manifest=CommandGenerationHostManifest(primitive_registry=registry), ) - rendered = {output.path.relative_to(tmp_path).as_posix(): output.content for output in outputs} + rendered = { + output.path.relative_to(tmp_path).as_posix(): output.content + for output in outputs + } assert "todo.audit" in rendered["todo_cli_pkg/operations/todo.list.report.json"] - assert "todo.audit" in rendered["todo_ts_pkg/resources/operations/todo.list.report.json"] + assert ( + "todo.audit" + in rendered["todo_ts_pkg/resources/operations/todo.list.report.json"] + ) def test_resource_copies_skip_python_cache_artifacts(tmp_path: Path) -> None: @@ -875,7 +1023,9 @@ def test_resource_copies_skip_python_cache_artifacts(tmp_path: Path) -> None: cache_dir = tmp_path / "payload" / "__pycache__" cache_dir.mkdir() (cache_dir / "todos.cpython-313.pyc").write_bytes(b"\xb1\x00invalid bytecode") - (tmp_path / "payload" / "stale.pyo").write_bytes(b"\xb1\x00invalid optimized bytecode") + (tmp_path / "payload" / "stale.pyo").write_bytes( + b"\xb1\x00invalid optimized bytecode" + ) stale = generate_command_packages( manifest, @@ -891,7 +1041,9 @@ def test_resource_copies_skip_python_cache_artifacts(tmp_path: Path) -> None: assert not (tmp_path / "todo_cli_pkg" / "_payload" / "stale.pyo").exists() -def test_canonical_command_artifacts_expose_implementation_independent_truth(tmp_path: Path) -> None: +def test_canonical_command_artifacts_expose_implementation_independent_truth( + tmp_path: Path, +) -> None: manifest = _fixture_manifest(tmp_path) artifacts = canonical_command_artifacts(manifest) @@ -902,15 +1054,27 @@ def test_canonical_command_artifacts_expose_implementation_independent_truth(tmp assert artifact.program == "todoctl" assert artifact.adapter_id == "todo.list.cli" assert artifact.command_name == "list" - assert artifact.operation_ref == {"id": "todo.list.report", "path": "operations/todo.list.report.json"} - assert artifact.runtime_binding["primitive_refs"] == ["filesystem.read", "json.parse", "payload.assemble", "output.emit"] + assert artifact.operation_ref == { + "id": "todo.list.report", + "path": "operations/todo.list.report.json", + } + assert artifact.runtime_binding["primitive_refs"] == [ + "filesystem.read", + "json.parse", + "payload.assemble", + "output.emit", + ] assert artifact.conformance_refs == ("todo.list.process",) assert artifact.projection_boundary["universal"] == ("command identity",) assert artifact.projection_boundary["target_specific"] == ("parser wiring",) - assert artifact.projection_boundary["runtime_owned"] == ("portable primitive execution",) + assert artifact.projection_boundary["runtime_owned"] == ( + "portable primitive execution", + ) -def test_canonical_command_artifacts_exclude_target_specific_package_fields(tmp_path: Path) -> None: +def test_canonical_command_artifacts_exclude_target_specific_package_fields( + tmp_path: Path, +) -> None: artifact = canonical_command_artifacts(_fixture_manifest(tmp_path))[0] artifact_fields = set(artifact.__dataclass_fields__) @@ -932,8 +1096,13 @@ def test_typescript_command_package_resource_is_target_scoped(tmp_path: Path) -> source_path="command_package_ir.json", regenerate_command="python generate.py", ) - rendered = {output.path.relative_to(tmp_path).as_posix(): output.content for output in outputs} - package_resource = json.loads(rendered["todo_ts_pkg/resources/command_package.json"]) + rendered = { + output.path.relative_to(tmp_path).as_posix(): output.content + for output in outputs + } + package_resource = json.loads( + rendered["todo_ts_pkg/resources/command_package.json"] + ) package_json = json.loads(rendered["todo_ts_pkg/package.json"]) assert package_resource["target_resource_scope"] == { @@ -954,7 +1123,10 @@ def test_typescript_command_package_resource_is_target_scoped(tmp_path: Path) -> metadata = package_resource["generation_metadata"] assert metadata == package_json["agenticWorkspace"]["generationMetadata"] assert metadata["generator"]["version"] == package_version("command-generation") - assert metadata["source_ir"]["schema_version"] == "command-generation/command-package-ir/v1" + assert ( + metadata["source_ir"]["schema_version"] + == "command-generation/command-package-ir/v1" + ) assert metadata["target"] == { "kind": "typescript", "package_name": "todo-fixture-typescript", @@ -962,26 +1134,47 @@ def test_typescript_command_package_resource_is_target_scoped(tmp_path: Path) -> } -def test_generated_target_layout_versions_are_declared_and_placed_in_metadata(tmp_path: Path) -> None: +def test_generated_target_layout_versions_are_declared_and_placed_in_metadata( + tmp_path: Path, +) -> None: outputs = render_outputs( _fixture_manifest_with_typescript(tmp_path), repo_root=tmp_path, source_path="command_package_ir.json", regenerate_command="python generate.py", ) - rendered = {output.path.relative_to(tmp_path).as_posix(): output.content for output in outputs} + rendered = { + output.path.relative_to(tmp_path).as_posix(): output.content + for output in outputs + } python_resource = json.loads(rendered["todo_cli_pkg/command_package.json"]) - typescript_resource = json.loads(rendered["todo_ts_pkg/resources/command_package.json"]) + typescript_resource = json.loads( + rendered["todo_ts_pkg/resources/command_package.json"] + ) typescript_package = json.loads(rendered["todo_ts_pkg/package.json"]) assert PYTHON_TARGET_LAYOUT_VERSION == "command-generation/python-target-layout/v1" - assert TYPESCRIPT_TARGET_LAYOUT_VERSION == "command-generation/typescript-target-layout/v1" - assert python_resource["generation_metadata"]["target"]["layout_version"] == PYTHON_TARGET_LAYOUT_VERSION - assert typescript_resource["generation_metadata"]["target"]["layout_version"] == TYPESCRIPT_TARGET_LAYOUT_VERSION - assert typescript_package["agenticWorkspace"]["generationMetadata"] == typescript_resource["generation_metadata"] + assert ( + TYPESCRIPT_TARGET_LAYOUT_VERSION + == "command-generation/typescript-target-layout/v1" + ) + assert ( + python_resource["generation_metadata"]["target"]["layout_version"] + == PYTHON_TARGET_LAYOUT_VERSION + ) + assert ( + typescript_resource["generation_metadata"]["target"]["layout_version"] + == TYPESCRIPT_TARGET_LAYOUT_VERSION + ) + assert ( + typescript_package["agenticWorkspace"]["generationMetadata"] + == typescript_resource["generation_metadata"] + ) -def test_non_aw_fixture_freshness_reports_python_and_typescript_targets(tmp_path: Path) -> None: +def test_non_aw_fixture_freshness_reports_python_and_typescript_targets( + tmp_path: Path, +) -> None: manifest = _fixture_manifest_with_typescript(tmp_path) generate_command_packages( @@ -1012,7 +1205,9 @@ def family(path: Path) -> str | None: required_target_families=("python", "typescript"), target_family_for_path=family, ) - (tmp_path / "todo_ts_pkg" / "src" / "cli.mjs").write_text("// stale\n", encoding="utf-8") + (tmp_path / "todo_ts_pkg" / "src" / "cli.mjs").write_text( + "// stale\n", encoding="utf-8" + ) stale = generated_output_freshness_report( outputs, repo_root=tmp_path, @@ -1024,10 +1219,14 @@ def family(path: Path) -> str | None: assert set(fresh["rendered_output_count_by_family"]) == {"python", "typescript"} assert fresh["missing_target_families"] == [] assert stale["status"] == "stale-or-incomplete" - assert stale["stale_outputs_by_family"] == {"typescript": ["todo_ts_pkg/src/cli.mjs"]} + assert stale["stale_outputs_by_family"] == { + "typescript": ["todo_ts_pkg/src/cli.mjs"] + } -def test_typescript_cli_append_option_accumulates_repeated_values(tmp_path: Path) -> None: +def test_typescript_cli_append_option_accumulates_repeated_values( + tmp_path: Path, +) -> None: if shutil.which("node") is None: pytest.skip("node is required for TypeScript CLI execution") manifest = _fixture_manifest_with_typescript_append_option(tmp_path) @@ -1077,7 +1276,13 @@ def test_typescript_cli_append_option_defaults_to_empty_list(tmp_path: Path) -> ) result = subprocess.run( - ["node", str(tmp_path / "todo_ts_pkg" / "src" / "cli.mjs"), "list", "--format", "json"], + [ + "node", + str(tmp_path / "todo_ts_pkg" / "src" / "cli.mjs"), + "list", + "--format", + "json", + ], cwd=tmp_path, text=True, capture_output=True, @@ -1131,7 +1336,15 @@ def test_typescript_cli_append_option_validates_choices(tmp_path: Path) -> None: ) result = subprocess.run( - ["node", str(tmp_path / "todo_ts_pkg" / "src" / "cli.mjs"), "list", "--tag", "alpha", "--tag", "gamma"], + [ + "node", + str(tmp_path / "todo_ts_pkg" / "src" / "cli.mjs"), + "list", + "--tag", + "alpha", + "--tag", + "gamma", + ], cwd=tmp_path, text=True, capture_output=True, @@ -1179,7 +1392,13 @@ def test_non_aw_fixture_typescript_payload_view_primitive(tmp_path: Path) -> Non ) result = subprocess.run( - ["node", str(tmp_path / "todo_ts_pkg" / "src" / "cli.mjs"), "list", "--format", "json"], + [ + "node", + str(tmp_path / "todo_ts_pkg" / "src" / "cli.mjs"), + "list", + "--format", + "json", + ], cwd=tmp_path, text=True, capture_output=True, @@ -1193,7 +1412,9 @@ def test_non_aw_fixture_typescript_payload_view_primitive(tmp_path: Path) -> Non assert payload["values"]["items"] == [{"title": "Write test"}] -def test_non_aw_fixture_typescript_select_by_value_preserves_falsy_keys(tmp_path: Path) -> None: +def test_non_aw_fixture_typescript_select_by_value_preserves_falsy_keys( + tmp_path: Path, +) -> None: if shutil.which("node") is None: pytest.skip("node is required for TypeScript CLI execution") @@ -1254,7 +1475,9 @@ def test_non_aw_fixture_typescript_select_by_value_preserves_falsy_keys(tmp_path assert json.loads(result.stdout) == {"selected": "false-key"} -def test_non_aw_fixture_typescript_payload_view_rejects_invalid_limits(tmp_path: Path) -> None: +def test_non_aw_fixture_typescript_payload_view_rejects_invalid_limits( + tmp_path: Path, +) -> None: if shutil.which("node") is None: pytest.skip("node is required for TypeScript CLI execution") @@ -1288,7 +1511,13 @@ def test_non_aw_fixture_typescript_payload_view_rejects_invalid_limits(tmp_path: ) result = subprocess.run( - ["node", str(tmp_path / "todo_ts_pkg" / "src" / "cli.mjs"), "list", "--format", "json"], + [ + "node", + str(tmp_path / "todo_ts_pkg" / "src" / "cli.mjs"), + "list", + "--format", + "json", + ], cwd=tmp_path, text=True, capture_output=True, @@ -1335,7 +1564,13 @@ def test_non_aw_fixture_typescript_transaction_plan_primitive(tmp_path: Path) -> ) result = subprocess.run( - ["node", str(tmp_path / "todo_ts_pkg" / "src" / "cli.mjs"), "list", "--format", "json"], + [ + "node", + str(tmp_path / "todo_ts_pkg" / "src" / "cli.mjs"), + "list", + "--format", + "json", + ], cwd=tmp_path, text=True, capture_output=True, @@ -1345,12 +1580,16 @@ def test_non_aw_fixture_typescript_transaction_plan_primitive(tmp_path: Path) -> assert result.returncode == 0, result.stderr payload = json.loads(result.stdout) assert payload["dry_run"] is True - assert payload["actions"] == [{"action": "create", "kind": "file", "path": "notes/new.md"}] + assert payload["actions"] == [ + {"action": "create", "kind": "file", "path": "notes/new.md"} + ] assert payload["mutation_safety"]["apply_status"] == "package-owned" assert payload["mutation_safety"]["apply_primitive"] == "fixture.transaction.apply" -def test_non_aw_fixture_typescript_transaction_plan_rejects_invalid_resource_path(tmp_path: Path) -> None: +def test_non_aw_fixture_typescript_transaction_plan_rejects_invalid_resource_path( + tmp_path: Path, +) -> None: if shutil.which("node") is None: pytest.skip("node is required for TypeScript CLI execution") @@ -1380,7 +1619,13 @@ def test_non_aw_fixture_typescript_transaction_plan_rejects_invalid_resource_pat ) result = subprocess.run( - ["node", str(tmp_path / "todo_ts_pkg" / "src" / "cli.mjs"), "list", "--format", "json"], + [ + "node", + str(tmp_path / "todo_ts_pkg" / "src" / "cli.mjs"), + "list", + "--format", + "json", + ], cwd=tmp_path, text=True, capture_output=True, @@ -1391,7 +1636,9 @@ def test_non_aw_fixture_typescript_transaction_plan_rejects_invalid_resource_pat assert "transaction.plan resource path must be relative" in result.stderr -def test_non_aw_fixture_typescript_host_owned_primitive_success_path(tmp_path: Path) -> None: +def test_non_aw_fixture_typescript_host_owned_primitive_success_path( + tmp_path: Path, +) -> None: if shutil.which("node") is None: pytest.skip("node is required for TypeScript host-owned primitive execution") registry = PrimitiveRegistry.from_definitions( @@ -1441,7 +1688,9 @@ def test_non_aw_fixture_typescript_host_owned_primitive_success_path(tmp_path: P assert json.loads(result.stdout)["host_marker"] == "decorated-by-ts-host" -def test_non_aw_fixture_typescript_host_owned_primitive_requires_target_support(tmp_path: Path) -> None: +def test_non_aw_fixture_typescript_host_owned_primitive_requires_target_support( + tmp_path: Path, +) -> None: registry = PrimitiveRegistry.from_definitions( [ { @@ -1449,13 +1698,17 @@ def test_non_aw_fixture_typescript_host_owned_primitive_requires_target_support( "kind": "host-owned", "description": "Fixture host-owned TypeScript result decorator.", "target_support": {"typescript": "unsupported"}, - "unsupported_targets": {"typescript": "fixture host primitive is intentionally missing"}, + "unsupported_targets": { + "typescript": "fixture host primitive is intentionally missing" + }, "owner": "todo fixture", } ] ) - with pytest.raises(ValueError, match="fixture host primitive is intentionally missing"): + with pytest.raises( + ValueError, match="fixture host primitive is intentionally missing" + ): render_outputs( _fixture_manifest_with_host_owned_typescript_primitive(tmp_path), repo_root=tmp_path, @@ -1465,7 +1718,9 @@ def test_non_aw_fixture_typescript_host_owned_primitive_requires_target_support( ) -def test_non_aw_fixture_typescript_cli_covers_nested_required_positional_and_append(tmp_path: Path) -> None: +def test_non_aw_fixture_typescript_cli_covers_nested_required_positional_and_append( + tmp_path: Path, +) -> None: if shutil.which("node") is None: pytest.skip("node is required for TypeScript CLI execution") @@ -1506,7 +1761,9 @@ def test_non_aw_fixture_typescript_cli_covers_nested_required_positional_and_app assert payload["tags"] == ["docs", "tests"] -def test_typescript_generated_test_uses_valid_required_subcommand_sample_invocations(tmp_path: Path) -> None: +def test_typescript_generated_test_uses_valid_required_subcommand_sample_invocations( + tmp_path: Path, +) -> None: if shutil.which("node") is None: pytest.skip("node is required for generated TypeScript test execution") @@ -1518,7 +1775,9 @@ def test_typescript_generated_test_uses_valid_required_subcommand_sample_invocat check=False, ) - test_source = (tmp_path / "todo_ts_pkg" / "test" / "command-package.test.mjs").read_text(encoding="utf-8") + test_source = ( + tmp_path / "todo_ts_pkg" / "test" / "command-package.test.mjs" + ).read_text(encoding="utf-8") result = subprocess.run( ["node", "--test", "test/command-package.test.mjs"], cwd=tmp_path / "todo_ts_pkg", @@ -1527,14 +1786,24 @@ def test_typescript_generated_test_uses_valid_required_subcommand_sample_invocat check=False, ) - assert '["list", "project", "alpha", "--priority", "high", "--format", "json"]' in test_source - assert '["list", "project", "__SPACED_TARGET__", "--priority", "high"]' in test_source - assert "generated runnable adapter rejects command without required subcommand" in test_source + assert ( + '["list", "project", "alpha", "--priority", "high", "--format", "json"]' + in test_source + ) + assert ( + '["list", "project", "__SPACED_TARGET__", "--priority", "high"]' in test_source + ) + assert ( + "generated runnable adapter rejects command without required subcommand" + in test_source + ) assert "missing subcommand for list" in test_source assert result.returncode == 0, result.stderr -def test_typescript_generated_test_samples_required_store_true_and_integer_specs(tmp_path: Path) -> None: +def test_typescript_generated_test_samples_required_store_true_and_integer_specs( + tmp_path: Path, +) -> None: if shutil.which("node") is None: pytest.skip("node is required for generated TypeScript test execution") @@ -1546,7 +1815,9 @@ def test_typescript_generated_test_samples_required_store_true_and_integer_specs check=False, ) - test_source = (tmp_path / "todo_ts_pkg" / "test" / "command-package.test.mjs").read_text(encoding="utf-8") + test_source = ( + tmp_path / "todo_ts_pkg" / "test" / "command-package.test.mjs" + ).read_text(encoding="utf-8") result = subprocess.run( ["node", "--test", "test/command-package.test.mjs"], cwd=tmp_path / "todo_ts_pkg", @@ -1555,8 +1826,11 @@ def test_typescript_generated_test_samples_required_store_true_and_integer_specs check=False, ) - assert '["count", "1", "--confirmed", "--limit", "1", "--format", "json"]' in test_source - assert "--confirmed\", \"value" not in test_source + assert ( + '["count", "1", "--confirmed", "--limit", "1", "--format", "json"]' + in test_source + ) + assert '--confirmed", "value' not in test_source assert result.returncode == 0, result.stderr @@ -1596,7 +1870,9 @@ def test_typescript_cli_coerces_integer_positionals_and_options(tmp_path: Path) assert payload["limit"] == 3 -def test_non_aw_fixture_typescript_cli_validates_required_nested_option(tmp_path: Path) -> None: +def test_non_aw_fixture_typescript_cli_validates_required_nested_option( + tmp_path: Path, +) -> None: if shutil.which("node") is None: pytest.skip("node is required for TypeScript CLI execution") @@ -1608,7 +1884,13 @@ def test_non_aw_fixture_typescript_cli_validates_required_nested_option(tmp_path check=False, ) result = subprocess.run( - ["node", str(tmp_path / "todo_ts_pkg" / "src" / "cli.mjs"), "list", "project", "alpha"], + [ + "node", + str(tmp_path / "todo_ts_pkg" / "src" / "cli.mjs"), + "list", + "project", + "alpha", + ], cwd=tmp_path, text=True, capture_output=True, @@ -1625,7 +1907,9 @@ def test_generated_targets_include_operation_fragment_support(tmp_path: Path) -> operation = json.loads(operation_path.read_text(encoding="utf-8")) read_step = operation["ir_plan"]["steps"].pop(0) operation["ir_plan"]["fragments"] = [{"id": "load-todos", "steps": [read_step]}] - operation["ir_plan"]["steps"].insert(0, {"id": "load", "uses_fragment": "load-todos"}) + operation["ir_plan"]["steps"].insert( + 0, {"id": "load", "uses_fragment": "load-todos"} + ) operation_path.write_text(json.dumps(operation, indent=2), encoding="utf-8") outputs = render_outputs( @@ -1634,14 +1918,30 @@ def test_generated_targets_include_operation_fragment_support(tmp_path: Path) -> source_path="command_package_ir.json", regenerate_command="python generate.py", ) - rendered = {output.path.relative_to(tmp_path).as_posix(): output.content for output in outputs} + rendered = { + output.path.relative_to(tmp_path).as_posix(): output.content + for output in outputs + } - assert json.loads(rendered["todo_cli_pkg/operations/todo.list.report.json"])["ir_plan"]["fragments"][0]["id"] == "load-todos" - assert "expand_operation_steps" in rendered["todo_cli_pkg/primitives/primitive_executor.py"] - assert "def expand_operation_steps" in rendered["todo_cli_pkg/primitives/operation_composition.py"] + assert ( + json.loads(rendered["todo_cli_pkg/operations/todo.list.report.json"])[ + "ir_plan" + ]["fragments"][0]["id"] + == "load-todos" + ) + assert ( + "expand_operation_steps" + in rendered["todo_cli_pkg/primitives/primitive_executor.py"] + ) + assert ( + "def expand_operation_steps" + in rendered["todo_cli_pkg/primitives/operation_composition.py"] + ) -def test_python_host_primitive_support_keeps_generated_executor_skeleton(tmp_path: Path) -> None: +def test_python_host_primitive_support_keeps_generated_executor_skeleton( + tmp_path: Path, +) -> None: manifest = _fixture_manifest(tmp_path) support_path = tmp_path / "contracts" / "python_host_primitive_support.py" support_path.write_text( @@ -1662,12 +1962,21 @@ def test_python_host_primitive_support_keeps_generated_executor_skeleton(tmp_pat "generated_root": "generated", }, ) - rendered = {output.path.relative_to(tmp_path).as_posix(): output.content for output in outputs} + rendered = { + output.path.relative_to(tmp_path).as_posix(): output.content + for output in outputs + } primitive_executor = rendered["todo_cli_pkg/primitives/primitive_executor.py"] support = rendered["todo_cli_pkg/primitives/host_primitive_support.py"] - assert "Host primitive support: contracts/python_host_primitive_support.py" in primitive_executor - assert "Portable primitive dispatch and executor structure belong to command-generation." in primitive_executor + assert ( + "Host primitive support: contracts/python_host_primitive_support.py" + in primitive_executor + ) + assert ( + "Portable primitive dispatch and executor structure belong to command-generation." + in primitive_executor + ) assert "def execute_primitive(" in primitive_executor assert "execute_primitive = " not in primitive_executor assert "HOST_SENTINEL = 'host-owned-primitive-support'" in support @@ -1675,8 +1984,12 @@ def test_python_host_primitive_support_keeps_generated_executor_skeleton(tmp_pat def test_typescript_host_primitive_support_keeps_generated_runtime_shell() -> None: root = Path(__file__).resolve().parents[1] - manifest_source = (root / "src" / "command_generation" / "host_manifest.py").read_text(encoding="utf-8") - renderer_source = (root / "src" / "command_generation" / "targets" / "typescript.py").read_text(encoding="utf-8") + manifest_source = ( + root / "src" / "command_generation" / "host_manifest.py" + ).read_text(encoding="utf-8") + renderer_source = ( + root / "src" / "command_generation" / "targets" / "typescript.py" + ).read_text(encoding="utf-8") assert "typescript_runtime_support_path" not in manifest_source assert "typescript_runtime_support_path" not in renderer_source @@ -1684,7 +1997,9 @@ def test_typescript_host_primitive_support_keeps_generated_runtime_shell() -> No assert "function executePrimitive(" in renderer_source -def test_generated_local_runtime_facade_documents_and_preserves_patch_semantics() -> None: +def test_generated_local_runtime_facade_documents_and_preserves_patch_semantics() -> ( + None +): source_module = types.ModuleType("fake_source_runtime_for_facade") def first_value() -> str: @@ -1730,8 +2045,13 @@ def second_value() -> str: assert cast(Callable[[], str], facade_globals["runtime_value"])() == "second" facade_globals["runtime_value"] = lambda: "facade-only" - assert cast(Callable[[], str], getattr(source_module, "runtime_value"))() == "second" - assert cast(Callable[[], str], facade_globals["runtime_value"])() == "facade-only" + assert ( + cast(Callable[[], str], getattr(source_module, "runtime_value"))() + == "second" + ) + assert ( + cast(Callable[[], str], facade_globals["runtime_value"])() == "facade-only" + ) finally: sys.modules.pop(source_module.__name__, None) @@ -1771,12 +2091,19 @@ def test_generated_json_output_fallback_delegates_declared_text_views() -> None: assert "print(_emit_output(values=values, arguments=arguments), end='')" in rendered -def test_generated_output_emit_text_views_execute_in_python_and_typescript(tmp_path: Path) -> None: +def test_generated_output_emit_text_views_execute_in_python_and_typescript( + tmp_path: Path, +) -> None: if shutil.which("node") is None: pytest.skip("node is required for TypeScript generated-runtime conformance") manifest = _fixture_manifest_with_typescript(tmp_path) package = cast(dict[str, object], cast(list[object], manifest["packages"])[0]) - operation_executor = cast(dict[str, object], cast(dict[str, object], package["python_runtime_binding"])["operation_executor"]) + operation_executor = cast( + dict[str, object], + cast(dict[str, object], package["python_runtime_binding"])[ + "operation_executor" + ], + ) cast(list[object], operation_executor["initial_values"]).extend( [ {"name": "profile", "arg": "profile", "default": "compact"}, @@ -1842,18 +2169,45 @@ def test_generated_output_emit_text_views_execute_in_python_and_typescript(tmp_p "Items: {items|join:, |empty:(none)}", "Missing: {missing|join:, |empty:(none)}", "Warnings count: {warnings|len}", - {"when": "warnings", "lines": ["Warnings:", {"for_each": {"path": "warnings", "template": "- {}"}}]}, - {"when": "metadata", "lines": ["Metadata:", {"json": "metadata"}]}, - {"when": "empty_object", "lines": ["Empty object should not render"]}, + { + "when": "warnings", + "lines": [ + "Warnings:", + { + "for_each": { + "path": "warnings", + "template": "- {}", + } + }, + ], + }, + { + "when": "metadata", + "lines": ["Metadata:", {"json": "metadata"}], + }, + { + "when": "empty_object", + "lines": ["Empty object should not render"], + }, { "for_each": { "path": "records", - "lines": ["Record: {name} ({status})", "Root profile: {root.profile}"], + "lines": [ + "Record: {name} ({status})", + "Root profile: {root.profile}", + ], } }, ], }, - {"id": "fixture.default", "default": True, "lines": ["Default profile: {profile}", "Items: {items|join:, |empty:(none)}"]}, + { + "id": "fixture.default", + "default": True, + "lines": [ + "Default profile: {profile}", + "Items: {items|join:, |empty:(none)}", + ], + }, ] }, "outputs": ["result"], @@ -1875,7 +2229,9 @@ def test_generated_output_emit_text_views_execute_in_python_and_typescript(tmp_p sys.path.insert(0, str(tmp_path)) try: py_cli = importlib.import_module("todo_cli_pkg.cli") - py_executor = importlib.import_module("todo_cli_pkg.primitives.operation_executor") + py_executor = importlib.import_module( + "todo_cli_pkg.primitives.operation_executor" + ) py_contract = py_cli.generated_operation_contract("todo.list.report") values = { "format": "text", @@ -1884,7 +2240,13 @@ def test_generated_output_emit_text_views_execute_in_python_and_typescript(tmp_p "items": ["alpha", "beta"], "records": [{"name": "one", "status": "ready"}], "warnings": ["check config"], - "metadata": {"source": "fixture", "city": "Malm\u00f6", "10": "a", "2": "b", "nested": {"10": "inner-a", "2": "inner-b"}}, + "metadata": { + "source": "fixture", + "city": "Malm\u00f6", + "10": "a", + "2": "b", + "nested": {"10": "inner-a", "2": "inner-b"}, + }, "empty_object": {}, "missing": [], "active": True, @@ -1908,7 +2270,14 @@ def test_generated_output_emit_text_views_execute_in_python_and_typescript(tmp_p "process.stdout.write(JSON.stringify(result));\n", encoding="utf-8", ) - ts_result = subprocess.run(["node", str(runner)], cwd=tmp_path, text=True, encoding="utf-8", capture_output=True, check=False) + ts_result = subprocess.run( + ["node", str(runner)], + cwd=tmp_path, + text=True, + encoding="utf-8", + capture_output=True, + check=False, + ) assert ts_result.returncode == 0, ts_result.stderr ts_text = json.loads(ts_result.stdout) @@ -1943,8 +2312,12 @@ def test_generated_output_emit_text_views_execute_in_python_and_typescript(tmp_p sys.path.insert(0, str(tmp_path)) try: py_cli = importlib.import_module("todo_cli_pkg.cli") - py_executor = importlib.import_module("todo_cli_pkg.primitives.operation_executor") - py_default = py_executor.run_operation_callable(py_cli.generated_operation_contract("todo.list.report"), default_values) + py_executor = importlib.import_module( + "todo_cli_pkg.primitives.operation_executor" + ) + py_default = py_executor.run_operation_callable( + py_cli.generated_operation_contract("todo.list.report"), default_values + ) finally: sys.path.remove(str(tmp_path)) for name in list(sys.modules): @@ -1957,27 +2330,55 @@ def test_generated_output_emit_text_views_execute_in_python_and_typescript(tmp_p "process.stdout.write(JSON.stringify(result));\n", encoding="utf-8", ) - ts_default = subprocess.run(["node", str(runner)], cwd=tmp_path, text=True, encoding="utf-8", capture_output=True, check=False) + ts_default = subprocess.run( + ["node", str(runner)], + cwd=tmp_path, + text=True, + encoding="utf-8", + capture_output=True, + check=False, + ) assert ts_default.returncode == 0, ts_default.stderr - assert py_default == json.loads(ts_default.stdout) == "Default profile: expanded\nItems: (none)\n" + assert ( + py_default + == json.loads(ts_default.stdout) + == "Default profile: expanded\nItems: (none)\n" + ) def write_generated_emit_arguments(arguments: dict[str, object]) -> None: for generated_operation in ( tmp_path / "todo_cli_pkg" / "operations" / "todo.list.report.json", - tmp_path / "todo_ts_pkg" / "resources" / "operations" / "todo.list.report.json", + tmp_path + / "todo_ts_pkg" + / "resources" + / "operations" + / "todo.list.report.json", ): malformed = json.loads(generated_operation.read_text(encoding="utf-8")) - cast(dict[str, object], cast(list[object], cast(dict[str, object], malformed["ir_plan"])["steps"])[1])["arguments"] = arguments - generated_operation.write_text(json.dumps(malformed, indent=2), encoding="utf-8") + cast( + dict[str, object], + cast( + list[object], cast(dict[str, object], malformed["ir_plan"])["steps"] + )[1], + )["arguments"] = arguments + generated_operation.write_text( + json.dumps(malformed, indent=2), encoding="utf-8" + ) - def assert_generated_text_view_error(arguments: dict[str, object], expected_message: str) -> None: + def assert_generated_text_view_error( + arguments: dict[str, object], expected_message: str + ) -> None: write_generated_emit_arguments(arguments) sys.path.insert(0, str(tmp_path)) try: py_cli = importlib.import_module("todo_cli_pkg.cli") - py_executor = importlib.import_module("todo_cli_pkg.primitives.operation_executor") + py_executor = importlib.import_module( + "todo_cli_pkg.primitives.operation_executor" + ) with pytest.raises(Exception, match=expected_message): - py_executor.run_operation_callable(py_cli.generated_operation_contract("todo.list.report"), values) + py_executor.run_operation_callable( + py_cli.generated_operation_contract("todo.list.report"), values + ) finally: sys.path.remove(str(tmp_path)) for name in list(sys.modules): @@ -1990,29 +2391,58 @@ def assert_generated_text_view_error(arguments: dict[str, object], expected_mess "process.stdout.write(JSON.stringify(result));\n", encoding="utf-8", ) - malformed_ts = subprocess.run(["node", str(runner)], cwd=tmp_path, text=True, encoding="utf-8", capture_output=True, check=False) + malformed_ts = subprocess.run( + ["node", str(runner)], + cwd=tmp_path, + text=True, + encoding="utf-8", + capture_output=True, + check=False, + ) assert malformed_ts.returncode != 0 assert expected_message in malformed_ts.stderr - assert_generated_text_view_error({"text_views": "not-a-list"}, "output.emit text_views must be a list") assert_generated_text_view_error( - {"text_views": [{"id": "bad.match", "match": {"items": ["alpha"]}, "lines": ["Bad"]}]}, + {"text_views": "not-a-list"}, "output.emit text_views must be a list" + ) + assert_generated_text_view_error( + { + "text_views": [ + {"id": "bad.match", "match": {"items": ["alpha"]}, "lines": ["Bad"]} + ] + }, "output.emit text view match values must be JSON scalars", ) assert_generated_text_view_error( { "text_views": [ - {"id": "bad.placeholder", "match": {"kind": "todo-list/v1"}, "lines": ["Metadata: {metadata}"]} + { + "id": "bad.placeholder", + "match": {"kind": "todo-list/v1"}, + "lines": ["Metadata: {metadata}"], + } ] }, "output.emit text view placeholders require JSON scalars", ) assert_generated_text_view_error( - {"text_views": [{"id": "bad.join", "match": {"kind": "todo-list/v1"}, "lines": ["Records: {records|join:, }"]}]}, + { + "text_views": [ + { + "id": "bad.join", + "match": {"kind": "todo-list/v1"}, + "lines": ["Records: {records|join:, }"], + } + ] + }, "output.emit join filter requires a list of JSON scalars", ) assert_generated_text_view_error( - {"text_views": [{"id": "bad.number", "match": {"score": 1e-7}, "lines": ["Bad"]}]}, + { + "text_views": [ + {"id": "bad.number", "match": {"score": 1e-7}, "lines": ["Bad"]} + ] + }, "output.emit text view match values must be JSON scalars", ) assert_generated_text_view_error( @@ -2028,26 +2458,55 @@ def assert_generated_text_view_error(arguments: dict[str, object], expected_mess }, "output.emit text view has unsupported fields", ) + assert_generated_text_view_error( {"text_views": [{"id": "bad.default-type", "default": [], "lines": ["Bad"]}]}, "output.emit text view default must be a boolean", ) assert_generated_text_view_error( - {"text_views": [{"id": "bad.literal-type", "match": {"kind": "todo-list/v1"}, "lines": [{"literal": {"a": 1}}]}]}, + { + "text_views": [ + { + "id": "bad.literal-type", + "match": {"kind": "todo-list/v1"}, + "lines": [{"literal": {"a": 1}}], + } + ] + }, "output.emit literal line value must be a string", ) assert_generated_text_view_error( - {"text_views": [{"id": "bad.template-type", "match": {"kind": "todo-list/v1"}, "lines": [{"template": 42}]}]}, + { + "text_views": [ + { + "id": "bad.template-type", + "match": {"kind": "todo-list/v1"}, + "lines": [{"template": 42}], + } + ] + }, "output.emit template line value must be a string", ) assert_generated_text_view_error( - {"text_views": [{"id": "bad.json-path-type", "match": {"kind": "todo-list/v1"}, "lines": [{"json": ["metadata"]}]}]}, + { + "text_views": [ + { + "id": "bad.json-path-type", + "match": {"kind": "todo-list/v1"}, + "lines": [{"json": ["metadata"]}], + } + ] + }, "output.emit json line path must be a string", ) assert_generated_text_view_error( { "text_views": [ - {"id": "bad.when-path-type", "match": {"kind": "todo-list/v1"}, "lines": [{"when": ["warnings"], "lines": ["Bad"]}]} + { + "id": "bad.when-path-type", + "match": {"kind": "todo-list/v1"}, + "lines": [{"when": ["warnings"], "lines": ["Bad"]}], + } ] }, "output.emit when line path must be a string", @@ -2055,7 +2514,11 @@ def assert_generated_text_view_error(arguments: dict[str, object], expected_mess assert_generated_text_view_error( { "text_views": [ - {"id": "bad.for-each-path-type", "match": {"kind": "todo-list/v1"}, "lines": [{"for_each": {"path": ["warnings"], "template": "- {}"}}]} + { + "id": "bad.for-each-path-type", + "match": {"kind": "todo-list/v1"}, + "lines": [{"for_each": {"path": ["warnings"], "template": "- {}"}}], + } ] }, "output.emit for_each path must be a string", @@ -2063,7 +2526,13 @@ def assert_generated_text_view_error(arguments: dict[str, object], expected_mess assert_generated_text_view_error( { "text_views": [ - {"id": "bad.for-each-template-type", "match": {"kind": "todo-list/v1"}, "lines": [{"for_each": {"path": "warnings", "template": {"line": "- {}"}}}]} + { + "id": "bad.for-each-template-type", + "match": {"kind": "todo-list/v1"}, + "lines": [ + {"for_each": {"path": "warnings", "template": {"line": "- {}"}}} + ], + } ] }, "output.emit for_each template must be a string", @@ -2120,8 +2589,13 @@ def assert_generated_text_view_error(arguments: dict[str, object], expected_mess sys.path.insert(0, str(tmp_path)) try: py_cli = importlib.import_module("todo_cli_pkg.cli") - py_executor = importlib.import_module("todo_cli_pkg.primitives.operation_executor") - with pytest.raises(Exception, match="output.emit text view JSON numbers must be finite safe integers"): + py_executor = importlib.import_module( + "todo_cli_pkg.primitives.operation_executor" + ) + with pytest.raises( + Exception, + match="output.emit text view JSON numbers must be finite safe integers", + ): py_executor.run_operation_callable( py_cli.generated_operation_contract("todo.list.report"), bad_json_number_values, @@ -2138,12 +2612,279 @@ def assert_generated_text_view_error(arguments: dict[str, object], expected_mess "process.stdout.write(JSON.stringify(result));\n", encoding="utf-8", ) - bad_json_number_ts = subprocess.run(["node", str(runner)], cwd=tmp_path, text=True, encoding="utf-8", capture_output=True, check=False) + bad_json_number_ts = subprocess.run( + ["node", str(runner)], + cwd=tmp_path, + text=True, + encoding="utf-8", + capture_output=True, + check=False, + ) assert bad_json_number_ts.returncode != 0 - assert "output.emit text view JSON numbers must be finite safe integers" in bad_json_number_ts.stderr + assert ( + "output.emit text view JSON numbers must be finite safe integers" + in bad_json_number_ts.stderr + ) + + +def test_generated_payload_project_contract_matches_interpreter_in_python_and_typescript( + tmp_path: Path, +) -> None: + if shutil.which("node") is None: + pytest.skip("node is required for TypeScript generated-runtime conformance") + manifest = _fixture_manifest_with_typescript(tmp_path) + package = cast(dict[str, object], cast(list[object], manifest["packages"])[0]) + command = cast(dict[str, object], cast(list[object], package["commands"])[0]) + runtime_binding = cast(dict[str, object], command["runtime_binding"]) + runtime_binding["primitive_refs"] = [ + *cast(list[str], runtime_binding["primitive_refs"]), + "payload.project", + ] + operation_executor = cast( + dict[str, object], + cast(dict[str, object], package["python_runtime_binding"])[ + "operation_executor" + ], + ) + cast(list[object], operation_executor["initial_values"]).append( + {"name": "select", "arg": "select", "default": None} + ) + near_budget_key = "n" * 94 + bmp_boundary_key = "\ue000" + non_bmp_boundary_key = "\U00010000" + payload = { + **{ + str(index): ( + {bmp_boundary_key: "bmp", non_bmp_boundary_key: "non-bmp"} + if index == 3 + else {near_budget_key: index} + ) + for index in range(4, -1, -1) + }, + "kind": "fixture/payload/v1", + "summary": {"count": 2}, + "items": [{"name": "alpha"}, {"name": "beta"}], + "wide": {f"field{i}": i for i in range(80)}, + } + source_command = "s" * 128 + inventory_command = "i" * 128 + detail_command = "d" * 128 + project_arguments = { + "source": "result", + "source_command": source_command, + "selected_output_kind": "fixture/custom/selected-output/v1", + "selector_inventory_command": inventory_command, + "selector_detail_command": detail_command, + } + operation_path = tmp_path / "contracts" / "operations" / "todo.list.report.json" + operation = json.loads(operation_path.read_text(encoding="utf-8")) + cast(dict[str, object], operation["ir_plan"])["steps"] = [ + { + "id": "assemble", + "uses": "payload.assemble", + "arguments": {"fields": {"template": payload}}, + "outputs": ["result"], + }, + { + "id": "project", + "uses": "payload.project", + "arguments": project_arguments, + "outputs": ["result"], + }, + ] + operation_path.write_text(json.dumps(operation, indent=2), encoding="utf-8") + + assert ( + generate_command_packages( + manifest, + repo_root=tmp_path, + source_path="command_package_ir.json", + regenerate_command="python generate.py", + check=False, + ) + == [] + ) + + def interpreter_result(select: str) -> dict[str, object]: + return execute_primitive( + "payload.project", + values={ + "operation_id": "todo.list.report", + "select": select, + "result": payload, + }, + arguments=project_arguments, + context=PrimitiveContext(cwd=tmp_path), + ) + + def python_result(select: str) -> object: + sys.path.insert(0, str(tmp_path)) + try: + py_cli = importlib.import_module("todo_cli_pkg.cli") + py_executor = importlib.import_module( + "todo_cli_pkg.primitives.operation_executor" + ) + return py_executor.run_operation_callable( + py_cli.generated_operation_contract("todo.list.report"), + {"select": select}, + ) + finally: + sys.path.remove(str(tmp_path)) + for name in list(sys.modules): + if name == "todo_cli_pkg" or name.startswith("todo_cli_pkg."): + sys.modules.pop(name, None) + runner = tmp_path / "invoke-payload-project.mjs" + + def typescript_result(select: str) -> object: + runner.write_text( + "import { invokeGeneratedOperation } from './todo_ts_pkg/src/runtime.mjs';\n" + f"const values = {{ select: {json.dumps(select)} }};\n" + "const result = invokeGeneratedOperation({ operationId: 'todo.list.report', operationPath: 'operations/todo.list.report.json', values });\n" + "process.stdout.write(JSON.stringify(result));\n", + encoding="utf-8", + ) + result = subprocess.run( + ["node", str(runner)], + cwd=tmp_path, + text=True, + encoding="utf-8", + capture_output=True, + check=False, + ) + assert result.returncode == 0, result.stderr + return json.loads(result.stdout) -def test_generated_module_front_door_handler_delegates_with_data_driven_argv_and_help() -> None: + def compact_json_utf8_size(value: object) -> int: + return len( + json.dumps(value, ensure_ascii=False, separators=(",", ":")).encode("utf-8") + ) + + selectors = ["summary.count", "items.0.name", *[f"missing{i}" for i in range(30)]] + invalid_selector_result = interpreter_result(",".join(selectors)) + assert ( + python_result(",".join(selectors)) + == typescript_result(",".join(selectors)) + == invalid_selector_result + ) + assert ( + invalid_selector_result["kind"] == "fixture/custom/selector-validation-error/v1" + ) + assert invalid_selector_result["status"] == "invalid-selector" + assert invalid_selector_result["requested_selectors"] == selectors + assert invalid_selector_result["unknown_selectors"] == selectors[2:] + assert set(cast(dict[str, object], invalid_selector_result["suggestions"])) == set( + selectors[2:] + ) + assert all( + len(suggestions) <= 1 + for suggestions in cast( + dict[str, list[str]], invalid_selector_result["suggestions"] + ).values() + ) + inventory = cast(dict[str, object], invalid_selector_result["selector_inventory"]) + assert invalid_selector_result["source_command"] == source_command + assert inventory["discovery_command"] == inventory_command + assert inventory["inventory_command"] == detail_command + assert len(cast(list[str], inventory["sample"])) <= cast( + int, inventory["sample_limit"] + ) + assert inventory["sample"] == [ + "0", + f"0.{near_budget_key}", + "1", + f"1.{near_budget_key}", + "2", + f"2.{near_budget_key}", + "3", + f"3.{bmp_boundary_key}", + ] + assert "values" not in invalid_selector_result + assert compact_json_utf8_size(invalid_selector_result) < 6000 + + worst_selectors = [f"{'m' * 14}{index:02d}" for index in range(32)] + worst_result = interpreter_result(",".join(worst_selectors)) + assert ( + python_result(",".join(worst_selectors)) + == typescript_result(",".join(worst_selectors)) + == worst_result + ) + assert worst_result["status"] == "invalid-selector" + assert worst_result["requested_selectors"] == worst_selectors + assert worst_result["unknown_selectors"] == worst_selectors + assert compact_json_utf8_size(worst_result) < 6000 + + too_many_selectors = ",".join(f"wide.field{i}" for i in range(33)) + too_many_result = interpreter_result(too_many_selectors) + assert ( + python_result(too_many_selectors) + == typescript_result(too_many_selectors) + == too_many_result + ) + assert too_many_result["status"] == "invalid-selector-request" + assert len(cast(list[str], too_many_result["requested_selectors"])) == 32 + assert ( + cast(dict[str, object], too_many_result["selector_request"])["reason"] + == "too-many-selectors" + ) + assert "values" not in too_many_result + + too_large_selectors = ",".join( + [f"{'s' * 14}{index:02d}" for index in range(31)] + [f"{'s' * 15}31"] + ) + too_large_result = interpreter_result(too_large_selectors) + assert ( + python_result(too_large_selectors) + == typescript_result(too_large_selectors) + == too_large_result + ) + assert too_large_result["status"] == "invalid-selector-request" + too_large_request = cast(dict[str, object], too_large_result["selector_request"]) + assert too_large_request["reason"] == "selector-request-too-large" + assert too_large_request["selector_request_bytes"] == 513 + assert compact_json_utf8_size(too_large_result) < 6000 + + overlong_result = interpreter_result("x" * 257) + assert python_result("x" * 257) == typescript_result("x" * 257) == overlong_result + assert overlong_result["status"] == "invalid-selector-request" + assert overlong_result["requested_selectors"] == [] + assert ( + cast(dict[str, object], overlong_result["selector_request"])["reason"] + == "selector-too-long" + ) + assert compact_json_utf8_size(overlong_result) < 6000 + + astral = "\U0001f600" + accepted_astral = astral * 64 + accepted_astral_result = interpreter_result(accepted_astral) + assert ( + python_result(accepted_astral) + == typescript_result(accepted_astral) + == accepted_astral_result + ) + assert accepted_astral_result["status"] == "invalid-selector" + assert accepted_astral_result["requested_selectors"] == [accepted_astral] + assert compact_json_utf8_size(accepted_astral_result) < 6000 + + rejected_astral = astral * 65 + rejected_astral_result = interpreter_result(rejected_astral) + assert ( + python_result(rejected_astral) + == typescript_result(rejected_astral) + == rejected_astral_result + ) + assert rejected_astral_result["status"] == "invalid-selector-request" + rejected_astral_request = cast( + dict[str, object], rejected_astral_result["selector_request"] + ) + assert rejected_astral_request["reason"] == "selector-too-long" + assert rejected_astral_request["selector_bytes"] == 260 + assert compact_json_utf8_size(rejected_astral_result) < 6000 + + +def test_generated_module_front_door_handler_delegates_with_data_driven_argv_and_help() -> ( + None +): runtime_module = types.ModuleType("fake_module_front_door_runtime") calls: list[list[str]] = [] @@ -2186,14 +2927,25 @@ def print_help(payload: dict[str, object]) -> None: "help_payload_function": "help_payload", "help_text_function": "print_help", "missing_module_message": "demo module is required", - "stdout_replacements": [{"old": "demo-module ", "new": "demo-cli demo "}], + "stdout_replacements": [ + {"old": "demo-module ", "new": "demo-cli demo "} + ], "positionals": [{"commands": ["route"], "attr": "route_id"}], "option_specs": [ {"option": "--target", "attr": "target"}, {"option": "--verbose", "attr": "verbose", "kind": "flag"}, {"option": "--tag", "attr": "tags", "kind": "repeated"}, - {"option": "--group", "attr": "groups", "kind": "repeated_group"}, - {"option": "--path", "attr": "paths", "fallback_attr": "path", "kind": "repeated"}, + { + "option": "--group", + "attr": "groups", + "kind": "repeated_group", + }, + { + "option": "--path", + "attr": "paths", + "fallback_attr": "path", + "kind": "repeated", + }, ], } ] @@ -2210,15 +2962,29 @@ def error(self, message: str) -> None: generated_package = types.ModuleType("generated_demo") setattr(generated_package, "build_generated_parser", lambda: Parser()) setattr(generated_package, "generated_command_names", lambda: ["demo"]) - setattr(generated_package, "generated_operation_contract", lambda operation_id: {"id": operation_id}) - setattr(generated_package, "run_generated_command", lambda argv, handler: handler("demo.front-door", argv)) + setattr( + generated_package, + "generated_operation_contract", + lambda operation_id: {"id": operation_id}, + ) + setattr( + generated_package, + "run_generated_command", + lambda argv, handler: handler("demo.front-door", argv), + ) setattr(generated_package, "supports_generated_command", lambda command: True) primitives_package = types.ModuleType("generated_demo.primitives") - operation_executor_module = types.ModuleType("generated_demo.primitives.operation_executor") - setattr(operation_executor_module, "run_operation_ir", lambda operation, args: 0) + operation_executor_module = types.ModuleType( + "generated_demo.primitives.operation_executor" + ) + setattr( + operation_executor_module, "run_operation_ir", lambda operation, args: 0 + ) sys.modules["generated_demo"] = generated_package sys.modules["generated_demo.primitives"] = primitives_package - sys.modules["generated_demo.primitives.operation_executor"] = operation_executor_module + sys.modules["generated_demo.primitives.operation_executor"] = ( + operation_executor_module + ) module_globals: dict[str, object] = { "__name__": "generated_demo.runtime", "__package__": "generated_demo", @@ -2227,7 +2993,12 @@ def error(self, message: str) -> None: exec(rendered, module_globals) args = types.SimpleNamespace(demo_command=None, target="repo", format="text") - assert cast(Callable[[str, object], int], module_globals["_run_generated_operation"])("demo.front-door", args) == 0 + assert ( + cast( + Callable[[str, object], int], module_globals["_run_generated_operation"] + )("demo.front-door", args) + == 0 + ) args = types.SimpleNamespace( demo_command="route", @@ -2240,7 +3011,12 @@ def error(self, message: str) -> None: paths=[], path="fallback.txt", ) - assert cast(Callable[[str, object], int], module_globals["_run_generated_operation"])("demo.front-door", args) == 7 + assert ( + cast( + Callable[[str, object], int], module_globals["_run_generated_operation"] + )("demo.front-door", args) + == 7 + ) assert calls == [ [ "route", @@ -2363,7 +3139,10 @@ def emit_payload(*, payload: dict[str, object], format_name: str) -> None: "function": "payload_function", "support_import_module": runtime_module.__name__, "result": "emit_payload", - "emit_payload": {"import_module": runtime_module.__name__, "function": "_emit_payload"}, + "emit_payload": { + "import_module": runtime_module.__name__, + "function": "_emit_payload", + }, "arguments": [ { "name": "target_root", @@ -2371,10 +3150,18 @@ def emit_payload(*, payload: dict[str, object], format_name: str) -> None: "attr": "target", "validate_command": "demo", }, - {"name": "changed_paths", "kind": "list_attr", "attr": "changed"}, + { + "name": "changed_paths", + "kind": "list_attr", + "attr": "changed", + }, {"name": "dry_run", "kind": "bool_attr", "attr": "dry_run"}, {"name": "task_text", "kind": "attr", "attr": "task"}, - {"name": "profile", "kind": "diagnostic_profile", "default": "tiny"}, + { + "name": "profile", + "kind": "diagnostic_profile", + "default": "tiny", + }, ], } ] @@ -2386,15 +3173,29 @@ def emit_payload(*, payload: dict[str, object], format_name: str) -> None: generated_package = types.ModuleType("generated_argparse_demo") setattr(generated_package, "build_generated_parser", lambda: object()) setattr(generated_package, "generated_command_names", lambda: ["demo"]) - setattr(generated_package, "generated_operation_contract", lambda operation_id: {"id": operation_id}) - setattr(generated_package, "run_generated_command", lambda argv, handler: handler("demo.report", argv)) + setattr( + generated_package, + "generated_operation_contract", + lambda operation_id: {"id": operation_id}, + ) + setattr( + generated_package, + "run_generated_command", + lambda argv, handler: handler("demo.report", argv), + ) setattr(generated_package, "supports_generated_command", lambda command: True) primitives_package = types.ModuleType("generated_argparse_demo.primitives") - operation_executor_module = types.ModuleType("generated_argparse_demo.primitives.operation_executor") - setattr(operation_executor_module, "run_operation_ir", lambda operation, args: 0) + operation_executor_module = types.ModuleType( + "generated_argparse_demo.primitives.operation_executor" + ) + setattr( + operation_executor_module, "run_operation_ir", lambda operation, args: 0 + ) sys.modules["generated_argparse_demo"] = generated_package sys.modules["generated_argparse_demo.primitives"] = primitives_package - sys.modules["generated_argparse_demo.primitives.operation_executor"] = operation_executor_module + sys.modules["generated_argparse_demo.primitives.operation_executor"] = ( + operation_executor_module + ) module_globals: dict[str, object] = { "__name__": "generated_argparse_demo.runtime", "__package__": "generated_argparse_demo", @@ -2410,7 +3211,12 @@ def emit_payload(*, payload: dict[str, object], format_name: str) -> None: format="json", ) - assert cast(Callable[[str, object], int], module_globals["_run_generated_operation"])("demo.report", args) == 0 + assert ( + cast( + Callable[[str, object], int], module_globals["_run_generated_operation"] + )("demo.report", args) + == 0 + ) assert calls == [ {"validate": "demo", "target_root": "repo"}, { @@ -2454,7 +3260,9 @@ def test_contract_owned_conformance_case_runs_black_box_cli(tmp_path: Path) -> N result, failures = run_cli_conformance_case( case=case, - target=CliConformanceTarget(label="python-fixture", command=(sys.executable, str(cli)), cwd=fixture_root), + target=CliConformanceTarget( + label="python-fixture", command=(sys.executable, str(cli)), cwd=fixture_root + ), fixture_root=fixture_root, ) @@ -2467,13 +3275,18 @@ def test_contract_owned_conformance_case_runs_black_box_cli(tmp_path: Path) -> N def test_contract_owned_conformance_case_reports_output_drift(tmp_path: Path) -> None: contract = load_contract_conformance_case("todo.list.process") cli = tmp_path / "todo_cli.py" - cli.write_text("import json\nprint(json.dumps({'kind': 'todo-list/v1', 'item_count': 3}))\n", encoding="utf-8") + cli.write_text( + "import json\nprint(json.dumps({'kind': 'todo-list/v1', 'item_count': 3}))\n", + encoding="utf-8", + ) case = process_case_from_contract(contract=contract, command_placeholder="todo_cli") fixture_root = materialize_case_fixture(case=case, root=tmp_path / "fixtures") _result, failures = run_cli_conformance_case( case=case, - target=CliConformanceTarget(label="python-fixture", command=(sys.executable, str(cli)), cwd=fixture_root), + target=CliConformanceTarget( + label="python-fixture", command=(sys.executable, str(cli)), cwd=fixture_root + ), fixture_root=fixture_root, ) @@ -2485,13 +3298,17 @@ def test_contract_owned_conformance_case_reports_output_drift(tmp_path: Path) -> def test_contract_owned_conformance_case_checks_text_stdout(tmp_path: Path) -> None: contract = load_contract_conformance_case("todo.list-text.process") cli = tmp_path / "todo_cli.py" - cli.write_text("print('Todo items:\\n- Write contract-owned test')\n", encoding="utf-8") + cli.write_text( + "print('Todo items:\\n- Write contract-owned test')\n", encoding="utf-8" + ) case = process_case_from_contract(contract=contract, command_placeholder="todo_cli") fixture_root = materialize_case_fixture(case=case, root=tmp_path / "fixtures") result, failures = run_cli_conformance_case( case=case, - target=CliConformanceTarget(label="python-fixture", command=(sys.executable, str(cli)), cwd=fixture_root), + target=CliConformanceTarget( + label="python-fixture", command=(sys.executable, str(cli)), cwd=fixture_root + ), fixture_root=fixture_root, ) @@ -2500,7 +3317,9 @@ def test_contract_owned_conformance_case_checks_text_stdout(tmp_path: Path) -> N assert result.stdout == "Todo items:\n- Write contract-owned test\n" -def test_contract_owned_conformance_case_reports_text_stdout_drift(tmp_path: Path) -> None: +def test_contract_owned_conformance_case_reports_text_stdout_drift( + tmp_path: Path, +) -> None: contract = load_contract_conformance_case("todo.list-text.process") cli = tmp_path / "todo_cli.py" cli.write_text("print('Todo items:\\n- Different item')\n", encoding="utf-8") @@ -2509,7 +3328,9 @@ def test_contract_owned_conformance_case_reports_text_stdout_drift(tmp_path: Pat _result, failures = run_cli_conformance_case( case=case, - target=CliConformanceTarget(label="python-fixture", command=(sys.executable, str(cli)), cwd=fixture_root), + target=CliConformanceTarget( + label="python-fixture", command=(sys.executable, str(cli)), cwd=fixture_root + ), fixture_root=fixture_root, ) @@ -2527,7 +3348,11 @@ def test_contract_owned_operation_case_runs_function_adapter() -> None: case=case, target=FunctionConformanceTarget( label="python-function", - invoke=lambda values: {"kind": "todo-list/v1", "item_count": 2, "format": values["format"]}, + invoke=lambda values: { + "kind": "todo-list/v1", + "item_count": 2, + "format": values["format"], + }, ), ) @@ -2536,7 +3361,9 @@ def test_contract_owned_operation_case_runs_function_adapter() -> None: assert result.selected_fields == {"kind": "todo-list/v1", "item_count": 2} -def test_contract_owned_operation_case_runs_typescript_function_adapter(tmp_path: Path) -> None: +def test_contract_owned_operation_case_runs_typescript_function_adapter( + tmp_path: Path, +) -> None: if shutil.which("node") is None: pytest.skip("node is required for TypeScript function conformance") manifest = _fixture_manifest_with_typescript(tmp_path) @@ -2593,7 +3420,9 @@ def test_contract_owned_operation_case_checks_expected_function_error() -> None: case=case, target=FunctionConformanceTarget( label="python-function", - invoke=lambda _values: (_ for _ in ()).throw(ValueError("invalid format: yaml")), + invoke=lambda _values: (_ for _ in ()).throw( + ValueError("invalid format: yaml") + ), ), ) @@ -2602,7 +3431,9 @@ def test_contract_owned_operation_case_checks_expected_function_error() -> None: assert "invalid format" in result.error -def test_conformance_ownership_inventory_accounts_for_shared_and_consumer_surfaces() -> None: +def test_conformance_ownership_inventory_accounts_for_shared_and_consumer_surfaces() -> ( + None +): inventory = conformance_ownership_inventory() owns = cast(list[Mapping[str, object]], inventory["owns"]) @@ -2615,9 +3446,15 @@ def test_conformance_ownership_inventory_accounts_for_shared_and_consumer_surfac "bundled-conformance-case-resources", } <= owned assert "FunctionConformanceTarget" in cast(list[str], inventory["extension_points"]) - assert "TypescriptFunctionConformanceTarget" in cast(list[str], inventory["extension_points"]) - assert "consumer proof routing and installed-package lifecycle tests" in cast(list[str], inventory["consumer_owned"]) - assert "consumer-specific behavior remains in the consumer repo" in str(inventory["completion_rule"]) + assert "TypescriptFunctionConformanceTarget" in cast( + list[str], inventory["extension_points"] + ) + assert "consumer proof routing and installed-package lifecycle tests" in cast( + list[str], inventory["consumer_owned"] + ) + assert "consumer-specific behavior remains in the consumer repo" in str( + inventory["completion_rule"] + ) def test_contract_conformance_cases_manifest_loads_package_owned_cases() -> None: @@ -2627,10 +3464,15 @@ def test_contract_conformance_cases_manifest_loads_package_owned_cases() -> None assert manifest["schema_version"] == "command-generation/conformance-cases/v1" assert cases["todo.list.process"]["category"] == "convert" - assert load_contract_conformance_case("todo.list.operation")["operation_id"] == "todo.list.report" + assert ( + load_contract_conformance_case("todo.list.operation")["operation_id"] + == "todo.list.report" + ) -def test_generated_output_freshness_report_counts_hashes_and_staleness_by_host_target_family(tmp_path: Path) -> None: +def test_generated_output_freshness_report_counts_hashes_and_staleness_by_host_target_family( + tmp_path: Path, +) -> None: py_path = tmp_path / "out" / "python" / "cli.py" ts_path = tmp_path / "out" / "typescript" / "cli.mjs" py_path.parent.mkdir(parents=True) @@ -2651,14 +3493,21 @@ def test_generated_output_freshness_report_counts_hashes_and_staleness_by_host_t assert report["status"] == "stale-or-incomplete" assert report["rendered_output_count_by_family"] == {"python": 1, "typescript": 1} assert report["stale_output_count_by_family"] == {"typescript": 1} - assert report["stale_outputs_by_family"] == {"typescript": ["out/typescript/cli.mjs"]} + assert report["stale_outputs_by_family"] == { + "typescript": ["out/typescript/cli.mjs"] + } assert report["missing_target_families"] == [] assert set(report["expected_digest_by_family"]) == {"python", "typescript"} assert "do not rewrite generated files" in report["cheap_check_rule"] def test_generic_generator_source_has_no_aw_product_literals() -> None: - source = (Path(__file__).resolve().parents[1] / "src" / "command_generation" / "generator.py").read_text(encoding="utf-8") + source = ( + Path(__file__).resolve().parents[1] + / "src" + / "command_generation" + / "generator.py" + ).read_text(encoding="utf-8") forbidden = [ "agentic-workspace", @@ -2675,9 +3524,12 @@ def test_generic_generator_source_has_no_aw_product_literals() -> None: def test_generator_delegates_to_internal_target_renderers() -> None: - generator_source = (Path(__file__).resolve().parents[1] / "src" / "command_generation" / "generator.py").read_text( - encoding="utf-8" - ) + generator_source = ( + Path(__file__).resolve().parents[1] + / "src" + / "command_generation" + / "generator.py" + ).read_text(encoding="utf-8") python_target = importlib.import_module("command_generation.targets.python") typescript_target = importlib.import_module("command_generation.targets.typescript") @@ -2693,7 +3545,10 @@ def test_payload_assemble_builds_declarative_package_file_list(tmp_path: Path) - result = execute_primitive( "payload.assemble", values={ - "files": [{"relative_path": "required.md"}, {"relative_path": "optional.md"}], + "files": [ + {"relative_path": "required.md"}, + {"relative_path": "optional.md"}, + ], "skill_files": [{"relative_path": "fixture-skill/SKILL.md"}], }, arguments={ @@ -2730,9 +3585,12 @@ def test_output_emit_renders_file_lists_as_text_lines(tmp_path: Path) -> None: def test_generic_primitive_executor_has_no_aw_path_literals() -> None: - source = (Path(__file__).resolve().parents[1] / "src" / "command_generation" / "primitive_executor.py").read_text( - encoding="utf-8" - ) + source = ( + Path(__file__).resolve().parents[1] + / "src" + / "command_generation" + / "primitive_executor.py" + ).read_text(encoding="utf-8") assert ".agentic-workspace" not in source @@ -2745,7 +3603,9 @@ def test_primitive_registry_rejects_unsupported_target(tmp_path: Path) -> None: "id": "filesystem.read", "kind": "portable", "target_support": {"python": "unsupported"}, - "unsupported_targets": {"python": "fixture intentionally disables file reads"}, + "unsupported_targets": { + "python": "fixture intentionally disables file reads" + }, } ] ) @@ -2787,7 +3647,9 @@ def test_primitive_registry_checks_steps_inside_fragments(tmp_path: Path) -> Non "id": "filesystem.read", "kind": "portable", "target_support": {"python": "unsupported"}, - "unsupported_targets": {"python": "fixture intentionally disables file reads"}, + "unsupported_targets": { + "python": "fixture intentionally disables file reads" + }, } ] ) @@ -2812,10 +3674,15 @@ def test_primitive_registry_round_trips_host_metadata() -> None: "input_schema_ref": "contracts/operations/todo.list.report.json#/inputs", "output_schema_ref": "contracts/operations/todo.list.report.json#/output", "effects": {"read_only": True, "writes_repo_state": False}, - "target_support": {"python": "host-implemented", "typescript": "unsupported"}, + "target_support": { + "python": "host-implemented", + "typescript": "unsupported", + }, "owner": "fixture", "conformance_ref": "todo.list.process", - "unsupported_targets": {"typescript": "fixture has no TypeScript domain runtime"}, + "unsupported_targets": { + "typescript": "fixture has no TypeScript domain runtime" + }, } ] ) @@ -2828,7 +3695,10 @@ def test_primitive_registry_round_trips_host_metadata() -> None: assert definition.conformance_refs == ("todo.list.process",) with pytest.raises(ValueError, match="fixture has no TypeScript domain runtime"): registry.ensure_supported("todo.domain.load", "typescript") - assert registry.to_jsonable()[0]["unsupported_targets"]["typescript"] == "fixture has no TypeScript domain runtime" + assert ( + registry.to_jsonable()[0]["unsupported_targets"]["typescript"] + == "fixture has no TypeScript domain runtime" + ) def test_builtin_registry_declares_portable_primitives() -> None: @@ -2843,7 +3713,9 @@ def test_builtin_registry_declares_portable_primitives() -> None: def test_builtin_registry_classifies_primitive_ownership_boundaries() -> None: - definitions = {item["id"]: item for item in BUILTIN_PORTABLE_PRIMITIVES.to_jsonable()} + definitions = { + item["id"]: item for item in BUILTIN_PORTABLE_PRIMITIVES.to_jsonable() + } assert {item["kind"] for item in definitions.values()} <= {"portable", "host-owned"} assert definitions["filesystem.read"]["kind"] == "portable" @@ -2853,10 +3725,17 @@ def test_builtin_registry_classifies_primitive_ownership_boundaries() -> None: assert definitions["transaction.plan"]["kind"] == "portable" assert definitions["operation.call"]["kind"] == "host-owned" assert definitions["operation.call"]["target_support"]["python"] == "implemented" - assert definitions["operation.call"]["target_support"]["typescript"] == "unsupported" + assert ( + definitions["operation.call"]["target_support"]["typescript"] == "unsupported" + ) assert definitions["operation.dispatch"]["kind"] == "host-owned" - assert definitions["operation.dispatch"]["target_support"]["python"] == "implemented" - assert definitions["operation.dispatch"]["target_support"]["typescript"] == "unsupported" + assert ( + definitions["operation.dispatch"]["target_support"]["python"] == "implemented" + ) + assert ( + definitions["operation.dispatch"]["target_support"]["typescript"] + == "unsupported" + ) assert definitions["python.function.call"]["kind"] == "host-owned" assert definitions["typescript.domain.execute"]["kind"] == "host-owned" @@ -2864,7 +3743,9 @@ def test_builtin_registry_classifies_primitive_ownership_boundaries() -> None: def test_transitional_primitives_are_absent_from_builtin_registry() -> None: - definitions = {item["id"]: item for item in BUILTIN_PORTABLE_PRIMITIVES.to_jsonable()} + definitions = { + item["id"]: item for item in BUILTIN_PORTABLE_PRIMITIVES.to_jsonable() + } removed_ids = { "workspace.root.resolve", "payload.status", @@ -2884,12 +3765,16 @@ def test_transitional_primitives_are_absent_from_builtin_registry() -> None: def test_downstream_specific_primitive_coordination_docs_are_removed() -> None: root = Path(__file__).resolve().parents[1] - registry_source = (root / "src" / "command_generation" / "primitive_registry.py").read_text(encoding="utf-8") + registry_source = ( + root / "src" / "command_generation" / "primitive_registry.py" + ).read_text(encoding="utf-8") assert "agentic-workspace" not in registry_source assert "--aw-primitive-ownership" not in registry_source assert not (root / "docs" / "transitional-primitive-retirement.md").exists() - assert not (root / "docs" / "transitional-primitive-downstream-coordination.md").exists() + assert not ( + root / "docs" / "transitional-primitive-downstream-coordination.md" + ).exists() def _target_extension_contract(**overrides: object) -> dict[str, object]: @@ -2929,7 +3814,11 @@ def _target_extension_contract(**overrides: object) -> dict[str, object]: }, "maintenance_boundary": { "per_operation_feature_maintenance": False, - "allowed": ["runtime dependency updates", "target compatibility fixes", "projection bugs"], + "allowed": [ + "runtime dependency updates", + "target compatibility fixes", + "projection bugs", + ], }, } contract.update(overrides) @@ -2975,8 +3864,12 @@ def test_target_extension_contract_validates_and_projects_matrix_entries() -> No ) -def test_required_target_proof_matrix_requires_evidence_for_implemented_targets() -> None: - required = required_target_proof_matrix_entries([_target_extension_contract(), _typescript_target_extension_contract()]) +def test_required_target_proof_matrix_requires_evidence_for_implemented_targets() -> ( + None +): + required = required_target_proof_matrix_entries( + [_target_extension_contract(), _typescript_target_extension_contract()] + ) evidence_inventory = current_target_proof_evidence_inventory() evidence_ids = {item["evidence_id"] for item in evidence_inventory} @@ -2996,18 +3889,26 @@ def test_required_target_proof_matrix_requires_evidence_for_implemented_targets( "runtime-boundary", "primitive-support", } - assert all(item["source"].startswith("tests/test_public_api.py::test_") for item in evidence_inventory) + assert all( + item["source"].startswith("tests/test_public_api.py::test_") + for item in evidence_inventory + ) assert missing_target_proof_matrix_entries(required, evidence_ids) == () def test_structured_target_proof_evidence_inventory_types_current_evidence() -> None: - required = required_target_proof_matrix_entries([_target_extension_contract(), _typescript_target_extension_contract()]) + required = required_target_proof_matrix_entries( + [_target_extension_contract(), _typescript_target_extension_contract()] + ) required_by_id = {entry["evidence_id"]: entry for entry in required} structured = structured_target_proof_evidence_inventory() flat = current_target_proof_evidence_inventory() assert {item["evidence_id"] for item in structured} == set(required_by_id) - assert flat == tuple({"evidence_id": item["evidence_id"], "source": item["source"]} for item in structured) + assert flat == tuple( + {"evidence_id": item["evidence_id"], "source": item["source"]} + for item in structured + ) assert {item["evidence_type"] for item in structured} == { "conformance-case", "ordinary-test", @@ -3024,7 +3925,9 @@ def test_structured_target_proof_evidence_inventory_types_current_evidence() -> def test_required_target_proof_matrix_reports_missing_evidence() -> None: required = required_target_proof_matrix_entries([_target_extension_contract()]) - missing = missing_target_proof_matrix_entries(required, {"python:python.function:direct-operation-success"}) + missing = missing_target_proof_matrix_entries( + required, {"python:python.function:direct-operation-success"} + ) assert {entry["proof_kind"] for entry in missing} == { "direct-operation-structured-error", @@ -3057,7 +3960,9 @@ def test_target_extension_contract_rejects_product_semantics_ownership() -> None } ) - with pytest.raises(TargetExtensionContractError, match="target_owns_product_semantics"): + with pytest.raises( + TargetExtensionContractError, match="target_owns_product_semantics" + ): validate_target_extension_contract(contract) @@ -3069,5 +3974,7 @@ def test_target_extension_contract_rejects_per_operation_feature_maintenance() - } ) - with pytest.raises(TargetExtensionContractError, match="per_operation_feature_maintenance"): + with pytest.raises( + TargetExtensionContractError, match="per_operation_feature_maintenance" + ): validate_target_extension_contract(contract)