333 lines
13 KiB
Markdown
333 lines
13 KiB
Markdown
# 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 in `docs/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`
|
||
- `note` never emails; `customer-email` always emails (ugly confirm flag)
|
||
- Browse/actions **queue drafts** by default (apply later)
|
||
- Dual tools OK for now: tickets workbench vs full `redmine_contacts.py` CRM 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|suggest|export|…` |
|
||
| Contacts | `contacts-sync` · `contacts-search` · `contact-show` · `contact-set` · `ticket-contact --fetch` |
|
||
| Files | `attachments list|download|upload` |
|
||
| Tracking/order hints | `entities show|scan` · `entities nmi` · browse `m` |
|
||
| 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]]
|
||
|
||
- [x] Stable cache path (not per-run timestamped dirs)
|
||
- [x] Incremental full-fetch skip when `updated_on` unchanged
|
||
- [x] Server-side `updated_on` + `status_id` (remote flags)
|
||
- [x] Local post-filters retained
|
||
- [x] Sync progress + `--quiet` for cron
|
||
- [x] Cache never auto-deletes old ticket JSON
|
||
- [x] Run-stats ring buffer (`--run-log`)
|
||
- [x] Cron: live + dev CS scripts; **6h** remote window; rematerialize queues after sync
|
||
- [x] Optional contacts cron: `cron-scripts/sync-live-contacts.sh`
|
||
- [ ] Confirm long-term cadence (currently ~every few minutes) is still desired
|
||
- [x] Full fetch includes `attachments` metadata (not just journals/helpdesk)
|
||
|
||
```bash
|
||
./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]]
|
||
|
||
- [x] JSON path filters + `includes()` / `since()` / AND/OR / parens
|
||
- [x] Nested helpdesk/contact paths
|
||
- [x] `--ids-only`, ordering, max caps
|
||
- [x] Table columns: assignee, contact, control tags
|
||
- [x] `--has-attachments` (cache metadata)
|
||
- [x] **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`
|
||
|
||
- [x] Ticket → Markdown (`view`) + MD cache under `markdown/`
|
||
- [x] Pagers: glow / bat / batcat / cat
|
||
- [x] `walk` (n/p/q)
|
||
- [x] **`browse` loop:** 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
|
||
|
||
```bash
|
||
./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`
|
||
|
||
- [x] `saved-filters/*.expr` + `@name`
|
||
- [x] `materialize-queues` symlink folders
|
||
- [x] `note` vs `customer-email` (hard separation; Helpdesk `email_note` path)
|
||
- [x] Draft list/cancel/apply; draft binds `base_url`; obvious `draft_id=`
|
||
- [x] `set-fields` + name→id (`meta-refresh`)
|
||
- [x] Parent `--allow-prod-writes` (not subparser-shadowed)
|
||
- [x] Minimal `--config` + `bin/rtl` / `bin/rtd` / `bin/qaa`
|
||
- [ ] Nested config defaults (`search.max-tickets` etc.) — [[features/config-files]]
|
||
- [ ] Note body from file + optional pandoc→Textile (steering list)
|
||
|
||
```bash
|
||
./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`
|
||
|
||
- [x] Local wipeable store under `control-tags/tags.json`
|
||
- [x] add / remove / list / show / for-issue / wipe
|
||
- [x] suggest (`--where` rules, optional `--apply` local)
|
||
- [x] `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)
|
||
|
||
- [x] Durable mirror: `<cache>/contacts/`
|
||
- [x] `contacts-sync` / `contacts-search` / `contact-show`
|
||
- [x] `contact-set` draft/apply (tags, company, names, `--set`)
|
||
- [x] `ticket-contact` bridge; **`--fetch` implies show+refresh** (bugfix)
|
||
- [x] 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`
|
||
|
||
- [x] `attachments list` (cache-first, `--refresh`)
|
||
- [x] `attachments download` (one / `--all`)
|
||
- [x] `attachments upload --apply` (POST `/uploads.json` + issue `uploads`; no email)
|
||
- [x] 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`
|
||
|
||
- [x] Pattern extract: UPS/USPS, order/SKU/account + LDR `item_no`
|
||
- [x] Shown in Markdown view
|
||
- [x] `entities show|scan`
|
||
- [x] `entities nmi` bridge to `mrscClient-php bin/nmi` (cs-brief / resolve)
|
||
- [x] Noise filters on medium-confidence `order_id` hits
|
||
- [ ] Richer NMI membership index (known order/item windows) if CLI alone isn’t enough
|
||
- [x] One-key NMI from browse menu (`m` → private note draft)
|
||
|
||
---
|
||
|
||
## Open polish / small bugs (browse + ops)
|
||
|
||
- [x] Browse: fzf header/keybinding discoverability
|
||
- [x] Browse: optional preview window in fzf (`--no-preview` to disable)
|
||
- [x] Browse: louder draft queue banner (`announce_draft`)
|
||
- [x] Browse: `m` → entities nmi brief → private note draft
|
||
- [x] Entity noise filters + LDR 9-digit `item_no`
|
||
- [x] Note `--body-file` (already) + `--to-textile` (pandoc)
|
||
- [x] `search`/`browse`/`walk` `--open-only`
|
||
- [x] 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**
|
||
1. Deeper `contact-set` (custom fields) or keep routing to `redmine_contacts.py`
|
||
2. Control-tag LLM refine from JSONL export
|
||
3. 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 cookbook**
|
||
- `docs/ticket_sync_tool_plan.md` — original sync plan
|
||
- `docs/2026-07-14_localsearch_before_sanity.md` — historical
|
||
- `docs/ticket_spam_cleanup_handoff_2026-07-09.md` — spam consumers
|
||
- `docs/raw-notes/features/*.md` — detail files
|
||
- `configs/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
|
||
|
||
1. Put full detail in `docs/raw-notes/features/<name>.md`
|
||
2. Leave status + short bullets + link here
|
||
3. Prefer updating the feature file over re-pasting long prose into this index
|
||
4. 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
|
||
- [x] Parent flag only; subparser copies removed (child defaults overwrote parent)
|
||
|
||
### Draft id not obvious
|
||
- [x] `draft_id=` / `apply_hint=` key=value output
|
||
|
||
### Drafts ignore create-time base-url
|
||
- [x] Apply prefers draft `base_url`; conflict needs `--allow-base-url-override`
|
||
|
||
### Configs / short aliases
|
||
- [x] Minimal `--config` + `bin/rtl|rtd|qaa`
|
||
|
||
### `ticket-contact --fetch` alone did nothing
|
||
- [x] `--fetch` implies 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) |
|