BF Readup is a Redmine plugin that tracks reading behavior per user and per issue and presents this data in clear, actionable views on My Page.
Unlike traditional “read/unread” solutions based on status flags or timestamps, BF Readup is built around actual user interaction:
- when an issue is opened
- how long it is viewed
- which journals were actually visible
- when the user can be proven to have seen the latest change
The result is a far more accurate representation of what is genuinely new to you.
Shows issues with new activity since you last confirmed reading them.
Issues are prioritized using rule-based logic that takes roles, mentions, history, and system policies into account.
The column set and column order in this view are currently hard-coded by design.
While the order is expected to remain stable, future versions may allow users to hide individual columns via personal settings.
The screenshot shows both the Recently visited and Most read widgets side by side.
These widgets intentionally display fewer columns than the Updates since you last read widget.
They focus on interaction history and reading time rather than change analysis, and the column sets are therefore deliberately different.
Shows where time is actually spent, based on accumulated reading time per issue.
BF Readup widgets integrate directly into Redmine’s My Page and can be freely combined with standard widgets.
-
Updates since you last read
- Prioritized list of issues with new changes
- Shows number of new entries and their authors
- Manual and bulk “mark as read”
- Policy-based restrictions on what can be marked as read
-
Recently read issues
- When you actually last read the issue
- Sorted by real interaction, not metadata
-
Most read issues
- Time-based statistics per user
- Shows where attention is actually spent
-
Advanced priority rules
- Rule-based priority engine
- Supports roles (assigned, author, watcher, mentioned)
- Custom Field matchers (e.g. matching user email)
-
Debug mode
- Shows raw data, decision logic, and filter results
- Local only via LocalStorage
Users can control which prioritization rules are active, while system policies may enforce mandatory rules that cannot be disabled.
Administrators can configure:
- heartbeat interval
- lookback period
- maximum priority allowed for “mark as read”
- full priority rule ordering
- custom field matchers
Internally, BF Readup uses a Custom Field matcher to identify the initiator of an issue.
This is based on an internal custom field that represents the person who initiated the issue, which may differ from the user who technically created it in Redmine.
Key characteristics of this setup:
- the initiator field is separate from Redmine’s user list
- it may contain values for people without system accounts
- the field is kept synchronized, but not coupled, to Redmine users
- this allows external participants or non-logged-in staff to be treated as first-class initiators
This approach reflects real-world workflows where the originator of work is not always the same person who registers the issue in the system.
BF Readup consists of three main parts:
bf_readup_visitstable for tracking read interactions- A QueryEngine that determines:
- what is new
- what is prioritized
- what is allowed to be marked as read
- Full interaction history stored in
extra_data
- Turbo-safe initialization
- Diff-based rendering (FLIP-inspired)
- LocalStorage caching for performance
- Time-based columns updated dynamically
- My Page blocks (Updates / Recent / Most)
- Issue view (heartbeat + journal detection)
- Admin UI for rules and settings
The requirement is intentional and architectural, not incidental.
We explicitly depend on PostgreSQL in order to build dynamic and evolvable data models for how issues are read, revisited, and analyzed over time.
Concretely:
- All analytical, behavioral, and extensible data is stored in
extra_data(jsonb) - This enables:
- schema-less evolution of analytics
- forward-compatible feature development
- per-issue, per-journal, per-user behavioral modeling
- Querying and indexing rely on PostgreSQL JSONB features, including GIN indexes
We deliberately separate data concerns.
Classical relational columns are used only for:
- last viewed timestamp
- user reference
- issue and journal references
- heartbeat and session tracking
- fast, deterministic lookups (who last viewed what, and when)
All analysis and derived data lives in jsonb:
- read and revisit patterns
- change sensitivity
- prioritization signals
- scoring and ranking models
- future analytical extensions
This keeps the relational schema stable and minimal, while allowing the analytical model to evolve without schema churn.
➡️ The plugin is PostgreSQL-only by design.
Support for MySQL or other databases is explicitly out of scope.
No degraded or fallback compatibility mode is planned, as such a mode would undermine the core data model and long-term design goals.
BF Readup is:
- ❌ not a commercial plugin
- ❌ not a community-driven roadmap project
- ❌ not designed for maximum general compatibility
It is instead:
- ✅ an internal tool developed alongside a Redmine rollout
- ✅ a practical solution to real, day-to-day needs
- ✅ an open codebase that others are welcome to learn from
I am explicit that:
- the project evolves based on my and my company’s needs
- users “follow along” with the project’s direction, not the other way around
- third-party demands on roadmap or direction are not accepted
💡 Good ideas, improvements, and bug fixes are always welcome.
This project was developed with ChatGPT as a co-creator.
This is:
- openly acknowledged
- a deliberate part of the development workflow
- not hidden or downplayed
Architecture, implementation, and decisions are always human-reviewed and intentional.
No formal license has been defined yet.
Until then:
- use the code at your own risk
- no guarantees are provided
- no long-term support commitments are implied




