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
8 changes: 8 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@
- uv: add optional `exclude-newer = "P7D"` only after confirming the local
`uv` version supports relative `exclude-newer` durations.
- pnpm: keep `minimumReleaseAge: 10080` in `pnpm-workspace.yaml`.
- Bundler: use `source "https://rubygems.org", cooldown: 7` only with Bundler
`4.0.13` or newer, then pin that Bundler version in `Gemfile.lock`.
- CI should use locked installs:
- `bun install --frozen-lockfile`
- `uv sync --locked` when a Python workspace is present
- `pnpm install --frozen-lockfile` when pnpm is used.
- `bundle install` with deployment/frozen settings when Ruby is used.
- Commit lockfiles.

## Repository Shape
Expand All @@ -25,6 +28,7 @@
- `scripts`: stable project entrypoints.
- `docs`: contributor-facing documentation.
- `stacks/python`: optional Python API/shared-package workspace.
- `stacks/ruby`: optional Ruby/Rails/Rack workspace conventions.
- `.context`: gitignored workspace-local scratch for Conductor and agents.

## Development Workflow
Expand Down Expand Up @@ -55,6 +59,10 @@
target machine is older, ask before upgrading uv; do not write `P7D` or
`7 days` into `pyproject.toml` or `uv.toml` because older uv clients fail
during settings discovery.
- Before adding Bundler cooldown config, run `bundle --version`. The
`cooldown:` source option requires Bundler `4.0.13` or newer. If Bundler is
older, ask before upgrading it; do not add cooldown syntax that the target
repo's Bundler cannot parse.
- The TypeScript stack includes Drizzle examples for database access. Keep
Drizzle when it fits; replace it when the target repo already uses another
data-access layer.
Expand Down
4 changes: 3 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ The canonical agent instructions are in `AGENTS.md`.
Follow the same repo rules as Codex:

- Read before editing.
- Prefer repo-provided scripts. Use `bun run` for root tooling and `uv run` when a Python workspace is present.
- Prefer repo-provided scripts. Use `bun run` for root tooling, `uv run` when a
Python workspace is present, and `bundle exec` when a Ruby workspace is
present.
- Keep changes scoped.
- Update `.env.example`, tests, and docs when contracts change.
- Use gitignored `.context/` for concise workspace-local operational memory.
Expand Down
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ Point an agent at this repo when starting or normalizing a project. The agent sh
- Bun and pnpm as first-class JavaScript package manager examples, with Bun
shown first.
- Optional `uv` Python workspace conventions.
- Dependency cooldowns for Bun, pnpm, and uv.
- Optional Ruby/Rails/Rack conventions.
- Dependency cooldowns for Bun, pnpm, uv, and Bundler.
- Host-run development services.
- Docker Compose examples for local infrastructure such as databases and
caches.
Expand Down Expand Up @@ -117,6 +118,8 @@ This repository intentionally includes files that conflict with each other. It i
- `stacks/typescript/`: framework-neutral TypeScript conventions, Drizzle
examples, Biome, Vitest.
- `stacks/python/`: optional Python API/shared-package workspace.
- `stacks/ruby/`: optional Ruby/Rails/Rack conventions with Bundler cooldown
guidance.
- `stacks/typescript/pnpm/`: pnpm root files and CI fragment for larger TypeScript workspaces.
- `extras/dev-scripts/`: JS-first script variants and JS implementations of helper scripts.
- `extras/dockerfiles/`: opt-in Dockerfile examples for deployment parity.
Expand All @@ -135,7 +138,8 @@ Keep root defaults for most new projects: shell wrappers, shell worktree ports,
- Start with root hygiene files, then select only the stacks and extras that
match the target repo.
- Treat `stacks/` as peer language/runtime convention packs. TypeScript and
Python are examples, not universal defaults.
Python are examples, not universal defaults. Ruby is available as an
opt-in convention pack when the target repo actually uses Ruby.
- Treat `extras/` as opt-in workflows or deployment helpers that may require
repo settings, real owners, or team process.
- Keep `.context/` gitignored. Promote durable learnings into tracked docs
Expand Down
36 changes: 36 additions & 0 deletions docs/supply-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,41 @@ minimumReleaseAgeExclude:

`minimumReleaseAge` is in minutes.

## Ruby and Bundler

Use this when a project selects the Ruby stack or otherwise has a `Gemfile`.

Bundler cooldowns require Bundler `4.0.13` or newer. Before adding cooldown to
a downstream repo, check:

```bash
bundle --version
```

If Bundler is older than `4.0.13`, update Bundler and pin the same version in
`Gemfile.lock` so local development and CI resolve dependencies with the same
client behavior:

```bash
gem install bundler -v 4.0.13
bundle lock --bundler=4.0.13
```

Then prefer a committed per-source cooldown in `Gemfile`:

```ruby
source "https://rubygems.org", cooldown: 7
```

This policy is intentionally committed with the dependency source instead of
living only in a developer's local Bundler config. It gives fresh public gem
releases time to be vetted while keeping private or internal gem sources free to
declare their own policy.

Use `cooldown: 0`, `bundle install --cooldown 0`, or `BUNDLE_COOLDOWN=0` only
for intentional exceptions such as emergency security fixes. Document the
exception in the PR or release note.

## CI

Use frozen or locked installs:
Expand All @@ -81,6 +116,7 @@ Use frozen or locked installs:
bun install --frozen-lockfile
uv sync --locked
pnpm install --frozen-lockfile
BUNDLE_DEPLOYMENT=true BUNDLE_FROZEN=true bundle install
```

Renovate should use `minimumReleaseAge = "7 days"` so dependency PRs do not fight package-manager cooldowns.
Expand Down
28 changes: 28 additions & 0 deletions docs/tooling.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,31 @@ Keep Python configuration in `pyproject.toml`. The Python stack shows Pydantic
settings/boundary schemas and Alembic migrations as examples; keep them when
they fit the target repo and replace them when an existing choice is better.

## Ruby Stack

Use Bundler for Ruby installs and command execution when the target project
selects `stacks/ruby`.

The Ruby stack is intentionally a convention pack rather than a generated app.
Copy its `Gemfile.example` to `Gemfile`, generate a project-specific
`Gemfile.lock` with a compatible Bundler, and add Rails, Rack, Sidekiq, or other
runtime gems only when the target repo needs them.

Required checks for a typical Ruby project:

```bash
bundle check
bundle exec rubocop
bundle exec rspec
```

Use Rails or Rack conventions when the target repo already has them. The Ruby
stack intentionally does not make Rails a root default; it provides scripts that
adapt to `bin/dev`, Rails, Rack, RSpec, or Minitest-style test directories.

When adding Bundler cooldowns, require Bundler `4.0.13` or newer and pin that
Bundler version in `Gemfile.lock` before handing off the target repo.

## Dependency Safety

Use dependency cooldowns and frozen installs:
Expand All @@ -49,6 +74,9 @@ Use dependency cooldowns and frozen installs:
must check `uv --no-config --version` before writing relative cooldowns
downstream and ask before upgrading old uv installations.
- pnpm: `pnpm-workspace.yaml` should set `minimumReleaseAge: 10080` minutes.
- Bundler: `Gemfile` should use
`source "https://rubygems.org", cooldown: 7` when Bundler is `4.0.13` or
newer.
- CI should use locked or frozen installs.

Regenerate lockfiles when cooldown settings change so CI validates committed
Expand Down
25 changes: 23 additions & 2 deletions skills/508-devkit/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ Canonical repo: https://github.com/508-dev/508-devkit
- Root files provide broadly useful hygiene: agent instructions, shell
entrypoints, worktree-safe ports, Docker Compose examples, GitHub templates,
dependency cooldowns, and docs.
- `stacks/` contains language/runtime convention packs. TypeScript and Python
are examples, not universal defaults.
- `stacks/` contains language/runtime convention packs. TypeScript, Python, and
Ruby are examples, not universal defaults.
- `extras/` contains opt-in add-ons such as Dockerfiles, dev containers,
object storage, CODEOWNERS, Gitleaks, Dependency Review, and TODO-to-issue.
- `.context/` is workspace-local agent scratch and must not be committed.
Expand All @@ -27,6 +27,8 @@ Canonical repo: https://github.com/508-dev/508-devkit
- `DECISIONS.md` when present.
- `AGENTS.md`, `CLAUDE.md`, Cursor rules.
- `pyproject.toml`, `uv.lock` when Python is present.
- `Gemfile`, `Gemfile.lock`, `.ruby-version`, `gems.rb`, and `gems.locked`
when Ruby is present.
- `package.json`, `bun.lock`, `pnpm-lock.yaml`, `bunfig.toml`, `pnpm-workspace.yaml`.
- Compose files.
- `.github/workflows`.
Expand All @@ -44,6 +46,9 @@ Canonical repo: https://github.com/508-dev/508-devkit
- Use the Python stack only when the target repo is Python or the user asks
for Python conventions. Treat Pydantic settings and Alembic migrations as
examples, not requirements.
- Use the Ruby stack only when the target repo is Ruby or the user asks for
Ruby conventions. Treat Rails and Rack paths as adaptive examples, not
requirements.
- Use Drizzle examples only when TypeScript-side database access is wanted
and Drizzle fits the repo. Keep an existing data-access layer when one is
already established.
Expand Down Expand Up @@ -88,6 +93,14 @@ When selecting the Python stack and copying its `scripts/dev.sh`, also copy
`stacks/python/scripts/worktree-ports.py` or adapt the script to the root shell
port helper.

When selecting the Ruby stack and copying its `scripts/dev.sh`, also copy the
root `scripts/worktree-ports.sh` helper or adapt the script to the target repo's
existing port strategy. Keep the root shell helper as the canonical port helper
unless the target repo has a specific reason to standardize scripts in Ruby. If
an ADE exposes a product-specific port variable, map it to `PORT`,
`WORKTREE_PRIMARY_PORT`, or `WORKTREE_PORT_BLOCK_START` in that ADE's wrapper
instead of adding the product-specific name to reusable stack scripts.

## Frontend Frameworks

Do not infer a frontend framework from this devkit. `stacks/typescript` is a TypeScript convention workspace, not a finished Vite, Next.js, or TanStack app.
Expand Down Expand Up @@ -124,3 +137,11 @@ or newer. If the target machine has an older uv, ask the user whether they want
to upgrade uv. Do not write relative cooldowns into `pyproject.toml`, `uv.toml`,
or `~/.config/uv/uv.toml` for old uv clients because every uv command can fail
during settings discovery.

## Ruby Bundler Cooldowns

Before writing Bundler dependency cooldowns, run `bundle --version`.
`source "https://rubygems.org", cooldown: 7` requires Bundler `4.0.13` or
newer. If the target machine has an older Bundler, ask before upgrading it. Once
cooldown is added, pin the compatible Bundler version in `Gemfile.lock` with
`bundle lock --bundler=4.0.13` or newer.
11 changes: 11 additions & 0 deletions stacks/ruby/Gemfile.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

source "https://rubygems.org", cooldown: 7

ruby ">= 3.2"

group :development, :test do
gem "rspec", require: false
gem "rubocop", require: false
gem "rubocop-performance", require: false
end
67 changes: 67 additions & 0 deletions stacks/ruby/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Ruby Stack

Use this stack when the target project needs Ruby, Rails, Rack, or Ruby-first
service tooling.

## Contains

- `Gemfile.example`: minimal Bundler policy with a public RubyGems cooldown.
- `scripts/{setup,dev,lint,test,check-all}.sh`: validation and local
development wrappers for a copied Ruby stack.

The root devkit keeps `scripts/worktree-ports.sh` as the canonical port helper
so projects are not forced to include Ruby just for port allocation.
If an ADE or workspace orchestrator has its own reserved port variable, map it
to `PORT` or `WORKTREE_PRIMARY_PORT` in that environment's wrapper script
instead of adding ADE-specific names to this stack.

## Apply

Copy the relevant files into the target repo root:

```bash
mkdir -p scripts
cp stacks/ruby/Gemfile.example Gemfile
cp scripts/worktree-ports.sh scripts/
cp stacks/ruby/scripts/*.sh scripts/
Comment thread
michaelmwu marked this conversation as resolved.
```

Then check the local Bundler version:

```bash
bundle --version
```

Bundler cooldowns require Bundler `4.0.13` or newer. If the target repo has an
older Bundler, ask before upgrading. After upgrading, pin Bundler in the
lockfile:

```bash
gem install bundler -v 4.0.13
bundle install
bundle lock --bundler=4.0.13
```

Then validate the copied stack:

```bash
./scripts/check-all.sh
```

## Agent Notes

- Do not copy this stack just because a repo has scripts. Select it only when
Ruby is part of the target runtime or tooling.
- Keep `source "https://rubygems.org", cooldown: 7` on public RubyGems sources
when Bundler is `4.0.13` or newer.
- Use `cooldown: 0`, `bundle install --cooldown 0`, or `BUNDLE_COOLDOWN=0`
only for intentional exceptions such as urgent security fixes.
- Commit `Gemfile.lock` after applying the stack to a real target repo.
- Keep root port helpers shell-based unless the target repo intentionally wants
Ruby helper scripts.
- If the target repo already has its own port helper, adapt `scripts/dev.sh`
instead of overwriting that helper.
- If the target ADE exposes a product-specific port variable, map it to `PORT`
or `WORKTREE_PRIMARY_PORT` outside the reusable stack scripts.
- Update `.env.example` whenever settings fields change.
- Run `./scripts/check-all.sh` from the copied Ruby stack before handing off.
8 changes: 8 additions & 0 deletions stacks/ruby/scripts/check-all.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env sh
set -eu

cd "$(dirname "$0")/.."

bundle check
./scripts/lint.sh
./scripts/test.sh
40 changes: 40 additions & 0 deletions stacks/ruby/scripts/dev.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/usr/bin/env sh
set -eu

cd "$(dirname "$0")/.."

export WEB_HOST="${WEB_HOST:-127.0.0.1}"
export RACK_ENV="${RACK_ENV:-development}"
export RAILS_ENV="${RAILS_ENV:-development}"

if [ -x ./scripts/worktree-ports.sh ]; then
eval "$(./scripts/worktree-ports.sh export)"
export PORT="${PORT:-$WEB_PORT}"
else
export PORT="${PORT:-${WORKTREE_PRIMARY_PORT:-3000}}"
fi

echo "508 Devkit Ruby stack"
echo " Web: http://${WEB_HOST}:${PORT}"
if [ -n "${POSTGRES_HOST_PORT:-}" ]; then
echo " Postgres: 127.0.0.1:${POSTGRES_HOST_PORT}"
fi
if [ -n "${REDIS_HOST_PORT:-}" ]; then
echo " Redis: 127.0.0.1:${REDIS_HOST_PORT}"
fi
echo

if [ -x bin/dev ]; then
exec bin/dev
fi

if [ -x bin/rails ]; then
exec bundle exec rails server --binding "$WEB_HOST" --port "$PORT"
fi

if [ -f config.ru ]; then
exec bundle exec rackup --host "$WEB_HOST" --port "$PORT"
fi

echo "No Ruby dev entrypoint found. Add bin/dev, bin/rails, or config.ru." >&2
exit 1
6 changes: 6 additions & 0 deletions stacks/ruby/scripts/lint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/usr/bin/env sh
set -eu

cd "$(dirname "$0")/.."

bundle exec rubocop
17 changes: 17 additions & 0 deletions stacks/ruby/scripts/setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/env sh
set -eu

cd "$(dirname "$0")/.."

if ! command -v bundle >/dev/null 2>&1; then
echo "Bundler is required. Install Bundler 4.0.13 or newer before setup." >&2
exit 1
fi

version="$(bundle --version | awk '{print $3}')"
if ! ruby -e 'exit(Gem::Version.new(ARGV[0]) >= Gem::Version.new("4.0.13") ? 0 : 1)' "$version"; then
echo "Bundler ${version} is too old for Gemfile cooldown syntax; install Bundler 4.0.13 or newer." >&2
exit 1
fi

bundle install
18 changes: 18 additions & 0 deletions stacks/ruby/scripts/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/env sh
set -eu

cd "$(dirname "$0")/.."

if [ -d spec ]; then
exec bundle exec rspec
fi

if [ -x bin/rails ]; then
exec bundle exec rails test
fi

if [ -d test ]; then
exec bundle exec ruby -Itest -e 'Dir["test/**/*_test.rb"].sort.each { |file| require "./#{file}" }'
fi
Comment thread
michaelmwu marked this conversation as resolved.

echo "No spec/ or test/ directory found."