Skip to content

fix(adt): reuse an object's existing open transport on write instead of 409-ing (#144) - #145

Open
zooloo303 wants to merge 2 commits into
oisee:mainfrom
zooloo303:fix/144-reuse-open-transport
Open

fix(adt): reuse an object's existing open transport on write instead of 409-ing (#144)#145
zooloo303 wants to merge 2 commits into
oisee:mainfrom
zooloo303:fix/144-reuse-open-transport

Conversation

@zooloo303

@zooloo303 zooloo303 commented Jul 3, 2026

Copy link
Copy Markdown

Summary

Fixes #144. When editing an object that is already captured in an open transport
request of the current user, vsp binds the write to a new request, and SAP
rejects it with:

409 ExceptionResourceLockConflict
Object ... is already locked in request <id> ... cannot be edited under a new request

ADT / Eclipse reuse the lock's existing request automatically. This does the same:
when the caller supplies no transport, fall back to the request SAP already returns
on the LOCK response as LockResult.CorrNr.

Change

New helper resolveWriteTransport(supplied, lockCorrNr, opName)
(pkg/adt/transport_reuse.go), wired in right after the lock in every update path
that locks an object and then binds the write to the caller's transport:

Commit 1

  • WriteProgram, WriteClass (pkg/adt/workflows.go)
  • EditSourceWithOptions (pkg/adt/workflows_edit.go)

Commit 2 — the remaining paths, so the fix is comprehensive rather than partial:

  • writeClassMethodUpdate — CLAS method-level surgical update (pkg/adt/workflows_source.go)
  • writeSourceUpdate — INTF and DDLS/BDEF/SRVD branches
  • writeSourceUpdate — class testclasses include
  • UpdateFromFile — file-based deploy of an existing object (pkg/adt/workflows_deploy.go)

The writeSourceCreate paths are deliberately left alone: a newly created object is
not yet captured in a request, so LockResult.CorrNr is empty and the fallback would
be a no-op.

Safety

The fallback re-runs checkTransportableEdit on the resolved request at every call
site, so auto-reuse cannot bypass --allow-transportable-edits or the
--allowed-transports whitelist. An explicitly-supplied transport is used unchanged
(already gated at the top-level mutation gate). A local object (no open request) is
unchanged (empty transport).

Tests

TestResolveWriteTransport covers explicit-wins, local/no-op, fallback-when-allowed,
and the safety-critical case where the fallback is blocked because transportable
edits are disabled. go build ./..., go vet ./pkg/adt/ and go test ./... are green.

Verified live against an on-prem system for the commit-1 paths: editing an object
already bound to an open request now succeeds without the caller supplying the
transport (previously a 409), and the object is left clean afterwards. The commit-2
sites are one-line call-site wirings of the same already-tested helper and have not
been separately exercised live — happy to do that on any of them if you'd like it
before this lands.

Scope

Now covers all update paths. Deletes are not included: DeleteObject doesn't lock
(it receives a lockHandle from its callers), so the same reuse would have to land at
each locking caller — a separate change from the write paths, and out of scope here.

Note: this touches the same write paths as #125 / #108; it composes cleanly with them
(verified by stacking all three locally and exercising the combined path live).

Thanks to @txape10 for independently confirming the re-check approach on their fork and
flagging writeClassMethodUpdate — commit 2 closes that gap and the rest of the class.

When the caller supplies no transport, fall back to the request the object is
already bound to (LockResult.CorrNr) instead of binding the write to a new
request and hitting 409 ExceptionResourceLockConflict. The fallback re-checks
transportable-edit policy so it never bypasses --allow-transportable-edits.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@txape10

txape10 commented Jul 29, 2026

Copy link
Copy Markdown

Good catch on the re-check — this closes a real gap in the original #144 fix (a transport only discovered after Lock could otherwise bypass AllowTransportableEdits/AllowedTransports).

On our fork (txape10/vibing-steampunk) we applied this same approach — re-validating the transportable-edit policy before adopting lock.CorrNr — to all the WriteX/EditSource/delete call sites we have. Doing that surfaced one more spot worth flagging here: writeClassMethodUpdate (in pkg/adt/workflows_source.go) takes a transport parameter and passes it straight to UpdateSource, but never reads lock.CorrNr from the LockObject call at all. So for that call site, the original #144 symptom (spurious 409 on an object already captured in an open request) is still there, unaddressed by either the #144 fix or this PR.

Not asking you to fold that in here — just flagging it as a related gap in case it's useful when this lands, since it's the same class of bug this PR fixes elsewhere.

…isee#144)

Applies the same resolveWriteTransport fallback to every remaining write path
that locks an object and then binds the update to the caller's transport,
so an object already captured in an open request is not rejected with a
spurious 409 ExceptionResourceLockConflict:

- writeClassMethodUpdate  - CLAS method-level surgical update
- writeSourceUpdate       - INTF and DDLS/BDEF/SRVD branches
- writeSourceUpdate       - class testclasses include
- UpdateFromFile          - file-based deploy of an existing object

Each site re-runs the transportable-edit policy on the resolved request via
the shared helper, so auto-reuse still cannot bypass
--allow-transportable-edits or the --allowed-transports whitelist.

The writeSourceCreate paths are deliberately left alone: a newly created
object is not yet captured in a request, so LockResult.CorrNr is empty and
the fallback would be a no-op.

Reported-by: @txape10
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@zooloo303

Copy link
Copy Markdown
Author

Thanks — and good confirmation that the re-check is worth keeping; that was the part I
was least sure a reviewer would want, since it makes the fallback stricter than a
plain lock.CorrNr adoption.

You're right about writeClassMethodUpdate, and doing the same sweep you did turned up
the same broader pattern, so I've folded it all in as a second commit rather than leave
the fix partial:

  • writeClassMethodUpdate — CLAS method-level surgical update
  • writeSourceUpdate — INTF and DDLS/BDEF/SRVD branches
  • writeSourceUpdate — class testclasses include
  • UpdateFromFile — file-based deploy of an existing object

All of them go through the same resolveWriteTransport helper, so the transportable-edit
re-check applies uniformly at each site.

Two notes on where I drew the line:

  • The writeSourceCreate paths are deliberately untouched — a newly created object isn't
    yet captured in a request, so CorrNr is empty and the fallback is a no-op there.
  • Deletes are out of scope. DeleteObject doesn't lock (it takes a lockHandle from its
    callers), so the reuse would have to land at each locking caller. Since you've already
    done that on your fork, that might be worth its own PR — I'd happily review it.

Being straight about verification: the commit-1 paths are live-verified against an on-prem
system; the commit-2 sites are one-line wirings of the same already-tested helper and
haven't been separately exercised live. Happy to verify any of them live if that would help
before this lands.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

edit should auto-reuse an object's existing open transport instead of 409-ing

2 participants