# Redmine CLI runtime The live wrappers are deliberately short: ```bash bin/rcontacts-live search acme bin/rtickets-live search --where 'assigned_to IS NULL' bin/rtl search --where 'assigned_to IS NULL' # short live alias bin/rtd search --where 'assigned_to IS NULL' # explicit LAN/dev lane ``` `--help` works without credentials. The wrappers resolve configuration independently of the current working directory and pass ordinary arguments unchanged to `redmine_contacts.py` or `redmine_tickets.py`. ## Diagnose configuration without API traffic ```bash bin/rcontacts-live --doctor bin/rtickets-live --show-config bin/rtl --doctor-nmi ``` Diagnostics show effective paths and each setting’s source. Secrets are never printed. `network_checked: false` means readiness is local; it does not prove remote authentication. ## Precedence and files Highest priority wins: 1. wrapper CLI options; 2. exported environment; 3. selected Redmine env file; 4. ticket JSON/YAML `--config` values; 5. defaults. Select an env file with `--env-file PATH` or `REDMINE_ENV_FILE`. Otherwise the first existing file is used: - live: checkout `.env`, legacy `bin/.env`, `~/.redmine.live`; - dev: checkout `.env.dev`, `bin/.env.dev`, `~/.redmine.dev`. Dev never falls back to the live file. An explicitly selected missing, empty, or malformed file is an error. Files use `NAME=value` and are parsed as data; shell commands are not evaluated. ```text REDMINE_API_KEY required for API operations REDMINE_BASE_URL live default https://rm.ldrprep.com REDMINE_PROJECT default customer-service REDMINE_BASE_CACHE common mutable-data root REDMINE_CONTACTS_CACHE exact full-CRM snapshot JSON file REDMINE_TICKETS_CACHE exact ticket-cache directory REDMINE_LOCK_TIMEOUT writer wait, seconds; default 60 REDMINE_ENV_FILE explicit env profile REDMINE_API_KEY_FILE file containing only the Redmine key ``` NMI bridge variables (`NMI_CLI`, `NMI_ENV_FILE`, `NMI_MODE`, endpoint/credential variables) pass through to PHP. `rtl` defaults NMI to production; `rtd` defaults it to dev. `--doctor-nmi` runs the actual PHP resolver offline. ## Cache layout `REDMINE_BASE_CACHE` chooses the common root, defaulting to checkout `cache/` live and `cache-dev/` dev. - Tickets normally use `/redmine_tickets/`. - Other endpoint/project combinations get deterministic distinct ticket directories. - Fresh contacts setups use `/redmine_contacts/_.json`. - If historical `/redmine_contacts` is already a **file** for normal live use, the wrapper preserves and uses it. There is no automatic migration. - Tickets’ `/contacts/` mirror is a different per-contact format, not the full contacts snapshot. Exact `--cache-dir DIR` (tickets) and `--cache FILE` (contacts) win. Contacts rejects a cache directory because `redmine_contacts.py` expects one JSON file. `ticket-contact` prints a shell-quoted full-CRM handoff that retains endpoint, project, env profile, API-key-file context, and cache file without putting a key in argv. ## Concurrency and cron Writers use POSIX advisory locks around read/modify/write transactions and unique same-directory temporary files followed by atomic replacement. A process’s death releases its kernel lock; the persistent `.lock` file is only an inode anchor. A later holder removes matching temporary residue left by a killed writer. Contention is visible on stderr. After `REDMINE_LOCK_TIMEOUT`, the process exits nonzero rather than making a cron failure silent. ```cron REDMINE_ENV_FILE=/etc/ldr/redmine-live.env /opt/redmine/bin/rtickets-live sync --remote-updated-since 6h --run-log --quiet ``` ## Completion and safety ```bash source bin/redmine-runtime-completion.bash ``` This registers all four wrappers and includes the established contacts completion surface. The wrappers do not weaken write safety: production ticket writes still require `--allow-prod-writes`; customer email still requires its long confirmation; NMI retains its independent mutation gates.