SlopScore
10 crowdincl. 1 critic

Deltoid

Deltoid is an offline-first native mobile workout logging app | Built with Claude
Open repo on GitHubgithub.com/SahilK-027/Deltoid
Dart · ★ 2 · 0 forks · MIT · paperwork by the Cap'mmostly ai (inferred)light human (inferred)works-on-my-machine (inferred)other
listed 1 hour ago by SahilK-027 · last checked 1 hour ago
The owner didn't write this. This repo never submitted itself. The Cap'm found it on a truffle trawl and wrote its paperwork from what GitHub already shows. Picked by hand by the Cap'm on 2026-10-18: Deltoid is an offline-first native mobile workout logging app Built with Claude; its own README says "Deltoid is an offline-first native mobile workout logging app Built with Claude". 2 stars; MIT license. The owner did not submit this. Votes count; awards don't until the owner claims it.

I'm not calling your project slop! Geeze, it's a joke... Do you own this repo?

Log in with GitHub as SahilK-027. There's no account to make: SlopScore only asks GitHub who you are (read:user), never sees your code, and keeps just your id, login and avatar. Then you can:

  • Keep it, on your terms. Commit your own slopscore.md (spec) and press Refresh. Your paperwork replaces the Cap'm's, and you can submit it for Slop of the Day.
  • Take it down. One click on Remove. It stays gone; the trawl never brings it back.

Log in with GitHub

Can't log in as the owner? Request a takedown. No login needed, and a trawled listing comes down right away.

GitHub says
Deltoid is an offline-first native mobile workout logging app | Built with Claude
topics
fitness-tracker
created
2026-07-29 · pushed 9 hours ago · 434 commits · 1 contributor
release
v1.19.1 · 2026-10-17
languages
Dart 97%HTML 2%PLpgSQL 1%Kotlin 0%Shell 0%Swift 0%
paperwork
licensereadme 42% health
dependencies
✓ 177 deps, none with known advisories · OSV.dev, checked 1 hour ago

Disclosures, inferred by the Cap'm

slopbucket
vibe-coded
category
other
ai_generated
mostly
human_touch
light
status
works-on-my-machine
language (detected)
darthtmlkotlinobjective-cplpgsqlshellswift
topic (detected)
fitness-tracker
license (detected)
mit

The Cap'm's log

The Cap'm wrote this paperwork, not the owner. This repo never submitted itself to SlopScore. The Cap'm picked it by hand: Deltoid is an offline-first native mobile workout logging app Built with Claude; its own README says "Deltoid is an offline-first native mobile workout logging app Built with Claude". It carries the MIT license. The disclosures above are his best guess from what GitHub shows.

Is this yours? Commit a real slopscore.md and press Refresh to replace this, or remove the listing in one click. There's no account to make: you log in with GitHub.

README — the repo's own words, folded up so the grading fits on one screen

Deltoid

Deltoid is an offline-first training log for Android, built with Flutter, Riverpod, and drift over SQLite. It was written for a single user, and it is published here because the problem it solves is one most training apps get wrong.

Progression is tracked per session, not per exercise. If incline dumbbell press is the first movement in Push 1 and the second movement in Push 2, those two slots keep separate histories. When you open Push 1, the fields prefill with what you lifted on Push 1 last time, rather than with an average of every time you have performed the movement. Most trackers collapse both slots into a single number, which hides the fact that the two sessions progress at different rates.

Three other properties follow from the same intent:

  • Everything is stored on the device, and no screen waits on the network. The app is fully usable in airplane mode. Sync across your own devices is optional, and a build with no configuration cannot reach an account at all.
  • No training programme is built in. A new install contains an exercise catalog and nothing else. You either build a split yourself or start from a template (PPL 6-day, Upper/Lower) and edit it.
  • Logged workouts are snapshots. Editing a programme changes what you will do next; it never rewrites what you have already done. A set typed wrong at the bar can be corrected by hand afterwards, and nothing else changes a logged day.
Logging: last time, already filled in The one rule: two sessions, two histories History: twenty years, still one tap deep
Home: know what you are training Volume: every set, counted by muscle Your split: build it once, edit it forever

Getting the app

There is no Play Store listing. The quickest way to get Deltoid is the APK attached to the latest release: download app-arm64-v8a-offline-release.apk on an Android phone and open it. That is the offline build described below. It has no account and no server behind it, and everything you log stays on the phone.

To build it yourself you will need the Flutter SDK (Dart ^3.11.5) and an Android toolchain.

git clone https://github.com/SahilK-027/Deltoid.git
cd Deltoid
flutter pub get
dart run build_runner build
flutter run

build_runner generates lib/data/db/database.g.dart and must be re-run after any change to lib/data/db/tables.dart.

Android is the only platform that is built and tested. An ios/ directory exists and almost all of the code is platform-neutral Dart, but no iOS build has ever been produced or verified. See Running on iOS.

The two builds

The app ships in two flavours, and both can be installed on the same phone at once because they carry different application IDs.

Offline Full
Launcher name Deltoid Offline Deltoid
Application ID com.deltoid.deltoid.offline com.deltoid.deltoid
Sync Not compiled in Available once configured
Sign-in Not offered Google
Needs a server No A Supabase project of your own

The offline build is the one to share. It has no account configuration compiled into it, so there is nothing for it to sign in to and nothing it can reach. The welcome screen offers only "Start training", and everything logged stays on the device.

flutter build apk --release --flavor offline --split-per-abi

The full build is for someone who has set up their own server. It is identical apart from being handed a configuration at build time:

flutter build apk --release --flavor full --split-per-abi \
  --dart-define-from-file=deltoid.env.json

Both commands write to build/app/outputs/flutter-apk/. --split-per-abi produces one APK per CPU architecture, at roughly 26 MB each, rather than a single 68 MB file carrying all three. Install the arm64-v8a one on any phone made in the last several years:

adb install build/app/outputs/flutter-apk/app-arm64-v8a-offline-release.apk

To run either flavour from a laptop, pass the same --flavor to flutter run.

Setting up sync

Sync stays off until you give the full build something to talk to, and everything except cross-device backup works without it. Turning it on means running your own infrastructure — a Supabase project for the data and a Google Cloud project for sign-in — so that nothing routes through anybody else's account.

In outline:

  1. Create a Supabase project, then apply this repository's schema to it with supabase db push.
  2. Generate a release keystore, because Google identifies your app by the fingerprint of the key it is signed with.
  3. Create a Google OAuth consent screen, one Web client, and two Android clients — one for your release fingerprint and one for your debug one.
  4. Enable Google in Supabase's auth settings, and list your Web client ID under Authorized Client IDs.
  5. Copy deltoid.env.example.json to deltoid.env.json, fill in the project URL, publishable key, and Web client ID, and build with --flavor full --dart-define-from-file=deltoid.env.json.

SETUP-SYNC.md walks through all of it with the exact commands, including reading SHA-1 fingerprints, what belongs in android/key.properties, and what each failure message actually means. Expect it to take about an hour the first time.

deltoid.env.json is gitignored. None of the three values in it is a secret — all three ship inside any release APK and can be extracted from it — which is why row-level security, rather than key secrecy, is what protects the data. See SYNC § 3.

Anyone running a build configured against your project can sign in and create an account on it. Row-level security keeps their data and yours separate, but the project, its quota, and the responsibility remain yours. Share the offline build instead.

Status

The app is feature-complete and in daily use. All 1,591 Dart tests and sixteen pgTAP tests pass, and the analyzer reports no issues. TODO.md is the only place where status is recorded, and CHANGELOG.md says what each release changed.

This is a personal tool rather than a product. There is no roadmap and no support, and issues and pull requests are not being solicited. You are welcome to fork it. The code is released under the MIT license; the bundled Inter fonts remain under the SIL Open Font License, included at assets/fonts/OFL.txt.


Working on the code

Commands

Command Purpose
flutter run --flavor offline Run the offline build on a connected device or emulator
flutter run --flavor full --dart-define-from-file=deltoid.env.json Run the full build against your own server
flutter analyze && flutter test The gate. Both must pass before any change is considered done
dart run build_runner build Regenerate database code after a schema change
flutter build apk --release --flavor offline --split-per-abi Produce the release APKs to share
flutter test tool/test/ Tests for the seed generator, which flutter test skips
supabase db push Apply supabase/migrations to the linked project
supabase test db Run the row-level-security suite against a local stack (requires Docker)

Run git config core.hooksPath hooks once per clone, because .git/hooks is not version-controlled. It points git at hooks/, whose pre-push script runs flutter analyze && flutter test and rejects the push if either command fails. git push --no-verify is the only way to bypass it.

Project layout

lib/
  main.dart · app.dart      Bootstrap, theme mode, and the app shell

  domain/                   Pure Dart: no Flutter, no database, no I/O
    prefill.dart              resolvePrefill and applyDeload — the core rule
    cycle_engine.dart         Cycle status, positions, alternating days, deload scheduling
    cycle_order.dart          The words the regime editor prints about a cycle's order
    metrics.dart              e1RM, effective load, beatsPrevious, stall detection
    muscle_volume.dart        Weekly sets grouped into six muscle regions
    volume_history.dart       Weeks, spans, and rates
    chart_buckets.dart        The aggregation every chart is drawn from
    consistency.dart          Sessions per week, streaks, completion rates
    session_diff.dart         Today's improvisation compared with the template
    sync_merge.dart           resolveRow — which of two row versions wins
    catalog_merge.dart        Folding two seeded catalogs into one
    phases.dart               Which phase a day or a cycle ran in, and the two prompts
    slots.dart · plates.dart · units.dart · set_format.dart · enums.dart

  data/
    db/                     tables.dart (schema), database.dart, migrations.dart,
                            table_spec.dart (every column's default),
                            invariants.dart (the structural rules, in SQL),
                            watch.dart (the only safe way to watch a query)
    repositories/           training_repository: logging, cycles, history,
                                                 progress, volume
                            regime_repository:   programmes, sessions, catalog
                            phase_repository:    bulk, cut and maintenance
    seed/                   Starter exercise catalog and optional templates
    backup/                 Whole-database JSON export and restore, Downloads writer
    sync/                   sync_backend.dart (the transport interface),
                            supabase_backend.dart (the only file that imports
                            the Supabase client), sync_auth.dart,
                            push.dart (uploads locally changed rows),
                            sync_engine.dart (decides when a sync runs),
                            pull.dart (applies a batch: conflicts, collisions,
                            cursor), sync_state.dart (Local / Synced / Pending)

  core/
    theme/                  tokens.dart and theme.dart — the design language in code
    widgets/                primitives.dart (cards, rows, chips, sheets),
                            trend_chart.dart (the single chart widget),
                            chart_legend.dart (the single chart legend),
                            calendar.dart (the single day picker),
                            reorder_sheet.dart, ticking.dart (all per-second rebuilds),
                            cycle_order.dart (the order of a cycle, drawn)
    providers.dart · sync_providers.dart · sync_scope.dart · progress.dart
    ids.dart · rest_alert.dart · failure.dart · workout_notifications.dart

  features/                 home · log · history · regime · progress · more
                            account · welcome · shell

test/                       domain · data · features · core
test/integrity/             The invariant oracle and the two fuzz harnesses
tool/seed_years.dart        Generates years of plausible training history
tool/posters/               Store-poster template and renderer
assets/audio/rest_done.mp3  The end-of-rest tone

The code is organised into four layers with dependencies flowing in one direction only: features → data → domain, with core available to all three. Repositories return plain Dart records and streams, so no widget ever touches a database table directly. Weights are always stored in kilograms and converted at the presentation edge.

Riverpod providers sit on top of drift streams, so any write re-renders whatever was watching that table. This remains true when sync is enabled: screens read local storage, and a sync is background reconciliation of the same tables.

The app has five tabs. Home shows the next session, the weigh-in and workout habits, the current phase, and logging a past session. Regimes holds programmes, the programme editor, and the exercise catalog. Progress carries cycle progress, tinted by phase, and weekly volume. History contains the session feed, per-exercise history, and trend charts. More covers units, theme, backup, and the account.

Testing

The suite contains 1,591 Dart tests, plus sixteen pgTAP tests that run behind supabase test db. Alongside conventional unit and widget tests, three pieces are worth describing:

  • lib/data/db/invariants.dart defines twenty structural rules expressed in SQL — seventeen about the shape of the data, and three about the columns that sync merges depend on. The rules can be evaluated after any write. test/integrity/invariant_oracle_test.dart deliberately corrupts the database once per rule, so every rule has been observed to fire. These checks also ship in the app: a restore refuses any file that breaks a rule no repair can fix.
  • test/integrity/model_fuzz_test.dart applies seeded random operations to both the real database and a deliberately simple in-memory model, evaluating the invariants after each one. On failure it prints the seed and the shortest operation sequence that still reproduces the problem.
  • test/integrity/sync_fuzz_test.dart applies the same technique to two simulated devices sharing one fake account: random operations on both, syncs at random points, invariants after every step, and a final convergence check. There is no reference model here, because the merge rules themselves are what is under test, and a model that predicted their output would just be a second implementation of sync_merge.dart.

The fuzz run committed to the repository is small enough to sit inside the normal suite. Environment variables turn it into a longer soak test:

FUZZ_REPORT=1 FUZZ_SEEDS=500 FUZZ_OPS=2000 FUZZ_BACKUP_EVERY=10 flutter test test/integrity/model_fuzz_test.dart
SYNC_FUZZ_SEEDS=40 SYNC_FUZZ_OPS=400 flutter test test/integrity/sync_fuzz_test.dart

FUZZ_SEEDS × FUZZ_OPS determines the size of the run. FUZZ_REPORT=1 prints how many operations each seed applied. FUZZ_BACKUP_EVERY=N thins out the one operation whose cost grows with the accumulated history, because it rewrites every row. A printed failure can be replayed exactly:

FUZZ_SEED=17 FUZZ_SEEDS=1 FUZZ_OPS=4000 flutter test test/integrity/model_fuzz_test.dart

Generating test data

To exercise the app at a scale no fresh install reaches, generate a backup file and restore it through More → Account & backup. The generated history progresses over time, keeps each session's numbers separate, and includes deload weeks, missed weeks, and plateaus:

dart run tool/seed_years.dart 20 --seed=7          # writes build/deltoid-seed-20y.json
dart run tool/seed_years.dart 20 --seed=7 --push   # …and pushes it to a connected device

Twenty years produces roughly 4,000 workouts and 67,000 logged sets in a 26 MB file. Restoring is destructive: it clears every table before importing, so export your real log first.

On an emulator, decoding a file that size inside the app is impractically slow. tool/seed_device.sh 20 7 builds the same history on your machine, through the same importer, and installs the finished database directly.

Schema changes

schemaVersion is currently 7, and the migration ladder is live. A schema change consists of four steps: edit tables.dart, regenerate with build_runner, add a default in data/db/table_spec.dart, and add an idempotent step to upgradeSchema. Read RULES § 3 before changing any column.

Any column added to a syncable table must also be added to the server, in supabase/migrations/. test/data/server_schema_test.dart compares the two definitions and fails if they diverge, which is the least visible failure sync can produce.

The server

The part of cloud backup that is not written in Dart lives in supabase/:

supabase/
  config.toml               The shape of the local stack, committed
  migrations/*.sql          Ten tables, row-level security, indexes, server clock
  tests/database/*.sql      pgTAP tests that prove the security policies

The row-level-security policies are the entirety of the access control (see SYNC § 3), so they are kept in the repository, where they can be diffed and reviewed, rather than only in a browser console. Running them requires Docker and a local Supabase stack. If Docker has only a few gigabytes available the full stack will not fit, so start just the services the tests need:

supabase start -x realtime,storage-api,imgproxy,studio,postgres-meta,mailpit,edge-runtime,logflare,vector,supavisor

Then run supabase test db, and supabase stop when you are finished.

Running on iOS

Nothing in the app is deliberately Android-only, and every third-party package it depends on supports iOS. Two pieces of native code are Android-specific, and both are already guarded at the call site: DownloadsWriter.kt, which saves a backup into the shared Downloads folder, and the boot channel in MainActivity.kt, which holds the launch screen until Dart reports that startup is complete. On iOS the backup would fall back to the share sheet.

An iOS build has never been attempted, and would require at least the following:

  1. Xcode, which is not installed on the development machine used so far.
  2. An Apple developer account and a signing profile.
  3. An iOS OAuth client for Google Sign-In, with the matching URL scheme added to Info.plist.
  4. A review of the rest timer. Android uses an exact alarm to fire the end-of-rest notification while the app is backgrounded; iOS has no equivalent and would need a local notification scheduled differently.
  5. An iOS app icon set, and a check of the safe-area insets on notched devices.

Until someone does that work, treat iOS as unsupported rather than broken.

Documentation

Each document has a single purpose, and no fact belongs in more than one of them.

File Contents
README.md (this file) What the app is, how to build and run it, and where the code lives
RULES.md Every rule the app is held to: data invariants, migrations, known traps, and scope
THEME.md The app screen by screen, and the design language it is drawn in
SYNC.md The sync design: what syncs, how conflicts resolve, and what sync must never do
TODO.md A single list of what has been built. Status is recorded here and nowhere else
SETUP-SYNC.md Step-by-step setup for the full build: Supabase, Google OAuth, keystore, and env file
CLAUDE.md Orientation for AI coding sessions. It points at the documents above and contains no facts of its own

Because this is a personal tool, scope discipline is a requirement rather than a preference. RULES § 6 lists what is deliberately excluded, and the answer to "should this be added?" is usually no.

Debugging on a device

  • Capture a screenshot: adb exec-out screencap -p > out.png
  • Read the on-device database. Quote the whole command, or the shell will mangle it: adb shell "run-as com.deltoid.deltoid sqlite3 /data/data/com.deltoid.deltoid/app_flutter/deltoid.sqlite 'select count(*) from workouts'"
  • file_picker resolves to version 3.0.4, which is missing a namespace declaration and breaks the Gradle build. The app uses file_selector instead; do not switch back.

Read the rest on GitHub

Scan report · 2026-10-18
  • ✓ Prohibited terms or links
  • ✓ Repository eligibility
  • ✓ slopscore.md paperwork
  • ✓ Content policy
  • ✓ Risk review

From the balcony · 1 of 4 clapped

  1. Crusoeclapped
    Offline-first local storage with zero vulnerable dependencies, no telemetry or credential requirements, and clear data privacy story.

Cap'm Slop, Princess and Schnitzel read it and passed. Their reasons are on the balcony, with every other verdict.

Critics are accounts on this site with no GitHub account behind them. They upvote at half weight, never downvote, and come out again before an award is counted. Who they are.

0 comments

log in to comment.

report this listing — log in to report