Skip to content

Commit 39e57a2

Browse files
decebalclaude
andcommitted
feat: rename ralph to rewind-cn, add chronis bridge and release infrastructure
- Rename crates: ralph-cli → rewind-cn, ralph-core → rewind-cn-core - Add chronis bridge for external task tracking (cn CLI) - Add TOON format output for MCP tools (~50% fewer tokens) - Add agent worker with chronis auto-detection - Add CI workflow (fmt, clippy, test) and release workflow (4-target cross-compile) - Add Makefile with dev and release commands - Add git-cliff changelog config and release-plz config - Add beads migration script (beads-rust → chronis) - Fix all clippy warnings Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent c9d72fc commit 39e57a2

62 files changed

Lines changed: 1692 additions & 498 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/settings.local.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"permissions": {
3+
"allow": [
4+
"Bash(cargo test:*)",
5+
"Bash(cargo build:*)",
6+
"Bash(cargo clippy:*)",
7+
"Bash(cn:*)"
8+
]
9+
}
10+
}

.github/workflows/ci.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
env:
10+
CARGO_TERM_COLOR: always
11+
12+
jobs:
13+
check:
14+
name: Check
15+
runs-on: ubuntu-latest
16+
steps:
17+
- uses: actions/checkout@v6
18+
19+
- name: Install Rust
20+
uses: dtolnay/rust-toolchain@stable
21+
with:
22+
components: clippy, rustfmt
23+
24+
- name: Cache Rust dependencies
25+
uses: Swatinem/rust-cache@v2
26+
27+
- name: Check formatting
28+
run: cargo fmt --all -- --check
29+
30+
- name: Clippy
31+
run: cargo clippy --all-targets -- -D warnings
32+
33+
- name: Tests
34+
run: cargo test --all

.github/workflows/release.yml

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- "v*.*.*"
7+
workflow_dispatch:
8+
inputs:
9+
version:
10+
description: "Version to release (e.g., 1.0.0)"
11+
required: true
12+
type: string
13+
prerelease:
14+
description: "Is this a prerelease?"
15+
required: false
16+
default: false
17+
type: boolean
18+
19+
permissions:
20+
contents: write
21+
id-token: write
22+
attestations: write
23+
24+
jobs:
25+
# ============================================================================
26+
# Validate Release
27+
# ============================================================================
28+
validate:
29+
name: Validate Release
30+
runs-on: ubuntu-latest
31+
outputs:
32+
version: ${{ steps.version.outputs.version }}
33+
is_prerelease: ${{ steps.version.outputs.is_prerelease }}
34+
steps:
35+
- uses: actions/checkout@v6
36+
37+
- name: Get version
38+
id: version
39+
run: |
40+
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
41+
VERSION="${{ github.event.inputs.version }}"
42+
IS_PRERELEASE="${{ github.event.inputs.prerelease }}"
43+
else
44+
VERSION="${GITHUB_REF#refs/tags/v}"
45+
if [[ "$VERSION" =~ (alpha|beta|rc) ]]; then
46+
IS_PRERELEASE="true"
47+
else
48+
IS_PRERELEASE="false"
49+
fi
50+
fi
51+
52+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
53+
echo "is_prerelease=$IS_PRERELEASE" >> "$GITHUB_OUTPUT"
54+
echo "Release version: $VERSION (prerelease: $IS_PRERELEASE)"
55+
56+
- name: Validate semver
57+
run: |
58+
VERSION="${{ steps.version.outputs.version }}"
59+
if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$ ]]; then
60+
echo "Invalid semantic version: $VERSION"
61+
exit 1
62+
fi
63+
64+
# ============================================================================
65+
# Quality Gates
66+
# ============================================================================
67+
quality:
68+
name: Quality Gates
69+
needs: validate
70+
runs-on: ubuntu-latest
71+
steps:
72+
- uses: actions/checkout@v6
73+
74+
- name: Install Rust
75+
uses: dtolnay/rust-toolchain@stable
76+
with:
77+
components: clippy, rustfmt
78+
79+
- name: Cache Rust dependencies
80+
uses: Swatinem/rust-cache@v2
81+
82+
- name: Check formatting
83+
run: cargo fmt --all -- --check
84+
85+
- name: Clippy
86+
run: cargo clippy --all-targets -- -D warnings
87+
88+
- name: Tests
89+
run: cargo test --all
90+
91+
# ============================================================================
92+
# Build Rust Binaries
93+
# ============================================================================
94+
build-binaries:
95+
name: Build (${{ matrix.artifact }})
96+
needs: [validate, quality]
97+
runs-on: ${{ matrix.os }}
98+
strategy:
99+
fail-fast: false
100+
matrix:
101+
include:
102+
- os: ubuntu-latest
103+
target: x86_64-unknown-linux-musl
104+
artifact: rewind-linux-x86_64
105+
- os: ubuntu-latest
106+
target: aarch64-unknown-linux-musl
107+
artifact: rewind-linux-aarch64
108+
- os: macos-latest
109+
target: x86_64-apple-darwin
110+
artifact: rewind-macos-x86_64
111+
- os: macos-latest
112+
target: aarch64-apple-darwin
113+
artifact: rewind-macos-aarch64
114+
115+
steps:
116+
- uses: actions/checkout@v6
117+
118+
- name: Install Rust
119+
uses: dtolnay/rust-toolchain@stable
120+
with:
121+
targets: ${{ matrix.target }}
122+
123+
- name: Ensure target is installed
124+
run: rustup target add ${{ matrix.target }}
125+
126+
- name: Cache Rust dependencies
127+
uses: Swatinem/rust-cache@v2
128+
with:
129+
key: ${{ matrix.target }}
130+
131+
- name: Install musl tools
132+
if: contains(matrix.target, 'musl')
133+
run: |
134+
sudo apt-get update
135+
sudo apt-get install -y musl-tools
136+
137+
- name: Install cross
138+
if: contains(matrix.target, 'aarch64') && contains(matrix.os, 'ubuntu')
139+
run: cargo install cross --git https://github.com/cross-rs/cross
140+
141+
- name: Build
142+
run: |
143+
if [[ "${{ matrix.target }}" == "aarch64-unknown-linux-musl" ]]; then
144+
cross build --release --locked --target ${{ matrix.target }} -p rewind-cn --bin rewind
145+
else
146+
cargo build --release --locked --target ${{ matrix.target }} -p rewind-cn --bin rewind
147+
fi
148+
149+
- name: Package binary
150+
run: |
151+
mkdir -p dist
152+
cp target/${{ matrix.target }}/release/rewind dist/${{ matrix.artifact }}
153+
chmod +x dist/${{ matrix.artifact }}
154+
155+
- name: Create tarball
156+
run: |
157+
cd dist
158+
tar -czvf ${{ matrix.artifact }}.tar.gz ${{ matrix.artifact }}
159+
160+
- name: Upload artifact
161+
uses: actions/upload-artifact@v7
162+
with:
163+
name: ${{ matrix.artifact }}
164+
path: dist/${{ matrix.artifact }}.tar.gz
165+
if-no-files-found: error
166+
167+
# ============================================================================
168+
# Create GitHub Release
169+
# ============================================================================
170+
create-release:
171+
name: Create Release
172+
needs: [validate, build-binaries]
173+
runs-on: ubuntu-latest
174+
steps:
175+
- uses: actions/checkout@v6
176+
with:
177+
fetch-depth: 0
178+
179+
- name: Download all artifacts
180+
uses: actions/download-artifact@v8
181+
with:
182+
path: artifacts
183+
merge-multiple: true
184+
continue-on-error: true
185+
186+
- name: Ensure artifacts directory exists
187+
run: mkdir -p artifacts
188+
189+
- name: Generate changelog
190+
id: changelog
191+
run: |
192+
PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
193+
194+
if [ -n "$PREV_TAG" ]; then
195+
CHANGES=$(git log --pretty=format:"- %s (%h)" "$PREV_TAG..HEAD")
196+
else
197+
CHANGES=$(git log --pretty=format:"- %s (%h)" HEAD~20..HEAD)
198+
fi
199+
200+
echo "$CHANGES" > changelog.txt
201+
202+
- name: Create release
203+
uses: softprops/action-gh-release@v2
204+
with:
205+
tag_name: v${{ needs.validate.outputs.version }}
206+
name: Rewind v${{ needs.validate.outputs.version }}
207+
body_path: changelog.txt
208+
prerelease: ${{ needs.validate.outputs.is_prerelease == 'true' }}
209+
generate_release_notes: true
210+
files: |
211+
artifacts/*.tar.gz
212+
fail_on_unmatched_files: false
213+
214+
- name: Generate release summary
215+
run: |
216+
{
217+
echo "## Release v${{ needs.validate.outputs.version }}"
218+
echo ""
219+
echo "### Binary Artifacts"
220+
echo "| Platform | Architecture | Download |"
221+
echo "|----------|-------------|----------|"
222+
echo "| Linux | x86_64 | rewind-linux-x86_64.tar.gz |"
223+
echo "| Linux | aarch64 | rewind-linux-aarch64.tar.gz |"
224+
echo "| macOS | x86_64 | rewind-macos-x86_64.tar.gz |"
225+
echo "| macOS | aarch64 | rewind-macos-aarch64.tar.gz |"
226+
} >> "$GITHUB_STEP_SUMMARY"

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
/target
2-
.ralph/
2+
.rewind/

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [Unreleased]
9+
10+
### Added
11+
- CQRS + Event Sourcing engine with allframe backend
12+
- CLI commands: init, status, plan, run, mcp
13+
- Chronis bridge for external task tracking (cn CLI)
14+
- MCP server with TOON format support
15+
- Agent worker with auto-detection of chronis CLI
16+
- Beads migration script (beads-rust to chronis)

CLAUDE.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# rewind-cn
2+
3+
Autonomous coding agent orchestrator built on CQRS + Event Sourcing.
4+
5+
## Crate Naming
6+
7+
- Binary: `rewind-cn` (CLI name: `rewind`)
8+
- Library: `rewind-cn-core`
9+
- Internal name: "rewind" (used in configs, MCP URIs, event type names)
10+
11+
## Architecture Rules
12+
13+
- **domain/** — Zero framework dependencies. No allframe traits except `Event`/`EventTypeName` on events.
14+
- **application/** — Pure business logic. No async, no framework coupling. Functions return `Result<Vec<RewindEvent>, RewindError>`.
15+
- **infrastructure/** — allframe integration, I/O, external tool bridges (chronis, MCP).
16+
17+
## Build & Test
18+
19+
```bash
20+
cargo test # Run all 22+ tests
21+
cargo build # Build workspace
22+
cargo clippy # Lint
23+
```
24+
25+
## Chronis Integration
26+
27+
This project uses [chronis](https://github.com/nicholasgasior/chronis) (`cn` CLI) as an external task tracker.
28+
29+
### Task Ownership Model
30+
- **Chronis** owns the task backlog (definitions, dependencies, readiness)
31+
- **Rewind** owns execution state (sessions, agent assignments, event sourcing)
32+
33+
### Agent Workflow (cn → rewind bridge)
34+
```bash
35+
cn ready --toon # Get next available (open + unblocked) task
36+
cn claim <id> --toon # Claim: maps to assign_task + start_task
37+
cn done <id> --toon # Complete: maps to complete_task
38+
```
39+
40+
### Skill Pipeline (for new features)
41+
```
42+
1. /rewind-prd → Generate PRD with user stories
43+
2. /rewind-beads → Convert PRD to chronis beads (cn task create)
44+
3. rewind run --tracker chronis --epic <id> → Execute
45+
```
46+
47+
## MCP Server
48+
49+
Tools support optional `format: "toon"` parameter for token-optimized output (~50% fewer tokens).
50+
TOON uses pipe-delimited rows instead of JSON objects.
51+
52+
## Event Store
53+
54+
- Production: `AllSourceBackend` at `.rewind/data/`
55+
- Tests: `InMemoryBackend`
56+
- Single event enum: `RewindEvent` (tagged JSON via serde)
57+
- Custom `event_type_name()`: `"rewind.domain.event"` (required by AllSourceBackend)
58+
59+
## Conventions
60+
61+
- IDs use UUID v4 via newtype wrappers (`TaskId`, `EpicId`, `SessionId`, `AgentId`)
62+
- All commands are plain structs in `application/commands.rs`
63+
- Command handlers are pure sync functions in `application/handlers.rs`
64+
- Infrastructure bridges wrap commands with allframe's `Command` trait in `command_bridge.rs`

0 commit comments

Comments
 (0)