Skip to content

Decide the canonical source-mapping contract across the provider boundary #271

Description

@e54-bot

Readiness: decided — see ADR-0013 and the decision comment below; implementation issues are split per owner.

Goal

Decide the canonical source-mapping contract that lets Workshop-level evidence produced from a source-language program (lint findings, analysis, element cost, validation diagnostics, validated edits) be attributed to the original authored source, and decide which part of that contract must be settled before the workshop-rs 1.0 API freeze (#252).

Context

  • opy-rs lowering already attaches rule, condition, and action spans to workshop_rs::Program through Program::set_rule_span / set_condition_span / set_action_span (crates/opy-rs/src/compiler/lowering.rs).
  • That mapping is lost at the provider boundary: lpp/compile returns an opaque Workshop artifact (text), and the LPP spec explicitly defers a canonical Workshop artifact format to the ecosystem ("LPP will not freeze one without concrete evidence").
  • Wright re-parses the returned Workshop text. wright-driver's SourceProvenance currently has a single Unmapped variant, so every Wright-owned lint/analyze finding on an OPY project is reported against <provider-artifact>. Preserve Wright lint and analyze semantics across the OPY provider boundary wright#246 was completed by labeling this evidence explicitly unmapped, which is truthful but leaves the actual mapping undelivered.
  • Real-project evidence: Bastion (ci: integrate Wright real-project validation OWBastion/Bastion#214) validates OPY → Workshop through Wright + opy-provider; Wright's own findings on that project cannot point at .opy lines.
  • Current workshop-rs source-mapping model (Program in program.rs, docs/source-preservation.md): spans live in a private side table indexed by rule/condition/action position, while rules / actions are public Vecs. Granularity stops at direct action arguments; nested Value expressions have no span. Inserting or removing an element through the public fields silently shifts every later mapping.

The goal requires diagnostics, analysis, and validated edits to work on real projects for humans and agents; on the primary real-world language (OPY) that currently stops at the provider boundary.

Decisions required

  1. Identity model (1.0-blocking). How a mapping stays attached to a canonical node:
    • position-indexed side table (current) with mutation constrained so it cannot desync;
    • stable node identities issued by Program with a side table keyed by identity;
    • inline optional spans on public nodes.
      Trade-offs: mutation safety for edits/transforms, compatibility with the requested public construction sugar (Expose a typed Workshop action and value API #178) and public fields, and 1.x extensibility.
  2. Granularity (1.0-blocking if it changes public shape). Whether nested value expressions carry mappings, or rule/condition/action/direct-argument is the durable contract.
  3. Mapping target. Whether a mapping is a workshop-rs Span into an attached SourceFile, or a source-language-owned opaque reference (e.g. URI + range) that workshop-rs carries without interpreting, including multi-file includes and macro-expanded / generated code with no single authored location.
  4. Transport across the provider boundary. Options include Workshop text plus a structural source map keyed by canonical node path, or a versioned serialized canonical program. Owner of the format (expected: workshop-rs, as the canonical Workshop artifact) and how LPP advertises it (capability) without LPP defining Workshop semantics.
  5. Mapping through Wright transforms. How mappings survive wright-transform profiles and which findings remain explicitly generated/unmapped.

Scope

  • Record the decisions above (ADR in the owning repository, current contract in docs/).
  • Identify which decisions change the workshop-rs public API and fold them into Freeze the workshop-rs public API for 1.0 #252 before the freeze.
  • Split follow-up implementation issues per owner: workshop-rs (model/format), language-provider-protocol (capability), opy-rs (provider emission), Wright (consumption and presentation).

Non-goals

  • Implementing the contract in this issue.
  • Changing LPP to carry provider AST/HIR or workshop-rs internal WIR.
  • DEL/OSTW provider mapping (deltin-rs has no provider yet; the contract must not preclude it).
  • Exact whole-file formatting or byte-identical re-emission.

Acceptance criteria

  • Each decision above is recorded with its rationale and rejected alternatives.
  • The workshop-rs 1.0 candidate's public source-mapping API matches the decided identity model and granularity, or Freeze the workshop-rs public API for 1.0 #252 explicitly records that the model is out of the 1.x contract.
  • Follow-up implementation issues exist in each owning repository with a shared real-project acceptance check: Wright lint/analyze findings on a pinned Bastion revision resolve to valid .opy locations, and unmappable evidence stays explicitly unmapped.

Dependencies / ownership

Planning notes

Non-binding recommendation for the design discussion: a structural source map (canonical node path → source-owned location) carried beside the Workshop text keeps LPP neutral, avoids freezing a serialized Program wire format, and matches the existing position-based model, provided workshop-rs constrains public mutation so paths cannot silently desync. Whether that constraint is compatible with the #178 construction sugar is the key question to settle first.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions