feat(gatorwalk-factory): retarget a work item to another issue, journaled (swamp-club #2798) #388

Merged
seth merged 2 commits from cue/2798-gatorwalk-factory-retarget into main 2026-09-30 18:51:22 +00:00
Owner

Closes swamp-club #2798. Part of the tracker-seam decision; #2799 (mark a duplicate) builds on it.

What

  • retarget work-item method (engine side): externalRefs (JSON object), reason, the usual expect inputs and onBehalfOf. It replaces externalRefs whole and journals a retargeted event (from, to, reason, actor). There is no gate and no stage change. It is refused on a finished work item, on a stale expectation, for a map that names no ticket (no non-empty stable id), and for an unchanged map. It commits through the committing store, so the awaiting event and metrics are recomputed; CEL can read item.externalRefs.
  • publish routing (tracker side): ticketSegments in projection.ts splits the journal into one segment per ticket at each retarget of this tracker's ref.
    • Events up to the retarget go to the old ticket, then a "moved to" note. The old ticket's status moves only up to the stage at the retarget.
    • The new ticket gets a "continued here from" note, the current status (its first status write is unconditional), then later events.
    • One cursor follows the ticket and is written after each. The new ticket's note has its own ledger name (-opening), and the old ticket's status is keyed on the version before the retarget, so no delivery key names two tickets.
    • Refs added by a retarget get a "linked" note. For removed refs, the old ticket gets a "no longer reports" note, and publish is then up to date.
    • The reason is not posted (swamp-club#2284).
  • Summary report: shows the retarget in the timeline and a "Previously" line with the earlier refs.
  • Docs: DESIGN.md ("Tracker ids are data", the publisher's new Retargeting paragraph, delivery keys, claim's known gaps), README, and the driving skill (method table and a retarget passage).

The claim index is not moved; that is #2799. Until then, claim of the old ticket is refused and claim of the new one reserves a fresh key. This is documented and pinned by a test.

Tests

  • Walk: the swamp-extensions definition retargets mid-lifecycle. Stage, entries, products, approvals, dispatches and exits are unchanged, notify's issue binding names the new ticket, and the summary shows both refs.
  • Lab fake: after a retarget, publish writes new events only to the new issue; entry mode keeps entries per issue with the notes as ripples.
  • Tracker core: a cursor behind the retarget, A→B→C, another tracker's ref only, added and removed refs, and a failure on the new ticket followed by a re-run.
  • Real CLI: retarget with a JSON-string externalRefs.

Verified at fef06af: verify-build and verify-reviews all green (attestation 301c2d26). No manifest bump, because gatorwalk-factory has no manifest.

🤖 Generated with Claude Code

Closes swamp-club #2798. Part of the tracker-seam decision; #2799 (mark a duplicate) builds on it. ## What - **`retarget` work-item method** (engine side): `externalRefs` (JSON object), `reason`, the usual expect inputs and `onBehalfOf`. It replaces `externalRefs` whole and journals a `retargeted` event (`from`, `to`, `reason`, actor). There is no gate and no stage change. It is refused on a finished work item, on a stale expectation, for a map that names no ticket (no non-empty stable id), and for an unchanged map. It commits through the committing store, so the awaiting event and metrics are recomputed; CEL can read `item.externalRefs`. - **`publish` routing** (tracker side): `ticketSegments` in `projection.ts` splits the journal into one segment per ticket at each retarget of this tracker's ref. - Events up to the retarget go to the old ticket, then a "moved to" note. The old ticket's status moves only up to the stage at the retarget. - The new ticket gets a "continued here from" note, the current status (its first status write is unconditional), then later events. - One cursor follows the ticket and is written after each. The new ticket's note has its own ledger name (`-opening`), and the old ticket's status is keyed on the version before the retarget, so no delivery key names two tickets. - Refs added by a retarget get a "linked" note. For removed refs, the old ticket gets a "no longer reports" note, and `publish` is then up to date. - The reason is not posted (swamp-club#2284). - **Summary report**: shows the retarget in the timeline and a "Previously" line with the earlier refs. - **Docs**: DESIGN.md ("Tracker ids are data", the publisher's new Retargeting paragraph, delivery keys, claim's known gaps), README, and the driving skill (method table and a `retarget` passage). The claim index is not moved; that is #2799. Until then, `claim` of the old ticket is refused and `claim` of the new one reserves a fresh key. This is documented and pinned by a test. ## Tests - Walk: the swamp-extensions definition retargets mid-lifecycle. Stage, entries, products, approvals, dispatches and exits are unchanged, notify's `issue` binding names the new ticket, and the summary shows both refs. - Lab fake: after a retarget, publish writes new events only to the new issue; entry mode keeps entries per issue with the notes as ripples. - Tracker core: a cursor behind the retarget, A→B→C, another tracker's ref only, added and removed refs, and a failure on the new ticket followed by a re-run. - Real CLI: `retarget` with a JSON-string `externalRefs`. Verified at fef06af: verify-build and verify-reviews all green (attestation 301c2d26). No manifest bump, because gatorwalk-factory has no manifest. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
A work-item method `retarget` replaces externalRefs whole and journals a
`retargeted` event (old and new maps, reason, actor). No gate and no stage
change; refused on a finished work item, a stale expectation, an empty map
or an unchanged one. It is an engine method and knows nothing of trackers.

`publish` splits the journal into one segment per ticket at each retarget
of its tracker's ref: events up to the retarget go to the old ticket, later
ones to the new, each ticket gets a note (moved to / continued from), and the
new ticket's first status write is unconditional. One cursor follows the
ticket and is written after each; the new ticket's note has its own ledger
name. The claim index is left to #2799.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
fix(gatorwalk-factory): retarget names a ticket, and publish is up to date past a removed ref (swamp-club #2798)
All checks were successful
CI / Review Integrity (pull_request) Successful in 48s
CI / Validate Attestation (pull_request) Successful in 52s
fef06afada
From the verify-reviews low findings: retarget refuses a map with no
non-empty stable id (only <tracker>.display keys or empty values), which
would detach the work item from every tracker; and publish reports up to
date instead of logging a skip on every run once a retarget removed its
tracker's ref.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
seth merged commit 01571e1baa into main 2026-09-30 18:51:22 +00:00
seth deleted branch cue/2798-gatorwalk-factory-retarget 2026-09-30 18:51:23 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
swamp-club/swamp-extensions!388
No description provided.