13 KiB
Features / Roadmap: power-user Redmine client (redmine_tickets plane)
[!todo] Keep this file as the index: status, priority, and short summaries.
Long implementation detail lives indocs/raw-notes/features/*and is linked from here.
Update checkboxes when work lands; do not re-expand full specs into this file.
Last status refresh: 2026-07-15 (post browse-loop + attachments + entities + contacts bridge)
Product intent (what this is)
A scriptable power-user Redmine client for humans and agents:
- durable local ticket (+ contact) cache as the ops workbench
- property-based local search, saved filters, filesystem queues
- CLI / TUI-lite read path (Markdown +
glow/bat/fzf) then queued writes - place to invent/test filters, tools, and agent loops without living in the web UI
Not the system of record (Redmine is).
Not the vault/wiki (curated memory).
Not Qdrant (optional later semantic layer).
First project focus: customer-service.
| Env | Redmine | Cache | Wrapper |
|---|---|---|---|
| Prod | https://rm.ldrprep.com |
/home/jason/projects/redmine/cache/redmine_tickets |
bin/rtl |
| Dev / mutation testing | http://192.168.50.170 |
/home/jason/projects/redmine/cache-dev/redmine_tickets |
bin/rtd |
Safety defaults
- Writes/applies prefer LAN; prod needs parent
--allow-prod-writes notenever emails;customer-emailalways emails (ugly confirm flag)- Browse/actions queue drafts by default (apply later)
- Dual tools OK for now: tickets workbench vs full
redmine_contacts.pyCRM CLI, with bridges
Priority stack (current)
| Priority | Area | Status |
|---|---|---|
| P0 | Durable incremental ticket sync + remote filters | Done |
| P0 | Cron for CS refresh (+ queue rematerialize) | Running (6h window) |
| P1 | Property-based local search (--where) |
Done |
| P1 | Markdown view + walk | Done |
| P1 | Browse loop (fzf → pager → action menu) | MVP done (polish open) |
| P2 | Saved filters + symlink queues | Done |
| P2 | Queued notes / customer-email / set-fields | Done |
| P2 | Config wrappers (--config, rtl/rtd) |
Minimal done |
| P3 | Local control tags + suggest + JSONL export | Done (LLM refine open) |
| P3 | Contacts mirror + contact-set + ticket-contact | Done (cache unify optional) |
| P4 | Attachments list/download/upload | Done |
| P4 | Entity extract (patterns) + NMI bridge CLI | MVP done |
| Later | Full curses TUI / nested config DSL / master layout | Deferred |
| Adjacent | Semantic index / vault decompose / OCR rename skill | Separate tracks |
Capability map (what you can run today)
| Need | Command(s) |
|---|---|
| Refresh tickets | sync · cron: cron-scripts/sync-live-customer-service.sh |
| Search cache | search --where '…' · @saved · --control-tag · --has-attachments |
| Read ticket | view ID · walk · browse (loop) |
| Internal note | note (draft → draft apply) |
| Customer email | customer-email + confirm flag |
| Fields | set-fields (--status Closed, etc.) · meta-refresh |
| Local labels | `control-tag add |
| Contacts | contacts-sync · contacts-search · contact-show · contact-set · ticket-contact --fetch |
| Files | `attachments list |
| Tracking/order hints | `entities show |
| OCR scan rename | ocr-rename --issue-id / --where '@scanned-document' |
| Categories | categories list [--refresh] |
| Queues on disk | materialize-queues --config queues.example.json |
Cookbook: docs/cli_power_user_client.md
P0 — Sync foundation
Detail: features/sync-foundation
- Stable cache path (not per-run timestamped dirs)
- Incremental full-fetch skip when
updated_onunchanged - Server-side
updated_on+status_id(remote flags) - Local post-filters retained
- Sync progress +
--quietfor cron - Cache never auto-deletes old ticket JSON
- Run-stats ring buffer (
--run-log) - Cron: live + dev CS scripts; 6h remote window; rematerialize queues after sync
- Optional contacts cron:
cron-scripts/sync-live-contacts.sh - Confirm long-term cadence (currently ~every few minutes) is still desired
- Full fetch includes
attachmentsmetadata (not just journals/helpdesk)
./bin/rtl sync --remote-status-id '*' --remote-updated-since 2h --run-log --quiet
P1 — Local search
Detail: features/local-search-and-cli-client · redmine_ticket_query.py
Historical thin search: ../2026-07-14_localsearch_before_sanity
- JSON path filters +
includes()/since()/ AND/OR / parens - Nested helpdesk/contact paths
--ids-only, ordering, max caps- Table columns: assignee, contact, control tags
--has-attachments(cache metadata)- No default status filter — closed tickets appear if cached
- Optional default “open-ish” profile (if desired later)
P1 — Read path + browse loop
Detail: features/local-search-and-cli-client · redmine_ticket_view.py · redmine_ticket_browse.py
- Ticket → Markdown (
view) + MD cache undermarkdown/ - Pagers: glow / bat / batcat / cat
walk(n/p/q)browseloop: fzf (or numbered) → pager → action menu → list- Actions: view again, queue note/private note, queue fields, control-tag, contact handoff, entities, print id
- Mutations queue only
--once,--pager, filters same family as search
- Browse polish (user-noted “minor things”) — collect below under Open polish
- Full curses/TUI (linear-tui style) still later, not blocking ops
./bin/rtl browse --where 'assigned_to IS NULL' --max-tickets 80 --pager glow
P2 — Filters, queues, mutations
Detail: features/local-search-and-cli-client · drafts in redmine_ticket_drafts.py
saved-filters/*.expr+@namematerialize-queuessymlink foldersnotevscustomer-email(hard separation; Helpdeskemail_notepath)- Draft list/cancel/apply; draft binds
base_url; obviousdraft_id= set-fields+ name→id (meta-refresh)- Parent
--allow-prod-writes(not subparser-shadowed) - Minimal
--config+bin/rtl/bin/rtd/bin/qaa - Nested config defaults (
search.max-ticketsetc.) — features/config-files - Note body from file + optional pandoc→Textile (steering list)
./bin/rtl note --issue-id 41934 --body 'staff context' --private
./bin/rtl customer-email --issue-id 41934 --body '…' --i-understand-this-emails-the-customer
./bin/rtl --allow-prod-writes draft apply <id>
P3 — Control tags
Detail: features/control-tags · export: redmine_ticket_control_export.py
- Local wipeable store under
control-tags/tags.json - add / remove / list / show / for-issue / wipe
- suggest (
--whererules, optional--applylocal) export→ JSONL for training / refine- LLM refine loop (consume export, propose tags, human correct/incorrect)
P3 — Contacts
Detail: features/contacts-sync-integration · redmine_ticket_contacts.py
Full CRM still: redmine_contacts.py (separate cache/CLI by design for now)
- Durable mirror:
<cache>/contacts/ contacts-sync/contacts-search/contact-showcontact-setdraft/apply (tags, company, names,--set)ticket-contactbridge;--fetchimplies show+refresh (bugfix)- Contact column in ticket search
- Optional unify on-disk layout with
redmine_contacts.py(only if dual-cache pain grows)
P4 — Attachments
Detail: features/attachments · redmine_ticket_attachments.py
attachments list(cache-first,--refresh)attachments download(one /--all)attachments upload --apply(POST/uploads.json+ issueuploads; no email)- OCR rename workflow CLI (
ocr-rename) — features/ocr-rename
P4 — Entities + NMI
Detail: features/entity-type-mapper · redmine_ticket_entities.py · redmine_ticket_nmi.py
- Pattern extract: UPS/USPS, order/SKU/account + LDR
item_no - Shown in Markdown view
entities show|scanentities nmibridge tomrscClient-php bin/nmi(cs-brief / resolve)- Noise filters on medium-confidence
order_idhits - Richer NMI membership index (known order/item windows) if CLI alone isn’t enough
- One-key NMI from browse menu (
m→ private note draft)
Open polish / small bugs (browse + ops)
- Browse: fzf header/keybinding discoverability
- Browse: optional preview window in fzf (
--no-previewto disable) - Browse: louder draft queue banner (
announce_draft) - Browse:
m→ entities nmi brief → private note draft - Entity noise filters + LDR 9-digit
item_no - Note
--body-file(already) +--to-textile(pandoc) search/browse/walk--open-only- OCR attachment → rename (
ocr-rename) MVP - OCR cron pack for HR “Scanned document” inboxes
- categories create/update (list only for now)
Steering / next decisions (pick a lane)
A — Daily-driver polish — done for now
Reopen if browse UX still nags.
B — CS frontage / NMI / OCR — MVP+ done
Optional: OCR cron for HR; NMI membership index.
C — CRM / spam tooling ← good next lane
- Deeper
contact-set(custom fields) or keep routing toredmine_contacts.py - Control-tag LLM refine from JSONL export
- Issue categories create/update for triage taxonomy
D — Bigger product bets (defer unless intentional)
- Full curses TUI
- Unified contact cache
- Composable remote/local filter DSL
- Redmine project/version/custom-field admin
Explicitly out of this index (or later)
| Item | Notes |
|---|---|
| Semantic index v2 | semantic_index/ — feed from this cache |
| Vault decompose | separate track |
| Redmine admin: versions / custom field definitions / project modules | not ticket ops |
| Master global JSON + symlink org | nice-to-have |
Related docs
docs/cli_power_user_client.md— usage cookbookdocs/ticket_sync_tool_plan.md— original sync plandocs/2026-07-14_localsearch_before_sanity.md— historicaldocs/ticket_spam_cleanup_handoff_2026-07-09.md— spam consumersdocs/raw-notes/features/*.md— detail filesconfigs/live.json,configs/dev.json·bin/rtl·bin/rtd
Feature detail files
| File | Topic |
|---|---|
| features/sync-foundation | Sync / cache / remote filters |
| features/local-search-and-cli-client | Search, view, walk, queues, writes |
| features/control-tags | Local control tags |
| features/contacts-sync-integration | Contacts mirror + bridge |
| features/attachments | File attachments |
| features/entity-type-mapper | Entities + NMI |
| features/nmi-bridge | NMI env + browse m |
| features/ocr-rename | OCR subject rename |
| features/browse-loop | fzf browse loop |
| features/config-files | Config / aliases |
Maintenance rule
- Put full detail in
docs/raw-notes/features/<name>.md - Leave status + short bullets + link here
- Prefer updating the feature file over re-pasting long prose into this index
- When fixing a bug from Bug reports, check the box and leave the minimal repro snippet
Bug reports and QOL (resolved log)
--allow-prod-writes unrecognized / ignored
- Parent flag only; subparser copies removed (child defaults overwrote parent)
Draft id not obvious
draft_id=/apply_hint=key=value output
Drafts ignore create-time base-url
- Apply prefers draft
base_url; conflict needs--allow-base-url-override
Configs / short aliases
- Minimal
--config+bin/rtl|rtd|qaa
ticket-contact --fetch alone did nothing
--fetchimplies show + live refresh into contacts cache
Modules (code map)
| Module | Role |
|---|---|
redmine_tickets.py |
CLI entry + sync + routing |
redmine_ticket_query.py |
Local AST filters |
redmine_ticket_view.py |
Markdown render / pager |
redmine_ticket_browse.py |
fzf loop + action menu |
redmine_ticket_filters.py |
Saved filters / queues |
redmine_ticket_drafts.py |
Draft queue + write safety hosts |
redmine_ticket_meta.py |
Name→id meta cache |
redmine_ticket_control_tags.py |
Local tags store |
redmine_ticket_control_export.py |
Tag JSONL export |
redmine_ticket_contacts.py |
Contact mirror |
redmine_ticket_attachments.py |
Upload/download helpers |
redmine_ticket_entities.py |
Pattern extract |
redmine_ticket_nmi.py |
NMI CLI bridge |
redmine_ticket_ocr.py |
Attachment OCR + subject suggest |
redmine_contacts.py |
Full CRM CLI (sibling tool) |