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 635830881dcb16559a6bd0dac1966e75eb123dc9 Author: Stephen Mallette <[email protected]> AuthorDate: Sun Aug 9 08:26:01 2026 -0400 Add the tinker-dev and tinker-doc Agent Skills to 3.7-dev Backports the skills and bin/agent-setup.sh from master, retargeted to this branch: top-level gremlin-javascript and gremlint rather than a gremlin-js workspace, no gremlin-mcp, neo4j-gremlin and sparql-gremlin in the validation rules, and Java 8/11 in place of 17. The skill now also states the non-breaking constraint that governs a 3.7.x change. Assisted-by: Claude Code:claude-opus-5 --- .skills/tinker-dev/DESIGN.md | 38 +++ .skills/tinker-dev/SKILL.md | 164 ++++++++++ .../tinker-dev/references/dev-environment-setup.md | 123 +++++++ .skills/tinker-dev/scripts/check-env.sh | 160 +++++++++ .skills/tinker-doc/DESIGN.md | 33 ++ .skills/tinker-doc/SKILL.md | 141 ++++++++ .../tinker-doc/references/asciidoc-and-wiring.md | 175 ++++++++++ .skills/tinker-doc/references/books-and-voice.md | 363 +++++++++++++++++++++ .skills/tinker-doc/references/executable-blocks.md | 91 ++++++ bin/agent-setup.sh | 341 +++++++++++++++++++ 10 files changed, 1629 insertions(+) diff --git a/.skills/tinker-dev/DESIGN.md b/.skills/tinker-dev/DESIGN.md new file mode 100644 index 0000000000..4ad70ab7e3 --- /dev/null +++ b/.skills/tinker-dev/DESIGN.md @@ -0,0 +1,38 @@ +# tinker-dev — Design + +How this skill is structured and how to change it. The development guidance +itself — build recipes, the validation matrix, conventions, beads rules — is the +skill's *content* and lives in `SKILL.md` and `references/`. This document is only +the shape of the artifact and where new content goes. + +## Structure + +A guidance skill, not a program: + +| Path | Role | +|------|------| +| `SKILL.md` | the operational index — the guidance a reader needs up front | +| `references/*.md` | deep, task-specific material, loaded on demand | +| `scripts/check-env.sh` | environment preflight | + +It carries no logic of its own; its effect is what a reader does after reading it. + +## How to change it — where content goes + +Two rules govern the shape, so every change is a routing decision: + +- **Single source of truth — defer, don't duplicate.** General agent rules live + in the root `AGENTS.md`; canonical facts live in the repo (`CONTRIBUTING.md`, + `docs/src/`, `bin/asf-license-header.txt`). New guidance points at its source + and repeats only the TinkerPop-specific thing that is easy to miss. A fact + copied here drifts from its source — so it isn't. +- **Progressive disclosure.** `SKILL.md` stays a lean index; anything deep or + task-specific becomes a `references/` file linked from it, never inlined. + +Applying them: + +- A **general** agent rule → root `AGENTS.md`, not here. +- A **TinkerPop-specific, easy-to-miss** convention → a bullet in `SKILL.md`, + deferring to the canonical file. +- A **new validation rule** → the Definition-of-Done matrix in `SKILL.md`. +- A **new deep topic** → a `references/` file, linked from `SKILL.md`. diff --git a/.skills/tinker-dev/SKILL.md b/.skills/tinker-dev/SKILL.md new file mode 100644 index 0000000000..df4db69999 --- /dev/null +++ b/.skills/tinker-dev/SKILL.md @@ -0,0 +1,164 @@ +--- +name: tinker-dev +description: > + Development guidance for the Apache TinkerPop monorepo. Use when building, + testing, or contributing to TinkerPop's graph computing framework and its + multi-language Gremlin ecosystem (Java, Python, JavaScript, .NET, Go). + Covers coding conventions, build recipes, test evaluation, documentation, + and development environment setup. +license: Apache-2.0 +compatibility: Requires Java 8 or 11 (build with 11, cross-compiled to 8), Maven 3.5.3+, Docker. Individual GLVs may need Python, Node.js, .NET SDK, or Go. +metadata: + version: 1.0.0 + project: Apache TinkerPop +--- + +# TinkerPop Development Skill + +## Project Overview + +Apache TinkerPop is a graph computing framework providing a standard API (the Gremlin graph +traversal language) for graph databases and processors. The repository is a Maven multi-module +monorepo that wraps JVM code, Python, JavaScript/TypeScript, .NET, and Go under a single build. + +Canonical project documentation (prefer local files over external URLs): + +- `README.md` and `CONTRIBUTING.md` at the repo root +- Reference docs: `docs/src/reference/` +- Developer docs: `docs/src/dev/developer/` +- Provider docs and Gremlin Semantics: `docs/src/dev/provider/` +- IO and Serialization: `docs/src/dev/io/` +- Recipes: `docs/src/recipes/` +- Upgrade docs: `docs/src/upgrade/` +- Future plans: `docs/src/dev/future/` + +## Definition of Done + +A change is **not done** until a full Maven validation has passed locally. Run it before +presenting work for review — even when your targeted or unit tests already pass, and even if you +judge a full run unnecessary. Incremental testing during development is encouraged, but it does +**not** satisfy this gate. + +Validation is two steps. First, rebuild and install the whole reactor so every module picks up +your changes: + +```bash +mvn clean install -DskipTests +``` + +Then run `verify` scoped to what you changed, using the **broadest** rule that applies, and +always list every module you touched. + +**Self-contained modules** — changing one of these only requires validating that module: + +| Changed module | Validate command | +|---|---| +| Python GLV | `mvn verify -pl gremlin-python` | +| JavaScript GLV | `mvn verify -pl :gremlin-javascript` | +| .NET GLV | `mvn verify -pl :gremlin-dotnet,:gremlin-dotnet-source,:gremlin-dotnet-tests` | +| Go GLV | `mvn verify -pl :gremlin-go` | +| `gremlint` | `mvn verify -pl :gremlint` | +| Other single JVM module (e.g. `tinkergraph-gremlin`, `gremlin-console`, `neo4j-gremlin`, `sparql-gremlin`, `gremlin-archetype`) | `mvn verify -pl <module> -DskipIntegrationTests=false` | + +**Shared modules** — depended on by others, so changing them means validating the consumers too. +Use the broadest rule that matches: + +- **`gremlin-server`, `gremlin-driver`, or `gremlin-util`** define the wire protocol and + serialization that every GLV exercises → validate the changed module(s) **plus all GLVs**: + `mvn verify -pl <changed>,gremlin-python,:gremlin-javascript,:gremlin-dotnet,:gremlin-dotnet-source,:gremlin-dotnet-tests,:gremlin-go -DskipIntegrationTests=false` +- **`gremlin-core`, `gremlin-test`, or anything they depend on** ripple across the whole project, + including the OLAP engines (`hadoop-gremlin`, `spark-gremlin`) and the graph implementations + (`neo4j-gremlin`, `sparql-gremlin`) → run everything: + `mvn clean install -DskipIntegrationTests=false` +- **Unsure?** Treat it as the `gremlin-core` case and run everything. + +**Docs or code comments only** — no validation gate; this gate is for changes that affect behavior. + +### Things that quietly invalidate a run + +- **GLV tests are skipped unless activated.** A GLV is built and tested only when its `.glv` + sentinel exists: `gremlin-python/.glv`, `gremlin-go/.glv`, and *both* `gremlin-dotnet/src/.glv` + and `gremlin-dotnet/test/.glv` (JavaScript builds by default). Without the sentinel the module + is silently skipped — a green run may have tested nothing of your change. Create the sentinel + (or pass `-Pglv-python` / `-Pglv-go`) before validating a GLV change. +- **Grammar and feature-test changes need a reactor build to take effect.** The ANTLR grammar + (`gremlin-language/src/main/antlr4/Gremlin.g4`) and the Gherkin features under `gremlin-test` + drive code generation during `mvn clean install` — ANTLR parsers, and per-language test code + generated from the feature corpus (e.g. `gremlin-python/build/generate.groovy`, with + equivalents for Go, .NET, and JavaScript). After changing either, run `mvn clean install + -DskipTests` before relying on *any* test, including fast native ones, or you will be testing + stale generated code. +- **Judge pass/fail by the Maven exit code, not console text.** Do not `grep`/`tail` the build + output — tests deliberately emit expected errors, so the log misleads. Exit `0` = pass. For + Python, detailed per-suite results are in `gremlin-python/target/python3/python-reports/*.xml`. + +If you cannot run the validation (for example, Docker is unavailable), say so explicitly and +report the change as **not validated** — do not present it as done. + +## Repository Structure + +Most module names map directly to their purpose (`gremlin-core`, `gremlin-server`, +`gremlin-go`, etc.). Two things that aren't obvious: + +- **Every module is top-level**, including the JavaScript ones: `gremlin-javascript/` (the + `gremlin` npm driver) and `gremlint/` sit directly under the repository root. Later branches + group them under a `gremlin-js/` workspace, so a path from `master` will not resolve here. +- **Maven orchestrates the build for every module, including the non-JVM ones** (Python, + JavaScript, .NET, Go) — which is why the validation in the Definition of Done runs through + Maven rather than each language's native test runner. + +This branch also carries `neo4j-gremlin` and `sparql-gremlin` (graph implementations) and +`gremlin-archetype` (Maven archetypes for new projects), none of which exist on `master`. + +Cross-language Gherkin feature tests live in `gremlin-test/`. + +## Basic Build Commands + +Build everything: +```bash +mvn clean install +``` + +Build a specific module: +```bash +mvn clean install -pl <module-name> +``` + +For GLV-specific builds and the validation commands per module, see the **Definition of Done** +table above. For environment setup and GLV activation, see `references/dev-environment-setup.md`. + +## Conventions + +The general Do/Don't rules and "when in doubt" guidance live in the root `AGENTS.md` — the +single source of truth, not repeated here. The conventions below are the TinkerPop-specific +ones that are easy to miss: + +- **This is a maintenance branch.** `3.7-dev` takes non-breaking bug fixes and enhancements + only. Within the 3.7.x line a change must not alter existing behavior, introduce new APIs, + change serialization formats, or modify protocols — a client of one 3.7.x version must still + interact correctly with a server of another. Anything that fails that test belongs on a later + branch. See `docs/src/dev/developer/for-committers.asciidoc`. +- **License header**: every new file needs the ASF header. Canonical text: `bin/asf-license-header.txt`. +- **Test naming**: a test using a `GraphProvider` with `AbstractGremlinTest` is suffixed + `Check`, not `Test`. +- **Gremlin language tests**: cross-language behavior is tested with Gherkin features under + `gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/` + (see `docs/src/dev/developer/for-committers.asciidoc`). +- **Docs are AsciiDoc** under `docs/src/` — never Markdown in the main docs tree. For writing or + revising any documentation (voice, per-book style, executable code blocks, AsciiDoc wiring), + use the **tinker-doc** skill. The mechanical minimum: add new content to the right book and update + its `index.asciidoc`. +- **Changelog**: for user-visible or API changes, add a concise `CHANGELOG.asciidoc` entry in the + correct version section. Do not invent version numbers or release names. See the **tinker-doc** + skill for changelog discipline (one entry per user-facing change, not per commit) and voice. + +Otherwise, match the existing code in neighboring files — explicit imports (no wildcards), +`final` where practical, SLF4J logging over `println`, Hamcrest matchers, and closing any +`Graph` you construct in a test. + +## Reference Guides + +Build and validate commands live in the **Definition of Done** table above. For the remaining +task-specific guidance, see: + +- [Development Environment Setup](references/dev-environment-setup.md) — fresh clone to working environment, prerequisites, GLV activation diff --git a/.skills/tinker-dev/references/dev-environment-setup.md b/.skills/tinker-dev/references/dev-environment-setup.md new file mode 100644 index 0000000000..f94935b6b5 --- /dev/null +++ b/.skills/tinker-dev/references/dev-environment-setup.md @@ -0,0 +1,123 @@ +# Development Environment Setup + +This guide walks through setting up a TinkerPop development environment from a fresh clone. +The canonical reference is `docs/src/dev/developer/development-environment.asciidoc`. + +## Prerequisites + +### Minimum (Java-only build) + +- **Java 8 or 11** — OpenJDK recommended, with Java 11 preferred: the build cross-compiles to + Java 8 bytecode. Use [SDKMAN!](https://sdkman.io/) to manage versions. Java 17 also compiles, + but deep reflection needs the `--add-opens` JVM options already configured in the pom files. + Use Java 11 for documentation generation with `bin/process-docs.sh`. +- **Maven 3.5.3+** — also manageable via SDKMAN. + +With just Java and Maven you can build JVM modules and get a clean `mvn clean install`, but +GLV builds and many integration tests will be skipped. + +### Full environment (all GLVs and integration tests) + +- **Docker and Docker Compose** — required for all GLV test execution. Docker Desktop includes + both. GLV tests run inside Docker containers, so local language runtimes are optional for + test execution via Maven. +- **Python 3.9–3.13** — optional for local development; Docker handles test execution. +- **Node.js 20+ / npm 10+** — optional; Maven downloads a local copy via `frontend-maven-plugin` + (`node.version` and `npm.version` in the root `pom.xml` pin the exact versions used by the + build). Consider [nvm](https://github.com/nvm-sh/nvm) for version management. +- **.NET SDK 6.0+** — optional; Docker handles test execution. `Gremlin.Net` targets + `netstandard2.0` and `net6.0`. +- **Go 1.25+** — optional; Docker handles test execution. + +## Environment Verification + +Run the included check script to verify your setup: + +```bash +bash .skills/tinker-dev/scripts/check-env.sh +``` + +This checks for Java, Maven, Docker, and optionally Python, Node.js, .NET, and Go. + +## First Build + +After cloning, verify the basic build works: + +```bash +mvn clean install -DskipTests +``` + +This builds all JVM modules. GLV modules will be skipped unless activated (see below). + +## Activating GLV Builds + +Each Gremlin Language Variant uses a `.glv` sentinel file to signal Maven that the GLV +should be built as part of a standard `mvn clean install`. The file can be empty. + +### Python +```bash +touch gremlin-python/.glv +``` + +### JavaScript / TypeScript +No `.glv` file needed — JS modules build by default via the `frontend-maven-plugin`. +Tests require Docker and are skipped with `-DskipTests`. + +### .NET +Both `src` and `test` directories need the sentinel: +```bash +touch gremlin-dotnet/src/.glv +touch gremlin-dotnet/test/.glv +``` + +### Go +```bash +touch gremlin-go/.glv +``` + +Once `.glv` files are in place, a standard `mvn clean install` will include those GLVs. +The `.glv` files are gitignored. + +Alternatively, activate GLV builds explicitly with Maven profiles without `.glv` files: +- Python: `mvn clean install -Pglv-python -pl :gremlin-python` +- .NET: `mvn clean install -Pgremlin-dotnet -pl :gremlin-dotnet,:gremlin-dotnet-source,:gremlin-dotnet-tests` +- Go: `mvn clean install -Pglv-go -pl :gremlin-go` + +## Groovy / Ivy Configuration + +The Gremlin Console is Groovy-based. For documentation generation or console testing with +SNAPSHOT dependencies, configure `~/.groovy/grapeConfig.xml`: + +```xml +<ivysettings> + <settings defaultResolver="downloadGrapes"/> + <resolvers> + <chain name="downloadGrapes"> + <ibiblio name="local" root="file:${user.home}/.m2/repository/" m2compatible="true"/> + <ibiblio name="central" root="https://repo1.maven.org/maven2/" m2compatible="true"/> + </chain> + </resolvers> +</ivysettings> +``` + +## Docker Permissions (Linux) + +If you encounter "Permission denied" errors: + +```bash +sudo groupadd docker +sudo usermod -aG docker $USER +newgrp docker +sudo chmod 666 /var/run/docker.sock +``` + +## IDE Setup + +TinkerPop does not mandate a specific IDE. Import the root `pom.xml` as a Maven project. +For AI coding agent configuration, see the "Using AI Coding Agents" section in +`docs/src/dev/developer/development-environment.asciidoc`. + +## Next Steps + +- See the **Definition of Done** table in `SKILL.md` for the build and validate commands per + module / GLV. diff --git a/.skills/tinker-dev/scripts/check-env.sh b/.skills/tinker-dev/scripts/check-env.sh new file mode 100755 index 0000000000..1be8ac2fe0 --- /dev/null +++ b/.skills/tinker-dev/scripts/check-env.sh @@ -0,0 +1,160 @@ +#!/usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Verify TinkerPop development environment prerequisites. +# Usage: bash .skills/tinker-dev/scripts/check-env.sh + +set -uo pipefail + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +NC='\033[0m' + +pass=0 +warn=0 +fail=0 + +ok() { echo -e " ${GREEN}✓${NC} $1"; pass=$((pass + 1)); } +skip() { echo -e " ${YELLOW}○${NC} $1"; warn=$((warn + 1)); } +bad() { echo -e " ${RED}✗${NC} $1"; fail=$((fail + 1)); } + +echo "TinkerPop Development Environment Check" +echo "========================================" +echo "" + +# --- Java --- +echo "Core prerequisites:" +if command -v java &>/dev/null; then + # Java 8 reports itself as "1.8.0_xxx", everything since as "11.0.2" or "17". + java_version=$(java -version 2>&1 | head -1 | sed 's/.*"\(.*\)".*/\1/') + case "$java_version" in + 1.*) java_version=$(echo "$java_version" | cut -d. -f2) ;; + *) java_version=$(echo "$java_version" | cut -d. -f1) ;; + esac + if [[ "$java_version" -eq 11 ]]; then + ok "Java $java_version (preferred for 3.7-dev)" + elif [[ "$java_version" -eq 8 ]]; then + ok "Java $java_version (supported; Java 11 is preferred and required for bin/process-docs.sh)" + elif [[ "$java_version" -gt 11 ]]; then + skip "Java $java_version found — 3.7-dev targets Java 8 and builds best on 11 (use sdkman.io to switch)" + else + bad "Java $java_version found — version 8 or 11 required (use sdkman.io to install)" + fi +else + bad "Java not found — install Java 8 or 11 (use sdkman.io)" +fi + +# --- Maven --- +if command -v mvn &>/dev/null; then + mvn_version=$(mvn --version 2>/dev/null | head -1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1) + ok "Maven $mvn_version (3.5.3+ required)" +else + bad "Maven not found — install Maven 3.5.3+ (try sdkman.io)" +fi + +# --- Docker --- +if command -v docker &>/dev/null; then + if docker info &>/dev/null; then + ok "Docker installed and running" + else + bad "Docker installed but not running — start Docker Desktop or the daemon" + fi +else + bad "Docker not found — install Docker Desktop (includes Compose)" +fi + +# --- Docker Compose --- +if docker compose version &>/dev/null 2>&1; then + ok "Docker Compose available" +elif command -v docker-compose &>/dev/null; then + ok "Docker Compose (standalone) available" +else + skip "Docker Compose not found — needed for GLV tests" +fi + +echo "" +echo "Optional (GLV development):" + +# --- Python --- +if command -v python3 &>/dev/null; then + py_version=$(python3 --version 2>&1 | grep -oE '[0-9]+\.[0-9]+') + ok "Python $py_version (3.9-3.13 supported for gremlin-python local dev)" +else + skip "Python 3 not found — Docker handles test execution, but local dev needs it" +fi + +# --- Node.js --- +if command -v node &>/dev/null; then + node_version=$(node --version 2>/dev/null | sed 's/v//') + ok "Node.js $node_version (20+ recommended for gremlin-javascript local dev)" +else + skip "Node.js not found — Maven downloads a local copy automatically" +fi + +# --- .NET SDK --- +if command -v dotnet &>/dev/null; then + dotnet_version=$(dotnet --version 2>/dev/null) + ok ".NET SDK $dotnet_version (6.0+ recommended for gremlin-dotnet local dev)" +else + skip ".NET SDK not found — Docker handles test execution" +fi + +# --- Go --- +if command -v go &>/dev/null; then + go_version=$(go version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+(\.[0-9]+)?') + ok "Go $go_version (1.25+ recommended for gremlin-go local dev)" +else + skip "Go not found — Docker handles test execution" +fi + +echo "" +echo "GLV activation status:" + +# --- .glv sentinel files --- +check_glv() { + local name="$1" + shift + local found=false + for f in "$@"; do + if [[ -f "$f" ]]; then + found=true + fi + done + if $found; then + ok "$name — activated (.glv present)" + else + skip "$name — not activated (create .glv to enable in standard builds)" + fi +} + +check_glv "gremlin-python" "gremlin-python/.glv" +check_glv "gremlin-dotnet" "gremlin-dotnet/src/.glv" "gremlin-dotnet/test/.glv" +check_glv "gremlin-go" "gremlin-go/.glv" + +echo "" +echo "----------------------------------------" +echo -e "Results: ${GREEN}$pass passed${NC}, ${YELLOW}$warn skipped${NC}, ${RED}$fail failed${NC}" + +if [[ $fail -gt 0 ]]; then + echo "" + echo "Fix the failed items above before building. See references/dev-environment-setup.md" + exit 1 +fi diff --git a/.skills/tinker-doc/DESIGN.md b/.skills/tinker-doc/DESIGN.md new file mode 100644 index 0000000000..b7fd2d679c --- /dev/null +++ b/.skills/tinker-doc/DESIGN.md @@ -0,0 +1,33 @@ +# tinker-doc — Design + +How this skill is structured and how to change it. The documentation guidance +itself — per-book voice, house style, the executable-block rules — is the skill's +*content* and lives in `SKILL.md` and `references/`. This document is only the +shape of the artifact and where new content goes. + +## Structure + +A guidance skill, not a program: + +| Path | Role | +|------|------| +| `SKILL.md` | the book map, house style, and the load-bearing rules up front | +| `references/*.md` | the voice guide and authoring mechanics, loaded on demand | + +## How to change it — where content goes + +Two rules govern the shape, so every change is a routing decision: + +- **Single source of truth — defer, don't duplicate.** Guidance that overlaps + development (build/validation, changelog discipline) defers to **tinker-dev** + rather than being restated. Canonical documentation lives under `docs/src/`. +- **Progressive disclosure.** `SKILL.md` holds the rule; the details live in a + `references/` file linked from it. + +Applying them: + +- A **new book or audience** → a row in the book map in `SKILL.md`, plus a voice + entry in `references/books-and-voice.md`. +- A **new authoring mechanic** → `references/executable-blocks.md` or + `references/asciidoc-and-wiring.md`; keep `SKILL.md` to the rule, not the how. +- A **new house-style rule** → the House style list in `SKILL.md`. diff --git a/.skills/tinker-doc/SKILL.md b/.skills/tinker-doc/SKILL.md new file mode 100644 index 0000000000..d09f918d8a --- /dev/null +++ b/.skills/tinker-doc/SKILL.md @@ -0,0 +1,141 @@ +--- +name: tinker-doc +description: > + Writing, editing and reviewing documentation for the Apache TinkerPop project. Use + when authoring or revising any content under docs/src/ — reference docs, recipes, + tutorials, upgrade/release notes, provider and developer guides — or the + CHANGELOG. Covers the TinkerPop documentation voice and the per-book style + expectations, the executable Gremlin code-block pipeline, and AsciiDoc + conventions. For building and validating code changes, see tinker-dev. +license: Apache-2.0 +compatibility: Documentation is AsciiDoc under docs/src/, never Markdown. +metadata: + version: 1.0.0 + project: Apache TinkerPop +--- + +# TinkerPop Documentation Skill + +This skill is about the **craft** of TinkerPop documentation: getting the voice, +audience, and shape of each kind of document right. The mechanics that keep the +build green (executable code blocks, AsciiDoc wiring) are covered in the +references and matter, but they serve the writing, not the other way around. + +All project documentation lives under `docs/src/` and is **AsciiDoc, never +Markdown**. It is organized into several books, each with its own audience and +purpose. The four directories under `docs/src/dev/` are themselves separate +books, not one: + +| Book | Path | Audience | Purpose | +|---|---|---|---| +| Reference | `docs/src/reference/` | Users | The complete, authoritative description of every feature and step | +| Recipes | `docs/src/recipes/` | Users | Reusable problem→solution patterns for real tasks | +| Tutorials | `docs/src/tutorials/` | Users (learning) | Story-driven walkthroughs of a focused topic | +| Upgrade | `docs/src/upgrade/` | Users (upgrading) | Announces what changed in a release and how to adapt | +| Provider | `docs/src/dev/provider/` | Graph providers | Implementation guidance, provider policies, and the canonical Gremlin semantics | +| IO | `docs/src/dev/io/` | Providers and advanced users | The serialization formats (GraphBinary, GraphSON, GraphML, Gryo) | +| Developer | `docs/src/dev/developer/` | Contributors | Contributing, development environment, committer and release process | +| Future | `docs/src/dev/future/` | Contributors | Design proposals for planned or possible changes | + +The full per-book voice guide, with grounded examples from the real docs, is in +[references/books-and-voice.md](references/books-and-voice.md). Read it before +writing in any book you have not written in before. The short version of each +book's character: + +- **Upgrade** announces. It tells a feature's story — what changed and why it + matters — and defers exhaustive detail to the reference docs via `See:` rather + than cataloging every edge case itself. A typical entry is short: a paragraph or + two and one example. It frames past shortcomings positively and treats breaking + changes as a priority. +- **Reference and Recipes** instruct. They are the focal point for correct + usage and established patterns: complete, user-friendly, and formal. +- **Tutorials** teach through a story but otherwise follow the Reference/Recipes + style. +- **Provider** documents internals in the Reference/Recipes style. The + `gremlin-semantics.asciidoc` file is special and load-bearing (see below). +- **IO** specifies the serialization formats in the same formal, complete style + as Provider. +- **Developer** docs are internal notes, instructions and standard processes for contributors. +- **Future** holds design proposals for changes under consideration. + +## House style (applies to every book) + +These rules hold across all documentation. Existing prose does not always obey +them; write to the standard, not to the weakest nearby example. + +1. **No em-dash or semicolon to break a sentence.** Prefer two separate + sentences, or find a transition word that lets the thought flow. (This is a + prose rule. Semicolons inside code, such as the `;[]` output-suppression + idiom, are unaffected.) +2. **Formal means no first or second person.** Do not address the reader as + "you" or speak as "I" or "we." Write about the subject, not about the author + or audience. "The `coalesce()`-step evaluates..." not "You can use + `coalesce()` to...". +3. **Prefer flowing paragraphs over lists.** Use a bulleted or numbered list + only when it itemizes something clearly quantifiable, such as the members of + an enumeration or a fixed set of options, each needing description. Do not use + a list for open-ended or subjective points (for example, "the benefits of a + traversal pattern"). Those belong in prose. +4. **Avoid hype and marketing language.** Describe what something does and why it + matters in plain, positive terms. Let the capability speak for itself. + +## Examples must be real and runnable + +TinkerPop documentation is unusual: most Gremlin examples are **executed at build +time** against a real graph, and their output is inlined automatically. A block +marked `[gremlin-groovy,modern]` is run against the modern toy graph and its +output is filled in for you. Never invent or hand-write the `==>` output of such +a block. + +The two graphs available to executable blocks are `modern` and `existing`. Use +a bare `[gremlin-groovy]` block for setup that should run without a graph. When +an example is purely illustrative and must *not* be executed (a hypothetical, an +error case, console session text), use `[source,text]` or `[source,groovy]` +instead. + +**Exception — upgrade documentation is always static.** Every example in +`docs/src/upgrade/` is a hand-written `[source,groovy]` or `[source,text]` block, +**never** an executable `[gremlin-groovy]` block, because an upgrade entry is a +snapshot of behavior at a fixed point in history (often an old-vs-new +comparison). This reverses the general rule above: in upgrade docs you **do** +write the `==>` output by hand. "Static" does not mean "no output" — a static +upgrade example should still show its results so the reader sees the payoff. If an +upgrade example is missing its output, the fix is to write the output in, **not** +to convert the block to executable. Do not be talked out of this by the rest of +the docs being executable, and do not assume the upgrade book contains executable +blocks — it does not. + +The authoring details — graph choice, output suppression, `<1>` callouts, and +the multi-language story — are in +[references/executable-blocks.md](references/executable-blocks.md). AsciiDoc +structure (anchors, cross-references, section-title length limits for the TOC, +the `x.y.z` version placeholder, per-book `index.asciidoc` wiring, admonitions, +images) is in +[references/asciidoc-and-wiring.md](references/asciidoc-and-wiring.md). + +## The semantics document is load-bearing + +`docs/src/dev/provider/gremlin-semantics.asciidoc` is the canonical description +of Gremlin's semantics. **It must be updated whenever Gremlin steps, the Gremlin +grammar (`Gremlin.g4`), or the semantics code change in any way.** When adding to +it, follow the patterns already established there for how a concept or step is +documented rather than inventing a new structure. See +[references/books-and-voice.md](references/books-and-voice.md#provider-documentation) +for what those patterns are. + +## Don't forget the changelog and upgrade docs + +Behavioral and API changes usually need more than reference updates: + +- **`CHANGELOG.asciidoc`** (repo root) gets a concise entry in the correct + version section. One short clause, written for what the user will see on + release day — the net user-facing change, from a usage perspective — not the + implementation detail. Do not invent version numbers or release names. + - The changelog is not a commit log. Not every commit earns an entry, and a + feature built over many commits should have **one** entry, not one per + increment. When extending an unreleased feature, fold the change into its + existing entry rather than appending a new line; if a change has no + user-visible effect, it needs no entry at all. +- **Upgrade docs** get an entry for user-visible features and especially for + breaking changes to public APIs or Gremlin semantics. A breaking change + without clear migration guidance in the upgrade docs is incomplete. diff --git a/.skills/tinker-doc/references/asciidoc-and-wiring.md b/.skills/tinker-doc/references/asciidoc-and-wiring.md new file mode 100644 index 0000000000..ae904cf37b --- /dev/null +++ b/.skills/tinker-doc/references/asciidoc-and-wiring.md @@ -0,0 +1,175 @@ +# AsciiDoc and Wiring + +TinkerPop documentation is AsciiDoc, never Markdown. This reference covers the +structural conventions an agent gets wrong most often. For voice see +`books-and-voice.md`; for runnable examples see `executable-blocks.md`. + +## Anchors and cross-references + +Define an anchor on its own line above the heading it names, then link to it from +anywhere with `<<anchor,display text>>`: + +``` +[[coalesce-step]] +=== Coalesce Step + +... see the <<where-step,where()-step>> for a related filter. +``` + +Give every section a stable, descriptive anchor and prefer cross-referencing an +existing section over restating its content. + +**`<<anchor>>` only resolves inside its own book.** Each book under `docs/src/` +builds as a separate document, so a cross-reference from one book to another does +not work even when the anchor exists. It also fails *silently* rather than +breaking the build: `<<subgraph-step>>` renders as the literal text +`[subgraph-step]`, and `<<gremlin-java-pdt,Gremlin-Java>>` renders as a dead +same-page link to a fragment the page does not contain. + +To point at another book, use a full `link:` to the published site instead: + +``` +link:https://tinkerpop.apache.org/docs/x.y.z/reference/#labels-step[labels()] +``` + +This matters most in the upgrade book, which is nearly always talking about +reference, provider, or IO material that lives elsewhere. Note that upgrade docs +pin a concrete version rather than `x.y.z` — see the exception below. + +## Section titles must fit the TOC + +Section titles that publish to the table of contents (the `====` level and above) +must not exceed 30 characters, or they wrap in the fixed-width TOC when published. + +## The `x.y.z` version placeholder + +Links into version-specific resources use the literal placeholder `x.y.z` in +place of a release number. The build substitutes the real version. This applies +to javadoc links, links into the published site, and GitHub source links: + +``` +link:https://tinkerpop.apache.org/docs/x.y.z/reference/#gremlin-console[Gremlin Console] +link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/.../GraphTraversal.html#coalesce(...)++[`coalesce(Traversal...)`] +``` + +Never hard-code a concrete version like `3.7.6` in these links. The `++...++` +double-plus wrapping is used to escape URLs that contain characters AsciiDoc would +otherwise interpret (such as the parentheses in a javadoc method signature). + +**Exception: upgrade documentation.** Links from the upgrade docs to other +versioned documentation are deliberately pinned to a concrete version (for +example `.../docs/3.7.4/reference/...`), not `x.y.z` and not `current`, because an +upgrade entry is a snapshot in time. This exception applies *only* to the upgrade +book. See +[books-and-voice.md](books-and-voice.md#upgrade-documentation) for the full +reasoning. + +If you need actually use `x.y.z` as a literal value, prefer `xx.yy.zz`. + +## Additional References blocks + +Reference step sections end with a pointer block under a bold label. It always +carries the javadoc link, and when the step has an entry in the Gremlin Semantics +documentation it also links there with the link text `` `Semantics` ``: + +``` +*Additional References* + +link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/.../GraphTraversal.html#conjoin(java.lang.String)++[`conjoin(String)`] +link:++https://tinkerpop.apache.org/docs/x.y.z/dev/provider/#conjoin-step++[`Semantics`] +``` + +The semantics link points to `dev/provider/#<step>-step` (the anchor in +`gremlin-semantics.asciidoc`), uses the `x.y.z` placeholder, and is wrapped in +`++...++`. Whenever a step's behavior is specified in the semantics document, +include this link so the reference and the specification stay connected. Match +this format when adding a new step so the reference stays uniform. + +## See blocks (upgrade documentation) + +Upgrade documentation has its own closing convention. An upgrade entry ends with a +`See:` line that points the reader to the sources where the change can be explored +in more depth. Where reference sections use `*Additional References*`, upgrade +entries use `See:`. + +A single pointer: + +``` +See: link:https://issues.apache.org/jira/browse/TINKERPOP-3225[TINKERPOP-3225] +``` + +Several pointers are comma-separated, each `link:` on its own line, all under one +`See:`: + +``` +See: link:https://issues.apache.org/jira/browse/TINKERPOP-2672[TINKERPOP-2672], +link:https://tinkerpop.apache.org/docs/3.7.1/reference/#asString-step[asString()-step], +link:https://tinkerpop.apache.org/docs/3.7.1/reference/#length-step[length()-step] +``` + +Conventions observed in the upgrade docs: + +- **JIRA issues** are the most common target. The link text is the bare ticket + id, `TINKERPOP-XXXX`, linking to `https://issues.apache.org/jira/browse/TINKERPOP-XXXX`. +- **Reference (and other doc) links** are version-pinned, not `x.y.z` (see the + upgrade exception above), with descriptive link text such as + `Reference Documentation - Metrics` or the step name `trim()-step`. +- **Mailing-list threads** point to `https://lists.apache.org/thread/...` with + link text naming the thread, often beginning `[DISCUSS]`. +- **Proposals** link to the proposal file on GitHub, for example + `https://github.com/apache/tinkerpop/blob/master/docs/src/dev/future/proposal-scoping-5.asciidoc`, + with a descriptive title as the link text. + +Use `See:` with the colon. A scattering of older entries omit it; the colon is the +standard. + +## Book wiring: `index.asciidoc` + +Each book has an `index.asciidoc` that sets document attributes and pulls in its +content files with `include::` directives, in reading order: + +``` +:docinfo: shared +:toc-position: left + +include::the-graph.asciidoc[] +include::the-traversal.asciidoc[] +``` + +A **new content file is invisible until it is included.** After creating a file, +add an `include::` for it to that book's `index.asciidoc` at the correct position. +Forgetting this is the most common reason new documentation does not appear. + +## Admonitions + +AsciiDoc admonitions call out information without breaking into a list. The four +in regular use, in rough order of frequency, are `NOTE:`, `IMPORTANT:`, `WARNING:`, +and `TIP:`. Use them for genuine asides and cautions, not as a substitute for +well-structured prose: + +``` +IMPORTANT: This tutorial assumes the Gremlin Console is installed. +``` + +## Images + +Images are referenced with `image::file.png[width=500]` for block images and +`image:file.png[width=130]` (single colon) for inline ones. Image files live +under `docs/static/images/`. Set a sensible `width`, and `align="center"` where +the surrounding content does. + +## Quick checklist for a new or changed doc + +The list below is a fixed, enumerable set of mechanical steps, which is exactly +the case where a list is appropriate: + +1. Right book, right file, AsciiDoc not Markdown. +2. Stable `[[anchor]]` on every new section. +3. Executable examples use `[gremlin-groovy,modern]` (or `existing`); no + hand-written `==>` output. Non-executed examples use `[source,text]`. +4. Version-specific links use the `x.y.z` placeholder. +5. New files added to the book's `index.asciidoc` via `include::`. +6. Semantics, grammar, or step changes reflected in + `gremlin-semantics.asciidoc`. +7. User-visible or breaking changes reflected in `CHANGELOG.asciidoc` and the + upgrade docs. diff --git a/.skills/tinker-doc/references/books-and-voice.md b/.skills/tinker-doc/references/books-and-voice.md new file mode 100644 index 0000000000..3548c0994b --- /dev/null +++ b/.skills/tinker-doc/references/books-and-voice.md @@ -0,0 +1,363 @@ +# Books and Voice + +Each book in `docs/src/` serves a different reader at a different moment. Getting +a document right starts with knowing which book it belongs to and writing in that +book's voice. The house-style rules in `SKILL.md` (no em-dash or sentence-breaking +semicolon, formal third person, paragraphs over lists, no hype) apply throughout. +The guidance below is what is specific to each book. + +--- + +## Upgrade Documentation + +`docs/src/upgrade/` — one file per minor line (`release-3.7.x.asciidoc`, etc.), +newest release section at the top, split into "Upgrading for Users" and, where +relevant, provider-facing notes. + +Upgrade documentation **announces** a release. It is the place where a feature is +introduced to the world. This shapes everything about how it reads. + +**It is not a coding reference; it tells a story.** An upgrade entry gives the +reader a clear, engaging account of what a feature is and why it matters, then +lets them follow the `See:` references for the full detail when they want it. The +goal is to make the feature interesting and understood, not to specify it. State +the point of the change and show one good example, then stop. + +**Do not catalog every corner of the feature.** This is the most common way an +upgrade entry goes wrong. Resist enumerating every overload, every argument +variation, and every runtime behavior (what happens with zero, with a partial +result, when nothing remains, and so on). That exhaustive cataloging is precisely +what the reference and semantics docs are for, and the `See:` link sends the +reader there. A well-chosen example already demonstrates the important behaviors, +so a prose paragraph that narrates each case after it is usually redundant and +should be cut. + +A typical upgrade entry is short: a heading, a paragraph or two on what changed +and why it matters, one example, and the `See:` line. The clearest warning sign is +a paragraph after the example that walks through edge cases such as an empty +result, a zero argument, or an exhausted iterator. That reads as specification +rather than story, and it is what the reference docs behind the `See:` link are +for. The test for any sentence is whether it conveys why the feature is useful or +merely specifies how it behaves. The latter belongs in the reference docs. + +**Announce without hype.** The tone is positive and informative, never +marketing. Avoid "powerful," "blazing fast," "game-changing," and similar. +Describe the capability plainly and let it be interesting on its own terms. + +**Frame past shortcomings positively.** When a release fixes a bug or removes a +limitation, describe it as an improvement to how TinkerPop works now, not as a +confession of how broken it used to be. State what the old behavior was factually, +then what the new behavior is, without self-criticism. + +**Lead major changes with history.** When a release changes how TinkerPop does +something fundamental, breaks with tradition, or alters a public API, open the +section with a short account of how the project arrived here. That context is what +makes a disruptive change feel reasoned rather than arbitrary. This history is +often more nuanced than the changeset reveals, so do not try to reconstruct it by +searching the internet or inferring it from the diff. If the history is not +already at hand, ask the human for it. + +**Compare old and new syntax in examples.** The most useful upgrade example shows +the before and the after side by side. The established pattern uses comment +labels inside a single block: + +``` +[source,groovy] +---- +// 3.7.6 +gremlin> g.inject([null]).conjoin("-") +==>null + +// 3.7.7 +gremlin> g.inject([null]).conjoin("+") +==> +---- +``` + +**Upgrade examples must always be static blocks.** Use `[source,groovy]` or +`[source,text]`, never an executable `[gremlin-groovy]` block. An upgrade example +is a snapshot of behavior at a specific moment in the project's history, often +contrasting two versions at once. It must not be regenerated against the current +build, which would erase the very before-and-after the example exists to show. + +Static does not mean output-free. A static upgrade example should still include +its results, written in by hand, so the reader sees the payoff of the change: + +``` +[source,groovy] +---- +gremlin> g.V().has('age', gt(__.V(1).values('age'))).values('name') +==>josh +==>peter +---- +``` + +When reviewing upgrade docs, a query example shown without its expected results +is a real gap, but the fix is to **add the hand-written `==>` output**, never to +convert the block to executable. Converting it would defeat the snapshot purpose +and is always the wrong call in this book. + +**Examples should be impactful, not exhaustive or contrived.** Choose a use case +that catches attention and makes the value obvious. One well-chosen, realistic +example beats five that exercise every option. + +**Breaking changes are the priority.** Breaking changes to public APIs and to +Gremlin semantics are the most important thing upgrade documentation does. For +each one, state clearly what breaks, why, and exactly how a user minimizes the +impact (what to change in their code, what to check for). A breaking change +documented without a migration path is incomplete. + +**Close an entry with a `See:` block.** Most upgrade entries end with a `See:` +line that sends the reader to where the change can be explored further: the JIRA +issue, the relevant reference documentation, a `[DISCUSS]` mailing-list thread, a +design proposal, or several of these together. The JIRA issue is the most common +and uses the bare ticket id as its link text (`See: +link:...[TINKERPOP-XXXX]`). The full format, including how multiple targets are +listed, is in +[asciidoc-and-wiring.md](asciidoc-and-wiring.md#see-blocks-upgrade-documentation). + +**Pin cross-reference links to a concrete version.** This is the one place where +the usual `x.y.z` placeholder rule does *not* apply. Because an upgrade entry is a +snapshot tied to a specific release, a link out to the reference docs (or any +other versioned documentation) must name the actual version it refers to, not +`x.y.z` and not `current`: + +``` +See: link:https://tinkerpop.apache.org/docs/3.7.4/reference/#metrics[Reference Documentation - Metrics] +``` + +A `current` or `x.y.z` link would drift as new releases ship and eventually point +the reader at documentation that no longer matches the change being described. +This pinning is peculiar to upgrade documentation. Everywhere else, use the +`x.y.z` placeholder as normal. + +--- + +## Reference Documentation + +`docs/src/reference/` — the authoritative, complete description of TinkerPop. +`the-traversal.asciidoc` documents the steps; other files cover variants, +applications, and implementations. + +Reference documentation **instructs**. Where upgrade docs introduced a feature, +reference docs are the focal point for its proper, ongoing use. The style is +**user-friendly, complete, informative, and formal**. + +The established shape of a step section: + +``` +[[coalesce-step]] +=== Coalesce Step + +The `coalesce()`-step evaluates the provided traversals in order and returns the +first traversal that emits at least one element. + +[gremlin-groovy,modern] +---- +g.V(1).coalesce(outE('knows'), outE('created')).inV().path().by('name').by(label) +---- + +*Additional References* + +link:++https://tinkerpop.apache.org/javadocs/x.y.z/.../GraphTraversal.html#coalesce(...)++[`coalesce(Traversal...)`] +``` + +Conventions to follow: + +- A dedicated anchor (`[[coalesce-step]]`) and a `=== Title Case Step` heading. +- An opening sentence that defines the step in third person, naming it as + `` `coalesce()`-step `` and, where the docs do so for its neighbors, tagging + its category in bold (for example `*filter*` or `*map*`). +- One or more **executable** `[gremlin-groovy,modern]` examples that demonstrate + real behavior on the toy graph. +- An `*Additional References*` block linking to the javadoc, and, when the step + has an entry in the Gremlin Semantics documentation, a `` `Semantics` `` link to + `dev/provider/#<step>-step`. Both use the `x.y.z` version placeholder (see + `asciidoc-and-wiring.md`). + +Completeness matters here in a way it does not in the upgrade docs. The reference +is where a user goes to learn how a step actually behaves in all its normal uses. + +--- + +## Recipes Documentation + +`docs/src/recipes/` — one file per pattern. + +Recipes share Reference's voice (user-friendly, complete, informative, formal) +but are organized around a **task** rather than a feature. A recipe states a +problem a user genuinely has, then builds the traversal that solves it, +explaining the reasoning as it goes. + +The signature recipe device is the **numbered callout**, which attaches prose +explanation to specific lines without breaking the flow of the example: + +``` +[gremlin-groovy,modern] +---- +g.V(1).bothE() <1> +g.V(1).bothE().where(otherV().hasId(2)) <2> +---- + +<1> There are three edges from the vertex with the identifier of "1". +<2> Filter those three edges using the `where()`-step ... +``` + +Recipes often layer from a simple case to progressively richer ones, which suits +the "build up a solution" framing. Keep the prose between examples flowing and +formal. + +--- + +## Tutorials + +`docs/src/tutorials/` — `getting-started`, `the-gremlin-console`, +`gremlins-anatomy`, `gremlin-language-variants`, each in its own directory. + +Tutorials are **story-driven** and cover one focused topic, taking a reader from +unfamiliar to capable. Beyond that narrative arc, they follow the +Reference/Recipes style: complete, informative, and formal. Tutorials make heavy +use of `link:` cross-references out to the published site (with the `x.y.z` +placeholder) to connect the story to the reference material, and they open with +`:docinfo: shared` directives and a logo image. + +Note that some existing tutorial prose slips into second person ("inspire you to +new levels"). That is legacy phrasing, not the standard. New and revised tutorial +content should stay in formal third person like the rest of the documentation. + +--- + +## Provider Documentation + +`docs/src/dev/provider/` — for graph providers implementing TinkerPop, and to +some extent for advanced users. The voice matches Reference and Recipes: +informative, complete, formal. + +### The semantics document + +`gremlin-semantics.asciidoc` is the canonical specification of how Gremlin +behaves: equality, comparability, orderability, equivalence, type promotion, and +the semantics of each construct. **It must be updated whenever Gremlin steps, the +grammar (`Gremlin.g4`), or the semantics code change in any way.** Treat it as +part of the change, not an afterthought. + +The semantics document is language-agnostic: it specifies Gremlin behavior for +every GLV. Do not name Java-specific types, exception classes, syntax, or library +functions in normative prose. The reference implementation is linked from each +step's `See:` block, and that is the appropriate place for any Java-flavored +specifics. For exceptions, name the TinkerPop error category rather than a Java +exception class. The categories are defined by the Errors table near the top of +the document, which is the authority on which ones exist — do not use a category +that is not a row in that table. + +For types, use the `GType` enum names (`STRING`, `INT`, `LONG`, `BIGINT`, +`BIGDECIMAL`, `FLOAT`, `DOUBLE`, `BOOLEAN`, `UUID`, `DATETIME`, `DURATION`, +`BINARY`, `CHAR`, `NULL`, `NUMBER`, `LIST`, `SET`, `MAP`, plus graph types), not +the Java class names that back them. This applies to collections and generics as +much as to scalars: write `SET<STRING>`, `LIST<STRING>`, `MAP<STRING, any>`, and +`Traversal<any, STRING>` — never `Set<String>`, `Collection<String>`, or +`Traversal<?, ?>`. Use `any` for an unconstrained type parameter. + +Note that `GType` has no `Collection` supertype, so there is nothing to translate +`Collection<String>` directly into. `LIST` and `SET` are distinct types and +neither subsumes the other, so a step accepting either takes "a `LIST` or `SET` of +`STRING`". Check the step's source rather than assuming, since narrowing the +contract to one of them is a real error. + +Java-specific naming also leaks in through configuration and enum names, not just +types and exceptions. `LabelCardinality` and its constants (`ONE`, `ONE_OR_MORE`, +`ZERO_OR_MORE`) are a Java-side concept that never appears in the grammar, so the +capability is described in prose instead ("a graph that permits the set of labels +on an element to change"). If a name is not in `Gremlin.g4` and not a `GType`, it +probably does not belong in normative prose. + +Type definitions live in `== Types`; per-type Equality, Comparability, and +Orderability behavior lives in the Comparability chapter. Do not duplicate +definitions across the two. The `Java reference exception` column of the Errors +table is the one intentional Java reference in the document. + +The document has two parts. The conceptual sections near the top (equality, +comparability, orderability, equivalence, type promotion) specify the +cross-cutting behaviors and change rarely. The large `== Steps` section documents +each Gremlin step individually, and that is where most edits land: per-step +coverage is still incomplete, and new steps, changed semantics, and new overloads +all show up here. Expect to spend your time in `== Steps`, not in the equality and +comparability material. + +When adding or revising a step, **follow the per-step template already in use.** +Each step is anchored as `[[<step>-step]]` under a `=== stepName()` heading and +fills in the same labeled fields, in order: + +- `*Description:*` — one or two sentences on what the step does. +- `*Syntax:*` — each overload as a backticked signature, multiple overloads + separated by `|` (for example `` `asString()` | `asString(Scope scope)` ``). + Use TypeScript-style `name: TYPE` parameter form with GType names for + primitives (`STRING`, `INT`, `BOOLEAN`, etc.), `any` for unconstrained + values, and PascalCase for TinkerPop concept types (`Traversal`, + `Traverser`, `Scope`, `P`, `T`, `GType`, etc.). Varargs use the trailing + ellipsis form (`STRING...`). Document only the overloads that appear in + the ANTLR grammar (`gremlin-language/src/main/antlr4/Gremlin.g4`); do + not document method-level overloads that exist only as Java + `GraphTraversal` sugar. +- A `[width="100%",options="header"]` table with the columns + `Start Step | Mid Step | Modulated | Domain | Range`. +- `*Arguments:*` — one bullet per argument. (A genuine enumeration, so a list is + the right choice here.) Use `None` as the body for steps that take no arguments. +- `*Modulation:*` — describes the modulators the step accepts (such as + `from()`/`to()` or `by()`). Use `None` as the body for steps that take no + modulators. +- `*Considerations:*` — prose covering edge cases, grammar restrictions, and any + GLV-specific notes. +- `*Exceptions:*` — the conditions under which the step raises an error, named by + the TinkerPop error category rather than a Java exception class. Use `None` as + the body for steps that raise no errors. +- A closing `See:` line linking to the step's source file(s) and its reference + entry, using the `x.y.z` placeholder. + +A new overload usually means adding its signature to `*Syntax:*` and describing +the new argument under `*Arguments:*`. A semantic change usually means revising +`*Considerations:*` or `*Exceptions:*`. Match a nearby existing step rather than +inventing a new structure, and fill the gap when documenting a step that has no +entry yet. When editing an `*Exceptions:*` section, verify the claim against the +step's source class. + +--- + +## Developer Documentation + +`docs/src/dev/developer/` — internal notes for contributors (contributing, +development environment, for-committers, release, administration). + +This is in-project material written for people working on TinkerPop itself. The +formal house style still applies. There is little beyond that to special-case: +keep it accurate, keep it current with the actual process, and match the +structure of the surrounding committer documentation. + +--- + +## IO Documentation + +`docs/src/dev/io/` — one file per serialization format (`graphbinary.asciidoc`, +`graphson.asciidoc`, `graphml.asciidoc`, `gryo.asciidoc`). + +IO documentation specifies the wire and file formats that providers and advanced +users implement against. It carries the same formal, complete, precise voice as +Provider documentation. Accuracy is paramount: this is a specification, so type +mappings, byte layouts, and format versions must match the implementation +exactly. Match the structure already used for the format being edited rather than +reorganizing it, and keep examples illustrative (`[source,text]` or +`[source,json]`) rather than executable. + +--- + +## Future Documentation + +`docs/src/dev/future/` — numbered design proposals (`proposal-*.asciidoc`) for +changes under consideration. + +Future documentation captures proposed and planned work, so it reads differently +from the rest. A proposal argues for a direction: it lays out motivation, the +proposed design, alternatives, and open questions. The formal house style still +applies, but the content is forward-looking and provisional rather than a +description of how TinkerPop behaves today. When adding a proposal, follow the +numbering and structure of the existing `proposal-*` files. diff --git a/.skills/tinker-doc/references/executable-blocks.md b/.skills/tinker-doc/references/executable-blocks.md new file mode 100644 index 0000000000..92e91e31f9 --- /dev/null +++ b/.skills/tinker-doc/references/executable-blocks.md @@ -0,0 +1,91 @@ +# Executable Code Blocks + +The defining feature of TinkerPop documentation is that most Gremlin examples are +**executed when the docs are built**. Each executable block is fed to a live +Gremlin Console, run against a real graph, and its actual `==>` output is inlined +automatically. This is what keeps the documentation honest, and it has a direct +consequence for how examples are written: the author supplies the Gremlin, never +the output. + +## Block types + +| Block opener | Executed? | Use for | +|---|---|--------------------------------------------------------------------------------------------------------------| +| `[gremlin-groovy,modern]` | Yes, against the **modern** toy graph | The default for most examples | +| `[gremlin-groovy,existing]` | Yes, against the **existing** graph | Examples that build on or mutate graph state from the immediately previous block | +| `[gremlin-groovy]` | Yes, with **no** graph bound | Setup that does not need a graph | +| `[source,groovy]` | No | Illustrative Groovy that must not run (hypotheticals, two-version comparisons); **all upgrade-doc examples** | +| `[source,text]` | No | Console sessions, error cases, or specification examples shown verbatim | + +Only `modern` and `existing` are available as graph names. The **modern** graph +is the standard six-element toy graph used almost everywhere. Reach for `existing` +only when an example genuinely needs to persist or mutate state that a later block +depends on. + +### Never invent output + +For any **executed** block, write only the Gremlin lines. Do **not** type the +`==>` result lines yourself. They are filled in automatically. Hand-written output +in an executable block is wrong by definition and will be overwritten or will +conflict with reality. + +Conversely, in a non-executed `[source,text]` or `[source,groovy]` block you +write the whole thing, prompts and output included, because nothing runs it. This +is how upgrade docs show before/after behavior and how the semantics document +specifies results. + +### Upgrade documentation always uses static blocks + +Upgrade documentation must **never** use an executable `[gremlin-groovy]` block. +Every example in the upgrade docs is a static `[source,groovy]` or `[source,text]` +block whose output is written by hand. An upgrade example is a snapshot of how +something behaved at a particular point in the project's history, frequently +showing an old version next to a new one. Regenerating it against the current +build would defeat its purpose, so these examples are deliberately kept static. + +## Output suppression: the `;[]` idiom + +When a line in an executable block is setup whose output would be noise (a +variable assignment, a schema-building call), append `;[]` to it. The statement +still executes, but it returns an empty list, so no `==>` line is emitted: + +``` +[gremlin-groovy,modern] +---- +v1 = g.V(1).next();[] +v2 = g.V(2).next();[] +g.V(v1).bothE().where(otherV().is(v2)) +---- +``` + +Here the two assignments run silently and only the final traversal produces +visible output. + +## Numbered callouts + +To explain specific lines without interrupting the example, use AsciiDoc +callouts. Mark lines with `<1>`, `<2>`, and so on, then list the explanations +immediately after the block: + +``` +[gremlin-groovy,modern] +---- +g.V(1).bothE() <1> +g.V(1).bothE().where(otherV().hasId(2)) <2> +---- + +<1> There are three edges from the vertex with the identifier of "1". +<2> Filter those edges with `where()` ... +``` + +Align the callout markers into a column for readability, as the existing recipes +do. This is the primary explanatory device in the recipes book. + +## The multi-language story + +The reference examples are written in Gremlin-Groovy. When documenting a step, +write the Groovy example. Do not hand-maintain parallel copies in every language +inside a step section unless the surrounding content already does so. The +dedicated per-language material (connecting, imports, configuration) lives in +`reference/gremlin-variants.asciidoc`, organized under `[[gremlin-python]]`, +`[[gremlin-javascript]]`, and similar anchors. diff --git a/bin/agent-setup.sh b/bin/agent-setup.sh new file mode 100755 index 0000000000..db09b49eb1 --- /dev/null +++ b/bin/agent-setup.sh @@ -0,0 +1,341 @@ +#!/usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Set up AI coding agent integration with TinkerPop's Agent Skills. +# +# TinkerPop maintains Agent Skills in .skills/: +# tinker-dev - Development guidance (coding conventions, build recipes, etc.) +# tinker-doc - Documentation authoring and review (voice, per-book style, etc.) +# +# Different AI coding tools discover skills in different directories. This script +# creates the necessary symlinks or shims so your tool can find the skills. +# +# Usage: +# bin/agent-setup.sh <agent> +# bin/agent-setup.sh --list +# bin/agent-setup.sh --all +# +# Examples: +# bin/agent-setup.sh claude # Set up for Claude Code +# bin/agent-setup.sh kiro # Set up for Kiro +# bin/agent-setup.sh --all # Set up for all supported agents +# +# Supported agents: +# claude - Claude Code (.claude/skills/) +# copilot - GitHub Copilot (.github/skills/ and .agents/skills/) +# cursor - Cursor (.cursor/skills/) +# codex - OpenAI Codex (.codex/skills/) +# junie - JetBrains Junie (.junie/skills/) +# kiro - Kiro (.kiro/skills/) + +set -uo pipefail + +SKILLS=("tinker-dev" "tinker-doc") + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +NC='\033[0m' + +ok() { echo -e " ${GREEN}✓${NC} $1"; } +skip() { echo -e " ${YELLOW}○${NC} $1"; } +bad() { echo -e " ${RED}✗${NC} $1"; } + +usage() { + echo "Usage: bin/agent-setup.sh <agent|--list|--all|--contributor [agent]>" + echo "" + echo "Agents: claude, copilot, cursor, codex, junie, kiro" + echo "" + echo "Options:" + echo " --list List supported agents and their skill discovery paths" + echo " --all Set up shims for all supported agents" + echo " --contributor Also install beads workflow hooks (committers; claude, kiro)" + echo " --help Show this message" +} + +# Verify we're in the repo root +if [[ ! -d ".skills/tinker-dev" ]]; then + bad "Cannot find .skills/tinker-dev — run this script from the TinkerPop repository root." + exit 1 +fi + +# Remove TinkerPop skill entries (symlinks or copies) from a tool's skill +# directory before the current ones are (re)created. Matching only "tinker*" +# keeps any custom skills the user maintains intact. Entries for skills that no +# longer exist (e.g. a renamed "tinkerpop-dev") are reported as stale removals; +# current skills are cleared silently so the setup step can recreate them +# cleanly — this also fixes symlinks whose relative target path has changed. +purge_tinker_skills() { + local target_dir="$1" + [[ -d "$target_dir" ]] || return 0 + + local entry name skill is_current + for entry in "$target_dir"/tinker*; do + # If the glob matched nothing it stays literal — skip non-existent paths. + [[ -e "$entry" || -L "$entry" ]] || continue + name=$(basename "$entry") + is_current=0 + for skill in "${SKILLS[@]}"; do + [[ "$name" == "$skill" ]] && is_current=1 && break + done + rm -rf "$entry" + [[ "$is_current" -eq 0 ]] && skip "removed stale skill $target_dir/$name" + done +} + +# Create a symlink from a tool's skill directory to our canonical skill +setup_symlink() { + local tool_name="$1" + local target_dir="$2" + local skill_name="$3" + local skill_dir=".skills/$skill_name" + + mkdir -p "$target_dir" + local link_path="$target_dir/$skill_name" + + if [[ -L "$link_path" ]]; then + skip "$tool_name: symlink already exists at $link_path" + return 0 + fi + + if [[ -e "$link_path" ]]; then + bad "$tool_name: $link_path already exists and is not a symlink — skipping" + return 1 + fi + + # Compute relative path from target_dir to skill_dir + local rel_path + rel_path=$(python3 -c "import os.path; print(os.path.relpath('$skill_dir', '$target_dir'))" 2>/dev/null) + if [[ -z "$rel_path" ]]; then + rel_path=$(perl -e 'use File::Spec; print File::Spec->abs2rel("'"$skill_dir"'", "'"$target_dir"'")' 2>/dev/null) + fi + if [[ -z "$rel_path" ]]; then + bad "$tool_name: could not compute relative path (need python3 or perl)" + return 1 + fi + + ln -s "$rel_path" "$link_path" + ok "$tool_name: created symlink $link_path -> $rel_path" +} + +# Kiro doesn't follow symlinks in .kiro/skills/, so we copy the skill directory +# instead. See: https://github.com/kirodotdev/Kiro/issues (symlink support). +setup_kiro() { + mkdir -p ".kiro/skills" + purge_tinker_skills ".kiro/skills" + for skill_name in "${SKILLS[@]}"; do + local target_dir=".kiro/skills/$skill_name" + cp -r ".skills/$skill_name" "$target_dir" + ok "kiro: copied $skill_name to $target_dir" + done + echo "" + echo -e " ${YELLOW}NOTE:${NC} Kiro uses copies, not symlinks. If you update skills in" + echo -e " .skills/, re-run this script to sync the changes." +} + +setup_agent() { + local agent="$1" + case "$agent" in + claude) + purge_tinker_skills ".claude/skills" + for skill in "${SKILLS[@]}"; do + setup_symlink "claude" ".claude/skills" "$skill" + done + ;; + copilot) + purge_tinker_skills ".github/skills" + purge_tinker_skills ".agents/skills" + for skill in "${SKILLS[@]}"; do + setup_symlink "copilot (.github)" ".github/skills" "$skill" + setup_symlink "copilot (.agents)" ".agents/skills" "$skill" + done + ;; + cursor) + purge_tinker_skills ".cursor/skills" + for skill in "${SKILLS[@]}"; do + setup_symlink "cursor" ".cursor/skills" "$skill" + done + ;; + codex) + purge_tinker_skills ".codex/skills" + for skill in "${SKILLS[@]}"; do + setup_symlink "codex" ".codex/skills" "$skill" + done + ;; + junie) + purge_tinker_skills ".junie/skills" + for skill in "${SKILLS[@]}"; do + setup_symlink "junie" ".junie/skills" "$skill" + done + ;; + kiro) + setup_kiro + ;; + *) + bad "Unknown agent: $agent" + echo "" + usage + return 1 + ;; + esac +} + +# --- Contributor hooks (opt-in) --------------------------------------------- +# +# Beads is a committer tool, so hook wiring is opt-in via --contributor. The +# logic lives in bin/beads-agent-hook.sh; the JSON under bin/agent-hooks/ only +# names events and invokes it. Installs are idempotent: our entries are found +# by their beads-agent-hook.sh reference (claude) or tinkerpop-beads- name +# prefix (kiro), removed, then rewritten. + +HOOK_AGENTS=("claude" "kiro") + +setup_claude_hooks() { + local settings=".claude/settings.local.json" + mkdir -p ".claude" + [[ -f "$settings" ]] || echo '{}' > "$settings" + + if ! python3 - "$settings" "bin/agent-hooks/claude.json" <<'PY' +import json, sys + +settings_path, hooks_path = sys.argv[1], sys.argv[2] +with open(settings_path) as fh: + settings = json.load(fh) +with open(hooks_path) as fh: + ours = {k: v for k, v in json.load(fh)["hooks"].items()} + +MARKER = "beads-agent-hook.sh" + + +def is_ours(entry): + return any(MARKER in h.get("command", "") for h in entry.get("hooks", [])) + + +# Sweep every event, not just the ones we are about to write: an event we no +# longer wire (PreCompact, once) must not be orphaned in the user's settings. +existing = settings.setdefault("hooks", {}) +for event in list(existing): + kept = [e for e in existing[event] if not is_ours(e)] + if kept: + existing[event] = kept + else: + del existing[event] + +for event, entries in ours.items(): + existing[event] = existing.get(event, []) + entries + +with open(settings_path, "w") as fh: + json.dump(settings, fh, indent=2) + fh.write("\n") +PY + then + bad "claude: could not merge hooks into $settings" + return 1 + fi + ok "claude: merged beads hooks into $settings (SessionStart, Stop, UserPromptSubmit)" +} + +setup_kiro_hooks() { + mkdir -p ".kiro/hooks" + cp "bin/agent-hooks/kiro.json" ".kiro/hooks/tinkerpop-beads.json" + ok "kiro: wrote .kiro/hooks/tinkerpop-beads.json (SessionStart, Stop, UserPromptSubmit)" +} + +setup_hooks() { + local agent="$1" + + if ! command -v bd >/dev/null 2>&1; then + skip "$agent: 'bd' not found — hooks installed anyway, they stay silent without it" + fi + + case "$agent" in + claude) setup_claude_hooks ;; + kiro) setup_kiro_hooks ;; + *) + bad "No hook support for: $agent" + echo " Hooks are available for: ${HOOK_AGENTS[*]}" + return 1 + ;; + esac +} + +list_agents() { + echo "Supported agents and their skill discovery paths:" + echo "" + echo " Skills: ${SKILLS[*]}" + echo "" + echo " claude .claude/skills/<skill>/ -> symlink to .skills/<skill>" + echo " copilot .github/skills/<skill>/ -> symlink to .skills/<skill>" + echo " .agents/skills/<skill>/ -> symlink to .skills/<skill>" + echo " cursor .cursor/skills/<skill>/ -> symlink to .skills/<skill>" + echo " codex .codex/skills/<skill>/ -> symlink to .skills/<skill>" + echo " junie .junie/skills/<skill>/ -> symlink to .skills/<skill>" + echo " kiro .kiro/skills/<skill>/ -> copy of .skills/<skill> (re-run to sync)" +} + +# --- Main --- + +if [[ $# -eq 0 ]]; then + usage + exit 1 +fi + +case "$1" in + --help|-h) + usage + ;; + --list) + list_agents + ;; + --all) + echo "Setting up all agent integrations..." + echo "" + for agent in claude copilot cursor codex junie kiro; do + setup_agent "$agent" + done + echo "" + echo "Done. Symlinked directories and generated files are gitignored." + echo "Add them to .gitignore if they aren't already." + ;; + --contributor) + shift + if [[ $# -eq 0 ]]; then + echo "Setting up beads hooks for all supported agents..." + echo "" + for agent in "${HOOK_AGENTS[@]}"; do + setup_hooks "$agent" + done + else + echo "Setting up beads hooks for $1..." + echo "" + setup_hooks "$1" + fi + echo "" + echo "Hooks are advisory — they remind, they never block. Logic lives in" + echo "bin/beads-agent-hook.sh; run it directly to see what an agent is shown:" + echo "" + echo " bin/beads-agent-hook.sh stop" + ;; + *) + echo "Setting up $1..." + echo "" + setup_agent "$1" + ;; +esac
