feat(gatorwalk-factory): parent, blocked-by, related and duplicate relations in every tracker (swamp-club #2796) #403

Merged
seth merged 3 commits from cue/2796-gatorwalk-factory-parent into main 2026-10-01 02:49:37 +00:00
Owner

Fixes swamp-club #2796.

What

gatorwalk-factory trackers gain issue relations: parent_of, blocked_by, related_to and duplicate_of.

  • Contract: relate(from, type, to) and unrelate(...) on every adapter. A fetched ticket's relations each come with a direction. Both calls can be repeated safely: already related, or already absent, writes nothing.

  • Shared rules (_lib/tracker/core/relations.ts): the Lab's rules are checked before every write, so all three trackers refuse the same relations:

    • a ticket related to itself
    • a second parent
    • a parent cycle (walked 10 deep)
    • a second canonical
    • a duplicate of a duplicate
    • a duplicate that has duplicates of its own

    A missing ticket at either end is not_found. A relation counts as existing when either ticket shows it.

  • Methods: the three tracker models get relate and unrelate, recorded in the delivery ledger. The key names the source, type and target, so one journal version can relate several tickets (for breakdown later). publish is unchanged.

  • Built-in tracker: a relation is kept on both tickets' records. Finishing a half-written relation runs the rules again.

  • Lab: the relationship calls from issue-lifecycle's root client are ported into gatorwalk's copy:

    • Relationships are read from GET, and ones the adapter can't read are skipped.
    • unrelate deletes by relationship id, and the empty 204 reply is accepted.
    • A member key gets auth for blocked_by and for relations on another user's issues.
  • Linear: parent_of uses the child's parentId. blocked_by is Linear's blocks read the other way, duplicate_of is duplicate and related_to is related. Linear enforces none of the rules itself, so the fake doesn't either; that shows the shared check is what refuses.

  • Conformance: the suite covers relate, unrelate, read-back on both ends, every refusal, not_found and the ledger, for all three backends against their fakes.

  • Docs:

    • DESIGN.md "Trackers": the tracker owns relations. Swamp reads them at decision points and never reconciles them; a change made in the tracker is seen at the next read.
    • README: usage and per-tracker notes.
    • SKILL.md: a short note.

Also in this PR

scripts/build_attestation.ts now ignores sensitiveFormat and writtenReferences at the top level of an evaluated workflow. The current swamp CLI writes them into every evaluated workflow, so every run was being refused. The fix is ported from swamp's own fix (swamp-club#2815). The same keys anywhere below the top level, and any other unexpected top-level key, are still refused.

Verification

  • verify-build 413d4c8d and verify-reviews 248160ae both passed on db96806089 (13/14 steps, idempotency skipped).
  • All three reviews passed.
  • Attestation 2314d32c is posted.

Known low-severity notes from the reviews, not addressed here:

  • Linear's 250-entry read is shared across blocks, duplicate and related links, not separate per kind.
  • A Linear related link made from the other ticket isn't detected, so relating the other way adds a second link.

🤖 Generated with Claude Code

Fixes swamp-club #2796. ## What gatorwalk-factory trackers gain issue relations: `parent_of`, `blocked_by`, `related_to` and `duplicate_of`. - **Contract:** `relate(from, type, to)` and `unrelate(...)` on every adapter. A fetched ticket's `relations` each come with a direction. Both calls can be repeated safely: already related, or already absent, writes nothing. - **Shared rules** (`_lib/tracker/core/relations.ts`): the Lab's rules are checked before every write, so all three trackers refuse the same relations: - a ticket related to itself - a second parent - a parent cycle (walked 10 deep) - a second canonical - a duplicate of a duplicate - a duplicate that has duplicates of its own A missing ticket at either end is `not_found`. A relation counts as existing when either ticket shows it. - **Methods:** the three tracker models get `relate` and `unrelate`, recorded in the delivery ledger. The key names the source, type and target, so one journal version can relate several tickets (for breakdown later). `publish` is unchanged. - **Built-in tracker:** a relation is kept on both tickets' records. Finishing a half-written relation runs the rules again. - **Lab:** the relationship calls from issue-lifecycle's root client are ported into gatorwalk's copy: - Relationships are read from GET, and ones the adapter can't read are skipped. - `unrelate` deletes by relationship id, and the empty 204 reply is accepted. - A member key gets `auth` for `blocked_by` and for relations on another user's issues. - **Linear:** `parent_of` uses the child's `parentId`. `blocked_by` is Linear's `blocks` read the other way, `duplicate_of` is `duplicate` and `related_to` is `related`. Linear enforces none of the rules itself, so the fake doesn't either; that shows the shared check is what refuses. - **Conformance:** the suite covers relate, unrelate, read-back on both ends, every refusal, `not_found` and the ledger, for all three backends against their fakes. - **Docs:** - DESIGN.md "Trackers": the tracker owns relations. Swamp reads them at decision points and never reconciles them; a change made in the tracker is seen at the next read. - README: usage and per-tracker notes. - SKILL.md: a short note. ## Also in this PR `scripts/build_attestation.ts` now ignores `sensitiveFormat` and `writtenReferences` at the top level of an evaluated workflow. The current swamp CLI writes them into every evaluated workflow, so every run was being refused. The fix is ported from swamp's own fix (swamp-club#2815). The same keys anywhere below the top level, and any other unexpected top-level key, are still refused. ## Verification - verify-build `413d4c8d` and verify-reviews `248160ae` both passed on db96806089d9 (13/14 steps, idempotency skipped). - All three reviews passed. - Attestation `2314d32c` is posted. Known low-severity notes from the reviews, not addressed here: - Linear's 250-entry read is shared across blocks, duplicate and related links, not separate per kind. - A Linear `related` link made from the other ticket isn't detected, so relating the other way adds a second link. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
The tracker contract gains relate and unrelate, and a fetched ticket carries
its relations with a direction. The Lab's rules (one parent per child, no
parent cycle, one canonical per duplicate, no duplicate chain) are checked in
shared code (core/relations.ts) before every write, so the built-in tracker,
the swamp-club Lab and Linear refuse the same relations, though Linear keeps
no rules of its own. relate and unrelate are tracker methods with the
delivery ledger, keyed per relation so one journal version can relate
several tickets.

- built-in: a relation lives on both tickets' records, the subject's side
  written first and removed last
- Lab: the root client's relationship calls, ported into the fork; GET reads
  relationships, DELETE's empty 204 is accepted
- Linear: parent field, and blocks/duplicate/related links (blocked_by is
  blocks read the other way)
- conformance covers relate, unrelate, read-back, every refusal and the
  ledger for all three backends against their fakes
- DESIGN.md records that the tracker owns relations; README and the skill
  show relate and unrelate

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Since swamp-club#2171, swamp writes sensitiveFormat (and writtenReferences,
when a run records any) at the root of every run's evaluated workflow.
checkWorkflowProvenance refused them as fields the committed definition
lacks, so no run from a current binary could be attested. They describe how
swamp wrote the snapshot, not what the run executed: set them aside at the
root only, as swamp's own build_attestation.ts does (swamp-club#2815).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
fix(gatorwalk-factory): relations survive Lab entries they cannot read, long child lists and half writes (swamp-club #2796)
All checks were successful
CI / Validate Attestation (pull_request) Successful in 1m3s
CI / Review Integrity (pull_request) Successful in 1m38s
db96806089
From the verification reviews:

- Lab: getIssue skips a relationship it cannot read, as issue-lifecycle's
  client does, so set_status, claim and publish never fail on one.
- A relation exists when either ticket shows it, so Linear's 250-children
  page cannot hide a parent; unrelate reads both ends.
- built-in: finishing a half-written relation runs the rules again, so it
  cannot add a second parent related in between.
- The ledger key for a relation names its source too, so one key can relate
  two tickets to the same target.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
seth merged commit 5368cb002b into main 2026-10-01 02:49:37 +00:00
seth deleted branch cue/2796-gatorwalk-factory-parent 2026-10-01 02:49:38 +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!403
No description provided.