Document jj integration and add a tag-triggered release workflow
The README gains a "Using with jj" section with the merge-tools config
d3j drops into, and its status section catches up with reality: the
pipeline and CLI work end to end. The release workflow follows the
ranger repo's shape — a v* tag builds aarch64 macOS and Linux binaries,
publishes a GitHub release, and pushes a DotSlash file.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AWq3C7nEepKJBPoJhVKZMu
diff --git a/.github/workflows/dotslash-config.json b/.github/workflows/dotslash-config.json
new file mode 100644
index 0000000..428f237
--- /dev/null
+++ b/.github/workflows/dotslash-config.json
@@ -0,0 +1,18 @@
+{
+ "outputs": {
+ "d3j": {
+ "platforms": {
+ "macos-aarch64": {
+ "regex": "^d3j-aarch64-apple-darwin",
+ "format": "tar.gz",
+ "path": "d3j"
+ },
+ "linux-aarch64": {
+ "regex": "^d3j-aarch64-unknown-linux-gnu",
+ "format": "tar.gz",
+ "path": "d3j"
+ }
+ }
+ }
+ }
+}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..32bb9ef
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,83 @@
+name: Release
+
+on:
+ push:
+ tags:
+ - 'v*'
+
+permissions: {}
+
+jobs:
+ build-macos:
+ runs-on: macos-latest
+ steps:
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
+ with:
+ persist-credentials: false
+
+ - name: Build macOS
+ run: |
+ cargo build --release
+ tar -czf d3j-aarch64-apple-darwin.tar.gz -C target/release d3j
+
+ - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
+ with:
+ name: d3j-aarch64-apple-darwin
+ path: d3j-aarch64-apple-darwin.tar.gz
+
+ build-linux:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
+ with:
+ persist-credentials: false
+
+ - name: Build Linux
+ run: |
+ cargo install cross --git https://github.com/cross-rs/cross
+ cross build --release --target aarch64-unknown-linux-gnu
+ tar -czf d3j-aarch64-unknown-linux-gnu.tar.gz -C target/aarch64-unknown-linux-gnu/release d3j
+
+ - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
+ with:
+ name: d3j-aarch64-unknown-linux-gnu
+ path: d3j-aarch64-unknown-linux-gnu.tar.gz
+
+ publish:
+ needs: [build-macos, build-linux]
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ steps:
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
+ with:
+ persist-credentials: false
+
+ - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
+ with:
+ merge-multiple: true
+
+ - name: Publish
+ run: |
+ gh release create "${GITHUB_REF_NAME}" \
+ --title "${GITHUB_REF_NAME}" \
+ --generate-notes \
+ d3j-aarch64-apple-darwin.tar.gz \
+ d3j-aarch64-unknown-linux-gnu.tar.gz
+ env:
+ GH_TOKEN: ${{ github.token }}
+
+ dotslash:
+ needs: publish
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ steps:
+ # dotslash-publish-release needs credentials to push a commit.
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
+ - uses: facebook/dotslash-publish-release@2539c4d8ae00a42773306c8731d2dd3724d979d2 # v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ config: .github/workflows/dotslash-config.json
+ tag: ${{ github.ref_name }}
diff --git a/README.md b/README.md
index d0f87a5..de0edfd 100644
--- a/README.md
+++ b/README.md
@@ -22,15 +22,12 @@ any tree-sitter grammar, and ships with Rust, Java, and JSON.
## Status
-Early development. The crate is scaffolded and two pieces are in place:
-
-- the language registry (`Lang`), which detects a grammar by file
- extension and loads its `node-types.json` metadata
-- the library-wide error space (`Error`)
-
-The diff, merge, checker, and synthesis stages — and the working
-CLI — are not built yet. The command-line interface described below is
-the planned shape, not a working one. See
+Early development, but the pipeline works end to end: parsing, the
+arena AST, base-to-branch diffing, the pushout merge, the correctness
+checker, output synthesis, and the `d3j` CLI are all in place for the
+bundled grammars (Rust, Java, JSON). Move detection, deeper Java
+conflict rules, and evaluation against the paper's replication
+datasets are still to come. See
[`docs/plans/2026-07-10-d3j-design.md`](docs/plans/2026-07-10-d3j-design.md)
for the full design and milestones.
@@ -67,7 +64,7 @@ To install the `d3j` binary:
just install # cargo install --locked --path .
```
-## Usage (planned)
+## Usage
The binary takes diff3-style argument order, so it drops into a
merge-driver configuration:
@@ -83,6 +80,40 @@ Both exit 2 on unparsable input or an unknown language — d3j never
silently falls back to a textual merge. Language is detected from the
file extension, with `--lang` as an override.
+## Using with jj
+
+d3j plugs into [Jujutsu](https://jj-vcs.dev) as a merge tool. With the
+binary installed, add this to your jj config (`jj config edit --user`):
+
+```toml
+[merge-tools.d3j]
+program = "d3j"
+merge-args = ["merge", "$base", "$left", "$right", "-o", "$output"]
+merge-conflict-exit-codes = [1]
+conflict-marker-style = "git"
+```
+
+Then resolve conflicts with it:
+
+```sh
+jj resolve --tool d3j # all conflicted files
+jj resolve --tool d3j 'glob:**/*.rs' # scope to one language
+```
+
+jj hands d3j temporary copies of the three sides (the files keep their
+extension, so language detection works) and reads the merged result
+back from `$output`. `merge-conflict-exit-codes` (jj ≥ 0.24) tells jj
+that exit 1 means the output still contains conflict markers, which jj
+parses back into its own conflict state; `conflict-marker-style =
+"git"` matches the diff3-style markers d3j emits. Exit 2 is
+deliberately not listed: an unparsable file or unknown language is a
+tool failure, and jj leaves the conflict untouched.
+
+Because only Rust, Java, and JSON grammars are bundled, scoping
+`jj resolve` with a fileset is the smoothest workflow — files in other
+languages fail with exit 2. Keep `ui.merge-editor` pointed at an
+interactive editor and reach for `--tool d3j` explicitly.
+
## Development
The `justfile` wraps the common tasks:
@@ -97,6 +128,13 @@ just mutants # mutation testing
CI runs formatting, clippy, coverage, and mutation testing on every push
and pull request.
+## Releases
+
+Pushing a `v*` tag triggers the release workflow, which builds macOS
+(aarch64) and Linux (aarch64) binaries, creates a GitHub release with
+generated notes, and publishes a [DotSlash](https://dotslash-cli.com)
+file for the release.
+
## Reference
- Paper: Mori & Hashimoto, "On the Correctness of Software Merge,"