Track your plates and chart your progress.
- ✓Actively maintained (<30d)
- ✓Clear description
- ✓Mature repo (>1y old)
- ✓Documented (README)
- !No standard license detected
git clone https://github.com/stephaniewilkinson/tectonicTools overview
# Tectonic
Tectonic is a barbell strength-training tracker. An account logs workouts; a workout
holds sets of an exercise at a weight and a rep count, in the order they are lifted. All
weights are integer pounds.
What makes it more than a list is that a session is written before it is lifted. A
program expands into ordinary set rows — warmup ramp included, every weight rounded to
something a bar can actually be loaded to — so lifting is tapping through a list that
already knows what comes next, and each row keeps the prescription beside what was
actually done. An MCP endpoint exposes the same data to an LLM as a connector, which is
the second half of this file.
The stack is Roda and Sequel on Postgres, with Rodauth for accounts and `rodauth-oauth`
for the OAuth 2.1 authorization server the MCP endpoint authenticates against. Views are
ERB with Tailwind from a CDN, and htmx for the parts of the session screen that have to
change without a page load. It deploys to Render.
## What it does
- **Session view** (`/workouts/:id/session`) — the gym-floor screen, as distinct from
`/workouts/:id`, which is the record of one. A progress bar across the top, sets grouped
by lift with warmups dimmed, the per-side plate breakdown under each weight ("per side
1×25 1×10"), and a Done button per set that toggles, so a mis-tap is undone by tapping
again. A set lifted differently is entered inline and the row turns amber rather than
lime, because done-as-written and done-differently have to read differently. The whole
session gets an RPE rating, with a bar-speed guide for choosing one.
- **Exercises** — 54 built-in barbell movements shared by every account, plus any you add
yourself. A built-in exercise is visible to everyone but the sets shown under it are
only ever your own.
- **Workouts** — list, record view, reschedule, delete.
- **Programs** — the engine below. Authored in Ruby and run from rake; there is no UI for
editing one yet.
- **Accounts** — Rodauth login, account creation, logout, remember-me.
- **MCP** — audited, per-account tools over an OAuth 2.1-authenticated endpoint at `/mcp`.
## Getting started
You need Ruby at the version in `.ruby-version` (the `Gemfile` reads the same file, so the
two cannot drift), a local Postgres, and — only for the browser specs — Firefox with
geckodriver.
```
bundle install
npm install # only for the stylesheet; see below
```
### Environment
`dotenv` loads `.env` when the app is required, and `.env-example` is a file to copy:
`cp .env-example .env` gives a working development setup, pointed at the
`tectonic_development` database `rake db:create` makes below. The keys that are secrets in
a deployment are commented out there rather than left blank, each with the command that
generates it, because a blank is worse than an absence here — dotenv sets the name to the
empty string, every fallback in this project tests for nil, and an empty `DATABASE_URL`
reaches `Sequel.connect` and raises rather than falling back to a default.
| Variable | Required | Notes |
| --- | --- | --- |
| `DATABASE_URL` | yes | `app.rb` connects at require time, so nothing loads without it. `.env.rb`, which only the `Rakefile` reads, defaults it from `RACK_ENV` to `postgres:///tectonic_development` or `postgres:///tectonic_test`, but it defaults with `||=`, so a value in `.env` wins and that default never fires. A test run is the exception: `spec_helper` names its own database whatever the environment already held, so a `.env` cannot reach the suite. |
| `SESSION_SECRET` | yes | **At least 64 bytes.** Roda's sessions plugin refuses a shorter one, and `app.rb` builds the app at require time, so a short or missing secret raises before a single route is reached. `.env-example` carries one that is long enough and says in its own text that it is for development; generate a real one with `ruby -rsecurerandom -e 'puts SecureRandom.hex(64)'` for anything deployed. |
| `RACK_ENV` | no | `development` unless set, and `development` is the only value that turns Sequel's query log on; `production` and `staging` initialise Sentry and require real OAuth keys. |
| `DB_LOG` | no | Attaches Sequel's query log to stdout whatever `RACK_ENV` says — set it to anything non-empty to trace queries against a deployment, and unset it again afterwards. Sequel logs statements with their bound values, so those lines carry email addresses and everybody's weights and reps, which is why nothing but development logs by default. |
| `SENTRY_DSN` | no | The Sentry project DSN, read only when `RACK_ENV` is `production` or `staging`. Without it — unset or empty — the app boots and serves with error reporting switched off and says so once on stderr: losing error reporting is not a reason to refuse to start, which is why this behaves unlike `OAUTH_JWT_PRIVATE_KEY`. |
| `RACK_TIMEOUT_SERVICE_TIMEOUT` | no | Seconds a request may hold a thread before `rack-timeout` raises inside it, `20` unless set, `0` to switch it off — which is what you want locally the moment you stop in a debugger. Read only by `config.ru`, so the suite never sees it. It covers both legs of the URLMap, MCP included; the MCP endpoint's long-lived `subscriptions/listen` streams are unaffected either way, because rack-timeout times `app.call` and a streaming body is written after that has returned. |
| `RAILS_MAX_THREADS` | no | `5` unless set, and read twice: `config/puma.rb` gives Puma that many threads and `lib/tectonic/db.rb` gives Sequel that many connections. One variable because the pool used to be Sequel's default of four against Puma's five threads, and a fifth request thread waiting on a connection is a `Sequel::PoolTimeout` on a request that had nothing wrong with it. The name is the host's convention rather than a claim about Rails. |
| `WEB_CONCURRENCY` | no | Puma workers, `0` unless set — one process, which is what `rackup` was already running, and as much as half a CPU has to offer. Above zero Puma forks, so the app is preloaded and each worker disconnects Sequel on boot rather than sharing the parent's connections. Every worker multiplies the connection count by `RAILS_MAX_THREADS`. It is also what switches `worker_timeout` on: Puma enforces that in cluster mode alone, so at the default of zero nothing reaps a request still running after the response was handed back, and `RACK_TIMEOUT_SERVICE_TIMEOUT` is the only timeout in play. |
The MCP and OAuth variables are all optional in development and are documented in the
table further down.
### Database
```
bundle exec rake db:create # createuser tectonic, then the development and test databases
bundle exec rake db:migrate # applies migrate/001_schema.rb against DATABASE_URL
bundle exec rake library:exercises # loads the 54 built-in movements, idempotent on name
```
`db:create` shells out to `createuser -U postgres` and `createdb -U postgres -O tectonic`,
so it assumes a superuser role named `postgres` and gives the databases to a `tectonic`
role. If your Postgres has neither, `createdb tectonic_development` by hand and go
straight to `db:migrate`, which only needs `DATABASE_URL` to point somewhere it can
connect.
The schema is one squashed baseline. `db:migrate` stamps a database that already carries
the tables rather than trying to rebuild or roll it back, so an existing database from
before the squash adopts the baseline instead of breaking on it.
### Running it
```
bundle exec rackup config.ru # http://localhost:9292
```
`config.ru` mounts two apps side by side under `Rack::URLMap`: the MCP endpoint at `/mcp`,
which has its own bearer-token auth and never touches Roda's sessions or CSRF, and the
Roda app at everything else. `/` redirects to `/welcome` until you are logged in, so start
by signing up at `/create-account`.
### The stylesheet
```
npm run build:css # assets/css/app.css -> assets/css/styles.css
```
`assets/css/styles.css` is compiled from `assets/css/app.css` and **committed**, so nothing
but a stylesheet edit needs Node at all — `bundle install` and a database are enough to run
the app and the suite.
Rebuild it after adding a Tailwind class that was not already in use. CI rebuilds and fails
on a difference, so a stale file is caught before it ships rather than after: a purged class
breaks nothing anywhere except on the page it was meant to style, in production, silently.
`tailwind.config.js` scans `app.rb` and `lib/**/*.rb` as well as the templates, because
`button_style`, `rpe_style`, `row_style` and the calendar's `STYLES` table build class names
in Ruby and appear in no template.
It used to be `<script src="https://cdn.tailwindcss.com">` — the Play CDN, which compiles in
the browser. That meant no CSS at all with JavaScript off, and a third party on every page
including the consent screen.
## Running the tests
```
bundle exec rake test # the whole suite
bundle exec ruby -Ispec spec/set_scheme_spec.rb # one file
```
Neither variable has to be passed in. `spec_helper` sets `RACK_ENV` and then names the
database itself — `TEST_DATABASE_URL` when a run wants one of its own, and
`postgres:///tectonic_test` otherwise — whatever the shell, a `.env` or the `Rakefile` had
already chosen. That matters more than it looks: `spec_helper` empties every table after
every test, so a suite pointed at your development database would not seed it, it would
clear it. The built-in exercise library is the one thing kept, because a deployed database
holds it before anybody signs up. `rake 'db:reset[name]'` still rebuilds a database whose
schema has gone wrong, but a run no longer leaves rows behind for it to clear away.
Two prerequisites, neither obvious from the failure you get without them:
- **Postgres, migrated.** `app.rb` connects at require time, so even the pure unit specs
need a live database as soon as `spec_helper` loads the app.
- **A real Firefox.** `spec/exercises_spec.rb`, `route_ownership_spec.rb`,
`sessioWhat people ask about tectonic
What is stephaniewilkinson/tectonic?
+
stephaniewilkinson/tectonic is tools for the Claude AI ecosystem. Track your plates and chart your progress. It has 1 GitHub stars and its last recorded update is dated 2026-09-15.
How do I install tectonic?
+
You can install tectonic by cloning the repository (https://github.com/stephaniewilkinson/tectonic) or following the README instructions on GitHub. ClaudeWave also provides quick install blocks on this page.
Is stephaniewilkinson/tectonic safe to use?
+
Our security agent has analyzed stephaniewilkinson/tectonic and assigned a Trust Score of 67/100 (tier: OK). See the full breakdown of passed checks and flags on this page.
Who maintains stephaniewilkinson/tectonic?
+
stephaniewilkinson/tectonic is maintained by stephaniewilkinson. The last recorded GitHub activity is dated 2026-09-15, with 18 open issues.
Are there alternatives to tectonic?
+
Yes. On ClaudeWave you can browse similar tools at /categories/tools, sorted by popularity or recent activity.
Deploy tectonic to your cloud
Ship this repo to production in minutes. Each platform spins up its own environment with editable env vars.
Maintain this repo? Add a badge to your README
Drop the badge into your GitHub README to show it's tracked on ClaudeWave. Each badge links back to this page and reflects the live Trust Score.
[](https://claudewave.com/repo/stephaniewilkinson-tectonic)<a href="https://claudewave.com/repo/stephaniewilkinson-tectonic"><img src="https://claudewave.com/api/badge/stephaniewilkinson-tectonic" alt="Featured on ClaudeWave: stephaniewilkinson/tectonic" width="320" height="64" /></a>More Tools
A single CLAUDE.md file to improve Claude Code behavior, derived from Andrej Karpathy's observations on LLM coding pitfalls.
An AI skill that provides design intelligence for building professional UI/UX across multiple platforms.
🪨 why use many token when few token do trick — Claude Code skill that cuts 65% of tokens by talking like caveman
CLI proxy that reduces LLM token consumption by 60-90% on common dev commands. Single Rust binary, zero dependencies
The fastest, litest AI Gateway. Rust core with Python SDK. Call 100+ LLM APIs in OpenAI (or native) format with cost tracking, guardrails, load balancing, and logging [Bedrock, Azure, OpenAI, Anthropic, OpenAI, VertexAI, vLLM, Nvidia NIM]
Use Claude Code, Codex, Pi, and OpenCode (and 6 other harnesses) for free (1.3B+ free tokens) from your terminal, app, IDE, or phone, and now from the browser with native browser sessions (multi-harness + multi-model) like OpenClaw (voice supported + ToS friendly)