Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,6 @@ jobs:

- name: Test
run: npm test

- name: Install/update contract smoke
run: bash scripts/smoke-install-update.sh
13 changes: 8 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
name: Release
name: Release Metadata

# GitHub Releases are cut from package.json's version only.
# GitHub Releases are optional metadata cut from package.json's version only.
# They are not the CLI/application release authority. The app release boundary
# is the reviewed dev -> main merge consumed by scripts/install.sh and
# `rlmx update`.
# Do not derive ad-hoc hotfix release tags in this workflow: npm publishing
# uses the same package version in version.yml, and rlmx --version reads
# uses the same SDK package version in version.yml, and rlmx --version reads
# src/version.ts generated from that package version. If the exact release
# already exists, skip; a new release requires an explicit version bump.
# already exists, skip; a new metadata release requires an explicit version bump.

on:
push:
Expand Down Expand Up @@ -85,7 +88,7 @@ jobs:
run: |
TAG="${{ steps.ver.outputs.tag }}"
if [ -z "$RELEASE_NOTES" ]; then
RELEASE_NOTES="Initial release of rlmx."
RELEASE_NOTES="RLMX release metadata for the main branch application boundary and SDK package version."
fi
printf '%s' "$RELEASE_NOTES" > /tmp/release-notes.md
gh release create "${TAG}" \
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/rolling-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ jobs:

Auto-maintained rolling promotion PR from \`dev\` to \`main\`.

Merging this PR is the canonical RLMX application release boundary.
Git-installed CLIs receive this commit through \`scripts/install.sh\`
or \`rlmx update\`. npm remains SDK-only.

**Process:**
- This PR is automatically created and kept open
- Human reviews and merges when ready
Expand Down
37 changes: 21 additions & 16 deletions .github/workflows/version.yml
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
name: Version
name: SDK Package

# Single source of npm publishing for @automagik/rlmx.
# Single source of SDK-only npm publishing for @automagik/rlmx.
#
# npm is NOT the canonical CLI/application release channel. End-user CLI
# installs and updates are git-managed through scripts/install.sh and
# `rlmx update`, with public `main` as the release boundary after a reviewed
# dev -> main merge.
#
# Trigger Context npm tag Bump?
# ----------------------------------- ------- -------- ------
# workflow_run (CI success on dev) dev @next yes
# workflow_run (merge PR to main) main @latest no
# workflow_dispatch (manual) dev @next yes
# workflow_run (CI success on dev) SDK dev @next yes
# workflow_run (merge PR to main) SDK main @latest no
# workflow_dispatch (manual) SDK dev @next yes
#
# On dev triggers we DERIVE a fresh version (today + build-count),
# commit + tag + push back to dev, then publish as @next.
#
# On main triggers we DO NOT bump — the version that
# dev already tagged is in package.json. We just publish that exact
# version as @latest, so npm and GitHub Release agree. Bumping on main
# caused historical drift between release tag and npm latest. Any green
# main push is eligible; protected main is the release boundary.
# On main triggers we DO NOT bump — the version that dev already tagged is in
# package.json. We just publish that exact SDK artifact as @latest, so npm and
# GitHub Release metadata agree. The CLI/application release already happened
# when the protected dev -> main merge made the commit reachable to
# scripts/install.sh and `rlmx update`.
#
# Auth: npm OIDC Trusted Publishing. The package's only Trusted
# Publisher entry on npmjs.com is THIS file (version.yml). All other
Expand Down Expand Up @@ -133,9 +138,9 @@ jobs:
git tag "v${VERSION}"
git push --atomic origin "HEAD:refs/heads/${BRANCH}" "refs/tags/v${VERSION}"

# On main context we publish whatever package.json currently holds
# that's the version dev tagged on the same chain, which is exactly
# what @latest should point at.
# On main context we publish whatever package.json currently holds as
# SDK metadata/artifacts only. This must not be treated as the CLI app
# release authority.
- name: Resolve publish version
id: pubver
run: |
Expand Down Expand Up @@ -169,9 +174,9 @@ jobs:
# but disable it explicitly to keep the contract identical to genie
# and avoid surprise failures. OIDC token exchange still happens.
NPM_CONFIG_PROVENANCE: "false"
# On main context: publish package.json's current version as @latest
# (no bump, matches what dev already tagged).
# On dev context: publish the version we just bumped to as @next.
# On main context: publish package.json's current SDK version as
# @latest (no bump, matches what dev already tagged).
# On dev context: publish the SDK version we just bumped to as @next.
run: |
VERSION="${{ steps.pubver.outputs.version }}"
TAG="${{ steps.context.outputs.npm_tag }}"
Expand Down
49 changes: 48 additions & 1 deletion dist/src/cli.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions dist/src/schema.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

73 changes: 73 additions & 0 deletions docs/release-contract.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# RLMX Release Contract

RLMX follows the Hermes/Genie install/update shape:

- `scripts/install.sh` is the canonical installer.
- `rlmx update` updates an installed checkout by fetching the latest public `main` commits.
- npm is SDK-only distribution. npm is not the canonical end-user CLI release channel.
- The canonical release boundary is a PR merge from `dev` to `main`.

## Channels

### CLI / application channel

The CLI is git-installed:

1. `install.sh` clones or refreshes the public repository.
2. It checks out `main`.
3. It installs dependencies.
4. It builds local `dist/`.
5. It links the `rlmx` executable into the user's bin directory.

After install, `rlmx update` performs the same update path in-place against `origin/main`.

### npm channel

The npm package is SDK-only:

- npm publishes library/SDK artifacts for programmatic consumers.
- npm dist-tags are not the canonical CLI release signal.
- `rlmx --version` reports the package/runtime version embedded in the checkout, but CLI freshness is primarily determined by the git commit on `main`.

## Main release boundary

`main` is the canonical release branch.

A release happens when a PR merges from `dev` to `main`:

1. CI passes on the PR.
2. The merge lands on `main`.
3. `main` becomes the install/update target.
4. `install.sh` and `rlmx update` fetch that commit.
5. GitHub release metadata may be created from the package version, but it is not the install authority.

## Coherence invariants

- Public git tags and GitHub Releases must not lie about package contents.
- If a tag is named `vX`, the target commit's `package.json` and `src/version.ts` must also be `X`.
- `rlmx update` must never use npm to update the CLI application.
- npm publishing must not create or imply a CLI release.
- Deployment-specific private policy must stay outside public RLMX.

## Update semantics

`rlmx update`:

- Runs from the installed repository root.
- Fetches `origin main --tags`.
- Refuses to overwrite local changes unless explicitly forced.
- Resets the checkout to `origin/main` for managed installs.
- Runs dependency install and build.
- Reports old commit, new commit, and version.

This mirrors the practical Hermes model: the installer owns the app checkout; the app update command refreshes that checkout.

## Workflow implementation

The release system follows this contract:

- `CI` builds, typechecks, tests, and runs an install/update smoke against a temporary git `main` remote.
- `SDK Package` publishes npm artifacts for programmatic consumers only.
- The npm manifest does not expose a `bin`, so npm does not act as the canonical CLI installer.
- `Release Metadata` may create GitHub Releases from the package version, but only as coherent metadata.
- The rolling `dev` → `main` PR states that merging it is the application release boundary.
3 changes: 0 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 3 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
{
"name": "@automagik/rlmx",
"version": "0.260528.2",
"description": "RLM algorithm CLI for coding agents prompt externalization, Python REPL with symbolic recursion, code-driven navigation",
"description": "RLM algorithm SDK for coding agents \u2014 prompt externalization, Python REPL with symbolic recursion, code-driven navigation",
"type": "module",
"publishConfig": {
"access": "public"
},
"bin": {
"rlmx": "dist/src/cli.js"
},
"main": "./dist/src/index.js",
"types": "./dist/src/index.d.ts",
"files": [
Expand All @@ -24,7 +21,8 @@
"test": "node --test dist/tests/*.test.js",
"prepare": "node scripts/prepare.mjs",
"bump-version": "node scripts/version.mjs",
"check": "tsc --noEmit"
"check": "tsc --noEmit",
"install:local": "bash scripts/install.sh"
},
"dependencies": {
"@earendil-works/pi-ai": "0.77.0",
Expand All @@ -49,7 +47,6 @@
"llm",
"repl",
"research",
"cli",
"agent"
],
"license": "MIT",
Expand Down
43 changes: 43 additions & 0 deletions scripts/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/usr/bin/env bash
set -euo pipefail

RLMX_REPO_URL="${RLMX_REPO_URL:-https://github.com/automagik-dev/rlmx.git}"
RLMX_BRANCH="${RLMX_BRANCH:-main}"
RLMX_INSTALL_DIR="${RLMX_INSTALL_DIR:-$HOME/.rlmx/rlmx}"
RLMX_BIN_DIR="${RLMX_BIN_DIR:-$HOME/.local/bin}"

echo "==> Installing RLMX"
echo "repo: $RLMX_REPO_URL"
echo "branch: $RLMX_BRANCH"
echo "dir: $RLMX_INSTALL_DIR"
echo "bin: $RLMX_BIN_DIR"

mkdir -p "$RLMX_BIN_DIR" "$(dirname "$RLMX_INSTALL_DIR")"

if [ -d "$RLMX_INSTALL_DIR/.git" ]; then
echo "==> Existing checkout found; refreshing"
git -C "$RLMX_INSTALL_DIR" fetch origin "$RLMX_BRANCH" --tags
git -C "$RLMX_INSTALL_DIR" checkout "$RLMX_BRANCH"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If the existing checkout has any local modifications or untracked files that conflict, git checkout without the force flag will fail and abort the installation. Using checkout -f ensures that the checkout succeeds by discarding local changes, which is the expected behavior for a clean managed refresh.

Suggested change
git -C "$RLMX_INSTALL_DIR" checkout "$RLMX_BRANCH"
git -C "$RLMX_INSTALL_DIR" checkout -f "$RLMX_BRANCH"

git -C "$RLMX_INSTALL_DIR" reset --hard "origin/$RLMX_BRANCH"
Comment on lines +17 to +21
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Existing installs ignore RLMX_REPO_URL.

On the refresh path, the script always fetches from the checkout’s current origin, so rerunning the installer with a different RLMX_REPO_URL silently keeps using the old remote while the banner says otherwise. Either update origin first or fail if it does not match the requested repo.

Suggested fix
 if [ -d "$RLMX_INSTALL_DIR/.git" ]; then
   echo "==> Existing checkout found; refreshing"
+  git -C "$RLMX_INSTALL_DIR" remote set-url origin "$RLMX_REPO_URL"
   git -C "$RLMX_INSTALL_DIR" fetch origin "$RLMX_BRANCH" --tags
   git -C "$RLMX_INSTALL_DIR" checkout "$RLMX_BRANCH"
   git -C "$RLMX_INSTALL_DIR" reset --hard "origin/$RLMX_BRANCH"
 else
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if [ -d "$RLMX_INSTALL_DIR/.git" ]; then
echo "==> Existing checkout found; refreshing"
git -C "$RLMX_INSTALL_DIR" fetch origin "$RLMX_BRANCH" --tags
git -C "$RLMX_INSTALL_DIR" checkout "$RLMX_BRANCH"
git -C "$RLMX_INSTALL_DIR" reset --hard "origin/$RLMX_BRANCH"
if [ -d "$RLMX_INSTALL_DIR/.git" ]; then
echo "==> Existing checkout found; refreshing"
git -C "$RLMX_INSTALL_DIR" remote set-url origin "$RLMX_REPO_URL"
git -C "$RLMX_INSTALL_DIR" fetch origin "$RLMX_BRANCH" --tags
git -C "$RLMX_INSTALL_DIR" checkout "$RLMX_BRANCH"
git -C "$RLMX_INSTALL_DIR" reset --hard "origin/$RLMX_BRANCH"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/install.sh` around lines 17 - 21, The refresh path uses the existing
origin instead of the requested RLMX_REPO_URL; update the script to read the
current origin URL (git -C "$RLMX_INSTALL_DIR" remote get-url origin), compare
it to $RLMX_REPO_URL, and if they differ either set the origin to the requested
URL (git -C "$RLMX_INSTALL_DIR" remote set-url origin "$RLMX_REPO_URL") before
fetch/checkout/reset or exit with a clear error; ensure this logic surrounds the
existing git -C "$RLMX_INSTALL_DIR" fetch/checkout/reset calls that reference
origin and uses the RLMX_* variables named in the diff.

else
if [ -e "$RLMX_INSTALL_DIR" ]; then
echo "error: $RLMX_INSTALL_DIR exists but is not a git checkout" >&2
exit 1
fi
echo "==> Cloning"
git clone --branch "$RLMX_BRANCH" "$RLMX_REPO_URL" "$RLMX_INSTALL_DIR"
fi

cd "$RLMX_INSTALL_DIR"

echo "==> Installing dependencies"
npm ci

echo "==> Building"
npm run build
Comment on lines +33 to +37
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Install dev dependencies before building

When this installer runs in an environment with NODE_ENV=production, npm ci omits dev dependencies by default (checked with NODE_ENV=production npm config get omit, which returns dev), so the following npm run build cannot find dev-only tools like typescript/tsc. That makes fresh installs fail on production-configured hosts even though the committed dist/ exists; the same pattern also appears in rlmx update, so force dev dependencies for the managed build path (for example npm ci --include=dev or clearing omit).

Useful? React with 👍 / 👎.


ln -sfn "$RLMX_INSTALL_DIR/dist/src/cli.js" "$RLMX_BIN_DIR/rlmx"
chmod +x "$RLMX_INSTALL_DIR/dist/src/cli.js"

echo "==> Installed"
"$RLMX_BIN_DIR/rlmx" --version
Loading
Loading