Update handoff docs for completed engine layer
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled

Mark engine as done in overview and build order. Update next-steps to
focus on test gap analysis. Add engine file map, data flow, and dev
setup quirks (Zeitwerk conflict, engine routes conflict) to architecture.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-08 20:14:19 -06:00
parent 286db51fcd
commit ab1f29269e
3 changed files with 91 additions and 58 deletions

View File

@@ -4,13 +4,28 @@
```
lib/
whetrails.rb Public entry point. Exposes Whetrails.inspect_model(ModelClass)
whetrails.rb Public entry point. Requires engine if Rails is loaded.
whetrails/
version.rb VERSION constant (0.1.0)
chain.rb Data structures (ModelChain, CallbackEntry, ValidatorEntry)
inspector.rb Core engine — reads Rails internals, builds ModelChain
formatter.rb Terminal renderer — formats ModelChain as plain text
cli.rb CLI logic — boots Rails, resolves model name, calls inspector
engine.rb Rails::Engine definition (isolate_namespace Whetrails)
engine_routes.rb Engine routes — resources :models + root
app/
controllers/whetrails/
application_controller.rb Base: before_action loads all user-defined AR models
models_controller.rb index (redirect to first model), show (render chain)
helpers/whetrails/
models_helper.rb vscode_uri, short_path, condition_label, validator_type_label
views/
layouts/whetrails/
application.html.erb Layout: sidebar model list + main content area
whetrails/models/
index.html.erb Empty — index action always redirects
show.html.erb Lifecycle timeline + validators table
exe/
whetrails Executable entry point — calls Whetrails::CLI.run(ARGV)
@@ -22,6 +37,8 @@ app/models/order.rb Development fixture model with sample callbacks/
## Data flow
### CLI
```
exe/whetrails
→ CLI#run_inspect
@@ -40,6 +57,23 @@ exe/whetrails
→ Formatter#render prints to stdout
```
### Engine (browser UI)
```
GET /whetrails/
→ ModelsController#index
→ eager_load! + discover user models via Object.const_source_location
→ redirect_to first model
GET /whetrails/models/:id (:id is the model name, e.g. "Order")
→ ModelsController#show
→ params[:id].constantize → model class
→ Inspector#inspect_model → ModelChain
→ renders show.html.erb: lifecycle timeline grouped by event,
each callback shows filter name, vscode:// source link, condition badges,
module badge if defined in a concern
```
## Key Rails API details
These are the actual Rails internals the inspector uses — documented here because they are
@@ -88,6 +122,34 @@ validator.options[:if] # => conditions (NOT instance variables — different
validator.options[:unless]
```
## Dev setup quirks
This repo is both the gem source and the development Rails app. That creates two issues
that were solved and should not be undone:
**1. Zeitwerk autoloading conflict**
`config/application.rb` has `config.autoload_lib(ignore: %w[assets tasks whetrails.rb whetrails])`.
The `whetrails.rb` and `whetrails` entries are critical: without them, Zeitwerk tries to
autoload `lib/whetrails/chain.rb` as `Whetrails::Chain` (wrong constant name), crashing on
every request. The gem files use manual `require_relative` — they must not be Zeitwerk-autoloaded.
**2. Engine routes conflict**
`lib/whetrails/engine.rb` overrides the engine's routing path:
```ruby
config.paths.add "config/routes.rb", with: "lib/whetrails/engine_routes.rb"
```
Without this, Rails uses `config/routes.rb` as the engine's routes file (same directory),
which causes `mount Whetrails::Engine` to execute twice and fail with a duplicate route name error.
Engine routes live in `lib/whetrails/engine_routes.rb` instead.
**3. Gem not in Gemfile by default**
The gem is listed as `gem "whetrails", path: "."` in `Gemfile`. This is intentional —
`Bundler.require` must explicitly load the gem so `Whetrails::Engine` is defined before
`config/routes.rb` tries to mount it.
## Known limitations
- **Proc source location**: Proc-based callbacks (e.g. `before_save { do_something }`) report

View File

@@ -1,64 +1,34 @@
# Next Steps
## Immediate: Rails Engine (browser UI)
## Immediate: Test Gap Analysis
Mount a UI at `/whetrails` inside the host Rails app. A developer adds the gem to their
Gemfile, mounts the engine in `config/routes.rb`, starts the server, and visits `/whetrails`.
The engine is done. The next layer uses the extracted chain as input to find uncovered
callbacks and validators.
### What the UI needs to show
### How it works
- A list of all models in the app (left sidebar or index page)
- Click a model → full chain view (same data as CLI, but visual)
- Lifecycle timeline: each stage as a row, callbacks as nodes within it
- Source file/line as a clickable link (opens in editor via `vscode://` URI scheme)
- Conditions rendered inline (`if: :paid?` shown as a badge)
- Callbacks from concerns/gems visually distinguished from model-defined ones
### Technical approach for the engine
```ruby
# lib/whetrails/engine.rb
module Whetrails
class Engine < ::Rails::Engine
isolate_namespace Whetrails
end
end
```
Rails engine conventions:
- Routes go in `config/routes.rb` inside the gem
- Controllers in `app/controllers/whetrails/`
- Views in `app/views/whetrails/`
- The host app mounts it: `mount Whetrails::Engine, at: "/whetrails"`
The engine controllers call `Whetrails::Inspector` directly — no duplication of logic.
### Model discovery
The engine index page needs to list all AR models in the host app:
```ruby
Rails.application.eager_load!
models = ApplicationRecord.descendants # or ActiveRecord::Base.descendants
```
## After engine: Test Gap Analysis
Once the chain is extracted, gap analysis is:
1. Run the test suite with SimpleCov (or equivalent) to get line coverage data
2. For each callback method in the chain, check whether its source lines are covered
3. For each validator, check whether the validation path is exercised
1. Run the host app's test suite with SimpleCov to get line coverage data
2. For each `CallbackEntry` in the `ModelChain`, check whether `source_file:source_line`
is covered in the SimpleCov report
3. For each `ValidatorEntry`, check whether the validation path is exercised
4. Report: "these 3 callbacks have no test coverage"
The chain data structure (`ModelChain`) is already the right shape for this — each
`CallbackEntry` has `source_file` and `source_line`, which can be cross-referenced
against SimpleCov's coverage report.
The `ModelChain` data structure is already the right shape — each `CallbackEntry` has
`source_file` and `source_line`, ready to cross-reference against SimpleCov output.
## Product / distribution
### Integration point
- Publish to RubyGems.org once the engine is working
- The gem should be dev/test-only (add to Gemfile under `group :development`)
Surface this in the engine UI: add a "Coverage" column to each callback row, showing
green/red/unknown based on whether SimpleCov data is available and whether the line
is covered.
SimpleCov writes JSON to `coverage/.resultset.json` after a test run. Parse that file
directly — no need to instrument the test suite beyond what SimpleCov already does.
## After gap analysis: distribution
- Publish to RubyGems.org
- The gem should be dev/test-only (add under `group :development`)
- Pricing model TBD — could be open source with a paid cloud version, or MIT with
a commercial license for teams
@@ -70,3 +40,5 @@ against SimpleCov's coverage report.
- Dev database: SQLite at `storage/development.sqlite3`
- Gitea running in Docker on localhost:3000 (container name: `gitea`)
- Gitea API token for `bill`: `c2948775b80bcaa0ba65c6359658d0d48c340c4e`
- Dev server runs on port 3100 (`bin/rails server -p 3100`)
- Visit `http://localhost:3100/whetrails` to see the engine UI

View File

@@ -10,20 +10,19 @@ This is the first half of a two-part tool. The second half (not yet built) uses
## Current state (as of 2026-06-08)
Two layers are complete:
Three layers are complete:
1. **Core inspector** — extracts the full chain from any ActiveRecord model class at runtime
2. **CLI**`whetrails inspect <ModelName>` boots the host Rails app and renders the chain to the terminal
The next layer (Rails engine / browser UI) has not been started.
3. **Rails engine** — browser UI mounted at `/whetrails`, sidebar model list, lifecycle timeline with vscode source links
## Build order
```
[DONE] 1. Gem core lib/whetrails/inspector.rb
[DONE] 2. CLI exe/whetrails + lib/whetrails/cli.rb
[NEXT] 3. Rails engine browser UI mounted at /whetrails
4. Test gap layer uses chain data to find uncovered callbacks/validators
[DONE] 3. Rails engine browser UI mounted at /whetrails
[NEXT] 4. Test gap layer uses chain data to find uncovered callbacks/validators
```
## Repository