Files
redmine/docs/raw-notes/features-for-redmine_tickets-sync.md
T
Jason Thistlethwaite 9ee9eb3ec6 Organized
2026-10-17 20:50:35 -04:00

333 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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) |