Skip to content

Validated edits and semantic rename for raw Workshop, through the agent contract and wright rename #434

Description

@e54-bot

Readiness: needs-decision on the two points under Decisions. The rest of the scope is settled. Also blocked on #433, since exact identifier spans are required.

Problem

Wright advertises four edit operations. None of them succeeds on real input.

  • validateEditTransaction and semanticRename cannot succeed by construction. In crates/wright-driver/src/edit.rs, validate_source_kind rejects raw Workshop with edit-unsupported-kind. For OPY, both functions end in an unconditional refusal(vec![source_provider_unavailable()]) once every precondition has passed. I reproduced this on main (0.3.0): a well-formed transaction against overpy-cake.ws returns edit-unsupported-kind.
  • providerValidateEdit and providerSemanticRename have a real implementation in crates/wright-driver/src/provider_edit.rs, but it is tested only against a mock provider. The shipped opy-provider does not implement LPP rename or edit validation yet (Implement LPP rename and edit validation in opy-provider opy-rs#403 is open).
  • Tests lock in the refusal: crates/wright-cli/tests/serve.rs asserts ok == false for both non-provider operations, and crates/wright-driver/tests/edit.rs asserts source-provider-unavailable.

So today Wright has no working validated source edit for any language. Yet goal.md principle 5 names "apply targeted edits" as core to agent workflows, and #134 lists "validated source-oriented mutation" in the 1.0 contract.

Raw Workshop is the one language where Wright can close this gap with no provider. workshop-rs owns parsing and validation, Program::edit_source produces checked source edits, and #433 provides exact identifier spans.

Why rename first, not lint fixes

None of the six current lint rules has a behavior-preserving mechanical fix:

  • min-wait-loop and while-without-wait: any fix changes timing.
  • repeated-value: extracting the value needs a new variable slot and proof that the value is pure.
  • expensive-loop-check and ongoing-condition-hot-path: these are heuristics, with no single correct edit.
  • duplicate-condition: it reports false positives (duplicate-condition reports independent If blocks as unreachable with exact evidence #432), and deleting a branch is not safe to automate.

Rename preserves behavior by definition, and textual replace cannot do it safely. A global variable and a player variable may share a name, and a name can appear as a substring of another name or inside a string. Rename is the edit where Wright's semantic knowledge is the whole difference, for humans and agents alike.

Scope

  • Edit validation for raw Workshop: apply the transaction to the supplied sources, then load the result again through the session's own workshop-rs parse and validation path. The transaction is ok only if no error diagnostic results. The existing precondition, overlap, and atomicity rules apply unchanged.
  • Semantic rename for raw Workshop covers global variables, player variables, and subroutines:
    • Build the transaction from the exact declaration and reference spans (Report reference and declaration spans at the identifier, from workshop-rs provenance #433). Every edit replaces exactly one identifier.
    • Refuse when the new name collides with an existing symbol in the same namespace.
    • Refuse when the new name does not survive reparsing.
    • After the edit, confirm that the renamed symbol has the same reference count and kinds as before. Any mismatch is a refusal.
  • Name addressing for the rename target, consistent with Expose semantic queries through the Wright inspect workflow #429. Position addressing stays accepted.
  • CLI: wright rename <NAME> <NEW_NAME> [INPUT], a shared human/agent surface with a wright-result/v1 envelope under --format json. Write behavior is decision D2.
  • Update docs/agent-contract.md, docs/cli/commands.md, and docs/architecture/tooling.md for the raw Workshop edit path.

Decisions

D1. Which operations carry raw Workshop edits. Recommended: the non-provider validateEditTransaction and semanticRename become the raw Workshop path, owned by Wright and backed by workshop-rs. The provider* operations remain the source-language path. OPY input sent to a non-provider operation gets a structured refusal that names the provider operation. The change is additive in behavior: inputs that were always refused can now succeed, and no schema member changes. The alternative is a new operation pair, which would leave two permanently dead operations advertised.

D2. Whether wright rename writes files. Recommended: by default it prints the validated change as a diff and writes nothing, and --write applies it atomically after validation. --write refuses if any source's identity changed since it was read. This would make rename the first Wright command that modifies user source. Since the agent contract says "Wright proposes and validates; a caller remains responsible for applying them", --write puts the CLI in the caller's role while leaving the agent operation unchanged. The alternative, preview only, keeps Wright read-only but leaves humans applying diffs by hand.

Non-goals

Acceptance criteria

  • Renaming cakePos to cakePosition in overpy-cake.ws returns an ok transaction. The transaction edits the declaration and all 17 references, and each edit replaces exactly the identifier. A test applies the transaction and asserts that the only changed text is those identifiers.
  • The renamed program passes check, and the renamed symbol has the same reference count and kinds as before the rename. Covered by a test.
  • Renaming to a name already used in the same namespace (cakePos to i2) is refused with a stable diagnostic code and no transaction. Covered by a test.
  • A global and a player variable with the same name: renaming one leaves every reference to the other untouched. Covered by a test.
  • A new name that does not survive reparsing is refused with no transaction. Covered by a test.
  • validateEditTransaction on raw Workshop returns ok with previews for an edit that keeps the program valid. It returns a refusal carrying the reparse diagnostics for an edit that breaks the program. Covered by tests.
  • OPY input to a non-provider edit operation returns a structured refusal that names the provider operation. Covered by a test.
  • wright rename behaves as D2 decides. If D2 is accepted: without --write the input file's bytes are unchanged; with --write the file matches the validated preview; and a file modified between read and write is refused, with the file left untouched. Tests cover all three.
  • The tests that asserted unconditional refusal for raw Workshop edits are replaced by the behavior above. The PR states that this expectation change is intentional.
  • capabilities advertises the same operation list, and the agent-contract schema tests pass.
  • Ablation: removing the post-edit reference-count check makes a test that injects a mismatching edit fail.

Dependencies / ownership

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

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions