This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit fd352cac0bbab0c01387aed867b25d7aec5628d8
Author: Stephen Mallette <[email protected]>
AuthorDate: Tue Oct 13 11:08:25 2026 -0400

    Use relates-to instead of related for beads links
    
    Assisted-by: Claude Code:claude-opus-5-5
---
 .beads/PRIME.md                                  | 10 ++++++----
 .skills/tinker-review/references/schema.md       |  4 ++--
 .skills/tinker-review/scripts/discovery/beads.js | 10 +++++-----
 .skills/tinker-review/scripts/enrichment/api.js  |  4 ++--
 .skills/tinker-review/scripts/renderer/render.js |  4 ++--
 .skills/tinker-review/test/beads.test.js         | 10 +++++-----
 bin/beads-commits.py                             |  2 +-
 7 files changed, 23 insertions(+), 21 deletions(-)

diff --git a/.beads/PRIME.md b/.beads/PRIME.md
index c707737c87..7ff9cda6ab 100644
--- a/.beads/PRIME.md
+++ b/.beads/PRIME.md
@@ -60,7 +60,7 @@ bd create --type=decision --parent=<root> --title="Chose X" 
--metadata '{"reject
 # An approach you tried and abandoned is the strongest sibling — someone 
already walked it.
 bd create --type=decision --parent=<root> --title="Y" --metadata 
'{"rejected":true}' \
           --description="what Y concretely was, why it lost, what settled it"
-bd dep add <decision> <alternative> -t related    # never put either of these 
on a task bead
+bd dep add <decision> <alternative> -t relates-to # never put either of these 
on a task bead
 bd close <decision> <alternative>   # both: a decision is resolved the moment 
you write it
 ```
 
@@ -247,7 +247,7 @@ root (feature/epic/task)
   ├─parent-child─▶ record           apache/tinkerpop#2891
   ├─parent-child─▶ record           apache/tinkerpop@374b0c76d0   (written at 
merge)
   ├─parent-child─▶ decision  "chose X"            {rejected: false}
-  │                  └─related─▶ decision "Y"     {rejected: true}
+  │                  └─relates-to─▶ decision "Y"  {rejected: true}
   ├─parent-child─▶ task A "implement X" ──caused-by──▶ decision "chose X"
   ├─parent-child─▶ task B ──blocks──▶ task A     (B waits for A)
   └─parent-child─▶ task C                        (no blocker: starts with A)
@@ -275,7 +275,7 @@ a list, and `bd ready` cannot tell you anything useful 
about it.
   and `bd search <ref> --status all` finds a ticket id but not a URL. For any 
ref,
   `bd search tp --external-contains <ref> --status all` works — `tp` matches 
every bead id.
   The PR record is written at merge (section 4), not by hand. When a second 
root needs an
-  artifact that already has a record, add `bd dep add <root> <record> -t 
related` rather than
+  artifact that already has a record, add `bd dep add <root> <record> -t 
relates-to` rather than
   a duplicate. `--suggest` on that root will not see it, so supply those 
commits by hand at
   merge.
 - Records are the only link between beads and code; commit messages carry no 
bead ID. The
@@ -283,6 +283,8 @@ a list, and `bd ready` cannot tell you anything useful 
about it.
   `bd query 'notes="<sha>"' --all` returns the record and its `parent`, for 
any commit in the
   landing. Quote the sha: an unquoted one starting with a digit is lexed as a 
number and the
   query fails to parse.
+- **Write `relates-to` with `bd dep add -t relates-to`, never `bd dep 
relate`** — `relate` writes
+  the edge in both directions, and every consumer reads one row per link.
 - Only **one dependency type per pair** — `blocks` and `discovered-from` 
cannot coexist
   between the same two beads.
 - Never construct a bead ID; use whatever `bd create` returns. Child IDs 
encode birth
@@ -357,7 +359,7 @@ bd query "metadata.rejected=true" --all     # roads not 
taken; --all or closed b
 bd comment <id> "..."            # a fact with no fork in it (never on a task 
bead)
 bd create --type=... --parent=<root> --description=... --labels=...
 bd dep add <task> <blocker>      # default type is blocks: <task> waits for 
<blocker>
-bd dep add <a> <b> -t caused-by|related|discovered-from|supersedes
+bd dep add <a> <b> -t caused-by|relates-to|discovered-from|supersedes
 bd dep cycles                    # a plan with a cycle cannot execute
 bd update <id> --claim | -s pinned | --external-ref=TINKERPOP-NNNN
 bd search <text>
diff --git a/.skills/tinker-review/references/schema.md 
b/.skills/tinker-review/references/schema.md
index 5fdb3f0f75..08506be14f 100644
--- a/.skills/tinker-review/references/schema.md
+++ b/.skills/tinker-review/references/schema.md
@@ -123,7 +123,7 @@ records. `labels` is the comma-joined label list.
 
 `rootRole` is set only on roots: `primary` — the root of this PR's own record
 (`apache/tinkerpop#N`); `owner` — the root a matched JIRA/dev@/proposal record
-belongs to; `shared` — a root that links a matched record with `related`
+belongs to; `shared` — a root that links a matched record with `relates_to`
 (connected work). Query the PR's own work with `.has("rootRole", "primary")`
 and a root's subtree with `.has("root", id)`.
 
@@ -213,4 +213,4 @@ All `EXTRACTED` except `governs`: each is a fact in the 
beads database.
 | `child_of` | Bead | Bead | bd's parent-child tree: the bead belongs to its 
parent |
 | `records` | Bead (record) | Discussion | The record's external ref names 
this Discussion (the PR, a JIRA, a dev@ thread, a proposal). Only drawn to 
Discussions discovery created. |
 | `governs` | Bead | Function, Type or File | The bead — usually a decision — 
is about this code. Written in Phase 2 by `linkBead`, so it is `INFERRED` by 
default, unlike the rest of this table. |
-| `blocks`, `caused_by`, `related`, `supersedes`, `discovered_from`, … | Bead 
| Bead | bd's other dependency types, carried over with `-` written as `_`. 
`related` pairs a decision with the alternative it was chosen over; `caused_by` 
runs from a task to the decision that shaped it; `supersedes` from a newer 
decision to the one it replaces. Drawn only between loaded beads. |
+| `blocks`, `caused_by`, `relates_to`, `supersedes`, `discovered_from`, … | 
Bead | Bead | bd's other dependency types, carried over with `-` written as 
`_`. `relates_to` pairs a decision with the alternative it was chosen over; 
`caused_by` runs from a task to the decision that shaped it; `supersedes` from 
a newer decision to the one it replaces. Drawn only between loaded beads. |
diff --git a/.skills/tinker-review/scripts/discovery/beads.js 
b/.skills/tinker-review/scripts/discovery/beads.js
index 0e7eee2a1d..059e958725 100644
--- a/.skills/tinker-review/scripts/discovery/beads.js
+++ b/.skills/tinker-review/scripts/discovery/beads.js
@@ -22,7 +22,7 @@
 // puts every external artifact — JIRA, PR, dev@ thread, proposal — on a 
`record`
 // bead that is a parent-child child of its root, and only records carry an
 // external ref. A second root that needs the same artifact links to the 
existing
-// record with a `related` edge. So a record whose ref names one of the PR's
+// record with a `relates-to` edge. So a record whose ref names one of the PR's
 // artifacts leads to the owning root and any sharing roots, and everything
 // reached that way is EXTRACTED.
 //
@@ -56,7 +56,7 @@ export const BEADS_REQUIRED_MESSAGE =
  * @property {string|null} closedAt
  * @property {string} root - Id of the root this bead belongs to.
  * @property {{ to: string, type: string }[]} edges - Outgoing bd dependencies 
other than
- *   parent-child (`blocks`, `caused-by`, `related`, `supersedes`, 
`discovered-from`, ...).
+ *   parent-child (`blocks`, `caused-by`, `relates-to`, `supersedes`, 
`discovered-from`, ...).
  */
 
 /**
@@ -65,7 +65,7 @@ export const BEADS_REQUIRED_MESSAGE =
  * @property {string} id - Root bead id.
  * @property {"primary"|"owner"|"shared"} role - `primary`: the root of this 
PR's own record
  *   (`apache/tinkerpop#N`), the work the PR delivers. `owner`: the root a 
matched record belongs
- *   to. `shared`: a root that links a matched record with `related` — 
connected work elsewhere.
+ *   to. `shared`: a root that links a matched record with `relates-to` — 
connected work elsewhere.
  * @property {string[]} via - Ids of the matched records that reached this 
root.
  */
 
@@ -186,7 +186,7 @@ function toBead(raw) {
  *    is also applied, as a title/ID match, so it is the project's ID prefix — 
which
  *    every bead matches — rather than the ref, which a record's title need 
not repeat.
  * 2. Each matched record's top-most ancestor is its owner root; every bead 
that
- *    links the record with `related` leads to a shared root. A PR record's 
owner
+ *    links the record with `relates-to` leads to a shared root. A PR record's 
owner
  *    root is primary. Every root reached is loaded — connected work is 
context.
  * 3. Load each root's subtree. `bd children --json` returns one level only 
(its
  *    text output is the tree), so the candidates come from
@@ -248,7 +248,7 @@ export async function discoverBeads({ seeds, prRef, run }) {
 
   for (const { seed, record } of result.matches) {
     reach(await topOf(record), seed === prRef ? "primary" : "owner", record);
-    const sharers = (await run(["dep", "list", record, "--direction", "up", 
"-t", "related"])) || [];
+    const sharers = (await run(["dep", "list", record, "--direction", "up", 
"-t", "relates-to"])) || [];
     for (const s of sharers) reach(await topOf(s.id), "shared", record);
   }
   result.roots = [...roots.values()].sort((a, b) => RANK[a.role] - 
RANK[b.role]);
diff --git a/.skills/tinker-review/scripts/enrichment/api.js 
b/.skills/tinker-review/scripts/enrichment/api.js
index fc0dc14630..cd044cad1f 100644
--- a/.skills/tinker-review/scripts/enrichment/api.js
+++ b/.skills/tinker-review/scripts/enrichment/api.js
@@ -185,7 +185,7 @@ export async function listExternalRefs(g) {
  * and the alternatives they turned down — so the reviewer can read the change
  * against them. Roots come first (primary, then owner, then shared), then each
  * root's decisions, tasks and records. A decision lists the rejected
- * alternatives it was chosen over (`related` to a rejected decision) and any
+ * alternatives it was chosen over (a `relates-to` edge to a rejected 
decision) and any
  * decision that supersedes it; judge the code against the newest decision in a
  * supersedes chain. `governs` lists code already linked with `linkBead`.
  * Empty when no record named the PR or its discussions.
@@ -204,7 +204,7 @@ export async function listBeads(g, filter = {}) {
   if (filter.status) t = t.has("status", filter.status);
   const rows = await t.project("bead", "alternatives", "supersededBy", 
"governs")
     .by(__.valueMap())
-    .by(__.both("related").has("rejected", true).values("beadId").fold())
+    .by(__.both("relates_to").has("rejected", true).values("beadId").fold())
     .by(__.in_("supersedes").values("beadId").fold())
     .by(__.out("governs").coalesce(__.values("path"), 
__.values("name")).fold())
     .toList();
diff --git a/.skills/tinker-review/scripts/renderer/render.js 
b/.skills/tinker-review/scripts/renderer/render.js
index 012af4ef87..a986db4879 100644
--- a/.skills/tinker-review/scripts/renderer/render.js
+++ b/.skills/tinker-review/scripts/renderer/render.js
@@ -250,12 +250,12 @@ function renderDecisions(decisions, beadsById) {
   for (const d of decisions) {
     for (const e of d.edges || []) if (e.type === "supersedes") 
supersededBy.set(e.to, d.id);
   }
-  // A chosen decision and its rejected alternatives are joined by `related`, 
in either direction.
+  // A chosen decision and its rejected alternatives are joined by 
`relates-to`, in either direction.
   const rejectedOf = new Map();
   const paired = new Set();
   for (const d of decisions) {
     for (const e of d.edges || []) {
-      if (e.type !== "related") continue;
+      if (e.type !== "relates-to") continue;
       const other = beadsById.get(e.to);
       if (!other || other.type !== "decision") continue;
       const [chosen, alt] = d.rejected === false && other.rejected === true ? 
[d, other]
diff --git a/.skills/tinker-review/test/beads.test.js 
b/.skills/tinker-review/test/beads.test.js
index 6e3094c7ac..086a07da26 100644
--- a/.skills/tinker-review/test/beads.test.js
+++ b/.skills/tinker-review/test/beads.test.js
@@ -48,17 +48,17 @@ const DB = [
   bead("tp-a", "feature", { comments: 2 }),
   bead("tp-a.1", "record", { parent: "tp-a", ref: "TINKERPOP-1234", title: "PR 
and JIRA" }),
   bead("tp-a.2", "record", { parent: "tp-a", ref: 
"https://lists.apache.org/thread/abc";, title: "dev@ thread: topic" }),
-  bead("tp-a.3", "decision", { parent: "tp-a", rejected: false, deps: 
[["tp-a.4", "related"]], comments: 1 }),
+  bead("tp-a.3", "decision", { parent: "tp-a", rejected: false, deps: 
[["tp-a.4", "relates-to"]], comments: 1 }),
   bead("tp-a.4", "decision", { parent: "tp-a", rejected: true }),
   bead("tp-a.5", "task", { parent: "tp-a", deps: [["tp-a.3", "caused-by"]] }),
   bead("tp-a.5.1", "task", { parent: "tp-a.5" }),
   // A second root that shares tp-a's JIRA record instead of duplicating it.
-  bead("tp-b", "epic", { deps: [["tp-a.1", "related"]] }),
+  bead("tp-b", "epic", { deps: [["tp-a.1", "relates-to"]] }),
   bead("tp-b.1", "record", { parent: "tp-b", ref: "apache/tinkerpop#99" }),
   bead("tp-b.2", "decision", { parent: "tp-b", rejected: false }),
   // Unrelated work that relates to a bead inside tp-a: reachable by bd dep 
tree
   // from tp-a, but not part of tp-a's subtree.
-  bead("tp-c", "task", { deps: [["tp-a.3", "related"]] }),
+  bead("tp-c", "task", { deps: [["tp-a.3", "relates-to"]] }),
   // Substring trap for --external-contains.
   bead("tp-d", "feature"),
   bead("tp-d.1", "record", { parent: "tp-d", ref: "TINKERPOP-12345" }),
@@ -172,7 +172,7 @@ test("beads carry the rejected flag, external ref and 
non-tree edges", async ()
   assert.equal(get("tp-a.4").rejected, true);
   assert.equal(get("tp-a.5").rejected, null, "only decisions have a rejected 
flag");
   assert.equal(get("tp-a.1").externalRef, "TINKERPOP-1234");
-  assert.deepEqual(get("tp-a.3").edges, [{ to: "tp-a.4", type: "related" }]);
+  assert.deepEqual(get("tp-a.3").edges, [{ to: "tp-a.4", type: "relates-to" 
}]);
   assert.deepEqual(get("tp-a.5").edges, [{ to: "tp-a.3", type: "caused-by" }]);
   assert.equal(get("tp-a.5.1").parent, "tp-a.5");
 });
@@ -230,7 +230,7 @@ const found = {
   beads: [
     shaped("tp-a", "feature", { status: "open", labels: ["gremlin-core", 
"3.7.7"] }),
     shaped("tp-a.1", "record", { parent: "tp-a", externalRef: 
"apache/tinkerpop#99" }),
-    shaped("tp-a.2", "decision", { parent: "tp-a", edges: [{ to: "tp-a.3", 
type: "related" }] }),
+    shaped("tp-a.2", "decision", { parent: "tp-a", edges: [{ to: "tp-a.3", 
type: "relates-to" }] }),
     shaped("tp-a.3", "decision", { parent: "tp-a", rejected: true }),
     shaped("tp-a.4", "decision", { parent: "tp-a", edges: [{ to: "tp-a.2", 
type: "supersedes" }] }),
     shaped("tp-a.5", "task", { parent: "tp-a", status: "in_progress" }),
diff --git a/bin/beads-commits.py b/bin/beads-commits.py
index d74684714e..a7c231e205 100755
--- a/bin/beads-commits.py
+++ b/bin/beads-commits.py
@@ -186,7 +186,7 @@ def record_pr(root, ref, dry_run):
         elif dry_run:
             print(f"--dry-run: would relate {root} to {bead['id']} ({ref})")
         else:
-            bd("dep", "add", root, bead["id"], "-t", "related")
+            bd("dep", "add", root, bead["id"], "-t", "relates-to")
             print(f"related {root} to {bead['id']}, which records {ref} under 
{bead.get('parent')}")
         return
     if dry_run:

Reply via email to