diff --git a/docs/research/sdl-editor-support/evidence.md b/docs/research/sdl-editor-support/evidence.md new file mode 100644 index 000000000..e53871a70 --- /dev/null +++ b/docs/research/sdl-editor-support/evidence.md @@ -0,0 +1,166 @@ +# SDL editor support evidence + +Observed 2026-10-09, and L12 on 2026-10-10, on repository revision +`35122105b0c1bb648754625d8e3920eafa9bc599`. The +[feasibility note](feasibility.md) interprets these observations. The probes +called the language-service helpers in-process, with the working tree at that +revision and the repository Python environment (CPython 3.14.4, free-threaded). +Nothing was mocked, and no scenario was launched. The probes establish helper +and schema behavior on small inputs. They are not a usability, conformance or +security result. + +## Minimal probe inputs + +Specimen N is a valid document with one feature and one node: + +```yaml +name: demo +features: + app: {type: service, source: webapp} +nodes: + web: + type: compute + os: linux + resources: {ram: 2 GiB, cpu: 1} + features: [app] +``` + +Specimen P is a valid document with a participant relationship: + +```yaml +name: audit +entities: + team: {role: red} +agents: + one: {affiliations: [team]} + two: {affiliations: [team]} +propositions: + ok: + description: declared state + subjects: [agents.one] + basis: declared_state + predicate: + kind: boolean + property: ready + semantic_ref: urn:raes:declared-property:ready + operator: equals + expected: true +assertions: + done: {proposition: ok, role: postcondition, polarity: positive} +relationships: + pair: + type: participant + source: agents.one + target: agents.two + participant: {kind: cooperation} +``` + +Specimen M is a valid document in which every value has its long form: + +```yaml +name: demo +features: + app: {type: service, source: {name: webapp, version: '*'}} +nodes: + web: {type: compute, os: linux, resources: {ram: 2 GiB, cpu: 1}, features: {app: client}, roles: {client: {username: www}}} +``` + +Each row below names the edit that it applies to a specimen. Lines and columns +are 1-based, as the helpers report them. + +## Language-service observations + +| ID | Operation | Observed result | Limit | +| --- | --- | --- | --- | +| L1 | `language_completions(P, cursor_path="/relationships/pair/target")`, then `language_diagnostics` of P with each offered `insert_text` as the target | Context `reference:targetable`, with six items: `agents.one`, `agents.two`, `assertions.done`, `entities.team`, `propositions.ok` and `relationships.pair`. Only `two` is valid. `one` fails with "Relationship 'pair' requires distinct participants". The other four fail because the target must "resolve unambiguously to a declared agent". | Finding F5 in the [participant audit](../participant-identity/audit.md) reports the same mismatch, and #1339 tracks it | +| L2 | Compare each list in `SECTION_FIELD_COMPLETIONS` with the property names that the published schema declares for that section | 18 of the 33 lists differ from the schema. Together they omit 69 schema fields. The `features` list also offers `version`, which the schema does not declare. The table below lists the differences. | The comparison merges every union branch. Nine schema sections have no list. | +| L3 | `language_completions` on a document whose only feature is `app: {type: service}`, with `cursor_path="/features/app"` and `prefix="v"`; then `language_diagnostics` with `version: '1.0'` added to that feature | Completion offers `version`. Diagnostics report `sdl.model.invalid`, "Extra inputs are not permitted", at `/features/app/version`. | | +| L4 | `language_completions(text, cursor_path="/nodes/web/features")` on N and on four edits of N | N gives `ok` with `app`. `features:` with no value gives `ok` with `app`. `features: [` gives `invalid` with `sdl.parse`. `fea` in place of the `features` line gives `invalid` with `sdl.parse`. `type: VM` gives `invalid` with `sdl.legacy_node_type_vm`. | A parse failure anywhere in the document removes every item | +| L5 | `language_diagnostics` on four documents | Adding `colour: blue` under `web` in N gives `sdl.model.invalid` at `/nodes/web/colour`, on line 10, columns 13 to 17. The key starts at column 5. `features: [missing]` in N gives `sdl.semantic` at the `semantic_validation` stage, with no path and no range. `name: demo` followed by `name: again` gives `sdl.mapping_key_conflict` at `/name`, on line 2, columns 1 to 5. An unclosed `web: {type: compute` gives `sdl.parse` with an empty path and one point, line 4, column 1. | The point in the last case is the end of the input | +| L6 | `language_format` on a document with three comments and the feature `app: {type: Service, source: webapp}` | Status `formatted`. The output has no comments. `type: Service` becomes `type: service`, and `source: webapp` becomes a mapping of `name: webapp` and `version: '*'`. | | +| L7 | `apply_structured_edit` on N with two added comments, operation `set`, pointer `/nodes/web/os`, value `windows` | Status `edited`. The output has no comments. `source: webapp` expands as in L6, and `features: [app]` becomes the mapping `app: ''`. | | +| L8 | `parse_sdl_file` and `language_diagnostics` on `contracts/fixtures/sdl/variation-points-v1/composition/root.yaml` | `parse_sdl_file` returns an `ExpandedScenario`. On the same text, `language_diagnostics` returns `sdl.parse`, "SDL imports require file-backed parsing via parse_sdl_file()". Turning off `semantic_validation` gives the same result. | | +| L9 | File sizes of the tracked `*.sdl.yaml` files, against `_MAX_INPUT_BYTES` | The limit is 65,536 bytes. None of the 40 files is over it. The largest is `examples/scenarios/hospital-ransomware-surgery-day.sdl.yaml`, at 46,073 bytes. | | +| L10 | `language_completions(N, cursor_path=path)` for `/nodes`, `/nodes/web`, `/nodes/web/resources` and `/nodes/web/os` | Each call returns context `section:nodes` and the same nine fields: `type`, `description`, `os`, `resources`, `features`, `conditions`, `services`, `roles` and `endpoint_persona`. | | +| L11 | Median of 5 warm calls of `language_diagnostics`, and of `language_completions` at `/nodes`, on the L9 file. Median of 3 fresh interpreters that import `raes` and make one diagnostics call. | At load average 7.2: 172 ms and 159 ms warm, and 1,207 ms cold. At 13.1: 174 ms and 163 ms warm, and 1,297 ms cold. At 22.5: 377 ms and 373 ms warm, and 2,939 ms cold. | An 8-core Apple M3 that other work shared. The timings are indicative. | +| L12 | The five helpers on two documents: `contracts/fixtures/sdl/materialized-scenario-v1/valid/reference.json` with N's node `web` added, and N. In both, the node's `features` is `[missing]`. Completion uses `cursor_path="/nodes/web/features"`, references use the symbol `web`, and the edit is L7's. | On the fixture, `language_diagnostics` returns `invalid` with `sdl.semantic`. `language_format` raises `SDLValidationError`, "Node 'web' references undefined feature 'missing'". Completion and references return `ok`, and the edit returns `edited_with_diagnostics` with `sdl.semantic`. The fixture's JSON text and its YAML dump behave the same. On N nothing raises, and `language_format` returns `formatted_with_diagnostics` with `sdl.semantic`. | | + +L2 counts, for each list, the distinct property names that the schema declares +for one entry of the section. For a section that is one object, such as +`realization`, it counts that object's properties. The count follows `$ref` and +merges `anyOf`, `oneOf` and `allOf` branches. These lists differ: + +| Section | Offered | In schema | Schema fields not offered | +| --- | --- | --- | --- | +| `accounts` | 7 | 14 | `description`, `disabled`, `groups`, `home`, `mail`, `materialization_profile`, `shell` | +| `agents` | 7 | 12 | `allowed_subnets`, `authority_anchors`, `description`, `observation_boundaries`, `operating_scope` | +| `behavior_specifications` | 17 | 18 | `autonomous_execution` | +| `conditions` | 5 | 10 | `environment`, `name`, `retries`, `start_period`, `timeout` | +| `content` | 7 | 13 | `description`, `destination`, `sensitive`, `tags`, `text`, `text_from` | +| `entities` | 4 | 7 | `description`, `events`, `mission` | +| `events` | 2 | 5 | `description`, `name`, `source` | +| `evidence_requirements` | 17 | 24 | `capture_requirement_ref`, `capture_spec_ref`, `description`, `field_selectors`, `notes`, `observation_demand`, `output_contract` | +| `features` | 4 | 7 | `description`, `destination`, `environment`, `name`. The list also offers `version`. | +| `infrastructure` | 4 | 6 | `acls`, `description` | +| `injects` | 3 | 6 | `description`, `environment`, `name` | +| `nodes` | 9 | 16 | `architecture`, `asset_value`, `injects`, `os_distribution`, `os_version`, `runtime`, `source` | +| `objectives` | 7 | 9 | `description`, `name` | +| `realization` | 2 | 3 | `constraints` | +| `relationships` | 11 | 17 | `database_access`, `description`, `forwarding_edge`, `mail_access`, `proxy_upstream`, `service_integration` | +| `scripts` | 4 | 6 | `description`, `name` | +| `stories` | 2 | 4 | `description`, `name` | +| `workflows` | 2 | 5 | `compensation`, `description`, `timeout` | + +These schema sections have no list: `action_contracts`, `execution_policy`, +`forwarding_agents`, `generated_artifacts`, `imports`, `module`, +`observation_boundaries`, `outcome_interpretation_rules` and +`persistent_volumes`. The schema's other four top-level properties are scalars. + +## Schema observations + +S1 to S3 load each document with `yaml.safe_load` and validate it against +`contracts/schemas/sdl/sdl-authoring-input-v1.json` with the +`Draft202012Validator` of `jsonschema` 4.26.0. That is how an editor association +checks raw source. + +| ID | Operation | Observed result | Limit | +| --- | --- | --- | --- | +| S1 | The 21 `*.sdl.yaml` files under `examples/` and `docs/public/_static/examples/` | `language_diagnostics` accepts all 21. The schema rejects one, `docs/public/_static/examples/first-scenario.sdl.yaml`: its `/nodes/lab-network/type` is `Switch`, and the schema allows only `compute` and `switch`. | | +| S2 | Six documents that `language_diagnostics` accepts: P without `relationships`, four edits of M, and a document with the node `web` and `infrastructure: {web: 2}` | P without `relationships` has no schema error. The other five have one each. The edits of M are `type: Service` in the feature, `source: webapp`, `features: [app]` in place of the node's `features` and `roles`, and `roles: {client: www}`. | | +| S3 | Four documents that `language_diagnostics` rejects | `features: {missing: client}` in M (`sdl.semantic`) passes the schema. So does `name: demo` followed by `name: again` (`sdl.mapping_key_conflict`). `colour: blue` in M's node (`sdl.model.invalid`) and the key `Features` in M (`sdl.noncanonical_field`) fail it. | `yaml.safe_load` keeps only the last duplicate key, so the schema never sees the first | +| S4 | Read the schema's annotations, and request its `$id` with `curl -L` | `x-raes-validates-raw-source` is `false`. `$id` is `https://openrae.github.io/rae/schemas/sdl-authoring-input-v1.json`, and the request returned HTTP 404 on 2026-10-09 at 11:45 UTC. | | +| S5 | Compare the schema at tag `v6.0.1` with this revision. Run `language_diagnostics` on `name: demo` plus `execution_policy: {}` at both. | At `v6.0.1` the schema has 45 top-level properties and 602 `$defs` entries, and no top-level `execution_policy`. At this revision it has 46 and 606, including `execution_policy`. The document is valid at this revision. With the `v6.0.1` package tree on `PYTHONPATH`, it fails with `sdl.model.invalid`, "Extra inputs are not permitted", at `/execution_policy`. | The `v6.0.1` run used this revision's dependencies, not the wheel's | +| Y1 | yaml-language-server 1.24.0 from npm, over stdio, with the association that a VS Code `yamlValidation` entry for `*.sdl.yaml` produces. The schema is the prototype's copy of this revision's file, SHA-256 `162121460f19a555f4e7c469a3e4574fe4ed5ecabe628836bf1880ede5f1aba0`. | 20 of the 21 S1 files get no diagnostics. `first-scenario.sdl.yaml` gets "Value is not accepted" on `type`. Each of the five accepted shorthand and case forms in S2 gets one diagnostic, and the unresolved reference in S3 gets none. On an empty line inside `/nodes/web`, where only `type` is present, completion offers 15 fields. Hover on `type` shows the `NodeType` description. | Red Hat's YAML extension 1.24.0 uses this server version. VS Code itself was not started. | + +## Inspected seams + +These source locations support the claims that a probe does not show. All +package paths are relative to `implementations/python/packages/`. + +| Concern | Source and inspected symbol | +| --- | --- | +| Field lists | `raes/_language_metadata.py`: `SECTION_FIELD_COMPLETIONS`, a hand-kept tuple of field names for each section | +| Completion depth | `raes/language_service.py`: `language_completions`. A path of one section name, and any longer path that does not end in a reference field, both read `SECTION_FIELD_COMPLETIONS[pointer[0]]`. | +| Reference completion | `raes/_language_metadata.py`: `REFERENCE_COMPLETION_TARGETS`; `raes/language_service.py`: `_completion_target_section`, `_reference_completion_items` | +| Completion on text that does not parse | `raes/language_service.py`: `_load_completion_data` returns the parse diagnostic instead of items | +| Semantic diagnostics | `raes/language_service.py`: `language_diagnostics` turns each message in `SDLValidationError.errors` into an `sdl.semantic` record without a path or range | +| Document context | Each helper takes text only. `language_diagnostics` calls `parse_sdl` without a path. | +| Handled exceptions | `raes/language_service.py`: `language_diagnostics` catches `SDLParseError` and `SDLValidationError`. `_load_completion_data`, `_try_declaration_index` and `language_format` catch `SDLParseError`. `_declaration_index_from_data` catches the pydantic `ValidationError`. `apply_structured_edit` catches `SDLParseError` and `ValueError`, and `_split_pointer_or_empty` catches `ValueError`. `raes/_language_references.py`: `_compose_yaml`, which `find_references` calls, catches `SDLParseError`. `_qualified_symbol_section`, `_is_variation_member_symbol` and `_bare_symbol` catch `TypeError` and `ValueError` from symbol parsing. `format_sdl_source` (`raes/formatting.py`) re-parses its output with `parse_sdl(..., skip_semantic_validation=True)`, and `parse_sdl` (`raes/parser.py`) still runs `SemanticValidator` for a `MaterializedScenario`, so `language_format` lets `SDLValidationError` reach the caller. Any other exception reaches the caller. | +| Input limit | `raes/language_service.py`: `_MAX_INPUT_BYTES`, `_size_error` | +| Formatting | `raes/language_service.py`: `language_format`, whose docstring reads "Migrate recognized legacy spellings and return canonical SDL YAML"; `raes/formatting.py`: `format_sdl_source` | +| Module cache | `raes/module_registry/__init__.py`: `_oci_cache_dir` returns `.raes/module-cache` under the base directory | +| Import trust | `raes/composition/_expand.py`: `expand_sdl_modules` calls `load_lockfile` and `load_trust_policy` on the document's directory. `raes/module_registry/models.py`: `load_trust_policy` returns `TrustPolicy()`, with no registries, when `raes-trust.yaml` is missing; `RegistryTrustPolicy.require_signatures` defaults to `True`. `raes/module_registry/resolution.py`: `_resolve_oci_import` raises "is not allowed by trust policy" for an unlisted registry before any request. For a listed registry it then fetches the manifest, config and bundle, checks signatures if the registry requires them, and only then calls `_extract_bundle_to_cache`. The integration test `test_untrusted_and_unsigned_oci_imports_fail_closed` in `implementations/python/tests/test_sdl_module_registry.py` asserts both refusals. | +| Schema for an installed `raes` | `raes_contracts/corpus.py`: `corpus_family_root`. The wheel build bundles the contract corpus at `raes_contracts/_corpus`, as the comment above `[tool.hatch.build.targets.wheel.hooks.custom]` in `implementations/python/pyproject.toml` describes. | +| Python range | `implementations/python/pyproject.toml` at tag `v6.0.1`: `requires-python = ">=3.11,<3.15"` | + +## Reproduce + +The prototype's +[evidence scripts](https://github.com/doublewhy/vscode-raes-sdl/tree/feac96e0d5262d914c11deecb209426851915763/evidence) +automate L1 to L11, S1 to S3 and Y1. Each script takes the path of a RAES +checkout and runs under that checkout's Python environment. The Y1 script also +takes the directory where `yaml-language-server@1.24.0` is installed. At this +revision the scripts printed the results above, and L11 records three runs of +the latency script. S4 and S5 used `curl`, `git show` and `git archive` on tag +`v6.0.1`. L12 called the helpers directly, in-process, on the inputs that its +row names. diff --git a/docs/research/sdl-editor-support/feasibility.md b/docs/research/sdl-editor-support/feasibility.md new file mode 100644 index 000000000..b165c4420 --- /dev/null +++ b/docs/research/sdl-editor-support/feasibility.md @@ -0,0 +1,245 @@ +# SDL editor support feasibility + +Date: 2026-10-09 + +This note answers issue #1412: should RAES support SDL authoring in VS Code, and what scope +would justify the work? It is research evidence, not contract authority. + +The observations use repository revision `35122105b0c1bb648754625d8e3920eafa9bc599`. The +[evidence record](evidence.md) holds the probe inputs and outputs, and IDs such as L4 name its +rows. Claims about the editor client rest on a prototype extension at +[doublewhy/vscode-raes-sdl](https://github.com/doublewhy/vscode-raes-sdl/tree/feac96e0d5262d914c11deecb209426851915763). +The prototype stays outside this repository because the issue asks for an exploration, and an +extension here would be executable adoption. + +## Recommendation + +Build one language server over the existing helpers and keep each editor client thin. A VS Code +extension is worth having as that thin client. It does not need its own SDL logic. + +The smallest useful feature set is: + +1. File association for `*.sdl.yaml`. +2. Diagnostics from `language_diagnostics`, debounced while the author types. +3. Reference completion and go to definition from `language_completions` and + `language_references`. + +Leave out formatting, edit actions, and schema diagnostics until the gaps below are closed. +Formatting and structured edits drop comments. Schema diagnostics disagree with RAES validation. + +Reusable components already exist. The five helpers carry the semantics. The published schema +serves completion and hover. `raes_contracts.corpus` locates the schema that matches an installed +`raes`. From the prototype, the stdio server and its cursor mapper are reusable starting points. + +These helper changes would remove most of the work that each client now repeats: + +1. Accept a line and column, or return a map from positions to paths. +2. Give semantic diagnostics a path and a source range. +3. Derive field completion from the model at every depth, not from hand-kept lists. +4. Offer only references that the field's purpose accepts, as #1339 requires. +5. Accept an optional document path, so local imports can resolve. +6. Return a diagnostic for every input instead of raising. + +### Alternatives + +The issue lists four alternatives: + +1. **YAML tooling with the schema association and explicit CLI validation: rejected as the + main path.** The association gives completion and hover, but its diagnostics disagree with + RAES (S2, S3 and Y1). `raes semantic validate` reports RAES results only when the author runs + it. Both remain useful without an extension. +2. **A highlighting and snippet extension: rejected.** Highlighting adds little over YAML, and + neither feature gives diagnostics or references. +3. **Direct VS Code language-feature providers over the helpers: rejected.** They still need a + Python process to run the helpers, and they serve only VS Code. Other editors and Monaco would + each need their own providers. +4. **One language server with thin editor clients: selected.** One server serves every LSP + client, and the editor-specific code stays small. + +## What the language service supports + +[`raes.language_service`](../../../implementations/python/packages/raes/language_service.py) +has five helpers that return plain dictionaries. The MCP server wraps them as the +`language_service` tool family. This repository ships no language server. + +| Helper | Input | Result | Editor use | +| --- | --- | --- | --- | +| `language_completions` | Text, a JSON-pointer-like `cursor_path`, a prefix | Top-level keys, section fields, or reference candidates | Completion | +| `language_references` | Text, a bare or qualified symbol | Definitions and occurrences with 1-based ranges | Go to definition, find references | +| `language_diagnostics` | Text, a `semantic_validation` flag | Parse, structural, and semantic records | Diagnostics | +| `language_format` | Text | Canonical YAML with migration advisories | Formatting | +| `apply_structured_edit` | Text, an operation, a JSON pointer, a value | Edited YAML with diagnostics | Code actions | + +The helpers reuse the repository parser and semantic validator. Each one refuses input over +64 KiB. The [language-service guide](../../explain/sdl/language-service.md) describes the +payloads. + +## What the editor integration needs + +A VS Code integration needs these parts: + +- A language contribution that maps `*.sdl.yaml` to a language identifier. +- A client that starts the server. With `vscode-languageclient`, the prototype's client is 41 + lines. +- A server that turns cursor positions into helper paths and helper ranges into LSP ranges. It + also debounces diagnostics and copes with documents that do not parse yet. +- A setting that picks the Python interpreter. The `raes` version installed there decides the + semantics. +- A Workspace Trust declaration, because that setting names a program to run. If the server + resolves imports, the workspace's `raes-trust.yaml` also decides which registries an import may + fetch from. +- A package built with `vsce`. A VS Code package cannot declare Python dependencies, so authors + install `raes` with `pip`. + +## What the prototype demonstrates + +The prototype has a TextMate grammar, a schema association, and a stdio language server. The +server uses only the Python standard library and `raes`. Sixteen tests drive the server over +stdio with the messages that the VS Code client sends. Nine test the cursor mapper, and six +tokenize SDL with VS Code's YAML grammar. All 31 passed locally against revision `35122105`, +and in [CI run 37926735623](https://github.com/doublewhy/vscode-raes-sdl/actions/runs/37926735623) +with `raes` 6.0.1 from PyPI. In that run, `vsce package` built a 571 KB package. Nothing is +published to the Marketplace, and no test starts VS Code itself. + +| Feature | Result | +| --- | --- | +| Diagnostics | Parse and structural errors keep their RAES ranges. Semantic errors have no location, so they appear on line 1 with a note. | +| Completion | Works on documents that parse. Otherwise the server answers from the last version that parsed and marks the items. | +| Go to definition | Works inside one file through `language_references`. | +| Highlighting | Top-level SDL keys and `${variable}` placeholders get their own scopes. A test checks the key list against the schema. | +| Schema association | Completion and hover work in yaml-language-server 1.24.0. Its diagnostics disagree with RAES. | +| Formatting | Not offered, because the helper drops comments. | + +The server forwards reference candidates unfiltered, so the #1339 mismatch stays visible. It +drops field names only after `key:` or inside a flow sequence, where no mapping key can go. That +is a YAML rule, not an SDL rule. When a helper raises, the server reports one diagnostic and +keeps running. Two tests check this with helpers that raise on a marker line. + +## Gaps + +| Area | Observation | Evidence | +| --- | --- | --- | +| Reference completion | For a participant relationship `target`, completion offers six declarations. Validation accepts one, the agent that is not the source. This is the #1339 mismatch. | L1; finding F5 in the [participant identity audit](../participant-identity/audit.md) | +| Field lists | The hand-kept lists miss 69 schema fields across 18 of 33 sections. The `features` list offers `version`, which validation rejects. | L2 and L3; `SECTION_FIELD_COMPLETIONS` in [`_language_metadata.py`](../../../implementations/python/packages/raes/_language_metadata.py) | +| Completion depth | Field completion uses only the section name. `/nodes`, `/nodes/web`, and `/nodes/web/resources` get the same nine fields. | L10; the field branches of `language_completions` in [`language_service.py`](../../../implementations/python/packages/raes/language_service.py) | +| Cursor paths | The helpers take a JSON pointer, so each client derives one from the text. The prototype's mapper has 204 lines and skips multi-line flow collections, block scalars, anchors, tags, and complex keys. | Prototype [`server/sdl_cursor.py`](https://github.com/doublewhy/vscode-raes-sdl/blob/feac96e0d5262d914c11deecb209426851915763/server/sdl_cursor.py) | +| Source ranges | Semantic diagnostics have no path or range. An unknown field is ranged on its value, not its key. A YAML syntax error is a single point. | L5 | +| Incomplete documents | Completion needs a document that parses. An unclosed `[` or a key without its colon gives a parse error and no items. | L4 | +| Imported modules | The helpers take no file path, so a document with `imports` is invalid even without semantic validation. `parse_sdl_file` accepts the same fixture. | L8 | +| Workspace context | The helpers see one document. Nothing indexes the workspace or follows references across files. | [`language_service.py`](../../../implementations/python/packages/raes/language_service.py) | +| Robustness | Every helper turns `SDLParseError` into a result. `language_diagnostics` also turns `SDLValidationError` into one, and `apply_structured_edit` and `language_format` check their output with `language_diagnostics`. But `language_format` lets `SDLValidationError` reach the caller for a document with `materialization_provenance`. For such a document, `parse_sdl` runs the semantic validator even though `format_sdl_source` skips it. Other exceptions reach the caller too, unless a catch listed under inspected seams handles them, so a server must guard every call. | L12; handled exceptions under [inspected seams](evidence.md#inspected-seams) | +| Large scenarios | The largest repository example is 46 KB, under the 64 KiB limit. Each call parses the whole text. On that file a warm call took 160 to 175 ms at load average 7 to 13, and about 375 ms at load average 22. A cold start took 1.2 to 2.9 s. The machine was shared, so the timings are indicative. | L9 and L11 | +| Formatting and edits | Both drop comments and expand shorthand. Formatting also lowers enum case and migrates recognized legacy spellings. | L6 and L7; the `language_format` docstring | +| Schema validation | The schema describes the normalized object, so `x-raes-validates-raw-source` is `false`. It flags accepted shorthand and case variants and misses semantic errors. | S2 to S4 and Y1 | +| Schema versions | The schema's `$id` returned HTTP 404. The schema on `dev` declares `execution_policy`, which `raes` 6.0.1 rejects. | S4 and S5 | + +The schema check flags one of the 21 accepted examples (S1): the quickstart's +`first-scenario.sdl.yaml` uses `type: Switch`. Any fix for imports must also handle registry +imports. When `parse_sdl` gets a path and the document has `imports`, it reads `raes.lock.json` +and `raes-trust.yaml` from the document's directory. It refuses an OCI import before any request +unless that trust file lists the import's registry. For a listed registry it fetches the module. +Once the registry's signature policy passes, it writes `.raes/module-cache` next to the document. +The trust file is workspace content, so a path-aware server should resolve imports only in a +trusted workspace (Import trust under [inspected seams](evidence.md#inspected-seams)). + +## Answers to the issue's questions + +### Which features are most useful + +Diagnostics come first. They need no cursor mapping and reuse the full validator. Reference +completion and go to definition come next. They help most on reference-heavy sections, but +completion inherits the #1339 mismatch. Schema hover shows field descriptions. Highlighting adds +little over plain YAML. Snippets and a document outline were not prototyped. Snippets give no +diagnostics or references (alternative 2), so they rank low. None of the five helpers returns the +document's declarations with their ranges, so an outline needs new helper or server work. It +would help in long scenarios, but it ranks after diagnostics and references. Formatting should +wait until comments survive it. + +### Can YAML support and the schema provide enough value + +They are enough for completion and hover, not for diagnostics. Inside a node, schema completion +offered the 15 node fields not yet present (Y1). The helper's list for nodes has 9 fields in all +(L10). Hover showed the schema's descriptions. But schema diagnostics flagged all five accepted +shorthand and case forms that the probe tried. They also missed an unresolved reference. A +dedicated extension is warranted only to add RAES diagnostics and reference features. + +Red Hat's YAML extension serves only the languages in its own document selector ([R2](#references)). +So an extension must choose: + +- Its own language identifier, with SDL highlighting but no schema completion. +- The `yaml` identifier, with schema completion but plain YAML highlighting. + +The prototype supports both. + +### Would an LSP adapter enable reuse + +Yes, for desktop editors. Any LSP client can start the same stdio command. VS Code needs the thin +extension described above. Other editors that speak LSP should need only their own settings for +the command and the file pattern. They were not tested. + +Monaco is different. A browser page cannot start a Python process. A Monaco client would need a +server reached over a WebSocket, for example through +[monaco-languageclient](https://github.com/TypeFox/monaco-languageclient). Running `raes` in +the browser was not evaluated. [monaco-yaml](https://github.com/remcohaszing/monaco-yaml) gives +Monaco the schema features, as the YAML extension does in VS Code. A VS Code extension does not +run inside Monaco. + +### What gaps exist + +See [Gaps](#gaps). The cursor-to-path mapping, source ranges, incomplete documents, imports, +workspace context, and large scenarios each have a row there. So does the #1339 completion +mismatch. + +### How packaging and versions should work + +- **File association.** The repository names 40 SDL files `*.sdl.yaml` (L9). VS Code prefers the + longest matching extension, so `.sdl.yaml` wins over `.yaml` ([R1](#references)). Files such as + the composition fixtures end in `.yaml` and need a `files.associations` entry. +- **Version selection.** The server runs in an interpreter that the author picks, and it reports + the `raes` version. A schema bundled in the extension can drift from that version, as S5 shows. + The `raes` wheel bundles its own copy, reachable through + `raes_contracts.corpus.corpus_family_root`, so a server could serve the matching schema. +- **Local and offline use.** The helpers work on the buffer text in-process. They read no other + document and use no network. Resolving imports would change that, so it should stay opt-in. + Red Hat's YAML extension downloads the SchemaStore catalog by default + (`yaml.schemaStore.enable`, [R3](#references)). +- **Packaging.** `vsce package` works without bundling Python. Authors install `raes` from PyPI. + Version 6.0.1 requires Python 3.11 to 3.14. In Restricted Mode, VS Code ignores a workspace + value of the interpreter setting if the extension lists it in `restrictedConfigurations` + ([R4](#references)). + +## Compatibility notes + +- **SDL semantics stay in RAES.** The prototype server adds no SDL rules. The grammar's key list + mirrors the schema's top-level properties, and a test checks that they match. +- **No launch and no fetch.** The helpers parse and validate text. Nothing runs a scenario or + downloads content. +- **Formatting.** `language_format` drops comments, expands shorthand, normalizes enum case, and + migrates legacy spellings. Format on save would rewrite authored text, so formatting should be + an explicit command at most. +- **Monaco.** Shared services are the helpers, the published schema, and one language server. + Editor adapters are the VS Code client, a Monaco client, and other editors' settings. + +## Method + +The probes call the helpers on small documents and on the repository examples. They compare the +results with the published schema and with yaml-language-server 1.24.0. Version 1.24.0 is the +server that Red Hat's YAML extension 1.24.0 uses. Nothing was mocked. The +[evidence record](evidence.md) gives each probe's input and output and the source locations +behind the other claims. It also links to the scripts that reproduce most probes. They live in +the prototype's repository, not in this one. The prototype's tests and CI run cover only the +editor client. + +## References + +- R1: VS Code 1.140.0, + [`languagesAssociations.ts`](https://github.com/microsoft/vscode/blob/1.140.0/src/vs/editor/common/services/languagesAssociations.ts), + the longest extension match. +- R2: Red Hat YAML extension 1.24.0, + [`src/extension.ts`](https://github.com/redhat-developer/vscode-yaml/blob/1.24.0/src/extension.ts), + the client document selector and the `yamlValidation` association. +- R3: Red Hat YAML extension 1.24.0, + [`package.json`](https://github.com/redhat-developer/vscode-yaml/blob/1.24.0/package.json), + the `yaml.schemaStore.enable` default. +- R4: VS Code, [Workspace Trust extension guide](https://code.visualstudio.com/api/extension-guides/workspace-trust). +- VS Code, [language server extension guide](https://code.visualstudio.com/api/language-extensions/language-server-extension-guide).